一键导入
sif-soknad-modify-step
Legg til nye spørsmål/felter i et eksisterende steg i en søknadsapp som bruker @sif/soknad og @sif/rhf.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Legg til nye spørsmål/felter i et eksisterende steg i en søknadsapp som bruker @sif/soknad og @sif/rhf.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Mønster for å kombinere flere API-hooks til én asynkron laster (useInitialData + InitialDataLoader) i sif-soknad-apper.
Legg til et nytt steg i en søknadsapp som bruker @sif/soknad og @sif/rhf — oppretter alle filer og oppdaterer config/routing/i18n.
Arkitekturreferanse og beslutningslogg for @sif/soknad-app — rammeverket for søknadsapper i sif-brukerdialog.
Sett opp OppsummeringSteg i en v2-app — DTO-basert oppsummering med FormSummary, bekreftelsescheckbox og i18n-nøkler.
Veiledning for oppsett av en ny søknadsapp med @sif/soknad-app — SøknadRouter, SøknadAppProvider, AppContext og routing shell.
Implementer et vedleggssteg i en v2-app — VedleggPanel, PersistedVedlegg, hydration og MSW-handlere.
| name | sif-soknad-modify-step |
| type | action |
| description | Legg til nye spørsmål/felter i et eksisterende steg i en søknadsapp som bruker @sif/soknad og @sif/rhf. |
types.ts — nye felter i FormFields og FormValues<Prefix>Form.tsx — nye skjemakomponenter med validering<prefix>StegUtils.ts — mapping for nye felteri18n/nb.ts og nn.ts — labels og valideringsmeldingerSoknadsdata og formValuesToSoknadsdata ved behovsif-soknad-add-step.sif-intl.sif-api.Agenten trenger KUN å lese steget som skal endres. Les disse filene:
src/app/steps/<steg>/types.ts — eksisterende felter og typersrc/app/steps/<steg>/<Prefix>Form.tsx — eksisterende skjema-komponentsrc/app/steps/<steg>/<prefix>StegUtils.ts — eksisterende mappingsrc/app/steps/<steg>/i18n/nb.ts — eksisterende tekstersrc/app/types/Soknadsdata.ts — eksisterende søknadsdatatypesrc/app/steps/<steg>/i18n/nn.ts — hvis steget allerede har separat nynorsk-filIkke les andre steg eller utforsk mappestruktur utover dette.
Når en søknad porteres til nytt format, bruk ferdige paneler fra @sif/soknad-forms fremfor å implementere tilsvarende logikk manuelt. Panelene er typesikre, RHF-baserte og inneholder korrekt visning og i18n.
| Panel | Beskrivelse | Viktige props |
|---|---|---|
VelgRegistrertBarnPanel<T> | Radiogruppe for valg av registrert barn med fødselsdato og kildeinfo | name, registrerteBarn, inkluderAnnetBarn?, annetBarnLabel?, validate? |
Eksporterte konstanter:
ANNET_BARN = 'annetBarn' — verdien som settes i skjemaet når bruker velger "annet barn"Bruksmønster:
import { ANNET_BARN, VelgRegistrertBarnPanel } from '@sif/soknad-forms';
<VelgRegistrertBarnPanel<MyFormValues>
name={MyFormFields.barnetSøknadenGjelder}
registrerteBarn={registrerteBarn}
inkluderAnnetBarn={true}
validate={validateField(MyFormFields.barnetSøknadenGjelder, getRequiredFieldValidator())}
/>;
Ved migrering fra gammel VelgBarnFormPart (fra @navikt/sif-common-forms-ds): erstatt den alltid med VelgRegistrertBarnPanel. Legg til @sif/soknad-forms i dependencies hvis det mangler i app-ens package.json.
createSifFormComponents<T>() fra @sif/rhf gir disse komponentene:
| Komponent | Bruksområde | Verditype i FormValues | Validator |
|---|---|---|---|
YesOrNoQuestion | Ja/nei-spørsmål | YesOrNo | getYesOrNoValidator() |
CheckboxGroup | Flervalg med checkboxer | string[] / enum[] | getListValidator({ required: true }) |
RadioGroup | Enkeltvalg med radioknapper | string / enum | getRequiredFieldValidator() |
TextField | Fritekstfelt | string | getStringValidator({ required: true }) |
Textarea | Lengre fritekst | string | getStringValidator({ required: true, maxLength }) |
NumberInput | Tallfelt | string | getNumberValidator({ required: true }) |
Datepicker | Datovelger | string | getDateValidator() |
DateRangePicker | Fra/til-datoperiode | string (per felt) | getDateValidator() per felt + validate-prop på gruppen |
Select | Nedtrekksliste | string | getRequiredFieldValidator() |
Checkbox | Enkelt avkrysningsboks | boolean | getCheckedValidator() |
Alle validatorer importeres fra @navikt/sif-validation.
useSifValidate(scope) tar inn et scope (f.eks. 'barnForm') og returnerer validateField(fieldName, validator). Denne genererer i18n-nøkkel for feilmelding:
<scope>.validation.<fieldName>.<errorCode>
Scope-konvensjon: Bruk <prefix>Form der <prefix> er lowerCamelCase av stegets domenenavn (f.eks. barnForm, bostedForm, andreYtelserForm).
Eksempler på error-koder fra @navikt/sif-validation:
| Validator | Feilkode(r) |
|---|---|
getYesOrNoValidator | yesOrNoIsUnanswered |
getListValidator | listIsEmpty, listHasTooFewItems, listHasTooManyItems |
getRequiredFieldValidator | noValue |
getStringValidator | stringHasNoValue, stringIsTooShort, stringIsTooLong |
getCheckedValidator | notChecked |
getDateValidator | dateHasNoValue, dateHasInvalidFormat, dateIsBeforeMin, dateIsAfterMax |
getFødselsnummerValidator | fødselsnummerHasNoValue, fødselsnummerIsNot11Chars, fødselsnummerIsInvalid, fødselsnummerAsHnrIsNotAllowed |
Viktig: Bruk alltid de eksakte feilkodene fra validatorens enum — ikke dikk opp egne. Feil kode → valideringsmelding vises aldri. Du finner alle koder i packages/sif-validation/src/get*Validator.ts via enum Validate*Error.
For DateRangePicker brukes getDateValidator per felt — ikke getDateRangeValidator. SifDateRangePicker håndterer cross-field min/max automatisk. Cross-field validering (fom > tom) legges i validate-prop på gruppen:
<DateRangePicker
name="periode"
legend={text('...')}
validate={validateField('periode', ({ fromDate, toDate }) => {
if (fromDate && toDate && fromDate > toDate) return 'fromDateIsAfterToDate';
})}
fromInputProps={{
name: FormFields.fom,
validate: validateField(
FormFields.fom,
getDateValidator({ required: true, min: minDate, max: maxDate }),
(errorCode) => {
if (errorCode === 'dateIsBeforeMin' && minDate) return { dato: sifIntl.date(minDate, 'compact') };
if (errorCode === 'dateIsAfterMax' && maxDate) return { dato: sifIntl.date(maxDate, 'compact') };
},
),
}}
toInputProps={{
name: FormFields.tom,
validate: validateField(
FormFields.tom,
getDateValidator({ required: true, min: minDate, max: maxDate }),
(errorCode) => {
if (errorCode === 'dateIsBeforeMin' && minDate) return { dato: sifIntl.date(minDate, 'compact') };
if (errorCode === 'dateIsAfterMax' && maxDate) return { dato: sifIntl.date(maxDate, 'compact') };
},
),
}}
/>
Valideringsnøkler i i18n/nb.ts må matche dette mønsteret:
// scope = 'andreYtelserForm', felt = 'andreYtelser', feilkode = 'listIsEmpty':
'andreYtelserForm.validation.andreYtelser.listIsEmpty': 'Du må velge minst én ytelse',
Fra brukerens beskrivelse, bestem:
lowerCamelCase) — f.eks. andreYtelsertypes.tsexport enum <EnumNavn> {
VERDI_1 = 'VERDI_1',
VERDI_2 = 'VERDI_2',
// ...
}
export enum <Prefix>FormFields {
eksisterendeFelt = 'eksisterendeFelt',
nyttFelt = 'nyttFelt', // ← legg til
}
export interface <Prefix>FormValues extends StepFormValues {
[<Prefix>FormFields.eksisterendeFelt]?: YesOrNo;
[<Prefix>FormFields.nyttFelt]?: <EnumNavn>[]; // ← legg til
}
i18n/nb.tsLegg til tekster for spørsmålet, hvert alternativ og valideringsfeil:
export const <prefix>StegMessages_nb = {
// eksisterende nøkler...
'<prefix>Steg.spørsmål.<felt>': '<spørsmålstekst>',
'<prefix>Steg.<felt>.<ENUM_VERDI_1>': '<visningstekst>',
'<prefix>Steg.<felt>.<ENUM_VERDI_2>': '<visningstekst>',
'<prefix>Form.validation.<felt>.<feilkode>': '<valideringstekst>',
};
For nøkkelstruktur-konvensjoner, ICU-parametre (
{param},plural) og bruk av<AppText>vs.text()-hook → sesif-intl.
i18n/nn.tsStart med å spre nb og la nynorsk-oversettelse gjøres manuelt av utvikler i etterkant:
import { <prefix>StegMessages_nb } from './nb';
export const <prefix>StegMessages_nn: Record<keyof typeof <prefix>StegMessages_nb, string> = {
...<prefix>StegMessages_nb,
};
Ikke oversett tekstene automatisk. TypeScript sikrer full nøkkeldekning via
Record<keyof typeof ..._nb, string>.
Soknadsdata.tsUtvid søknadsdatatypen med det nye feltet:
export type <Prefix>Søknadsdata = {
eksisterendeFelt: boolean;
nyttFelt?: <EnumNavn>[]; // ← legg til (optional hvis betinget)
};
<prefix>StegUtils.tstoFormValues — map søknadsdata → skjemaverdierLegg til mapping av det nye feltet. For array-felt:
return {
eksisterendeFelt: søknadsdata.eksisterendeFelt ? YesOrNo.YES : YesOrNo.NO,
nyttFelt: søknadsdata.nyttFelt, // ← legg til
};
toSøknadsdata — map skjemaverdier → søknadsdataHåndter betinget logikk (nullstill felt hvis forutsetning ikke er oppfylt):
return {
eksisterendeFelt: data.eksisterendeFelt === YesOrNo.YES,
nyttFelt: data.eksisterendeFelt === YesOrNo.YES
? data.nyttFelt as <EnumNavn>[] | undefined
: undefined, // ← rydd opp når betingelse ikke er oppfylt
};
<Prefix>Form.tsxcreateSifFormComponentsconst { YesOrNoQuestion, CheckboxGroup } = createSifFormComponents<<Prefix>FormValues>();
import { getListValidator, getYesOrNoValidator } from '@navikt/sif-validation';
useSifValidateconst { validateField } = useSifValidate('<prefix>Form');
Scope-en bestemmer prefixen i valideringsnøkkelen: <prefix>Form.validation.<felt>.<feilkode>.
const methods = useSøknadRhfForm(stepId, defaultValues);
const triggerFelt = methods.watch(<Prefix>FormFields.eksisterendeFelt);
For betinget visning, wrap i en betingelse:
<FormLayout.Questions>
<YesOrNoQuestion ... />
{triggerFelt === YesOrNo.YES && (
<CheckboxGroup
name={<Prefix>FormFields.nyttFelt}
legend={text('<prefix>Steg.spørsmål.<felt>')}
validate={validateField(
<Prefix>FormFields.nyttFelt,
getListValidator({ required: true }),
)}
checkboxes={[
{ value: <EnumNavn>.VERDI_1, label: text('<prefix>Steg.<felt>.VERDI_1') },
{ value: <EnumNavn>.VERDI_2, label: text('<prefix>Steg.<felt>.VERDI_2') },
]}
/>
)}
</FormLayout.Questions>
Kjør npx tsc --noEmit i app-mappen.
Bruk methods.watch() for å observere et annet felt, og vis det nye feltet betinget med vanlig &&-rendering:
const methods = useSøknadRhfForm(stepId, defaultValues);
const feltVerdi = methods.watch(<Prefix>FormFields.triggerFelt);
// I JSX — ett felt:
{feltVerdi === YesOrNo.YES && (
<NyKomponent ... />
)}
// Flere felt som gruppe — bruk fragment, ikke FormLayout.Questions:
{feltVerdi === YesOrNo.YES && (
<>
<FeltA ... />
<FeltB ... />
</>
)}
FormLayout.Questions er ikke nødvendig rundt betinget innhold — gapet styres av den ytre FormLayout.Questions.
I toSøknadsdata, sett avhengige felter til undefined når betingelsen ikke er oppfylt:
nyttFelt: data.triggerFelt === YesOrNo.YES ? data.nyttFelt : undefined,
Når felter skjules (unmountes) beholdes feilmeldinger i RHF sin form state. Rydd opp med clearErrors i en useEffect:
useEffect(() => {
if (feltVerdi !== YesOrNo.YES) {
methods.clearErrors([<Prefix>FormFields.nyttFelt]);
}
}, [feltVerdi]);
AriaLiveRegion skal kun brukes rundt dynamiske meldinger som skjermlesere skal annonsere. Bruk ikke AriaLiveRegion rundt spørsmål/skjemafelter — bruk && i stedet.
Kombinert med QuestionRelatedMessage (fra @navikt/sif-common-ui) trekkes meldingen visuelt nærmere spørsmålet den tilhører via Bleed med negativ top-margin:
import { AriaLiveRegion } from '@sif/soknad-ui/components';
import { QuestionRelatedMessage } from '@navikt/sif-common-ui';
<YesOrNoQuestion name={...} ... />
<AriaLiveRegion visible={feltVerdi === YesOrNo.NO}>
<QuestionRelatedMessage>
<SifInfoCard>...</SifInfoCard>
</QuestionRelatedMessage>
</AriaLiveRegion>
QuestionRelatedMessage inne i AriaLiveRegion fungerer korrekt — Bleed's negative top-margin trekker meldingen opp mot spørsmålet over.
Komponentvalg ved migrering: Sjekk alltid hvilken status og presentasjon v1 bruker på tilsvarende melding. Bruk SifInfoCard for info/warning, InlineMessage når den gamle løsningen var inline, og LocalAlert status="error" for feil. Ikke gjett. Finn meldingskomponenten i v1 (gjerne under alert/-mappe i steget) og bevar betydningen.
# Eksempel fra v1-mappestruktur:
steps/om-barnet/alert/IkkeKroniskEllerFuksjonshemningAlert.tsx → SifInfoCard
steps/om-barnet/alert/TrengerIkkeSøkeForBarnAlert.tsx → SifInfoCard
steps/oppsummering/alert/UgyldigSøknadAlert.tsx → LocalAlert status="error"
types.ts — evt. ny enum, nytt felt i FormFields og FormValuesi18n/nb.ts — spørsmålstekst, alternativ-labels, valideringsmeldingi18n/nn.ts — opprettet eller oppdatert med nynorsk (typed mot nb)Soknadsdata.ts — nytt felt i søknadsdatatype*StegUtils.ts — mapping begge veier, med opprydding av betingede felter*Form.tsx — ny komponent, evt. betinget visning, riktig validatortsc --noEmit passerer