Død over tooltips

TypeScript-hack for diskriminerte unioner, død over tooltips og automatisk UU-testing i ukas ForrigeUke.

Utvikler Marcus Haaland i Bekk.
Publisert

<forrigeUke />

ForrigeUke er en artikkelserie som oppsummerer hva som skjedde i frontend-verden i uka som var. Innleggene skrives av frontend-faggruppene til Bekk, og kan også følges på bekk.no/fag.

Dette var uka for retro programmering og obligatoriske ritualer 

— og 2811 ting skjedde i frontendverdenen!

TypeScript-hack for diskriminerte unioner

Du kan regelen? Når TkDodo skriver en bloggpost, må den leses. 

Denne gang deler han om TypeScript-hjelpefunksjonen “Omit”.

“Omit” fjerner en verdi fra en type:

type SelectProps = {
  onChange: (value: string) => void
  value: string | null
  options: ReadonlyArray<SelectOption>
}

// 👇 UserSelectProps utelater "options"
type UserSelectProps = Omit<SelectProps, "options">

Dette er et mønster vi har mange steder i designsystemet vårt, hvor for eksempel inputfelt deler mange props, men noen av verdiene er forskjellige.

Et eksempel er med props for Select, men du ønsker at komponenten skal styre "options" selv:

// 👇 Props setter onChange og value, mens "options" settes i komponent
function UserSelect(props: UserSelectProps) {
  const users = useSuspenseQuery(usersQueryOptions)

  return <Select {...props} options={users.data} />
}

Problemet TkDodo trekker frem, er at når du prøver å bruke Omit sammen med discriminated unions — altså verdier som kan være enten av én gruppe verdier eller en annen.

Vi kan ønske oss å ha en valgfri "clearable"- prop, som skal kunne nullstille Select-en. Men den vil også endre hvordan onChange blir. Så vi trenger at komponenten kan være en av to typer:

type BaseSelectProps = {
  options: ReadonlyArray<SelectOption>
  value: string | null
}

type ClearableSelectProps = BaseSelectProps & {
  clearable: true
  // 👇 Hvis clearable, kan vi sette onChange med null
  onChange: (value: string | null) => void
}

type UnclearableSelectProps = BaseSelectProps & {
  clearable?: false
  // 👇 Hvis ikke clearable, er parameteret alltid en string
  onChange: (value: string) => void
}

type SelectProps = ClearableSelectProps | UnclearableSelectProps

Koden ser grei ut, men IDE-en vil klage når SelectProps tas i bruk på UserSelect-komponenten. Her klarer ikke Omit å ta hensyn til variantene. Litt overraskende, ettersom TypeScript klarer å kjøre Doom kun via typene, bør det være mulig å skrive en Omit-hjelper uten at systemet knekker 😅.

Løsningen er TypeScripts svar på if-setninger, kalt Distributive Conditional Types. De kan du lese mer om i bloggposten: https://tkdodo.eu/blog/omit-for-discriminated-unions-in-type-script

Død over tooltips

Nå kjenner du til regelen.

Og TkDodo liker heller ikke tooltips. Når du ser på bruken av tooltips, er det ikke veldig overraskende.

Det er lett å feile på universell utforming. Tooltip-komponenten fra material UI fungerer for tastatur om du bruker den rundt en ikonknapp, men ikke om du bruker den rundt ikke-interaktive elementer som et ikon:

// 👇 IconButton er interaktiv, så aktiverer Tooltip ved fokus
<Tooltip title="Delete">
  <IconButton>
    
  </IconButton>
</Tooltip>

// 👇 Icon er ikke interaktiv, så aktiverer Tooltip ved hover, men ikke fokus
<Tooltip title="Home">
  <DeleteIcon />
</Tooltip>

Det er fort gjort at utviklere sjekker for hover-effekt, men ikke for tastaturbruk — selv om det er noe du bør gjøre.

