diff --git a/package-lock.json b/package-lock.json
index b6bf317728..ec622e1229 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28583,6 +28583,7 @@
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@react-spectrum/theme-default": "^3.5.1",
+ "@react-types/shared": "^3.22.1",
"bootstrap": "4.6.2",
"classnames": "^2.3.1",
"event-target-shim": "^6.0.2",
@@ -31032,6 +31033,7 @@
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@react-spectrum/theme-default": "^3.5.1",
+ "@react-types/shared": "^3.22.1",
"bootstrap": "4.6.2",
"classnames": "^2.3.1",
"event-target-shim": "^6.0.2",
diff --git a/packages/code-studio/src/styleguide/Pickers.tsx b/packages/code-studio/src/styleguide/Pickers.tsx
index e698c57555..6170ec79da 100644
--- a/packages/code-studio/src/styleguide/Pickers.tsx
+++ b/packages/code-studio/src/styleguide/Pickers.tsx
@@ -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';
@@ -40,9 +40,32 @@ export function Pickers(): JSX.Element {
- Item Bbb
-
- Complex Ccc
+ Complex Ccc with text that should be truncated
+
+
+ {/* eslint-disable react/jsx-curly-brace-presence */}
+ {'String 1'}
+ {'String 2'}
+ {'String 3'}
+
+ - Item Aaa
+ - Item Bbb
+ -
+
+ Complex Ccc
+
+
+
+ - Item Ddd
+ - Item Eee
+ -
+
+ Complex Fff
+
+
+
);
diff --git a/packages/components/package.json b/packages/components/package.json
index 428e02001e..12193bcabb 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -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",
diff --git a/packages/components/src/spectrum/Item.tsx b/packages/components/src/spectrum/Item.tsx
new file mode 100644
index 0000000000..a9371db928
--- /dev/null
+++ b/packages/components/src/spectrum/Item.tsx
@@ -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;
diff --git a/packages/components/src/spectrum/Section.tsx b/packages/components/src/spectrum/Section.tsx
new file mode 100644
index 0000000000..d8b89f24af
--- /dev/null
+++ b/packages/components/src/spectrum/Section.tsx
@@ -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;
diff --git a/packages/components/src/spectrum/index.ts b/packages/components/src/spectrum/index.ts
index aea9fbd736..ec4f31aa20 100644
--- a/packages/components/src/spectrum/index.ts
+++ b/packages/components/src/spectrum/index.ts
@@ -1 +1,3 @@
export * from './picker';
+export * from './Item';
+export * from './Section';
diff --git a/packages/components/src/spectrum/picker/Picker.tsx b/packages/components/src/spectrum/picker/Picker.tsx
index 68345b26f4..668f9bdc8c 100644
--- a/packages/components/src/spectrum/picker/Picker.tsx
+++ b/packages/components/src/spectrum/picker/Picker.tsx
@@ -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). */
@@ -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(
@@ -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 `