Skip to content

Commit

Permalink
feat: Added section support to Picker (#1847)
Browse files Browse the repository at this point in the history
- Picker component now supports <Section> children
- Re-exported Spectrum Item + Section components

Supports deephaven/deephaven-plugins/issues/292
  • Loading branch information
bmingles authored Mar 4, 2024
1 parent 37932d9 commit 1381ee7
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 78 deletions.
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 26 additions & 3 deletions packages/code-studio/src/styleguide/Pickers.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { Picker } from '@deephaven/components';
import { Item, Picker, Section } from '@deephaven/components';
import { vsPerson } from '@deephaven/icons';
import { Flex, Icon, Item, Text } from '@adobe/react-spectrum';
import { Flex, Icon, Text } from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { sampleSectionIdAndClasses } from './utils';

Expand Down Expand Up @@ -40,9 +40,32 @@ export function Pickers(): JSX.Element {
<Item>Item Bbb</Item>
<Item textValue="Complex Ccc">
<PersonIcon />
<Text>Complex Ccc</Text>
<Text>Complex Ccc with text that should be truncated</Text>
</Item>
</Picker>

<Picker label="Sections" tooltip>
{/* eslint-disable react/jsx-curly-brace-presence */}
{'String 1'}
{'String 2'}
{'String 3'}
<Section title="Section A">
<Item>Item Aaa</Item>
<Item>Item Bbb</Item>
<Item textValue="Complex Ccc">
<PersonIcon />
<Text>Complex Ccc</Text>
</Item>
</Section>
<Section>
<Item>Item Ddd</Item>
<Item>Item Eee</Item>
<Item textValue="Complex Fff">
<PersonIcon />
<Text>Complex Fff</Text>
</Item>
</Section>
</Picker>
</Flex>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},
"dependencies": {
"@adobe/react-spectrum": "^3.34.1",
"@react-types/shared": "^3.22.1",
"@deephaven/icons": "file:../icons",
"@deephaven/log": "file:../log",
"@deephaven/react-hooks": "file:../react-hooks",
Expand Down
13 changes: 13 additions & 0 deletions packages/components/src/spectrum/Item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Wrapping Spectrum `Item` components will break functionality due to the way
* they are consumed by collection components. They are only used to pass data
* and don't render anything on their own, so they don't need to be wrapped.
* See https://github.com/adobe/react-spectrum/blob/main/packages/%40react-stately/collections/src/Item.ts#L17
*/
import { Item } from '@adobe/react-spectrum';

export type { ItemProps } from '@react-types/shared';

export { Item };

export default Item;
13 changes: 13 additions & 0 deletions packages/components/src/spectrum/Section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Wrapping Spectrum `Section` components will break functionality due to the way
* they are consumed by collection components. They are only used to pass data
* and don't render anything on their own, so they don't need to be wrapped.
* See https://github.com/adobe/react-spectrum/blob/main/packages/%40react-stately/collections/src/Section.ts#L18
*/
import { Section } from '@adobe/react-spectrum';

export type { SectionProps } from '@react-types/shared';

export { Section };

export default Section;
2 changes: 2 additions & 0 deletions packages/components/src/spectrum/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './picker';
export * from './Item';
export * from './Section';
59 changes: 46 additions & 13 deletions packages/components/src/spectrum/picker/Picker.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { useMemo } from 'react';
import { Item, Picker as SpectrumPicker } from '@adobe/react-spectrum';
import { Key, useCallback, useMemo } from 'react';
import { Picker as SpectrumPicker } from '@adobe/react-spectrum';
import cl from 'classnames';
import { Tooltip } from '../../popper';
import {
NormalizedSpectrumPickerProps,
normalizePickerItemList,
normalizeTooltipOptions,
PickerItem,
PickerItemOrSection,
PickerItemKey,
TooltipOptions,
NormalizedPickerItem,
isNormalizedPickerSection,
} from './PickerUtils';
import { PickerItemContent } from './PickerItemContent';
import { Item } from '../Item';
import { Section } from '../Section';

export type PickerProps = {
children: PickerItem | PickerItem[];
children: PickerItemOrSection | PickerItemOrSection[];
/** Can be set to true or a TooltipOptions to enable item tooltips */
tooltip?: boolean | TooltipOptions;
/** The currently selected key in the collection (controlled). */
Expand Down Expand Up @@ -54,11 +59,13 @@ export type PickerProps = {
*/
export function Picker({
children,
tooltip,
tooltip = true,
defaultSelectedKey,
selectedKey,
onChange,
onSelectionChange,
// eslint-disable-next-line camelcase
UNSAFE_className,
...spectrumPickerProps
}: PickerProps): JSX.Element {
const normalizedItems = useMemo(
Expand All @@ -71,10 +78,29 @@ export function Picker({
[tooltip]
);

const renderItem = useCallback(
({ key, content, textValue }: NormalizedPickerItem) => (
// The `textValue` prop gets used to provide the content of `<option>`
// elements that back the Spectrum Picker. These are not visible in the UI,
// but are used for accessibility purposes, so we set to an arbitrary
// 'Empty' value so that they are not empty strings.
<Item key={key as Key} textValue={textValue === '' ? 'Empty' : textValue}>
<PickerItemContent>{content}</PickerItemContent>
{tooltipOptions == null || content === '' ? null : (
<Tooltip options={tooltipOptions}>
{typeof content === 'boolean' ? String(content) : content}
</Tooltip>
)}
</Item>
),
[tooltipOptions]
);

return (
<SpectrumPicker
// eslint-disable-next-line react/jsx-props-no-spreading
{...spectrumPickerProps}
UNSAFE_className={cl('dh-picker', UNSAFE_className)}
items={normalizedItems}
// Type assertions are necessary for `selectedKey`, `defaultSelectedKey`,
// and `onSelectionChange` due to Spectrum types not accounting for
Expand All @@ -89,14 +115,21 @@ export function Picker({
onSelectionChange) as NormalizedSpectrumPickerProps['onSelectionChange']
}
>
{({ content, textValue }) => (
<Item textValue={textValue === '' ? 'Empty' : textValue}>
<PickerItemContent>{content}</PickerItemContent>
{tooltipOptions == null || content === '' ? null : (
<Tooltip options={tooltipOptions}>{content}</Tooltip>
)}
</Item>
)}
{itemOrSection => {
if (isNormalizedPickerSection(itemOrSection)) {
return (
<Section
key={itemOrSection.key}
title={itemOrSection.title}
items={itemOrSection.items}
>
{renderItem}
</Section>
);
}

return renderItem(itemOrSection);
}}
</SpectrumPicker>
);
}
Expand Down
6 changes: 6 additions & 0 deletions packages/components/src/spectrum/picker/PickerItemContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export function PickerItemContent({
content = <>&nbsp;</>;
}

if (typeof content === 'boolean') {
// Boolean values need to be stringified to render
// eslint-disable-next-line no-param-reassign
content = String(content);
}

return (
<Text UNSAFE_className={stylesCommon.spectrumEllipsis}>{content}</Text>
);
Expand Down
Loading

0 comments on commit 1381ee7

Please sign in to comment.