Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: Update alert appearance, make ‘warning’ the default and icon non-optional #993

Merged
merged 18 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions packages/css/src/components/alert/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
# Alert

Met een Alert breng je de gebruiker op de hoogte van een belangrijke of tijdgevoelige boodschap. De taak van de gebruiker wordt niet onderbroken.
Met een Alert breng je de gebruiker op de hoogte van een belangrijke of tijdgevoelige boodschap.
De taak van de gebruiker wordt niet onderbroken.

## Specificaties

### Kleurdefinities
Er zijn vier soorten meldingen:

- Geel: informatie
- Rood: fout
- Groen: goed
- **Waarschuwing** (oranje) als er actie nodig is om schade te voorkomen.
- **Foutmelding** (rood) om te laten weten dat er een fout is opgetreden.
- **Bevestiging** (groen) ter geruststelling dat een proces is voltooid.
- **Kennisgeving** (blauw) om een bericht onder de aandacht te brengen.

## Richtlijnen

- Standaard heeft het element geen sluiten knop. Optioneel kan deze toegevoegd worden.
- Optioneel kan een icoon worden weggelaten. Een geel Alert heeft nooit een icoon.
- Optioneel kan een titel worden weggelaten.
- Aangeraden wordt om een Alert in ieder geval van een paragraaf text te voorzien en deze eventueel te ondersteunen met een link, opsomming of knop.
- Plaats bij belangrijke en urgente informatie een oranje Alert direct onder de Header.
Voorbeelden: een storing van een systeem of afwijkende openingstijden van een stadsloket.
- Zorg voor voldoende witruimte rondom de Alert.
De witruimte van het grid is een goede maatstaf – zet de Alert in zijn eigen cel.
- Standaard kan de Alert niet gesloten worden.
Deze mogelijkheid kan toegevoegd worden.
- Optioneel kan de titel worden weggelaten.
35 changes: 11 additions & 24 deletions packages/css/src/components/alert/alert.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

.amsterdam-alert {
align-items: flex-start;
background-color: var(--amsterdam-alert-background-color);
border: var(--amsterdam-alert-border);
border-style: var(--amsterdam-alert-border-style);
border-width: var(--amsterdam-alert-border-width);
display: flex;
flex-direction: row;
gap: var(--amsterdam-alert-gap);
Expand All @@ -21,38 +21,25 @@
flex: auto;
}

@mixin reset {
-webkit-text-size-adjust: 100%;
}

.amsterdam-alert__title {
color: var(--amsterdam-alert-title-color);
font-family: var(--amsterdam-alert-title-font-family);
font-size: var(--amsterdam-alert-title-spacious-font-size);
font-weight: var(--amsterdam-alert-title-font-weight);
line-height: var(--amsterdam-alert-title-spacious-line-height);

.amsterdam-theme--compact & {
font-size: var(--amsterdam-alert-title-compact-font-size);
line-height: var(--amsterdam-alert-title-compact-line-height);
}

@include reset;
}

.amsterdam-alert--error {
background-color: var(--amsterdam-alert-error-background-color);
border-color: var(--amsterdam-alert-error-border-color);
}

.amsterdam-alert--info {
border-color: var(--amsterdam-alert-info-border-color);
}

.amsterdam-alert--success {
background-color: var(--amsterdam-alert-success-background-color);
border-color: var(--amsterdam-alert-success-border-color);
}

.amsterdam-alert--warning {
border-color: var(--amsterdam-alert-warning-border-color);
}

