Skip to content

Commit

Permalink
feat: actions slot for alert
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbert committed Jun 22, 2023
1 parent 1c16253 commit a91da02
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 4 deletions.
58 changes: 58 additions & 0 deletions components/alert/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,61 @@
# Alert

Belangrijk bericht dat informeert over de huidige activiteit van de gebruiker.

## Toepassing

De _alert component_ is er voor berichten die de gebruiker snel moet weten, omdat ze belangrijk zijn voor het uitvoeren van de huidige taak. De alert is alleen voor eenvoudige berichten. Gebruik in de alert geen buttons, geen formulier-componenten en geen complexe opmaak zoals tabellen.

Let op: de alert component gebruiken kan essentieël zijn voor gebruikers van een schermvoorlezer, maar onjuist gebruik kan heel erg vervelend zijn.

Gebruik niet een alert voor een algemene aankondiging die op meerdere pagina's staat, als het niet per se relevant is voor de huidige taak van de bezoeker. De alert wordt door schermvoorlezers als eerste voorgelezen op elke pagina waar de alert staat, het kan lastig zijn de website te gebruiken als de schermlezer elke keer wordt geblokkeerd met steeds dezelfde melding. Gebruik in die situaties de notification banner component.

## Componenten die lijken op alert

- de _alert_: wordt zo snel mogelijk aangekondigd.
- de _notification banner_: wordt ook snel aangekondigd, omdat het één van eerste onderdelen van de pagina is als je de banner plaatst aan het begin van de page header. Je kunt de banner overslaan een _skip link_.
- de _error appearance_ van de _form field description_: voor feedback in een formulier die hoort bij een form control. Wordt aangekondigd door schermlezers wanneer de form control focus heeft.

## Tekst

Schrijf een bericht voor de alert die ook duidelijk is als de gebruiker geen icoon of signaalkleur ziet.

Let op: als de tekst met een script aangepast wordt dan wordt de alert in zijn geheel nogmaals voorgelezen door een schermvoorlezer. Een tekst met een veranderend onderdeel zoals "Over 14 minuten en 59 seconden verloopt je sessie.", heeft als effect dat de schermlezer alleen nog de alert voorleest en de pagina verder onbruikbaar is.

## HTML

Het belangrijkste onderdeel van de alert is het bericht, plaats die in een `<div role="alert">`.

Gebruik bij voorkeur een _paragraph component_ voor de inhoud van het bericht, voor een goede `font-size` en `line-height` en zodat de alert `margin` heeft wanneer de CSS niet geladen kan worden.

Als je wel een button gebruikt (bijvoorbeeld om de alert te verbergen), plaats die dan nooit binnen het element met `role="alert"`, maar daarbuiten.

```html
<div class="utrecht-alert">
<div class="utrecht-alert__icon">
<!-- optioneel: een icon -->
</div>
<div class="utrecht-alert__message" role="alert">
<!-- het bericht, bijvoorbeeld: -->
<p class="utrecht-paragraph">Let op: er is nu een storing waardoor...</p>
</div>
<div class="utrecht-alert__actions">
<!-- optioneel en nog niet getest met gebruikers: een button, buiten het bericht -->
</div>
</div>
```

## _Actions_

Zorg dat er alternatieve manieren zijn om de acties in een alert uit te voeren. Als er geen alternatieve manier is, gebruik dan een _alert dialog_ in plaats van een _alert_.

Actions zoals buttons worden in de praktijk vaak gebruikt bij een alert, maar er is nog niet een duidelijk stappenplan om de acties toegankelijk te maken.

Screen reader gebruikers krijgen alleen het bericht in de _message_, omdat die `aria-live="true"` heeft. Links en buttons in de _message_ worden niet aangekondigd als link of button. De toetsenbord focus kan heel ergens anders op de pagina zijn, waardoor het voor een gebruiker onduidelijk hoe je een _call to action_ moet uitvoeren.

## Relevante WCAG eisen

