Error Summary
Når en innsending stopper, skal brukeren få vite hvorfor, og komme til feltet med ett klikk. Serveren skriver hele boksen. Komponenten gjør de to tingene nettleseren ikke gjør selv: flytter fokus hit når boksen kommer til syne, og gir fokus til selve kontrollen når en lenke følges.
// main.ts, kjøres én gang når appen starterimport { defineFsErrorSummary } from "@fristil/designsystem/error-summary"import "@fristil/designsystem/error-summary.css"
defineFsErrorSummary()Oppsummering med to feil
Section titled “Oppsummering med to feil”Markupen
Section titled “Markupen”const feil = fs.errorSummary({ count: feilene.length, id: "feil" })<fs-error-summary class="fs-error-summary" role="alert" tabindex="-1" id="feil"> <h2 class="fs-error-summary__title">Skjemaet har to feil</h2> <ul class="fs-list"> <li><a href="#epost">Skriv en e-postadresse med krøllalfa</a></li> <li><a href="#fodselsdato">Skriv datoen som dag, måned og år</a></li> </ul></fs-error-summary>Klassen, role="alert", tabindex="-1" og overskriften kommer fra serveren. Komponenten lagde dem tidligere selv, og da forsvant både klassen og overskriften ved første oppdatering fra serveren, uten å komme tilbake. Se Markup og oppførsel.
Er det ingen feil, setter byggefunksjonen hidden på boksen. En tom oppsummering med overskriften «Skjemaet har 0 feil» er verre enn ingen.
Lenkene peker på feltets id
Section titled “Lenkene peker på feltets id”Hver lenke i lista skal peke på id-en til kontrollen som feilet. Peker den på en <label>, følger komponenten for videre til kontrollen.
<li><a href="#fodselsdato">Skriv datoen som dag, måned og år</a></li>...<input class="fs-input" id="fodselsdato" type="date">I en app
Section titled “I en app”<form novalidate> <fs-error-summary class="fs-error-summary" role="alert" tabindex="-1" id="feil" hidden> <h2 class="fs-error-summary__title"></h2> <ul class="fs-list"></ul> </fs-error-summary>
<label class="fs-label" for="epost">E-postadresse</label> <input class="fs-input" id="epost" type="email" /> <button class="fs-button">Send søknad</button></form>
<script type="module"> import { defineFsErrorSummary } from "@fristil/designsystem/error-summary" defineFsErrorSummary()
const boks = document.getElementById("feil")
function visFeil(feilene) { boks.querySelector("ul").innerHTML = feilene .map((f) => `<li><a href="#${f.id}">${f.melding}</a></li>`) .join("")
// Overskriften og hidden skrives her, slik en server ville gjort det. if (feilene.length > 0) { boks.querySelector(".fs-error-summary__title").textContent = feilene.length === 1 ? "Skjemaet har én feil" : `Skjemaet har ${feilene.length} feil` } boks.hidden = feilene.length === 0 }</script>Komponenten flytter fokus hit i det hidden tas bort.
import { fs } from "@fristil/designsystem/react"import { defineFsErrorSummary } from "@fristil/designsystem/error-summary"
defineFsErrorSummary()
export function Feiloppsummering({ feilene }: { feilene: Feil[] }) { const boks = fs.errorSummary({ count: feilene.length, id: "feil" })
return ( <fs-error-summary {...boks.container}> <h2 {...boks.title}> {feilene.length === 1 ? "Skjemaet har én feil" : `Skjemaet har ${feilene.length} feil`} </h2> <ul {...fs.list()}> {feilene.map((f) => ( <li key={f.id}><a href={`#${f.id}`}>{f.melding}</a></li> ))} </ul> </fs-error-summary> )}---import { fs } from "@fristil/designsystem"
const boks = fs.errorSummary({ count: feilene.length, id: "feil" })---
<fs-error-summary {...boks.container}> <h2 {...boks.title}>Skjemaet har {feilene.length} feil</h2> <ul class="fs-list"> {feilene.map((f) => <li><a href={`#${f.id}`}>{f.melding}</a></li>)} </ul></fs-error-summary>
<script> import { defineFsErrorSummary } from "@fristil/designsystem/error-summary" defineFsErrorSummary()</script><fs-error-summary class="fs-error-summary" role="alert" tabindex="-1" id="feil" hidden> <h2 class="fs-error-summary__title"></h2> <ul class="fs-list"></ul></fs-error-summary>Serveren sender hele boksen på nytt når skjemaet valideres, med overskriften og lista ferdig utfylt, og uten hidden når det finnes feil. Komponenten oppdager at boksen ble synlig og flytter fokus dit.
Tilgjengelighet
Section titled “Tilgjengelighet”Boksen er role="alert", så den leses opp når den kommer til syne. Den har tabindex="-1" for å kunne få fokus uten å bli en tabbestopp.
To ting er lette å glemme, og begge er grunnen til at komponenten finnes:
- Fokus flyttes til boksen når den blir synlig. Uten det vet ikke den som hører siden at innsendingen stoppet. Sett
data-autofocus="false"hvis du ikke vil ha det. Attributtet heter ikkeautofocus, fordi det er en boolsk egenskap på elementet, og React 19 setter egenskapen i stedet for attributtet. Da ville avslaget ikke virket. - Fokus går til selve kontrollen når en lenke følges. En vanlig ankerlenke ruller bare dit, og neste tastetrykk fortsetter der fokus sto før, altså i oppsummeringen.
Komponenten kobler seg også på lenker serveren sender inn senere, så en oppsummering som oppdateres mens brukeren retter, fortsetter å virke.
Klasser du kan style
Section titled “Klasser du kan style”| Klasse | Hva det er |
|---|---|
.fs-error-summary | Selve boksen. Ligger på <fs-error-summary> |
.fs-error-summary__title | Overskriften over lista |
| Variabel | Reserve |
|---|---|
--fs-error-summary-padding | var(--size-4) |
--fs-error-summary-radius | var(--size-1) |
.fs-error-summary { --fs-error-summary-padding: var(--size-6);}