aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/layout/meta.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-02 17:01:57 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commit36890cfafeba6e30782df1260d7f9e678c7da4bf (patch)
tree1abe20cf36d60e048b75828dd5516529e504ddd8 /src/components/molecules/layout/meta.tsx
parent4f768afe543bbf9e1857c41d03804f8e37ab3512 (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.tsx103
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>
);
};