From 36890cfafeba6e30782df1260d7f9e678c7da4bf Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Mon, 2 Oct 2023 17:01:57 +0200 Subject: refactor(components): rewrite DescriptionList component * add a `spacing` prop * replace `layout` prop with `isInline` prop * remove `items` prop (and classNames props) in favor of new components: Description, Group, Term * remove `withSeparator` prop (CSS content is announced by screen readers and Firefox/Safari have no support for alternative text so the consumer should add itself an element with `aria-hidden` if it need a separator) Be aware, Meta component and its consumers can be visually broken, they should be refactored before using them in production. --- src/components/molecules/layout/card.tsx | 10 +-- src/components/molecules/layout/meta.module.scss | 17 +++- src/components/molecules/layout/meta.stories.tsx | 34 ++------ src/components/molecules/layout/meta.test.tsx | 8 +- src/components/molecules/layout/meta.tsx | 103 ++++++++++++----------- src/components/molecules/layout/page-footer.tsx | 4 +- src/components/molecules/layout/page-header.tsx | 9 +- 7 files changed, 81 insertions(+), 104 deletions(-) (limited to 'src/components/molecules') diff --git a/src/components/molecules/layout/card.tsx b/src/components/molecules/layout/card.tsx index 722e5a5..c9e7a90 100644 --- a/src/components/molecules/layout/card.tsx +++ b/src/components/molecules/layout/card.tsx @@ -72,15 +72,7 @@ export const Card: FC = ({ {tagline ?
{tagline}
: null} {meta ? (
- +
) : null} diff --git a/src/components/molecules/layout/meta.module.scss b/src/components/molecules/layout/meta.module.scss index f572b65..26faac3 100644 --- a/src/components/molecules/layout/meta.module.scss +++ b/src/components/molecules/layout/meta.module.scss @@ -1,3 +1,16 @@ -.value { - word-break: break-all; +.list { + .description:not(:first-of-type) { + &::before { + display: inline; + float: left; + content: "/"; + margin-right: var(--itemSpacing); + } + } + + &--stack { + .term { + flex: 0 0 100%; + } + } } diff --git a/src/components/molecules/layout/meta.stories.tsx b/src/components/molecules/layout/meta.stories.tsx index 50ed252..6faa265 100644 --- a/src/components/molecules/layout/meta.stories.tsx +++ b/src/components/molecules/layout/meta.stories.tsx @@ -1,7 +1,5 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import descriptionListItemStories from '../../atoms/lists/description-list-group.stories'; -import descriptionListStories from '../../atoms/lists/description-list.stories'; -import { Meta as MetaComponent, MetaData } from './meta'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Meta as MetaComponent, type MetaData } from './meta'; /** * Meta - Storybook Meta @@ -9,12 +7,8 @@ import { Meta as MetaComponent, MetaData } from './meta'; export default { title: 'Molecules/Layout', component: MetaComponent, - args: { - itemsLayout: 'inline-values', - withSeparator: false, - }, + args: {}, argTypes: { - className: descriptionListStories.argTypes?.className, data: { description: 'The page metadata.', type: { @@ -23,24 +17,6 @@ export default { value: {}, }, }, - groupClassName: descriptionListStories.argTypes?.groupClassName, - itemsLayout: { - ...descriptionListItemStories.argTypes?.layout, - table: { - ...descriptionListItemStories.argTypes?.layout?.table, - defaultValue: { summary: 'inline-values' }, - }, - }, - labelClassName: descriptionListStories.argTypes?.labelClassName, - layout: descriptionListStories.argTypes?.layout, - valueClassName: descriptionListStories.argTypes?.valueClassName, - withSeparator: { - ...descriptionListStories.argTypes?.withSeparator, - table: { - ...descriptionListStories.argTypes?.withSeparator?.table, - defaultValue: { summary: true }, - }, - }, }, } as ComponentMeta; @@ -51,10 +27,10 @@ const Template: ComponentStory = (args) => ( const data: MetaData = { publication: { date: '2022-04-09', time: '01:04:00' }, thematics: [ - + Category 1 , - + Category 2 , ], diff --git a/src/components/molecules/layout/meta.test.tsx b/src/components/molecules/layout/meta.test.tsx index f19c408..0635fc3 100644 --- a/src/components/molecules/layout/meta.test.tsx +++ b/src/components/molecules/layout/meta.test.tsx @@ -1,15 +1,15 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; +import { render, screen as rtlScreen } from '../../../../tests/utils'; import { getFormattedDate } from '../../../utils/helpers'; import { Meta } from './meta'; const data = { publication: { date: '2022-04-09' }, thematics: [ - + Category 1 , - + Category 2 , ], @@ -19,7 +19,7 @@ describe('Meta', () => { it('format a date string', () => { render(); expect( - screen.getByText(getFormattedDate(data.publication.date)) + rtlScreen.getByText(getFormattedDate(data.publication.date)) ).toBeInTheDocument(); }); }); diff --git a/src/components/molecules/layout/meta.tsx b/src/components/molecules/layout/meta.tsx index 53128a7..094c420 100644 --- a/src/components/molecules/layout/meta.tsx +++ b/src/components/molecules/layout/meta.tsx @@ -1,16 +1,19 @@ -import { FC, ReactNode } from 'react'; +import type { FC, ReactNode } from 'react'; import { useIntl } from 'react-intl'; import { getFormattedDate, getFormattedTime } from '../../../utils/helpers'; import { DescriptionList, type DescriptionListProps, - type DescriptionListItem, Link, + Group, + Term, + Description, } from '../../atoms'; +import styles from './meta.module.scss'; export type CustomMeta = { label: string; - value: ReactNode | ReactNode[]; + value: ReactNode; }; export type MetaComments = { @@ -106,24 +109,16 @@ export type MetaData = { website?: string; }; -export type MetaKey = keyof MetaData; +const isCustomMeta = ( + key: keyof MetaData, + _value: unknown +): _value is MetaData['custom'] => key === 'custom'; -export type MetaProps = Omit< - DescriptionListProps, - 'items' | 'withSeparator' -> & { +export type MetaProps = Omit & { /** * The meta data. */ data: MetaData; - /** - * The items layout. - */ - itemsLayout?: DescriptionListItem['layout']; - /** - * If true, use a slash to delimitate multiple values. Default: true. - */ - withSeparator?: DescriptionListProps['withSeparator']; }; /** @@ -132,11 +127,13 @@ export type MetaProps = Omit< * Renders the given metadata. */ export const Meta: FC = ({ + className = '', data, - itemsLayout = 'inline-values', - withSeparator = true, + isInline = false, ...props }) => { + const layoutClass = styles[isInline ? 'list--inline' : 'list--stack']; + const listClass = `${styles.list} ${layoutClass} ${className}`; const intl = useIntl(); /** @@ -316,7 +313,7 @@ export const Meta: FC = ({ * @param {ValueOf} value - The meta value. * @returns {string|ReactNode|ReactNode[]} - The formatted value. */ - const getValue = ( + const getValue = ( key: T, value: MetaData[T] ): string | ReactNode | ReactNode[] => { @@ -338,12 +335,11 @@ export const Meta: FC = ({ { postsCount: value as number } ); case 'website': - const url = value as string; - return ( - - {url} + return typeof value === 'string' ? ( + + {value} - ); + ) : null; default: return value as string | ReactNode | ReactNode[]; } @@ -355,36 +351,45 @@ export const Meta: FC = ({ * @param {MetaData} items - The meta. * @returns {DescriptionListItem[]} The formatted description list items. */ - const getItems = (items: MetaData): DescriptionListItem[] => { - const listItems: DescriptionListItem[] = Object.entries(items) - .map(([key, value]) => { - if (!key || !value) return; - - const metaKey = key as MetaKey; + const getItems = (items: MetaData) => { + const entries = Object.entries(items) as [ + keyof MetaData, + MetaData[keyof MetaData], + ][]; + const listItems = entries.map(([key, meta]) => { + if (!meta) return null; - return { - id: metaKey, - label: - metaKey === 'custom' - ? (value as CustomMeta).label - : getLabel(metaKey), - layout: itemsLayout, - value: - metaKey === 'custom' && (value as CustomMeta) - ? (value as CustomMeta).value - : getValue(metaKey, value), - } as DescriptionListItem; - }) - .filter((item): item is DescriptionListItem => !!item); + return ( + + + {isCustomMeta(key, meta) ? meta.label : getLabel(key)} + + {Array.isArray(meta) ? ( + meta.map((singleMeta, index) => ( + /* eslint-disable-next-line react/no-array-index-key -- Unsafe, + * but also temporary. This component should be removed or + * refactored. */ + + {isCustomMeta(key, singleMeta) + ? singleMeta + : getValue(key, singleMeta)} + + )) + ) : ( + + {isCustomMeta(key, meta) ? meta.value : getValue(key, meta)} + + )} + + ); + }); return listItems; }; return ( - + + {getItems(data)} + ); }; diff --git a/src/components/molecules/layout/page-footer.tsx b/src/components/molecules/layout/page-footer.tsx index 5f3b176..375cbc4 100644 --- a/src/components/molecules/layout/page-footer.tsx +++ b/src/components/molecules/layout/page-footer.tsx @@ -15,7 +15,5 @@ export type PageFooterProps = Omit & { * Render a footer to display page meta. */ export const PageFooter: FC = ({ meta, ...props }) => ( -
- {meta ? : null} -
+
{meta ? : null}
); diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx index 92650c5..b727cc1 100644 --- a/src/components/molecules/layout/page-header.tsx +++ b/src/components/molecules/layout/page-header.tsx @@ -56,14 +56,7 @@ export const PageHeader: FC = ({ {title} {meta ? ( - + ) : null} {intro ? getIntro() : null} -- cgit v1.2.3