Gå til innholdet

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 starter
import { defineFsErrorSummary } from "@fristil/designsystem/error-summary"
import "@fristil/designsystem/error-summary.css"
defineFsErrorSummary()
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.

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">
<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.

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 ikke autofocus, 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.

KlasseHva det er
.fs-error-summarySelve boksen. Ligger på <fs-error-summary>
.fs-error-summary__titleOverskriften over lista
VariabelReserve
--fs-error-summary-paddingvar(--size-4)
--fs-error-summary-radiusvar(--size-1)
.fs-error-summary {
--fs-error-summary-padding: var(--size-6);
}