From f861e6a269ba9f62700776d3cd13b644a9e836d4 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Wed, 20 Sep 2023 16:38:54 +0200 Subject: refactor: use named export for everything except pages Next expect a default export for pages so only those components should use default exports. Everything else should use named exports to reduce the number of import statements. --- .../atoms/lists/description-list-group.module.scss | 40 +++++++ .../atoms/lists/description-list-group.stories.tsx | 132 +++++++++++++++++++++ .../atoms/lists/description-list-group.test.tsx | 17 +++ .../atoms/lists/description-list-group.tsx | 70 +++++++++++ .../atoms/lists/description-list-item.module.scss | 40 ------- .../atoms/lists/description-list-item.stories.tsx | 132 --------------------- .../atoms/lists/description-list-item.test.tsx | 17 --- .../atoms/lists/description-list-item.tsx | 73 ------------ .../atoms/lists/description-list.stories.tsx | 2 +- .../atoms/lists/description-list.test.tsx | 2 +- src/components/atoms/lists/description-list.tsx | 38 +++--- src/components/atoms/lists/index.ts | 3 + src/components/atoms/lists/list.stories.tsx | 10 +- src/components/atoms/lists/list.test.tsx | 2 +- src/components/atoms/lists/list.tsx | 11 +- 15 files changed, 291 insertions(+), 298 deletions(-) create mode 100644 src/components/atoms/lists/description-list-group.module.scss create mode 100644 src/components/atoms/lists/description-list-group.stories.tsx create mode 100644 src/components/atoms/lists/description-list-group.test.tsx create mode 100644 src/components/atoms/lists/description-list-group.tsx delete mode 100644 src/components/atoms/lists/description-list-item.module.scss delete mode 100644 src/components/atoms/lists/description-list-item.stories.tsx delete mode 100644 src/components/atoms/lists/description-list-item.test.tsx delete mode 100644 src/components/atoms/lists/description-list-item.tsx create mode 100644 src/components/atoms/lists/index.ts (limited to 'src/components/atoms/lists') diff --git a/src/components/atoms/lists/description-list-group.module.scss b/src/components/atoms/lists/description-list-group.module.scss new file mode 100644 index 0000000..aba90ce --- /dev/null +++ b/src/components/atoms/lists/description-list-group.module.scss @@ -0,0 +1,40 @@ +.term { + color: var(--color-fg-light); + font-weight: 600; +} + +.description { + margin: 0; + word-break: break-all; +} + +.wrapper { + display: flex; + width: fit-content; + + &--has-separator { + .description:not(:first-of-type) { + &::before { + content: "/\0000a0"; + } + } + } + + &--inline, + &--inline-values { + flex-flow: row wrap; + column-gap: var(--spacing-2xs); + } + + &--inline-values { + row-gap: var(--spacing-2xs); + + .term { + flex: 1 1 100%; + } + } + + &--stacked { + flex-flow: column wrap; + } +} diff --git a/src/components/atoms/lists/description-list-group.stories.tsx b/src/components/atoms/lists/description-list-group.stories.tsx new file mode 100644 index 0000000..e6766a3 --- /dev/null +++ b/src/components/atoms/lists/description-list-group.stories.tsx @@ -0,0 +1,132 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { DescriptionListGroup } from './description-list-group'; + +export default { + title: 'Atoms/Typography/Lists/DescriptionList/Item', + component: DescriptionListGroup, + args: { + layout: 'stacked', + withSeparator: false, + }, + argTypes: { + className: { + control: { + type: 'text', + }, + description: 'Set additional classnames to the list item wrapper.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, + descriptionClassName: { + control: { + type: 'text', + }, + description: 'Set additional classnames to the list item description.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, + label: { + control: { + type: 'text', + }, + description: 'The item label.', + type: { + name: 'string', + required: true, + }, + }, + layout: { + control: { + type: 'select', + }, + description: 'The item layout.', + options: ['inline', 'inline-values', 'stacked'], + table: { + category: 'Options', + defaultValue: { summary: 'stacked' }, + }, + type: { + name: 'string', + required: false, + }, + }, + termClassName: { + control: { + type: 'text', + }, + description: 'Set additional classnames to the list item term.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, + value: { + description: 'The item value.', + type: { + name: 'object', + required: true, + value: {}, + }, + }, + withSeparator: { + control: { + type: 'boolean', + }, + description: 'Add a slash as separator between multiple values.', + table: { + category: 'Options', + defaultValue: { summary: false }, + }, + type: { + name: 'boolean', + required: false, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +export const SingleValueStacked = Template.bind({}); +SingleValueStacked.args = { + label: 'Recusandae vitae tenetur', + value: ['praesentium'], + layout: 'stacked', +}; + +export const SingleValueInlined = Template.bind({}); +SingleValueInlined.args = { + label: 'Recusandae vitae tenetur', + value: ['praesentium'], + layout: 'inline', +}; + +export const MultipleValuesStacked = Template.bind({}); +MultipleValuesStacked.args = { + label: 'Recusandae vitae tenetur', + value: ['praesentium', 'voluptate', 'tempore'], + layout: 'stacked', +}; + +export const MultipleValuesInlined = Template.bind({}); +MultipleValuesInlined.args = { + label: 'Recusandae vitae tenetur', + value: ['praesentium', 'voluptate', 'tempore'], + layout: 'inline-values', + withSeparator: true, +}; diff --git a/src/components/atoms/lists/description-list-group.test.tsx b/src/components/atoms/lists/description-list-group.test.tsx new file mode 100644 index 0000000..4e665e0 --- /dev/null +++ b/src/components/atoms/lists/description-list-group.test.tsx @@ -0,0 +1,17 @@ +import { render, screen } from '../../../../tests/utils'; +import { DescriptionListGroup } from './description-list-group'; + +const itemLabel = 'Repellendus corporis facilis'; +const itemValue = ['quos', 'eum']; + +describe('DescriptionListGroup', () => { + it('renders a couple of label', () => { + render(); + expect(screen.getByRole('term')).toHaveTextContent(itemLabel); + }); + + it('renders the right number of values', () => { + render(); + expect(screen.getAllByRole('definition')).toHaveLength(itemValue.length); + }); +}); diff --git a/src/components/atoms/lists/description-list-group.tsx b/src/components/atoms/lists/description-list-group.tsx new file mode 100644 index 0000000..63ae541 --- /dev/null +++ b/src/components/atoms/lists/description-list-group.tsx @@ -0,0 +1,70 @@ +import { FC, ReactNode, useId } from 'react'; +import styles from './description-list-group.module.scss'; + +export type ItemLayout = 'inline' | 'inline-values' | 'stacked'; + +export type DescriptionListGroupProps = { + /** + * Set additional classnames to the list item wrapper. + */ + className?: string; + /** + * Set additional classnames to the list item description. + */ + descriptionClassName?: string; + /** + * The item label. + */ + label: string; + /** + * The item layout. + */ + layout?: ItemLayout; + /** + * Set additional classnames to the list item term. + */ + termClassName?: string; + /** + * The item value. + */ + value: ReactNode | ReactNode[]; + /** + * If true, use a slash to delimitate multiple values. + */ + withSeparator?: boolean; +}; + +/** + * DescriptionListItem component + * + * Render a couple of dt/dd wrapped in a div. + */ +export const DescriptionListGroup: FC = ({ + className = '', + descriptionClassName = '', + label, + termClassName = '', + value, + layout = 'stacked', + withSeparator = false, +}) => { + const id = useId(); + const layoutStyles = styles[`wrapper--${layout}`]; + const separatorStyles = withSeparator ? styles['wrapper--has-separator'] : ''; + const itemValues = Array.isArray(value) ? value : [value]; + const groupClass = `${styles.wrapper} ${layoutStyles} ${separatorStyles} ${className}`; + + return ( +
+
{label}
+ {itemValues.map((currentValue, index) => ( +
+ {currentValue} +
+ ))} +
+ ); +}; diff --git a/src/components/atoms/lists/description-list-item.module.scss b/src/components/atoms/lists/description-list-item.module.scss deleted file mode 100644 index aba90ce..0000000 --- a/src/components/atoms/lists/description-list-item.module.scss +++ /dev/null @@ -1,40 +0,0 @@ -.term { - color: var(--color-fg-light); - font-weight: 600; -} - -.description { - margin: 0; - word-break: break-all; -} - -.wrapper { - display: flex; - width: fit-content; - - &--has-separator { - .description:not(:first-of-type) { - &::before { - content: "/\0000a0"; - } - } - } - - &--inline, - &--inline-values { - flex-flow: row wrap; - column-gap: var(--spacing-2xs); - } - - &--inline-values { - row-gap: var(--spacing-2xs); - - .term { - flex: 1 1 100%; - } - } - - &--stacked { - flex-flow: column wrap; - } -} diff --git a/src/components/atoms/lists/description-list-item.stories.tsx b/src/components/atoms/lists/description-list-item.stories.tsx deleted file mode 100644 index c7beb0d..0000000 --- a/src/components/atoms/lists/description-list-item.stories.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import DescriptionListItemComponent from './description-list-item'; - -export default { - title: 'Atoms/Typography/Lists/DescriptionList/Item', - component: DescriptionListItemComponent, - args: { - layout: 'stacked', - withSeparator: false, - }, - argTypes: { - className: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the list item wrapper.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - descriptionClassName: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the list item description.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - label: { - control: { - type: 'text', - }, - description: 'The item label.', - type: { - name: 'string', - required: true, - }, - }, - layout: { - control: { - type: 'select', - }, - description: 'The item layout.', - options: ['inline', 'inline-values', 'stacked'], - table: { - category: 'Options', - defaultValue: { summary: 'stacked' }, - }, - type: { - name: 'string', - required: false, - }, - }, - termClassName: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the list item term.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - value: { - description: 'The item value.', - type: { - name: 'object', - required: true, - value: {}, - }, - }, - withSeparator: { - control: { - type: 'boolean', - }, - description: 'Add a slash as separator between multiple values.', - table: { - category: 'Options', - defaultValue: { summary: false }, - }, - type: { - name: 'boolean', - required: false, - }, - }, - }, -} as ComponentMeta; - -const Template: ComponentStory = ( - args -) => ; - -export const SingleValueStacked = Template.bind({}); -SingleValueStacked.args = { - label: 'Recusandae vitae tenetur', - value: ['praesentium'], - layout: 'stacked', -}; - -export const SingleValueInlined = Template.bind({}); -SingleValueInlined.args = { - label: 'Recusandae vitae tenetur', - value: ['praesentium'], - layout: 'inline', -}; - -export const MultipleValuesStacked = Template.bind({}); -MultipleValuesStacked.args = { - label: 'Recusandae vitae tenetur', - value: ['praesentium', 'voluptate', 'tempore'], - layout: 'stacked', -}; - -export const MultipleValuesInlined = Template.bind({}); -MultipleValuesInlined.args = { - label: 'Recusandae vitae tenetur', - value: ['praesentium', 'voluptate', 'tempore'], - layout: 'inline-values', - withSeparator: true, -}; diff --git a/src/components/atoms/lists/description-list-item.test.tsx b/src/components/atoms/lists/description-list-item.test.tsx deleted file mode 100644 index 07632a6..0000000 --- a/src/components/atoms/lists/description-list-item.test.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { render, screen } from '../../../../tests/utils'; -import DescriptionListItem from './description-list-item'; - -const itemLabel = 'Repellendus corporis facilis'; -const itemValue = ['quos', 'eum']; - -describe('DescriptionListItem', () => { - it('renders a couple of label', () => { - render(); - expect(screen.getByRole('term')).toHaveTextContent(itemLabel); - }); - - it('renders the right number of values', () => { - render(); - expect(screen.getAllByRole('definition')).toHaveLength(itemValue.length); - }); -}); diff --git a/src/components/atoms/lists/description-list-item.tsx b/src/components/atoms/lists/description-list-item.tsx deleted file mode 100644 index 9505d01..0000000 --- a/src/components/atoms/lists/description-list-item.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { FC, ReactNode, useId } from 'react'; -import styles from './description-list-item.module.scss'; - -export type ItemLayout = 'inline' | 'inline-values' | 'stacked'; - -export type DescriptionListItemProps = { - /** - * Set additional classnames to the list item wrapper. - */ - className?: string; - /** - * Set additional classnames to the list item description. - */ - descriptionClassName?: string; - /** - * The item label. - */ - label: string; - /** - * The item layout. - */ - layout?: ItemLayout; - /** - * Set additional classnames to the list item term. - */ - termClassName?: string; - /** - * The item value. - */ - value: ReactNode | ReactNode[]; - /** - * If true, use a slash to delimitate multiple values. - */ - withSeparator?: boolean; -}; - -/** - * DescriptionListItem component - * - * Render a couple of dt/dd wrapped in a div. - */ -const DescriptionListItem: FC = ({ - className = '', - descriptionClassName = '', - label, - termClassName = '', - value, - layout = 'stacked', - withSeparator = false, -}) => { - const id = useId(); - const layoutStyles = styles[`wrapper--${layout}`]; - const separatorStyles = withSeparator ? styles['wrapper--has-separator'] : ''; - const itemValues = Array.isArray(value) ? value : [value]; - - return ( -
-
{label}
- {itemValues.map((currentValue, index) => ( -
- {currentValue} -
- ))} -
- ); -}; - -export default DescriptionListItem; diff --git a/src/components/atoms/lists/description-list.stories.tsx b/src/components/atoms/lists/description-list.stories.tsx index 347fd78..0194817 100644 --- a/src/components/atoms/lists/description-list.stories.tsx +++ b/src/components/atoms/lists/description-list.stories.tsx @@ -1,5 +1,5 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import DescriptionList, { DescriptionListItem } from './description-list'; +import { DescriptionList, DescriptionListItem } from './description-list'; /** * DescriptionList - Storybook Meta diff --git a/src/components/atoms/lists/description-list.test.tsx b/src/components/atoms/lists/description-list.test.tsx index 11b4965..6190c5c 100644 --- a/src/components/atoms/lists/description-list.test.tsx +++ b/src/components/atoms/lists/description-list.test.tsx @@ -1,5 +1,5 @@ import { render } from '../../../../tests/utils'; -import DescriptionList, { DescriptionListItem } from './description-list'; +import { DescriptionList, DescriptionListItem } from './description-list'; const items: DescriptionListItem[] = [ { id: 'term-1', label: 'Term 1:', value: ['Value for term 1'] }, diff --git a/src/components/atoms/lists/description-list.tsx b/src/components/atoms/lists/description-list.tsx index a8e2d53..d97e505 100644 --- a/src/components/atoms/lists/description-list.tsx +++ b/src/components/atoms/lists/description-list.tsx @@ -1,7 +1,8 @@ -import { FC } from 'react'; -import DescriptionListItem, { - type DescriptionListItemProps, -} from './description-list-item'; +import { FC, HTMLAttributes } from 'react'; +import { + DescriptionListGroup, + type DescriptionListGroupProps, +} from './description-list-group'; import styles from './description-list.module.scss'; export type DescriptionListItem = { @@ -12,22 +13,21 @@ export type DescriptionListItem = { /** * The list item layout. */ - layout?: DescriptionListItemProps['layout']; + layout?: DescriptionListGroupProps['layout']; /** * A list label. */ - label: DescriptionListItemProps['label']; + label: DescriptionListGroupProps['label']; /** * An array of values for the list item. */ - value: DescriptionListItemProps['value']; + value: DescriptionListGroupProps['value']; }; -export type DescriptionListProps = { - /** - * Set additional classnames to the list wrapper. - */ - className?: string; +export type DescriptionListProps = Omit< + HTMLAttributes, + 'children' +> & { /** * Set additional classnames to the `dt`/`dd` couple wrapper. */ @@ -51,7 +51,7 @@ export type DescriptionListProps = { /** * If true, use a slash to delimitate multiple values. */ - withSeparator?: DescriptionListItemProps['withSeparator']; + withSeparator?: DescriptionListGroupProps['withSeparator']; }; /** @@ -59,7 +59,7 @@ export type DescriptionListProps = { * * Render a description list. */ -const DescriptionList: FC = ({ +export const DescriptionList: FC = ({ className = '', groupClassName = '', items, @@ -67,19 +67,21 @@ const DescriptionList: FC = ({ layout = 'column', valueClassName = '', withSeparator, + ...props }) => { const layoutModifier = `list--${layout}`; + const listClass = `${styles.list} ${styles[layoutModifier]} ${className}`; /** * Retrieve the description list items. * - * @param {DescriptionListItem[]} listItems - An array of items. + * @param {DescriptionListGroup[]} listItems - An array of items. * @returns {JSX.Element[]} The description list items. */ const getItems = (listItems: DescriptionListItem[]): JSX.Element[] => { return listItems.map(({ id, layout: itemLayout, label, value }) => { return ( - = ({ }; return ( -
+
{getItems(items)}
); }; - -export default DescriptionList; diff --git a/src/components/atoms/lists/index.ts b/src/components/atoms/lists/index.ts new file mode 100644 index 0000000..d16fb34 --- /dev/null +++ b/src/components/atoms/lists/index.ts @@ -0,0 +1,3 @@ +export * from './description-list'; +export * from './description-list-group'; +export * from './list'; diff --git a/src/components/atoms/lists/list.stories.tsx b/src/components/atoms/lists/list.stories.tsx index eac3cd3..c4f3c3b 100644 --- a/src/components/atoms/lists/list.stories.tsx +++ b/src/components/atoms/lists/list.stories.tsx @@ -1,12 +1,12 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import ListComponent, { type ListItem } from './list'; +import { List, type ListItem } from './list'; /** * List - Storybook Meta */ export default { title: 'Atoms/Typography/Lists', - component: ListComponent, + component: List, args: { kind: 'unordered', }, @@ -64,11 +64,9 @@ export default { }, }, }, -} as ComponentMeta; +} as ComponentMeta; -const Template: ComponentStory = (args) => ( - -); +const Template: ComponentStory = (args) => ; const items: ListItem[] = [ { id: 'item-1', value: 'Item 1' }, diff --git a/src/components/atoms/lists/list.test.tsx b/src/components/atoms/lists/list.test.tsx index fbe56a4..18ffed2 100644 --- a/src/components/atoms/lists/list.test.tsx +++ b/src/components/atoms/lists/list.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '../../../../tests/utils'; -import List, { type ListItem } from './list'; +import { List, type ListItem } from './list'; const items: ListItem[] = [ { id: 'item-1', value: 'Item 1' }, diff --git a/src/components/atoms/lists/list.tsx b/src/components/atoms/lists/list.tsx index aa0a241..8fc9672 100644 --- a/src/components/atoms/lists/list.tsx +++ b/src/components/atoms/lists/list.tsx @@ -40,7 +40,7 @@ export type ListProps = { * * Render either an ordered or an unordered list. */ -const List: FC = ({ +export const List: FC = ({ className = '', items, itemsClassName = '', @@ -48,6 +48,7 @@ const List: FC = ({ }) => { const ListTag = kind === 'ordered' ? 'ol' : 'ul'; const kindClass = `list--${kind}`; + const listClass = `${styles.list} ${styles[kindClass]} ${className}`; /** * Retrieve the list items. @@ -69,11 +70,5 @@ const List: FC = ({ )); }; - return ( - - {getItems(items)} - - ); + return {getItems(items)}; }; - -export default List; -- cgit v1.2.3