Skip to content

Commit

Permalink
On submit validation + input specific error messages (#121)
Browse files Browse the repository at this point in the history
* Add specific error messages in validators

* Add onSubmit validation to gcds-input

* Add validate on submit to components

* Add French text for error messages

* PR feedback: Clean up code

* Update var to let in fieldset-validators
  • Loading branch information
ethanWallace authored Mar 22, 2023
1 parent a6e9c63 commit 2f92ec5
Show file tree
Hide file tree
Showing 20 changed files with 352 additions and 128 deletions.
48 changes: 36 additions & 12 deletions packages/angular/src/lib/stencil-generated/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export class GcdsButton {
}
}


import type { GcdsErrorInterface as IGcdsCheckboxGcdsErrorInterface } from '@cdssnc/gcds-components';
export declare interface GcdsCheckbox extends Components.GcdsCheckbox {
/**
* Emitted when the checkbox has focus.
Expand All @@ -124,6 +124,10 @@ export declare interface GcdsCheckbox extends Components.GcdsCheckbox {
* Update value based on user input.
*/
gcdsChange: EventEmitter<CustomEvent<any>>;
/**
* Emitted when the input has a validation error.
*/
gcdsError: EventEmitter<CustomEvent<IGcdsCheckboxGcdsErrorInterface>>;

}

Expand All @@ -143,7 +147,7 @@ export class GcdsCheckbox {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsChange']);
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsChange', 'gcdsError']);
}
}

Expand Down Expand Up @@ -208,7 +212,7 @@ export class GcdsErrorMessage {
}
}


import type { GcdsErrorInterface as IGcdsFieldsetGcdsErrorInterface } from '@cdssnc/gcds-components';
export declare interface GcdsFieldset extends Components.GcdsFieldset {
/**
* Emitted when the fieldset has a validation error.
Expand All @@ -218,6 +222,10 @@ export declare interface GcdsFieldset extends Components.GcdsFieldset {
* Emitted when the fieldset has a validation error.
*/
gcdsGroupErrorClear: EventEmitter<CustomEvent<void>>;
/**
* Emitted when the input has a validation error.
*/
gcdsError: EventEmitter<CustomEvent<IGcdsFieldsetGcdsErrorInterface>>;

}

Expand All @@ -237,11 +245,11 @@ export class GcdsFieldset {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['gcdsGroupError', 'gcdsGroupErrorClear']);
proxyOutputs(this, this.el, ['gcdsGroupError', 'gcdsGroupErrorClear', 'gcdsError']);
}
}


import type { GcdsErrorInterface as IGcdsFileUploaderGcdsErrorInterface } from '@cdssnc/gcds-components';
export declare interface GcdsFileUploader extends Components.GcdsFileUploader {
/**
* Emitted when the uploader has focus.
Expand All @@ -259,6 +267,10 @@ export declare interface GcdsFileUploader extends Components.GcdsFileUploader {
* Remove file and update value.
*/
gcdsRemoveFile: EventEmitter<CustomEvent<any>>;
/**
* Emitted when the input has a validation error.
*/
gcdsError: EventEmitter<CustomEvent<IGcdsFileUploaderGcdsErrorInterface>>;

}

Expand All @@ -278,7 +290,7 @@ export class GcdsFileUploader {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsFileUploaderChange', 'gcdsRemoveFile']);
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsFileUploaderChange', 'gcdsRemoveFile', 'gcdsError']);
}
}

Expand Down Expand Up @@ -387,7 +399,7 @@ export class GcdsIcon {
}
}


import type { GcdsErrorInterface as IGcdsInputGcdsErrorInterface } from '@cdssnc/gcds-components';
export declare interface GcdsInput extends Components.GcdsInput {
/**
* Emitted when the input has focus.
Expand All @@ -401,6 +413,10 @@ export declare interface GcdsInput extends Components.GcdsInput {
* Update value based on user input.
*/
gcdsChange: EventEmitter<CustomEvent<any>>;
/**
* Emitted when the input has a validation error.
*/
gcdsError: EventEmitter<CustomEvent<IGcdsInputGcdsErrorInterface>>;

}

