diff --git a/.changeset/lucky-ducks-camp.md b/.changeset/lucky-ducks-camp.md new file mode 100644 index 00000000000..4072bfd4510 --- /dev/null +++ b/.changeset/lucky-ducks-camp.md @@ -0,0 +1,5 @@ +--- +"@primer/components": patch +--- + +Use `Item` `index` as fallback key within `List` groups diff --git a/src/ActionList/List.tsx b/src/ActionList/List.tsx index 3c9761244e9..6c050a5b689 100644 --- a/src/ActionList/List.tsx +++ b/src/ActionList/List.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react' +import React from 'react' import type {AriaRole} from '../utils/types' import {Group, GroupProps} from './Group' import {Item, ItemProps} from './Item' @@ -6,7 +6,6 @@ import {Divider} from './Divider' import styled from 'styled-components' import {get} from '../constants' import {SystemCssProperties} from '@styled-system/css' -import {uniqueId} from '../utils/uniqueId' export type ItemInput = ItemProps | (Partial & {renderItem: typeof Item}) @@ -151,10 +150,10 @@ export function List(props: ListProps): JSX.Element { * An `Item`-level, `Group`-level, or `List`-level custom `Item` renderer, * or the default `Item` renderer. */ - const renderItem = (itemProps: ItemInput, item: ItemInput) => { + const renderItem = (itemProps: ItemInput, item: ItemInput, itemIndex: number) => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const ItemComponent = ('renderItem' in itemProps && itemProps.renderItem) || props.renderItem || Item - const key = itemProps.key ?? itemProps.id?.toString() ?? uniqueId() + const key = itemProps.key ?? itemProps.id?.toString() ?? itemIndex.toString() return ( & {renderItem?: typeof Item; renderGroup?: typeof Group}))[] = [] // Collect rendered `Item`s into `Group`s, avoiding excess iteration over the lists of `items` and `groupMetadata`: - const singleGroupId = useMemo(uniqueId, []) - if (!isGroupedListProps(props)) { // When no `groupMetadata`s is provided, collect rendered `Item`s into a single anonymous `Group`. - groups = [{items: props.items.map(item => renderItem(item, item)), groupId: singleGroupId}] + groups = [{items: props.items.map((item, index) => renderItem(item, item, index)), groupId: '0'}] } else { // When `groupMetadata` is provided, collect rendered `Item`s into their associated `Group`s. @@ -192,6 +189,7 @@ export function List(props: ListProps): JSX.Element { for (const itemProps of props.items) { // Look up the group associated with the current item. const group = groupMap.get(itemProps.groupId) + const itemIndex = group?.items?.length ?? 0 // Upsert the group to include the current item (rendered). groupMap.set(itemProps.groupId, { @@ -204,7 +202,8 @@ export function List(props: ListProps): JSX.Element { ...(group && 'renderItem' in group && {renderItem: group.renderItem}), ...itemProps }, - itemProps + itemProps, + itemIndex ) ] })