diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-02 17:01:57 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 |
| commit | 36890cfafeba6e30782df1260d7f9e678c7da4bf (patch) | |
| tree | 1abe20cf36d60e048b75828dd5516529e504ddd8 /src/components/molecules/layout/meta.tsx | |
| parent | 4f768afe543bbf9e1857c41d03804f8e37ab3512 (diff) | |
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.
Diffstat (limited to 'src/components/molecules/layout/meta.tsx')
| -rw-r--r-- | src/components/molecules/layout/meta.tsx | 103 |
1 files changed, 54 insertions, 49 deletions
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<DescriptionListProps, 'children'> & { /** * 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<MetaProps> = ({ + 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<MetaProps> = ({ * @param {ValueOf<MetaData>} value - The meta value. * @returns {string|ReactNode|ReactNode[]} - The formatted value. */ - const getValue = <T extends MetaKey>( + const getValue = <T extends keyof MetaData>( key: T, value: MetaData[T] ): string | ReactNode | ReactNode[] => { @@ -338,12 +335,11 @@ export const Meta: FC<MetaProps> = ({ { postsCount: value as number } ); case 'website': - const url = value as string; - return ( - <Link href={url} external={true}> - {url} + return typeof value === 'string' ? ( + <Link href={value} external={true}> + {value} </Link> - ); + ) : null; default: return value as string | ReactNode | ReactNode[]; } @@ -355,36 +351,45 @@ export const Meta: FC<MetaProps> = ({ * @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 ( + <Group isInline key={key} spacing="2xs"> + <Term className={styles.term}> + {isCustomMeta(key, meta) ? meta.label : getLabel(key)} + </Term> + {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. */ + <Description className={styles.description} key={index}> + {isCustomMeta(key, singleMeta) + ? singleMeta + : getValue(key, singleMeta)} + </Description> + )) + ) : ( + <Description className={styles.description}> + {isCustomMeta(key, meta) ? meta.value : getValue(key, meta)} + </Description> + )} + </Group> + ); + }); return listItems; }; return ( - <DescriptionList - {...props} - items={getItems(data)} - withSeparator={withSeparator} - /> + <DescriptionList {...props} className={listClass} isInline={isInline}> + {getItems(data)} + </DescriptionList> ); }; |