Expand All @@ -420,7 +436,7 @@ export class GcdsInput {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsChange']);
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsChange', 'gcdsError']);
}
}

Expand Down Expand Up @@ -551,7 +567,7 @@ export class GcdsRadio {
}
}


import type { GcdsErrorInterface as IGcdsSelectGcdsErrorInterface } from '@cdssnc/gcds-components';
export declare interface GcdsSelect extends Components.GcdsSelect {
/**
* Update value based on user selection.
Expand All @@ -565,6 +581,10 @@ export declare interface GcdsSelect extends Components.GcdsSelect {
* Emitted when the select loses focus.
*/
gcdsBlur: EventEmitter<CustomEvent<void>>;
/**
* Emitted when the input has a validation error.
*/
gcdsError: EventEmitter<CustomEvent<IGcdsSelectGcdsErrorInterface>>;

}

Expand All @@ -584,7 +604,7 @@ export class GcdsSelect {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['gcdsSelectChange', 'gcdsFocus', 'gcdsBlur']);
proxyOutputs(this, this.el, ['gcdsSelectChange', 'gcdsFocus', 'gcdsBlur', 'gcdsError']);
}
}

Expand Down Expand Up @@ -651,7 +671,7 @@ export class GcdsStepper {
}
}


import type { GcdsErrorInterface as IGcdsTextareaGcdsErrorInterface } from '@cdssnc/gcds-components';
export declare interface GcdsTextarea extends Components.GcdsTextarea {
/**
* Emitted when the textarea has focus.
Expand All @@ -665,6 +685,10 @@ export declare interface GcdsTextarea extends Components.GcdsTextarea {
* Update value based on user input.
*/
gcdsChange: EventEmitter<CustomEvent<any>>;
/**
* Emitted when the input has a validation error.
*/
gcdsError: EventEmitter<CustomEvent<IGcdsTextareaGcdsErrorInterface>>;

}

Expand All @@ -684,7 +708,7 @@ export class GcdsTextarea {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsChange']);
proxyOutputs(this, this.el, ['gcdsFocus', 'gcdsBlur', 'gcdsChange', 'gcdsError']);
}
}

