aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/templates
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-16 12:46:38 +0200
committerArmand Philippot <git@armandphilippot.com>2022-05-16 12:46:38 +0200
commit2155550fa36a3bc3c8f66e0926530123b4018cd4 (patch)
tree1b7472d7ceeb9c95b2c6de6440b48b94405e155e /src/components/templates
parent8a55aa83bd4b64d1d989cb49b7d9c3fdc1cc6ea5 (diff)
refactor: use custom hook for breadcrumb items and schema
Diffstat (limited to 'src/components/templates')
-rw-r--r--src/components/templates/layout/layout.stories.tsx54
-rw-r--r--src/components/templates/layout/layout.test.tsx12
-rw-r--r--src/components/templates/layout/layout.tsx26
-rw-r--r--src/components/templates/page/page-layout.stories.tsx12
-rw-r--r--src/components/templates/page/page-layout.test.tsx41
-rw-r--r--src/components/templates/page/page-layout.tsx11
-rw-r--r--src/components/templates/sectioned/sectioned-layout.stories.tsx24
-rw-r--r--src/components/templates/sectioned/sectioned-layout.test.tsx9
-rw-r--r--src/components/templates/sectioned/sectioned-layout.tsx8
9 files changed, 149 insertions, 48 deletions
diff --git a/src/components/templates/layout/layout.stories.tsx b/src/components/templates/layout/layout.stories.tsx
index 2415412..105e808 100644
--- a/src/components/templates/layout/layout.stories.tsx
+++ b/src/components/templates/layout/layout.stories.tsx
@@ -1,5 +1,4 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { IntlProvider } from 'react-intl';
import LayoutComponent from './layout';
/**
@@ -8,6 +7,10 @@ import LayoutComponent from './layout';
export default {
title: 'Templates/LayoutBase',
component: LayoutComponent,
+ args: {
+ breadcrumbSchema: [],
+ isHome: false,
+ },
argTypes: {
children: {
control: {
@@ -19,6 +22,31 @@ export default {
required: true,
},
},
+ breadcrumbSchema: {
+ control: {
+ type: 'null',
+ },
+ description: 'The JSON schema for breadcrumb items.',
+ type: {
+ name: 'object',
+ required: true,
+ value: {},
+ },
+ },
+ isHome: {
+ control: {
+ type: 'boolean',
+ },
+ description: 'Determine if it is the homepage.',
+ table: {
+ category: 'Options',
+ defaultValue: { summary: false },
+ },
+ type: {
+ name: 'boolean',
+ required: false,
+ },
+ },
className: {
control: {
type: 'text',
@@ -35,19 +63,17 @@ export default {
},
decorators: [
(Story) => (
- <IntlProvider locale="en">
- <div
- id="__next"
- style={{
- flex: 1,
- display: 'flex',
- flexFlow: 'column nowrap',
- minHeight: '100vh',
- }}
- >
- <Story />
- </div>
- </IntlProvider>
+ <div
+ id="__next"
+ style={{
+ flex: 1,
+ display: 'flex',
+ flexFlow: 'column nowrap',
+ minHeight: '100vh',
+ }}
+ >
+ <Story />
+ </div>
),
],
parameters: {
diff --git a/src/components/templates/layout/layout.test.tsx b/src/components/templates/layout/layout.test.tsx
index 914e1cd..94145ec 100644
--- a/src/components/templates/layout/layout.test.tsx
+++ b/src/components/templates/layout/layout.test.tsx
@@ -1,34 +1,36 @@
import { render, screen } from '@test-utils';
+import { BreadcrumbList } from 'schema-dts';
import Layout from './layout';
const body =
'Sit dolorem eveniet. Sit sit odio nemo vitae corrupti modi sint est rerum. Pariatur quidem maiores distinctio. Quia et illum aspernatur est cum.';
+const breadcrumbSchema: BreadcrumbList['itemListElement'][] = [];
describe('Layout', () => {
it('renders the website header', () => {
- render(<Layout>{body}</Layout>);
+ render(<Layout breadcrumbSchema={breadcrumbSchema}>{body}</Layout>);
expect(screen.getByRole('banner')).toBeInTheDocument();
});
it('renders the website main content', () => {
- render(<Layout>{body}</Layout>);
+ render(<Layout breadcrumbSchema={breadcrumbSchema}>{body}</Layout>);
expect(screen.getByRole('main')).toBeInTheDocument();
});
it('renders the website footer', () => {
- render(<Layout>{body}</Layout>);
+ render(<Layout breadcrumbSchema={breadcrumbSchema}>{body}</Layout>);
expect(screen.getByRole('contentinfo')).toBeInTheDocument();
});
it('renders a skip to content link', () => {
- render(<Layout>{body}</Layout>);
+ render(<Layout breadcrumbSchema={breadcrumbSchema}>{body}</Layout>);
expect(
screen.getByRole('link', { name: 'Skip to content' })
).toBeInTheDocument();
});
it('renders an article', () => {
- render(<Layout>{body}</Layout>);
+ render(<Layout breadcrumbSchema={breadcrumbSchema}>{body}</Layout>);
expect(screen.getByRole('article')).toHaveTextContent(body);
});
});
diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx
index bfb918b..9e9282b 100644
--- a/src/components/templates/layout/layout.tsx
+++ b/src/components/templates/layout/layout.tsx
@@ -13,7 +13,13 @@ import useSettings from '@utils/hooks/use-settings';
import Script from 'next/script';
import { FC, ReactNode } from 'react';
import { useIntl } from 'react-intl';
-import { Person, SearchAction, WebSite, WithContext } from 'schema-dts';
+import {
+ BreadcrumbList,
+ Person,
+ SearchAction,
+ WebSite,
+ WithContext,
+} from 'schema-dts';
import styles from './layout.module.scss';
export type QueryAction = SearchAction & {
@@ -22,6 +28,10 @@ export type QueryAction = SearchAction & {
export type LayoutProps = Pick<HeaderProps, 'isHome'> & {
/**
+ * The breadcrumb JSON schema.
+ */
+ breadcrumbSchema: BreadcrumbList['itemListElement'][];
+ /**
* The layout main content.
*/
children: ReactNode;
@@ -36,7 +46,12 @@ export type LayoutProps = Pick<HeaderProps, 'isHome'> & {
*
* Render the base layout used by all pages.
*/
-const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
+const Layout: FC<LayoutProps> = ({
+ breadcrumbSchema,
+ children,
+ isHome,
+ ...props
+}) => {
const intl = useIntl();
const { website } = useSettings();
const { baseline, copyright, locales, name, picture, url } = website;
@@ -153,12 +168,17 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
id="schema-layout"
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- ></Script>
+ />
<Script
id="schema-branding"
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(brandingSchema) }}
/>
+ <Script
+ id="schema-breadcrumb"
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
+ />
<noscript>
<div className={styles['noscript-spacing']}></div>
</noscript>
diff --git a/src/components/templates/page/page-layout.stories.tsx b/src/components/templates/page/page-layout.stories.tsx
index 8e518aa..002b951 100644
--- a/src/components/templates/page/page-layout.stories.tsx
+++ b/src/components/templates/page/page-layout.stories.tsx
@@ -16,6 +16,7 @@ export default {
component: PageLayoutComponent,
args: {
allowComments: false,
+ breadcrumbSchema: [],
},
argTypes: {
allowComments: {
@@ -40,6 +41,17 @@ export default {
value: {},
},
},
+ breadcrumbSchema: {
+ control: {
+ type: null,
+ },
+ description: 'The JSON schema for breadcrumb items.',
+ type: {
+ name: 'object',
+ required: true,
+ value: {},
+ },
+ },
children: {
control: {
type: 'text',
diff --git a/src/components/templates/page/page-layout.test.tsx b/src/components/templates/page/page-layout.test.tsx
index b8fff6a..b3aea8b 100644
--- a/src/components/templates/page/page-layout.test.tsx
+++ b/src/components/templates/page/page-layout.test.tsx
@@ -1,4 +1,5 @@
import { render, screen } from '@test-utils';
+import { BreadcrumbList } from 'schema-dts';
import PageLayout from './page-layout';
const title = 'Incidunt ad earum';
@@ -6,13 +7,18 @@ const breadcrumb = [
{ id: 'home', url: '#', name: 'Home' },
{ id: 'page', url: '#', name: title },
];
+const breadcrumbSchema: BreadcrumbList['itemListElement'][] = [];
const children =
'Reprehenderit aut quis aperiam magnam quia id. Vero enim animi placeat quia. Laborum sit odio minima. Dolores et debitis eaque iste quidem. Omnis aliquam illum porro ea non. Quaerat totam iste quos ex facilis officia accusantium.';
describe('PageLayout', () => {
it('renders the page title', () => {
render(
- <PageLayout breadcrumb={breadcrumb} title={title}>
+ <PageLayout
+ breadcrumb={breadcrumb}
+ breadcrumbSchema={breadcrumbSchema}
+ title={title}
+ >
{children}
</PageLayout>
);
@@ -23,7 +29,11 @@ describe('PageLayout', () => {
it('renders the page content', () => {
render(
- <PageLayout breadcrumb={breadcrumb} title={title}>
+ <PageLayout
+ breadcrumb={breadcrumb}
+ breadcrumbSchema={breadcrumbSchema}
+ title={title}
+ >
{children}
</PageLayout>
);
@@ -32,7 +42,11 @@ describe('PageLayout', () => {
it('renders the breadcrumb', () => {
render(
- <PageLayout breadcrumb={breadcrumb} title={title}>
+ <PageLayout
+ breadcrumb={breadcrumb}
+ breadcrumbSchema={breadcrumbSchema}
+ title={title}
+ >
{children}
</PageLayout>
);
@@ -43,7 +57,12 @@ describe('PageLayout', () => {
it('renders the table of contents', () => {
render(
- <PageLayout breadcrumb={breadcrumb} title={title} withToC={true}>
+ <PageLayout
+ breadcrumb={breadcrumb}
+ breadcrumbSchema={breadcrumbSchema}
+ title={title}
+ withToC={true}
+ >
{children}
</PageLayout>
);
@@ -54,7 +73,12 @@ describe('PageLayout', () => {
it('renders the comment form', () => {
render(
- <PageLayout breadcrumb={breadcrumb} title={title} allowComments={true}>
+ <PageLayout
+ breadcrumb={breadcrumb}
+ breadcrumbSchema={breadcrumbSchema}
+ title={title}
+ allowComments={true}
+ >
{children}
</PageLayout>
);
@@ -79,7 +103,12 @@ describe('PageLayout', () => {
},
];
render(
- <PageLayout breadcrumb={breadcrumb} title={title} comments={comments}>
+ <PageLayout
+ breadcrumb={breadcrumb}
+ breadcrumbSchema={breadcrumbSchema}
+ title={title}
+ comments={comments}
+ >
{children}
</PageLayout>
);
diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx
index 4e4ff00..bc90f4c 100644
--- a/src/components/templates/page/page-layout.tsx
+++ b/src/components/templates/page/page-layout.tsx
@@ -25,7 +25,10 @@ import { useIntl } from 'react-intl';
import Layout, { type LayoutProps } from '../layout/layout';
import styles from './page-layout.module.scss';
-export type PageLayoutProps = {
+export type PageLayoutProps = Pick<
+ LayoutProps,
+ 'breadcrumbSchema' | 'isHome'
+> & {
/**
* True if the page accepts new comments. Default: false.
*/
@@ -59,10 +62,6 @@ export type PageLayoutProps = {
*/
intro?: PageHeaderProps['intro'];
/**
- * True if it is homepage. Default: false.
- */
- isHome?: LayoutProps['isHome'];
- /**
* The page title.
*/
title: PageHeaderProps['title'];
@@ -85,6 +84,7 @@ const PageLayout: FC<PageLayoutProps> = ({
children,
allowComments = false,
breadcrumb,
+ breadcrumbSchema,
comments,
footerMeta,
headerMeta,
@@ -170,6 +170,7 @@ const PageLayout: FC<PageLayoutProps> = ({
return (
<Layout
+ breadcrumbSchema={breadcrumbSchema}
isHome={isHome}
className={`${styles.article} ${styles[articleModifier]}`}
>
diff --git a/src/components/templates/sectioned/sectioned-layout.stories.tsx b/src/components/templates/sectioned/sectioned-layout.stories.tsx
index 9ff3b75..ce31a83 100644
--- a/src/components/templates/sectioned/sectioned-layout.stories.tsx
+++ b/src/components/templates/sectioned/sectioned-layout.stories.tsx
@@ -1,5 +1,4 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { IntlProvider } from 'react-intl';
import SectionedLayoutComponent from './sectioned-layout';
/**
@@ -8,7 +7,21 @@ import SectionedLayoutComponent from './sectioned-layout';
export default {
title: 'Templates/Sectioned',
component: SectionedLayoutComponent,
+ args: {
+ breadcrumbSchema: [],
+ },
argTypes: {
+ breadcrumbSchema: {
+ control: {
+ type: null,
+ },
+ description: 'The JSON schema for breadcrumb items.',
+ type: {
+ name: 'object',
+ required: true,
+ value: {},
+ },
+ },
sections: {
description: 'The different sections.',
type: {
@@ -18,15 +31,6 @@ export default {
},
},
},
- decorators: [
- (Story) => (
- <IntlProvider locale="en">
- <div id="__next">
- <Story />
- </div>
- </IntlProvider>
- ),
- ],
parameters: {
layout: 'fullscreen',
},
diff --git a/src/components/templates/sectioned/sectioned-layout.test.tsx b/src/components/templates/sectioned/sectioned-layout.test.tsx
index 334d1cc..9b8bab5 100644
--- a/src/components/templates/sectioned/sectioned-layout.test.tsx
+++ b/src/components/templates/sectioned/sectioned-layout.test.tsx
@@ -1,6 +1,8 @@
import { render, screen } from '@test-utils';
+import { BreadcrumbList } from 'schema-dts';
import SectionedLayout from './sectioned-layout';
+const breadcrumbSchema: BreadcrumbList['itemListElement'][] = [];
const sections = [
{
title: 'Section 1',
@@ -26,7 +28,12 @@ const sections = [
describe('SectionedLayout', () => {
it('renders the correct number of section', () => {
- render(<SectionedLayout sections={sections} />);
+ render(
+ <SectionedLayout
+ breadcrumbSchema={breadcrumbSchema}
+ sections={sections}
+ />
+ );
expect(screen.getAllByRole('heading', { name: /^Section/ })).toHaveLength(
sections.length
);
diff --git a/src/components/templates/sectioned/sectioned-layout.tsx b/src/components/templates/sectioned/sectioned-layout.tsx
index 36ca039..58d5ad0 100644
--- a/src/components/templates/sectioned/sectioned-layout.tsx
+++ b/src/components/templates/sectioned/sectioned-layout.tsx
@@ -3,11 +3,11 @@ import Section, {
type SectionVariant,
} from '@components/atoms/layout/section';
import { FC } from 'react';
-import Layout from '../layout/layout';
+import Layout, { type LayoutProps } from '../layout/layout';
export type Section = Pick<SectionProps, 'content' | 'title'>;
-export type SectionedLayoutProps = {
+export type SectionedLayoutProps = Pick<LayoutProps, 'breadcrumbSchema'> & {
/**
* An array of objects describing each section.
*/
@@ -19,7 +19,7 @@ export type SectionedLayoutProps = {
*
* Render a sectioned layout.
*/
-const SectionedLayout: FC<SectionedLayoutProps> = ({ sections }) => {
+const SectionedLayout: FC<SectionedLayoutProps> = ({ sections, ...props }) => {
const getSections = (items: SectionProps[]) => {
return items.map((section, index) => {
const variant: SectionVariant = index % 2 ? 'light' : 'dark';
@@ -37,7 +37,7 @@ const SectionedLayout: FC<SectionedLayoutProps> = ({ sections }) => {
});
};
- return <Layout>{getSections(sections)}</Layout>;
+ return <Layout {...props}>{getSections(sections)}</Layout>;
};
export default SectionedLayout;