diff --git a/package-lock.json b/package-lock.json index f79edcb..5bd01ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@web/test-runner-commands": "^0.5.13", "@web/test-runner-playwright": "^0.8.8", "construct-style-sheets-polyfill": "^3.0.5", - "element-internals-polyfill": "^1.1.19", + "element-internals-polyfill": "^1.3.11", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-simple-import-sort": "^7.0.0", @@ -4556,11 +4556,10 @@ "peer": true }, "node_modules/element-internals-polyfill": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.1.19.tgz", - "integrity": "sha512-deGDqTkxXtYAQl/VSH5xXWfCe4zEVCkWCYrVeNOPtg3F6W1i0JYRjqPU+MZO9mS1P2UxkkD2vPH+Mb6W/CDicA==", - "dev": true, - "license": "MIT" + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.3.11.tgz", + "integrity": "sha512-SQLQNVY4wMdpnP/F/HtalJbpEenQd46Avtjm5hvUdeTs3QU0zHFNX5/AmtQIPPcfzePb0ipCkQGY4GwYJIhLJA==", + "dev": true }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -10558,12 +10557,12 @@ }, "packages/form-control": { "name": "@open-wc/form-control", - "version": "0.7.0", + "version": "1.0.0", "license": "MIT" }, "packages/form-helpers": { "name": "@open-wc/form-helpers", - "version": "0.2.3", + "version": "1.0.0", "license": "MIT" } } diff --git a/package.json b/package.json index 7d351a9..7d78ec4 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@web/test-runner-commands": "^0.5.13", "@web/test-runner-playwright": "^0.8.8", "construct-style-sheets-polyfill": "^3.0.5", - "element-internals-polyfill": "^1.1.19", + "element-internals-polyfill": "^1.3.11", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-simple-import-sort": "^7.0.0", diff --git a/packages/form-control/demo/complex-demo.ts b/packages/form-control/demo/complex-demo.ts index c6e4193..99e3afe 100644 --- a/packages/form-control/demo/complex-demo.ts +++ b/packages/form-control/demo/complex-demo.ts @@ -23,11 +23,11 @@ input { font-size: 16px; padding: 4px; } -/** Default invalid state */ -:host(:--show-error) input { +/** Default invalid state with fallback https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet#using_a_try...catch_block */ +:host(:is(:--show-error, :state(show-error))) input { border-color: red; } -:host(:--show-error) span { +:host(:is(:--show-error, :state(show-error))) span { color: red; } diff --git a/packages/form-control/demo/switch.style.ts b/packages/form-control/demo/switch.style.ts index 77af4e4..e2afa50 100644 --- a/packages/form-control/demo/switch.style.ts +++ b/packages/form-control/demo/switch.style.ts @@ -26,13 +26,13 @@ sheet.replace(` top: 1px; left: 1px; } -:host(:not(:--checked):hover), :host(:not(:--checked):focus) { +:host(:not(:is(:--checked, :state(checked))):hover), :host(:not(:is(:--checked, :state(checked))):focus) { background: #cccccc; } :host(:not([state--checked]):hover), :host(:not([state--checked]):focus) { background: #cccccc; } -:host(:not(:--checked):active) { +:host(:not(:is(:--checked, :state(checked))):active) { background: #bbbbbb; } :host(:not([state--checked]):active) { @@ -44,31 +44,31 @@ sheet.replace(` :host(:active)::after { background: #eeeeee; } -:host(:--checked) { +:host(:is(:--checked, :state(checked))) { background: ForestGreen; } :host([state--checked]) { background: ForestGreen; } -:host(:--checked:hover) { +:host(:is(:--checked, :state(checked)):hover) { background: Green; } :host([state--checked]:hover) { background: Green; } -:host(:--checked:focus) { +:host(:is(:--checked, :state(checked)):focus) { background: Green; } :host([state--checked]:focus) { background: Green; } -:host(:--checked:active) { +:host(:is(:--checked, :state(checked)):active) { background: DarkGreen; } :host([state--checked]:active) { background: DarkGreen; } -:host(:--checked)::after { +:host(:is(:--checked, :state(checked)))::after { left: calc(100% - 24px); } :host([state--checked])::after { diff --git a/packages/form-control/demo/switch.ts b/packages/form-control/demo/switch.ts index f69aae1..67b8523 100644 --- a/packages/form-control/demo/switch.ts +++ b/packages/form-control/demo/switch.ts @@ -46,11 +46,22 @@ export class DemoSwitch extends FormControlMixin(LitElement) { } protected updated(changed: Map): void { - if (changed.has('value') || changed.has('checked')) { + const checked: any = 'checked'; + if (changed.has('value') || changed.has(checked)) { this.setValue(this.value); } - if (changed.has('checked')) { - this.internals.states[this.checked ? 'add' : 'delete']('--checked'); + if (changed.has(checked)) { + const states = this.internals.states; + if(this.checked) { + try { + states.add(checked); + } catch { + states.add(`--${checked}`); + } + } else { + states.delete(checked); + states.delete(`--${checked}`); + } } } } diff --git a/packages/form-control/src/FormControlMixin.ts b/packages/form-control/src/FormControlMixin.ts index 607d256..0173526 100644 --- a/packages/form-control/src/FormControlMixin.ts +++ b/packages/form-control/src/FormControlMixin.ts @@ -18,7 +18,7 @@ export function FormControlMixin< */ declare static formControlValidators: Validator[]; - /** + /** * If set to true the control described should be evaluated and validated * as part of a group. Like a radio, if any member of the group's validity * changes the the other members should update as well. @@ -35,7 +35,7 @@ export function FormControlMixin< * validator will be evaluated whenever the host's required attribute * is updated. */ - static get observedAttributes(): string[] { + static get observedAttributes(): string[] { const validatorAttributes = this.validators.map((validator) => validator.attribute).flat(); const observedAttributes = super.observedAttributes || []; @@ -286,10 +286,10 @@ export function FormControlMixin< */ declare validityCallback: (validationKey: string) => string | void; - /** - * Called when the control's validationMessage should be changed - * @param message { string } - The new validation message - */ + /** + * Called when the control's validationMessage should be changed + * @param message { string } - The new validation message + */ declare validationMessageCallback: (message: string) => void; /** @@ -318,7 +318,7 @@ export function FormControlMixin< /** * Check to see if an error should be shown. This method will also - * update the internals state object with the --show-error state + * update the internals state object with the show-error state * if necessary. * @private */ @@ -327,16 +327,23 @@ export function FormControlMixin< return false; } - const showError = this.#forceError || (this.#touched && !this.validity.valid && !this.#focused); + const showError = this.#forceError || (this.#touched && !this.validity.valid && !this.#focused), + states = this.internals.states, + // Casted to any, because the polyfill forces dashed-ident syntax https://github.com/calebdwilliams/element-internals-polyfill/issues/126 + // TODO: Remove any cast when the issue is resolved + state: any = 'show-error'; - /** - * At the time of writing Firefox doesn't support states - * TODO: Remove when check for states when fully support is in place - */ - if (showError && this.internals.states) { - this.internals.states.add('--show-error'); - } else if (this.internals.states) { - this.internals.states.delete('--show-error'); + // https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet#compability_with_dashed-ident_syntax + + if (showError) { + try { + states.add(state); + } catch { + states.add(`--${state}`); + } + } else { + states.delete(state); + states.delete(`--${state}`); } return showError; @@ -419,12 +426,12 @@ export function FormControlMixin< /** Once all the async validators have settled, resolve validationComplete */ Promise.allSettled(asyncValidators) .then(() => { - /** Don't resolve validations if the signal is aborted */ - if (!abortController?.signal.aborted) { - this.#isValidationPending = false; - this.#validationCompleteResolver?.(); - } - }); + /** Don't resolve validations if the signal is aborted */ + if (!abortController?.signal.aborted) { + this.#isValidationPending = false; + this.#validationCompleteResolver?.(); + } + }); /** * If async validators are present: @@ -484,7 +491,7 @@ export function FormControlMixin< } } - /** Reset control state when the form is reset */ + /** Reset control state when the form is reset */ formResetCallback() { this.#touched = false; this.#forceError = false;