diff --git a/.changeset/swift-forks-drop.md b/.changeset/swift-forks-drop.md new file mode 100644 index 0000000000..e429584679 --- /dev/null +++ b/.changeset/swift-forks-drop.md @@ -0,0 +1,6 @@ +--- +"@digdir/designsystemet-css": patch +"@digdir/designsystemet-react": patch +--- + +Breadcrumbs: Rename `Breadcrumbs.Root` to `Breadcrumbs` and remove `Breadcrumbs.Nav` diff --git a/packages/css/breadcrumbs.css b/packages/css/breadcrumbs.css index df85e2bc75..7081f3d3aa 100644 --- a/packages/css/breadcrumbs.css +++ b/packages/css/breadcrumbs.css @@ -1,72 +1,63 @@ .ds-breadcrumbs { --dsc-breadcrumbs-spacing: var(--ds-spacing-2); --dsc-breadcrumbs-chevron-size: var(--ds-sizing-6); - --dsc-breadcrumbs-link-color: inherit; -} - -.ds-breadcrumbs--sm { - --dsc-breadcrumbs-spacing: var(--ds-spacing-1); - --dsc-breadcrumbs-chevron-size: var(--ds-sizing-5); -} -.ds-breadcrumbs--md { - --dsc-breadcrumbs-spacing: var(--ds-spacing-2); - --dsc-breadcrumbs-chevron-size: var(--ds-sizing-6); -} - -.ds-breadcrumbs--lg { - --dsc-breadcrumbs-spacing: var(--ds-spacing-3); - --dsc-breadcrumbs-chevron-size: var(--ds-sizing-7); -} + &[data-size='sm'] { + --dsc-breadcrumbs-spacing: var(--ds-spacing-1); + --dsc-breadcrumbs-chevron-size: var(--ds-sizing-5); + } -.ds-breadcrumbs__list { - display: flex; - flex-wrap: wrap; - list-style-type: none; - margin: 0; - padding: 0; - gap: var(--dsc-breadcrumbs-spacing) 0; -} + &[data-size='lg'] { + --dsc-breadcrumbs-spacing: var(--ds-spacing-3); + --dsc-breadcrumbs-chevron-size: var(--ds-sizing-7); + } -.ds-breadcrumbs__item:where(:not(:last-child))::after, -.ds-breadcrumbs > .ds-breadcrumbs__link::before { - background: currentcolor; - content: ''; - display: inline-block; - height: var(--dsc-breadcrumbs-chevron-size); - margin-inline: var(--dsc-breadcrumbs-spacing); - mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath d='M9.47 5.97a.75.75 0 0 1 1.06 0l5.5 5.5a.75.75 0 0 1 0 1.06l-5.5 5.5a.75.75 0 1 1-1.06-1.06L14.44 12 9.47 7.03a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E") - 50% / contain no-repeat; - vertical-align: middle; - width: var(--dsc-breadcrumbs-chevron-size); -} + & > :is(ol, ul) { + display: flex; + flex-wrap: wrap; + list-style-type: none; + margin: 0; + padding: 0; + gap: var(--dsc-breadcrumbs-spacing) 0; + } -/* When link is direct child of Breadcrumbs, make it back button */ -.ds-breadcrumbs > .ds-breadcrumbs__link::before { - margin: 0; - rotate: 180deg; -} + & a:not(:focus-visible) { + color: inherit; + } -.ds-breadcrumbs__link { - --dsc-link-color: var(--dsc-breadcrumbs-link-color); - --dsc-link-color-visited: var(--dsc-breadcrumbs-link-color); -} + & a[aria-current='page'] { + text-decoration: none; + } -.ds-breadcrumbs__link[aria-current='page'] { - text-decoration: none; -} + /* Draw chevron between items and before back link */ + & li:where(:not(:last-child))::after, + & > :not(ol, ul)::before { + background: currentcolor; + content: ''; + display: inline-block; + height: var(--dsc-breadcrumbs-chevron-size); + margin-inline: var(--dsc-breadcrumbs-spacing); + mask: center / contain no-repeat + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath d='M9.47 5.97a.75.75 0 0 1 1.06 0l5.5 5.5a.75.75 0 0 1 0 1.06l-5.5 5.5a.75.75 0 1 1-1.06-1.06L14.44 12 9.47 7.03a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E"); + vertical-align: middle; + width: var(--dsc-breadcrumbs-chevron-size); + } -.ds-breadcrumbs > .ds-breadcrumbs__link:where(:not(:only-child)) { - display: none; -} + /* When link is direct child of Breadcrumbs, make it back button */ + & > :not(ol, ul)::before { + margin: 0; + rotate: 180deg; + } -@media (max-width: 650px) { - .ds-breadcrumbs > .ds-breadcrumbs__nav:where(:not(:only-child)) { - display: none; + @media (max-width: 650px) { + & > :is(ol, ul):not(:only-child) { + display: none; /* Hide list when mobile and having back link */ + } } - .ds-breadcrumbs > .ds-breadcrumbs__link { - display: block; - width: fit-content; + @media (min-width: 651px) { + & > :not(ol, ul) { + display: none; /* Hide back link when desktop and having list */ + } } } diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx index 4e51412621..d406c32781 100644 --- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx +++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx @@ -17,38 +17,36 @@ hvor de er i en struktur, for eksempel på en nettside. Da kan de lettere bytte Den siste lenken i brødsmulestien blir automatisk markert med `aria-current="page"`. ```tsx - + Nivå 3 - - - - Nivå 1 - - - Nivå 2 - - - - + + + Nivå 1 + + + Nivå 2 + + + ``` ## Varianter av `Breadcrumb` ### Kun tilbake-knapp -Hvis du legger en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs.Root`, vil denne lenken vises som en tilbake-knapp. Det er viktig at du er konsistent i din løsning og bruker enten tilbake-knapp eller sti. +Hvis du legger en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs`, vil denne lenken vises som en tilbake-knapp. Det er viktig at du er konsistent i din løsning og bruker enten tilbake-knapp eller sti. ### Kun sti -Hvis du legger en `Breadcrumbs.List` som direkte barn av `Breadcrumbs.Root`, vil denne vises som en sti. +Hvis du legger en `Breadcrumbs.List` som direkte barn av `Breadcrumbs`, vil denne vises som en sti. ### Både sti på desktop og tilbake-knapp på mobil -Hvis du legger både en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs.Root` og en `Breadcrumbs.List`, vil tilbake-knapp vises på mobil, og sti vises på desktop. +Hvis du legger både en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs` og en `Breadcrumbs.List`, vil tilbake-knapp vises på mobil, og sti vises på desktop. diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx index 50a6aa81c5..80f7e84192 100644 --- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx @@ -4,18 +4,18 @@ import { Breadcrumbs } from '.'; export default { title: 'Komponenter/Breadcrumbs', - component: Breadcrumbs.Root, + component: Breadcrumbs, args: { size: 'md', }, } as Meta; -export const Preview: StoryFn = (args) => ( - - - Nivå 3 - - +export const Preview: StoryFn = (args) => ( + <> + + + Nivå 3 + Nivå 1 @@ -30,107 +30,99 @@ export const Preview: StoryFn = (args) => ( Nivå 4 - - + + ); -export const ListOnly: StoryFn = (args) => ( - - - - - Nivå 1 - - - Nivå 2 - - - Nivå 3 - - - Nivå 4 - - - - +export const ListOnly: StoryFn = (args) => ( + + + + Nivå 1 + + + Nivå 2 + + + Nivå 3 + + + Nivå 4 + + + ); -export const BackOnly: StoryFn = (args) => ( - +export const BackOnly: StoryFn = (args) => ( + Nivå 3 - + ); -export const LongItems: StoryFn = (args) => ( - +export const LongItems: StoryFn = (args) => ( + Slik søker du om helsesertifikat for sjømat - - - - Hjem - - - - Eksport til land utenfor EU/EØS - - - - Eksport av mat og drikke - - - - Eksport av fisk og sjømat - - - - - Veiledning om helsesertifikat for sjømat - - - - - Slik søker du om helsesertifikat for sjømat - - - - - Slik søker du om helsesertifikat i ny eksportløsning - - - - - + + + Hjem + + + + Eksport til land utenfor EU/EØS + + + + Eksport av mat og drikke + + + Eksport av fisk og sjømat + + + + Veiledning om helsesertifikat for sjømat + + + + + Slik søker du om helsesertifikat for sjømat + + + + + Slik søker du om helsesertifikat i ny eksportløsning + + + + ); -export const MobileViewport: StoryFn = (args) => ( - +export const MobileViewport: StoryFn = (args) => ( + Nivå 3 - - - - Nivå 1 - - - Nivå 2 - - - Nivå 3 - - - Nivå 4 - - - - + + + Nivå 1 + + + Nivå 2 + + + Nivå 3 + + + Nivå 4 + + + ); MobileViewport.parameters = { diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx index c2e7159ba4..dff199e5bc 100644 --- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx +++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx @@ -1,15 +1,15 @@ import { render, screen } from '@testing-library/react'; -import type { BreadcrumbsRootProps } from './BreadcrumbsRoot'; +import type { BreadcrumbsProps } from './Breadcrumbs'; import { Breadcrumbs } from './'; -const renderWithRoot = (props?: BreadcrumbsRootProps) => +const renderWithRoot = (props?: BreadcrumbsProps) => render( - - - Nivå 3 - - + <> + + + Nivå 3 + Nivå 1 @@ -24,11 +24,11 @@ const renderWithRoot = (props?: BreadcrumbsRootProps) => Nivå 4 - - , + + , ); -describe('Breadcrumbs.Root', () => { +describe('Breadcrumbs', () => { it('should render correctly with default props', () => { renderWithRoot(); @@ -58,30 +58,28 @@ describe('Breadcrumbs.List', () => { // Re-render with additional level render( - + Nivå 3 - - - - Nivå 1 - - - Nivå 2 - - - Nivå 3 - - - Nivå 4 - - - Nivå 5 - - - - , + + + Nivå 1 + + + Nivå 2 + + + Nivå 3 + + + Nivå 4 + + + Nivå 5 + + + , ); expect(links.at(-2)).not.toHaveAttribute('aria-current', 'page'); diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx new file mode 100644 index 0000000000..c9caf11a2f --- /dev/null +++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -0,0 +1,33 @@ +import cl from 'clsx/lite'; +import { type HTMLAttributes, forwardRef } from 'react'; +import { Paragraph } from '../Typography'; + +export type BreadcrumbsProps = { + /** + * Sets the screen reader label for the Breadcrumbs area + * @default 'Du er her' + */ + 'aria-label'?: string; + /** + * Sets the size of the component + * @default md + */ + size?: 'sm' | 'md' | 'lg'; +} & HTMLAttributes; + +export const Breadcrumbs = forwardRef( + ( + { 'aria-label': ariaLabel = 'Du er her:', className, size, ...rest }, + ref, + ) => ( + +