Hopp til innhold

Hvordan lage et nytt prosjekt med Jøkul

Når du skal bruke Jøkul i et nytt prosjekt er det en del å huske på.

Designer som skal lage et nytt prosjekt?

Nye skisser i Figma trenger bare sørge for at Jøkul sitt komponentbibliotek er i bruk under Assets. Etter det blir Jøkul tilgjengelig i skissene dine.

Resten av denne guiden retter seg mot utviklere.

Pakker

Om du skal bygge et nytt prosjekt med Jøkul er det noen grunnleggende pakker du alltid må ha installert:

I tillegg har vi pakker for mange ulike komponenter, og noen pakker med hjelpefunksjoner for validering, formatering, og nyttige React hooks. Det er lettere å få oversikt over komponentene og hva de gjør under Komponenter.

Installer avhengigheter

For å installere det grunnleggende:

yarn add @fremtind/jkl-core @fremtind/jkl-webfonts
yarn add --dev @fremtind/browserslist-config-jkl
Vi bruker Yarn for pakkehåndtering i Jøkul. Hvis ditt prosjekt bruker NPM, kan du selvfølgelig installere pakkene med npm install som vanlig.

Kort om de forskjellige pakkene i Jøkul

Jøkuls kode er delt inn i mange pakker, typisk to pakker per komponent.

Hvorfor to?

Vi skiller på CSS-pakker og React-pakker i Jøkul. Du kan bruke komponentene i Jøkul som rene stilark dersom prosjektet ditt ikke bruker React, for eksempel om du skal bruke Jøkul i et CMS. Alle CSS-stilark finnes i både minifisert og uminifisert utgave. Sørg for å bruke den minifiserte i produksjon.

Det er sterkt anbefalt å bruke React-komponentene om du overhode har mulighet.

Hvis du bruker React-komponenten blir stilpakken automatisk installert som en avhengighet. Du trenger med andre ord ikke både React- og stilpakken i package.json.

Et eksempel på bruk av en komponent

Ta Accordion som et eksempel. Skal du bruke React-komponenten installerer du den med yarn add @fremtind/jkl-accordion-react og får automatisk med riktig versjon av @fremtind/jkl-accordion som en avhengighet. Dersom du bare bruker stilpakken, installerer du den med yarn add @fremtind/jkl-accordion.

Når du skal ta i bruk en React-komponent trenger du å laste inn både stilen og komponenten. Hvis du har satt opp en CSS-loader i prosjektet ditt kan du importere stilen direkte i TypeScript. Du kan også velge å importere den i en av dine .scss- eller .css-filer.

import "@fremtind/jkl-core/core.min.css";
import "@fremtind/jkl-accordion/accordion.min.css";
import { Accordion, AccordionItem } from "@fremtind/jkl-accordion";

Initialisering

For at komponentene i Jøkul skal virke riktig trenger de å vite om brukeren navigerer i løsningen med mus, tastatur eller touch. Det gjøres ved å kjøre initTabListener funksjonen fra @fremtind/jkl-core. Det trengs bare gjøres én gang, helst så tidlig som det lar seg gjøre. Normalt vil det være i entrypointet i applikasjonen din.

initTabListener ser på hvordan siden interageres med – museklikk, tastetrykk, eller touch – og setter automatisk et data-attributt på <body>, som Jøkul bruker til blant annet å vise enda tydeligere fokusringer ved tastaturnavigasjon.

import { initTabListener } from "@fremtind/jkl-core";

initTabListener();

Fonter

For at blant annet fonter skal bli riktige må du sette jkl-klassen på rotnivå i appen din, for eksempel på <body> eller <div>-en hvor appen din rendres.

<div class="jkl" id="app">
    <!-- app rendres her -->
</div>

Bruk med Sass

Om du bruker Sass-stilark i prosjektet ditt er dette den enkleste måten å få riktige fonter. I rot-.scss-filen din:

// NB! bruk riktig importsyntaks for din sass-loader (~ er brukt her)
@use "~@fremtind/jkl-webfonts/webfonts.scss" with (
    $webfonts-dir: "../relative/path/to/node_modules/@fremtind/jkl-webfonts/fonts"
);

Variabelen $webfonts-dir angir hvor filene ligger. Hvis den ikke spesifiseres vil stilarket se etter fontfilene i mappen /fonts.

Bruk med CSS