Om du bruker Tooltip på en interaktiv komponent, er ikke tooltips- funksjonalitet like tilgjengelig på alle enheter. For eksempel advarerer NAVs komponentbibliotek om at Tooltip-komponenten er mindre egnet for touch-enheter, ettersom hjelpetekst kun aktiveres av fokus eller hover.

Et annet problem er overraskelser. Noen ganger kan du se tooltips brukt løpende i tekst, uten et hint om at det er hjelptekst der. Andre ganger ser du en knapp, som du har ingen anelse om hva gjør — og du skulle gjerne hatt en tooltip.

TkDodos problem er ikke tooltip som en funksjonalitet, men som en komponent. Det er lett å bruke Tooltip-komponenten feil. Heller anbefaler han å bruke tooltip som et mønster, som kan være innbakt i andre komponenter.

Som at en knapp får en hjelpende tekst via “title”-prop. Eller at løpende tekst kan få en tooltip-funksjonalitet via en InfoText-komponent, som har en markering som viser at den er interaktiv og har mer info tilgjengelig.

TkDodo gikk til steget med å markere sin Tooltip-komponent som deprecated. Jeg tror ikke du trenger å gjøre noe så drastisk, men det er absolutt verdt å ta en sjekk på om tooltips-bruken deres er UU-vennlig og ikke gir brukerne noen overraskelser.

Automatisk UU-testing

Apropos UU, kom jeg over en detaljert guide på hvordan du kan teste for universell utforming. Jeg synder selv på test-skriving, men her fant jeg mange tips som krever minimalt med kode.

Selv har jeg brukt Lighthouse sin accessibility-sjekk mye, som gir meg beskjed om hva som er feil på hele sider. Men det er tregt, så det er fort å droppe å starte den automatiske sjekken. Artikkelen tipsa imidlertid om at sjekken også var mulig å gjøre i CI, så da kan jeg garantere at UU-sjekken er gjort, så jeg ikke introduserer nye feil.

Artikkelen introduserte også et annet verktøy jeg ikke hadde hørt om: pa11y. Den gjør lignende sjekk som Lighthouse, men mye raskere.

Den er veldig lett å komme i gang med, så jeg brukte oss likegodt som forsøkskanin:

# installer pa11y i terminalen:
npm install -g pa11y

# kjør en test:
pa11y bekk.no/fag

Jeg skal innrømme at returverdien ikke var tom:

Her viser den at vi har syndet på manglende legend-element i fieldset. Den klagde også på fargekontrast, men da jeg manuelt sjekka, var det hvitt på sort, så jeg vet ikke om jeg så det samme som verktøyet. Men vi tar hvert fall med oss ett issue tilbake til backlogen 🫡.

«Universell utforming — nyttig for alle, nødvendig for noen.»

Å gjøre universell utforming riktig er ganske overveldende, og verktøy kan hjelpe oss, men selv det å sette det opp kan være terskel. Så forfatteren trekker frem 3 minimumskrav alle prosjekter bør oppfylle. Dette er svært enkle grep, som gir masse verdi:

  • alltid bruk eslint-plugin-jsx-a11y, så du får varsler om UU- feil løpende i koden
  • om du bruker Storybook, legg til plugin @storybook/addon-a11y, så du får varsel om brudd, som fargekontrast
  • ikke stol blindt på automatiske tester, så gjør også manuell testing

Automatiske tester kan plukke opp tekniske feil, men manuell testing er også nødvendig. Manuell testing kan oppdage problemer ved brukervennlighet, som at en knapp er umulig å navigere til. Eller at en alt-tekst ikke bare er satt, men også at teksten gir mening i konteksten.

Så jeg anbefaler å hvert fall sjekke av de tre minimumskravene. Også oppfordrer jeg deg å leke litt med resten av verktøyene nevnt i artikkelen. Det er jo ganske fett at små kodeendringer kan ha så stor påvirkning på om noen klarer å bruke appen din eller ikke.

Det var alt for denne gang. Ha en riktig fin uke! 👋

Powered by Labrador CMS