Skip to content

Commit 1381ee7

Browse files
authored
feat: Added section support to Picker (#1847)
- Picker component now supports <Section> children - Re-exported Spectrum Item + Section components Supports deephaven/deephaven-plugins/issues/292
1 parent 37932d9 commit 1381ee7

13 files changed

Lines changed: 382 additions & 78 deletions

File tree

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/code-studio/src/styleguide/Pickers.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
2-
import { Picker } from '@deephaven/components';
2+
import { Item, Picker, Section } from '@deephaven/components';
33
import { vsPerson } from '@deephaven/icons';
4-
import { Flex, Icon, Item, Text } from '@adobe/react-spectrum';
4+
import { Flex, Icon, Text } from '@adobe/react-spectrum';
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
66
import { sampleSectionIdAndClasses } from './utils';
77

@@ -40,9 +40,32 @@ export function Pickers(): JSX.Element {
4040
<Item>Item Bbb</Item>
4141
<Item textValue="Complex Ccc">
4242
<PersonIcon />
43-
<Text>Complex Ccc</Text>
43+
<Text>Complex Ccc with text that should be truncated</Text>
4444
</Item>
4545
</Picker>
46+
47+
<Picker label="Sections" tooltip>
48+
{/* eslint-disable react/jsx-curly-brace-presence */}
49+
{'String 1'}
50+
{'String 2'}
51+
{'String 3'}
52+
<Section title="Section A">
53+
<Item>Item Aaa</Item>
54+
<Item>Item Bbb</Item>
55+
<Item textValue="Complex Ccc">
56+
<PersonIcon />
57+
<Text>Complex Ccc</Text>
58+
</Item>
59+
</Section>
60+
<Section>
61+
<Item>Item Ddd</Item>
62+
<Item>Item Eee</Item>
63+
<Item textValue="Complex Fff">
64+
<PersonIcon />
65+
<Text>Complex Fff</Text>
66+
</Item>
67+
</Section>
68+
</Picker>
4669
</Flex>
4770
</div>
4871
);

packages/components/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
},
2626
"dependencies": {
2727
"@adobe/react-spectrum": "^3.34.1",
28+
"@react-types/shared": "^3.22.1",
2829
"@deephaven/icons": "file:../icons",
2930
"@deephaven/log": "file:../log",
3031
"@deephaven/react-hooks": "file:../react-hooks",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Wrapping Spectrum `Item` components will break functionality due to the way
3+
* they are consumed by collection components. They are only used to pass data
4+
* and don't render anything on their own, so they don't need to be wrapped.
5+
* See https://github.com/adobe/react-spectrum/blob/main/packages/%40react-stately/collections/src/Item.ts#L17
6+
*/
7+
import { Item } from '@adobe/react-spectrum';
8+
9+
export type { ItemProps } from '@react-types/shared';
10+
11+
export { Item };
12+
13+
export default Item;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Wrapping Spectrum `Section` components will break functionality due to the way
3+
* they are consumed by collection components. They are only used to pass data
4+
* and don't render anything on their own, so they don't need to be wrapped.
5+
* See https://github.com/adobe/react-spectrum/blob/main/packages/%40react-stately/collections/src/Section.ts#L18
6+
*/
7+
import { Section } from '@adobe/react-spectrum';
8+
9+
export type { SectionProps } from '@react-types/shared';
10+
11+
export { Section };
12+
13+
export default Section;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export * from './picker';
2+
export * from './Item';
3+
export * from './Section';

packages/components/src/spectrum/picker/Picker.tsx

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1-
import { useMemo } from 'react';
2-
import { Item, Picker as SpectrumPicker } from '@adobe/react-spectrum';
1+
import { Key, useCallback, useMemo } from 'react';
2+
import { Picker as SpectrumPicker } from '@adobe/react-spectrum';
3+
import cl from 'classnames';
34
import { Tooltip } from '../../popper';
45
import {
56
NormalizedSpectrumPickerProps,
67
normalizePickerItemList,
78
normalizeTooltipOptions,
8-
PickerItem,
9+
PickerItemOrSection,
910
PickerItemKey,
1011
TooltipOptions,
12+
NormalizedPickerItem,
13+
isNormalizedPickerSection,
1114
} from './PickerUtils';
1215
import { PickerItemContent } from './PickerItemContent';
16+
import { Item } from '../Item';
17+
import { Section } from '../Section';
1318

1419
export type PickerProps = {
15-
children: PickerItem | PickerItem[];
20+
children: PickerItemOrSection | PickerItemOrSection[];
1621
/** Can be set to true or a TooltipOptions to enable item tooltips */
1722
tooltip?: boolean | TooltipOptions;
1823
/** The currently selected key in the collection (controlled). */
@@ -54,11 +59,13 @@ export type PickerProps = {
5459
*/
5560
export function Picker({
5661
children,
57-
tooltip,
62+
tooltip = true,
5863
defaultSelectedKey,
5964
selectedKey,
6065
onChange,
6166
onSelectionChange,
67+
// eslint-disable-next-line camelcase
68+
UNSAFE_className,
6269
...spectrumPickerProps
6370
}: PickerProps): JSX.Element {
6471
const normalizedItems = useMemo(
@@ -71,10 +78,29 @@ export function Picker({
7178
[tooltip]
7279
);
7380

81+
const renderItem = useCallback(
82+
({ key, content, textValue }: NormalizedPickerItem) => (
83+
// The `textValue` prop gets used to provide the content of `<option>`
84+
// elements that back the Spectrum Picker. These are not visible in the UI,
85+
// but are used for accessibility purposes, so we set to an arbitrary
86+
// 'Empty' value so that they are not empty strings.
87+
<Item key={key as Key} textValue={textValue === '' ? 'Empty' : textValue}>
88+
<PickerItemContent>{content}</PickerItemContent>
89+
{tooltipOptions == null || content === '' ? null : (
90+
<Tooltip options={tooltipOptions}>
91+
{typeof content === 'boolean' ? String(content) : content}
92+
</Tooltip>
93+
)}
94+
</Item>
95+
),
96+
[tooltipOptions]
97+
);
98+
7499
return (
75100
<SpectrumPicker
76101
// eslint-disable-next-line react/jsx-props-no-spreading
77102
{...spectrumPickerProps}
103+
UNSAFE_className={cl('dh-picker', UNSAFE_className)}
78104
items={normalizedItems}
79105
// Type assertions are necessary for `selectedKey`, `defaultSelectedKey`,
80106
// and `onSelectionChange` due to Spectrum types not accounting for
@@ -89,14 +115,21 @@ export function Picker({
89115
onSelectionChange) as NormalizedSpectrumPickerProps['onSelectionChange']
90116
}
91117
>
92-
{({ content, textValue }) => (
93-
<Item textValue={textValue === '' ? 'Empty' : textValue}>
94-
<PickerItemContent>{content}</PickerItemContent>
95-
{tooltipOptions == null || content === '' ? null : (
96-
<Tooltip options={tooltipOptions}>{content}</Tooltip>
97-
)}
98-
</Item>
99-
)}
118+
{itemOrSection => {
119+
if (isNormalizedPickerSection(itemOrSection)) {
120+
return (
121+
<Section
122+
key={itemOrSection.key}
123+
title={itemOrSection.title}
124+
items={itemOrSection.items}
125+
>
126+
{renderItem}
127+
</Section>
128+
);
129+
}
130+
131+
return renderItem(itemOrSection);
132+
}}
100133
</SpectrumPicker>
101134
);
102135
}

packages/components/src/spectrum/picker/PickerItemContent.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export function PickerItemContent({
2323
content = <>&nbsp;</>;
2424
}
2525

26+
if (typeof content === 'boolean') {
27+
// Boolean values need to be stringified to render
28+
// eslint-disable-next-line no-param-reassign
29+
content = String(content);
30+
}
31+
2632
return (
2733
<Text UNSAFE_className={stylesCommon.spectrumEllipsis}>{content}</Text>
2834
);

0 commit comments

Comments
 (0)