Om du ikke bruker Sass kan du benytte deg av den vanlige .css-versjonen. Denne versjonen ser kun etter fontfilene i mappen /fonts. Om du vil legge fontfilene et annet sted må du kopiere CSS-filen node_modules/@fremtind/jkl-webfonts/webfonts.css og legge den inn i prosjektet ditt. Deretter må du endre url-en i hver @font-face-definisjon manuelt så det stemmer med hvor fontfilene dine ligger:

@font-face {
    font-family: "Fremtind Grotesk";
    font-display: fallback;
    font-weight: normal;
    font-style: normal;
    src: local("Fremtind Grotesk"),
        /* Endre de to neste linjene så de stemmer */
            url("/relative/path/to/font/files/FremtindGrotesk-Regular-Web.woff2") format("woff2"), url("/relative/path/to/font/files/FremtindGrotesk-Regular-Web.woff")
            format("woff");
}

Konfigurasjon

Mange av utviklerverktøyene i frontendverden kan lese en browserslist og automatisk sørge for å optimalisere koden som bygges og til slutt ender opp hos brukerne våre. Jøkul bruker en egen browserslist-config internt, og det er anbefalt at du bruker den samme i prosjektet ditt.

Etter å ha lagt til pakken som en devDependency bruker du den ved å legge til browserslist-feltet i package.json:

{
    "browserslist": ["extends @fremtind/browserslist-config-jkl"]
}

Polyfills

All kode blir transpilert til å støtte nettleserne i browserslist, men det kan være brukt funksjonalitet som ikke støttes i alle nettlesere uten å laste inn en polyfill. Disse polyfillene vil i så fall dokumenteres her.

I skrivende stund er det ingen polyfills som trengs, etter at vi droppet støtte for Internet Explorer 11.

Testing library

Det er en feil i @testing-library sin håndtering av Web Components hvis en test feiler. Det vil kunne vi en evig løkke som skriver ut stack trace til terminalen helt til terminalen tryner. Legg til dette i setupTests.ts dersom du bruker Jest.

import { configure } from "@testing-library/react";
import prettier from "prettier";

configure({
    getElementError: (message, container) => {
        return new Error(
            [
                message,
                prettier.format(container.innerHTML, {
                    parser: "html",
                    htmlWhitespaceSensitivity: "ignore",
                }),
            ]
                .filter(Boolean)
                .join("\n\n"),
        );
    },
});

Skjemaverktøy

React Hook Form

Mange team i Fremtind velger å bruke React Hook Form for å gjøre skjemavalidering.

Skjemakomponentene i Jøkul skal kunne brukes med react-hook-form på en enkel måte. I praksis betyr det at de skal fungere som uncontrolled componentsregister kan brukes i stedet for Controller.

import React, { FC } from "react";
import { useForm } from "react-hook-form";
import { PrimaryButton } from "@fremtind/jkl-button-react";
import { RadioButtonGroup, RadioButton } from "@fremtind/jkl-radio-button-react";

type FormValues = {
    housetype: string;
};

type Props = {
    onSubmit: () => void;
};

const HouseDetailsForm: FC<Props> = ({ onSubmit }) => {
    const { register, handleSubmit } = useForm<FormValues>();

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <RadioButtonGroup legend="Hvilken boligtype?" variant="large">
                {["Enebolig", "Tomannsbolig", "Rekkehus", "Leilighet"].map((type) => (
                    <RadioButton {...register("housetype", { required: true })} key={type} label={type} value={type} />
                ))}
            </RadioButtonGroup>
            <PrimaryButton type="submit">Gå videre</PrimaryButton>
        </form>
    );
};

export default HouseDetailsForm;

Enkelte komponenter kan likevel trenge Controller i en overgangsperiode. Om du kommer over en komponent som fortsatt krever Controller må du gjerne lage et issue på GitHub, og veldig gjerne komme med en pull request!

import React, { FC } from "react";
import { useForm, Controller } from "react-hook-form";
import { PrimaryButton } from "@fremtind/jkl-button-react";
import { Select } from "@fremtind/jkl-select-react";

type FormValues = {
    housetype: string;
};

type Props = {
    onSubmit: () => void;
};

const AddressForm: FC<Props> = ({ onSubmit }) => {
    const { register, handleSubmit } = useForm<FormValues>();

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <Controller
                control={control}
                name="currency"
                defaultValue="USD"
                render={({ field: { onChange, value, ref } }) => (
                    <Select
                        ref={ref}
                        label="Valuta"
                        items={["USD", "EUR", "JPY"]}
                        onChange={onChange}
                        value={value}
                        width="6rem"
                    />
                )}
            />
            <PrimaryButton type="submit">Gå videre</PrimaryButton>
        </form>
    );
};

export default AddressForm;