Skip to content

Commit d2c7632

Browse files
Change date validator to use type {date: string, format: string} instead of string
1 parent 30f2a5a commit d2c7632

10 files changed

Lines changed: 226 additions & 51 deletions

File tree

packages/web/specs/components.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,10 +2616,10 @@
26162616
},
26172617
{
26182618
"name": "validator",
2619-
"type": "(string | ValidatorEntry | Validator<string>)[]",
2619+
"type": "(string | ValidatorEntry | Validator<{ date: string; format: string; }>)[]",
26202620
"complexType": {
2621-
"original": "Array<\n string | ValidatorEntry | Validator<string>\n >",
2622-
"resolved": "(string | ValidatorEntry | Validator<string>)[]",
2621+
"original": "Array<\n string | ValidatorEntry | Validator<{date: string, format: string}>\n >",
2622+
"resolved": "(string | ValidatorEntry | Validator<{ date: string; format: string; }>)[]",
26232623
"references": {
26242624
"Array": {
26252625
"location": "global",
@@ -2650,7 +2650,7 @@
26502650
"type": "ValidatorEntry"
26512651
},
26522652
{
2653-
"type": "Validator<string>)[]"
2653+
"type": "Validator<{ date: string; format: string; }>)[]"
26542654
}
26552655
],
26562656
"optional": false,

packages/web/src/components.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ export namespace Components {
358358
* Array of validators
359359
*/
360360
"validator": Array<
361-
string | ValidatorEntry | Validator<string>
361+
string | ValidatorEntry | Validator<{date: string, format: string}>
362362
>;
363363
/**
364364
* Read-only property of the date-input, returns a ValidityState object that represents the validity states this element is in.
@@ -2741,7 +2741,7 @@ declare namespace LocalJSX {
27412741
* Array of validators
27422742
*/
27432743
"validator"?: Array<
2744-
string | ValidatorEntry | Validator<string>
2744+
string | ValidatorEntry | Validator<{date: string, format: string}>
27452745
>;
27462746
/**
27472747
* Read-only property of the date-input, returns a ValidityState object that represents the validity states this element is in.

packages/web/src/components/gcds-checkboxes/test/gcds-checkboxes.e2e.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ test.describe('gcds-checkboxes', () => {
190190
};
191191
};
192192

193-
(el as HTMLGcdsDateInputElement).validator = [selectAll()];
193+
(el as HTMLGcdsCheckboxesElement).validator = [selectAll()];
194194
});
195195

196196
await page.waitForChanges();
@@ -238,7 +238,7 @@ test.describe('gcds-checkboxes', () => {
238238
};
239239

240240
// @ts-expect-error Old format of validator is different than new format. Will still run in JS environments
241-
(el as HTMLGcdsDateInputElement).validator = [selectAll()];
241+
(el as HTMLGcdsCheckboxesElement).validator = [selectAll()];
242242
});
243243

244244
await page.waitForChanges();

packages/web/src/components/gcds-date-input/gcds-date-input.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class GcdsDateInput {
5454
// Array to store which native HTML errors are happening on the input
5555
private htmlValidationErrors = [];
5656

57-
_validator: Validator<string> = defaultValidator;
57+
_validator: Validator<{date: string, format: string}> = defaultValidator;
5858

5959
/**
6060
* Name attribute for the date input.
@@ -173,7 +173,7 @@ export class GcdsDateInput {
173173
* Array of validators
174174
*/
175175
@Prop({ mutable: true }) validator: Array<
176-
string | ValidatorEntry | Validator<string>
176+
string | ValidatorEntry | Validator<{date: string, format: string}>
177177
>;
178178
@Watch('validator')
179179
validateValidator() {
@@ -269,12 +269,18 @@ export class GcdsDateInput {
269269
*/
270270
@Method()
271271
async validate() {
272+
const format = this.format;
273+
const value = format === 'full' || format === 'iso'
274+
? `${this.yearValue}-${this.monthValue}-${this.dayValue}`
275+
: `${this.yearValue}-${this.monthValue}`;
276+
272277
this.hasError = handleValidationResult(
273278
this.el as HTMLGcdsDateInputElement,
274-
this._validator.validate(
275-
this.format === 'full' || this.format === 'iso'
276-
? `${this.yearValue}-${this.monthValue}-${this.dayValue}`
277-
: `${this.yearValue}-${this.monthValue}`,
279+
280+
this._validator.validate({
281+
date: value,
282+
format: format,
283+
}
278284
),
279285
this.legend,
280286
this.gcdsError,

packages/web/src/components/gcds-date-input/readme.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ A date input is a space to enter a known date.
1111

1212
## Properties
1313

14-
| Property | Attribute | Description | Type | Default |
15-
| --------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ----------- |
16-
| `autofocus` | `autofocus` | If true, the date-input will be focused on component render | `boolean` | `undefined` |
17-
| `disabled` | `disabled` | Specifies if the date input is disabled or not. | `boolean` | `false` |
18-
| `errorMessage` | `error-message` | Error message displayed below the legend and above form fields. | `string` | `undefined` |
19-
| `form` | `form` | The ID of the form that the date-input field belongs to. | `string` | `undefined` |
20-
| `format` _(required)_ | `format` | Set this property to full to show month, day, and year form elements. Set it to compact to show only the month and year form elements. | `"compact" \| "full" \| "iso"` | `undefined` |
21-
| `hint` | `hint` | Hint displayed below the legend and above form fields. | `string` | `undefined` |
22-
| `legend` _(required)_ | `legend` | Fieldset legend | `string` | `undefined` |
23-
| `max` | `max` | The maximum date that the date-input field can accept. Format: YYYY-MM-DD or YYYY-MM | `string` | `undefined` |
24-
| `min` | `min` | The minimum date that the date-input field can accept. Format: YYYY-MM-DD or YYYY-MM | `string` | `undefined` |
25-
| `name` _(required)_ | `name` | Name attribute for the date input. | `string` | `undefined` |
26-
| `required` | `required` | Specifies if a form field is required or not. | `boolean` | `false` |
27-
| `validateOn` | `validate-on` | Set event to call validator | `"blur" \| "other" \| "submit"` | `'blur'` |
28-
| `validator` | `validator` | Array of validators | `(string \| ValidatorEntry \| Validator<string>)[]` | `undefined` |
29-
| `validity` | `validity` | Read-only property of the date-input, returns a ValidityState object that represents the validity states this element is in. | `ValidityState` | `undefined` |
30-
| `value` | `value` | Combined date value from the two/three form elements. Format: YYYY-MM-DD or YYYY-MM | `string` | `undefined` |
14+
| Property | Attribute | Description | Type | Default |
15+
| --------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----------- |
16+
| `autofocus` | `autofocus` | If true, the date-input will be focused on component render | `boolean` | `undefined` |
17+
| `disabled` | `disabled` | Specifies if the date input is disabled or not. | `boolean` | `false` |
18+
| `errorMessage` | `error-message` | Error message displayed below the legend and above form fields. | `string` | `undefined` |
19+
| `form` | `form` | The ID of the form that the date-input field belongs to. | `string` | `undefined` |
20+
| `format` _(required)_ | `format` | Set this property to full to show month, day, and year form elements. Set it to compact to show only the month and year form elements. | `"compact" \| "full" \| "iso"` | `undefined` |
21+
| `hint` | `hint` | Hint displayed below the legend and above form fields. | `string` | `undefined` |
22+
| `legend` _(required)_ | `legend` | Fieldset legend | `string` | `undefined` |
23+
| `max` | `max` | The maximum date that the date-input field can accept. Format: YYYY-MM-DD or YYYY-MM | `string` | `undefined` |
24+
| `min` | `min` | The minimum date that the date-input field can accept. Format: YYYY-MM-DD or YYYY-MM | `string` | `undefined` |
25+
| `name` _(required)_ | `name` | Name attribute for the date input. | `string` | `undefined` |
26+
| `required` | `required` | Specifies if a form field is required or not. | `boolean` | `false` |
27+
| `validateOn` | `validate-on` | Set event to call validator | `"blur" \| "other" \| "submit"` | `'blur'` |
28+
| `validator` | `validator` | Array of validators | `(string \| ValidatorEntry \| Validator<{ date: string; format: string; }>)[]` | `undefined` |
29+
| `validity` | `validity` | Read-only property of the date-input, returns a ValidityState object that represents the validity states this element is in. | `ValidityState` | `undefined` |
30+
| `value` | `value` | Combined date value from the two/three form elements. Format: YYYY-MM-DD or YYYY-MM | `string` | `undefined` |
3131

3232

3333
## Events

packages/web/src/components/gcds-date-input/stories/gcds-date-input.stories.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,22 @@ isoFr.args = {
281281
validateOn: '',
282282
};
283283

284+
// ------ Date input ISO Format Required ------
285+
286+
export const IsoRequired = Template.bind({});
287+
IsoRequired.args = {
288+
name: 'required-default',
289+
legend: 'Date input',
290+
format: 'iso',
291+
value: '',
292+
hint: '',
293+
errorMessage: '',
294+
required: true,
295+
disabled: false,
296+
lang: 'en',
297+
validateOn: '',
298+
};
299+
284300
// ------ Date input Format Compact English ------
285301

286302
export const CompactEN = Template.bind({});

packages/web/src/components/gcds-date-input/stories/overview.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ A date input is a space to enter a known date.
4848

4949
<Canvas of={DateInput.DefaultState} story={{ inline: true }} />
5050

51-
#### Required
51+
#### Required - Full
5252

5353
<Canvas of={DateInput.Required} story={{ inline: true }} />
5454

55+
#### Required - ISO
56+
57+
<Canvas of={DateInput.IsoRequired} story={{ inline: true }} />
58+
5559
#### Error message
5660

5761
<Canvas of={DateInput.Error} story={{ inline: true }} />

packages/web/src/components/gcds-date-input/test/gcds-date-input.e2e.ts

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ test.describe('gcds-date-input', () => {
357357
el => (el as HTMLGcdsDateInputElement).errorMessage,
358358
);
359359

360-
expect(errorMessage).toEqual(dateInputErrorMessage.en.missingmonth);
360+
expect(errorMessage).toEqual(dateInputErrorMessage.en.missingmonthinput);
361361
});
362362
test('Validation - Missing month and day', async ({ page }) => {
363363
const element = page.locator('gcds-date-input');
@@ -477,7 +477,57 @@ test.describe('gcds-date-input', () => {
477477
el => (el as HTMLGcdsDateInputElement).errorMessage,
478478
);
479479

480-
expect(errorMessage).toEqual(dateInputErrorMessage.en.missingmonthinput);
480+
expect(errorMessage).toEqual(dateInputErrorMessage.en.invalidmonth);
481+
});
482+
test('Validation - iso - Missing month & day', async ({ page }) => {
483+
const element = page.locator('gcds-date-input');
484+
485+
// Wait for element to attach and become visible, allowing up to 10s
486+
await element.waitFor({ state: 'attached' });
487+
await element.waitFor({ state: 'visible' });
488+
await element.waitFor({ timeout: 10000 });
489+
490+
await element.evaluate(
491+
(el: HTMLGcdsDateInputElement) => (el.format = 'iso'),
492+
);
493+
494+
await page.waitForChanges();
495+
496+
await element.locator('input[name="year"]').fill('1234');
497+
498+
await element.evaluate(el => (el as HTMLGcdsDateInputElement).validate());
499+
500+
const errorMessage = await element.evaluate(
501+
el => (el as HTMLGcdsDateInputElement).errorMessage,
502+
);
503+
504+
expect(errorMessage).toEqual(dateInputErrorMessage.en.missingmonthinputday);
505+
});
506+
test('Validation - iso - Missing month & year', async ({ page }) => {
507+
const element = page.locator('gcds-date-input');
508+
509+
// Wait for element to attach and become visible, allowing up to 10s
510+
await element.waitFor({ state: 'attached' });
511+
await element.waitFor({ state: 'visible' });
512+
await element.waitFor({ timeout: 10000 });
513+
514+
await element.evaluate(
515+
(el: HTMLGcdsDateInputElement) => (el.format = 'iso'),
516+
);
517+
518+
await page.waitForChanges();
519+
520+
await element.locator('input[name="day"]').fill('25');
521+
522+
await element.evaluate(el => (el as HTMLGcdsDateInputElement).validate());
523+
524+
const errorMessage = await element.evaluate(
525+
el => (el as HTMLGcdsDateInputElement).errorMessage,
526+
);
527+
528+
expect(errorMessage).toEqual(
529+
dateInputErrorMessage.en.missingmonthinputyear,
530+
);
481531
});
482532
test('Validation - Custom validation', async ({ page }) => {
483533
const element = page.locator('gcds-date-input');
@@ -490,8 +540,8 @@ test.describe('gcds-date-input', () => {
490540
await element.evaluate(el => {
491541
const expectYear = (year: string) => {
492542
return {
493-
validate: (value: string) => {
494-
const dates = value.split('-');
543+
validate: ({ date }: { date: string; }) => {
544+
const dates = date.split('-');
495545
return {
496546
valid: dates[0] === year,
497547
reason: {
@@ -541,8 +591,8 @@ test.describe('gcds-date-input', () => {
541591
await element.evaluate(el => {
542592
const expectYear = (year: string) => {
543593
return {
544-
validate: (value: string) => {
545-
const dates = value.split('-');
594+
validate: ({ date }: { date: string }) => {
595+
const dates = date.split('-');
546596
return dates[0] === year;
547597
},
548598
errorMessage: {

packages/web/src/validators/input-validators/input-validators.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export const dateInputErrorMessage = {
7272
missingdayyear: 'Enter the day and year.',
7373
invalidyearlength: 'Year must be 4 digits.',
7474
invalidyear: 'Enter a valid year.',
75+
invalidmonth: 'Enter a valid month.',
7576
invalidday: 'Enter a valid day.',
7677
},
7778
fr: {
@@ -87,12 +88,16 @@ export const dateInputErrorMessage = {
8788
missingdayyear: "Saisissez le jour et l'année.",
8889
invalidyearlength: "L'année doit inclure 4 chiffres.",
8990
invalidyear: 'Entrez une année valide.',
91+
invalidmonth: 'Saisissez un mois valide',
9092
invalidday: 'Saisissez un jour valide.',
9193
},
9294
};
9395

94-
export const requiredDateInput: Validator<string> = {
95-
validate: (date: string) => {
96+
export const requiredDateInput: Validator<{
97+
date: string;
98+
format: 'full' | 'compact' | 'iso';
99+
}> = {
100+
validate: ({ date, format }) => {
96101
if (isValidDate(date)) {
97102
return {
98103
valid: true,
@@ -110,8 +115,6 @@ export const requiredDateInput: Validator<string> = {
110115
year: splitDate[0],
111116
};
112117

113-
const format = splitDate.length === 3 ? 'full' : 'compact';
114-
115118
const error = getDateInputError(dateObject, format);
116119

117120
return error;
@@ -222,6 +225,12 @@ export const getDateInputError = (
222225
errorResponse.reason.en = dateInputErrorMessage.en.invalidyear;
223226
errorResponse.reason.fr = dateInputErrorMessage.fr.invalidyear;
224227

228+
// Invalid month
229+
} else if (Number(month) < 1 || Number(month) > 12) {
230+
errorResponse.errors.month = true;
231+
errorResponse.reason.en = dateInputErrorMessage.en.invalidmonth;
232+
errorResponse.reason.fr = dateInputErrorMessage.fr.invalidmonth;
233+
225234
// Invalid day
226235
} else if (!isValidDay(`${year}-${month}-${day}`)) {
227236
errorResponse.errors.day = true;

0 commit comments

Comments
 (0)