Expand Down
26 changes: 25 additions & 1 deletion packages/web/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { Validator, ValidatorEntry } from "./validators";
import { GcdsErrorInterface, Validator, ValidatorEntry } from "./validators";
export namespace Components {
interface GcdsAlert {
/**
Expand Down Expand Up @@ -1195,6 +1195,10 @@ declare namespace LocalJSX {
* Update value based on user input.
*/
"onGcdsChange"?: (event: GcdsCheckboxCustomEvent<any>) => void;
/**
* Emitted when the input has a validation error.
*/
"onGcdsError"?: (event: GcdsCheckboxCustomEvent<GcdsErrorInterface>) => void;
/**
* Emitted when the checkbox has focus.
*/
Expand Down Expand Up @@ -1259,6 +1263,10 @@ declare namespace LocalJSX {
* The title for the contents of the fieldset
*/
"legend": string;
/**
* Emitted when the input has a validation error.
*/
"onGcdsError"?: (event: GcdsFieldsetCustomEvent<GcdsErrorInterface>) => void;
/**
* Emitted when the fieldset has a validation error.
*/
Expand Down Expand Up @@ -1321,6 +1329,10 @@ declare namespace LocalJSX {
* Emitted when the uploader loses focus.
*/
"onGcdsBlur"?: (event: GcdsFileUploaderCustomEvent<void>) => void;
/**
* Emitted when the input has a validation error.
*/
"onGcdsError"?: (event: GcdsFileUploaderCustomEvent<GcdsErrorInterface>) => void;
/**
* Update value based on user selection.
*/
Expand Down Expand Up @@ -1531,6 +1543,10 @@ declare namespace LocalJSX {
* Update value based on user input.
*/
"onGcdsChange"?: (event: GcdsInputCustomEvent<any>) => void;
/**
* Emitted when the input has a validation error.
*/
"onGcdsError"?: (event: GcdsInputCustomEvent<GcdsErrorInterface>) => void;
/**
* Emitted when the input has focus.
*/
Expand Down Expand Up @@ -1739,6 +1755,10 @@ declare namespace LocalJSX {
* Emitted when the select loses focus.
*/
"onGcdsBlur"?: (event: GcdsSelectCustomEvent<void>) => void;
/**
* Emitted when the input has a validation error.
*/
"onGcdsError"?: (event: GcdsSelectCustomEvent<GcdsErrorInterface>) => void;
/**
* Emitted when the select has focus.
*/
Expand Down Expand Up @@ -1859,6 +1879,10 @@ declare namespace LocalJSX {
* Update value based on user input.
*/
"onGcdsChange"?: (event: GcdsTextareaCustomEvent<any>) => void;
/**
* Emitted when the input has a validation error.
*/
"onGcdsError"?: (event: GcdsTextareaCustomEvent<GcdsErrorInterface>) => void;
/**
* Emitted when the textarea has focus.
*/
Expand Down
21 changes: 20 additions & 1 deletion packages/web/src/components/gcds-checkbox/gcds-checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Element, Event, EventEmitter, Listen, Method, State, Prop, Watch, Host, h } from '@stencil/core';
import { assignLanguage, elementGroupCheck, inheritAttributes, observerConfig } from '../../utils/utils';
import { Validator, defaultValidator, ValidatorEntry, getValidator, requiredValidator } from '../../validators';
import { Validator, defaultValidator, ValidatorEntry, getValidator, requiredValidator, GcdsErrorInterface } from '../../validators';

@Component({
tag: 'gcds-checkbox',
Expand Down Expand Up @@ -206,11 +206,30 @@ export class GcdsCheckbox {
async validate() {
if (!this._validator.validate(this.checked) && this._validator.errorMessage) {
this.errorMessage = this._validator.errorMessage[this.lang];
this.gcdsError.emit({ id: `#${this.checkboxId}`, message: this.errorMessage });
} else {
this.errorMessage = "";
}
}

/**
* Emitted when the input has a validation error.
*/
@Event() gcdsError!: EventEmitter<GcdsErrorInterface>;

@Listen("submit", { target: 'document' })
submitListener(e) {
if (e.target == this.el.closest("form")) {
if (this.validateOn && this.validateOn != "other") {
this.validate();
}

if (this.hasError) {
e.preventDefault();
}
}
}

/*
* Observe lang attribute change
*/
Expand Down
11 changes: 6 additions & 5 deletions packages/web/src/components/gcds-checkbox/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@

## Events

| Event | Description | Type |
| ------------ | -------------------------------------- | ------------------- |
| `gcdsBlur` | Emitted when the checkbox loses focus. | `CustomEvent<void>` |
| `gcdsChange` | Update value based on user input. | `CustomEvent<any>` |
| `gcdsFocus` | Emitted when the checkbox has focus. | `CustomEvent<void>` |
| Event | Description | Type |
| ------------ | ---------------------------------------------- | --------------------------------- |
| `gcdsBlur` | Emitted when the checkbox loses focus. | `CustomEvent<void>` |
| `gcdsChange` | Update value based on user input. | `CustomEvent<any>` |
| `gcdsError` | Emitted when the input has a validation error. | `CustomEvent<GcdsErrorInterface>` |
| `gcdsFocus` | Emitted when the checkbox has focus. | `CustomEvent<void>` |


## Methods
Expand Down
21 changes: 20 additions & 1 deletion packages/web/src/components/gcds-fieldset/gcds-fieldset.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Prop, Element, Method, Event, EventEmitter, Listen, State, Host, Watch, h } from '@stencil/core';
import { assignLanguage, observerConfig } from '../../utils/utils';
import { Validator, defaultValidator, ValidatorEntry, getValidator, requiredValidator } from '../../validators';
import { Validator, defaultValidator, ValidatorEntry, getValidator, requiredValidator, GcdsErrorInterface } from '../../validators';
import { validateFieldsetElements } from '../../validators/fieldset-validators/fieldset-validators';

@Component({
Expand Down Expand Up @@ -130,6 +130,7 @@ export class GcdsFieldset {
if (!this._validator.validate(this.fieldsetId) && this._validator.errorMessage) {
this.errorMessage = this._validator.errorMessage[this.lang];
this.gcdsGroupError.emit(this.errorMessage);
this.gcdsError.emit({ id: `#${this.fieldsetId}`, message: this.errorMessage });
} else {
this.errorMessage = "";
this.gcdsGroupErrorClear.emit();
Expand Down Expand Up @@ -162,6 +163,24 @@ export class GcdsFieldset {
}
}

/**
* Emitted when the input has a validation error.
*/
@Event() gcdsError!: EventEmitter<GcdsErrorInterface>;

@Listen("submit", { target: 'document' })
submitListener(e) {
if (e.target == this.el.closest("form")) {
if (this.validateOn && this.validateOn != "other") {
this.validate();
}

if (this.hasError) {
e.preventDefault();
}
}
}

/*
* Observe lang attribute change
*/
Expand Down
9 changes: 5 additions & 4 deletions packages/web/src/components/gcds-fieldset/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@

## Events

| Event | Description | Type |
| --------------------- | ------------------------------------------------- | --------------------- |
| `gcdsGroupError` | Emitted when the fieldset has a validation error. | `CustomEvent<string>` |
| `gcdsGroupErrorClear` | Emitted when the fieldset has a validation error. | `CustomEvent<void>` |
| Event | Description | Type |
| --------------------- | ------------------------------------------------- | --------------------------------- |
| `gcdsError` | Emitted when the input has a validation error. | `CustomEvent<GcdsErrorInterface>` |
| `gcdsGroupError` | Emitted when the fieldset has a validation error. | `CustomEvent<string>` |
| `gcdsGroupErrorClear` | Emitted when the fieldset has a validation error. | `CustomEvent<void>` |


## Methods
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Element, Event, EventEmitter, Prop, Watch, State, Method, Host, h } from '@stencil/core';
import { Component, Element, Event, EventEmitter, Prop, Watch, State, Method, Host, h, Listen } from '@stencil/core';
import { assignLanguage, inheritAttributes, observerConfig } from '../../utils/utils';
import { Validator, defaultValidator, ValidatorEntry, getValidator, requiredValidator } from '../../validators';
import { Validator, defaultValidator, ValidatorEntry, getValidator, requiredValidator, GcdsErrorInterface } from '../../validators';

@Component({
tag: 'gcds-file-uploader',
Expand Down Expand Up @@ -221,11 +221,30 @@ export class GcdsFileUploader {
async validate() {
if (!this._validator.validate(this.value.length) && this._validator.errorMessage) {
this.errorMessage = this._validator.errorMessage[this.lang];
this.gcdsError.emit({ id: `#${this.uploaderId}`, message: this.errorMessage });
} else {
this.errorMessage = "";
}
}

/**
* Emitted when the input has a validation error.
*/
@Event() gcdsError!: EventEmitter<GcdsErrorInterface>;

@Listen("submit", { target: 'document' })
submitListener(e) {
if (e.target == this.el.closest("form")) {
if (this.validateOn && this.validateOn != "other") {
this.validate();
}

if (this.hasError) {
e.preventDefault();
}
}
}

/*
* Observe lang attribute change
*/
Expand Down
Loading

0 comments on commit 2f92ec5

Please sign in to comment.