From 9fee7bddf3b5dda261cfe900cc6bf2b733799d36 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Mon, 28 Oct 2024 15:51:30 +0100 Subject: [PATCH] :sparkles: [open-formulieren/open-forms#4546] Implement conditional error display On each render, the softRequiredErrors component checks which components have 'empty' values and extracts their labels. Only if there are empty components is the error message displayed. --- src/formio/components/SoftRequiredErrors.js | 16 +++- .../components/SoftRequiredErrors.stories.js | 76 ++++++++++++++++++- src/formio/templates/missingFields.ejs | 5 +- 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/formio/components/SoftRequiredErrors.js b/src/formio/components/SoftRequiredErrors.js index 9c39a99b3..808eb4e2e 100644 --- a/src/formio/components/SoftRequiredErrors.js +++ b/src/formio/components/SoftRequiredErrors.js @@ -1,3 +1,4 @@ +import FormioUtils from 'formiojs/utils'; import {Formio} from 'react-formio'; const FormioContentField = Formio.Components.components.content; @@ -20,7 +21,20 @@ class SoftRequiredErrors extends FormioContentField { } get content() { - const missingFieldLabels = ['Foo', 'Bar']; + // figure out which components use soft required validation + const softRequiredComponents = []; + FormioUtils.eachComponent(this.root.components, component => { + if (component.component.openForms?.softRequired) { + softRequiredComponents.push(component); + } + }); + + const missingFieldLabels = []; + // check which components have an empty value + for (const component of softRequiredComponents) { + const isEmpty = component.isEmpty(); + if (isEmpty) missingFieldLabels.push(component.label); + } if (!missingFieldLabels.length) return ''; diff --git a/src/formio/components/SoftRequiredErrors.stories.js b/src/formio/components/SoftRequiredErrors.stories.js index c6990f27a..d5b3a8479 100644 --- a/src/formio/components/SoftRequiredErrors.stories.js +++ b/src/formio/components/SoftRequiredErrors.stories.js @@ -1,5 +1,9 @@ +import {expect, userEvent, waitFor, within} from '@storybook/test'; + import {withUtrechtDocument} from 'story-utils/decorators'; +import {sleep} from 'utils'; +import {UPLOAD_URL, mockFileUploadDelete, mockFileUploadPost} from './FileField.mocks'; import {MultipleFormioComponents} from './story-util'; export default { @@ -12,12 +16,17 @@ export default { type: 'file', key: 'file', storage: 'url', - url: '/dummy', - label: 'File', + url: UPLOAD_URL, + label: 'Soft required file', multiple: false, openForms: {softRequired: true}, }, - {type: 'textfield', key: 'textfield', label: 'Text', openForms: {softRequired: true}}, + { + type: 'textfield', + key: 'textfield', + label: 'Soft required text', + openForms: {softRequired: true}, + }, { type: 'softRequiredErrors', html: ` @@ -37,7 +46,66 @@ export default { }, parameters: { controls: {sort: 'requiredFirst'}, + msw: { + handlers: [mockFileUploadPost, mockFileUploadDelete], + }, + }, +}; + +export const EmptyFields = { + play: async ({canvasElement}) => { + const canvas = within(canvasElement); + + await canvas.findByText('Not all required fields are filled out. That can get expensive!'); + const list = await canvas.findByRole('list', {name: 'Empty fields'}); + const listItems = within(list).getAllByRole('listitem'); + expect(listItems).toHaveLength(2); + const content = listItems.map(item => item.textContent); + expect(content).toEqual(['Soft required file', 'Soft required text']); }, }; -export const Default = {}; +export const FillField = { + args: { + components: [ + { + type: 'textfield', + key: 'textfield', + label: 'Soft required text', + openForms: {softRequired: true}, + }, + { + type: 'softRequiredErrors', + html: ` +

Not all required fields are filled out. That can get expensive!

+ + {{ missingFields }} + +

Are you sure you want to continue?

+ `, + }, + ], + }, + play: async ({canvasElement, step}) => { + const canvas = within(canvasElement); + // formio... :thisisfine: + await sleep(100); + + const ERROR_TEXT = 'Not all required fields are filled out. That can get expensive!'; + + await step('Initial state', async () => { + expect(await canvas.findByText(ERROR_TEXT)).toBeVisible(); + const list = await canvas.findByRole('list', {name: 'Empty fields'}); + const listItems = within(list).getAllByRole('listitem'); + expect(listItems).toHaveLength(1); + }); + + await step('Fill out field and remove error', async () => { + const input = canvas.getByLabelText('Soft required text'); + await userEvent.type(input, 'Not empty'); + await waitFor(() => { + expect(canvas.queryByText(ERROR_TEXT)).toBeNull(); + }); + }); + }, +}; diff --git a/src/formio/templates/missingFields.ejs b/src/formio/templates/missingFields.ejs index 392cb7b3d..2f85f9dc0 100644 --- a/src/formio/templates/missingFields.ejs +++ b/src/formio/templates/missingFields.ejs @@ -1,4 +1,7 @@ -