diff --git a/packages/components/src/spectrum/picker/Picker.tsx b/packages/components/src/spectrum/picker/Picker.tsx
index 30702010a3..b816018731 100644
--- a/packages/components/src/spectrum/picker/Picker.tsx
+++ b/packages/components/src/spectrum/picker/Picker.tsx
@@ -17,7 +17,10 @@ import { PickerItemContent } from './PickerItemContent';
import { Item, Section } from '../shared';
export type PickerProps = {
- children: PickerItemOrSection | PickerItemOrSection[];
+ children:
+ | PickerItemOrSection
+ | PickerItemOrSection[]
+ | NormalizedPickerItem[];
/** Can be set to true or a TooltipOptions to enable item tooltips */
tooltip?: boolean | TooltipOptions;
/** The currently selected key in the collection (controlled). */
@@ -99,31 +102,38 @@ export function Picker({
);
const renderItem = useCallback(
- ({ key, item }: NormalizedPickerItem) => (
- // The `textValue` prop gets used to provide the content of ``
- // 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?.content == null ? null : (
- <>
-
{item.content}
- {tooltipOptions == null || item.content === '' ? null : (
-
- {createTooltipContent(item.content)}
-
- )}
- >
- )}
-
- ),
+ (normalizedItem: NormalizedPickerItem) => {
+ // In Windowed data scenarios, the `item` is loaded asynchronously.
+ // Fallback to the top-level `key` if the `item` is not yet available.
+ const key = normalizedItem.item?.key ?? normalizedItem.key;
+ const { item } = normalizedItem;
+
+ return (
+ // The `textValue` prop gets used to provide the content of ` `
+ // 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?.content == null ? null : (
+ <>
+
{item.content}
+ {tooltipOptions == null || item.content === '' ? null : (
+
+ {createTooltipContent(item.content)}
+
+ )}
+ >
+ )}
+
+ );
+ },
[tooltipOptions]
);
@@ -150,7 +160,9 @@ export function Picker({
if (isNormalizedPickerSection(itemOrSection)) {
return (
diff --git a/packages/components/src/spectrum/picker/PickerUtils.ts b/packages/components/src/spectrum/picker/PickerUtils.ts
index 2340762340..28a3eabbf8 100644
--- a/packages/components/src/spectrum/picker/PickerUtils.ts
+++ b/packages/components/src/spectrum/picker/PickerUtils.ts
@@ -41,6 +41,18 @@ export type PickerItemKey = Key | boolean;
*/
export type PickerSelectionChangeHandler = (key: PickerItemKey) => void;
+export interface NormalizedPickerItemData {
+ key?: PickerItemKey;
+ content: ReactNode;
+ textValue?: string;
+}
+
+export interface NormalizedPickerSectionData {
+ key?: Key;
+ title?: ReactNode;
+ items: NormalizedPickerItem[];
+}
+
/**
* The Picker supports a variety of item types, including strings, numbers,
* booleans, and more complex React elements. This type represents a normalized
@@ -48,18 +60,12 @@ export type PickerSelectionChangeHandler = (key: PickerItemKey) => void;
* in separate util methods.
*/
export type NormalizedPickerItem = KeyedItem<
- {
- content: ReactNode;
- textValue?: string;
- },
+ NormalizedPickerItemData,
PickerItemKey | undefined
>;
export type NormalizedPickerSection = KeyedItem<
- {
- title?: ReactNode;
- items: NormalizedPickerItem[];
- },
+ NormalizedPickerSectionData,
Key | undefined
>;
@@ -108,6 +114,17 @@ export function isPickerItemOrSection(
);
}
+export function isNormalizedPickerItemList(
+ node: PickerItemOrSection | PickerItemOrSection[] | NormalizedPickerItem[]
+): node is NormalizedPickerItem[] {
+ return (
+ Array.isArray(node) &&
+ node.length > 0 &&
+ !isPickerItemOrSection(node[0]) &&
+ 'key' in node[0]
+ );
+}
+
/**
* Determine if an object is a normalized Picker section.
* @param maybeNormalizedPickerSection The object to check
@@ -228,11 +245,20 @@ function normalizePickerItem(
* @returns An array of normalized picker items
*/
export function normalizePickerItemList(
- itemsOrSections: PickerItemOrSection | PickerItemOrSection[]
+ itemsOrSections:
+ | PickerItemOrSection
+ | PickerItemOrSection[]
+ | NormalizedPickerItem[]
): (NormalizedPickerItem | NormalizedPickerSection)[] {
+ // If already normalized, just return as-is
+ if (isNormalizedPickerItemList(itemsOrSections)) {
+ return itemsOrSections;
+ }
+
const itemsArray = Array.isArray(itemsOrSections)
? itemsOrSections
: [itemsOrSections];
+
return itemsArray.map(normalizePickerItem);
}