From 3d7692398ba7946a7c8033d4171b96c319871913 Mon Sep 17 00:00:00 2001 From: Oliver Viljamaa Date: Wed, 13 May 2020 22:07:35 +0300 Subject: [PATCH 1/2] Validate forms on submit, restyle validation messages --- src/admin/BulkUserCreationPage.tsx | 4 +++- src/i18n/en.json | 3 ++- src/i18n/et.json | 3 ++- src/identity/AddressForm.tsx | 4 +++- src/identity/ProfileForm.tsx | 4 +++- src/testing/AddTestToIdentifierPage.test.tsx | 22 +++++--------------- src/testing/AddTestToIdentifierPage.tsx | 6 ++++-- src/testing/TestFields.tsx | 6 +++++- src/theme.ts | 12 +++++++++-- 9 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/admin/BulkUserCreationPage.tsx b/src/admin/BulkUserCreationPage.tsx index bc1691e..62d08be 100644 --- a/src/admin/BulkUserCreationPage.tsx +++ b/src/admin/BulkUserCreationPage.tsx @@ -78,7 +78,9 @@ const BulkUserCreationPage: FC = () => { }); const fieldError = (key: keyof FormFields) => - form.submitCount > 0 && form.errors[key] ? {form.errors[key]} : null; + form.submitCount > 0 && form.errors[key] ? ( + {form.errors[key]} + ) : null; return ( <> diff --git a/src/i18n/en.json b/src/i18n/en.json index 1960b7f..524af4c 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -99,5 +99,6 @@ "addTestToIdentifierPage.form.identifier.invalid.magicLink": "Please check the email address", "addTestToIdentifierPage.form.identifier.invalid.estonianId": "Please check the identification code", "addTestToIdentifierPage.button": "Add test result", - "addTestToIdentifierPage.success": "Test result for {{ identifier }} added." + "addTestToIdentifierPage.success": "Test result for {{ identifier }} added.", + "testFields.error.generic": "Please check the input" } diff --git a/src/i18n/et.json b/src/i18n/et.json index 4fcef32..1af2f9e 100644 --- a/src/i18n/et.json +++ b/src/i18n/et.json @@ -99,5 +99,6 @@ "addTestToIdentifierPage.form.identifier.invalid.magicLink": "Palun kontrolli meiliaadressi", "addTestToIdentifierPage.form.identifier.invalid.estonianId": "Palun kontrolli isikukoodi", "addTestToIdentifierPage.button": "Lisa testitulemus", - "addTestToIdentifierPage.success": "Testitulemus isikule {{ identifier }} lisatud." + "addTestToIdentifierPage.success": "Testitulemus isikule {{ identifier }} lisatud.", + "testFields.error.generic": "Palun kontrolli sisestatud väärtust" } diff --git a/src/identity/AddressForm.tsx b/src/identity/AddressForm.tsx index cf91db4..f1a4e37 100644 --- a/src/identity/AddressForm.tsx +++ b/src/identity/AddressForm.tsx @@ -50,7 +50,9 @@ export const AddressForm = ({ onComplete }: { onComplete: (address: Address) => }); const fieldError = (key: keyof AddressFormFields) => - form.touched[key] && form.errors[key] ? {form.errors[key]} : null; + form.submitCount > 0 && form.errors[key] ? ( + {form.errors[key]} + ) : null; const field = (name: keyof AddressFormFields, label: string) => { const id = `identity-${name}`; diff --git a/src/identity/ProfileForm.tsx b/src/identity/ProfileForm.tsx index 889b010..6a16fc5 100644 --- a/src/identity/ProfileForm.tsx +++ b/src/identity/ProfileForm.tsx @@ -46,7 +46,9 @@ export const ProfileForm = ({ onComplete }: { onComplete: (profile: Profile) => }); const fieldError = (key: keyof ProfileFormFields) => - form.touched[key] && form.errors[key] ? {form.errors[key]} : null; + form.submitCount > 0 && form.errors[key] ? ( + {form.errors[key]} + ) : null; return ( diff --git a/src/testing/AddTestToIdentifierPage.test.tsx b/src/testing/AddTestToIdentifierPage.test.tsx index ea657d4..3867bf7 100644 --- a/src/testing/AddTestToIdentifierPage.test.tsx +++ b/src/testing/AddTestToIdentifierPage.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; +import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import nock, { Scope } from 'nock'; @@ -25,35 +25,23 @@ describe(AddTestToIdentifierPage, () => { expect(identifierInput()).toHaveFocus(); }); - it('only allows submitting the form once the identifier is valid and required test fields are filled, showing validation errors', async () => { + it('creates a user and adds a PCR test for that user only when identifier is valid and required test fields are filled', async () => { mockHttp().get('/api/v1/test-types').reply(200, [aTestType()]); renderWrapped(); - const positiveOption = await screen.findByRole('radio', { name: 'Positive' }); + const negativeOption = await screen.findByRole('radio', { name: 'Negative' }); + const positiveOption = screen.getByRole('radio', { name: 'Positive' }); userEvent.type(identifierInput(), '79210030814'); // invalid id code submit(); await screen.findByText(/check the identification code/i); // validation error userEvent.type(identifierInput(), '39210030814'); // valid id code + submit(); await waitForElementToBeRemoved(screen.queryByText(/check the identification code/i)); // no validation error await userEvent.type(notesInput(), 'Some notes'); - expect(submitButton()).toBeDisabled(); // as a radio option has not been selected - userEvent.click(positiveOption); - await waitFor(() => expect(submitButton()).not.toBeDisabled()); - }); - - it('creates a user and and adds a PCR test for that user', async () => { - mockHttp().get('/api/v1/test-types').reply(200, [aTestType()]); - renderWrapped(); - - const negativeOption = await screen.findByRole('radio', { name: 'Negative' }); - const positiveOption = screen.getByRole('radio', { name: 'Positive' }); - - userEvent.type(identifierInput(), '39210030814'); - await userEvent.type(notesInput(), 'Some notes'); userEvent.click(negativeOption); userEvent.click(positiveOption); // to confirm the latest value is sent diff --git a/src/testing/AddTestToIdentifierPage.tsx b/src/testing/AddTestToIdentifierPage.tsx index 1b9f33b..473022e 100644 --- a/src/testing/AddTestToIdentifierPage.tsx +++ b/src/testing/AddTestToIdentifierPage.tsx @@ -85,7 +85,9 @@ export const AddTestToIdentifierPage: FC = () => { }); const fieldError = (key: keyof FormFields) => - form.touched[key] && form.errors[key] ? {form.errors[key]} : null; + form.submitCount > 0 && form.errors[key] ? ( + {form.errors[key]} + ) : null; return ( <> @@ -128,7 +130,7 @@ export const AddTestToIdentifierPage: FC = () => { {selectedTestType && } - diff --git a/src/testing/TestFields.tsx b/src/testing/TestFields.tsx index 8883217..ee2cb2d 100644 --- a/src/testing/TestFields.tsx +++ b/src/testing/TestFields.tsx @@ -13,7 +13,11 @@ interface TestFieldsProps { export const TestFields: FC = ({ form, testType }) => { const fieldError = (key: keyof TestType['resultsSchema']['properties']) => - form.touched[key] && form.errors[key] ? {form.errors[key]} : null; + form.submitCount > 0 && form.errors[key] ? ( + + testFields.error.generic + + ) : null; // TODO: Extract generic JSON schema generator return ( diff --git a/src/theme.ts b/src/theme.ts index 417ff5d..5255583 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -18,6 +18,8 @@ const theme = { colors: { ...baseTheme.colors, primary: '#096DD9', + success: '#52c41a', // Primary green from https://ant.design/docs/spec/colors + error: '#f5222d', // Primary red from https://ant.design/docs/spec/colors }, sizes: { pageWidth: '600px', @@ -102,10 +104,10 @@ const theme = { paddingRight: 4, }, success: { - background: '#52c41a', // Primary green from https://ant.design/docs/spec/colors + backgroundColor: 'success', }, error: { - background: '#f5222d', // Primary red from https://ant.design/docs/spec/colors + backgroundColor: 'error', }, }, badges: { @@ -148,6 +150,12 @@ const theme = { borderBottom: '1px solid #DEDEDE', }, }, + text: { + validation: { + color: 'error', + fontSize: 1, + }, + }, }; export default theme; From ae1f0a590ebed9902a5b98a9e92367b8504e225d Mon Sep 17 00:00:00 2001 From: Oliver Viljamaa Date: Wed, 13 May 2020 23:52:55 +0300 Subject: [PATCH 2/2] Show validation error after blur as well --- src/admin/BulkUserCreationPage.tsx | 2 +- src/identity/AddressForm.tsx | 2 +- src/identity/ProfileForm.tsx | 2 +- src/testing/AddTestToIdentifierPage.tsx | 2 +- src/testing/TestFields.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/admin/BulkUserCreationPage.tsx b/src/admin/BulkUserCreationPage.tsx index 62d08be..8e47762 100644 --- a/src/admin/BulkUserCreationPage.tsx +++ b/src/admin/BulkUserCreationPage.tsx @@ -78,7 +78,7 @@ const BulkUserCreationPage: FC = () => { }); const fieldError = (key: keyof FormFields) => - form.submitCount > 0 && form.errors[key] ? ( + (form.submitCount > 0 || form.touched[key]) && form.errors[key] ? ( {form.errors[key]} ) : null; diff --git a/src/identity/AddressForm.tsx b/src/identity/AddressForm.tsx index f1a4e37..32f8cfd 100644 --- a/src/identity/AddressForm.tsx +++ b/src/identity/AddressForm.tsx @@ -50,7 +50,7 @@ export const AddressForm = ({ onComplete }: { onComplete: (address: Address) => }); const fieldError = (key: keyof AddressFormFields) => - form.submitCount > 0 && form.errors[key] ? ( + (form.submitCount > 0 || form.touched[key]) && form.errors[key] ? ( {form.errors[key]} ) : null; diff --git a/src/identity/ProfileForm.tsx b/src/identity/ProfileForm.tsx index 6a16fc5..7acbde8 100644 --- a/src/identity/ProfileForm.tsx +++ b/src/identity/ProfileForm.tsx @@ -46,7 +46,7 @@ export const ProfileForm = ({ onComplete }: { onComplete: (profile: Profile) => }); const fieldError = (key: keyof ProfileFormFields) => - form.submitCount > 0 && form.errors[key] ? ( + (form.submitCount > 0 || form.touched[key]) && form.errors[key] ? ( {form.errors[key]} ) : null; diff --git a/src/testing/AddTestToIdentifierPage.tsx b/src/testing/AddTestToIdentifierPage.tsx index 473022e..7b786de 100644 --- a/src/testing/AddTestToIdentifierPage.tsx +++ b/src/testing/AddTestToIdentifierPage.tsx @@ -85,7 +85,7 @@ export const AddTestToIdentifierPage: FC = () => { }); const fieldError = (key: keyof FormFields) => - form.submitCount > 0 && form.errors[key] ? ( + (form.submitCount > 0 || form.touched[key]) && form.errors[key] ? ( {form.errors[key]} ) : null; diff --git a/src/testing/TestFields.tsx b/src/testing/TestFields.tsx index ee2cb2d..2fcf474 100644 --- a/src/testing/TestFields.tsx +++ b/src/testing/TestFields.tsx @@ -13,7 +13,7 @@ interface TestFieldsProps { export const TestFields: FC = ({ form, testType }) => { const fieldError = (key: keyof TestType['resultsSchema']['properties']) => - form.submitCount > 0 && form.errors[key] ? ( + (form.submitCount > 0 || form.touched[key]) && form.errors[key] ? ( testFields.error.generic