From 6c063b147e8f0bf5d36804d3bd10dfde2aee7f42 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 18 Oct 2024 11:37:44 +0200 Subject: [PATCH] Add StudioList component --- .../components/StudioList/StudioList.test.tsx | 84 +++++++++++++++++++ .../src/components/StudioList/StudioList.tsx | 27 ++++++ .../StudioList/StudioListOrdered.stories.tsx | 34 ++++++++ .../components/StudioList/StudioListRoot.tsx | 11 +++ .../StudioListUnordered.stories.tsx | 34 ++++++++ .../src/components/StudioList/index.ts | 1 + .../studio-components/src/components/index.ts | 1 + 7 files changed, 192 insertions(+) create mode 100644 frontend/libs/studio-components/src/components/StudioList/StudioList.test.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioList/StudioList.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioList/StudioListOrdered.stories.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioList/StudioListRoot.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioList/StudioListUnordered.stories.tsx create mode 100644 frontend/libs/studio-components/src/components/StudioList/index.ts diff --git a/frontend/libs/studio-components/src/components/StudioList/StudioList.test.tsx b/frontend/libs/studio-components/src/components/StudioList/StudioList.test.tsx new file mode 100644 index 00000000000..5d4c5b3cbee --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioList/StudioList.test.tsx @@ -0,0 +1,84 @@ +import type { RenderResult } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import type { ForwardedRef } from 'react'; +import React from 'react'; +import type { StudioListRootProps } from './StudioList'; +import { StudioList } from './StudioList'; +import { testRootClassNameAppending } from '../../test-utils/testRootClassNameAppending'; +import { testRefForwarding } from '../../test-utils/testRefForwarding'; +import { testCustomAttributes } from '../../test-utils/testCustomAttributes'; + +type TestCase = { + renderList: (props?: StudioListRootProps, ref?: ForwardedRef) => RenderResult; +}; + +const testCases: Record = { + UnorderedList: { + renderList: renderUnorderedList, + }, + OrderedList: { + renderList: renderOrderedList, + }, +}; + +describe.each(Object.keys(testCases))('%s', (listType) => { + const { renderList } = testCases[listType]; + + it('Renders a list', () => { + renderList(); + expect(screen.getByRole('list')).toBeInTheDocument(); + }); + + it('Renders list items', () => { + renderList(); + expect(screen.getAllByRole('listitem')).toHaveLength(3); + }); + + it('Applies class name to the root', () => { + testRootClassNameAppending((className) => renderList({ className })); + }); + + it('Forwards the ref to the root element if given', () => { + testRefForwarding((ref) => renderList({}, ref)); + }); + + it('Appends custom attributes to the root element', () => { + testCustomAttributes(renderList); + }); +}); + +function renderUnorderedList( + props: StudioListRootProps = {}, + ref?: ForwardedRef, +): RenderResult { + return render( + + + + + , + ); +} + +function renderOrderedList( + props: StudioListRootProps = {}, + ref?: ForwardedRef, +): RenderResult { + return render( + + + + + , + ); +} + +function ListItems() { + return ( + <> + Item 1 + Item 2 + Item 3 + + ); +} diff --git a/frontend/libs/studio-components/src/components/StudioList/StudioList.tsx b/frontend/libs/studio-components/src/components/StudioList/StudioList.tsx new file mode 100644 index 00000000000..f78eecab045 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioList/StudioList.tsx @@ -0,0 +1,27 @@ +import type { + ListHeadingProps, + ListItemProps, + ListUnorderedProps, +} from '@digdir/designsystemet-react'; +import { List } from '@digdir/designsystemet-react'; +import { StudioListRoot } from './StudioListRoot'; + +type StudioListComponent = typeof List; + +export const StudioList: StudioListComponent = { + ...List, + Root: StudioListRoot, +}; + +StudioList.Root.displayName = 'StudioList.Root'; +StudioList.Item.displayName = 'StudioList.Item'; +StudioList.Heading.displayName = 'StudioList.Heading'; +StudioList.Ordered.displayName = 'StudioList.OrderedList'; +StudioList.Unordered.displayName = 'StudioList.UnorderedList'; + +export type { StudioListRootProps } from './StudioListRoot'; + +export type StudioListItemProps = ListItemProps; +export type StudioListUnorderedProps = ListUnorderedProps; +export type StudioListOrderedProps = ListUnorderedProps; +export type StudioListHeadingProps = ListHeadingProps; diff --git a/frontend/libs/studio-components/src/components/StudioList/StudioListOrdered.stories.tsx b/frontend/libs/studio-components/src/components/StudioList/StudioListOrdered.stories.tsx new file mode 100644 index 00000000000..7cd19aeeb3b --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioList/StudioListOrdered.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { StudioList } from './StudioList'; + +type Story = StoryFn; + +const meta: Meta = { + title: 'Components/StudioList/Ordered', + component: StudioList.Root, + argTypes: { + size: { + control: 'select', + options: ['sm', 'md', 'lg'], + }, + }, +}; +export const Preview: Story = (args): React.ReactElement => ; + +Preview.args = { + children: ( + <> + Lorem ipsum + + Nunc cursus turpis eget ligula blandit lobortis. + Donec et mauris id sapien laoreet placerat. + + Proin quam tortor, ullamcorper at justo nec, iaculis dapibus nisi. + + + + ), + size: 'sm', +}; +export default meta; diff --git a/frontend/libs/studio-components/src/components/StudioList/StudioListRoot.tsx b/frontend/libs/studio-components/src/components/StudioList/StudioListRoot.tsx new file mode 100644 index 00000000000..c74db9606cf --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioList/StudioListRoot.tsx @@ -0,0 +1,11 @@ +import type { ListProps } from '@digdir/designsystemet-react'; +import { List } from '@digdir/designsystemet-react'; +import React, { forwardRef } from 'react'; + +export type StudioListRootProps = ListProps; + +export const StudioListRoot = forwardRef((props, ref) => { + return ; +}); + +StudioListRoot.displayName = 'StudioList.Root'; diff --git a/frontend/libs/studio-components/src/components/StudioList/StudioListUnordered.stories.tsx b/frontend/libs/studio-components/src/components/StudioList/StudioListUnordered.stories.tsx new file mode 100644 index 00000000000..29730eb98c5 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioList/StudioListUnordered.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { StudioList } from './StudioList'; + +type Story = StoryFn; + +const meta: Meta = { + title: 'Components/StudioList/Unordered', + component: StudioList.Root, + argTypes: { + size: { + control: 'select', + options: ['sm', 'md', 'lg'], + }, + }, +}; +export const Preview: Story = (args): React.ReactElement => ; + +Preview.args = { + children: ( + <> + Lorem ipsum + + + Vivamus magna turpis, ornare in lectus eget, lacinia vehicula mauris. + + In hac habitasse platea dictumst. + Aliquam luctus mi erat, quis mattis sem aliquam eu. + + + ), + size: 'sm', +}; +export default meta; diff --git a/frontend/libs/studio-components/src/components/StudioList/index.ts b/frontend/libs/studio-components/src/components/StudioList/index.ts new file mode 100644 index 00000000000..a1b93dfbb51 --- /dev/null +++ b/frontend/libs/studio-components/src/components/StudioList/index.ts @@ -0,0 +1 @@ +export * from './StudioList'; diff --git a/frontend/libs/studio-components/src/components/index.ts b/frontend/libs/studio-components/src/components/index.ts index c7719a112b6..0a6fbcf7356 100644 --- a/frontend/libs/studio-components/src/components/index.ts +++ b/frontend/libs/studio-components/src/components/index.ts @@ -27,6 +27,7 @@ export * from './StudioInputTable'; export * from './StudioLabelAsParagraph'; export * from './StudioLabelWrapper'; export * from './StudioLink'; +export * from './StudioList'; export * from './StudioModal'; export * from './StudioNativeSelect'; export * from './StudioNotFoundPage';