diff --git a/.changeset/slow-colts-pick.md b/.changeset/slow-colts-pick.md new file mode 100644 index 000000000..733a2b52b --- /dev/null +++ b/.changeset/slow-colts-pick.md @@ -0,0 +1,5 @@ +--- +'@aragon/gov-ui-kit': minor +--- + +Update `DataListItem` component to wrap in `div` when non-interactive diff --git a/src/core/components/dataList/dataListContainer/dataListContainerSkeletonLoader.tsx b/src/core/components/dataList/dataListContainer/dataListContainerSkeletonLoader.tsx index 28225140c..f25e89fb0 100644 --- a/src/core/components/dataList/dataListContainer/dataListContainerSkeletonLoader.tsx +++ b/src/core/components/dataList/dataListContainer/dataListContainerSkeletonLoader.tsx @@ -4,7 +4,12 @@ export interface IDataListContainerSkeletonLoaderProps {} export const DataListContainerSkeletonLoader: React.FC = () => { return ( - +
diff --git a/src/core/components/dataList/dataListItem/dataListItem.stories.tsx b/src/core/components/dataList/dataListItem/dataListItem.stories.tsx index e543c62d9..55bd38091 100644 --- a/src/core/components/dataList/dataListItem/dataListItem.stories.tsx +++ b/src/core/components/dataList/dataListItem/dataListItem.stories.tsx @@ -20,7 +20,7 @@ type Story = StoryObj; export const Default: Story = { args: { children: 'Data list item', - className: 'min-h-12', + className: 'flex items-center min-h-12', }, }; diff --git a/src/core/components/dataList/dataListItem/dataListItem.test.tsx b/src/core/components/dataList/dataListItem/dataListItem.test.tsx index 4a7ae7462..2df8a58f1 100644 --- a/src/core/components/dataList/dataListItem/dataListItem.test.tsx +++ b/src/core/components/dataList/dataListItem/dataListItem.test.tsx @@ -31,20 +31,6 @@ describe(' component', () => { expect(link.classList).toContain('cursor-pointer'); }); - it('marks the item as hidden when the data list is on initialLoading state', () => { - const context = { state: 'initialLoading' as const }; - const props = { href: '/test' }; - render(createTestComponent({ context, props })); - expect(screen.queryByRole('link')).not.toBeInTheDocument(); - }); - - it('marks the item as hidden when the data list is on loading state with no elements being currently rendered', () => { - const context = { state: 'loading' as const, childrenItemCount: 0 }; - const props = { href: '/test' }; - render(createTestComponent({ context, props })); - expect(screen.queryByRole('link')).not.toBeInTheDocument(); - }); - it('does not throw error when not placed inside the DataListContextProvider', () => { const context = null; expect(() => render(createTestComponent({ context }))).not.toThrow(); @@ -58,9 +44,22 @@ describe(' component', () => { expect(button.classList).toContain('cursor-pointer'); }); - it('renders the item as a non interactive button when both onClick and href property are not set', () => { - const props = { onClick: undefined, href: undefined }; + it('renders the item as a non interactive div when both onClick and href property are not set', () => { + const props = { children: 'test-data-list-item', onClick: undefined, href: undefined }; + render(createTestComponent({ props })); + const button = screen.queryByRole('button', { name: props.children }); + const link = screen.queryByRole('link', { name: props.children }); + const text = screen.getByText(props.children); + expect(button).not.toBeInTheDocument(); + expect(link).not.toBeInTheDocument(); + expect(text).toBeInTheDocument(); + }); + + it('renders the item as an interactive button when onClick property is set', () => { + const props = { children: 'test-data-list-item', onClick: jest.fn() }; render(createTestComponent({ props })); - expect(screen.getByRole('button').classList).not.toContain('cursor-pointer'); + const button = screen.getByRole('button', { name: props.children }); + expect(button).toBeInTheDocument(); + expect(button.classList).toContain('cursor-pointer'); }); }); diff --git a/src/core/components/dataList/dataListItem/dataListItem.tsx b/src/core/components/dataList/dataListItem/dataListItem.tsx index 38da467ac..d03fc5693 100644 --- a/src/core/components/dataList/dataListItem/dataListItem.tsx +++ b/src/core/components/dataList/dataListItem/dataListItem.tsx @@ -1,22 +1,17 @@ import classNames from 'classnames'; -import { type AnchorHTMLAttributes, type ButtonHTMLAttributes, useContext } from 'react'; +import { type AnchorHTMLAttributes, type ButtonHTMLAttributes, type HTMLAttributes } from 'react'; import { LinkBase } from '../../link'; -import { dataListContext } from '../dataListContext'; -export type IDataListItemProps = ButtonHTMLAttributes | AnchorHTMLAttributes; +export type IDataListItemProps = + | ButtonHTMLAttributes + | AnchorHTMLAttributes + | HTMLAttributes; export const DataListItem: React.FC = (props) => { const { className, ...otherProps } = props; - // Use the dataListContext directly to support usage of DataListItem component outside the DataListContextProvider. - const { state, childrenItemCount } = useContext(dataListContext) ?? {}; - - // The DataListElement is a skeleton element on initial loading or loading state when no items are being - // rendered (e.g. after a reset filters action) - const isSkeletonElement = state === 'initialLoading' || (state === 'loading' && childrenItemCount === 0); - const isLinkElement = 'href' in otherProps && otherProps.href != null && otherProps.href !== ''; - const isInteractiveElement = !isSkeletonElement && (isLinkElement || props.onClick != null); + const isInteractiveElement = isLinkElement || props.onClick != null; const actionItemClasses = classNames( 'w-full rounded-xl border border-neutral-100 bg-neutral-0 px-4 text-left shadow-neutral-sm transition-all', // Default @@ -27,17 +22,15 @@ export const DataListItem: React.FC = (props) => { className, ); - const commonProps = { - className: actionItemClasses, - 'aria-hidden': isSkeletonElement, - tabIndex: isSkeletonElement ? -1 : 0, - }; + if (isLinkElement) { + return ; + } - if (!isLinkElement) { + if (isInteractiveElement) { const { type = 'button', ...buttonProps } = otherProps as ButtonHTMLAttributes; - return