-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Introduce separate Link List component (#1051)
- Loading branch information
1 parent
d97d023
commit 7ccf23d
Showing
20 changed files
with
687 additions
and
118 deletions.
There are no files selected for viewing
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
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,16 @@ | ||
# Link List | ||
|
||
A collection of related links. | ||
|
||
## Design | ||
|
||
Every list item starts with a chevron. | ||
It emphasizes the list structure and thematic coherence. | ||
The chevron is part of the link. | ||
Therefore, it is blue and clickable. | ||
|
||
## Guidelines | ||
|
||
Use a Link List to present multiple links within a theme. | ||
|
||
For additional guidelines, refer to the [Link](?path=/docs/navigation-link--docs) component. |
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,80 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright (c) 2024 Gemeente Amsterdam | ||
*/ | ||
|
||
@mixin reset-list { | ||
box-sizing: border-box; | ||
list-style: none; | ||
margin-block: 0; | ||
padding-inline-start: 0; | ||
-webkit-text-size-adjust: 100%; | ||
} | ||
|
||
.amsterdam-link-list { | ||
@include reset-list; | ||
|
||
display: grid; | ||
gap: var(--amsterdam-link-list-gap); | ||
} | ||
|
||
.amsterdam-link-list__link { | ||
align-items: flex-start; | ||
color: var(--amsterdam-link-list-link-color); | ||
display: inline-flex; | ||
font-family: var(--amsterdam-link-list-link-font-family); | ||
font-size: var(--amsterdam-link-list-link-spacious-medium-font-size); | ||
font-weight: var(--amsterdam-link-list-link-font-weight); | ||
gap: var(--amsterdam-link-list-link-gap); | ||
line-height: var(--amsterdam-link-list-link-spacious-medium-line-height); | ||
outline-offset: var(--amsterdam-link-list-link-outline-offset); | ||
text-decoration-line: var(--amsterdam-link-list-link-text-decoration-line); | ||
text-decoration-thickness: var(--amsterdam-link-list-link-text-decoration-thickness); | ||
text-underline-offset: var(--amsterdam-link-list-link-text-underline-offset); | ||
|
||
.amsterdam-theme--compact & { | ||
font-size: var(--amsterdam-link-list-link-compact-medium-font-size); | ||
line-height: var(--amsterdam-link-list-link-compact-medium-line-height); | ||
} | ||
|
||
&:hover { | ||
color: var(--amsterdam-link-list-link-hover-color); | ||
text-decoration-line: var(--amsterdam-link-list-link-hover-text-decoration-line); | ||
} | ||
} | ||
|
||
.amsterdam-link-list__link--small { | ||
font-size: var(--amsterdam-link-list-link-spacious-small-font-size); | ||
line-height: var(--amsterdam-link-list-link-spacious-small-line-height); | ||
|
||
.amsterdam-theme--compact & { | ||
font-size: var(--amsterdam-link-list-link-compact-small-font-size); | ||
line-height: var(--amsterdam-link-list-link-compact-small-line-height); | ||
} | ||
} | ||
|
||
.amsterdam-link-list__link--large { | ||
font-size: var(--amsterdam-link-list-link-spacious-large-font-size); | ||
line-height: var(--amsterdam-link-list-link-spacious-large-line-height); | ||
|
||
.amsterdam-theme--compact & { | ||
font-size: var(--amsterdam-link-list-link-compact-large-font-size); | ||
line-height: var(--amsterdam-link-list-link-compact-large-line-height); | ||
} | ||
} | ||
|
||
.amsterdam-link-list__link--on-background-dark { | ||
color: var(--amsterdam-link-list-link-on-background-dark-color); | ||
|
||
&:hover { | ||
color: var(--amsterdam-link-list-link-on-background-dark-hover-color); | ||
} | ||
} | ||
|
||
.amsterdam-link-list__link--on-background-light { | ||
color: var(--amsterdam-link-list-link-on-background-light-color); | ||
|
||
&:hover { | ||
color: var(--amsterdam-link-list-link-on-background-light-hover-color); | ||
} | ||
} |
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
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,41 @@ | ||
import '@testing-library/jest-dom' | ||
import { render, screen } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { LinkList } from './LinkList' | ||
|
||
describe('Link list', () => { | ||
it('renders', () => { | ||
render(<LinkList />) | ||
|
||
const component = screen.getByRole('list') | ||
|
||
expect(component).toBeInTheDocument() | ||
expect(component).toBeVisible() | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
render(<LinkList />) | ||
|
||
const component = screen.getByRole('list') | ||
|
||
expect(component).toHaveClass('amsterdam-link-list') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
render(<LinkList className="extra" />) | ||
|
||
const component = screen.getByRole('list') | ||
|
||
expect(component).toHaveClass('amsterdam-link-list extra') | ||
}) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLUListElement>() | ||
|
||
render(<LinkList ref={ref} />) | ||
|
||
const component = screen.getByRole('list') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
}) |
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,29 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright (c) 2024 Gemeente Amsterdam | ||
*/ | ||
|
||
import clsx from 'clsx' | ||
import { forwardRef } from 'react' | ||
import type { ForwardedRef, ForwardRefExoticComponent, HTMLAttributes, PropsWithChildren, RefAttributes } from 'react' | ||
import { LinkListLink } from './LinkListLink' | ||
|
||
export interface LinkListProps extends PropsWithChildren<HTMLAttributes<HTMLUListElement>> {} | ||
|
||
interface LinkListComponent extends ForwardRefExoticComponent<LinkListProps & RefAttributes<HTMLUListElement>> { | ||
Link: typeof LinkListLink | ||
} | ||
|
||
/** A collection of related links. */ | ||
export const LinkList = forwardRef( | ||
({ children, className, ...restProps }: LinkListProps, ref: ForwardedRef<HTMLUListElement>) => { | ||
return ( | ||
<ul ref={ref} className={clsx('amsterdam-link-list', className)} {...restProps}> | ||
{children} | ||
</ul> | ||
) | ||
}, | ||
) as LinkListComponent | ||
|
||
LinkList.Link = LinkListLink | ||
LinkList.displayName = 'LinkList' |
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,63 @@ | ||
import '@testing-library/jest-dom' | ||
import { render, screen } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { LinkList } from './LinkList' | ||
|
||
describe('Link list link', () => { | ||
it('renders', () => { | ||
const { container } = render(<LinkList.Link href="#" />) | ||
|
||
const listItem = screen.getByRole('listitem') | ||
const link = screen.getByRole('link') | ||
const icon = container.querySelector('svg') | ||
|
||
expect(listItem).toBeInTheDocument() | ||
expect(listItem).toBeVisible() | ||
expect(link).toBeInTheDocument() | ||
expect(link).toBeVisible() | ||
expect(icon).toBeInTheDocument() | ||
expect(icon).toBeVisible() | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
render(<LinkList.Link href="#" />) | ||
|
||
const component = screen.getByRole('link') | ||
|
||
expect(component).toHaveClass('amsterdam-link-list__link') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
render(<LinkList.Link href="#" className="extra" />) | ||
|
||
const component = screen.getByRole('link') | ||
|
||
expect(component).toHaveClass('amsterdam-link-list__link extra') | ||
}) | ||
|
||
it('renders a class name for the small size', () => { | ||
render(<LinkList.Link href="#" size="small" />) | ||
|
||
const component = screen.getByRole('link') | ||
|
||
expect(component).toHaveClass('amsterdam-link-list__link--small') | ||
}) | ||
|
||
it('renders a class name for the background color', () => { | ||
render(<LinkList.Link href="#" onBackground="dark" />) | ||
|
||
const component = screen.getByRole('link') | ||
|
||
expect(component).toHaveClass('amsterdam-link-list__link--on-background-dark') | ||
}) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLAnchorElement>() | ||
|
||
render(<LinkList.Link href="#" ref={ref} />) | ||
|
||
const component = screen.getByRole('link') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
}) |
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,73 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright (c) 2024 Gemeente Amsterdam | ||
*/ | ||
|
||
import { ChevronRightIcon } from '@amsterdam/design-system-react-icons' | ||
import clsx from 'clsx' | ||
import { forwardRef } from 'react' | ||
import type { | ||
AnchorHTMLAttributes, | ||
ForwardedRef, | ||
ForwardRefExoticComponent, | ||
PropsWithChildren, | ||
RefAttributes, | ||
} from 'react' | ||
import { Icon } from '../Icon' | ||
|
||
type BackgroundName = 'default' | 'light' | 'dark' | ||
|
||
export interface LinkListLinkProps extends PropsWithChildren<AnchorHTMLAttributes<HTMLAnchorElement>> { | ||
/** The target url for the link. */ | ||
href: string | ||
/** | ||
* An icon to display instead of the default chevron. | ||
* Don’t mix custom icons with chevrons in one list. | ||
*/ | ||
icon?: Function | ||
/** Whether the link sits on a dark background. */ | ||
onBackground?: BackgroundName | ||
/** | ||
* The text size for the link. | ||
* Use the same size for all items in the list. | ||
*/ | ||
size?: 'small' | 'large' | ||
} | ||
|
||
interface LinkListLinkComponent | ||
extends ForwardRefExoticComponent<LinkListLinkProps & RefAttributes<HTMLAnchorElement>> {} | ||
|
||
const iconSizeMap = { | ||
small: 'level-6', | ||
medium: 'level-5', | ||
large: 'level-4', | ||
} as const | ||
|
||
/** One link with a Link List. */ | ||
export const LinkListLink = forwardRef( | ||
( | ||
{ children, className, href, icon, onBackground, size, ...restProps }: LinkListLinkProps, | ||
ref: ForwardedRef<HTMLAnchorElement>, | ||
) => { | ||
return ( | ||
<li> | ||
<a | ||
className={clsx( | ||
'amsterdam-link-list__link', | ||
onBackground && `amsterdam-link-list__link--on-background-${onBackground}`, | ||
size && `amsterdam-link-list__link--${size}`, | ||
className, | ||
)} | ||
href={href} | ||
ref={ref} | ||
{...restProps} | ||
> | ||
<Icon svg={icon ?? ChevronRightIcon} size={iconSizeMap[size ?? 'medium']} /> | ||
{children} | ||
</a> | ||
</li> | ||
) | ||
}, | ||
) as LinkListLinkComponent | ||
|
||
LinkListLink.displayName = 'LinkList.Link' |
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,3 @@ | ||
# React Link List component | ||
|
||
[Link List documentation](../../../css/src/link-list/README.md) |
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,3 @@ | ||
export { LinkList } from './LinkList' | ||
export type { LinkListProps } from './LinkList' | ||
export type { LinkListLinkProps } from './LinkListLink' |
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
Oops, something went wrong.