@lumianalytics/survey
v0.5.0
Published
Self-contained React widget for configurable Lumi surveys.
Readme
Lumi Survey
📖 Ny bruker? Se Kom i gang-guiden for å komme raskt i gang.
Selvstendig React-widget for å samle inn brukertilbakemeldinger via Lumi.
Ny her? Følg Kom i gang i rot-README for installasjon, backend-oppsett og NAIS-tilgang. Denne guiden dekker widget-konfigurasjon og avanserte features.
Sett opp en survey
Her er et komplett eksempel — en emoji-rating med valgfri oppfølgingstekst:
import "@lumianalytics/survey/styles.css";
import { LumiSurveyDock } from "@lumianalytics/survey";
import type { LumiSurveyTransport } from "@lumianalytics/survey";
const transport: LumiSurveyTransport = {
submit: async (submission) => {
await fetch("/api/survey", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(submission.transportPayload),
});
},
};
const survey = {
type: "rating",
questions: [
{
id: "rating",
type: "rating",
variant: "emoji",
prompt: "Hvordan var opplevelsen din?",
required: true,
},
{
id: "feedback",
type: "text",
prompt: "Har du andre tilbakemeldinger?",
maxLength: 1000,
visibleIf: { field: "ANSWER", questionId: "rating", operator: "EXISTS" },
},
],
};
<LumiSurveyDock surveyId="min-flate" survey={survey} transport={transport} />;visibleIf gjør at tekstfeltet først vises etter at brukeren har valgt en emoji. Se Progresjon for flere eksempler.
Spørsmålstyper
En survey er et LumiSurveyConfig-objekt med spørsmål i rekkefølge.
Rating
Fire varianter med ulik skala:
| Variant | Skala | Beskrivelse |
| :--- | :--- | :--- |
| emoji | 1–5 | 😡🙁😐😀😍 |
| thumbs | 1–2 | 👎👍 |
| stars | 1–5 | ⭐⭐⭐⭐⭐ |
| nps | 0–10 | Nummerte knapper med lav/høy-label |
{
id: "nps",
type: "rating",
variant: "nps",
prompt: "Hvor sannsynlig er det at du vil anbefale oss?",
lowLabel: "Lite sannsynlig",
highLabel: "Svært sannsynlig",
required: true,
}Text
Fritekstfelt med valgfri maks-lengde.
{
id: "comment",
type: "text",
prompt: "Hva kan vi forbedre?",
maxLength: 1000,
}Single choice / Multi choice
{
id: "reason",
type: "singleChoice",
prompt: "Hva var du her for å gjøre?",
options: [
{ value: "apply", label: "Søke om noe" },
{ value: "status", label: "Sjekke status" },
{ value: "other", label: "Annet" },
],
}multiChoice støtter variant: "checkbox" (default) eller variant: "combobox" for mange valg.
Progresjon: visibleIf
For de fleste surveyer er det nok å vise oppfølgingsspørsmål kun når det er relevant (progressive disclosure).
{
id: "comment",
type: "text",
prompt: "Hva gikk galt?",
visibleIf: {
field: "ANSWER",
questionId: "rating",
operator: "LT",
value: 3,
},
}Operatorer: EXISTS, EQ, NEQ, GT, LT, CONTAINS.
Storage-strategi
Widgeten husker at brukeren har lukket (dismissed) surveyen, og respekterer en valgfri cooldown-periode før den vises igjen. Du velger hvordan den husker dette:
| Strategi | Merknad |
| :--- | :--- |
| localStorage (default) | Bruker localStorage direkte |
| none | Ingen persistering, surveyen vises hver gang |
Pakken eksporterer ferdige surveyer og builder-funksjoner for de vanligste brukscasene:
import {
DEFAULT_SURVEY_RATING,
DEFAULT_SURVEY_DISCOVERY,
DEFAULT_SURVEY_SERVICE_FEEDBACK,
createTopTasksSurvey,
createTaskPrioritySurvey,
createRatingSurvey,
createDiscoverySurvey,
} from "@lumianalytics/survey";| Preset / Builder | Beskrivelse |
| :--- | :--- |
| DEFAULT_SURVEY_RATING | Emoji-rating + valgfri fritekst |
| DEFAULT_SURVEY_DISCOVERY | "Hva kom du hit for å gjøre?" + oppfølging |
| DEFAULT_SURVEY_SERVICE_FEEDBACK | Tjenestevurdering med detaljer |
| createRatingSurvey({...}) | Tilpasset rating med egne spørsmål og oppfølging |
| createDiscoverySurvey({...}) | Tilpasset discovery-survey |
| createTopTasksSurvey({tasks}) | Top Tasks (McGovern-metoden) |
| createTaskPrioritySurvey({tasks}) | Task Priority / Long Neck-rangering |
Eksempel med builder:
const topTasks = createTopTasksSurvey({
tasks: [
{ value: "apply", label: "Søke om sykepenger" },
{ value: "status", label: "Sjekke status" },
],
});
<LumiSurveyDock surveyId="top-tasks" survey={topTasks} transport={transport} />;Rask tommelfingerregel for å velge riktig surveytype. Poenget er å få handlingsbare data med minst mulig friksjon for brukeren.
| Surveytype | Når bruke | Hva du får ut | Typiske fallgruver |
| --- | --- | --- | --- |
| rating | "Pulse" etter en konkret oppgave eller flyt | Trend over tid + (valgfri) årsak i fritekst | For generelt spørsmål, for mange spørsmål, for hyppig visning |
| discovery | Utforskning: hva kom brukeren hit for å gjøre? | Frie tekstsvar + "fikk du gjort det?" + ev. blocker | For mye tekst, dårlig segmentering, samler identifikatorer i context |
| topTasks | Måle suksess for kjerneoppgaver (McGovern) | Suksess/feil per oppgave + blocker-innsikt | For mange/få oppgaver, ikke randomisert rekkefølge, uklare oppgavenavn |
| taskPriority | Strategisk: hva er viktigst å prioritere? (Long Neck) | Rangering av viktigste oppgaver (top N) | For få tasks, ikke randomisert, feil UI-variant for mange tasks |
| custom | Når du må kombinere eller branch'e | Skreddersydd spørreflyt | Blir fort "for mye", vanskelig å sammenligne over tid |
Anbefaling: Start med rating eller discovery, og gå videre til topTasks/taskPriority når dere har en tydelig hypoteseliste.
Best practices
- Hold det kort: 1–2 spørsmål er ofte nok (rating + valgfri tekst).
- Still spørsmål om en konkret opplevelse ("etter du gjorde X"), ikke hele produktet.
- Bruk progresjon: vis fritekst først etter at rating er valgt (
visibleIf). - Bruk
context.tagsfor segmentering (lav kardinalitet), ogcontext.debugkun for feilsøking (høy kardinalitet). - Unngå identifikatorer i
context(og ikke auto-collectpathnamepå dynamiske ruter). - Velg en stabil
surveyIdper flate/bruksmønster (ikke per deploy).
Go-live sjekkliste
- Importer styling:
@lumianalytics/survey/styles.css. - Implementer
transport.submitsom sendersubmission.transportPayloadtil din backend. - Sett riktig
storageStrategy(localStorage/none). - Test ende-til-ende (innsending → dashboard).
LumiSurveyDock props
| Prop | Type | Påkrevd | Beskrivelse |
| :--- | :--- | :---: | :--- |
| surveyId | string | ✅ | Unik identifikator for surveyen (f.eks. soknad-kvittering) |
| survey | LumiSurveyConfig | ✅ | Konfigurasjonsobjektet for spørsmålene |
| transport | LumiSurveyTransport | ✅ | Objekt med submit-funksjon for innsending |
| context | object | ❌ | Metadata/tags/debug for segmentering |
| behavior | object | ❌ | Styrer åpning, lukking, cooldown og storage-strategi |
| events | object | ❌ | Event-callbacks for sporing og livssyklus |
| labels | object | ❌ | Tekster for UI-elementer (send-knapp, feilmeldinger) |
| success | object | ❌ | Konfigurer suksess-visning (tittel, tekst, auto-lukk) |
| style | object | ❌ | Visuell styling (posisjon, farger, classNames) |
Transport og payload
Widgeten sender submission.transportPayload til din backend. Payloaden er stabil og
versjonert (schemaVersion: 1). Den inkluderer:
surveyId,surveyType,submittedAt,startedAtanswers: Normalisert struktur per spørsmålcontext: tags/debug/auto-collectet miljøinfo
Backend-oppsett (token exchange, NAIS-tilgang) er beskrevet i Koble til backend.
Kontekst og personvern
Auto-collectes i browser:
viewport(bredde/høyde)deviceType(mobile/tablet/desktop)userAgent
url og pathname auto-collectes ikke som default. Hvis dere har statiske ruter uten
identifikatorer kan dere opt-in:
<LumiSurveyDock behavior={{ collectLocation: true }} />Hvis rutene kan inneholde ID-er, send heller en sanitert verdi:
<LumiSurveyDock context={{ pathname: "/sak/:id" }} />Tags vs debug
context.tags: Lav kardinalitet, brukes til segmentering og grafer i dashboard.context.debug: Høy kardinalitet, kun for detaljvisning av enkeltinnsendinger.
Events (hooks)
Registrer event-callbacks ved å sende et events-objekt til LumiSurveyDock.
const events = {
onViewDock: (surveyId) => {},
onAnswer: (questionId, value) => {},
onSubmitStart: (submission) => {},
onSubmitSuccess: (submission) => {},
onSubmitError: (cause) => {},
onValidationFailed: (missingQuestionIds) => {},
onReset: () => {},
onDismissalPersistFailed: (cause) => {},
};Bruk logic når du faktisk må endre flyten (hoppe, skippe, eller avslutte tidlig). Hvis du bare vil vise/skjule oppfølgingsspørsmål, bruk heller visibleIf.
const surveyWithLogic = {
type: "custom",
questions: [
{
id: "rating",
type: "rating",
prompt: "Hvor fornøyd er du?",
logic: [
{
condition: { field: "ANSWER", operator: "LT", value: 3 },
action: { type: "JUMP_TO", targetId: "comment" },
},
{
condition: { field: "ANSWER", operator: "GT", value: 2 },
action: { type: "SUBMIT" },
},
],
},
{
id: "comment",
type: "text",
prompt: "Hva kan vi forbedre?",
},
],
};Feilsøking (vanlige problemer)
- Survey dukker ikke opp: Sjekk at
behavior.initialOpenikke er satt tilfalse, og atstorageStrategyikke skjuler den pga. cooldown. - Ingen data i dashboard: Verifiser at backend sender
submission.transportPayloadtil riktig endepunkt. - Layout virker "tom": Sørg for at
@lumianalytics/survey/styles.csser importert. - Dismissed-tilstand persisteres ikke: Sjekk at
storageStrategyikke er satt til"none".
