diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-20 11:02:20 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-20 19:20:21 +0100 |
| commit | d5ade2359539648845a5854ed353b29367961d74 (patch) | |
| tree | 45a49d90090408887135a971a7fd79c45d9dcd94 /src | |
| parent | 6ab9635a22d69186c8a24181ad5df7736e288577 (diff) | |
refactor(components): extract MetaItem from MetaList
* replace `items` prop on MetaList with `children` prop: it was too
restrictive and the global options was not really useful. It is better
too give control to the consumers.
Diffstat (limited to 'src')
29 files changed, 787 insertions, 974 deletions
diff --git a/src/components/molecules/card/card.stories.tsx b/src/components/molecules/card/card.stories.tsx index bc9cbd4..7cc2ad4 100644 --- a/src/components/molecules/card/card.stories.tsx +++ b/src/components/molecules/card/card.stories.tsx @@ -1,6 +1,7 @@ import type { ComponentMeta, Story } from '@storybook/react'; import NextImage from 'next/image'; import { Button, ButtonLink, Link, Time } from '../../atoms'; +import { MetaItem } from '../meta-list'; import { Card, type CardProps } from './card'; import { CardActions } from './card-actions'; import { CardBody } from './card-body'; @@ -71,17 +72,13 @@ export const HeaderMeta = Template.bind({}); HeaderMeta.args = { children: ( <CardHeader> - <CardMeta - isInline - items={[ - { id: 'author', label: 'Written by:', value: 'The author' }, - { - id: 'publication-date', - label: 'Published on:', - value: <Time date={new Date().toISOString()} />, - }, - ]} - /> + <CardMeta isInline> + <MetaItem label="Written by:" value="The author" /> + <MetaItem + label="Published on:" + value={<Time date={new Date().toISOString()} />} + /> + </CardMeta> </CardHeader> ), }; @@ -107,27 +104,23 @@ export const FooterMeta = Template.bind({}); FooterMeta.args = { children: <CardFooter />, meta: ( - <CardMeta - items={[ - { - id: 'categories', - label: 'Categories:', - value: [ - { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, - { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, - ], - }, - { - id: 'tags', - label: 'Tags:', - value: [ - { id: 'tag-1', value: 'Tag 1' }, - { id: 'tag-2', value: 'Tag 2' }, - { id: 'tag-3', value: 'Tag 3' }, - ], - }, - ]} - /> + <CardMeta> + <MetaItem + label="Categories:" + value={[ + { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, + { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, + ]} + /> + <MetaItem + label="Tags:" + value={[ + { id: 'tag-1', value: 'Tag 1' }, + { id: 'tag-2', value: 'Tag 2' }, + { id: 'tag-3', value: 'Tag 3' }, + ]} + /> + </CardMeta> ), }; @@ -155,17 +148,13 @@ CompositionTitleMeta.args = { children: ( <CardHeader> <CardTitle>The card title</CardTitle> - <CardMeta - isInline - items={[ - { id: 'author', label: 'Written by:', value: 'The author' }, - { - id: 'publication-date', - label: 'Published on:', - value: <Time date={new Date().toISOString()} />, - }, - ]} - /> + <CardMeta isInline> + <MetaItem label="Written by:" value="The author" /> + <MetaItem + label="Published on:" + value={<Time date={new Date().toISOString()} />} + /> + </CardMeta> </CardHeader> ), }; @@ -175,17 +164,13 @@ CompositionCoverTitleMeta.args = { children: ( <CardHeader> <CardTitle>The card title</CardTitle> - <CardMeta - isInline - items={[ - { id: 'author', label: 'Written by:', value: 'The author' }, - { - id: 'publication-date', - label: 'Published on:', - value: <Time date={new Date().toISOString()} />, - }, - ]} - /> + <CardMeta isInline> + <MetaItem label="Written by:" value="The author" /> + <MetaItem + label="Published on:" + value={<Time date={new Date().toISOString()} />} + /> + </CardMeta> </CardHeader> ), cover: ( @@ -250,17 +235,13 @@ CompositionTitleMetaBody.args = { <> <CardHeader> <CardTitle>The card title</CardTitle> - <CardMeta - isInline - items={[ - { id: 'author', label: 'Written by:', value: 'The author' }, - { - id: 'publication-date', - label: 'Published on:', - value: <Time date={new Date().toISOString()} />, - }, - ]} - /> + <CardMeta isInline> + <MetaItem label="Written by:" value="The author" /> + <MetaItem + label="Published on:" + value={<Time date={new Date().toISOString()} />} + /> + </CardMeta> </CardHeader> <CardBody> Nihil magnam tempora voluptatem. Reiciendis ut cum vel. Odit et @@ -278,17 +259,13 @@ CompositionCoverTitleMetaBody.args = { <> <CardHeader> <CardTitle>The card title</CardTitle> - <CardMeta - isInline - items={[ - { id: 'author', label: 'Written by:', value: 'The author' }, - { - id: 'publication-date', - label: 'Published on:', - value: <Time date={new Date().toISOString()} />, - }, - ]} - /> + <CardMeta isInline> + <MetaItem label="Written by:" value="The author" /> + <MetaItem + label="Published on:" + value={<Time date={new Date().toISOString()} />} + /> + </CardMeta> </CardHeader> <CardBody> Nihil magnam tempora voluptatem. Reiciendis ut cum vel. Odit et @@ -399,27 +376,23 @@ CompositionTitleBodyActionsMeta.args = { </> ), meta: ( - <CardMeta - items={[ - { - id: 'categories', - label: 'Categories:', - value: [ - { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, - { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, - ], - }, - { - id: 'tags', - label: 'Tags:', - value: [ - { id: 'tag-1', value: 'Tag 1' }, - { id: 'tag-2', value: 'Tag 2' }, - { id: 'tag-3', value: 'Tag 3' }, - ], - }, - ]} - /> + <CardMeta> + <MetaItem + label="Categories:" + value={[ + { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, + { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, + ]} + /> + <MetaItem + label="Tags:" + value={[ + { id: 'tag-1', value: 'Tag 1' }, + { id: 'tag-2', value: 'Tag 2' }, + { id: 'tag-3', value: 'Tag 3' }, + ]} + /> + </CardMeta> ), }; @@ -455,27 +428,23 @@ CompositionCoverTitleBodyActionsMeta.args = { </CardCover> ), meta: ( - <CardMeta - items={[ - { - id: 'categories', - label: 'Categories:', - value: [ - { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, - { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, - ], - }, - { - id: 'tags', - label: 'Tags:', - value: [ - { id: 'tag-1', value: 'Tag 1' }, - { id: 'tag-2', value: 'Tag 2' }, - { id: 'tag-3', value: 'Tag 3' }, - ], - }, - ]} - /> + <CardMeta> + <MetaItem + label="Categories:" + value={[ + { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, + { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, + ]} + /> + <MetaItem + label="Tags:" + value={[ + { id: 'tag-1', value: 'Tag 1' }, + { id: 'tag-2', value: 'Tag 2' }, + { id: 'tag-3', value: 'Tag 3' }, + ]} + /> + </CardMeta> ), }; @@ -485,17 +454,13 @@ CompositionAllContents.args = { <> <CardHeader> <CardTitle>The card title</CardTitle> - <CardMeta - isInline - items={[ - { id: 'author', label: 'Written by:', value: 'The author' }, - { - id: 'publication-date', - label: 'Published on:', - value: <Time date={new Date().toISOString()} />, - }, - ]} - /> + <CardMeta isInline> + <MetaItem label="Written by:" value="The author" /> + <MetaItem + label="Published on:" + value={<Time date={new Date().toISOString()} />} + /> + </CardMeta> </CardHeader> <CardBody> Nihil magnam tempora voluptatem. Reiciendis ut cum vel. Odit et @@ -522,26 +487,22 @@ CompositionAllContents.args = { </CardCover> ), meta: ( - <CardMeta - items={[ - { - id: 'categories', - label: 'Categories:', - value: [ - { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, - { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, - ], - }, - { - id: 'tags', - label: 'Tags:', - value: [ - { id: 'tag-1', value: 'Tag 1' }, - { id: 'tag-2', value: 'Tag 2' }, - { id: 'tag-3', value: 'Tag 3' }, - ], - }, - ]} - /> + <CardMeta> + <MetaItem + label="Categories:" + value={[ + { id: 'cat-1', value: <Link href="#cat1">Category 1</Link> }, + { id: 'cat-2', value: <Link href="#cat2">Category 2</Link> }, + ]} + /> + <MetaItem + label="Tags:" + value={[ + { id: 'tag-1', value: 'Tag 1' }, + { id: 'tag-2', value: 'Tag 2' }, + { id: 'tag-3', value: 'Tag 3' }, + ]} + /> + </CardMeta> ), }; diff --git a/src/components/molecules/card/card.test.tsx b/src/components/molecules/card/card.test.tsx index 40a5830..769185b 100644 --- a/src/components/molecules/card/card.test.tsx +++ b/src/components/molecules/card/card.test.tsx @@ -1,6 +1,7 @@ import { describe, expect, it } from '@jest/globals'; import { render, screen as rtlScreen } from '@testing-library/react'; import NextImage from 'next/image'; +import { MetaItem } from '../meta-list'; import { Card } from './card'; import { CardFooter } from './card-footer'; import { CardHeader } from './card-header'; @@ -66,7 +67,11 @@ describe('Card', () => { render( <Card - meta={<CardMeta items={[{ id: 'any', label: term, value: desc }]} />} + meta={ + <CardMeta> + <MetaItem label={term} value={desc} /> + </CardMeta> + } > <CardFooter /> </Card> @@ -83,7 +88,11 @@ describe('Card', () => { render( <Card - meta={<CardMeta items={[{ id: 'any', label: term, value: desc }]} />} + meta={ + <CardMeta> + <MetaItem label={term} value={desc} /> + </CardMeta> + } > {body} </Card> diff --git a/src/components/molecules/layout/page-footer.stories.tsx b/src/components/molecules/layout/page-footer.stories.tsx index 48c8c17..994e888 100644 --- a/src/components/molecules/layout/page-footer.stories.tsx +++ b/src/components/molecules/layout/page-footer.stories.tsx @@ -1,4 +1,6 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Link } from '../../atoms'; +import { MetaItem, MetaList } from '../meta-list'; import { PageFooter as PageFooterComponent } from './page-footer'; /** @@ -39,22 +41,17 @@ const Template: ComponentStory<typeof PageFooterComponent> = (args) => ( <PageFooterComponent {...args} /> ); -const meta = [ - { - id: 'more-about', - label: 'More posts about:', - value: ( - <a key="topic-1" href="#topic1"> - Topic name - </a> - ), - }, -]; - /** * Page Footer Stories - With meta */ export const PageFooter = Template.bind({}); PageFooter.args = { - meta, + children: ( + <MetaList> + <MetaItem + label="More posts about:" + value={<Link href="#topic1">Topic name</Link>} + /> + </MetaList> + ), }; diff --git a/src/components/molecules/layout/page-footer.test.tsx b/src/components/molecules/layout/page-footer.test.tsx index 7f0bcd5..dbd20f5 100644 --- a/src/components/molecules/layout/page-footer.test.tsx +++ b/src/components/molecules/layout/page-footer.test.tsx @@ -1,10 +1,10 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; +import { render, screen as rtlScreen } from '../../../../tests/utils'; import { PageFooter } from './page-footer'; describe('PageFooter', () => { it('renders a footer element', () => { render(<PageFooter />); - expect(screen.getByRole('contentinfo')).toBeInTheDocument(); + expect(rtlScreen.getByRole('contentinfo')).toBeInTheDocument(); }); }); diff --git a/src/components/molecules/layout/page-footer.tsx b/src/components/molecules/layout/page-footer.tsx index a93fced..e0ce2ef 100644 --- a/src/components/molecules/layout/page-footer.tsx +++ b/src/components/molecules/layout/page-footer.tsx @@ -1,12 +1,11 @@ -import type { FC } from 'react'; +import type { FC, ReactNode } from 'react'; import { Footer, type FooterProps } from '../../atoms'; -import { MetaList, type MetaItemData } from '../meta-list'; export type PageFooterProps = Omit<FooterProps, 'children'> & { /** - * The footer metadata. + * The footer contents. */ - meta?: MetaItemData[]; + children?: ReactNode; }; /** @@ -14,8 +13,6 @@ export type PageFooterProps = Omit<FooterProps, 'children'> & { * * Render a footer to display page meta. */ -export const PageFooter: FC<PageFooterProps> = ({ meta, ...props }) => ( - <Footer {...props}> - {meta ? <MetaList hasInlinedValues items={meta} /> : null} - </Footer> +export const PageFooter: FC<PageFooterProps> = ({ children, ...props }) => ( + <Footer {...props}>{children}</Footer> ); diff --git a/src/components/molecules/layout/page-header.stories.tsx b/src/components/molecules/layout/page-header.stories.tsx index 54d5fe8..97eae5a 100644 --- a/src/components/molecules/layout/page-header.stories.tsx +++ b/src/components/molecules/layout/page-header.stories.tsx @@ -1,4 +1,5 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { MetaItem, MetaList } from '../meta-list'; import { PageHeader } from './page-header'; /** @@ -62,32 +63,6 @@ const Template: ComponentStory<typeof PageHeader> = (args) => ( <PageHeader {...args} /> ); -const meta = [ - { id: 'publication-date', label: 'Published on:', value: '2022-04-09' }, - { - id: 'thematics', - label: 'Thematics:', - value: [ - { - id: 'cat-1', - value: ( - <a key="category1" href="#cat1"> - Category 1 - </a> - ), - }, - { - id: 'cat-2', - value: ( - <a key="category2" href="#cat2"> - Category 2 - </a> - ), - }, - ], - }, -]; - /** * Page Header Stories - Default */ @@ -111,7 +86,33 @@ WithIntro.args = { */ export const WithMeta = Template.bind({}); WithMeta.args = { - meta, + meta: ( + <MetaList> + <MetaItem isInline label="Published on:" value="2022-04-09" /> + <MetaItem + isInline + label="Thematics:" + value={[ + { + id: 'cat-1', + value: ( + <a key="category1" href="#cat1"> + Category 1 + </a> + ), + }, + { + id: 'cat-2', + value: ( + <a key="category2" href="#cat2"> + Category 2 + </a> + ), + }, + ]} + /> + </MetaList> + ), title: 'Excepturi nesciunt illum', }; @@ -122,6 +123,32 @@ export const WithIntroAndMeta = Template.bind({}); WithIntroAndMeta.args = { intro: 'Minima dolor nihil. Velit atque odit totam enim. Quisquam reprehenderit ut et inventore et nihil libero exercitationem. Cumque similique magni placeat et. Et sed est cumque labore. Et quia similique.', - meta, + meta: ( + <MetaList> + <MetaItem isInline label="Published on:" value="2022-04-09" /> + <MetaItem + isInline + label="Thematics:" + value={[ + { + id: 'cat-1', + value: ( + <a key="category1" href="#cat1"> + Category 1 + </a> + ), + }, + { + id: 'cat-2', + value: ( + <a key="category2" href="#cat2"> + Category 2 + </a> + ), + }, + ]} + /> + </MetaList> + ), title: 'Excepturi nesciunt illum', }; diff --git a/src/components/molecules/layout/page-header.test.tsx b/src/components/molecules/layout/page-header.test.tsx index 1f1a139..82aa7e1 100644 --- a/src/components/molecules/layout/page-header.test.tsx +++ b/src/components/molecules/layout/page-header.test.tsx @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; +import { render, screen as rtlScreen } from '../../../../tests/utils'; import { PageHeader } from './page-header'; const title = 'Non nemo amet'; @@ -9,11 +9,13 @@ const intro = describe('PageHeader', () => { it('renders a title', () => { render(<PageHeader title={title} intro={intro} />); - expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(title); + expect(rtlScreen.getByRole('heading', { level: 1 })).toHaveTextContent( + title + ); }); it('renders an introduction', () => { render(<PageHeader title={title} intro={intro} />); - expect(screen.getByText(intro)).toBeInTheDocument(); + expect(rtlScreen.getByText(intro)).toBeInTheDocument(); }); }); diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx index ea0dd2c..e70d66c 100644 --- a/src/components/molecules/layout/page-header.tsx +++ b/src/components/molecules/layout/page-header.tsx @@ -1,6 +1,5 @@ import type { FC, ReactNode } from 'react'; import { Header, Heading } from '../../atoms'; -import { MetaList, type MetaItemData } from '../meta-list'; import styles from './page-header.module.scss'; export type PageHeaderProps = { @@ -15,7 +14,7 @@ export type PageHeaderProps = { /** * The page metadata. */ - meta?: MetaItemData[]; + meta?: ReactNode; /** * The page title. */ @@ -55,9 +54,7 @@ export const PageHeader: FC<PageHeaderProps> = ({ <Heading className={styles.title} level={1}> {title} </Heading> - {meta ? ( - <MetaList className={styles.meta} hasInlinedItems items={meta} /> - ) : null} + {meta} {intro ? getIntro() : null} </div> </Header> diff --git a/src/components/molecules/meta-list/meta-list.stories.tsx b/src/components/molecules/meta-list/meta-list.stories.tsx index 463ec96..d73c5b9 100644 --- a/src/components/molecules/meta-list/meta-list.stories.tsx +++ b/src/components/molecules/meta-list/meta-list.stories.tsx @@ -1,6 +1,7 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { Link } from '../../atoms'; -import { type MetaItemData, MetaList } from './meta-list'; +import { MetaItem } from './meta-item'; +import { MetaList } from './meta-list'; /** * MetaList - Storybook Meta @@ -24,7 +25,7 @@ const Template: ComponentStory<typeof MetaList> = (args) => ( <MetaList {...args} /> ); -const items: MetaItemData[] = [ +const items = [ { id: 'comments', label: 'Comments', value: 'No comments.' }, { id: 'category', @@ -57,7 +58,7 @@ const items: MetaItemData[] = [ */ export const Default = Template.bind({}); Default.args = { - items, + children: items.map(({ id, ...item }) => <MetaItem key={id} {...item} />), }; /** @@ -65,6 +66,6 @@ Default.args = { */ export const Inlined = Template.bind({}); Inlined.args = { + children: items.map(({ id, ...item }) => <MetaItem key={id} {...item} />), isInline: true, - items, }; diff --git a/src/components/molecules/meta-list/meta-list.test.tsx b/src/components/molecules/meta-list/meta-list.test.tsx index cc4d2fa..e4e2860 100644 --- a/src/components/molecules/meta-list/meta-list.test.tsx +++ b/src/components/molecules/meta-list/meta-list.test.tsx @@ -1,31 +1,44 @@ import { describe, expect, it } from '@jest/globals'; import { render, screen as rtlScreen } from '@testing-library/react'; -import { type MetaItemData, MetaList } from './meta-list'; +import { MetaItem } from './meta-item'; +import { MetaList } from './meta-list'; describe('MetaList', () => { it('renders a list of meta items', () => { - const items: MetaItemData[] = [ + const items = [ { id: 'item1', label: 'Item 1', value: 'Value 1' }, { id: 'item2', label: 'Item 2', value: 'Value 2' }, { id: 'item3', label: 'Item 3', value: 'Value 3' }, { id: 'item4', label: 'Item 4', value: 'Value 4' }, ]; - render(<MetaList items={items} />); + render( + <MetaList> + {items.map(({ id, ...item }) => ( + <MetaItem key={id} {...item} /> + ))} + </MetaList> + ); expect(rtlScreen.getAllByRole('term')).toHaveLength(items.length); expect(rtlScreen.getAllByRole('definition')).toHaveLength(items.length); }); it('can render a centered list of meta items', () => { - const items: MetaItemData[] = [ + const items = [ { id: 'item1', label: 'Item 1', value: 'Value 1' }, { id: 'item2', label: 'Item 2', value: 'Value 2' }, { id: 'item3', label: 'Item 3', value: 'Value 3' }, { id: 'item4', label: 'Item 4', value: 'Value 4' }, ]; - render(<MetaList isCentered items={items} />); + render( + <MetaList isCentered> + {items.map(({ id, ...item }) => ( + <MetaItem key={id} {...item} /> + ))} + </MetaList> + ); const terms = rtlScreen.getAllByRole('term'); @@ -33,47 +46,23 @@ describe('MetaList', () => { }); it('can render an inlined list of meta items', () => { - const items: MetaItemData[] = [ + const items = [ { id: 'item1', label: 'Item 1', value: 'Value 1' }, { id: 'item2', label: 'Item 2', value: 'Value 2' }, { id: 'item3', label: 'Item 3', value: 'Value 3' }, { id: 'item4', label: 'Item 4', value: 'Value 4' }, ]; - render(<MetaList isInline items={items} />); + render( + <MetaList isInline> + {items.map(({ id, ...item }) => ( + <MetaItem key={id} {...item} /> + ))} + </MetaList> + ); const terms = rtlScreen.getAllByRole('term'); expect(terms[0].parentElement?.parentElement).toHaveClass('list--inlined'); }); - - it('can render a list of meta items with bordered values', () => { - const items: MetaItemData[] = [ - { id: 'item1', label: 'Item 1', value: 'Value 1' }, - { id: 'item2', label: 'Item 2', value: 'Value 2' }, - { id: 'item3', label: 'Item 3', value: 'Value 3' }, - { id: 'item4', label: 'Item 4', value: 'Value 4' }, - ]; - - render(<MetaList hasBorderedValues items={items} />); - - const terms = rtlScreen.getAllByRole('term'); - - expect(terms[0].parentElement).toHaveClass('item--bordered-values'); - }); - - it('can render a list of meta items with inlined values', () => { - const items: MetaItemData[] = [ - { id: 'item1', label: 'Item 1', value: 'Value 1' }, - { id: 'item2', label: 'Item 2', value: 'Value 2' }, - { id: 'item3', label: 'Item 3', value: 'Value 3' }, - { id: 'item4', label: 'Item 4', value: 'Value 4' }, - ]; - - render(<MetaList hasInlinedValues items={items} />); - - const terms = rtlScreen.getAllByRole('term'); - - expect(terms[0].parentElement).toHaveClass('item--inlined-values'); - }); }); diff --git a/src/components/molecules/meta-list/meta-list.tsx b/src/components/molecules/meta-list/meta-list.tsx index 288fd9a..c19f0fd 100644 --- a/src/components/molecules/meta-list/meta-list.tsx +++ b/src/components/molecules/meta-list/meta-list.tsx @@ -1,54 +1,32 @@ -import { type ForwardRefRenderFunction, forwardRef } from 'react'; +import { + type ForwardRefRenderFunction, + forwardRef, + type ReactNode, +} from 'react'; import { DescriptionList, type DescriptionListProps } from '../../atoms'; -import { MetaItem, type MetaItemProps } from './meta-item'; import styles from './meta-list.module.scss'; -export type MetaItemData = Pick< - MetaItemProps, - | 'hasBorderedValues' - | 'hasInlinedValues' - | 'isCentered' - | 'isInline' - | 'label' - | 'value' +export type MetaListProps = Omit< + DescriptionListProps, + 'children' | 'spacing' > & { - id: string; + /** + * The meta items. + */ + children: ReactNode; + /** + * Should the meta be centered? + * + * @default false + */ + isCentered?: boolean; }; -export type MetaListProps = Omit<DescriptionListProps, 'children' | 'spacing'> & - Pick<MetaItemProps, 'hasBorderedValues' | 'hasInlinedValues'> & { - /** - * Should the items be inlined? - * - * @default false - */ - hasInlinedItems?: boolean; - /** - * Should the meta be centered? - * - * @default false - */ - isCentered?: boolean; - /** - * The meta items. - */ - items: MetaItemData[]; - }; - const MetaListWithRef: ForwardRefRenderFunction< HTMLDListElement, MetaListProps > = ( - { - className = '', - hasBorderedValues = false, - hasInlinedItems = false, - hasInlinedValues = false, - isCentered = false, - isInline = false, - items, - ...props - }, + { children, className = '', isCentered = false, isInline = false, ...props }, ref ) => { const listClass = [ @@ -60,17 +38,7 @@ const MetaListWithRef: ForwardRefRenderFunction< return ( <DescriptionList {...props} className={listClass} ref={ref}> - {items.map(({ id, ...item }) => ( - <MetaItem - hasBorderedValues={hasBorderedValues} - hasInlinedValues={hasInlinedValues} - isCentered={isCentered} - isInline={hasInlinedItems} - // Each item should be able to override the global settings. - {...item} - key={id} - /> - ))} + {children} </DescriptionList> ); }; diff --git a/src/components/organisms/comment/approved-comment/approved-comment.tsx b/src/components/organisms/comment/approved-comment/approved-comment.tsx index db5345b..233146d 100644 --- a/src/components/organisms/comment/approved-comment/approved-comment.tsx +++ b/src/components/organisms/comment/approved-comment/approved-comment.tsx @@ -12,6 +12,7 @@ import { CardTitle, CardFooter, CardActions, + MetaItem, } from '../../../molecules'; import styles from './approved-comment.module.scss'; @@ -138,20 +139,17 @@ const ApprovedCommentWithRef: ForwardRefRenderFunction< author.name )} </CardTitle> - <CardMeta - hasInlinedItems - items={[ - { - id: 'publication-date', - label: publicationDateLabel, - value: ( - <Link href={commentLink}> - <Time date={publicationDate} showTime /> - </Link> - ), - }, - ]} - /> + <CardMeta> + <MetaItem + isInline + label={publicationDateLabel} + value={ + <Link href={commentLink}> + <Time date={publicationDate} showTime /> + </Link> + } + /> + </CardMeta> </CardHeader> <CardBody className={styles.body} diff --git a/src/components/organisms/post-preview/post-preview-meta/post-preview-meta.tsx b/src/components/organisms/post-preview/post-preview-meta/post-preview-meta.tsx index 5a342da..54e359e 100644 --- a/src/components/organisms/post-preview/post-preview-meta/post-preview-meta.tsx +++ b/src/components/organisms/post-preview/post-preview-meta/post-preview-meta.tsx @@ -1,12 +1,13 @@ -import type { FC, ReactNode } from 'react'; +import type { FC, ReactElement, ReactNode } from 'react'; import { useIntl } from 'react-intl'; import type { PageLink } from '../../../../types'; import { getReadingTimeFrom } from '../../../../utils/helpers'; import { Link, Time, VisuallyHidden } from '../../../atoms'; import { CardMeta, - type MetaItemData, type CardMetaProps, + MetaItem, + type MetaItemProps, } from '../../../molecules'; const a11y = (chunks: ReactNode) => <VisuallyHidden>{chunks}</VisuallyHidden>; @@ -57,20 +58,7 @@ export type PostPreviewMetaData = { wordsCount?: number; }; -const validMetaKeys = [ - 'author', - 'comments', - 'publicationDate', - 'thematics', - 'topics', - 'updateDate', - 'wordsCount', -] satisfies (keyof PostPreviewMetaData)[]; - -const isValidMetaKey = (key: string): key is keyof PostPreviewMetaData => - (validMetaKeys as string[]).includes(key); - -export type PostPreviewMetaProps = Omit<CardMetaProps, 'items'> & { +export type PostPreviewMetaProps = Omit<CardMetaProps, 'children' | 'items'> & { /** * The post meta. */ @@ -83,23 +71,20 @@ export const PostPreviewMeta: FC<PostPreviewMetaProps> = ({ }) => { const intl = useIntl(); - const getAuthor = (): MetaItemData | undefined => { - if (!meta.author) return undefined; - - return { - id: 'author', - label: intl.formatMessage({ + const getAuthor = (author: string): ReactElement<MetaItemProps> => ( + <MetaItem + label={intl.formatMessage({ defaultMessage: 'Written by:', description: 'PostPreviewMeta: author label', id: '2U7ixo', - }), - value: meta.author, - }; - }; - - const getCommentsCount = (): MetaItemData | undefined => { - if (!meta.comments) return undefined; - + })} + value={author} + /> + ); + + const getComments = ( + comments: PostPreviewMetaComment + ): ReactElement<MetaItemProps> => { const commentsLabel = intl.formatMessage<ReactNode>( { defaultMessage: @@ -109,146 +94,121 @@ export const PostPreviewMeta: FC<PostPreviewMetaProps> = ({ }, { a11y, - commentsCount: meta.comments.count, - title: meta.comments.postHeading, + commentsCount: comments.count, + title: comments.postHeading, } ); - return { - id: 'comments', - label: intl.formatMessage({ - defaultMessage: 'Comments:', - description: 'PostPreviewMeta: comments label', - id: 'FCpPCm', - }), - value: meta.comments.url ? ( - <Link href={meta.comments.url}>{commentsLabel}</Link> - ) : ( - <>{commentsLabel}</> - ), - }; + return ( + <MetaItem + label={intl.formatMessage({ + defaultMessage: 'Comments:', + description: 'PostPreviewMeta: comments label', + id: 'FCpPCm', + })} + value={ + comments.url ? ( + <Link href={comments.url}>{commentsLabel}</Link> + ) : ( + <>{commentsLabel}</> + ) + } + /> + ); }; - const getPublicationDate = (): MetaItemData | undefined => { - if (!meta.publicationDate) return undefined; - - return { - id: 'publication-date', - label: intl.formatMessage({ + const getPublicationDate = (date: string): ReactElement<MetaItemProps> => ( + <MetaItem + label={intl.formatMessage({ defaultMessage: 'Published on:', description: 'PostPreviewMeta: publication date label', id: '+6f4p1', - }), - value: <Time date={meta.publicationDate} />, - }; - }; - - const getThematics = (): MetaItemData | undefined => { - if (!meta.thematics?.length) return undefined; - - return { - id: 'thematics', - label: intl.formatMessage( + })} + value={<Time date={date} />} + /> + ); + + const getThematics = (thematics: PageLink[]): ReactElement<MetaItemProps> => ( + <MetaItem + label={intl.formatMessage( { defaultMessage: '{thematicsCount, plural, =0 {Thematics:} one {Thematic:} other {Thematics:}}', description: 'PostPreviewMeta: thematics label', id: '9MTBCG', }, - { thematicsCount: meta.thematics.length } - ), - value: meta.thematics.map((thematic) => { + { thematicsCount: thematics.length } + )} + value={thematics.map((thematic) => { return { id: `thematic-${thematic.id}`, value: <Link href={thematic.url}>{thematic.name}</Link>, }; - }), - }; - }; - - const getTopics = (): MetaItemData | undefined => { - if (!meta.topics?.length) return undefined; + })} + /> + ); - return { - id: 'topics', - label: intl.formatMessage( + const getTopics = (topics: PageLink[]): ReactElement<MetaItemProps> => ( + <MetaItem + label={intl.formatMessage( { defaultMessage: '{topicsCount, plural, =0 {Topics:} one {Topic:} other {Topics:}}', description: 'PostPreviewMeta: topics label', id: 'aBQYbE', }, - { topicsCount: meta.topics.length } - ), - value: meta.topics.map((topic) => { + { topicsCount: topics.length } + )} + value={topics.map((topic) => { return { id: `topic-${topic.id}`, value: <Link href={topic.url}>{topic.name}</Link>, }; - }), - }; - }; + })} + /> + ); - const getUpdateDate = (): MetaItemData | undefined => { - if (!meta.updateDate || meta.updateDate === meta.publicationDate) - return undefined; - - return { - id: 'update-date', - label: intl.formatMessage({ + const getUpdateDate = (date: string): ReactElement<MetaItemProps> => ( + <MetaItem + label={intl.formatMessage({ defaultMessage: 'Updated on:', description: 'PostPreviewMeta: update date label', id: 'ZmRh0V', - }), - value: <Time date={meta.updateDate} />, - }; - }; - - const getReadingTime = (): MetaItemData | undefined => { - if (!meta.wordsCount) return undefined; - - return { - id: 'reading-time', - label: intl.formatMessage({ + })} + value={<Time date={date} />} + /> + ); + + const getReadingTime = (wordsCount: number): ReactElement<MetaItemProps> => ( + <MetaItem + label={intl.formatMessage({ defaultMessage: 'Reading time:', description: 'PostPreviewMeta: reading time label', id: 'B1lS/v', - }), - value: intl.formatMessage( + })} + value={intl.formatMessage( { defaultMessage: '{minutesCount, plural, =0 {Less than one minute} one {# minute} other {# minutes}}', description: 'PostPreviewMeta: rounded minutes count', id: 'y+13Ax', }, - { minutesCount: getReadingTimeFrom(meta.wordsCount).inMinutes() } - ), - }; - }; - - const items: MetaItemData[] = Object.keys(meta) - .filter(isValidMetaKey) - .map((key): MetaItemData | undefined => { - switch (key) { - case 'author': - return getAuthor(); - case 'comments': - return getCommentsCount(); - case 'publicationDate': - return getPublicationDate(); - case 'thematics': - return getThematics(); - case 'topics': - return getTopics(); - case 'updateDate': - return getUpdateDate(); - case 'wordsCount': - return getReadingTime(); - default: - throw new Error('Unsupported meta key.'); - } - }) - .filter((item): item is MetaItemData => item !== undefined); - - return <CardMeta {...props} items={items} />; + { minutesCount: getReadingTimeFrom(wordsCount).inMinutes() } + )} + /> + ); + + return ( + <CardMeta {...props}> + {meta.author ? getAuthor(meta.author) : null} + {meta.publicationDate ? getPublicationDate(meta.publicationDate) : null} + {meta.updateDate && meta.updateDate !== meta.publicationDate + ? getUpdateDate(meta.updateDate) + : null} + {meta.wordsCount ? getReadingTime(meta.wordsCount) : null} + {meta.thematics ? getThematics(meta.thematics) : null} + {meta.topics ? getTopics(meta.topics) : null} + {meta.comments ? getComments(meta.comments) : null} + </CardMeta> + ); }; diff --git a/src/components/organisms/project-overview/project-overview.tsx b/src/components/organisms/project-overview/project-overview.tsx index 2b8be0e..f524120 100644 --- a/src/components/organisms/project-overview/project-overview.tsx +++ b/src/components/organisms/project-overview/project-overview.tsx @@ -6,7 +6,7 @@ import { type ReactElement, } from 'react'; import { useIntl } from 'react-intl'; -import type { Maybe, ValueOf } from '../../../types'; +import type { ValueOf } from '../../../types'; import { Time, type SocialWebsite, @@ -14,7 +14,7 @@ import { SocialLink, Figure, } from '../../atoms'; -import { MetaList, type MetaItemData } from '../../molecules'; +import { MetaItem, type MetaItemProps, MetaList } from '../../molecules'; import styles from './project-overview.module.scss'; export type Repository = { @@ -155,27 +155,31 @@ const ProjectOverviewWithRef: ForwardRefRenderFunction< [intl] ); - const getMetaItems = useCallback((): MetaItemData[] => { + const getMetaItems = useCallback(() => { const keys = Object.keys(meta).filter(isValidMetaKey); return keys - .map((key): Maybe<MetaItemData> => { + .map((key) => { const value = meta[key]; - return value - ? { - id: key, - label: metaLabels[key], - value: getMetaValue(key, value), - hasBorderedValues: key === 'technologies', - hasInlinedValues: - (key === 'technologies' || key === 'repositories') && - Array.isArray(value) && - value.length > 1, + return value ? ( + <MetaItem + hasBorderedValues={key === 'technologies'} + hasInlinedValues={ + (key === 'technologies' || key === 'repositories') && + Array.isArray(value) && + value.length > 1 } - : undefined; + key={key} + label={metaLabels[key]} + value={getMetaValue(key, value)} + /> + ) : undefined; }) - .filter((item): item is MetaItemData => typeof item !== 'undefined'); + .filter( + (item): item is ReactElement<MetaItemProps> => + typeof item !== 'undefined' + ); }, [getMetaValue, meta, metaLabels]); return ( @@ -185,7 +189,9 @@ const ProjectOverviewWithRef: ForwardRefRenderFunction< {cover} </Figure> ) : null} - <MetaList className={styles.meta} isInline items={getMetaItems()} /> + <MetaList className={styles.meta} isInline> + {getMetaItems()} + </MetaList> </div> ); }; diff --git a/src/components/templates/page/page-layout.stories.tsx b/src/components/templates/page/page-layout.stories.tsx index 2b44933..6dcbeea 100644 --- a/src/components/templates/page/page-layout.stories.tsx +++ b/src/components/templates/page/page-layout.stories.tsx @@ -1,5 +1,6 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { ButtonLink, Heading, Link } from '../../atoms'; +import { MetaItem, MetaList } from '../../molecules'; import { LinksWidget, PostsList, SharingWidget } from '../../organisms'; import { LayoutBase } from '../layout/layout.stories'; import { PageLayout as PageLayoutComponent } from './page-layout'; @@ -270,38 +271,41 @@ Post.args = { breadcrumb: postBreadcrumb, title: pageTitle, intro: pageIntro, - headerMeta: [ - { id: 'publication-date', label: 'Published on:', value: '2020-03-14' }, - { - id: 'thematics', - label: 'Thematics:', - value: [ - { - id: 'cat-1', - value: ( - <Link key="cat1" href="#"> - Cat 1 - </Link> - ), - }, - { - id: 'cat-2', - value: ( - <Link key="cat2" href="#"> - Cat 2 - </Link> - ), - }, - ], - }, - ], - footerMeta: [ - { - id: 'read-more', - label: 'Read more about:', - value: <ButtonLink to="#">Topic 1</ButtonLink>, - }, - ], + headerMeta: ( + <MetaList> + <MetaItem isInline label="Published on:" value="2020-03-14" /> + <MetaItem + isInline + label="Thematic:" + value={[ + { + id: 'cat-1', + value: ( + <Link key="cat1" href="#"> + Cat 1 + </Link> + ), + }, + { + id: 'cat-2', + value: ( + <Link key="cat2" href="#"> + Cat 2 + </Link> + ), + }, + ]} + /> + </MetaList> + ), + footerMeta: ( + <MetaList> + <MetaItem + label="Read more about:" + value={<ButtonLink to="#">Topic 1</ButtonLink>} + /> + </MetaList> + ), children: ( <> <Heading level={2}>Impedit commodi rerum</Heading> @@ -497,7 +501,11 @@ export const Blog = Template.bind({}); Blog.args = { breadcrumb: postsListBreadcrumb, title: 'Blog', - headerMeta: [{ id: 'total', label: 'Total:', value: `${posts.length}` }], + headerMeta: ( + <MetaList> + <MetaItem isInline label="Total:" value={`${posts.length}`} /> + </MetaList> + ), children: <PostsList posts={posts} sortByYear />, widgets: [ <LinksWidget diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx index db71e07..75d308e 100644 --- a/src/components/templates/page/page-layout.tsx +++ b/src/components/templates/page/page-layout.tsx @@ -12,12 +12,7 @@ import { sendComment } from '../../../services/graphql'; import type { SendCommentInput } from '../../../types'; import { useHeadingsTree } from '../../../utils/hooks'; import { Heading, Sidebar } from '../../atoms'; -import { - PageFooter, - type PageFooterProps, - PageHeader, - type PageHeaderProps, -} from '../../molecules'; +import { PageFooter, PageHeader, type PageHeaderProps } from '../../molecules'; import { CommentForm, CommentsList, @@ -61,11 +56,11 @@ export type PageLayoutProps = { /** * The footer metadata. */ - footerMeta?: PageFooterProps['meta']; + footerMeta?: ReactNode; /** * The header metadata. */ - headerMeta?: PageHeaderProps['meta']; + headerMeta?: ReactNode; /** * The page id. */ @@ -240,8 +235,8 @@ export const PageLayout: FC<PageLayoutProps> = ({ {children} </div> )} - {footerMeta?.length ? ( - <PageFooter meta={footerMeta} className={styles.footer} /> + {footerMeta ? ( + <PageFooter className={styles.footer}>{footerMeta}</PageFooter> ) : null} <Sidebar aria-label={intl.formatMessage({ diff --git a/src/i18n/en.json b/src/i18n/en.json index 008f476..2091057 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -59,10 +59,6 @@ "defaultMessage": "Name:", "description": "ContactForm: name label" }, - "24FIsG": { - "defaultMessage": "Updated on:", - "description": "ThematicPage: update date label" - }, "28GZdv": { "defaultMessage": "Projects", "description": "Breadcrumb: projects label" @@ -155,10 +151,6 @@ "defaultMessage": "{date} at {time}", "description": "Time: readable date and time" }, - "9DfuHk": { - "defaultMessage": "Updated on:", - "description": "TopicPage: update date label" - }, "9MTBCG": { "defaultMessage": "{thematicsCount, plural, =0 {Thematics:} one {Thematic:} other {Thematics:}}", "description": "PostPreviewMeta: thematics label" @@ -291,10 +283,6 @@ "defaultMessage": "Thematics", "description": "BlogPage: thematics list widget title" }, - "HxZvY4": { - "defaultMessage": "Published on:", - "description": "ProjectsPage: publication date label" - }, "IVczxR": { "defaultMessage": "Go to page {number}", "description": "BlogPage: page number label" @@ -323,10 +311,6 @@ "defaultMessage": "Skip to content", "description": "Layout: Skip to content link" }, - "KV+NMZ": { - "defaultMessage": "Published on:", - "description": "TopicPage: publication date label" - }, "KVSWGP": { "defaultMessage": "Other thematics", "description": "ThematicPage: other thematics list widget title" @@ -427,10 +411,6 @@ "defaultMessage": "CV", "description": "Layout: main nav - cv link" }, - "RecdwX": { - "defaultMessage": "Published on:", - "description": "ArticlePage: publication date label" - }, "RvGb2c": { "defaultMessage": "{postsCount, plural, =0 {No articles} one {# article} other {# articles}}", "description": "Page: posts count meta" @@ -451,10 +431,6 @@ "defaultMessage": "An error occurred:", "description": "Contact: error message" }, - "UTGhUU": { - "defaultMessage": "Published on:", - "description": "ThematicPage: publication date label" - }, "VkAnvv": { "defaultMessage": "Send", "description": "ContactForm: send button" @@ -499,10 +475,6 @@ "defaultMessage": "Light theme", "description": "ThemeToggle: light theme label" }, - "ZAqGZ6": { - "defaultMessage": "Updated on:", - "description": "ArticlePage: update date label" - }, "ZB/Aw2": { "defaultMessage": "Partial includes only page url, views and duration.", "description": "AckeeToggle: tooltip message" @@ -719,10 +691,6 @@ "defaultMessage": "Theme:", "description": "ThemeToggle: theme label" }, - "tBX4mb": { - "defaultMessage": "Total:", - "description": "TopicPage: total label" - }, "tIZYpD": { "defaultMessage": "Partial", "description": "AckeeToggle: partial option name" @@ -739,10 +707,6 @@ "defaultMessage": "Website:", "description": "CommentForm: website label" }, - "uAL4iW": { - "defaultMessage": "{postsCount, plural, =0 {No articles} one {# article} other {# articles}}", - "description": "TopicPage: posts count meta" - }, "uZj4QI": { "defaultMessage": "Cancel reply", "description": "CommentsList: cancel reply button" @@ -767,10 +731,6 @@ "defaultMessage": "Free", "description": "HomePage: link to free thematic" }, - "wQrvgw": { - "defaultMessage": "Updated on:", - "description": "ProjectsPage: update date label" - }, "xaqaYQ": { "defaultMessage": "Sending mail...", "description": "ContactForm: spinner message on submit" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index c83d146..d6bdef1 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -59,10 +59,6 @@ "defaultMessage": "Nom :", "description": "ContactForm: name label" }, - "24FIsG": { - "defaultMessage": "Mis à jour le :", - "description": "ThematicPage: update date label" - }, "28GZdv": { "defaultMessage": "Projets", "description": "Breadcrumb: projects label" @@ -155,10 +151,6 @@ "defaultMessage": "{date} à {time}", "description": "Time: readable date and time" }, - "9DfuHk": { - "defaultMessage": "Mis à jour le :", - "description": "TopicPage: update date label" - }, "9MTBCG": { "defaultMessage": "{thematicsCount, plural, =0 {Thématiques :} one {Thématique :} other {Thématiques :}}", "description": "PostPreviewMeta: thematics label" @@ -291,10 +283,6 @@ "defaultMessage": "Thématiques", "description": "BlogPage: thematics list widget title" }, - "HxZvY4": { - "defaultMessage": "Publié le :", - "description": "ProjectsPage: publication date label" - }, "IVczxR": { "defaultMessage": "Aller à la page {number}", "description": "BlogPage: page number label" @@ -323,10 +311,6 @@ "defaultMessage": "Aller au contenu", "description": "Layout: Skip to content link" }, - "KV+NMZ": { - "defaultMessage": "Publié le :", - "description": "TopicPage: publication date label" - }, "KVSWGP": { "defaultMessage": "Autres thématiques", "description": "ThematicPage: other thematics list widget title" @@ -427,10 +411,6 @@ "defaultMessage": "CV", "description": "Layout: main nav - cv link" }, - "RecdwX": { - "defaultMessage": "Publié le :", - "description": "ArticlePage: publication date label" - }, "RvGb2c": { "defaultMessage": "{postsCount, plural, =0 {0 article} one {# article} other {# articles}}", "description": "Page: posts count meta" @@ -451,10 +431,6 @@ "defaultMessage": "Une erreur est survenue :", "description": "Contact: error message" }, - "UTGhUU": { - "defaultMessage": "Publié le :", - "description": "ThematicPage: publication date label" - }, "VkAnvv": { "defaultMessage": "Envoyer", "description": "ContactForm: send button" @@ -499,10 +475,6 @@ "defaultMessage": "Thème clair", "description": "ThemeToggle: light theme label" }, - "ZAqGZ6": { - "defaultMessage": "Mis à jour le :", - "description": "ArticlePage: update date label" - }, "ZB/Aw2": { "defaultMessage": "Partiel inclut seulement l’url de la page, le nombre de visites et la durée.", "description": "AckeeToggle: tooltip message" @@ -719,10 +691,6 @@ "defaultMessage": "Thème :", "description": "ThemeToggle: theme label" }, - "tBX4mb": { - "defaultMessage": "Total :", - "description": "TopicPage: total label" - }, "tIZYpD": { "defaultMessage": "Partiel", "description": "AckeeToggle: partial option name" @@ -739,10 +707,6 @@ "defaultMessage": "Site web :", "description": "CommentForm: website label" }, - "uAL4iW": { - "defaultMessage": "{postsCount, plural, =0 {0 article} one {# article} other {# articles}}", - "description": "TopicPage: posts count meta" - }, "uZj4QI": { "defaultMessage": "Annuler la réponse", "description": "CommentsList: cancel reply button" @@ -767,10 +731,6 @@ "defaultMessage": "Libre", "description": "HomePage: link to free thematic" }, - "wQrvgw": { - "defaultMessage": "Mis à jour le :", - "description": "ProjectsPage: update date label" - }, "xaqaYQ": { "defaultMessage": "Mail en cours d’envoi…", "description": "ContactForm: spinner message on submit" diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index 7875d1d..0cba7a6 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -15,10 +15,11 @@ import { PageLayout, SharingWidget, Spinner, - type MetaItemData, Time, type CommentData, Heading, + MetaList, + MetaItem, } from '../../components'; import { getAllArticlesSlugs, @@ -112,102 +113,6 @@ const ArticlePage: NextPageWithLayout<ArticlePageProps> = ({ const { content, id, intro, meta, title } = article; const { author, commentsCount, cover, dates, seo, thematics, topics } = meta; - const headerMeta: (MetaItemData | undefined)[] = [ - author - ? { - id: 'author', - label: intl.formatMessage({ - defaultMessage: 'Written by:', - description: 'ArticlePage: author label', - id: 'MJbZfX', - }), - value: author.name, - } - : undefined, - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'ArticlePage: publication date label', - id: 'RecdwX', - }), - value: <Time date={dates.publication} />, - }, - dates.update && dates.publication !== dates.update - ? { - id: 'update-date', - label: intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'ArticlePage: update date label', - id: 'ZAqGZ6', - }), - value: <Time date={dates.update} />, - } - : undefined, - { - id: 'reading-time', - label: intl.formatMessage({ - defaultMessage: 'Reading time:', - description: 'ArticlePage: reading time label', - id: 'Gw7X3x', - }), - value: readingTime, - }, - thematics - ? { - id: 'thematics', - label: intl.formatMessage({ - defaultMessage: 'Thematics:', - description: 'ArticlePage: thematics meta label', - id: 'CvOqoh', - }), - value: thematics.map((thematic) => { - return { - id: `thematic-${thematic.id}`, - value: ( - <Link key={thematic.id} href={thematic.url}> - {thematic.name} - </Link> - ), - }; - }), - } - : undefined, - ]; - const filteredHeaderMeta = headerMeta.filter( - (item): item is MetaItemData => !!item - ); - - const footerMetaLabel = intl.formatMessage({ - defaultMessage: 'Read more articles about:', - description: 'ArticlePage: footer topics list label', - id: '50xc4o', - }); - - const footerMeta: MetaItemData[] = topics - ? [ - { - id: 'more-about', - label: footerMetaLabel, - value: topics.map((topic) => { - return { - id: `topic--${topic.id}`, - value: ( - <ButtonLink - className={styles.btn} - key={topic.id} - to={topic.url} - > - {topic.logo ? <NextImage {...topic.logo} /> : null}{' '} - {topic.name} - </ButtonLink> - ), - }; - }), - }, - ] - : []; - const webpageSchema = getWebPageSchema({ description: intro, locale: CONFIG.locales.defaultLocale, @@ -333,8 +238,99 @@ const ArticlePage: NextPageWithLayout<ArticlePageProps> = ({ breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} comments={getComments(commentsData)} - footerMeta={footerMeta} - headerMeta={filteredHeaderMeta} + footerMeta={ + topics ? ( + <MetaList> + <MetaItem + hasInlinedValues + label={intl.formatMessage({ + defaultMessage: 'Read more articles about:', + description: 'ArticlePage: footer topics list label', + id: '50xc4o', + })} + value={topics.map((topic) => { + return { + id: `topic--${topic.id}`, + value: ( + <ButtonLink + className={styles.btn} + key={topic.id} + to={topic.url} + > + {topic.logo ? <NextImage {...topic.logo} /> : null}{' '} + {topic.name} + </ButtonLink> + ), + }; + })} + /> + </MetaList> + ) : undefined + } + headerMeta={ + <MetaList> + {author ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Written by:', + description: 'ArticlePage: author label', + id: 'MJbZfX', + })} + value={author.name} + /> + ) : null} + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Page: publication date label', + id: '4QbTDq', + })} + value={<Time date={dates.publication} />} + /> + {dates.update ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Page: update date label', + id: 'Ez8Qim', + })} + value={<Time date={dates.update} />} + /> + ) : null} + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Reading time:', + description: 'ArticlePage: reading time label', + id: 'Gw7X3x', + })} + value={readingTime} + /> + {thematics ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Thematics:', + description: 'ArticlePage: thematics meta label', + id: 'CvOqoh', + })} + value={thematics.map((thematic) => { + return { + id: `thematic-${thematic.id}`, + value: ( + <Link key={thematic.id} href={thematic.url}> + {thematic.name} + </Link> + ), + }; + })} + /> + ) : null} + </MetaList> + } id={id as number} intro={intro} title={title} diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx index 5a13e3e..6ed6eda 100644 --- a/src/pages/blog/index.tsx +++ b/src/pages/blog/index.tsx @@ -9,13 +9,14 @@ import { getLayout, Heading, LinksWidget, - type MetaItemData, Notice, PageLayout, PostsList, Pagination, type RenderPaginationLink, type RenderPaginationItemAriaLabel, + MetaList, + MetaItem, } from '../../components'; import { getArticles, @@ -183,28 +184,6 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ [intl] ); - const headerMeta: MetaItemData[] = totalArticles - ? [ - { - id: 'posts-count', - label: intl.formatMessage({ - defaultMessage: 'Total:', - description: 'Page: total label', - id: 'kNBXyK', - }), - value: intl.formatMessage( - { - defaultMessage: - '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', - description: 'Page: posts count meta', - id: 'RvGb2c', - }, - { postsCount: totalArticles } - ), - }, - ] - : []; - const paginationAriaLabel = intl.formatMessage({ defaultMessage: 'Pagination', description: 'BlogPage: pagination accessible name', @@ -234,7 +213,27 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ title={title} breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} - headerMeta={headerMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Total:', + description: 'Page: total label', + id: 'kNBXyK', + })} + value={intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', + description: 'Page: posts count meta', + id: 'RvGb2c', + }, + { postsCount: totalArticles } + )} + /> + </MetaList> + } widgets={[ <LinksWidget heading={ diff --git a/src/pages/blog/page/[number].tsx b/src/pages/blog/page/[number].tsx index 03b641b..27d1816 100644 --- a/src/pages/blog/page/[number].tsx +++ b/src/pages/blog/page/[number].tsx @@ -10,12 +10,13 @@ import { getLayout, Heading, LinksWidget, - type MetaItemData, PageLayout, PostsList, Pagination, type RenderPaginationLink, type RenderPaginationItemAriaLabel, + MetaList, + MetaItem, } from '../../../components'; import { getArticles, @@ -187,28 +188,6 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ [intl] ); - const headerMeta: MetaItemData[] = totalArticles - ? [ - { - id: 'posts-count', - label: intl.formatMessage({ - defaultMessage: 'Total:', - description: 'Page: total label', - id: 'kNBXyK', - }), - value: intl.formatMessage( - { - defaultMessage: - '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', - description: 'Page: posts count meta', - id: 'RvGb2c', - }, - { postsCount: totalArticles } - ), - }, - ] - : []; - const paginationAriaLabel = intl.formatMessage({ defaultMessage: 'Pagination', description: 'BlogPage: pagination accessible name', @@ -238,7 +217,27 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ title={pageTitleWithPageNumber} breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} - headerMeta={headerMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Total:', + description: 'Page: total label', + id: 'kNBXyK', + })} + value={intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', + description: 'Page: posts count meta', + id: 'RvGb2c', + }, + { postsCount: totalArticles } + )} + /> + </MetaList> + } widgets={[ <LinksWidget heading={ diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx index 5157249..0cda194 100644 --- a/src/pages/cv.tsx +++ b/src/pages/cv.tsx @@ -20,8 +20,9 @@ import { PageLayout, SocialMediaWidget, ListItem, - type MetaItemData, Time, + MetaList, + MetaItem, } from '../components'; import CVContent, { data, meta } from '../content/pages/cv.mdx'; import type { NextPageWithLayout } from '../types'; @@ -154,32 +155,6 @@ const CVPage: NextPageWithLayout = () => { id: '+Dre5J', }); - const headerMeta: (MetaItemData | undefined)[] = [ - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'Page: publication date label', - id: '4QbTDq', - }), - value: <Time date={dates.publication} />, - }, - dates.update - ? { - id: 'update-date', - label: intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'Page: update date label', - id: 'Ez8Qim', - }), - value: <Time date={dates.update} />, - } - : undefined, - ]; - const filteredMeta = headerMeta.filter( - (item): item is MetaItemData => !!item - ); - const cvCaption = intl.formatMessage( { defaultMessage: '<link>Download the CV in PDF</link>', @@ -282,7 +257,30 @@ const CVPage: NextPageWithLayout = () => { <PageLayout breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} - headerMeta={filteredMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Page: publication date label', + id: '4QbTDq', + })} + value={<Time date={dates.publication} />} + /> + {dates.update ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Page: update date label', + id: 'Ez8Qim', + })} + value={<Time date={dates.update} />} + /> + ) : null} + </MetaList> + } intro={intro} title={title} widgets={widgets} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 0306736..d708ac5 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -25,6 +25,7 @@ import { Section, type SectionProps, Time, + MetaItem, } from '../components'; import HomePageContent from '../content/pages/homepage.mdx'; import { getArticlesCard } from '../services/graphql'; @@ -332,18 +333,14 @@ const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => { ) : undefined } meta={ - <CardMeta - hasBorderedValues - hasInlinedValues - isCentered - items={[ - { - id: 'publication-date', - label: publicationDate, - value: <Time date={post.dates.publication} />, - }, - ]} - /> + <CardMeta isCentered> + <MetaItem + hasBorderedValues + isCentered + label={publicationDate} + value={<Time date={post.dates.publication} />} + /> + </CardMeta> } isCentered linkTo={`${ROUTES.ARTICLE}/${post.slug}`} diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx index 50f60f5..e07263f 100644 --- a/src/pages/mentions-legales.tsx +++ b/src/pages/mentions-legales.tsx @@ -11,8 +11,9 @@ import { Link, PageLayout, Figure, - type MetaItemData, Time, + MetaList, + MetaItem, } from '../components'; import LegalNoticeContent, { meta } from '../content/pages/legal-notice.mdx'; import type { NextPageWithLayout } from '../types'; @@ -48,32 +49,6 @@ const LegalNoticePage: NextPageWithLayout = () => { url: ROUTES.LEGAL_NOTICE, }); - const headerMeta: (MetaItemData | undefined)[] = [ - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'Page: publication date label', - id: '4QbTDq', - }), - value: <Time date={dates.publication} />, - }, - dates.update - ? { - id: 'update-date', - label: intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'Page: update date label', - id: 'Ez8Qim', - }), - value: <Time date={dates.update} />, - } - : undefined, - ]; - const filteredMeta = headerMeta.filter( - (item): item is MetaItemData => !!item - ); - const { asPath } = useRouter(); const webpageSchema = getWebPageSchema({ description: seo.description, @@ -101,7 +76,30 @@ const LegalNoticePage: NextPageWithLayout = () => { <PageLayout breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} - headerMeta={filteredMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Page: publication date label', + id: '4QbTDq', + })} + value={<Time date={dates.publication} />} + /> + {dates.update ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Page: update date label', + id: 'Ez8Qim', + })} + value={<Time date={dates.update} />} + /> + ) : null} + </MetaList> + } intro={intro} title={title} withToC={true} diff --git a/src/pages/projets/[slug].tsx b/src/pages/projets/[slug].tsx index 1aa9e7f..a8a4fea 100644 --- a/src/pages/projets/[slug].tsx +++ b/src/pages/projets/[slug].tsx @@ -19,12 +19,13 @@ import { List, ListItem, Figure, - type MetaItemData, Time, Grid, ProjectOverview, type ProjectMeta, type Repository, + MetaList, + MetaItem, } from '../../components'; import styles from '../../styles/pages/project.module.scss'; import type { NextPageWithLayout, ProjectPreview, Repos } from '../../types'; @@ -177,32 +178,6 @@ const ProjectPage: NextPageWithLayout<ProjectPageProps> = ({ project }) => { url: `${CONFIG.url}${asPath}`, }; - const headerMeta: (MetaItemData | undefined)[] = [ - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'ProjectsPage: publication date label', - id: 'HxZvY4', - }), - value: <Time date={dates.publication} />, - }, - dates.update && dates.update !== dates.publication - ? { - id: 'update-date', - label: intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'ProjectsPage: update date label', - id: 'wQrvgw', - }), - value: <Time date={dates.update} />, - } - : undefined, - ]; - const filteredHeaderMeta = headerMeta.filter( - (item): item is MetaItemData => !!item - ); - /** * Retrieve the project repositories. * @@ -319,7 +294,30 @@ const ProjectPage: NextPageWithLayout<ProjectPageProps> = ({ project }) => { intro={intro} breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} - headerMeta={filteredHeaderMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Page: publication date label', + id: '4QbTDq', + })} + value={<Time date={dates.publication} />} + /> + {dates.update ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Page: update date label', + id: 'Ez8Qim', + })} + value={<Time date={dates.update} />} + /> + ) : null} + </MetaList> + } withToC={true} widgets={[ <SharingWidget diff --git a/src/pages/projets/index.tsx b/src/pages/projets/index.tsx index 97b43e3..8feb701 100644 --- a/src/pages/projets/index.tsx +++ b/src/pages/projets/index.tsx @@ -19,6 +19,7 @@ import { Link, MetaList, PageLayout, + MetaItem, } from '../../components'; import PageContent, { meta } from '../../content/pages/projects.mdx'; import styles from '../../styles/pages/projects.module.scss'; @@ -86,20 +87,17 @@ const ProjectsPage: NextPageWithLayout<ProjectsPageProps> = ({ projects }) => { } meta={ technologies ? ( - <MetaList - hasBorderedValues - hasInlinedValues - isCentered - items={[ - { - id: 'technologies', - label: metaLabel, - value: technologies.map((techno) => { - return { id: techno, value: techno }; - }), - }, - ]} - /> + <MetaList isCentered> + <MetaItem + hasBorderedValues + hasInlinedValues + isCentered + label={metaLabel} + value={technologies.map((techno) => { + return { id: techno, value: techno }; + })} + /> + </MetaList> ) : undefined } isCentered diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx index 92035b0..0fb279b 100644 --- a/src/pages/recherche/index.tsx +++ b/src/pages/recherche/index.tsx @@ -9,13 +9,14 @@ import { getLayout, Heading, LinksWidget, - type MetaItemData, Notice, PageLayout, PostsList, Spinner, SearchForm, type SearchFormSubmit, + MetaList, + MetaItem, } from '../../components'; import { getArticles, @@ -132,28 +133,6 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({ getTotalArticles(query.s as string) ); - const headerMeta: MetaItemData[] = totalArticles - ? [ - { - id: 'posts-count', - label: intl.formatMessage({ - defaultMessage: 'Total:', - description: 'Page: total label', - id: 'kNBXyK', - }), - value: intl.formatMessage( - { - defaultMessage: - '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', - description: 'Page: posts count meta', - id: 'RvGb2c', - }, - { postsCount: totalArticles } - ), - }, - ] - : []; - const thematicsListTitle = intl.formatMessage({ defaultMessage: 'Thematics', description: 'SearchPage: thematics list widget title', @@ -215,7 +194,27 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({ title={title} breadcrumb={breadcrumbItems} breadcrumbSchema={breadcrumbSchema} - headerMeta={headerMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Total:', + description: 'Page: total label', + id: 'kNBXyK', + })} + value={intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', + description: 'Page: posts count meta', + id: 'RvGb2c', + }, + { postsCount: totalArticles } + )} + /> + </MetaList> + } widgets={[ <LinksWidget heading={ diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx index a475df9..d9734a3 100644 --- a/src/pages/sujet/[slug].tsx +++ b/src/pages/sujet/[slug].tsx @@ -10,10 +10,11 @@ import { getLayout, Heading, LinksWidget, - type MetaItemData, PageLayout, PostsList, Time, + MetaList, + MetaItem, } from '../../components'; import { getAllTopicsSlugs, @@ -61,62 +62,6 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ url: `${ROUTES.TOPICS}/${slug}`, }); - const headerMeta: (MetaItemData | undefined)[] = [ - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'TopicPage: publication date label', - id: 'KV+NMZ', - }), - value: <Time date={dates.publication} />, - }, - dates.update - ? { - id: 'update-date', - label: intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'TopicPage: update date label', - id: '9DfuHk', - }), - value: <Time date={dates.update} />, - } - : undefined, - officialWebsite - ? { - id: 'website', - label: intl.formatMessage({ - defaultMessage: 'Official website:', - description: 'TopicPage: official website label', - id: 'zoifQd', - }), - value: officialWebsite, - } - : undefined, - articles?.length - ? { - id: 'total', - label: intl.formatMessage({ - defaultMessage: 'Total:', - description: 'TopicPage: total label', - id: 'tBX4mb', - }), - value: intl.formatMessage( - { - defaultMessage: - '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', - description: 'TopicPage: posts count meta', - id: 'uAL4iW', - }, - { postsCount: articles.length } - ), - } - : undefined, - ]; - const filteredMeta = headerMeta.filter( - (item): item is MetaItemData => !!item - ); - const { asPath } = useRouter(); const webpageSchema = getWebPageSchema({ description: seo.description, @@ -181,7 +126,60 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ breadcrumbSchema={breadcrumbSchema} title={getPageHeading()} intro={intro} - headerMeta={filteredMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Page: publication date label', + id: '4QbTDq', + })} + value={<Time date={dates.publication} />} + /> + {dates.update ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Page: update date label', + id: 'Ez8Qim', + })} + value={<Time date={dates.update} />} + /> + ) : null} + {officialWebsite ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Official website:', + description: 'TopicPage: official website label', + id: 'zoifQd', + })} + value={officialWebsite} + /> + ) : null} + {articles ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Total:', + description: 'ThematicPage: total label', + id: 'lHkta9', + })} + value={intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', + description: 'ThematicPage: posts count meta', + id: 'iv3Ex1', + }, + { postsCount: articles.length } + )} + /> + ) : null} + </MetaList> + } widgets={ thematics ? [ diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx index ea8c6b0..9220ccd 100644 --- a/src/pages/thematique/[slug].tsx +++ b/src/pages/thematique/[slug].tsx @@ -9,10 +9,11 @@ import { getLayout, Heading, LinksWidget, - type MetaItemData, PageLayout, PostsList, Time, + MetaList, + MetaItem, } from '../../components'; import { getAllThematicsSlugs, @@ -53,51 +54,6 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ url: `${ROUTES.THEMATICS.INDEX}/${slug}`, }); - const headerMeta: (MetaItemData | undefined)[] = [ - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'ThematicPage: publication date label', - id: 'UTGhUU', - }), - value: <Time date={dates.publication} />, - }, - dates.update - ? { - id: 'update-date', - label: intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'ThematicPage: update date label', - id: '24FIsG', - }), - value: <Time date={dates.update} />, - } - : undefined, - articles - ? { - id: 'total', - label: intl.formatMessage({ - defaultMessage: 'Total:', - description: 'ThematicPage: total label', - id: 'lHkta9', - }), - value: intl.formatMessage( - { - defaultMessage: - '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', - description: 'ThematicPage: posts count meta', - id: 'iv3Ex1', - }, - { postsCount: articles.length } - ), - } - : undefined, - ]; - const filteredMeta = headerMeta.filter( - (item): item is MetaItemData => !!item - ); - const { asPath } = useRouter(); const webpageSchema = getWebPageSchema({ description: seo.description, @@ -154,7 +110,49 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ breadcrumbSchema={breadcrumbSchema} title={title} intro={intro} - headerMeta={filteredMeta} + headerMeta={ + <MetaList> + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Page: publication date label', + id: '4QbTDq', + })} + value={<Time date={dates.publication} />} + /> + {dates.update ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Page: update date label', + id: 'Ez8Qim', + })} + value={<Time date={dates.update} />} + /> + ) : null} + {articles ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Total:', + description: 'ThematicPage: total label', + id: 'lHkta9', + })} + value={intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', + description: 'ThematicPage: posts count meta', + id: 'iv3Ex1', + }, + { postsCount: articles.length } + )} + /> + ) : null} + </MetaList> + } widgets={ topics ? [ |
