diff --git a/schemas/json/layout/layout.schema.v1.json b/schemas/json/layout/layout.schema.v1.json index c2c3d80717..347623427b 100644 --- a/schemas/json/layout/layout.schema.v1.json +++ b/schemas/json/layout/layout.schema.v1.json @@ -78,7 +78,8 @@ "examples": [ { "title": "some.text.binding", - "help": "some.other.text.binding" + "help": "some.other.text.binding", + "requiredValidation": "some.custom.required.validation.message" } ] }, diff --git a/src/features/expressions/errors.ts b/src/features/expressions/errors.ts index 66b94aa4ce..c5176ce969 100644 --- a/src/features/expressions/errors.ts +++ b/src/features/expressions/errors.ts @@ -1,10 +1,7 @@ import type { ExprContext } from 'src/features/expressions/ExprContext'; export class ExprRuntimeError extends Error { - public constructor( - public context: ExprContext, - message: string, - ) { + public constructor(public context: ExprContext, message: string) { super(message); } } diff --git a/src/features/layout/update/updateFormLayoutSagas.ts b/src/features/layout/update/updateFormLayoutSagas.ts index 827163f750..9eaa8a1155 100644 --- a/src/features/layout/update/updateFormLayoutSagas.ts +++ b/src/features/layout/update/updateFormLayoutSagas.ts @@ -316,12 +316,10 @@ export function* watchInitialCalculatePageOrderAndMoveToNextPageSaga(): SagaIter const pageTriggers = state.formLayout.uiConfig.pageTriggers; const appHasCalculateTrigger = pageTriggers?.includes(Triggers.CalculatePageOrder) || - Object.keys(layouts).some( - (layout) => - layouts[layout]?.some( - (element) => - element.type === 'NavigationButtons' && element.triggers?.includes(Triggers.CalculatePageOrder), - ), + Object.keys(layouts).some((layout) => + layouts[layout]?.some( + (element) => element.type === 'NavigationButtons' && element.triggers?.includes(Triggers.CalculatePageOrder), + ), ); if (appHasCalculateTrigger) { yield put( diff --git a/src/features/logging.ts b/src/features/logging.ts index fa9a63cfd6..3abbe6118c 100644 --- a/src/features/logging.ts +++ b/src/features/logging.ts @@ -13,8 +13,9 @@ export function parseErrorArgs(args: any[]): string { return args .map((arg) => { if (arg instanceof AxiosError) { - return `Request failed, check the server logs for more details. ${arg.config?.method?.toUpperCase()} '${arg - .config?.url}': ${arg.message}`; + return `Request failed, check the server logs for more details. ${arg.config?.method?.toUpperCase()} '${ + arg.config?.url + }': ${arg.message}`; } if (arg instanceof Error) { return `${arg.name}: ${arg.message}`; diff --git a/src/layout/LayoutComponent.tsx b/src/layout/LayoutComponent.tsx index fef76fa28b..c1a2de0c14 100644 --- a/src/layout/LayoutComponent.tsx +++ b/src/layout/LayoutComponent.tsx @@ -163,6 +163,7 @@ export abstract class FormComponent if (!node.item.required) { return []; } + const { langAsString } = langTools; const formDataToValidate = { ...formData, ...overrideFormData }; const validationObjects: IValidationObject[] = []; @@ -170,18 +171,15 @@ export abstract class FormComponent const bindings = Object.entries(node.item.dataModelBindings ?? {}); for (const [bindingKey, field] of bindings) { const data = formDataToValidate[field]; + const textResourceBindings = node.item.textResourceBindings as ITextResourceBindings; if (!data?.length) { - const fieldName = getFieldName(node.item.textResourceBindings as ITextResourceBindings, langTools, bindingKey); - - validationObjects.push( - buildValidationObject( - node, - 'errors', - langTools.langAsString('form_filler.error_required', [fieldName]), - bindingKey, - ), - ); + const fieldName = getFieldName(textResourceBindings, langTools, bindingKey); + const errorMessage = textResourceBindings?.requiredValidation + ? langAsString(textResourceBindings?.requiredValidation) + : langAsString('form_filler.error_required', [fieldName]); + + validationObjects.push(buildValidationObject(node, 'errors', errorMessage, bindingKey)); } } return validationObjects; diff --git a/src/layout/Summary/SummaryComponent.test.tsx b/src/layout/Summary/SummaryComponent.test.tsx index 33b750a067..259408429a 100644 --- a/src/layout/Summary/SummaryComponent.test.tsx +++ b/src/layout/Summary/SummaryComponent.test.tsx @@ -29,7 +29,7 @@ describe('SummaryComponent', () => { textResourceBindings: {}, children: [], maxCount: 10, - }) as ExprUnresolved, + } as ExprUnresolved), ), ], }, diff --git a/src/layout/layout.d.ts b/src/layout/layout.d.ts index a3acec1360..e9b8fe4499 100644 --- a/src/layout/layout.d.ts +++ b/src/layout/layout.d.ts @@ -158,7 +158,11 @@ export type ITextResourceBindings = | undefined; export type TextBindingsForSummarizableComponents = 'summaryTitle' | 'summaryDescription' | 'summaryAccessibleTitle'; -export type TextBindingsForFormComponents = TextBindingsForSummarizableComponents | 'tableTitle' | 'shortName'; +export type TextBindingsForFormComponents = + | TextBindingsForSummarizableComponents + | 'tableTitle' + | 'shortName' + | 'requiredValidation'; export type TextBindingsForLabel = 'title' | 'description' | 'help'; export type ILayout = ExprUnresolved[]; diff --git a/src/utils/databindings.ts b/src/utils/databindings.ts index d8188974ad..55684d68e1 100644 --- a/src/utils/databindings.ts +++ b/src/utils/databindings.ts @@ -105,8 +105,9 @@ export function getIndexCombinations( } const repeatingGroupValues = Object.values(repeatingGroups); - const mainGroupMaxIndex = repeatingGroupValues.find((group) => group.dataModelBinding === baseGroupBindings[0]) - ?.index; + const mainGroupMaxIndex = repeatingGroupValues.find( + (group) => group.dataModelBinding === baseGroupBindings[0], + )?.index; if (mainGroupMaxIndex === undefined) { return combinations; diff --git a/src/utils/databindings/DataBinding.ts b/src/utils/databindings/DataBinding.ts index 4cbd06ef51..a6a8d9cedb 100644 --- a/src/utils/databindings/DataBinding.ts +++ b/src/utils/databindings/DataBinding.ts @@ -22,11 +22,7 @@ export class DataBindingPart { public readonly base: string; public arrayIndex: number | undefined = undefined; - public constructor( - public readonly parent: DataBinding, - public readonly parentIndex: number, - raw: string, - ) { + public constructor(public readonly parent: DataBinding, public readonly parentIndex: number, raw: string) { const arrayIndex = raw.match(/(\[\d+])?$/); if (arrayIndex && arrayIndex[1]) { this.arrayIndex = parseInt(arrayIndex[1].substring(1, arrayIndex[1].length - 1)); diff --git a/src/utils/layout/LayoutPages.ts b/src/utils/layout/LayoutPages.ts index 392e336e33..00d454200f 100644 --- a/src/utils/layout/LayoutPages.ts +++ b/src/utils/layout/LayoutPages.ts @@ -16,10 +16,7 @@ export class LayoutPages< > { private readonly objects: Collection; - public constructor( - private currentView?: keyof Collection, - objects?: Collection, - ) { + public constructor(private currentView?: keyof Collection, objects?: Collection) { this.objects = objects || ({} as any); for (const layoutKey of Object.keys(this.objects)) { const layout = this.objects[layoutKey]; diff --git a/src/utils/validation/validation.test.ts b/src/utils/validation/validation.test.ts index 7cd993c99e..a1f5a7a958 100644 --- a/src/utils/validation/validation.test.ts +++ b/src/utils/validation/validation.test.ts @@ -71,6 +71,10 @@ describe('utils > validation', () => { id: 'c4Title', value: 'component_4', }, + { + id: 'c4RequiredValidation', + value: 'Component_4 feltet er påkrevd og må besvares', + }, { id: 'c5Title', value: 'component_5', @@ -105,6 +109,7 @@ describe('utils > validation', () => { readOnly: false, textResourceBindings: { title: 'c4Title', + requiredValidation: 'c4RequiredValidation', }, }; diff --git a/src/utils/validation/validation.ts b/src/utils/validation/validation.ts index 6444e34902..7f96c72292 100644 --- a/src/utils/validation/validation.ts +++ b/src/utils/validation/validation.ts @@ -108,9 +108,8 @@ export function getParentGroup(groupId: string, layout: ILayout): ILayoutGroup | */ export function getGroupChildren(groupId: string, layout: ILayout): ExprUnresolved[] { const layoutGroup = layout.find((element) => element.id === groupId) as ILayoutGroup; - return layout.filter( - (element) => - layoutGroup?.children?.map((id) => (layoutGroup.edit?.multiPage ? id.split(':')[1] : id)).includes(element.id), + return layout.filter((element) => + layoutGroup?.children?.map((id) => (layoutGroup.edit?.multiPage ? id.split(':')[1] : id)).includes(element.id), ); } @@ -137,18 +136,17 @@ export function canFormBeSaved(validationResult: IValidationResult | null): bool if (!validations) { return true; } - return Object.keys(validations).every( - (layoutId: string) => - Object.keys(validations[layoutId])?.every((componentId: string) => { - const componentValidations: IComponentValidations = validations[layoutId][componentId]; - if (componentValidations === null) { - return true; - } - return Object.keys(componentValidations).every((bindingKey: string) => { - const componentErrors = componentValidations[bindingKey]?.errors; - return !componentErrors || componentErrors.length === 0; - }); - }), + return Object.keys(validations).every((layoutId: string) => + Object.keys(validations[layoutId])?.every((componentId: string) => { + const componentValidations: IComponentValidations = validations[layoutId][componentId]; + if (componentValidations === null) { + return true; + } + return Object.keys(componentValidations).every((bindingKey: string) => { + const componentErrors = componentValidations[bindingKey]?.errors; + return !componentErrors || componentErrors.length === 0; + }); + }), ); }