Skip to content

Commit

Permalink
Add length validation to name fields and refactor error state logic
Browse files Browse the repository at this point in the history
  • Loading branch information
jameswainwright-mt committed Feb 3, 2025
1 parent c18b487 commit 6f95c86
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 48 deletions.
11 changes: 2 additions & 9 deletions components/GovCheckboxes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,7 @@ const handleChange = (value: string) => {
</script>

<template>
<div
:class="`govuk-form-group ${props.context.state.invalid &&
props.context.messages.rule_required &&
props.context.messages.rule_required.visible
? 'govuk-form-group--error'
: ''
}`"
>
<div :class="`govuk-form-group ${showErrorState(props.context) ? 'govuk-form-group--error' : ''}`">
<div class="govuk-form-group">
<fieldset class="govuk-fieldset">
<legend class="govuk-fieldset__legend govuk-fieldset__legend--m">
Expand All @@ -55,7 +48,7 @@ const handleChange = (value: string) => {
</div>
<p v-if="props.context.state.invalid" class="govuk-error-message" :data-testid="`${id}_error`">
<span class="govuk-visually-hidden">Error:</span>
{{ props.context.messages.rule_required.value }}
{{ getErrorMessage(props.context) }}
</p>
<div class="govuk-checkboxes" data-module="govuk-checkboxes">
<div v-for="(option, index) in options" :key="option" class="govuk-checkboxes__item">
Expand Down
10 changes: 2 additions & 8 deletions components/GovDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,15 @@ function handleInput(e) {
</script>

<template>
<div
:class="`govuk-form-group ${props.context.state.invalid &&
props.context.messages.rule_required &&
props.context.messages.rule_required.visible
? 'govuk-form-group--error' : ''
}`"
>
<div :class="`govuk-form-group ${showErrorState(props.context) ? 'govuk-form-group--error' : ''}`">
<label class="govuk-label govuk-label--m" :for="id">
{{ label }}
</label>
<div v-if="help" :id="`${id}_hint`" class="govuk-hint">
{{ help }}
</div>
<p v-if="props.context.state.invalid" class="govuk-error-message" :data-testid="`${id}_error`">
<span class="govuk-visually-hidden">Error:</span> {{ props.context.messages.rule_required.value }}
<span class="govuk-visually-hidden">Error:</span> {{ getErrorMessage(props.context) }}
</p>
<select
:id="id"
Expand Down
11 changes: 2 additions & 9 deletions components/GovInputInt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,15 @@ function handleInput(e) {
</script>

<template>
<div
:class="`govuk-form-group ${props.context.state.invalid &&
props.context.messages.rule_required &&
props.context.messages.rule_required.visible
? 'govuk-form-group--error'
: ''
}`"
>
<div :class="`govuk-form-group ${showErrorState(props.context) ? 'govuk-form-group--error' : ''}`">
<label class="govuk-label govuk-label--m" :for="id">
{{ label }}
</label>
<div v-if="help" :id="`${id}_hint`" class="govuk-hint">
{{ help }}
</div>
<p v-if="props.context.state.invalid" class="govuk-error-message" :data-testid="`${id}_error`">
<span class="govuk-visually-hidden">Error:</span> {{ props.context.messages.rule_required?.value }}
<span class="govuk-visually-hidden">Error:</span> {{ getErrorMessage(props.context) }}
</p>
<div class="govuk-input__wrapper">
<input
Expand Down
11 changes: 2 additions & 9 deletions components/GovInputText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@ function handleInput(e) {
</script>

<template>
<div
:class="`govuk-form-group ${props.context.state.invalid &&
props.context.messages.rule_required &&
props.context.messages.rule_required.visible
? 'govuk-form-group--error'
: ''
}`"
>
<div :class="`govuk-form-group ${showErrorState(props.context) ? 'govuk-form-group--error' : ''}`">
<h1 class="govuk-label-wrapper">
<label class="govuk-label govuk-label--m" :for="id">
{{ label }}
Expand All @@ -40,7 +33,7 @@ function handleInput(e) {
{{ help }}
</div>
<p v-if="props.context.state.invalid" class="govuk-error-message" :data-testid="`${id}_error`">
<span class="govuk-visually-hidden">Error:</span> {{ props.context.messages.rule_required?.value }}
<span class="govuk-visually-hidden">Error:</span> {{ getErrorMessage(props.context) }}
</p>
<input
:id="id"
Expand Down
11 changes: 2 additions & 9 deletions components/GovInputWithSuffix.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,15 @@ function handleInput(e) {
</script>

<template>
<div
:class="`govuk-form-group ${props.context.state.invalid &&
props.context.messages.rule_required &&
props.context.messages.rule_required.visible
? 'govuk-form-group--error'
: ''
}`"
>
<div :class="`govuk-form-group ${showErrorState(props.context) ? 'govuk-form-group--error' : ''}`">
<label class="govuk-label govuk-label--m" :for="id">
{{ label }}
</label>
<div v-if="help" :id="`${id}_hint`" class="govuk-hint">
{{ help }}
</div>
<p v-if="props.context.state.invalid" class="govuk-error-message" :data-testid="`${id}_error`">
<span class="govuk-visually-hidden">Error:</span> {{ props.context.messages.rule_required?.value }}
<span class="govuk-visually-hidden">Error:</span> {{ getErrorMessage(props.context) }}
</p>
<div class="govuk-input__wrapper">
<input
Expand Down
4 changes: 2 additions & 2 deletions components/GovRadios.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function handleInput(e) {
</script>

<template>
<div :class="`govuk-form-group ${props.context.state.invalid ? 'govuk-form-group--error' : ''}`">
<div :class="`govuk-form-group ${showErrorState(props.context) ? 'govuk-form-group--error' : ''}`">
<fieldset
:id="id"
class="govuk-fieldset"
Expand All @@ -38,7 +38,7 @@ function handleInput(e) {
</div>
<GovDetails v-if="details" :summary-text="details.summaryText" :text="details.text" classes="govuk-!-margin-bottom-4" />
<p v-if="props.context.state.invalid" class="govuk-error-message" :data-testid="`${id}_error`">
<span class="govuk-visually-hidden">Error:</span> {{ props.context.messages.rule_required.value }}
<span class="govuk-visually-hidden">Error:</span> {{ getErrorMessage(props.context) }}
</p>
<div class="govuk-radios govuk-radios--small" data-module="govuk-radios">
<div v-for="key in Object.keys(options)" :key="key" class="govuk-radios__item">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const { handleInvalidSubmit, errorMessages } = useErrorSummary();
label="Name"
help="Name this pipework so it can be identified later"
name="name"
validation="required"
validation="required | length:1,50"
/>
<FormKit
id="location"
Expand Down
2 changes: 2 additions & 0 deletions pages/dwelling-details/shading/shading-form.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ describe('shading form', () => {
params: { shading: '1' }
}
});

await user.tab();
await user.clear(screen.getByTestId('name'));
await user.type(screen.getByTestId('name'), 'Wall');
await user.tab();
Expand Down
2 changes: 1 addition & 1 deletion pages/dwelling-details/summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mockNuxtImport, renderSuspended } from '@nuxt/test-utils/runtime';
import Summary from './summary.vue';
import { screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import hyphenate from '../../utils/hyphenate'
import hyphenate from '../../utils/hyphenate';

const navigateToMock = vi.hoisted(() => vi.fn());
mockNuxtImport('navigateTo', () => {
Expand Down
150 changes: 150 additions & 0 deletions utils/validation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@

describe('Show error state', () => {
it('returns true when state is invalid', () => {
const context = {
messages: {
rule_required: {
value: 'Field is required.',
visible: true
}
},
state: {
invalid: true
}
};

const result = showErrorState(context);

expect(result).toBe(true);
});

it('returns true when context contains at least 1 visible error message', () => {
const context = {
messages: {
rule_required: {
value: 'Field is required.',
visible: false
},
rule_length: {
value: 'Invalid length.',
visible: true
}
},
state: {
invalid: true
}
};

const result = showErrorState(context);

expect(result).toBe(true);
});

it('returns false when no visible error message is present', () => {
const context = {
messages: {
rule_required: {
value: 'Field is required.',
visible: false
}
},
state: {
invalid: true
}
};

const result = showErrorState(context);

expect(result).toBe(false);
});

it('returns false when no error message is present', () => {
const context = {
messages: {},
state: {
invalid: true
}
};

const result = showErrorState(context);

expect(result).toBe(false);
});

it('returns false when error message is not visible', () => {
const context = {
messages: {
rule_required: {
value: 'Field is required.',
visible: false
}
},
state: {
invalid: true
}
};

const result = showErrorState(context);

expect(result).toBe(false);
});

it('returns false when state is valid', () => {
const context = {
messages: {},
state: {
invalid: false
}
};

const result = showErrorState(context);

expect(result).toBe(false);
});
});

describe('Get error message', () => {
it('returns error message when context has error messages', () => {
const context = {
messages: {
rule_required: {
value: 'Field is required.',
visible: true
}
}
};

const result = getErrorMessage(context);

expect(result).toBe('Field is required.');
});

it('returns undefined when context has no error messages', () => {
const context = {
messages: {}
};

const result = getErrorMessage(context);

expect(result).toBeUndefined();
});

it('returns first visible error message when context has multiple error messages', () => {
const context = {
messages: {
rule_required: {
value: 'Field is required.',
visible: false
},
rule_length: {
value: 'Invalid length.',
visible: true
}
}
};

const result = getErrorMessage(context);

expect(result).toBe('Invalid length.');
});
});
17 changes: 17 additions & 0 deletions utils/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function showErrorState(context: Record<string, any>): boolean {
const messageKeys = Object.keys(context.messages)
.filter(key => context.messages[key].visible);

return context.state.invalid &&
messageKeys.length > 0 &&
context.messages[messageKeys[0]].visible;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getErrorMessage(context: Record<string, any>): string | undefined {
const messageKeys = Object.keys(context.messages)
.filter(key => context.messages[key].visible);

return messageKeys.length ? context.messages[messageKeys[0]].value : undefined;
}

0 comments on commit 6f95c86

Please sign in to comment.