Skip to content

Commit

Permalink
feat(quantity): update validity state on isRequired change
Browse files Browse the repository at this point in the history
  • Loading branch information
dpellier committed Nov 28, 2024
1 parent cde1f8a commit 23e799e
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class OdsInput {
@Event() odsChange!: EventEmitter<OdsInputChangeEventDetail>;
@Event() odsClear!: EventEmitter<void>;
@Event() odsFocus!: EventEmitter<void>;
@Event() odsInvalid!: EventEmitter<boolean>;
@Event() odsReset!: EventEmitter<void>;
@Event() odsToggleMask!: EventEmitter<void>;

Expand Down Expand Up @@ -122,13 +123,18 @@ export class OdsInput {
return this.internals.willValidate;
}

@Watch('isInvalid')
onIsInvalidChange(): void {
this.odsInvalid.emit(this.isInvalid);
}

@Watch('isMasked')
onMaskedChange(): void {
onIsMaskedChange(): void {
this.isPassword = isPassword(this.isMasked);
}

componentWillLoad(): void {
this.onMaskedChange();
this.onIsMaskedChange();

this.observer = new MutationObserver((mutations: MutationRecord[]) => {
for (const mutation of mutations) {
Expand Down
20 changes: 20 additions & 0 deletions packages/ods/src/components/input/tests/validity/ods-input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,26 @@ describe('ods-input validity', () => {
});

describe('watchers', () => {
describe('isInvalid', () => {
it('should triggers an odsInvalid event when value change', async() => {
await setup('<ods-input is-required value="e"></ods-input>');
const odsInvalidSpy = await page.spyOnEvent('odsInvalid');

await el.callMethod('clear');
await page.waitForChanges();

expect(odsInvalidSpy).toHaveReceivedEventTimes(1);
expect(odsInvalidSpy).toHaveReceivedEventDetail(true);

await el.type('a', { delay: 100 });
await page.click('body', { offset: { x: 200, y: 200 } }); // Blur
await page.waitForChanges();

expect(odsInvalidSpy).toHaveReceivedEventTimes(2);
expect(odsInvalidSpy).toHaveReceivedEventDetail(false);
});
});

describe('is-required', () => {
it('should update validity when is-required change', async() => {
await setup('<ods-input is-required></ods-input>');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,19 @@ export class OdsQuantity {
}
}

private async onOdsInvalid(event: CustomEvent<boolean>): Promise<void> {
await updateInternals(this.internals, this.value, this.odsInput);
this.isInvalid = event.detail;
}

private getHasError(): boolean {
return this.hasError || this.isInvalid;
}

render(): FunctionalComponent {
return (
<Host class="ods-quantity"
<Host
class="ods-quantity"
disabled={ this.isDisabled }
readonly={ this.isReadonly }>
<ods-button
Expand Down Expand Up @@ -185,6 +191,7 @@ export class OdsQuantity {
onKeyUp={ (event: KeyboardEvent): void => submitFormOnEnter(event, this.internals.form) }
onOdsBlur={ () => this.onOdsBlur() }
onOdsChange={ (event: OdsInputChangeEvent) => this.onOdsChange(event) }
onOdsInvalid={ (event: CustomEvent<boolean>) => this.onOdsInvalid(event) }
max={ this.max }
min={ this.min }
name={ this.name }
Expand Down
10 changes: 10 additions & 0 deletions packages/ods/src/components/quantity/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
<button id="form-check-validity-button" type="button">Check validity</button>
<button id="form-clear-button" type="button">Clear</button>
<button id="form-reset-button" type="button">Reset</button>
<button id="form-toggle-is-required-button" type="button">Toggle is required</button>
<button type="reset">Global Form Reset</button>
<button id="form-submit-button" type="submit">Submit</button>
</form>
Expand All @@ -111,6 +112,7 @@
const clearFormButton = document.querySelector('#form-clear-button');
const resetFormButton = document.querySelector('#form-reset-button');
const submitFormButton = document.querySelector('#form-submit-button');
const toggleIsRequiredButton = document.querySelector('#form-toggle-is-required-button');

checkValidityFormButton.addEventListener('click', async() => {
console.log('Check validity: ', await formQuantity.checkValidity());
Expand All @@ -124,6 +126,14 @@
await formQuantity.reset();
});

toggleIsRequiredButton.addEventListener('click', async() => {
if (formQuantity.hasAttribute('is-required')) {
formQuantity.removeAttribute('is-required');
} else {
formQuantity.setAttribute('is-required', 'true');
}
});

['odsBlur', 'odsClear', 'odsFocus', 'odsReset', 'odsChange'].forEach((eventName) =>{
formQuantity.addEventListener(eventName, (event) => {
// console.log(eventName, event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ describe('ods-quantity rendering', () => {
}, part);
}

async function isInErrorState(): Promise<boolean | undefined> {
return await page.evaluate(() => {
return document.querySelector('ods-quantity')?.shadowRoot?.querySelector('.ods-quantity__input')?.shadowRoot?.querySelector('.ods-input__input')?.classList.contains('ods-input__input--error');
});
}

async function setup(content: string, customStyle?: string): Promise<void> {
page = await newE2EPage();

Expand Down Expand Up @@ -102,51 +108,52 @@ describe('ods-quantity rendering', () => {
});
await page.waitForChanges();

const hasErrorClass = await page.evaluate(() => {
return document.querySelector('ods-quantity')?.shadowRoot?.querySelector('.ods-quantity__input')?.shadowRoot?.querySelector('.ods-input__input')?.classList.contains('ods-input__input--error');
});
expect(hasErrorClass).toBe(true);
expect(await isInErrorState()).toBe(true);
});

it('should toggle the error state on value change', async() => {
await setup('<form method="get" onsubmit="return false"><ods-quantity is-required></ods-quantity></form>');

await el.type('0');
await page.waitForChanges();
const hasErrorClass = await page.evaluate(() => {
return document.querySelector('ods-quantity')?.shadowRoot?.querySelector('.ods-quantity__input')?.shadowRoot?.querySelector('.ods-input__input')?.classList.contains('ods-input__input--error');
});
expect(hasErrorClass).toBe(false);

expect(await isInErrorState()).toBe(false);

await el.callMethod('clear');
await page.click('body', { offset: { x: 400, y: 400 } }); // Blur
await page.waitForChanges();

const hasErrorClass2 = await page.evaluate(() => {
return document.querySelector('ods-quantity')?.shadowRoot?.querySelector('.ods-quantity__input')?.shadowRoot?.querySelector('.ods-input__input')?.classList.contains('ods-input__input--error');
});
await page.waitForChanges();
expect(hasErrorClass2).toBe(true);
expect(await isInErrorState()).toBe(true);
});

it('should enforce the error state if has-error is set even on valid quantity', async() => {
await setup('<form method="get" onsubmit="return false"><ods-quantity is-required has-error value="0"></ods-quantity></form>');
await page.waitForChanges();

const hasErrorClass = await page.evaluate(() => {
return document.querySelector('ods-quantity')?.shadowRoot?.querySelector('.ods-quantity__input')?.shadowRoot?.querySelector('.ods-input__input')?.classList.contains('ods-input__input--error');
});
expect(hasErrorClass).toBe(true);
expect(await isInErrorState()).toBe(true);

await page.evaluate(() => {
document.querySelector<HTMLFormElement>('form')?.requestSubmit();
});
await page.waitForChanges();

const hasErrorClass2 = await page.evaluate(() => {
return document.querySelector('ods-quantity')?.shadowRoot?.querySelector('.ods-quantity__input')?.shadowRoot?.querySelector('.ods-input__input')?.classList.contains('ods-input__input--error');
});
expect(hasErrorClass2).toBe(true);
expect(await isInErrorState()).toBe(true);
});

it('should update error state on odsInvalid event', async() => {
await setup('<ods-quantity></ods-quantity>');

expect(await isInErrorState()).toBe(false);

input.triggerEvent('odsInvalid', { detail: true });
await page.waitForChanges();

expect(await isInErrorState()).toBe(true);

input.triggerEvent('odsInvalid', { detail: false });
await page.waitForChanges();

expect(await isInErrorState()).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,24 @@ describe('ods-quantity validity', () => {
expect(formValidity).toBe(true);
});
});

describe('watchers', () => {
describe('is-required', () => {
it('should update validity when is-required change', async() => {
await setup('<ods-quantity></ods-quantity>');

expect(await el.callMethod('checkValidity')).toBe(true);

await el.setAttribute('is-required', 'true');
await page.waitForChanges();

expect(await el.callMethod('checkValidity')).toBe(false);

await el.removeAttribute('is-required');
await page.waitForChanges();

expect(await el.callMethod('checkValidity')).toBe(true);
});
});
});
});

0 comments on commit 23e799e

Please sign in to comment.