aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-09 18:19:38 +0200
committerArmand Philippot <git@armandphilippot.com>2022-05-09 19:41:02 +0200
commit0d59a6d2995b4119865271ed1908ede0bb96497c (patch)
tree67688e41b7aa253aa58cc08aa360431b07382f9d /src/components/molecules
parent339c6957fe92c4ec1809159f09c55201d3794c18 (diff)
refactor: rewrite DescriptionList and Meta components
The meta can have different layout. The previous implementation was not enough to easily change the layout. Also, I prefer to restrict the meta types and it prevents me to repeat myself for the labels.
Diffstat (limited to 'src/components/molecules')
-rw-r--r--src/components/molecules/layout/card.module.scss39
-rw-r--r--src/components/molecules/layout/card.stories.tsx38
-rw-r--r--src/components/molecules/layout/card.test.tsx13
-rw-r--r--src/components/molecules/layout/card.tsx16
-rw-r--r--src/components/molecules/layout/meta.module.scss18
-rw-r--r--src/components/molecules/layout/meta.stories.tsx57
-rw-r--r--src/components/molecules/layout/meta.test.tsx22
-rw-r--r--src/components/molecules/layout/meta.tsx304
-rw-r--r--src/components/molecules/layout/page-footer.stories.tsx12
-rw-r--r--src/components/molecules/layout/page-footer.tsx4
-rw-r--r--src/components/molecules/layout/page-header.stories.tsx34
-rw-r--r--src/components/molecules/layout/page-header.tsx25
12 files changed, 450 insertions, 132 deletions
diff --git a/src/components/molecules/layout/card.module.scss b/src/components/molecules/layout/card.module.scss
index d5b9836..3af8f24 100644
--- a/src/components/molecules/layout/card.module.scss
+++ b/src/components/molecules/layout/card.module.scss
@@ -52,24 +52,35 @@
margin-bottom: var(--spacing-md);
}
- .items {
- flex-flow: row wrap;
- place-content: center;
- gap: var(--spacing-2xs);
- }
+ .meta {
+ &__item {
+ flex-flow: row wrap;
+ place-content: center;
+ gap: var(--spacing-2xs);
+ margin: auto;
+ }
+
+ &__label {
+ flex: 0 0 100%;
+ }
- .term {
- flex: 0 0 100%;
+ &__value {
+ padding: fun.convert-px(2) var(--spacing-xs);
+ border: fun.convert-px(1) solid var(--color-primary-darker);
+ color: var(--color-fg);
+ font-weight: 400;
+
+ &::before {
+ display: none;
+ }
+ }
}
- .description {
- padding: fun.convert-px(2) var(--spacing-xs);
- border: fun.convert-px(1) solid var(--color-primary-darker);
- color: var(--color-fg);
- font-weight: 400;
+ &:not(:disabled):focus {
+ text-decoration: none;
- &::before {
- display: none;
+ .title {
+ text-decoration: underline solid var(--color-primary) 0.3ex;
}
}
}
diff --git a/src/components/molecules/layout/card.stories.tsx b/src/components/molecules/layout/card.stories.tsx
index ed78d00..2e99bbb 100644
--- a/src/components/molecules/layout/card.stories.tsx
+++ b/src/components/molecules/layout/card.stories.tsx
@@ -8,6 +8,19 @@ export default {
title: 'Molecules/Layout/Card',
component: Card,
argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the card wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
cover: {
description: 'The card cover data (src, dimensions, alternative text).',
table: {
@@ -19,6 +32,21 @@ export default {
value: {},
},
},
+ coverFit: {
+ control: {
+ type: 'select',
+ },
+ description: 'The cover fit.',
+ options: ['contain', 'cover', 'fill', 'scale-down'],
+ table: {
+ category: 'Options',
+ defaultValue: { summary: 'cover' },
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
meta: {
description: 'The card metadata (a publication date for example).',
table: {
@@ -88,13 +116,9 @@ const cover = {
unoptimized: true,
};
-const meta = [
- {
- id: 'an-id',
- term: 'Voluptates',
- value: ['Autem', 'Eos'],
- },
-];
+const meta = {
+ thematics: ['Autem', 'Eos'],
+};
/**
* Card Stories - Default
diff --git a/src/components/molecules/layout/card.test.tsx b/src/components/molecules/layout/card.test.tsx
index 404bc7a..07c01e9 100644
--- a/src/components/molecules/layout/card.test.tsx
+++ b/src/components/molecules/layout/card.test.tsx
@@ -8,13 +8,10 @@ const cover = {
width: 640,
};
-const meta = [
- {
- id: 'an-id',
- term: 'Voluptates',
- value: ['Autem', 'Eos'],
- },
-];
+const meta = {
+ author: 'Possimus',
+ thematics: ['Autem', 'Eos'],
+};
const tagline = 'Ut rerum incidunt';
@@ -47,6 +44,6 @@ describe('Card', () => {
it('renders some meta', () => {
render(<Card title={title} titleLevel={2} url={url} meta={meta} />);
- expect(screen.getByText(meta[0].term)).toBeInTheDocument();
+ expect(screen.getByText(meta.author)).toBeInTheDocument();
});
});
diff --git a/src/components/molecules/layout/card.tsx b/src/components/molecules/layout/card.tsx
index 89f100e..e416bd5 100644
--- a/src/components/molecules/layout/card.tsx
+++ b/src/components/molecules/layout/card.tsx
@@ -1,13 +1,11 @@
import ButtonLink from '@components/atoms/buttons/button-link';
import Heading, { type HeadingLevel } from '@components/atoms/headings/heading';
-import DescriptionList, {
- type DescriptionListItem,
-} from '@components/atoms/lists/description-list';
import { FC } from 'react';
import ResponsiveImage, {
type ResponsiveImageProps,
} from '../images/responsive-image';
import styles from './card.module.scss';
+import Meta, { type MetaData } from './meta';
export type Cover = {
/**
@@ -44,7 +42,7 @@ export type CardProps = {
/**
* The card meta.
*/
- meta?: DescriptionListItem[];
+ meta?: MetaData;
/**
* The card tagline.
*/
@@ -96,13 +94,13 @@ const Card: FC<CardProps> = ({
<div className={styles.tagline}>{tagline}</div>
{meta && (
<footer className={styles.footer}>
- <DescriptionList
- items={meta}
+ <Meta
+ data={meta}
layout="inline"
className={styles.list}
- groupClassName={styles.items}
- termClassName={styles.term}
- descriptionClassName={styles.description}
+ groupClassName={styles.meta__item}
+ labelClassName={styles.meta__label}
+ valueClassName={styles.meta__value}
/>
</footer>
)}
diff --git a/src/components/molecules/layout/meta.module.scss b/src/components/molecules/layout/meta.module.scss
index 0485545..4194a6e 100644
--- a/src/components/molecules/layout/meta.module.scss
+++ b/src/components/molecules/layout/meta.module.scss
@@ -1,23 +1,5 @@
@use "@styles/abstracts/mixins" as mix;
-.list {
- display: grid;
- grid-template-columns: repeat(1, minmax(0, 1fr));
- gap: var(--spacing-sm);
-
- @include mix.media("screen") {
- @include mix.dimensions("2xs") {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-
- @include mix.dimensions("sm") {
- display: flex;
- flex-flow: column nowrap;
- gap: var(--spacing-2xs);
- }
- }
-}
-
.value {
word-break: break-all;
}
diff --git a/src/components/molecules/layout/meta.stories.tsx b/src/components/molecules/layout/meta.stories.tsx
index 0323f90..a1755a0 100644
--- a/src/components/molecules/layout/meta.stories.tsx
+++ b/src/components/molecules/layout/meta.stories.tsx
@@ -1,5 +1,5 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
-import MetaComponent from './meta';
+import MetaComponent, { MetaData } from './meta';
/**
* Meta - Storybook Meta
@@ -8,25 +8,41 @@ export default {
title: 'Molecules/Layout',
component: MetaComponent,
argTypes: {
- className: {
+ data: {
+ description: 'The page metadata.',
+ type: {
+ name: 'object',
+ required: true,
+ value: {},
+ },
+ },
+ itemsLayout: {
control: {
- type: 'text',
+ type: 'select',
},
- description: 'Set additional classnames to the meta wrapper.',
+ description: 'The items layout.',
+ options: ['inline', 'inline-values', 'stacked'],
table: {
- category: 'Styles',
+ category: 'Options',
+ defaultValue: { summary: 'inline-values' },
},
type: {
name: 'string',
required: false,
},
},
- data: {
- description: 'The page metadata.',
+ withSeparator: {
+ control: {
+ type: 'boolean',
+ },
+ description: 'Add a slash as separator between multiple values.',
+ table: {
+ category: 'Options',
+ defaultValue: { summary: true },
+ },
type: {
- name: 'object',
+ name: 'boolean',
required: true,
- value: {},
},
},
},
@@ -36,19 +52,16 @@ const Template: ComponentStory<typeof MetaComponent> = (args) => (
<MetaComponent {...args} />
);
-const data = {
- publication: { name: 'Published on:', value: 'April 9th 2022' },
- categories: {
- name: 'Categories:',
- value: [
- <a key="category1" href="#">
- Category 1
- </a>,
- <a key="category2" href="#">
- Category 2
- </a>,
- ],
- },
+const data: MetaData = {
+ publication: { date: '2022-04-09', time: '01:04:00' },
+ thematics: [
+ <a key="category1" href="#">
+ Category 1
+ </a>,
+ <a key="category2" href="#">
+ Category 2
+ </a>,
+ ],
};
/**
diff --git a/src/components/molecules/layout/meta.test.tsx b/src/components/molecules/layout/meta.test.tsx
index a738bdb..fe66d97 100644
--- a/src/components/molecules/layout/meta.test.tsx
+++ b/src/components/molecules/layout/meta.test.tsx
@@ -1,8 +1,24 @@
-import { render } from '@test-utils';
+import { render, screen } from '@test-utils';
+import { getFormattedDate } from '@utils/helpers/dates';
import Meta from './meta';
+const data = {
+ publication: { date: '2022-04-09' },
+ thematics: [
+ <a key="category1" href="#">
+ Category 1
+ </a>,
+ <a key="category2" href="#">
+ Category 2
+ </a>,
+ ],
+};
+
describe('Meta', () => {
- it('renders a Meta component', () => {
- render(<Meta data={{}} />);
+ it('format a date string', () => {
+ render(<Meta data={data} />);
+ expect(
+ screen.getByText(getFormattedDate(data.publication.date))
+ ).toBeInTheDocument();
});
});
diff --git a/src/components/molecules/layout/meta.tsx b/src/components/molecules/layout/meta.tsx
index d05396e..1401ac4 100644
--- a/src/components/molecules/layout/meta.tsx
+++ b/src/components/molecules/layout/meta.tsx
@@ -1,67 +1,312 @@
+import Link from '@components/atoms/links/link';
import DescriptionList, {
type DescriptionListProps,
type DescriptionListItem,
} from '@components/atoms/lists/description-list';
+import { getFormattedDate, getFormattedTime } from '@utils/helpers/dates';
import { FC, ReactNode } from 'react';
-import styles from './meta.module.scss';
+import { useIntl } from 'react-intl';
-export type MetaItem = {
+export type CustomMeta = {
+ label: string;
+ value: ReactNode | ReactNode[];
+};
+
+export type MetaDate = {
/**
- * The meta name.
+ * A date string. Ex: `2022-04-30`.
*/
- name: string;
+ date: string;
/**
- * The meta value.
+ * A time string. Ex: `10:25:59`.
*/
- value: ReactNode | ReactNode[];
-};
-
-export type MetaMap = {
- [key: string]: MetaItem | undefined;
+ time?: string;
+ /**
+ * Wrap the date with a link to the given target.
+ */
+ target?: string;
};
-export type MetaProps = {
+export type MetaData = {
+ /**
+ * The author name.
+ */
+ author?: string;
+ /**
+ * The comments count.
+ */
+ commentsCount?: string | JSX.Element;
+ /**
+ * The creation date.
+ */
+ creation?: MetaDate;
+ /**
+ * A custom label/value metadata.
+ */
+ custom?: CustomMeta;
+ /**
+ * The license name.
+ */
+ license?: string;
+ /**
+ * The popularity.
+ */
+ popularity?: string | JSX.Element;
+ /**
+ * The publication date.
+ */
+ publication?: MetaDate;
+ /**
+ * The estimated reading time.
+ */
+ readingTime?: string | JSX.Element;
+ /**
+ * An array of repositories.
+ */
+ repositories?: string[] | JSX.Element[];
+ /**
+ * An array of technologies.
+ */
+ technologies?: string[];
/**
- * Set additional classnames to the meta wrapper.
+ * An array of thematics.
*/
- className?: DescriptionListProps['className'];
+ thematics?: string[] | JSX.Element[];
+ /**
+ * An array of thematics.
+ */
+ topics?: string[] | JSX.Element[];
+ /**
+ * A total.
+ */
+ total?: string;
+ /**
+ * The update date.
+ */
+ update?: MetaDate;
+};
+
+export type MetaProps = Omit<
+ DescriptionListProps,
+ 'items' | 'withSeparator'
+> & {
/**
* The meta data.
*/
- data: MetaMap;
+ data: MetaData;
/**
- * The meta layout.
+ * The items layout.
*/
- layout?: DescriptionListProps['layout'];
+ itemsLayout?: DescriptionListItem['layout'];
/**
- * Determine if the layout should be responsive.
+ * If true, use a slash to delimitate multiple values. Default: true.
*/
- responsiveLayout?: DescriptionListProps['responsiveLayout'];
+ withSeparator?: DescriptionListProps['withSeparator'];
};
/**
* Meta component
*
- * Renders the page metadata.
+ * Renders the given metadata.
*/
-const Meta: FC<MetaProps> = ({ className, data, ...props }) => {
+const Meta: FC<MetaProps> = ({
+ data,
+ itemsLayout = 'inline-values',
+ withSeparator = true,
+ ...props
+}) => {
+ const intl = useIntl();
+
+ /**
+ * Retrieve the item label based on its key.
+ *
+ * @param {keyof MetaData} key - The meta key.
+ * @returns {string} The item label.
+ */
+ const getLabel = (key: keyof MetaData): string => {
+ switch (key) {
+ case 'author':
+ return intl.formatMessage({
+ defaultMessage: 'Written by:',
+ id: 'OI0N37',
+ description: 'Meta: author label',
+ });
+ case 'commentsCount':
+ return intl.formatMessage({
+ defaultMessage: 'Comments:',
+ id: 'jTVIh8',
+ description: 'Meta: comments label',
+ });
+ case 'creation':
+ return intl.formatMessage({
+ defaultMessage: 'Created on:',
+ id: 'b4fdYE',
+ description: 'Meta: creation date label',
+ });
+ case 'license':
+ return intl.formatMessage({
+ defaultMessage: 'License:',
+ id: 'AuGklx',
+ description: 'Meta: license label',
+ });
+ case 'popularity':
+ return intl.formatMessage({
+ defaultMessage: 'Popularity:',
+ id: 'pWTj2W',
+ description: 'Meta: popularity label',
+ });
+ case 'publication':
+ return intl.formatMessage({
+ defaultMessage: 'Published on:',
+ id: 'QGi5uD',
+ description: 'Meta: publication date label',
+ });
+ case 'readingTime':
+ return intl.formatMessage({
+ defaultMessage: 'Reading time:',
+ id: 'EbFvsM',
+ description: 'Meta: reading time label',
+ });
+ case 'repositories':
+ return intl.formatMessage({
+ defaultMessage: 'Repositories:',
+ id: 'DssFG1',
+ description: 'Meta: repositories label',
+ });
+ case 'technologies':
+ return intl.formatMessage({
+ defaultMessage: 'Technologies:',
+ id: 'ADQmDF',
+ description: 'Meta: technologies label',
+ });
+ case 'thematics':
+ return intl.formatMessage({
+ defaultMessage: 'Thematics:',
+ id: 'bz53Us',
+ description: 'Meta: thematics label',
+ });
+ case 'topics':
+ return intl.formatMessage({
+ defaultMessage: 'Topics:',
+ id: 'gJNaBD',
+ description: 'Meta: topics label',
+ });
+ case 'total':
+ return intl.formatMessage({
+ defaultMessage: 'Total:',
+ id: '92zgdp',
+ description: 'Meta: total label',
+ });
+ case 'update':
+ return intl.formatMessage({
+ defaultMessage: 'Updated on:',
+ id: 'tLC7bh',
+ description: 'Meta: update date label',
+ });
+ default:
+ return '';
+ }
+ };
+
+ /**
+ * Retrieve a formatted date (and time).
+ *
+ * @param {MetaDate} dateTime - A date object.
+ * @returns {JSX.Element} The formatted date wrapped in a time element.
+ */
+ const getDate = (dateTime: MetaDate): JSX.Element => {
+ const { date, time, target } = dateTime;
+
+ if (!dateTime.time) {
+ const isoDate = new Date(`${date}`).toISOString();
+ return target ? (
+ <Link href={target}>
+ <time dateTime={isoDate}>{getFormattedDate(dateTime.date)}</time>
+ </Link>
+ ) : (
+ <time dateTime={isoDate}>{getFormattedDate(dateTime.date)}</time>
+ );
+ }
+
+ const isoDateTime = new Date(`${date}T${time}`).toISOString();
+
+ return target ? (
+ <Link href={target}>
+ <time dateTime={isoDateTime}>
+ {intl.formatMessage(
+ {
+ defaultMessage: '{date} at {time}',
+ description: 'Meta: publication date and time',
+ id: 'fcHeyC',
+ },
+ {
+ date: getFormattedDate(dateTime.date),
+ time: getFormattedTime(`${dateTime.date}T${dateTime.time}`),
+ }
+ )}
+ </time>
+ </Link>
+ ) : (
+ <time dateTime={isoDateTime}>
+ {intl.formatMessage(
+ {
+ defaultMessage: '{date} at {time}',
+ description: 'Meta: publication date and time',
+ id: 'fcHeyC',
+ },
+ {
+ date: getFormattedDate(dateTime.date),
+ time: getFormattedTime(`${dateTime.date}T${dateTime.time}`),
+ }
+ )}
+ </time>
+ );
+ };
+
+ /**
+ * Retrieve the formatted item value.
+ *
+ * @param {keyof MetaData} key - The meta key.
+ * @param {ValueOf<MetaData>} value - The meta value.
+ * @returns {string|ReactNode|ReactNode[]} - The formatted value.
+ */
+ const getValue = <T extends keyof MetaData>(
+ key: T,
+ value: MetaData[T]
+ ): string | ReactNode | ReactNode[] => {
+ if (key === 'creation' || key === 'publication' || key === 'update') {
+ return getDate(value as MetaDate);
+ }
+ return value as string | ReactNode | ReactNode[];
+ };
+
/**
* Transform the metadata to description list item format.
*
- * @param {MetaMap} items - The meta.
+ * @param {MetaData} items - The meta.
* @returns {DescriptionListItem[]} The formatted description list items.
*/
- const getItems = (items: MetaMap): DescriptionListItem[] => {
+ const getItems = (items: MetaData): DescriptionListItem[] => {
const listItems: DescriptionListItem[] = Object.entries(items)
- .map(([key, item]) => {
- if (!item) return;
+ .map(([key, value]) => {
+ if (!key || !value) return;
- const { name, value } = item;
+ const metaKey = key as keyof MetaData;
return {
- id: key,
- term: name,
- value: Array.isArray(value) ? value : [value],
+ id: metaKey,
+ label:
+ metaKey === 'custom'
+ ? (value as CustomMeta).label
+ : getLabel(metaKey),
+ layout: itemsLayout,
+ value:
+ metaKey === 'custom'
+ ? (value as CustomMeta).value
+ : getValue(
+ metaKey,
+ value as string | string[] | JSX.Element | JSX.Element[]
+ ),
} as DescriptionListItem;
})
.filter((item): item is DescriptionListItem => !!item);
@@ -72,8 +317,7 @@ const Meta: FC<MetaProps> = ({ className, data, ...props }) => {
return (
<DescriptionList
items={getItems(data)}
- className={`${styles.list} ${className}`}
- descriptionClassName={styles.value}
+ withSeparator={withSeparator}
{...props}
/>
);
diff --git a/src/components/molecules/layout/page-footer.stories.tsx b/src/components/molecules/layout/page-footer.stories.tsx
index da0a3fa..31b7a49 100644
--- a/src/components/molecules/layout/page-footer.stories.tsx
+++ b/src/components/molecules/layout/page-footer.stories.tsx
@@ -1,4 +1,5 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
+import { MetaData } from './meta';
import PageFooterComponent from './page-footer';
/**
@@ -39,8 +40,15 @@ const Template: ComponentStory<typeof PageFooterComponent> = (args) => (
<PageFooterComponent {...args} />
);
-const meta = {
- topics: { name: 'More posts about:', value: <a href="#">Topic name</a> },
+const meta: MetaData = {
+ custom: {
+ label: 'More posts about:',
+ value: [
+ <a key="topic-1" href="#">
+ Topic name
+ </a>,
+ ],
+ },
};
/**
diff --git a/src/components/molecules/layout/page-footer.tsx b/src/components/molecules/layout/page-footer.tsx
index f522482..e998b1e 100644
--- a/src/components/molecules/layout/page-footer.tsx
+++ b/src/components/molecules/layout/page-footer.tsx
@@ -1,5 +1,5 @@
import { FC } from 'react';
-import Meta, { type MetaMap } from './meta';
+import Meta, { MetaData } from './meta';
export type PageFooterProps = {
/**
@@ -9,7 +9,7 @@ export type PageFooterProps = {
/**
* The footer metadata.
*/
- meta?: MetaMap;
+ meta?: MetaData;
};
/**
diff --git a/src/components/molecules/layout/page-header.stories.tsx b/src/components/molecules/layout/page-header.stories.tsx
index 6054845..d58f8b5 100644
--- a/src/components/molecules/layout/page-header.stories.tsx
+++ b/src/components/molecules/layout/page-header.stories.tsx
@@ -8,6 +8,19 @@ export default {
title: 'Molecules/Layout/PageHeader',
component: PageHeader,
argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the header element.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
intro: {
control: {
type: 'text',
@@ -50,18 +63,15 @@ const Template: ComponentStory<typeof PageHeader> = (args) => (
);
const meta = {
- publication: { name: 'Published on:', value: 'April 9th 2022' },
- categories: {
- name: 'Categories:',
- value: [
- <a key="category1" href="#">
- Category 1
- </a>,
- <a key="category2" href="#">
- Category 2
- </a>,
- ],
- },
+ publication: { date: '2022-04-09' },
+ thematics: [
+ <a key="category1" href="#">
+ Category 1
+ </a>,
+ <a key="category2" href="#">
+ Category 2
+ </a>,
+ ],
};
/**
diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx
index 1663085..9abe9af 100644
--- a/src/components/molecules/layout/page-header.tsx
+++ b/src/components/molecules/layout/page-header.tsx
@@ -1,7 +1,7 @@
import Heading from '@components/atoms/headings/heading';
-import styles from './page-header.module.scss';
-import Meta, { type MetaMap } from './meta';
import { FC } from 'react';
+import Meta, { type MetaData } from './meta';
+import styles from './page-header.module.scss';
export type PageHeaderProps = {
/**
@@ -15,7 +15,7 @@ export type PageHeaderProps = {
/**
* The page metadata.
*/
- meta?: MetaMap;
+ meta?: MetaData;
/**
* The page title.
*/
@@ -33,14 +33,29 @@ const PageHeader: FC<PageHeaderProps> = ({
meta,
title,
}) => {
+ const getIntro = () => {
+ return typeof intro === 'string' ? (
+ <div dangerouslySetInnerHTML={{ __html: intro }} />
+ ) : (
+ <div>{intro}</div>
+ );
+ };
+
return (
<header className={`${styles.wrapper} ${className}`}>
<div className={styles.body}>
<Heading level={1} className={styles.title} withMargin={false}>
{title}
</Heading>
- {meta && <Meta data={meta} className={styles.meta} layout="inline" />}
- {intro && <div>{intro}</div>}
+ {meta && (
+ <Meta
+ data={meta}
+ className={styles.meta}
+ layout="column"
+ itemsLayout="inline"
+ />
+ )}
+ {intro && getIntro()}
</div>
</header>
);