aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/organisms/layout/summary.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-10 19:37:51 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commitc87c615b5866b8a8f361eeb0764bfdea85740e90 (patch)
treec27bda05fd96bbe3154472e170ba1abd5f9ea499 /src/components/organisms/layout/summary.tsx
parent15522ec9146f6f1956620355c44dea2a6a75b67c (diff)
refactor(components): replace Meta component with MetaList
It removes items complexity by allowing consumers to use any label/value association. Translations should also be defined by the consumer. Each item can now be configured separately (borders, layout...).
Diffstat (limited to 'src/components/organisms/layout/summary.tsx')
-rw-r--r--src/components/organisms/layout/summary.tsx175
1 files changed, 133 insertions, 42 deletions
diff --git a/src/components/organisms/layout/summary.tsx b/src/components/organisms/layout/summary.tsx
index fa3dfe5..f5c16cd 100644
--- a/src/components/organisms/layout/summary.tsx
+++ b/src/components/organisms/layout/summary.tsx
@@ -2,6 +2,7 @@ import NextImage, { type ImageProps as NextImageProps } from 'next/image';
import type { FC, ReactNode } from 'react';
import { useIntl } from 'react-intl';
import type { Article, Meta as MetaType } from '../../../types';
+import { getFormattedDate } from '../../../utils/helpers';
import { useReadingTime } from '../../../utils/hooks';
import {
ButtonLink,
@@ -11,7 +12,7 @@ import {
Link,
Figure,
} from '../../atoms';
-import { Meta, type MetaData } from '../../molecules';
+import { MetaList, type MetaItemData } from '../../molecules';
import styles from './summary.module.scss';
export type Cover = Pick<NextImageProps, 'alt' | 'src' | 'width' | 'height'>;
@@ -69,42 +70,134 @@ export const Summary: FC<SummaryProps> = ({
),
}
);
- const { author, commentsCount, cover, dates, thematics, topics, wordsCount } =
- meta;
- const readingTime = useReadingTime(wordsCount, true);
+ const readingTime = useReadingTime(meta.wordsCount, true);
- const getMeta = (): MetaData => {
- return {
- author: author?.name,
- publication: { date: dates.publication },
- update:
- dates.update && dates.publication !== dates.update
- ? { date: dates.update }
- : undefined,
- readingTime,
- thematics: thematics?.map((thematic) => (
- <Link key={thematic.id} href={thematic.url}>
- {thematic.name}
- </Link>
- )),
- topics: topics?.map((topic) => (
- <Link key={topic.id} href={topic.url}>
- {topic.name}
- </Link>
- )),
- comments: {
- about: title,
- count: commentsCount ?? 0,
- target: `${url}#comments`,
+ /**
+ * Retrieve a formatted date (and time).
+ *
+ * @param {string} date - A date string.
+ * @returns {JSX.Element} The formatted date wrapped in a time element.
+ */
+ const getDate = (date: string): JSX.Element => {
+ const isoDate = new Date(`${date}`).toISOString();
+
+ return <time dateTime={isoDate}>{getFormattedDate(date)}</time>;
+ };
+
+ const getMetaItems = (): MetaItemData[] => {
+ const summaryMeta: MetaItemData[] = [
+ {
+ id: 'publication-date',
+ label: intl.formatMessage({
+ defaultMessage: 'Published on:',
+ description: 'Summary: publication date label',
+ id: 'TvQ2Ee',
+ }),
+ value: getDate(meta.dates.publication),
},
- };
+ ];
+
+ if (meta.dates.update && meta.dates.update !== meta.dates.publication)
+ summaryMeta.push({
+ id: 'update-date',
+ label: intl.formatMessage({
+ defaultMessage: 'Updated on:',
+ description: 'Summary: update date label',
+ id: 'f0Z/Po',
+ }),
+ value: getDate(meta.dates.update),
+ });
+
+ summaryMeta.push({
+ id: 'reading-time',
+ label: intl.formatMessage({
+ defaultMessage: 'Reading time:',
+ description: 'Summary: reading time label',
+ id: 'tyzdql',
+ }),
+ value: readingTime,
+ });
+
+ if (meta.author)
+ summaryMeta.push({
+ id: 'author',
+ label: intl.formatMessage({
+ defaultMessage: 'Written by:',
+ description: 'Summary: author label',
+ id: 'r/6HOI',
+ }),
+ value: meta.author.name,
+ });
+
+ if (meta.thematics)
+ summaryMeta.push({
+ id: 'thematics',
+ label: intl.formatMessage({
+ defaultMessage: 'Thematics:',
+ description: 'Summary: thematics label',
+ id: 'bk0WOp',
+ }),
+ value: meta.thematics.map((thematic) => {
+ return {
+ id: `thematic-${thematic.id}`,
+ value: <Link href={thematic.url}>{thematic.name}</Link>,
+ };
+ }),
+ });
+
+ if (meta.topics)
+ summaryMeta.push({
+ id: 'topics',
+ label: intl.formatMessage({
+ defaultMessage: 'Topics:',
+ description: 'Summary: topics label',
+ id: 'yIZ+AC',
+ }),
+ value: meta.topics.map((topic) => {
+ return {
+ id: `topic-${topic.id}`,
+ value: <Link href={topic.url}>{topic.name}</Link>,
+ };
+ }),
+ });
+
+ if (meta.commentsCount !== undefined) {
+ const commentsCount = intl.formatMessage(
+ {
+ defaultMessage:
+ '{commentsCount, plural, =0 {No comments} one {# comment} other {# comments}}<a11y> about {title}</a11y>',
+ description: 'Summary: comments count',
+ id: 'ye/vlA',
+ },
+ {
+ a11y: (chunks: ReactNode) => (
+ <span className="screen-reader-text">{chunks}</span>
+ ),
+ commentsCount: meta.commentsCount,
+ title,
+ }
+ );
+ summaryMeta.push({
+ id: 'comments-count',
+ label: intl.formatMessage({
+ defaultMessage: 'Comments:',
+ description: 'Summary: comments label',
+ id: 'bfPp0g',
+ }),
+ value: (
+ <Link href={`${url}#comments`}>{commentsCount as JSX.Element}</Link>
+ ),
+ });
+ }
+
+ return summaryMeta;
};
return (
<article className={styles.wrapper}>
- {cover ? (
+ {meta.cover ? (
<Figure>
- <NextImage {...cover} className={styles.cover} />
+ <NextImage {...meta.cover} className={styles.cover} />
</Figure>
) : null}
<header className={styles.header}>
@@ -121,21 +214,19 @@ export const Summary: FC<SummaryProps> = ({
dangerouslySetInnerHTML={{ __html: intro }}
/>
<ButtonLink className={styles['read-more']} to={url}>
- <>
- {readMore}
- <Icon
- aria-hidden={true}
- className={styles.icon}
- // eslint-disable-next-line react/jsx-no-literals -- Direction allowed
- orientation="right"
- // eslint-disable-next-line react/jsx-no-literals -- Shape allowed
- shape="arrow"
- />
- </>
+ {readMore}
+ <Icon
+ aria-hidden={true}
+ className={styles.icon}
+ // eslint-disable-next-line react/jsx-no-literals -- Direction allowed
+ orientation="right"
+ // eslint-disable-next-line react/jsx-no-literals -- Shape allowed
+ shape="arrow"
+ />
</ButtonLink>
</div>
<footer className={styles.footer}>
- <Meta className={styles.meta} data={getMeta()} spacing="xs" />
+ <MetaList className={styles.meta} items={getMetaItems()} />
</footer>
</article>
);