From 85c4c42bd601270d7be0f34a0767a34bb85e29bb Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Tue, 12 Dec 2023 18:50:03 +0100 Subject: refactor(hooks): rewrite useBreadcrumbs hook * use next/router to get the slug instead of using props * handle cases where the current page title is not provided * update JSON-LD schema to match the example in documentation * add tests --- .../nav/breadcrumbs/breadcrumbs.stories.tsx | 12 +- .../organisms/nav/breadcrumbs/breadcrumbs.test.tsx | 6 +- .../organisms/nav/breadcrumbs/breadcrumbs.tsx | 12 +- src/components/templates/page/page.stories.tsx | 4 +- src/components/templates/page/page.test.tsx | 4 +- src/i18n/en.json | 38 +++- src/i18n/fr.json | 38 +++- src/pages/404.tsx | 13 +- src/pages/article/[slug].tsx | 16 +- src/pages/blog/index.tsx | 25 +-- src/pages/blog/page/[number].tsx | 28 ++- src/pages/contact.tsx | 20 +- src/pages/cv.tsx | 22 +- src/pages/index.tsx | 15 +- src/pages/mentions-legales.tsx | 20 +- src/pages/projets/[slug].tsx | 20 +- src/pages/projets/index.tsx | 20 +- src/pages/recherche/index.tsx | 19 +- src/pages/sujet/[slug].tsx | 22 +- src/pages/thematique/[slug].tsx | 22 +- src/utils/constants.ts | 2 + src/utils/hooks/index.ts | 2 +- src/utils/hooks/use-breadcrumb.ts | 124 ----------- src/utils/hooks/use-breadcrumbs/index.ts | 1 + .../hooks/use-breadcrumbs/use-breadcrumbs.test.tsx | 232 +++++++++++++++++++++ src/utils/hooks/use-breadcrumbs/use-breadcrumbs.ts | 144 +++++++++++++ 26 files changed, 559 insertions(+), 322 deletions(-) delete mode 100644 src/utils/hooks/use-breadcrumb.ts create mode 100644 src/utils/hooks/use-breadcrumbs/index.ts create mode 100644 src/utils/hooks/use-breadcrumbs/use-breadcrumbs.test.tsx create mode 100644 src/utils/hooks/use-breadcrumbs/use-breadcrumbs.ts (limited to 'src') diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx b/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx index 4736b26..0b6fd27 100644 --- a/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx @@ -28,7 +28,7 @@ const Template: ComponentStory = (args) => ( */ export const OneItem = Template.bind({}); OneItem.args = { - items: [{ id: 'home', url: '#', name: 'Home' }], + items: [{ id: 'home', slug: '#', label: 'Home' }], }; /** @@ -37,8 +37,8 @@ OneItem.args = { export const TwoItems = Template.bind({}); TwoItems.args = { items: [ - { id: 'home', url: '#', name: 'Home' }, - { id: 'blog', url: '#', name: 'Blog' }, + { id: 'home', slug: '#', label: 'Home' }, + { id: 'blog', slug: '#', label: 'Blog' }, ], }; @@ -48,8 +48,8 @@ TwoItems.args = { export const ThreeItems = Template.bind({}); ThreeItems.args = { items: [ - { id: 'home', url: '#', name: 'Home' }, - { id: 'blog', url: '#', name: 'Blog' }, - { id: 'post1', url: '#', name: 'A Post' }, + { id: 'home', slug: '#', label: 'Home' }, + { id: 'blog', slug: '#', label: 'Blog' }, + { id: 'post1', slug: '#', label: 'A Post' }, ], }; diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx b/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx index 40bb1b8..ab72a31 100644 --- a/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx @@ -3,9 +3,9 @@ import { render, screen as rtlScreen } from '@testing-library/react'; import { Breadcrumbs, type BreadcrumbsItem } from './breadcrumbs'; const items: BreadcrumbsItem[] = [ - { id: 'home', url: '#', name: 'Home' }, - { id: 'blog', url: '#', name: 'Blog' }, - { id: 'post1', url: '#', name: 'A Post' }, + { id: 'home', slug: '#', label: 'Home' }, + { id: 'blog', slug: '#', label: 'Blog' }, + { id: 'post1', slug: '#', label: 'A Post' }, ]; describe('Breadcrumbs', () => { diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx b/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx index b6d3843..13434e1 100644 --- a/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx @@ -9,13 +9,13 @@ export type BreadcrumbsItem = { */ id: string; /** - * The item URL. + * The item label. */ - url: string; + label: string; /** - * The item name. + * The item slug. */ - name: string; + slug: string; }; export type BreadcrumbsProps = Omit & { @@ -46,10 +46,10 @@ const BreadcrumbsWithRef: ForwardRefRenderFunction< return ( {isLastItem ? ( - {item.name} + {item.label} ) : ( <> - + {sep} diff --git a/src/components/templates/page/page.stories.tsx b/src/components/templates/page/page.stories.tsx index 8b1616b..3f03b44 100644 --- a/src/components/templates/page/page.stories.tsx +++ b/src/components/templates/page/page.stories.tsx @@ -173,8 +173,8 @@ HeaderBody.args = { export const BreadcrumbsHeaderBody = Template.bind({}); BreadcrumbsHeaderBody.args = { breadcrumbs: [ - { id: 'home', name: 'Home', url: '#home' }, - { id: 'blog', name: 'Blog', url: '#blog' }, + { id: 'home', label: 'Home', slug: '#home' }, + { id: 'blog', label: 'Blog', slug: '#blog' }, ], children: ( <> diff --git a/src/components/templates/page/page.test.tsx b/src/components/templates/page/page.test.tsx index fb06cb1..afe93ce 100644 --- a/src/components/templates/page/page.test.tsx +++ b/src/components/templates/page/page.test.tsx @@ -24,8 +24,8 @@ describe('Page', () => { const body = 'Consequatur deleniti eligendi quidem sint et nobis ut qui. Dolores modi eos. Cupiditate aliquid sunt consequatur voluptatem laudantium.'; const breadcrumbs = [ - { id: 'home', name: 'Home', url: '#home' }, - { id: 'blog', name: 'Blog', url: '#blog' }, + { id: 'home', label: 'Home', slug: '#home' }, + { id: 'blog', label: 'Blog', slug: '#blog' }, ] satisfies BreadcrumbsItem[]; render( diff --git a/src/i18n/en.json b/src/i18n/en.json index 248c7db..f971c93 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -19,6 +19,10 @@ "defaultMessage": "Related topics", "description": "ThematicPage: related topics list widget title" }, + "/5tytV": { + "defaultMessage": "Page {number}", + "description": "UseBreadcrumbs: paginated route label" + }, "/EfcyW": { "defaultMessage": "It is now awaiting moderation.", "description": "PageComments: comment awaiting moderation" @@ -79,10 +83,6 @@ "defaultMessage": "Gitlab", "description": "ProjectPage: Gitlab repo label" }, - "28GZdv": { - "defaultMessage": "Projects", - "description": "Breadcrumb: projects label" - }, "2By3AZ": { "defaultMessage": "Open menu", "description": "SiteNavbar: main nav button label in navbar" @@ -231,9 +231,9 @@ "defaultMessage": "Loading the repository metadata...", "description": "ProjectPage: loading repository metadata" }, - "Es52wh": { - "defaultMessage": "Blog", - "description": "Breadcrumb: blog label" + "EH+dam": { + "defaultMessage": "404: Not found", + "description": "UseBreadcrumbs: page not found label" }, "FCpPCm": { "defaultMessage": "Comments:", @@ -307,6 +307,10 @@ "defaultMessage": "Skip to content", "description": "Layout: Skip to content link" }, + "K6aSZi": { + "defaultMessage": "Blog", + "description": "UseBreadcrumbs: blog label" + }, "KVSWGP": { "defaultMessage": "Other thematics", "description": "ThematicPage: other thematics list widget title" @@ -491,6 +495,10 @@ "defaultMessage": "{topicsCount, plural, =0 {Topics:} one {Topic:} other {Topics:}}", "description": "PostPreviewMeta: topics label" }, + "aZIuPO": { + "defaultMessage": "Home", + "description": "UseBreadcrumbs: home label" + }, "bAXtMT": { "defaultMessage": "{postsCount, plural, =0 {No posts} one {# post} other {# posts}}", "description": "PageHeader: total meta value" @@ -547,6 +555,10 @@ "defaultMessage": "Code blocks:", "description": "PrismThemeToggle: theme label" }, + "gSevGm": { + "defaultMessage": "Search results for \"{query}\"", + "description": "UseBreadcrumbs: search results label" + }, "gYbxP4": { "defaultMessage": "The comments are loading...", "description": "LoadingPageComments: loading message" @@ -563,14 +575,14 @@ "defaultMessage": "{postTitle} cover", "description": "PostPreview: an accessible name for the figure wrapping the cover" }, + "iHC3Qx": { + "defaultMessage": "Search", + "description": "UseBreadcrumbs: search label" + }, "iTLvLX": { "defaultMessage": "CC BY SA", "description": "SiteFooter: the license name" }, - "j5k9Fe": { - "defaultMessage": "Home", - "description": "Breadcrumb: home label" - }, "jJm8wd": { "defaultMessage": "Reading time:", "description": "PageHeader: reading time label" @@ -671,6 +683,10 @@ "defaultMessage": "Thematics are loading...", "description": "ThematicPage: loading thematics message" }, + "rkz8C6": { + "defaultMessage": "Projects", + "description": "UseBreadcrumbs: projects label" + }, "s57FTB": { "defaultMessage": "Share", "description": "Article: sharing widget title" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 4e8da8e..0989e07 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -19,6 +19,10 @@ "defaultMessage": "Sujets connexes", "description": "ThematicPage: related topics list widget title" }, + "/5tytV": { + "defaultMessage": "Page {number}", + "description": "UseBreadcrumbs: paginated route label" + }, "/EfcyW": { "defaultMessage": "Il est maintenant en attente de modération.", "description": "PageComments: comment awaiting moderation" @@ -79,10 +83,6 @@ "defaultMessage": "Gitlab", "description": "ProjectPage: Gitlab repo label" }, - "28GZdv": { - "defaultMessage": "Projets", - "description": "Breadcrumb: projects label" - }, "2By3AZ": { "defaultMessage": "Ouvrir le menu", "description": "SiteNavbar: main nav button label in navbar" @@ -231,9 +231,9 @@ "defaultMessage": "Chargement des métadonnées du dépôt…", "description": "ProjectPage: loading repository metadata" }, - "Es52wh": { - "defaultMessage": "Blog", - "description": "Breadcrumb: blog label" + "EH+dam": { + "defaultMessage": "404: Non trouvé", + "description": "UseBreadcrumbs: page not found label" }, "FCpPCm": { "defaultMessage": "Commentaires :", @@ -307,6 +307,10 @@ "defaultMessage": "Aller au contenu", "description": "Layout: Skip to content link" }, + "K6aSZi": { + "defaultMessage": "Blog", + "description": "UseBreadcrumbs: blog label" + }, "KVSWGP": { "defaultMessage": "Autres thématiques", "description": "ThematicPage: other thematics list widget title" @@ -491,6 +495,10 @@ "defaultMessage": "{topicsCount, plural, =0 {Sujets :} one {Sujet :} other {Sujets :}}", "description": "PostPreviewMeta: topics label" }, + "aZIuPO": { + "defaultMessage": "Accueil", + "description": "UseBreadcrumbs: home label" + }, "bAXtMT": { "defaultMessage": "{postsCount, plural, =0 {Aucun article} one {# article} other {# articles}}", "description": "PageHeader: total meta value" @@ -547,6 +555,10 @@ "defaultMessage": "Blocs de code :", "description": "PrismThemeToggle: theme label" }, + "gSevGm": { + "defaultMessage": "Résultats de la recherche pour « {query} »", + "description": "UseBreadcrumbs: search results label" + }, "gYbxP4": { "defaultMessage": "Les commentaires sont en cours de chargement…", "description": "LoadingPageComments: loading message" @@ -563,14 +575,14 @@ "defaultMessage": "Illustration de {postTitle}", "description": "PostPreview: an accessible name for the figure wrapping the cover" }, + "iHC3Qx": { + "defaultMessage": "Recherche", + "description": "UseBreadcrumbs: search label" + }, "iTLvLX": { "defaultMessage": "CC BY SA", "description": "SiteFooter: the license name" }, - "j5k9Fe": { - "defaultMessage": "Accueil", - "description": "Breadcrumb: home label" - }, "jJm8wd": { "defaultMessage": "Temps de lecture :", "description": "PageHeader: reading time label" @@ -671,6 +683,10 @@ "defaultMessage": "Les thématiques sont en cours de chargement…", "description": "ThematicPage: loading thematics message" }, + "rkz8C6": { + "defaultMessage": "Projets", + "description": "UseBreadcrumbs: projects label" + }, "s57FTB": { "defaultMessage": "Partager", "description": "Article: sharing widget title" diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 6ef0c55..450859c 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -36,7 +36,11 @@ import { CONFIG } from '../utils/config'; import { ROUTES } from '../utils/constants'; import { getLinksItemData } from '../utils/helpers'; import { loadTranslation, type Messages } from '../utils/helpers/server'; -import { useBreadcrumb, useThematicsList, useTopicsList } from '../utils/hooks'; +import { + useBreadcrumbs, + useThematicsList, + useTopicsList, +} from '../utils/hooks'; const link = (chunks: ReactNode) => {chunks}; @@ -110,10 +114,9 @@ const Error404Page: NextPageWithLayout = ({ data }) => { }), }, }; - const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ - title: messages.page.title, - url: ROUTES.NOT_FOUND, - }); + const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumbs( + messages.page.title + ); const searchSubmitHandler: SearchFormSubmit = useCallback( async ({ query }) => { diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index bd102a9..6333056 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -46,7 +46,7 @@ import { import { loadTranslation, type Messages } from '../../utils/helpers/server'; import { useArticle, - useBreadcrumb, + useBreadcrumbs, useComments, useHeadingsTree, usePrism, @@ -74,10 +74,9 @@ const ArticlePage: NextPageWithLayout = ({ data }) => { contentId: article.id, }, }); - const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ - title: data.post.title, - url: data.post.slug, - }); + const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumbs( + article.title + ); const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); const { attributes, className: prismClassName } = usePrism({ attributes: { @@ -172,6 +171,7 @@ const ArticlePage: NextPageWithLayout = ({ data }) => { webpageSchema, blogSchema, blogPostSchema, + breadcrumbSchema, ...getCommentsSchema(comments), ]); @@ -208,12 +208,6 @@ const ArticlePage: NextPageWithLayout = ({ data }) => { // eslint-disable-next-line react/no-danger -- Necessary for schema dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} /> -