Skip to content

Commit

Permalink
feat(quantity): add validity state docs
Browse files Browse the repository at this point in the history
  • Loading branch information
aesteves60 authored and dpellier committed Nov 28, 2024
1 parent 800882c commit a4bc6da
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export class OdsInput {
this.isInvalid = true;
}

@Listen('updateIsInvalid')
onUpdateIsInvalid(event: CustomEvent): void {
event.stopPropagation();
this.isInvalid = !this.internals.validity?.valid;
}

@Method()
async checkValidity(): Promise<boolean> {
return this.internals.checkValidity();
Expand All @@ -91,8 +97,12 @@ export class OdsInput {
return this.internals.validity;
}

/**
* Also update error state
*/
@Method()
async reportValidity(): Promise<boolean> {
this.isInvalid = !this.internals.validity.valid;
return this.internals.reportValidity();
}

Expand Down Expand Up @@ -123,7 +133,6 @@ export class OdsInput {
this.onMaskedChange();

this.observer = new MutationObserver((mutations: MutationRecord[]) => {

for (const mutation of mutations) {
if (mutation.attributeName === 'value') {
this.onValueChange(mutation.oldValue);
Expand All @@ -143,7 +152,6 @@ export class OdsInput {
this.onValueChange();

this.observer?.observe(this.el, {
attributes: true,
attributeFilter: ['value'],
attributeOldValue: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const VALUE_DEFAULT_VALUE = null;
})
export class OdsQuantity {
private odsInput?: HTMLElement & OdsInput;
private updateIsInvalid: boolean = false;

@AttachInternals() internals!: ElementInternals;

Expand Down Expand Up @@ -60,7 +61,7 @@ export class OdsQuantity {
@Method()
async clear(): Promise<void> {
await this.odsInput?.clear();
setTimeout(() => this.isInvalid = !this.internals.validity.valid, 0);
this.updateIsInvalid = true;
}

@Method()
Expand All @@ -81,7 +82,7 @@ export class OdsQuantity {
@Method()
async reset(): Promise<void> {
await this.odsInput?.reset();
setTimeout(() => this.isInvalid = !this.internals.validity.valid, 0);
this.updateIsInvalid = true;
}

@Method()
Expand All @@ -103,20 +104,26 @@ export class OdsQuantity {
await this.reset();
}

private decrement(): void {
private async decrement(): Promise<void> {
if (this.isDisabled || this.isReadonly) {
return;
}
const step = this.step || 1;
this.value = this.value !== null ? Number(this.value) - step : 0;
this.updateIsInvalid = true;
}

private increment(): void {
private async increment(): Promise<void> {
if (this.isDisabled || this.isReadonly) {
return;
}
const step = this.step || 1;
this.value = this.value !== null ? Number(this.value) + step : 0;
this.updateIsInvalid = true;
}

private onOdsBlur(): void {
this.isInvalid = !this.internals.validity.valid;
}

private async onOdsChange(event: OdsInputChangeEvent): Promise<void> {
Expand All @@ -127,6 +134,11 @@ export class OdsQuantity {
}

await updateInternals(this.internals, this.value, this.odsInput);
// update here after update internals
if (this.updateIsInvalid) {
await this.odsInput?.reportValidity();
this.isInvalid = !this.internals.validity.valid;
}
}

private getHasError(): boolean {
Expand Down Expand Up @@ -161,6 +173,7 @@ export class OdsQuantity {
isReadonly={ this.isReadonly }
isRequired={ this.isRequired }
onKeyUp={ (event: KeyboardEvent): void => submitFormOnEnter(event, this.internals.form) }
onOdsBlur={ () => this.onOdsBlur() }
onOdsChange={ (event: OdsInputChangeEvent) => this.onOdsChange(event) }
max={ this.max }
min={ this.min }
Expand Down
3 changes: 1 addition & 2 deletions packages/ods/src/components/quantity/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@
<form id="my-form">
<ods-quantity id="form-quantity"
is-required
default-value="0"
name="form-quantity">
</ods-quantity>

Expand Down Expand Up @@ -127,7 +126,7 @@

['odsBlur', 'odsClear', 'odsFocus', 'odsReset', 'odsChange'].forEach((eventName) =>{
formQuantity.addEventListener(eventName, (event) => {
console.log(eventName, event);
// console.log(eventName, event);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ describe('ods-quantity rendering', () => {
await el.callMethod('clear');
await page.click('body', { offset: { x: 400, y: 400 } }); // Blur
await page.waitForChanges();
await new Promise(resolve => setTimeout(resolve, 20000));

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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { type OdsQuantity } from '../../src';
describe('ods-quantity validity', () => {
let el: E2EElement;
let page: E2EPage;
let buttonMinus: E2EElement;
let buttonAdd: E2EElement;

async function setup(content: string): Promise<void> {
page = await newE2EPage();
Expand All @@ -12,6 +14,8 @@ describe('ods-quantity validity', () => {
await page.evaluate(() => document.body.style.setProperty('margin', '0px'));

el = await page.find('ods-quantity');
buttonMinus = await page.find('ods-quantity >>> [exportparts="button:button-minus"]');
buttonAdd = await page.find('ods-quantity >>> [exportparts="button:button-plus"]');
}

beforeEach(jest.clearAllMocks);
Expand Down Expand Up @@ -121,6 +125,28 @@ describe('ods-quantity validity', () => {

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

it('should update the validity state accordingly, with increment', async() => {
await setup('<ods-quantity is-required></ods-quantity>');

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

await buttonAdd.click();
await page.waitForChanges();

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

it('should update the validity state accordingly, with decrement', async() => {
await setup('<ods-quantity is-required></ods-quantity>');

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

await buttonMinus.click();
await page.waitForChanges();

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

describe('getValidationMessage', () => {
Expand Down Expand Up @@ -219,7 +245,7 @@ describe('ods-quantity validity', () => {

await el.callMethod('clear');
await page.waitForChanges();
await new Promise(resolve => setTimeout(resolve, 200));
await new Promise((resolve) => setTimeout(resolve, 200));

expect(await el.callMethod('checkValidity')).toBe(false);
await el.callMethod('reset');
Expand All @@ -235,7 +261,7 @@ describe('ods-quantity validity', () => {

await el.callMethod('clear');
await page.waitForChanges();
await new Promise(resolve => setTimeout(resolve, 200));
await new Promise((resolve) => setTimeout(resolve, 200));

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

Expand Down
95 changes: 82 additions & 13 deletions packages/storybook/stories/components/quantity/quantity.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,55 @@ const meta: Meta = {
export default meta;

export const Demo: StoryObj = {
render: (arg) => html`
render: (arg) => {
const validityStateTemplate = html`<br>
<div id="validity-state" style="display: grid; row-gap: 5px;"></div>
<script>
(async () => {
const divValidityState = document.querySelector('#validity-state');
const quantity = document.querySelector('.my-quantity');
await renderValidityState();
quantity.addEventListener('odsChange', async () => {
await renderValidityState();
})
async function renderValidityState() {
console.log('validity', await quantity.getValidity())
const validity = await quantity.getValidity()
divValidityState.innerHTML = '';
for (let key in validity) {
divValidityState.innerHTML += "<div>" + key + ": " + validity[key] + "</div>";
}
}
})();
</script>`;
return html`
<ods-quantity class="my-quantity"
ariaLabel="${arg.ariaLabel}"
ariaLabelledby="${arg.ariaLabelledby}"
has-error="${arg.hasError}"
is-clearable="${arg.isClearable}"
is-disabled="${arg.isDisabled}"
is-readonly="${arg.isReadonly}"
is-required="${arg.isRequired}"
max="${arg.max}"
min="${arg.min}"
placeholder="${arg.placeholder}"
step="${arg.step}">
</ods-quantity>
<style>
.my-quantity::part(input) {
${arg.customCssInput}
}
.my-quantity::part(button-minus) {
${arg.customCssButtonMinus}
}
.my-quantity::part(button-plus) {
${arg.customCssButtonAdd}
}
</style>
`,
${ arg.validityState ? validityStateTemplate : '' }
<style>
.my-quantity::part(input) {
${arg.customCssInput}
}
.my-quantity::part(button-minus) {
${arg.customCssButtonMinus}
}
.my-quantity::part(button-plus) {
${arg.customCssButtonAdd}
}
</style>
`;
},
argTypes: orderControls({
ariaLabel: {
control: 'text',
Expand Down Expand Up @@ -104,6 +128,14 @@ export const Demo: StoryObj = {
type: { summary: 'boolean' },
},
},
isRequired: {
control: 'boolean',
table: {
category: CONTROL_CATEGORY.general,
defaultValue: { summary: 'false' },
type: { summary: 'boolean' },
},
},
max: {
control: 'number',
table: {
Expand All @@ -128,11 +160,21 @@ export const Demo: StoryObj = {
type: { summary: 'number' },
},
},
validityState: {
table: {
category: CONTROL_CATEGORY.accessibility,
defaultValue: { summary: false },
type: { summary: 'boolean' },
},
control: 'boolean',
},
}),
args: {
hasError: false,
isDisabled: false,
isReadonly: false,
isRequired: false,
validityState: false,
},
};

Expand Down Expand Up @@ -220,3 +262,30 @@ export const CustomCSS: StoryObj = {
</style>
`,
};

export const ValidityState: StoryObj = {
tags: ['isHidden'],
render: () => html`
<ods-quantity is-required id="quantity-validity-state-demo">
</ods-quantity>
<div id="validity-state-demo"></div>
<script>
(() => {
const divValidityState = document.querySelector('#validity-state-demo');
const quantity = document.querySelector('#quantity-validity-state-demo');
renderValidityState();
quantity.addEventListener('odsChange', async () => {
await renderValidityState();
})
async function renderValidityState() {
console.log('validity', await quantity.getValidity())
const validity = await quantity.getValidity()
divValidityState.innerHTML = '';
for (let key in validity) {
divValidityState.innerHTML += "<div>" + key + ": " + validity[key] + "</div>";
}
}
})();
</script>
`,
};
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ Custom CSS:

<Canvas of={ QuantityStories.Step } sourceState="shown" />

<Heading label="Validity State" level={ 3 } />

<Canvas of={ QuantityStories.ValidityState } sourceState="shown" />

<Heading label="Included in a Form Field" level={ 3 } />

<Canvas of={ FormFieldQuantity } sourceState="shown" />
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,13 @@ Custom CSS:

<Heading label="Validity State" level={ 3 } />

<<<<<<< HEAD
You need to blur the field for validate.

<Canvas of={ TextareaStories.ValidityState } sourceState="shown" />
=======
<Canvas of={ TextareaStories.ValidityState } sourceState='shown' />
>>>>>>> 5c1cd5d85 (feat(quantity): add validity state docs)
<Heading label="Included in a Form Field" level={ 3 } />

Expand Down
Loading

0 comments on commit a4bc6da

Please sign in to comment.