Skip to content

Commit

Permalink
✨ [open-formulieren/open-forms#4546] Implement conditional error display
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sergei-maertens committed Oct 28, 2024
1 parent 85c5fd6 commit 9fee7bd
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 6 deletions.
16 changes: 15 additions & 1 deletion src/formio/components/SoftRequiredErrors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import FormioUtils from 'formiojs/utils';
import {Formio} from 'react-formio';

const FormioContentField = Formio.Components.components.content;
Expand All @@ -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 '';

Expand Down
76 changes: 72 additions & 4 deletions src/formio/components/SoftRequiredErrors.stories.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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: `
Expand All @@ -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: `
<p>Not all required fields are filled out. That can get expensive!</p>
{{ missingFields }}
<p>Are you sure you want to continue?</p>
`,
},
],
},
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();
});
});
},
};
5 changes: 4 additions & 1 deletion src/formio/templates/missingFields.ejs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<ul class="utrecht-unordered-list">
<span class="sr-only" id="{{ctx.id}}-missing-fields-header">
{{ ctx.t('Empty fields') }}
</span>
<ul class="utrecht-unordered-list" aria-labelledby="{{ctx.id}}-missing-fields-header">
{% for (const label of ctx.labels) { %}
<li class="utrecht-unordered-list__item">{{ label }}</li>
{% } %}
Expand Down

0 comments on commit 9fee7bd

Please sign in to comment.