diff --git a/.changeset/selectpanel-item-id-required.md b/.changeset/selectpanel-item-id-required.md new file mode 100644 index 00000000000..fde2131ba48 --- /dev/null +++ b/.changeset/selectpanel-item-id-required.md @@ -0,0 +1,5 @@ +--- +"@primer/react": major +--- + +SelectPanel: Make `id` required for `items` passed to SelectPanel diff --git a/packages/react/src/FilteredActionList/FilteredActionList.tsx b/packages/react/src/FilteredActionList/FilteredActionList.tsx index 61ccb33538e..667756e277a 100644 --- a/packages/react/src/FilteredActionList/FilteredActionList.tsx +++ b/packages/react/src/FilteredActionList/FilteredActionList.tsx @@ -284,7 +284,7 @@ export function FilteredActionList({ {group.header?.title ? group.header.title : `Group ${group.groupId}`} {getItemListForEachGroup(group.groupId).map(({key: itemKey, ...item}, itemIndex) => { - const key = itemKey ?? item.id?.toString() ?? itemIndex.toString() + const key = itemKey || item.id.toString() || itemIndex.toString() return ( { - const key = itemKey ?? item.id?.toString() ?? index.toString() + const key = itemKey || item.id.toString() || index.toString() return ( React.ReactEl export type ItemInput = | Merge, FilteredActionListItemProps> - | ((Partial & {renderItem: RenderItemFn}) & {key?: Key}) + | ((Partial & { + // un-partial these fields because they are required + id: FilteredActionListItemProps['id'] + renderItem: RenderItemFn + }) & { + key?: Key + }) export interface FilteredActionListItemProps { /** @@ -85,7 +91,7 @@ export interface FilteredActionListItemProps { /** * An id associated with this item. Should be unique between items */ - id?: number | string + id: number | string /** * Node to be included inside the item before the text. @@ -124,7 +130,14 @@ export interface GroupedListProps extends ListPropsBase { * A collection of `Item` props, plus associated group identifiers * and `Item`-level custom `Item` renderers. */ - items: ((FilteredActionListItemProps | (Partial & {renderItem: RenderItemFn})) & { + items: (( + | FilteredActionListItemProps + | (Partial & { + // un-partial these fields because they are required + id: FilteredActionListItemProps['id'] + renderItem: RenderItemFn + }) + ) & { groupId: string })[] } diff --git a/packages/react/src/SelectPanel/SelectPanel.examples.stories.tsx b/packages/react/src/SelectPanel/SelectPanel.examples.stories.tsx index 05ccab9f5b9..676f00d44db 100644 --- a/packages/react/src/SelectPanel/SelectPanel.examples.stories.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.examples.stories.tsx @@ -299,7 +299,7 @@ export const CustomItemRenderer = () => { onFilterChange={setFilter} overlayProps={{width: 'medium'}} renderItem={item => ( - + {item.text} )} diff --git a/packages/react/src/SelectPanel/SelectPanel.test.tsx b/packages/react/src/SelectPanel/SelectPanel.test.tsx index b42041c08c6..156442177ed 100644 --- a/packages/react/src/SelectPanel/SelectPanel.test.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.test.tsx @@ -33,15 +33,9 @@ const renderWithFlag = (children: React.ReactNode, flag: boolean) => { } const items: SelectPanelProps['items'] = [ - { - text: 'item one', - }, - { - text: 'item two', - }, - { - text: 'item three', - }, + {id: 'one', text: 'item one'}, + {id: 'two', text: 'item two'}, + {id: 'three', text: 'item three'}, ] function BasicSelectPanel(passthroughProps: Record) { @@ -933,15 +927,9 @@ for (const usingRemoveActiveDescendant of [false, true]) { renderWithFlag( , usingRemoveActiveDescendant, @@ -1186,19 +1174,9 @@ for (const usingRemoveActiveDescendant of [false, true]) { describe('sorting', () => { const items = [ - { - text: 'item one', - id: '3', - }, - { - text: 'item two', - id: '1', - selected: true, - }, - { - text: 'item three', - id: '2', - }, + {text: 'item one', id: '3'}, + {text: 'item two', id: '1', selected: true}, + {text: 'item three', id: '2'}, ] it('should render selected items at the top by default when FF on', async () => {