- [WCAG eis 1.1.1](https://www.w3.org/TR/WCAG21/#non-text-content): als de alert een icoon gebruikt met een bepaalde betekenis, moet de betekenis ook uit de tekst blijken.
- [WCAG eis 1.4.1](https://www.w3.org/TR/WCAG21/#use-of-color): als de alert een signaalkleur gebruikt moet de tekst datzelfde ook duidelijk maken, bijvoorbeeld met signaalwoorden. Gebruik bijvoorbeeld "Let op:" voor een waarschuwing.
- [WCAG eis 2.2.1](https://www.w3.org/TR/WCAG21/#timing-adjustable): laat de alert niet automatisch verdwijnen na een bepaalde tijd (lees de WCAG specificatie voor enkele uitzonderingen).
> > > > > > > e9a92eb92f (feat: actions slot for alert)
9 changes: 9 additions & 0 deletions components/alert/_alert-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- @license CC0-1.0 -->

<!-- markdownlint-disable first-line-h1 -->

Zorg dat er alternatieve manieren zijn om de acties in een alert uit te voeren. Als er geen alternatieve manier is, gebruik dan een _alert dialog_ in plaats van een _alert_.

Actions zoals buttons worden in de praktijk vaak gebruikt bij een alert, maar er is nog niet een duidelijk stappenplan om de acties toegankelijk te maken.

Screen reader gebruikers krijgen alleen het bericht in de _message_, omdat die `aria-live="true"` heeft. Links en buttons in de _message_ worden niet aangekondigd als link of button. De toetsenbord focus kan heel ergens anders op de pagina zijn, waardoor het voor een gebruiker onduidelijk hoe je een _call to action_ moet uitvoeren.
4 changes: 4 additions & 0 deletions components/alert/src/_mixin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
row-gap: var(--utrecht-alert-message-row-gap);
}

@mixin utrecht-alert__actions {
grid-area: actions;
}

@mixin utrecht-alert-type($type) {
--_utrecht-alert-icon-color: var(--utrecht-alert-icon-#{$type}-color);
--_utrecht-alert-background-color: var(--utrecht-alert-#{$type}-background-color);
Expand Down
4 changes: 4 additions & 0 deletions components/alert/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
@include utrecht-alert;
}

.utrecht-alert__actions {
@include utrecht-alert__actions;
}

.utrecht-alert__icon {
@include utrecht-alert__icon;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/component-library-react/src/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import { ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode
export type AlertType = 'info' | 'ok' | 'warning' | 'error';

export interface AlertProps extends HTMLAttributes<HTMLDivElement> {
actions?: ReactNode;
icon?: ReactNode;
type?: string | AlertType;
}

export const Alert = forwardRef(
(
{ children, className, icon, type, ...restProps }: PropsWithChildren<AlertProps>,
{ actions, children, className, icon, type, ...restProps }: PropsWithChildren<AlertProps>,
ref: ForwardedRef<HTMLDivElement>,
) => (
<div
Expand All @@ -38,6 +39,7 @@ export const Alert = forwardRef(
<div className="utrecht-alert__message" role="alert">
{children}
</div>
{actions && <div className="utrecht-alert__actions">{actions}</div>}
</div>
</div>
),
Expand Down
85 changes: 85 additions & 0 deletions packages/storybook-css/src/Alert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import technologyHtmlDocs from '@utrecht/alert-css/docs/technology-html.nl.md?ra
import usageDocs from '@utrecht/alert-css/docs/usage.nl.md?raw';
import wcagDocs from '@utrecht/alert-css/docs/wcag.nl.md?raw';
import tokensDefinition from '@utrecht/alert-css/src/tokens.json';
import {
Button,
ButtonGroup,
Link,
UnorderedList,
UnorderedListItem,
} from '@utrecht/component-library-react/src/css-module';
import alertActions from '@utrecht/components/alert/_alert-actions.md?raw';
import tokens from '@utrecht/design-tokens/dist/index.json';
import iconSet from '@utrecht/icon/dist/index.json';
import { mergeMarkdown } from '@utrecht/storybook-helpers/src/markdown';
Expand Down Expand Up @@ -93,24 +101,101 @@ export const Info: Story = {
args: {
type: 'info',
},
parameters: {
status: {
type: 'ALPHA',
},
},
};

export const OK: Story = {
args: {
type: 'ok',
},
parameters: {
status: {
type: 'ALPHA',
},
},
};

export const Warning: Story = {
args: {
type: 'warning',
},
parameters: {
status: {
type: 'ALPHA',
},
},
};

export const Error: Story = {
args: {
type: 'error',
},
parameters: {
status: {
type: 'ALPHA',
},
},
};

export const CallToActionLinks: Story = {
parameters: {
docs: {
description: {
story: `De link-teksten in dit voorbeeld zijn speciaal zo geschreven dat het ook duidelijke instructies zijn wanneer je niet weet dat het links zijn.
Als je de link-teksten goed schrijft dan is de alert ook duidelijk wanneer een _screen reader_ de alert aankondigt.`,
},
},
status: {
type: 'WORK IN PROGRESS',
},
},
args: {
type: 'error',
children: (
<>
<Heading2>Probleem met ingevulde gegevens</Heading2>
<UnorderedList>
<UnorderedListItem>
<Link href="#given-name">Vul je voornaam in.</Link>
</UnorderedListItem>
<UnorderedListItem>
<Link href="#postal-code">Vul een postcode in, bijvoorbeeld: 1011 AB.</Link>
</UnorderedListItem>
</UnorderedList>
</>
),
},
};

export const ActionsWarning: Story = {
parameters: {
docs: {
description: {
story: alertActions,
},
},
status: {
type: 'WORK IN PROGRESS',
},
},
args: {
type: 'warning',
children: (
<Paragraph>
De sessie is afgelopen, omdat je 15 minuten niets hebt gedaan. Je kan weer opnieuw beginnen.
</Paragraph>
),
actions: (
<ButtonGroup>
<Button appearance="primary-action-button">Opnieuw beginnen</Button>
</ButtonGroup>
),
},
};

export const WithIcon: Story = {
Expand Down
4 changes: 3 additions & 1 deletion packages/storybook-css/src/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import clsx from 'clsx';
import React, { PropsWithChildren, ReactNode } from 'react';

export interface AlertProps extends PropsWithChildren {
actions?: ReactNode;
icon?: ReactNode;
type?: string;
}

export const Alert = ({ children, icon = null, type }: PropsWithChildren<AlertProps>) => (
export const Alert = ({ actions, children, icon = null, type }: PropsWithChildren<AlertProps>) => (
<div
className={clsx('utrecht-alert', {
'utrecht-alert--error': type === 'error',
Expand All @@ -23,5 +24,6 @@ export const Alert = ({ children, icon = null, type }: PropsWithChildren<AlertPr
{children}
</div>
</div>
{actions && <div className="utrecht-alert__actions">{actions}</div>}
</div>
);
43 changes: 41 additions & 2 deletions packages/storybook-react/src/stories/Alert.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import type { Meta, StoryObj } from '@storybook/react';
import readme from '@utrecht/alert-css/README.md?raw';
import tokensDefinition from '@utrecht/alert-css/dist/tokens.mjs';
import { Alert, AlertProps, Heading1, Paragraph } from '@utrecht/component-library-react/dist/css-module';
import {
Alert,
AlertProps,
Button,
ButtonGroup,
Heading1,
Link,
Paragraph,
} from '@utrecht/component-library-react/dist/css-module';
import tokens from '@utrecht/design-tokens/dist/list.mjs';
import iconSet from '@utrecht/icon/dist/iconset.mjs';
import React from 'react';
Expand All @@ -13,7 +21,11 @@ interface AlertStoryProps extends AlertProps {

const AlertStory = ({ children, icon, ...props }: AlertStoryProps) => {
const IconElement = icon;
return <Alert icon={IconElement ? <IconElement /> : null}>{children}</Alert>;
return (
<Alert icon={IconElement ? <IconElement /> : null} {...props}>
{children}
</Alert>
);
};

const meta = {
Expand Down Expand Up @@ -93,4 +105,31 @@ export const WithIcon: Story = {
},
};

export const ActionsWarning: Story = {
args: {
type: 'warning',
children: (
<Paragraph>
De sessie is afgelopen, omdat je 15 minuten niets hebt gedaan. Je kan weer opnieuw beginnen.
</Paragraph>
),
actions: (
<ButtonGroup>
<Button appearance="primary-action-button">Opnieuw beginnen</Button>
</ButtonGroup>
),
},
};
export const LinkActionsWarning: Story = {
args: {
type: 'warning',
children: <Paragraph>Uw sessie is verlopen.</Paragraph>,
actions: (
<Paragraph>
Gebruik deze link om <Link href="#">opnieuw te beginnen</Link>.
</Paragraph>
),
},
};

export const DesignTokens = designTokenStory(meta);

0 comments on commit a91da02

Please sign in to comment.