-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
18d21a0
commit 970c414
Showing
10 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
...d/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbs.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Meta, Controls, Primary, Canvas } from '@storybook/blocks'; | ||
import * as StudioBreadcrumbsStories from './StudioBreadcrumbs.stories'; | ||
|
||
<Meta of={StudioBreadcrumbsStories} /> | ||
|
||
# StudioBreadcrumbs | ||
|
||
This is a copy of Designsystemet's `Breadcrumb` component from v1.0.0-next.35. It should eventually be replaced with an import from Designsystemet when we upgrade to v1. | ||
|
||
`StudioBreadcrumbs` is a navigation with a visible breadcrumb trail. Use this component to help users understand where they are within a structure, such as on a website. This allows them to more easily switch between the different levels of the structure. | ||
|
||
<Primary /> | ||
<Controls of={StudioBreadcrumbsStories.Preview} /> | ||
|
||
## How to use `Breadcrumb` | ||
|
||
The last link in the breadcrumb trail is automatically marked with `aria-current="page"`. | ||
|
||
```tsx | ||
<Breadcrumbs aria-label='You are here:'> | ||
<Breadcrumbs.List> | ||
<Breadcrumbs.Item> | ||
<Breadcrumbs.Link href='https://designsystemet.no/'>Level 1</Breadcrumbs.Link> | ||
</Breadcrumbs.Item> | ||
<Breadcrumbs.Item> | ||
<Breadcrumbs.Link href='https://designsystemet.no/niva-2/'>Level 2</Breadcrumbs.Link> | ||
</Breadcrumbs.Item> | ||
</Breadcrumbs.List> | ||
</Breadcrumbs> | ||
``` |
53 changes: 53 additions & 0 deletions
53
...tend/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbs.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
.ds-breadcrumbs { | ||
--dsc-breadcrumbs-spacing: var(--fds-spacing-2); | ||
--dsc-breadcrumbs-chevron-size: var(--fds-sizing-6); | ||
--dsc-breadcrumbs-icon-url: 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"); | ||
|
||
& > :is(ol, ul) { | ||
display: flex; | ||
flex-wrap: wrap; | ||
list-style-type: none; | ||
margin: 0; | ||
padding: 0; | ||
gap: var(--dsc-breadcrumbs-spacing) 0; | ||
} | ||
|
||
& a:not(:focus-visible) { | ||
color: inherit; | ||
} | ||
|
||
& a[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 var(--dsc-breadcrumbs-icon-url); | ||
vertical-align: middle; | ||
width: var(--dsc-breadcrumbs-chevron-size); | ||
} | ||
|
||
/* When link is direct child of Breadcrumbs, make it back button */ | ||
& > :not(ol, ul)::before { | ||
margin: 0; | ||
rotate: 180deg; | ||
} | ||
|
||
@media (width < 650px) { | ||
& > :is(ol, ul):not(:only-child) { | ||
display: none; /* Hide list when mobile and having back link */ | ||
} | ||
} | ||
|
||
@media (min-width: 650px) { | ||
& > :is(:not(ol, ul)):not(:only-child) { | ||
display: none; /* Hide back link when desktop and having list */ | ||
} | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
...end/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbs.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import type { Meta, StoryFn } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { StudioBreadcrumbs } from '.'; | ||
|
||
export default { | ||
title: 'Components/StudioBreadcrumbs', | ||
component: StudioBreadcrumbs, | ||
args: { | ||
'aria-label': 'You are here:', | ||
}, | ||
} as Meta; | ||
|
||
export const Preview: StoryFn<typeof StudioBreadcrumbs> = (args) => ( | ||
<StudioBreadcrumbs {...args}> | ||
<StudioBreadcrumbs.List> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Level 1</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Level 2</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Level 3</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Level 4</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
</StudioBreadcrumbs.List> | ||
</StudioBreadcrumbs> | ||
); | ||
|
||
export const Back: StoryFn<typeof StudioBreadcrumbs> = (args) => ( | ||
<StudioBreadcrumbs {...args}> | ||
<StudioBreadcrumbs.Link href='#' aria-label='Back to level 3'> | ||
Level 3 | ||
</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs> | ||
); |
82 changes: 82 additions & 0 deletions
82
frontend/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbs.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import type { StudioBreadcrumbsProps } from './StudioBreadcrumbs'; | ||
|
||
import { StudioBreadcrumbs } from './'; | ||
|
||
const renderWithRoot = (props?: StudioBreadcrumbsProps) => | ||
render( | ||
<StudioBreadcrumbs aria-label='Du er her:' {...props}> | ||
<StudioBreadcrumbs.Link href='#' aria-label='Tilbake til Nivå 3'> | ||
Nivå 3 | ||
</StudioBreadcrumbs.Link> | ||
<StudioBreadcrumbs.List> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 1</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 2</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 3</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 4</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
</StudioBreadcrumbs.List> | ||
</StudioBreadcrumbs>, | ||
); | ||
|
||
describe('StudioBreadcrumbs', () => { | ||
it('should render correctly with default props', () => { | ||
renderWithRoot(); | ||
|
||
expect(screen.getByRole('navigation')).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
describe('StudioBreadcrumbs.List', () => { | ||
it('should render with aria-current on last item', () => { | ||
renderWithRoot(); | ||
const links = screen.getAllByRole('link'); | ||
expect(links.at(0)).not.toHaveAttribute('aria-current', 'page'); | ||
expect(links.at(1)).not.toHaveAttribute('aria-current', 'page'); | ||
expect(links.at(2)).not.toHaveAttribute('aria-current', 'page'); | ||
expect(links.at(-1)).toHaveAttribute('aria-current', 'page'); | ||
}); | ||
|
||
it('should move aria-current to item when re-rendering', () => { | ||
renderWithRoot(); | ||
const links = screen.getAllByRole('link'); | ||
expect(links.at(-1)).toHaveAttribute('aria-current', 'page'); | ||
|
||
// Re-render with additional level | ||
render( | ||
<StudioBreadcrumbs aria-label='Du er her:'> | ||
<StudioBreadcrumbs.Link href='#' aria-label='Tilbake til Nivå 3'> | ||
Nivå 3 | ||
</StudioBreadcrumbs.Link> | ||
<StudioBreadcrumbs.List> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 1</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 2</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 3</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 4</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Item> | ||
<StudioBreadcrumbs.Link href='#'>Nivå 5</StudioBreadcrumbs.Link> | ||
</StudioBreadcrumbs.Item> | ||
</StudioBreadcrumbs.List> | ||
</StudioBreadcrumbs>, | ||
); | ||
|
||
expect(links.at(-2)).not.toHaveAttribute('aria-current', 'page'); | ||
expect(links.at(-1)).toHaveAttribute('aria-current', 'page'); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
frontend/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbs.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
import { type HTMLAttributes, forwardRef } from 'react'; | ||
import classes from './StudioBreadcrumbs.module.css'; | ||
|
||
export type StudioBreadcrumbsProps = { | ||
'aria-label'?: string; | ||
} & HTMLAttributes<HTMLElement>; | ||
|
||
const StudioBreadcrumbs = forwardRef<HTMLElement, StudioBreadcrumbsProps>( | ||
({ 'aria-label': ariaLabel = 'You are here:', className, ...rest }, ref) => ( | ||
<nav | ||
aria-label={ariaLabel} | ||
className={`${classes['ds-breadcrumbs']} ${className}`} | ||
ref={ref} | ||
{...rest} | ||
/> | ||
), | ||
); | ||
|
||
StudioBreadcrumbs.displayName = 'StudioBreadcrumbs'; | ||
|
||
export { StudioBreadcrumbs }; |
10 changes: 10 additions & 0 deletions
10
frontend/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbsItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { type HTMLAttributes, forwardRef } from 'react'; | ||
import React from 'react'; | ||
|
||
export type StudioBreadcrumbsItemProps = HTMLAttributes<HTMLLIElement>; | ||
|
||
export const StudioBreadcrumbsItem = forwardRef<HTMLLIElement, StudioBreadcrumbsItemProps>( | ||
function BreadcrumbsItem({ className, ...rest }, ref) { | ||
return <li ref={ref} {...rest} />; | ||
}, | ||
); |
12 changes: 12 additions & 0 deletions
12
frontend/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbsLink.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import React, { forwardRef } from 'react'; | ||
|
||
import { Link } from '@digdir/designsystemet-react'; | ||
import type { LinkProps } from '@digdir/designsystemet-react'; | ||
|
||
export type StudioBreadcrumbsLinkProps = LinkProps; | ||
|
||
export const StudioBreadcrumbsLink = forwardRef<HTMLAnchorElement, StudioBreadcrumbsLinkProps>( | ||
function BreadcrumbsLink(rest, ref) { | ||
return <Link ref={ref} {...rest} />; | ||
}, | ||
); |
23 changes: 23 additions & 0 deletions
23
frontend/libs/studio-components/src/components/StudioBreadcrumbs/StudioBreadcrumbsList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { useMergeRefs } from '@floating-ui/react'; | ||
import { type HTMLAttributes, forwardRef, useEffect, useRef } from 'react'; | ||
import React from 'react'; | ||
|
||
export type StudioBreadcrumbsListProps = HTMLAttributes<HTMLOListElement>; | ||
|
||
export const StudioBreadcrumbsList = forwardRef<HTMLOListElement, StudioBreadcrumbsListProps>( | ||
function BreadcrumbsList(rest, ref) { | ||
const innerRef = useRef<HTMLOListElement>(null); | ||
const mergedRefs = useMergeRefs([innerRef, ref]); | ||
|
||
// Set aria-current on last link | ||
useEffect(() => { | ||
const links = innerRef.current?.querySelectorAll(':scope > * > *') || []; | ||
const lastLink = links[links?.length - 1]; | ||
|
||
lastLink?.setAttribute('aria-current', 'page'); | ||
return () => lastLink?.removeAttribute('aria-current'); // Remove on re-render as React can re-use DOM elements | ||
}); | ||
|
||
return <ol ref={mergedRefs} {...rest} />; | ||
}, | ||
); |
20 changes: 20 additions & 0 deletions
20
frontend/libs/studio-components/src/components/StudioBreadcrumbs/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { StudioBreadcrumbs as StudioBreadcrumbsParent } from './StudioBreadcrumbs'; | ||
import { StudioBreadcrumbsItem } from './StudioBreadcrumbsItem'; | ||
import { StudioBreadcrumbsLink } from './StudioBreadcrumbsLink'; | ||
import { StudioBreadcrumbsList } from './StudioBreadcrumbsList'; | ||
|
||
export const StudioBreadcrumbs = Object.assign(StudioBreadcrumbsParent, { | ||
List: StudioBreadcrumbsList, | ||
Item: StudioBreadcrumbsItem, | ||
Link: StudioBreadcrumbsLink, | ||
}); | ||
|
||
StudioBreadcrumbs.List.displayName = 'StudioBreadcrumbs.List'; | ||
StudioBreadcrumbs.Item.displayName = 'StudioBreadcrumbs.Item'; | ||
StudioBreadcrumbs.Link.displayName = 'StudioBreadcrumbs.Link'; | ||
|
||
export type { StudioBreadcrumbsProps } from './StudioBreadcrumbs'; | ||
export type { StudioBreadcrumbsListProps } from './StudioBreadcrumbsList'; | ||
export type { StudioBreadcrumbsItemProps } from './StudioBreadcrumbsItem'; | ||
export type { StudioBreadcrumbsLinkProps } from './StudioBreadcrumbsLink'; | ||
export { StudioBreadcrumbsList, StudioBreadcrumbsItem, StudioBreadcrumbsLink }; | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters