Skip to content

Commit

Permalink
Merge pull request #180 from open-formulieren/issue/4637-optional-tra…
Browse files Browse the repository at this point in the history
…nslations-act-as-required

Issue/4637 optional translations act as required
  • Loading branch information
robinmolen authored Oct 2, 2024
2 parents 42f1e1f + 3a98100 commit c768a2d
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 9 deletions.
63 changes: 58 additions & 5 deletions src/components/ComponentConfiguration.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -976,8 +976,10 @@ export const FileUpload: Story = {
await step('File tab', async () => {
await userEvent.click(canvas.getByRole('link', {name: 'File'}));

await expect(canvas.getByLabelText('Maximum file size')).toHaveDisplayValue('10MB');
await expect(canvas.getByText('Note that the server upload limit is 50MB.')).toBeVisible();
await waitFor(async () => {
await expect(canvas.getByLabelText('Maximum file size')).toHaveDisplayValue('10MB');
await expect(canvas.getByText('Note that the server upload limit is 50MB.')).toBeVisible();
});

// check that the file types are visible
canvas.getByLabelText('File types').focus();
Expand Down Expand Up @@ -1206,6 +1208,21 @@ export const SelectBoxes: Story = {
// check that the option labels are in the translations table
expect(await editForm.findByText('Option label 1')).toBeVisible();
expect(await editForm.findByText('Second option')).toBeVisible();

// Check that all translations can be filled
const inputs = editForm.getAllByRole('textbox');
for (let input of inputs) {
await userEvent.type(input, 'manualTranslation');
expect(input).toHaveValue('manualTranslation');
await userEvent.clear(input);
expect(input).toHaveValue('');
}

// Removing focus from the last input
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));

// Check that none of the inputs have a Required error message
expect(await editForm.queryByText('Required')).toBeNull();
});

await step('Set up itemsExpression for options', async () => {
Expand Down Expand Up @@ -1241,7 +1258,9 @@ export const SelectBoxes: Story = {
openForms: {
dataSrc: 'variable',
itemsExpression: {var: 'someVar'},
translations: {},
translations: {
nl: {description: '', label: '', tooltip: ''},
},
},
defaultValue: {},
// Advanced tab
Expand Down Expand Up @@ -1412,6 +1431,21 @@ export const Radio: Story = {
// check that the option labels are in the translations table
expect(await editForm.findByText('Option label 1')).toBeVisible();
expect(await editForm.findByText('Second option')).toBeVisible();

// Check that all translations can be filled
const inputs = editForm.getAllByRole('textbox');
for (let input of inputs) {
await userEvent.type(input, 'manualTranslation');
await expect(input).toHaveValue('manualTranslation');
await userEvent.clear(input);
await expect(input).toHaveValue('');
}

// Removing focus from the last input
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));

// Check that none of the inputs have a Required error message
await expect(await editForm.queryByText('Required')).toBeNull();
});

await step('Set up itemsExpression for options', async () => {
Expand Down Expand Up @@ -1447,7 +1481,9 @@ export const Radio: Story = {
openForms: {
dataSrc: 'variable',
itemsExpression: {var: 'someVar'},
translations: {},
translations: {
nl: {description: '', label: '', tooltip: ''},
},
},
defaultValue: '',
// Advanced tab
Expand Down Expand Up @@ -1625,6 +1661,21 @@ export const Select: Story = {
// check that the option labels are in the translations table
expect(await editForm.findByText('Option label 1')).toBeVisible();
expect(await editForm.findByText('Second option')).toBeVisible();

// Check that all translations can be filled
const inputs = editForm.getAllByRole('textbox');
for (let input of inputs) {
await userEvent.type(input, 'manualTranslation');
await expect(input).toHaveValue('manualTranslation');
await userEvent.clear(input);
await expect(input).toHaveValue('');
}

// Removing focus from the last input
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));

// Check that none of the inputs have a Required error message
await expect(await editForm.queryByText('Required')).toBeNull();
});

await step('Set up itemsExpression for options', async () => {
Expand Down Expand Up @@ -1667,7 +1718,9 @@ export const Select: Story = {
openForms: {
dataSrc: 'variable',
itemsExpression: {var: 'someVar'},
translations: {},
translations: {
nl: {description: '', label: '', tooltip: ''},
},
},
defaultValue: '',
// Advanced tab
Expand Down
6 changes: 6 additions & 0 deletions src/registry/addressNL/edit.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ export const PostcodeValidationTabWithoutConfiguration: Story = {
await step('Navigate to validation tab and open Postcode validation', async () => {
await userEvent.click(canvas.getByRole('link', {name: 'Validation'}));
await userEvent.click(canvas.getAllByText('Postcode')[0]);

expect(await canvas.findByLabelText('Regular expression for postcode')).toBeVisible();
expect(await canvas.findByText('NL')).toBeVisible();
expect(await canvas.findByText('EN')).toBeVisible();
expect(await canvas.findByText('Error code')).toBeVisible();

const errorMessageInput = await canvas.findByLabelText('Error message for "pattern"');
expect(errorMessageInput).toHaveDisplayValue('');
});
Expand All @@ -53,10 +55,12 @@ export const CityValidationTabWithoutConfiguration: Story = {
await step('Navigate to validation tab and open City validation', async () => {
await userEvent.click(canvas.getByRole('link', {name: 'Validation'}));
await userEvent.click(canvas.getAllByText('City')[0]);

expect(await canvas.findByLabelText('Regular expression for city')).toBeVisible();
expect(await canvas.findByText('NL')).toBeVisible();
expect(await canvas.findByText('EN')).toBeVisible();
expect(await canvas.findByText('Error code')).toBeVisible();

const errorMessageInput = await canvas.findByLabelText('Error message for "pattern"');
expect(errorMessageInput).toHaveDisplayValue('');
});
Expand Down Expand Up @@ -93,6 +97,7 @@ export const PostcodeValidationTabWithConfiguration: Story = {
await userEvent.click(canvas.getByRole('link', {name: 'Validation'}));
await userEvent.click(canvas.getAllByText('Postcode')[0]);
const patternInput = await canvas.findByLabelText('Regular expression for postcode');

expect(patternInput).toBeVisible();
expect(patternInput).toHaveValue('1017 [A-Za-z]{2}');

Expand Down Expand Up @@ -136,6 +141,7 @@ export const CityValidationTabWithConfiguration: Story = {
await userEvent.click(canvas.getByRole('link', {name: 'Validation'}));
await userEvent.click(canvas.getAllByText('City')[0]);
const patternInput = await canvas.findByLabelText('Regular expression for city');

expect(patternInput).toBeVisible();
expect(patternInput).toHaveValue('Amsterdam');

Expand Down
32 changes: 30 additions & 2 deletions src/registry/radio/radio-validation.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,36 @@ export const ManualMinimumOneValue: Story = {
await userEvent.type(labelInput, 'Foo');
await userEvent.clear(labelInput);
await userEvent.keyboard('[Tab]');
await expect(await canvas.findByText('The option label is a required field.')).toBeVisible();
await expect(await canvas.findByText('The option value is a required field.')).toBeVisible();

expect(await canvas.findByText('The option label is a required field.')).toBeVisible();
expect(await canvas.findByText('The option value is a required field.')).toBeVisible();
});
},
};

export const TranslationsArentRequired: Story = {
name: "Translations: translations aren't required fields",
play: async ({canvasElement, step}) => {
const canvas = within(canvasElement);

await step("Translations aren't required fields", async () => {
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));
const editForm = within(canvas.getByTestId('componentEditForm'));

// Check that all translations can be filled
const inputs = editForm.getAllByRole('textbox');
for (let input of inputs) {
await userEvent.type(input, 'manualTranslation');
expect(input).toHaveValue('manualTranslation');
await userEvent.clear(input);
expect(input).toHaveValue('');
}

// Removing focus from the last input
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));

// Check that none of the inputs have a Required error message
expect(await editForm.queryByText('Required')).toBeNull();
});
},
};
57 changes: 57 additions & 0 deletions src/registry/select/select-validation.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {SelectComponentSchema} from '@open-formulieren/types';
import {expect, within} from '@storybook/test';
import userEvent from '@testing-library/user-event';

import ComponentEditForm from '@/components/ComponentEditForm';
Expand Down Expand Up @@ -96,3 +97,59 @@ test('There is always at least one option', async () => {
const firstValueLabel = await screen.findByTestId('input-data.values[0].label');
expect(firstValueLabel).toBeVisible();
});

test('All translations are optional', async () => {
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
const onSubmit = jest.fn();
const component = {
id: 'wqimsadk',
type: 'select',
key: 'select',
label: 'A select field',
dataSrc: 'values',
openForms: {
dataSrc: 'manual',
translations: {},
},
data: {
values: [],
},
defaultValue: '',
} satisfies SelectComponentSchema;

const builderInfo = {
title: 'Select',
icon: 'th-list',
group: 'basic',
weight: 70,
schema: {},
};
contextRender(
<ComponentEditForm
isNew
component={component}
builderInfo={builderInfo}
onCancel={jest.fn()}
onRemove={jest.fn()}
onSubmit={onSubmit}
/>
);

await user.click(screen.getByRole('tab', {name: 'Translations'}));
const editForm = within(screen.getByTestId('componentEditForm'));

// Check that all translations can be filled
const inputs = editForm.getAllByRole('textbox');
for (let input of inputs) {
await user.type(input, 'manualTranslation');
await expect(input).toHaveValue('manualTranslation');
await user.clear(input);
await expect(input).toHaveValue('');
}

// Removing focus from the last input
await user.click(screen.getByRole('tab', {name: 'Translations'}));

// Check that none of the inputs have a Required error message
await expect(await editForm.queryByText('Required')).toBeNull();
});
27 changes: 27 additions & 0 deletions src/registry/selectboxes/selectboxes-validation.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,33 @@ export const ManualMinimumOneValue: Story = {
},
};

export const TranslationsArentRequired: Story = {
name: "Translations: translations aren't required fields",
play: async ({canvasElement, step}) => {
const canvas = within(canvasElement);

await step("Translations aren't required fields", async () => {
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));
const editForm = within(canvas.getByTestId('componentEditForm'));

// Check that all translations can be filled
const inputs = editForm.getAllByRole('textbox');
for (let input of inputs) {
await userEvent.type(input, 'manualTranslation');
await expect(input).toHaveValue('manualTranslation');
await userEvent.clear(input);
await expect(input).toHaveValue('');
}

// Removing focus from the last input
await userEvent.click(canvas.getByRole('tab', {name: 'Translations'}));

// Check that none of the inputs have a Required error message
await expect(await editForm.queryByText('Required')).toBeNull();
});
},
};

export const MinAndMaxSelectedInitialValues: Story = {
name: 'Initial min and max count',
args: {
Expand Down
4 changes: 2 additions & 2 deletions src/registry/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const jsonSchema: z.ZodType<Json> = z.lazy(() =>

// Maps to @open-formulieren/types common.ts Option type.
const optionTranslationSchema = z.object({
label: z.string(),
label: z.string().optional(),
});

export const optionSchema = (intl: IntlShape) =>
Expand All @@ -109,7 +109,7 @@ export const optionSchema = (intl: IntlShape) =>
.object({
// zod doesn't seem to be able to use our supportedLanguageCodes for z.object keys,
// they need to be defined statically. So, 'record' it is.
translations: z.record(optionTranslationSchema.optional()),
translations: z.record(z.string(), optionTranslationSchema.optional()),
})
.optional(),
});
Expand Down

0 comments on commit c768a2d

Please sign in to comment.