diff --git a/packages/ui/src/components/Breadcrumb/Breadcrumb.test.tsx b/packages/ui/src/components/Breadcrumb/Breadcrumb.test.tsx new file mode 100644 index 000000000..489795eb2 --- /dev/null +++ b/packages/ui/src/components/Breadcrumb/Breadcrumb.test.tsx @@ -0,0 +1,93 @@ +import { render, screen } from '@testing-library/react'; +import Link from 'next/link'; +import { Breadcrumbs } from './index'; +import '@testing-library/jest-dom'; + +describe('Breadcrumb navigation', () => { + const props = { + label: 'Kruimelpad', + links: [ + { + href: 'https://www.utrecht.nl/', + label: 'Home', + current: false, + rel: 'home', + }, + { + href: '/', + label: 'Online loket', + current: true, + }, + ], + backLink: { + href: '/', + label: 'Online loket', + current: false, + }, + Link: Link, + }; + + it('renders a navigation landmark', () => { + render(); + + const navigation = screen.getByRole('navigation', { name: 'Kruimelpad' }); + + expect(navigation).toBeInTheDocument(); + }); + + it('renders links', () => { + render(); + + const links = screen.getAllByRole('link'); + + expect(links.length).toBe(props.links.length); + }); + + it('renders a link', () => { + render(); + + const link = screen.getByRole('link', { name: 'Home' }); + + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('href', 'https://www.utrecht.nl/'); + expect(link).toHaveAttribute('rel', 'home'); + }); + + it('renders a current link', () => { + render(); + + const link = screen.getByRole('link', { name: 'Online loket', current: 'page' }); + + expect(link).toBeInTheDocument(); + }); + + it('renders no separator for one link', () => { + const { container } = render(); + + const separator = container.querySelector('.utrecht-breadcrumb-nav__separator'); + + expect(separator).not.toBeInTheDocument(); + }); + + it('renders separators between items', () => { + const { container } = render(); + + const separators = container.querySelectorAll( + '.utrecht-breadcrumb-nav__item + .utrecht-breadcrumb-nav__separator + .utrecht-breadcrumb-nav__item', + ); + + expect(separators.length).toBe(props.links.length - 1); + }); + + it('renders a custom Link component', () => { + const { container } = render( + } />, + ); + + const links = container.querySelectorAll('.custom-link'); + + expect(links.length).toBe(props.links.length); + }); + + // TODO: Test small screen breadcrumb +}); diff --git a/packages/ui/src/components/Breadcrumb/index.module.scss b/packages/ui/src/components/Breadcrumb/index.module.scss index 8708698fc..953e64182 100644 --- a/packages/ui/src/components/Breadcrumb/index.module.scss +++ b/packages/ui/src/components/Breadcrumb/index.module.scss @@ -16,6 +16,12 @@ margin-inline-end: 8px; } +@media (width >= 360px) { + .utrecht-breadcrumb-nav__separator { + margin-inline-end: var(--utrecht-space-inline-xs); + } +} + .utrecht-breadcrumb-nav__link { --utrecht-link-focus-color: var(--utrecht-color-black); diff --git a/packages/ui/src/components/Breadcrumb/index.tsx b/packages/ui/src/components/Breadcrumb/index.tsx index 808acab4f..ce0618596 100644 --- a/packages/ui/src/components/Breadcrumb/index.tsx +++ b/packages/ui/src/components/Breadcrumb/index.tsx @@ -12,21 +12,24 @@ import './index.module.scss'; import styles from './index.module.scss'; import { useScreenSize } from '../../hooks'; +export const extendLink = (link: BreadcrumbLinkType) => ({ + // Use default `rel`, but actual optional rel should override this + rel: link.current ? undefined : link.href === '/' ? 'home' : 'up', + ...link, +}); + const css = classnames.bind(styles); type BreadcrumbLinkType = { href: string; label: string; current: boolean; + rel?: string; }; interface BreadcrumbProps extends BreadcrumbNavProps { links: BreadcrumbLinkType[]; Link?: ComponentType; - backLink?: { - href: string; - label: string; - current: boolean; - }; + backLink?: BreadcrumbLinkType; breakpoint?: number; } @@ -41,59 +44,51 @@ export const Breadcrumbs = ({ const smallScreen = Number(screenSize) <= breakpoint; - if (smallScreen && backLink?.href && backLink.label) { + const linkData = links.map(extendLink); + const backLinkData = backLink && extendLink(backLink); + + if ( + (smallScreen && backLinkData?.href && backLinkData.label) || + (links.length === 1 && backLinkData?.href && backLinkData.label) + ) { return ( - - - - {backLink?.label?.toLowerCase() !== 'home' && ( - - - - )} - {backLink.label} - - + + + {backLinkData?.rel === 'up' && } {backLinkData.label} + ); } return ( - - {links && - links.length > 0 && - links - .filter(({ label }) => label) - .map(({ href, label, current }: any, index: number) => ( - - - {links.length === 1 && label?.toLowerCase() !== 'home' && ( - - - - )} - {label} - {index !== links.length - 1 && ( - - - - )} - - - ))} + + {linkData + .filter(({ label }) => label) + .map(({ href, label, current, rel }: any, index: number) => ( + + + {label} + + {index !== links.length - 1 && ( + + + + )} + + ))} ); };