/* todo: move to action button component */
.amsterdam-alert__close {
background-color: var(--amsterdam-alert-close-background-color);
background-color: var(--amsterdam-alert-close-button-background-color);
dlnr marked this conversation as resolved.
Show resolved Hide resolved
border: 0;
cursor: pointer;
padding-block: 0;
Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/components/top-task-link/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Top Task Link

Via dit opvallende navigatie-element vindt de Amsterdammer snel de weg naar veel voorkomende taken.
Via dit opvallende navigatie-element vindt de gebruiker snel de weg naar veel voorkomende taken.

## Richtlijnen

- Een Top Task Link heeft zowel een titel als een beschrijving.
- De titel beschrijft de essentie van de pagina waarnaar wordt verwezen.
Deze trekt de aandacht bij het scannen van de pagina.
- De beschrijving geeft meer context.
Dit helpt de Amsterdammer bevestigen dat die zijn doel op de vervolgpagina inderdaad kan bereiken.
Dit helpt de gebruiker bevestigen dat die zijn doel op de vervolgpagina inderdaad kan bereiken.
- Houd beide teksten bondig. Een titel bestaat uit een paar woorden, de beschrijving uit 1 of 2 korte zinnen. De beschrijving eindigt dan ook op een punt of ander geschikt leesteken.
- Plaats voor brede vensters 3 of 4 Top Task Links naast elkaar op het [Grid](?path=/docs/react_layout-grid--docs).
Als je meer Top Task Links hebt, zet ze dan op de volgende rij.
Expand Down
28 changes: 4 additions & 24 deletions packages/react/src/Alert/Alert.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ describe('Alert', () => {
const { container } = render(<Alert />)

const component = container.querySelector(':only-child')
const icon = component?.querySelector('.amsterdam-alert__icon')
dlnr marked this conversation as resolved.
Show resolved Hide resolved

expect(component).toBeInTheDocument()
expect(component).toBeVisible()
expect(icon).toBeInTheDocument()
alimpens marked this conversation as resolved.
Show resolved Hide resolved
expect(icon).toBeVisible()
})

it('renders a design system BEM class name', () => {
Expand All @@ -26,9 +29,7 @@ describe('Alert', () => {

const component = container.querySelector(':only-child')

expect(component).toHaveClass('extra')

expect(component).toHaveClass('amsterdam-alert')
expect(component).toHaveClass('amsterdam-alert extra')
})

it('supports ForwardRef in React', () => {
Expand All @@ -41,27 +42,6 @@ describe('Alert', () => {
expect(ref.current).toBe(component)
})

it('renders the icon if icon is true and the severity is success', () => {
const { container } = render(<Alert icon={true} severity="success" />)

const component = container.querySelector(':only-child')

const icon = component?.querySelector('.amsterdam-alert__icon')

expect(icon).toBeInTheDocument()
expect(icon).toBeVisible()
})

it('does not render the icon if icon is true and the severity is default', () => {
const { container } = render(<Alert icon={true} />)

const component = container.querySelector(':only-child')

const icon = component?.querySelector('.amsterdam-alert__icon')

expect(icon).not.toBeInTheDocument()
})

it('renders the close button', () => {
const { container } = render(<Alert closeable={true} />)

Expand Down
60 changes: 41 additions & 19 deletions packages/react/src/Alert/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,33 @@
* Copyright (c) 2023 Gemeente Amsterdam
*/

import { AlertIcon, CheckmarkIcon, CloseIcon } from '@amsterdam/design-system-react-icons'
import { AlertIcon, CheckmarkIcon, CloseIcon, InfoIcon } from '@amsterdam/design-system-react-icons'
import clsx from 'clsx'
import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, useMemo } from 'react'
import { forwardRef } from 'react'
import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'
import { Heading } from '../Heading'
import type { HeadingProps } from '../Heading'
import { Icon } from '../Icon'
import { VisuallyHidden } from '../VisuallyHidden'

export interface AlertProps extends PropsWithChildren<HTMLAttributes<HTMLDivElement>> {
title?: string
severity?: undefined | 'error' | 'success'
/** Whether the alert can be dismissed by the user. Adds a button to the top right. */
closeable?: boolean
icon?: boolean
/**
* The hierarchical level of the alert title within the document.
* @default 2
*/
headingLevel?: HeadingProps['level']
/** Allows a callback when dismissing the alert. */
onClose?: () => void
/** Highlights the meaning or tone of the message. */
severity?: 'error' | 'info' | 'success' | 'warning'
/** The title for the alert. */
title?: string
}

interface AlertCloseProps extends HTMLAttributes<HTMLButtonElement> {
size?: 'level-5' | 'level-6'
size?: 'level-4' | 'level-5'
}

const AlertClose = forwardRef(
Expand All @@ -32,37 +43,48 @@ const AlertClose = forwardRef(

const iconSvgBySeverity = {
error: AlertIcon,
info: InfoIcon,
success: CheckmarkIcon,
warning: AlertIcon,
}

export const Alert = forwardRef(
(
{ children, className, title, severity, closeable, icon, onClose, ...restProps }: AlertProps,
{
children,
className,
headingLevel = 2,
title,
severity = 'warning',
closeable,
onClose,
...restProps
}: AlertProps,
ref: ForwardedRef<HTMLDivElement>,
) => {
const alertSize = title ? 'level-5' : 'level-6'
const alertSize = title ? 'level-4' : 'level-5'

const alertIcon = useMemo(() => {
if (!icon || !severity) {
return null
}

return <Icon size={alertSize} svg={iconSvgBySeverity[severity]} />
}, [icon, severity, alertSize])
const Element = title ? 'section' : 'div'

return (
<div
<Element
{...restProps}
ref={ref}
className={clsx('amsterdam-alert', severity && `amsterdam-alert--${severity}`, className)}
>
{alertIcon && <div className="amsterdam-alert__icon">{alertIcon}</div>}
<div className="amsterdam-alert__icon">
<Icon size={alertSize} svg={iconSvgBySeverity[severity]} />
</div>
<div className="amsterdam-alert__content">
{title && <span className="amsterdam-alert__title">{title}</span>}
{title && (
<Heading level={headingLevel} size="level-4">
{title}
</Heading>
)}
{children}
</div>
{closeable && <AlertClose size={alertSize} onClick={onClose} />}
</div>
</Element>
)
},
)
Expand Down
24 changes: 15 additions & 9 deletions proprietary/tokens/src/components/amsterdam/alert.tokens.json
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
{
"amsterdam": {
"alert": {
"background-color": { "value": "{amsterdam.color.yellow}" },
"border": { "value": "4px solid {amsterdam.color.yellow}" },
"border-width": { "value": "4px" },
"border-style": { "value": "solid" },
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
"gap": { "value": "1rem" },
"padding-block-start": { "value": "{amsterdam.spacing.inset.md}" },
"padding-block-end": { "value": "{amsterdam.spacing.inset.md}" },
"padding-inline-start": { "value": "{amsterdam.spacing.inset.lg}" },
"padding-inline-end": { "value": "{amsterdam.spacing.inset.lg}" },
"close-button": {
dlnr marked this conversation as resolved.
Show resolved Hide resolved
"background-color": { "value": "amsterdam.color.primary-white" }
},
"title": {
"color": { "value": "{amsterdam.color.primary-black}" },
"font-family": { "value": "{amsterdam.typography.font-family}" },
"font-weight": { "value": "{amsterdam.typography.font-weight.bold}" },
"spacious": {
"font-size": { "value": "{amsterdam.typography.spacious.text-level.5.font-size}" },
"line-height": { "value": "{amsterdam.typography.spacious.text-level.5.line-height}" }
"font-size": { "value": "{amsterdam.typography.spacious.text-level.4.font-size}" },
"line-height": { "value": "{amsterdam.typography.spacious.text-level.4.line-height}" }
},
"compact": {
"font-size": { "value": "{amsterdam.typography.compact.text-level.5.font-size}" },
"line-height": { "value": "{amsterdam.typography.compact.text-level.5.line-height}" }
"font-size": { "value": "{amsterdam.typography.compact.text-level.4.font-size}" },
"line-height": { "value": "{amsterdam.typography.compact.text-level.4.line-height}" }
}
},
"error": {
"background-color": { "value": "{amsterdam.color.primary-white}" },
"border-color": { "value": "{amsterdam.color.primary-red}" }
},
"info": {
"border-color": { "value": "{amsterdam.color.primary-blue}" }
},
"success": {
"background-color": { "value": "{amsterdam.color.primary-white}" },
"border-color": { "value": "{amsterdam.color.dark-green}" }
},
"warning": {
"border-color": { "value": "{amsterdam.color.orange}" }
},
"close": {
"background-color": { "value": "transparent" },
"fill": { "value": "{amsterdam.color.primary-black}" },
"hover": {
"fill": { "value": "{amsterdam.color.primary-blue}" }
Expand Down
44 changes: 38 additions & 6 deletions storybook/storybook-react/src/Alert/Alert.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,54 @@ import README from "../../../../packages/css/src/components/alert/README.md?raw"

<Markdown>{README}</Markdown>

## Voorbeelden

De standaard Alert is een waarschuwing.

<Primary />

<Controls />

### Information with inline link
### Warning

<Canvas of={AlertStories.InformationWithInlineLink} />
Toon een waarschuwing als er actie van de gebruiker nodig is.

### Error
<Canvas of={AlertStories.Warning} />

<Canvas of={AlertStories.Error} />
### Error

### Error with list
Als er een fout is opgetreden gebruiken we de rode alert.
Geef de gebruiker suggesties om het doel op een andere manier te bereiken.

<Canvas of={AlertStories.ErrorWithList} />
<Canvas of={AlertStories.Error} />

### Success

Laat het weten als een belangrijke actie geslaagd is.

<Canvas of={AlertStories.Success} />

### Info

Een informatieve melding kan meer nadruk te geven aan zaken die handig zijn om te volgen.

<Canvas of={AlertStories.Info} />

### With inline link

In de tekst kan een inline link staan om de gebruiker op weg te helpen.

<Canvas of={AlertStories.WithInlineLink} />

### With list

Ter verduidelijking kan opgemaakte tekst in de alert worden opgenomen.

<Canvas of={AlertStories.WithList} />

### Without title

Soms is een titel niet nodig.
Het icoon wordt automatisch iets kleiner.

<Canvas of={AlertStories.WithoutTitle} />
Loading