diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-24 19:26:47 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:15:27 +0100 |
| commit | 73e12fe8ae059ef70bbdf8716af421cb72aec76c (patch) | |
| tree | 2971d405b34b10b44ecb446cd591c96adc2206f1 /src | |
| parent | 3f8ae3f558446aba3870e90c899db25ad9321499 (diff) | |
refactor(components): rewrite Breadcrumbs component
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/molecules/nav/breadcrumb.module.scss | 10 | ||||
| -rw-r--r-- | src/components/molecules/nav/breadcrumb.stories.tsx | 81 | ||||
| -rw-r--r-- | src/components/molecules/nav/breadcrumb.test.tsx | 16 | ||||
| -rw-r--r-- | src/components/molecules/nav/breadcrumb.tsx | 130 | ||||
| -rw-r--r-- | src/components/molecules/nav/index.ts | 1 | ||||
| -rw-r--r-- | src/components/organisms/nav/breadcrumbs/breadcrumbs.module.scss | 7 | ||||
| -rw-r--r-- | src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx | 55 | ||||
| -rw-r--r-- | src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx | 22 | ||||
| -rw-r--r-- | src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx | 66 | ||||
| -rw-r--r-- | src/components/organisms/nav/breadcrumbs/index.ts | 1 | ||||
| -rw-r--r-- | src/components/organisms/nav/index.ts | 1 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.module.scss | 4 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.tsx | 15 | ||||
| -rw-r--r-- | src/i18n/en.json | 12 | ||||
| -rw-r--r-- | src/i18n/fr.json | 12 | ||||
| -rw-r--r-- | src/utils/hooks/use-breadcrumb.ts | 87 |
16 files changed, 224 insertions, 296 deletions
diff --git a/src/components/molecules/nav/breadcrumb.module.scss b/src/components/molecules/nav/breadcrumb.module.scss deleted file mode 100644 index 6786896..0000000 --- a/src/components/molecules/nav/breadcrumb.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -@use "../../../styles/abstracts/placeholders"; - -.item { - &:not(:last-of-type) { - &::after { - content: ">"; - margin-left: var(--spacing-2xs); - } - } -} diff --git a/src/components/molecules/nav/breadcrumb.stories.tsx b/src/components/molecules/nav/breadcrumb.stories.tsx deleted file mode 100644 index b6dd619..0000000 --- a/src/components/molecules/nav/breadcrumb.stories.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Breadcrumb } from './breadcrumb'; - -/** - * Breadcrumb - Storybook Meta - */ -export default { - title: 'Molecules/Navigation/Breadcrumb', - component: Breadcrumb, - argTypes: { - className: { - control: { - type: 'text', - }, - table: { - category: 'Styles', - }, - description: 'Set additional classnames to the nav element.', - type: { - name: 'string', - required: false, - }, - }, - itemClassName: { - control: { - type: 'text', - }, - table: { - category: 'Styles', - }, - description: 'Set additional classnames to the breadcrumb items.', - type: { - name: 'string', - required: false, - }, - }, - items: { - description: 'The breadcrumb items.', - type: { - name: 'object', - required: true, - value: {}, - }, - }, - }, -} as ComponentMeta<typeof Breadcrumb>; - -const Template: ComponentStory<typeof Breadcrumb> = (args) => ( - <Breadcrumb {...args} /> -); - -/** - * Breadcrumb Stories - One item - */ -export const OneItem = Template.bind({}); -OneItem.args = { - items: [{ id: 'home', url: '#', name: 'Home' }], -}; - -/** - * Breadcrumb Stories - Two items - */ -export const TwoItems = Template.bind({}); -TwoItems.args = { - items: [ - { id: 'home', url: '#', name: 'Home' }, - { id: 'blog', url: '#', name: 'Blog' }, - ], -}; - -/** - * Breadcrumb Stories - Three items - */ -export const ThreeItems = Template.bind({}); -ThreeItems.args = { - items: [ - { id: 'home', url: '#', name: 'Home' }, - { id: 'blog', url: '#', name: 'Blog' }, - { id: 'post1', url: '#', name: 'A Post' }, - ], -}; diff --git a/src/components/molecules/nav/breadcrumb.test.tsx b/src/components/molecules/nav/breadcrumb.test.tsx deleted file mode 100644 index 8aa0d63..0000000 --- a/src/components/molecules/nav/breadcrumb.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { Breadcrumb, type BreadcrumbItem } from './breadcrumb'; - -const items: BreadcrumbItem[] = [ - { id: 'home', url: '#', name: 'Home' }, - { id: 'blog', url: '#', name: 'Blog' }, - { id: 'post1', url: '#', name: 'A Post' }, -]; - -describe('Breadcrumb', () => { - it('renders a navigation', () => { - render(<Breadcrumb items={items} />); - expect(screen.getByRole('navigation')).toBeInTheDocument(); - }); -}); diff --git a/src/components/molecules/nav/breadcrumb.tsx b/src/components/molecules/nav/breadcrumb.tsx deleted file mode 100644 index 51f4633..0000000 --- a/src/components/molecules/nav/breadcrumb.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import Script from 'next/script'; -import type { FC } from 'react'; -import { useIntl } from 'react-intl'; -import type { - BreadcrumbList, - ListItem as ListItemType, - WithContext, -} from 'schema-dts'; -import { settings } from '../../../utils/config'; -import { Link, List, ListItem } from '../../atoms'; -import styles from './breadcrumb.module.scss'; - -export type BreadcrumbItem = { - /** - * The item id. - */ - id: string; - /** - * The item URL. - */ - url: string; - /** - * The item name. - */ - name: string; -}; - -export type BreadcrumbProps = { - /** - * Set additional classnames to the nav element. - */ - className?: string; - /** - * Set additional classnames to the breadcrumb items. - */ - itemClassName?: string; - /** - * The breadcrumb items - */ - items: BreadcrumbItem[]; -}; - -/** - * Breadcrumb component - * - * Render a breadcrumb navigation. - */ -export const Breadcrumb: FC<BreadcrumbProps> = ({ - itemClassName = '', - items, - ...props -}) => { - const intl = useIntl(); - - const ariaLabel = intl.formatMessage({ - defaultMessage: 'Breadcrumb', - description: 'Breadcrumb: an accessible name for the breadcrumb nav.', - id: '28nnDY', - }); - - /** - * Retrieve the breadcrumb list items. - * - * @param {BreadcrumbItem[]} list - The breadcrumb items. - * @returns {JSX.Element[]} The list items. - */ - const getListItems = (list: BreadcrumbItem[]): JSX.Element[] => - list.map((item, index) => { - const isLastItem = index === list.length - 1; - const itemStyles = isLastItem - ? `${styles.item} screen-reader-text` - : styles.item; - - return ( - <ListItem key={item.id} className={`${itemStyles} ${itemClassName}`}> - {isLastItem ? item.name : <Link href={item.url}>{item.name}</Link>} - </ListItem> - ); - }); - - /** - * Retrieve the breadcrumb list items with Schema.org format. - * - * @param {BreadcrumbItem[]} list - The breadcrumb items. - * @returns {ListItemType[]} An array of list items using Schema.org format. - */ - const getSchemaItems = (list: BreadcrumbItem[]): ListItemType[] => { - const schemaItems: ListItemType[] = []; - - list.forEach((item, index) => { - schemaItems.push({ - '@type': 'ListItem', - position: index + 1, - name: item.name, - item: item.url, - }); - }); - - return schemaItems; - }; - - const schemaJsonLd: WithContext<BreadcrumbList> = { - '@context': 'https://schema.org', - '@type': 'BreadcrumbList', - '@id': `${settings.url}/#breadcrumb`, - itemListElement: getSchemaItems(items), - }; - - return ( - <> - <Script - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - id="schema-breadcrumb" - type="application/ld+json" - /> - <nav aria-label={ariaLabel} {...props}> - <span className="screen-reader-text"> - {intl.formatMessage({ - defaultMessage: 'You are here:', - description: 'Breadcrumb: You are here prefix', - id: '16zl9Z', - })} - </span> - <List hideMarker isInline isOrdered spacing="2xs"> - {getListItems(items)} - </List> - </nav> - </> - ); -}; diff --git a/src/components/molecules/nav/index.ts b/src/components/molecules/nav/index.ts index 2f9b8e3..08e47b7 100644 --- a/src/components/molecules/nav/index.ts +++ b/src/components/molecules/nav/index.ts @@ -1,4 +1,3 @@ -export * from './breadcrumb'; export * from './nav-item'; export * from './nav-link'; export * from './nav-list'; diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.module.scss b/src/components/organisms/nav/breadcrumbs/breadcrumbs.module.scss new file mode 100644 index 0000000..1be81c4 --- /dev/null +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.module.scss @@ -0,0 +1,7 @@ +.wrapper { + width: fit-content; +} + +.sep { + margin-inline-start: var(--spacing-xs); +} diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx b/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx new file mode 100644 index 0000000..4736b26 --- /dev/null +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.stories.tsx @@ -0,0 +1,55 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Breadcrumbs } from './breadcrumbs'; + +/** + * Breadcrumbs - Storybook Meta + */ +export default { + title: 'Organisms/Nav/Breadcrumbs', + component: Breadcrumbs, + argTypes: { + items: { + description: 'The breadcrumb items.', + type: { + name: 'object', + required: true, + value: {}, + }, + }, + }, +} as ComponentMeta<typeof Breadcrumbs>; + +const Template: ComponentStory<typeof Breadcrumbs> = (args) => ( + <Breadcrumbs {...args} /> +); + +/** + * Breadcrumbs Stories - One item + */ +export const OneItem = Template.bind({}); +OneItem.args = { + items: [{ id: 'home', url: '#', name: 'Home' }], +}; + +/** + * Breadcrumbs Stories - Two items + */ +export const TwoItems = Template.bind({}); +TwoItems.args = { + items: [ + { id: 'home', url: '#', name: 'Home' }, + { id: 'blog', url: '#', name: 'Blog' }, + ], +}; + +/** + * Breadcrumbs Stories - Three items + */ +export const ThreeItems = Template.bind({}); +ThreeItems.args = { + items: [ + { id: 'home', url: '#', name: 'Home' }, + { id: 'blog', url: '#', name: 'Blog' }, + { id: 'post1', url: '#', name: 'A Post' }, + ], +}; diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx b/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx new file mode 100644 index 0000000..40bb1b8 --- /dev/null +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.test.tsx @@ -0,0 +1,22 @@ +import { describe, expect, it } from '@jest/globals'; +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' }, +]; + +describe('Breadcrumbs', () => { + it('renders a list of items wrapped in a nav element', () => { + const ariaLabel = 'error tempore iure'; + + render(<Breadcrumbs aria-label={ariaLabel} items={items} />); + + expect(rtlScreen.getByRole('navigation')).toHaveAccessibleName(ariaLabel); + expect(rtlScreen.getAllByRole('listitem')).toHaveLength(items.length); + // The last item should not be linked + expect(rtlScreen.getAllByRole('link')).toHaveLength(items.length - 1); + }); +}); diff --git a/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx b/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx new file mode 100644 index 0000000..b6d3843 --- /dev/null +++ b/src/components/organisms/nav/breadcrumbs/breadcrumbs.tsx @@ -0,0 +1,66 @@ +import { type ForwardRefRenderFunction, forwardRef } from 'react'; +import { Nav, VisuallyHidden, type NavProps } from '../../../atoms'; +import { NavItem, NavLink, NavList } from '../../../molecules'; +import styles from './breadcrumbs.module.scss'; + +export type BreadcrumbsItem = { + /** + * The item id. + */ + id: string; + /** + * The item URL. + */ + url: string; + /** + * The item name. + */ + name: string; +}; + +export type BreadcrumbsProps = Omit<NavProps, 'children'> & { + /** + * The breadcrumbs items. + */ + items: BreadcrumbsItem[]; +}; + +const BreadcrumbsWithRef: ForwardRefRenderFunction< + HTMLElement, + BreadcrumbsProps +> = ({ className = '', items, ...props }, ref) => { + const wrapperClass = `${styles.wrapper} ${className}`; + const sep = '>'; + + return ( + <Nav {...props} className={wrapperClass} ref={ref}> + <NavList + isInline + isOrdered + // eslint-disable-next-line react/jsx-no-literals + spacing="xs" + > + {items.map((item, index) => { + const isLastItem = items.length === index + 1; + + return ( + <NavItem key={item.id}> + {isLastItem ? ( + <VisuallyHidden>{item.name}</VisuallyHidden> + ) : ( + <> + <NavLink href={item.url} label={item.name} /> + <span aria-hidden className={styles.sep}> + {sep} + </span> + </> + )} + </NavItem> + ); + })} + </NavList> + </Nav> + ); +}; + +export const Breadcrumbs = forwardRef(BreadcrumbsWithRef); diff --git a/src/components/organisms/nav/breadcrumbs/index.ts b/src/components/organisms/nav/breadcrumbs/index.ts new file mode 100644 index 0000000..8d59853 --- /dev/null +++ b/src/components/organisms/nav/breadcrumbs/index.ts @@ -0,0 +1 @@ +export * from './breadcrumbs'; diff --git a/src/components/organisms/nav/index.ts b/src/components/organisms/nav/index.ts index cb72765..ad899e0 100644 --- a/src/components/organisms/nav/index.ts +++ b/src/components/organisms/nav/index.ts @@ -1 +1,2 @@ +export * from './breadcrumbs'; export * from './pagination'; diff --git a/src/components/templates/page/page-layout.module.scss b/src/components/templates/page/page-layout.module.scss index 09bb957..4615f60 100644 --- a/src/components/templates/page/page-layout.module.scss +++ b/src/components/templates/page/page-layout.module.scss @@ -6,13 +6,11 @@ @extend %grid; grid-column: 1 / -1; + width: 100%; padding: var(--spacing-md) 0; > * { grid-column: 2; - } - - &__items { font-size: var(--font-size-sm); } } diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx index dbac43e..3fd5b02 100644 --- a/src/components/templates/page/page-layout.tsx +++ b/src/components/templates/page/page-layout.tsx @@ -14,8 +14,6 @@ import type { Approved, SendCommentInput, SingleComment } from '../../../types'; import { useIsMounted } from '../../../utils/hooks'; import { Heading, Notice, type NoticeKind, Sidebar } from '../../atoms'; import { - Breadcrumb, - type BreadcrumbItem, PageFooter, type PageFooterProps, PageHeader, @@ -27,6 +25,8 @@ import { CommentsList, type CommentsListProps, TableOfContents, + Breadcrumbs, + type BreadcrumbsItem, } from '../../organisms'; import styles from './page-layout.module.scss'; @@ -62,7 +62,7 @@ export type PageLayoutProps = { /** * The breadcrumb items. */ - breadcrumb: BreadcrumbItem[]; + breadcrumb: BreadcrumbsItem[]; /** * The breadcrumb JSON schema. */ @@ -127,6 +127,11 @@ export const PageLayout: FC<PageLayoutProps> = ({ withToC = false, }) => { const intl = useIntl(); + const breadcrumbsLabel = intl.formatMessage({ + defaultMessage: 'Breadcrumb', + description: 'PageLayout: an accessible name for the breadcrumb nav.', + id: 'm6a3BD', + }); const commentsTitle = intl.formatMessage({ defaultMessage: 'Comments', description: 'PageLayout: comments title', @@ -211,9 +216,9 @@ export const PageLayout: FC<PageLayoutProps> = ({ id="schema-breadcrumb" type="application/ld+json" /> - <Breadcrumb + <Breadcrumbs + aria-label={breadcrumbsLabel} className={styles.breadcrumb} - itemClassName={styles.breadcrumb__items} items={breadcrumb} /> <PageHeader diff --git a/src/i18n/en.json b/src/i18n/en.json index 8581273..2fd2edf 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -35,10 +35,6 @@ "defaultMessage": "Tracking:", "description": "AckeeToggle: select label" }, - "16zl9Z": { - "defaultMessage": "You are here:", - "description": "Breadcrumb: You are here prefix" - }, "18h/t0": { "defaultMessage": "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", "description": "BlogPage: SEO - Meta description" @@ -63,10 +59,6 @@ "defaultMessage": "Projects", "description": "Breadcrumb: projects label" }, - "28nnDY": { - "defaultMessage": "Breadcrumb", - "description": "Breadcrumb: an accessible name for the breadcrumb nav." - }, "2D9tB5": { "defaultMessage": "Topics", "description": "BlogPage: topics list widget title" @@ -599,6 +591,10 @@ "defaultMessage": "Use Ctrl+c to copy", "description": "usePrism: copy button error text" }, + "m6a3BD": { + "defaultMessage": "Breadcrumb", + "description": "PageLayout: an accessible name for the breadcrumb nav." + }, "nGss/j": { "defaultMessage": "Ackee tracking (analytics)", "description": "AckeeToggle: tooltip title" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 5687cdc..bebfc47 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -35,10 +35,6 @@ "defaultMessage": "Suivi :", "description": "AckeeToggle: select label" }, - "16zl9Z": { - "defaultMessage": "Vous êtes ici :", - "description": "Breadcrumb: You are here prefix" - }, "18h/t0": { "defaultMessage": "Découvrez les articles d’{websiteName}. Il écrit à propos de développement web, de Linux et du libre essentiellement.", "description": "BlogPage: SEO - Meta description" @@ -63,10 +59,6 @@ "defaultMessage": "Projets", "description": "Breadcrumb: projects label" }, - "28nnDY": { - "defaultMessage": "Fil d’Ariane", - "description": "Breadcrumb: an accessible name for the breadcrumb nav." - }, "2D9tB5": { "defaultMessage": "Sujets", "description": "BlogPage: topics list widget title" @@ -599,6 +591,10 @@ "defaultMessage": "Utilisez Ctrl+c pour copier", "description": "usePrism: copy button error text" }, + "m6a3BD": { + "defaultMessage": "Fil d’Ariane", + "description": "PageLayout: an accessible name for the breadcrumb nav." + }, "nGss/j": { "defaultMessage": "Suivi Ackee (analytique)", "description": "AckeeToggle: tooltip title" diff --git a/src/utils/hooks/use-breadcrumb.ts b/src/utils/hooks/use-breadcrumb.ts index 5839299..57c27bd 100644 --- a/src/utils/hooks/use-breadcrumb.ts +++ b/src/utils/hooks/use-breadcrumb.ts @@ -1,11 +1,33 @@ /* eslint-disable max-statements */ import { useIntl } from 'react-intl'; import type { BreadcrumbList } from 'schema-dts'; -import type { BreadcrumbItem } from '../../components'; +import type { BreadcrumbsItem } from '../../components'; import { ROUTES } from '../constants'; import { slugify } from '../helpers'; import { useSettings } from './use-settings'; +const isArticle = (url: string) => url.startsWith(`${ROUTES.ARTICLE}/`); + +const isHome = (url: string) => url === '/'; + +const isPageNumber = (url: string) => url.includes('/page/'); + +const isProject = (url: string) => url.startsWith(`${ROUTES.PROJECTS}/`); + +const isSearch = (url: string) => url.startsWith(ROUTES.SEARCH); + +const isThematic = (url: string) => + url.startsWith(`${ROUTES.THEMATICS.INDEX}/`); + +const isTopic = (url: string) => url.startsWith(`${ROUTES.TOPICS}/`); + +const hasBlogAsParent = (url: string) => + isArticle(url) || + isPageNumber(url) || + isSearch(url) || + isThematic(url) || + isTopic(url); + export type useBreadcrumbProps = { /** * The current page title. @@ -21,7 +43,7 @@ export type useBreadcrumbReturn = { /** * The breadcrumb items. */ - items: BreadcrumbItem[]; + items: BreadcrumbsItem[]; /** * The breadcrumb JSON schema. */ @@ -40,57 +62,54 @@ export const useBreadcrumb = ({ }: useBreadcrumbProps): useBreadcrumbReturn => { const intl = useIntl(); const { website } = useSettings(); - const isArticle = url.startsWith(`${ROUTES.ARTICLE}/`); - const isHome = url === '/'; - const isPageNumber = url.includes('/page/'); - const isProject = url.startsWith(`${ROUTES.PROJECTS}/`); - const isSearch = url.startsWith(ROUTES.SEARCH); - const isThematic = url.startsWith(`${ROUTES.THEMATICS.INDEX}/`); - const isTopic = url.startsWith(`${ROUTES.TOPICS}/`); - - const homeLabel = intl.formatMessage({ - defaultMessage: 'Home', - description: 'Breadcrumb: home label', - id: 'j5k9Fe', - }); - const items: BreadcrumbItem[] = [{ id: 'home', name: homeLabel, url: '/' }]; + const labels = { + home: intl.formatMessage({ + defaultMessage: 'Home', + description: 'Breadcrumb: home label', + id: 'j5k9Fe', + }), + blog: intl.formatMessage({ + defaultMessage: 'Blog', + description: 'Breadcrumb: blog label', + id: 'Es52wh', + }), + projects: intl.formatMessage({ + defaultMessage: 'Projects', + description: 'Breadcrumb: projects label', + id: '28GZdv', + }), + }; + + const items: BreadcrumbsItem[] = [ + { id: 'home', name: labels.home, url: '/' }, + ]; const schema: BreadcrumbList['itemListElement'][] = [ { '@type': 'ListItem', position: 1, - name: homeLabel, + name: labels.home, item: website.url, }, ]; - if (isHome) return { items, schema }; + if (isHome(url)) return { items, schema }; - if (isArticle || isPageNumber || isSearch || isThematic || isTopic) { - const blogLabel = intl.formatMessage({ - defaultMessage: 'Blog', - description: 'Breadcrumb: blog label', - id: 'Es52wh', - }); - items.push({ id: 'blog', name: blogLabel, url: ROUTES.BLOG }); + if (hasBlogAsParent(url)) { + items.push({ id: 'blog', name: labels.blog, url: ROUTES.BLOG }); schema.push({ '@type': 'ListItem', position: 2, - name: blogLabel, + name: labels.blog, item: `${website.url}${ROUTES.BLOG}`, }); } - if (isProject) { - const projectsLabel = intl.formatMessage({ - defaultMessage: 'Projects', - description: 'Breadcrumb: projects label', - id: '28GZdv', - }); - items.push({ id: 'projects', name: projectsLabel, url: ROUTES.PROJECTS }); + if (isProject(url)) { + items.push({ id: 'projects', name: labels.projects, url: ROUTES.PROJECTS }); schema.push({ '@type': 'ListItem', position: 2, - name: projectsLabel, + name: labels.projects, item: `${website.url}${ROUTES.PROJECTS}`, }); } |
