Integrer @navikt/lumi-survey tilbakemeldingswidget i en Nav-frontendapplikasjon
Legg til en Lumi Survey-widget i applikasjonen din. Dekker installasjon, survey-konfigurasjon, backend-transportendepunkt og NAIS-oppsett.
Før du gjør endringer, kartlegg eksisterende oppsett:
package.json for eksisterende avhengigheter — er @navikt/ds-react og @navikt/ds-css installert?@navikt/ds-react ≥ 8.0.0). Lumi Survey krever v8. Hvis appen er på v7 eller lavere, må Aksel oppgraderes først — det er utenfor scope for denne skillen.tokenx, azure.application) og accessPolicy@navikt/lumi-survey-bruk (grep -r "lumi-survey" --include="*.ts" --include="*.tsx")_app.tsx, , , )layout.tsxmain.tsxroot.tsxEtter kartleggingen bør du ha svar på disse — de styrer resten av integrasjonen:
| Funn | Brukes i |
|---|---|
| Aksel-versjon (v8+ = ok, lavere = stopp) | Gate — ikke fortsett uten v8 |
| Rammeverk (Next.js / TanStack Start / Remix / etc.) | Fase 3c (stil-import), Fase 5 (backend-mønster) |
| Auth-type (TokenX / AzureAD / begge / ingen) | Fase 2 (auto-deteksjon), Fase 5 (endepunkt), Fase 6 (NAIS-konfig) |
| Stil-entry point (filnavn) | Fase 3c (der stiler importeres) |
| Backend-type (Node.js BFF / Kotlin BFF / annet) | Fase 5 (hvilken mal som brukes) |
| Eksisterende lumi-survey? | Hvis ja → tilpass eksisterende, ikke ny integrasjon |
Bruk disse funnene aktivt i de neste fasene — de bestemmer hvilke maler og konfigurasjoner som genereres.
Før du genererer kode, avklar det som trengs. Bruk kartleggingen fra Fase 1 til å besvare så mye som mulig automatisk — spør bare utvikleren om det du ikke kan utlede.
Les NAIS-manifestet fra Fase 1 og bestem auth-type:
| Funn i manifest | Auth-type | Audience-format |
|---|---|---|
tokenx.enabled: true | TokenX | <cluster>:team-esyfo:lumi-api |
azure.application.enabled: true | AzureAD | api://<cluster>.team-esyfo.lumi-api/.default |
| Begge er aktivert | Avhenger av brukergruppe — spør utvikleren | Se over |
| Ingen av delene | Auth mangler — spør utvikleren om oppsett | — |
Hvis auth-type er entydig, ikke spør — informer utvikleren om hva du fant: "Jeg ser at appen bruker TokenX. Bruker det for Lumi-integrasjonen."
Spør kun hvis det er tvetydig eller mangler.
Spør: "Hva slags tilbakemelding vil du samle inn?"
Presenter alternativer med anbefalinger:
| Type | Passer best for | Anbefaling |
|---|---|---|
| Rating (emoji 😡🙁😐😀😍) | Generell tilfredshet — "hvordan var opplevelsen?" | ✅ Anbefalt standard. Enkel, høy fullføringsrate. |
| Rating (tommel 👎👍) | Rask binær tilbakemelding på en spesifikk funksjon | Bra for målrettet, lavterskel feedback |
| Discovery | Forstå brukermål — "hva prøvde du å gjøre?" | Best for nye tjenester eller store redesign |
| Top Tasks | Måle oppgavesuksess/-feilrate (McGovern-metoden) | Best for etablerte tjenester med kjente brukeroppgaver |
| Task Priority | Rangere hva som betyr mest for brukerne (Long Neck-metoden) | Best for veikartprioritering |
| Tilpasset | Skreddersydde spørsmålsflyter med forgreningslogikk | Kun når forhåndsdefinerte typer ikke passer |
Hvis utvikleren er usikker, anbefal Rating med emoji-variant — det er det mest utprøvde mønsteret i Nav.
Hvis utvikleren valgte en rating-survey, spør: "Vil du ha et oppfølgingsspørsmål ved lav score? (anbefalt: ja — fritekstfelt ved score ≤ 2)"
Anbefaling: Ja — fritekst ved lav rating gir handlingsbar innsikt med minimal brukerbelastning. DEFAULT_SURVEY_RATING har dette innebygd.
For andre survey-typer (Discovery, Top Tasks, etc.) har de innebygde oppfølgingsspørsmål allerede — ikke spør.
Sjekk om .npmrc allerede har GitHub Packages-konfig. Hvis ikke:
@navikt:registry=https://npm.pkg.github.com
npm install @navikt/lumi-survey
Peer-avhengigheter (@navikt/ds-react, @navikt/ds-css) er vanligvis allerede installert i Nav-apper. Hvis ikke:
npm install @navikt/ds-react @navikt/ds-css
I appens globale stil-entry point (identifisert i Fase 1), sørg for denne importrekkefølgen:
import "@navikt/ds-css"; // Må komme først
import "@navikt/lumi-survey/styles.css"; // Deretter lumi-survey
Viktig: @navikt/ds-css MÅ importeres før @navikt/lumi-survey/styles.css.
Lag en egen fil for survey-konfigurasjonen (f.eks. survey.ts eller lumiSurvey.ts). Bruk satisfies LumiSurveyConfig for typesikkerhet.
Guiding: Basert på survey-typen valgt i Fase 2, hjelp utvikleren med å bygge opp konfigurasjonen steg for steg. Tilpass spørsmålstekster til tjenesten — bruk norsk og vær konkret.
import type { LumiSurveyConfig } from "@navikt/lumi-survey";
export const survey = {
type: "rating",
questions: [
{
id: "inntrykk",
type: "rating",
variant: "emoji", // "emoji" | "thumbs" | "stars" | "nps"
prompt: "Hva er ditt inntrykk av tjenesten?",
required: true,
},
{
id: "innspill",
type: "text",
prompt: "Har du noen kommentarer eller innspill?",
maxLength: 1000,
visibleIf: {
field: "ANSWER",
questionId: "inntrykk",
operator: "EXISTS",
},
},
],
} satisfies LumiSurveyConfig;
Tilpass til tjenesten:
prompt-tekster til noe som passer appen (f.eks. "Hvordan var det å søke om sykepenger?")visibleIf — vis oppfølging kun ved lav score (operator: "LT", value: 3) eller alltid (operator: "EXISTS")import type { LumiSurveyConfig } from "@navikt/lumi-survey";
export const survey = {
type: "discovery",
questions: [
{
id: "oppgave",
type: "text",
prompt: "Hva prøvde du å gjøre i dag?",
maxLength: 500,
required: true,
},
{
id: "fullfort",
type: "singleChoice",
prompt: "Fikk du gjort det du kom for?",
options: [
{ value: "ja", label: "Ja" },
{ value: "delvis", label: "Delvis" },
{ value: "nei", label: "Nei" },
],
required: true,
},
{
id: "blokkering",
type: "text",
prompt: "Hva hindret deg?",
maxLength: 1000,
visibleIf: {
field: "ANSWER",
questionId: "fullfort",
operator: "EQ",
value: "nei",
},
},
],
} satisfies LumiSurveyConfig;
import type { LumiSurveyConfig } from "@navikt/lumi-survey";
export const survey = {
type: "topTasks",
questions: [
{
id: "oppgave",
type: "singleChoice",
prompt: "Hva kom du hit for å gjøre?",
options: [
{ value: "soke", label: "Søke om ytelse" },
{ value: "status", label: "Sjekke status på søknad" },
{ value: "dokument", label: "Sende inn dokumentasjon" },
{ value: "annet", label: "Annet" },
],
randomize: true,
required: true,
},
{
id: "fullfort",
type: "singleChoice",
prompt: "Fikk du gjort det?",
options: [
{ value: "ja", label: "Ja" },
{ value: "delvis", label: "Delvis" },
{ value: "nei", label: "Nei" },
],
required: true,
},
{
id: "blokkering",
type: "text",
prompt: "Hva hindret deg?",
maxLength: 1000,
visibleIf: {
field: "ANSWER",
questionId: "fullfort",
operator: "NEQ",
value: "ja",
},
},
],
} satisfies LumiSurveyConfig;
import type { LumiSurveyConfig } from "@navikt/lumi-survey";
export const survey = {
type: "taskPriority",
questions: [
{
id: "viktigst",
type: "multiChoice",
variant: "checkbox",
prompt: "Hvilke oppgaver er viktigst for deg? (velg inntil 5)",
maxSelections: 5,
randomize: true,
options: [
{ value: "soke", label: "Søke om ytelse" },
{ value: "status", label: "Sjekke status" },
{ value: "dokument", label: "Sende dokumentasjon" },
{ value: "endring", label: "Melde endring" },
{ value: "utbetaling", label: "Se utbetalinger" },
// Legg til 15-40 oppgaver for robust Long Neck-analyse
],
required: true,
},
],
} satisfies LumiSurveyConfig;
Anbefaling: Inkluder 20-50 oppgaver for god Long Neck-analyse. Bruk randomize: true for å unngå rekkefølgebias.
| Type | Beskrivelse | Viktige props |
|---|---|---|
rating | Skala (emoji/tommel/stjerner/NPS) | variant, prompt, required |
text | Fritekst | prompt, maxLength, minRows, placeholder |
singleChoice | Velg én | prompt, options, randomize |
multiChoice | Velg flere | prompt, options, maxSelections, variant ("checkbox" | "combobox") |
visibleIfVis spørsmål basert på tidligere svar:
| Operator | Betydning | Eksempel |
|---|---|---|
EXISTS | Spørsmålet er besvart | Vis oppfølging uansett hva de svarte |
LT | Mindre enn (tall) | Vis ved lav rating (value: 3) |
GT | Større enn (tall) | Vis ved høy rating |
EQ | Lik (tekst/tall) | Vis ved spesifikt svar (value: "nei") |
NEQ | Ulik | Vis når svaret IKKE er en bestemt verdi |
CONTAINS | Inneholder (multi-choice) | Vis når et bestemt valg er valgt |
Transporten kobler widgeten til ditt backend-endepunkt:
import type { LumiSurveyTransport } from "@navikt/lumi-survey";
const transport: LumiSurveyTransport = {
async submit(submission) {
const response = await fetch("/api/lumi/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(submission.transportPayload),
});
if (!response.ok) {
throw new Error(`Lumi feedback-innsending feilet: ${response.status}`);
}
},
};
import { LumiSurveyDock } from "@navikt/lumi-survey";
import { survey } from "./survey";
<LumiSurveyDock
surveyId="<app-navn>-feedback"
survey={survey}
transport={transport}
/>
Velg en unik surveyId — bruk appnavnet som prefiks (f.eks. "sykepengesoknad-feedback", "modia-satisfaction"). Denne ID-en identifiserer surveyen i Lumi-dashboardet.
Send med metadata for å muliggjøre filtrering i Lumi-dashboardet:
<LumiSurveyDock
surveyId="<app-navn>-feedback"
survey={survey}
transport={transport}
context={{
tags: {
// Lav kardinalitet — disse blir filterdimensjoner i dashboardet
feature: "new-ui",
role: "employer",
},
}}
/>
Regler for kontekst:
tags: Kun verdier med lav kardinalitet (A/B-testgrupper, roller, features)debug: Høy kardinalitet tillatt (sesjons-ID-er, request-ID-er)For offentlige sider med Nav-dekoratøren (nav.no): standard consent-strategi fungerer automatisk.
For interne apper (Modia, admin-verktøy) uten dekoratøren:
<LumiSurveyDock
behavior={{ storageStrategy: "localStorage" }}
{...otherProps}
/>
Merk:
localStoragebrukes kun for ikke-sensitiv survey UX-state (f.eks. om brukeren har sett/lukket undersøkelsen). Aldri lagre tokens, PII eller sensitiv data i localStorage.
Se references/backend-transport.md for komplette backend-maler (Node.js og Kotlin).
Se references/nais-konfigurasjon.md for NAIS-manifest og accessPolicy-oppsett.
Etter implementasjon, verifiser hele flyten:
@navikt/lumi-survey installert og stiler importert (ds-css FØR lumi-survey)@navikt/ds-react ≥ 8.0.0)LumiSurveyDock rendres i appen med en unik surveyIdsatisfies LumiSurveyConfigsubmit-funksjon kaller ditt backend-endepunktLUMI_FEEDBACK_PATHLUMI_API_HOST, LUMI_AUDIENCE, LUMI_FEEDBACK_PATH og accessPolicy/api/tokenx/..., AzureAD → /api/azure/...)Hvis du bare vil komme raskt i gang uten skreddersøm, finnes det ferdige presets:
import { LumiSurveyDock, DEFAULT_SURVEY_RATING } from "@navikt/lumi-survey";
<LumiSurveyDock surveyId="<app-navn>-feedback" survey={DEFAULT_SURVEY_RATING} transport={transport} />
| Import | Type | Beskrivelse |
|---|---|---|
DEFAULT_SURVEY_RATING | Rating (emoji) | 5-punkts emoji + tekstoppfølging ved lav score |
DEFAULT_SURVEY_THUMBS | Rating (tommel) | Binær tommel opp/ned + tekstoppfølging |
DEFAULT_SURVEY_STARS | Rating (stjerner) | 5-stjerners rating + tekstoppfølging |
DEFAULT_SURVEY_NPS | Rating (NPS) | 0-10 Net Promoter Score |
DEFAULT_SURVEY_DISCOVERY | Discovery | Mål + fullføring + blokkeringsanalyse |
DEFAULT_SURVEY_SERVICE_FEEDBACK | Service feedback | Rating + forhåndsdefinerte årsaker + tekst |
Anbefaling: Bruk presets kun for rask prototyping. For produksjon, lag en skreddersydd konfigurasjon (Fase 4a) med spørsmålstekster tilpasset tjenesten.
Se references/avansert-konfigurasjon.md for forgreningslogikk, events og øvrige avanserte tilpasninger.