Hopp til innhold

React

Ferdige interaktive komponenter, og alltid i synk med CSSen.

Funksjonelle komponenter

Vi skriver funksjonelle komponenter med hooks, ikke class components. Hver komponent skrives som regel i sin egen fil, med noen få unntak.

Props eller composite components?

Når man lager komplekse komponenter vil man måtte vurdere om det er riktig med én komponent som tar inn mange props, eller om man skal bryte opp i flere komponenter.

Det vil variere fra gang til gang hva som gir mening. Som regel vil det å bryte opp i flere komponenter gi en større fleksibilitet, men det gjør også at komponenten får mindre kontroll.

Hvis du er usikker, foretrekk én komponent med props.

Kontroll versus fleksibilitet

Det er for eksempel enklere for en skjemakomponent å sørge for at en utvikler gir en label dersom dette er en prop, i stedet for at vi har en egen <Label>-komponent som kanskje eller kanskje ikke blir brukt.

Props enklere å oppdage

Med et moderne uvtiklingsverktøy vil props ofte dukke opp i editoren til utviklere og hjelpe med å oppdage og bruke features i komponenten. Hverken verktøy eller utviklere kan bruke komponenter de ikke vet finnes.

Rest parameters

Komponenter bør ta i mot rest parameters, gjerne kalt rest props i React-verden. Disse bør spreades på det første elementet i render-treet.

Med rest props blir det mulig for brukerne av komponenten å sende inn for eksempel data-attributter eller andre native HTML-attributter uten at disse er eksplisitt lagt inn i Jøkul.

export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
external?: boolean;
}
export const Link: FC<LinkProps> = (props) => {
const { className, children, external = false, ...rest } = props;
return (
<a
{...rest}
className={cn("jkl-link", className, {
"jkl-link--external": external,
})}
>
{children}
</a>
);
};

Rest props kan gi uventede resultater

Noen ganger er det viktig at brukerne av komponenten ikke kan overstyre en prop satt av komponenten. Det er lurt å tenke over hvor på elementet du sprer propsene.

Her er et eksempel fra AlertMessage. Vi setter data-theme="light" fordi bakgrunnsfargen i meldingsboksen gjør at tekstfargen som vanligvis brukes i mørkt tema får for dårlig kontrast. Her vil vi fjerne muligheten for at en bruker kan overstyre propen.

Her er et eksempel hvor det kan gå galt.

<div data-theme="light" {...rest}>

Hvis rest har en prop data-theme: "dark" vil propen vår bli overstyrt.

<div {...rest} data-theme="light">

I eksempelet over derimot vil vår egen data-theme-prop overstyre hva enn som ligger i ...rest, siden vår prop kommer til sist.

Med andre ord, gjør {...rest} før props som ikke skal kunne endres av brukeren. Om du er i tvil, legg {...rest} før alle andre props.

I pakker hvor vi har flere komponenter som jobber sammen, for eksempel i packages/table, kan det skje at en parent component ønsker å sette props på noen av sine children. I disse tilfellene skal det brukes Context.

Det skal ikke finnes props i en komponent sin typedefinisjon som settes automatisk av en annen komponent over den i komponenttreet. Dette er for å unngå forvirring hvor en utvikler setter en sånn prop til noe i sin kode, men opplever at den overskrives av Jøkul.

Typedefinisjoner

I Jøkul bruker vi TypeScript for å kode komponenter og støtteverktøy. Alle komponenter og funksjoner skal være typet.

Komponenter sine props skal være definert som et interface. Interfacenavnet er etter mønsteret KomponentnavnProps.

Interfacet skal eksporteres, så det er tilgjengelig for brukerne av komponenten. Eventuelle egne typer skal også eksporteres. Alle typer starter med stor forbokstav.

export type LoaderVariant = "small" | "medium" | "large";
export interface LoaderProps {
className?: string;
variant?: LoaderVariant;
textDescription: string;
/* ... */
}
/* ... */
export const Loader = (props: LoaderProps): JSX.Element => (
/* ... */
);

Eksporter fra pakkens index

Alle komponenter og typer skal eksporteres fra pakkens main, typisk index.ts på rotnivå i pakken.

export type { DataTableProps } from "./DataTable";
export { DataTable } from "./DataTable";
export type { TableProps } from "./Table";
export { Table } from "./Table";
/* Og så videre */

Tester

Komponenter skal ha enhetstester skrevet i Jest. Testene bør minst dekke happy path slik at vi kan fange opp eventuelle regresjoner.

Minst én av testene må være for å sjekke etter mangler rundt universell utforming med jest-axe. Husk at det kan være UU-mangler selv om axe ikke finner feil – verktøyet fanger opp langt fra alt, og tester bare øyeblikkstilstanden til komponenten. Om komponenten har flere states, gjør en axe-test per state.

Til slutt må komponenten ha en visuell regresjonstest. Til det bruker vi Cypress. Cypress-testen bruker det samme eksempelet som vises i portalen, og det bør være en test for hver mulige tilstand i eksempelet. Se på eksisterende tester (alle filnavnene slutter på .spec.js) for inspirasjon.

Dokumentasjon

Dokumentasjonen for portalen hentes fra de ulike pakkene. Alle React-pakker har en documentation-mappe hvor det typisk ligger en Komponentnavn.mdx-fil, sammen med én elle flere KomponentnavnExample.tsx-filer. Når du lager dokumentasjon lønner det seg å kjøre en utviklingsversjon av hele portalen, altså kjøre pnpm dev fra rotnivå av Jøkul-prosjektet.

Minstekrav til dokumentasjon i portalen er:

  • Kort ingress med komponentens formål
  • Et interaktivt eksempel rett under ingressen
  • Beskrive noen prinsipper for riktig og feil bruk
  • Kodeeksempel som oppdaterer seg etter valgene som er gjort i eksempelet (knobs)
  • React-props dokumentert i bunnen av siden (dette bygges automatisk fra typedefinisjonene)

Se på eksisterende komponentsider for inspirasjon.