-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Supports deephaven/deephaven-plugins#292
- Loading branch information
Showing
13 changed files
with
465 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import React from 'react'; | ||
import { Picker } from '@deephaven/components'; | ||
import { vsPerson } from '@deephaven/icons'; | ||
import { Flex, Icon, Item, Text } from '@adobe/react-spectrum'; | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||
import { sampleSectionIdAndClasses } from './utils'; | ||
|
||
function PersonIcon(): JSX.Element { | ||
return ( | ||
<Icon> | ||
<FontAwesomeIcon icon={vsPerson} /> | ||
</Icon> | ||
); | ||
} | ||
|
||
export function Pickers(): JSX.Element { | ||
return ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
<div {...sampleSectionIdAndClasses('pickers')}> | ||
<h2 className="ui-title">Pickers</h2> | ||
|
||
<Flex gap={14}> | ||
<Picker label="Single Child" tooltip={{ placement: 'bottom-end' }}> | ||
<Item>Aaa</Item> | ||
</Picker> | ||
|
||
<Picker label="Mixed Children Types" tooltip> | ||
{/* eslint-disable react/jsx-curly-brace-presence */} | ||
{'String 1'} | ||
{'String 2'} | ||
{'String 3'} | ||
{''} | ||
{'Some really long text that should get truncated'} | ||
{/* eslint-enable react/jsx-curly-brace-presence */} | ||
{444} | ||
{999} | ||
{true} | ||
{false} | ||
<Item>Item Aaa</Item> | ||
<Item>Item Bbb</Item> | ||
<Item textValue="Complex Ccc"> | ||
<PersonIcon /> | ||
<Text>Complex Ccc</Text> | ||
</Item> | ||
</Picker> | ||
</Flex> | ||
</div> | ||
); | ||
} | ||
|
||
export default Pickers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './picker'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { useMemo } from 'react'; | ||
import { Item, Picker as SpectrumPicker } from '@adobe/react-spectrum'; | ||
import { Tooltip } from '../../popper'; | ||
import { | ||
NormalizedSpectrumPickerProps, | ||
normalizePickerItemList, | ||
normalizeTooltipOptions, | ||
PickerItem, | ||
PickerItemKey, | ||
TooltipOptions, | ||
} from './PickerUtils'; | ||
import { PickerItemContent } from './PickerItemContent'; | ||
|
||
export type PickerProps = { | ||
children: PickerItem | PickerItem[]; | ||
/** Can be set to true or a TooltipOptions to enable item tooltips */ | ||
tooltip?: boolean | TooltipOptions; | ||
/** The currently selected key in the collection (controlled). */ | ||
selectedKey?: PickerItemKey | null; | ||
/** The initial selected key in the collection (uncontrolled). */ | ||
defaultSelectedKey?: PickerItemKey; | ||
/** | ||
* Handler that is called when the selection change. | ||
* Note that under the hood, this is just an alias for Spectrum's | ||
* `onSelectionChange`. We are renaming for better consistency with other | ||
* components. | ||
*/ | ||
onChange?: (key: PickerItemKey) => void; | ||
/** | ||
* Handler that is called when the selection changes. | ||
* @deprecated Use `onChange` instead | ||
*/ | ||
onSelectionChange?: (key: PickerItemKey) => void; | ||
} /* | ||
* Support remaining SpectrumPickerProps. | ||
* Note that `selectedKey`, `defaultSelectedKey`, and `onSelectionChange` are | ||
* re-defined above to account for boolean types which aren't included in the | ||
* React `Key` type, but are actually supported by the Spectrum Picker component. | ||
*/ & Omit< | ||
NormalizedSpectrumPickerProps, | ||
| 'children' | ||
| 'items' | ||
| 'onSelectionChange' | ||
| 'selectedKey' | ||
| 'defaultSelectedKey' | ||
>; | ||
|
||
/** | ||
* Picker component for selecting items from a list of items. Items can be | ||
* provided via the `items` prop or as children. Each item can be a string, | ||
* number, boolean, or a Spectrum <Item> element. The remaining props are just | ||
* pass through props for the Spectrum Picker component. | ||
* See https://react-spectrum.adobe.com/react-spectrum/Picker.html | ||
*/ | ||
export function Picker({ | ||
children, | ||
tooltip, | ||
defaultSelectedKey, | ||
selectedKey, | ||
onChange, | ||
onSelectionChange, | ||
...spectrumPickerProps | ||
}: PickerProps): JSX.Element { | ||
const normalizedItems = useMemo( | ||
() => normalizePickerItemList(children), | ||
[children] | ||
); | ||
|
||
const tooltipOptions = useMemo( | ||
() => normalizeTooltipOptions(tooltip), | ||
[tooltip] | ||
); | ||
|
||
return ( | ||
<SpectrumPicker | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...spectrumPickerProps} | ||
items={normalizedItems} | ||
// Type assertions are necessary for `selectedKey`, `defaultSelectedKey`, | ||
// and `onSelectionChange` due to Spectrum types not accounting for | ||
// `boolean` keys | ||
selectedKey={selectedKey as NormalizedSpectrumPickerProps['selectedKey']} | ||
defaultSelectedKey={ | ||
defaultSelectedKey as NormalizedSpectrumPickerProps['defaultSelectedKey'] | ||
} | ||
// `onChange` is just an alias for `onSelectionChange` | ||
onSelectionChange={ | ||
(onChange ?? | ||
onSelectionChange) as NormalizedSpectrumPickerProps['onSelectionChange'] | ||
} | ||
> | ||
{({ content, textValue }) => ( | ||
<Item textValue={textValue === '' ? 'Empty' : textValue}> | ||
<PickerItemContent>{content}</PickerItemContent> | ||
{tooltipOptions == null || content === '' ? null : ( | ||
<Tooltip options={tooltipOptions}>{content}</Tooltip> | ||
)} | ||
</Item> | ||
)} | ||
</SpectrumPicker> | ||
); | ||
} | ||
|
||
export default Picker; |
31 changes: 31 additions & 0 deletions
31
packages/components/src/spectrum/picker/PickerItemContent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { isValidElement, ReactNode } from 'react'; | ||
import { Text } from '@adobe/react-spectrum'; | ||
import stylesCommon from '../../SpectrumComponent.module.scss'; | ||
|
||
export interface PickerItemContentProps { | ||
children: ReactNode; | ||
} | ||
|
||
/** | ||
* Picker item content. Text content will be wrapped in a Spectrum Text | ||
* component with ellipsis overflow handling. | ||
*/ | ||
export function PickerItemContent({ | ||
children: content, | ||
}: PickerItemContentProps): JSX.Element { | ||
if (isValidElement(content)) { | ||
return content; | ||
} | ||
|
||
if (content === '') { | ||
// Prevent the item height from collapsing when the content is empty | ||
// eslint-disable-next-line no-param-reassign | ||
content = <> </>; | ||
} | ||
|
||
return ( | ||
<Text UNSAFE_className={stylesCommon.spectrumEllipsis}>{content}</Text> | ||
); | ||
} | ||
|
||
export default PickerItemContent; |
142 changes: 142 additions & 0 deletions
142
packages/components/src/spectrum/picker/PickerUtils.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import React from 'react'; | ||
import { Item, Text } from '@adobe/react-spectrum'; | ||
import { | ||
NormalizedPickerItem, | ||
normalizeTooltipOptions, | ||
normalizePickerItemList, | ||
PickerItem, | ||
} from './PickerUtils'; | ||
import type { PickerProps } from './Picker'; | ||
|
||
beforeEach(() => { | ||
expect.hasAssertions(); | ||
}); | ||
|
||
/* eslint-disable react/jsx-key */ | ||
const expectedNormalizations = new Map<PickerItem, NormalizedPickerItem>([ | ||
[ | ||
999, | ||
{ | ||
content: '999', | ||
key: 999, | ||
textValue: '999', | ||
}, | ||
], | ||
[ | ||
true, | ||
{ | ||
content: 'true', | ||
key: true, | ||
textValue: 'true', | ||
}, | ||
], | ||
[ | ||
false, | ||
{ | ||
content: 'false', | ||
key: false, | ||
textValue: 'false', | ||
}, | ||
], | ||
[ | ||
'', | ||
{ | ||
content: '', | ||
key: '', | ||
textValue: '', | ||
}, | ||
], | ||
[ | ||
'String', | ||
{ | ||
content: 'String', | ||
key: 'String', | ||
textValue: 'String', | ||
}, | ||
], | ||
[ | ||
<Item>Single string child no textValue</Item>, | ||
{ | ||
content: 'Single string child no textValue', | ||
key: 'Single string child no textValue', | ||
textValue: 'Single string child no textValue', | ||
}, | ||
], | ||
[ | ||
<Item> | ||
<span>No textValue</span> | ||
</Item>, | ||
{ | ||
content: <span>No textValue</span>, | ||
key: '', | ||
textValue: '', | ||
}, | ||
], | ||
[ | ||
<Item textValue="textValue">Single string</Item>, | ||
{ | ||
content: 'Single string', | ||
key: 'Single string', | ||
textValue: 'textValue', | ||
}, | ||
], | ||
[ | ||
<Item key="explicit.key" textValue="textValue"> | ||
Explicit key | ||
</Item>, | ||
{ | ||
content: 'Explicit key', | ||
key: 'explicit.key', | ||
textValue: 'textValue', | ||
}, | ||
], | ||
[ | ||
<Item textValue="textValue"> | ||
<i>i</i> | ||
<Text>Complex</Text> | ||
</Item>, | ||
{ | ||
content: [<i>i</i>, <Text>Complex</Text>], | ||
key: 'textValue', | ||
textValue: 'textValue', | ||
}, | ||
], | ||
]); | ||
/* eslint-enable react/jsx-key */ | ||
|
||
const mixedItems = [...expectedNormalizations.keys()]; | ||
|
||
const children = { | ||
empty: [] as PickerProps['children'], | ||
single: mixedItems[0] as PickerProps['children'], | ||
mixed: mixedItems as PickerProps['children'], | ||
}; | ||
|
||
describe('normalizePickerItemList', () => { | ||
it.each([children.empty, children.single, children.mixed])( | ||
'should return normalized picker items: %s', | ||
given => { | ||
const childrenArray = Array.isArray(given) ? given : [given]; | ||
|
||
const expected = childrenArray.map(item => | ||
expectedNormalizations.get(item) | ||
); | ||
|
||
const actual = normalizePickerItemList(given); | ||
expect(actual).toEqual(expected); | ||
} | ||
); | ||
}); | ||
|
||
describe('normalizeTooltipOptions', () => { | ||
it.each([ | ||
[undefined, null], | ||
[null, null], | ||
[false, null], | ||
[true, { placement: 'top-start' }], | ||
[{ placement: 'bottom-end' }, { placement: 'bottom-end' }], | ||
] as const)('should return: %s', (options, expected) => { | ||
const actual = normalizeTooltipOptions(options); | ||
expect(actual).toEqual(expected); | ||
}); | ||
}); |
Oops, something went wrong.