From e17f7bdfa63f4e82a188167f50706ac4fc14d55d Mon Sep 17 00:00:00 2001 From: Konstantinos Norgias Date: Thu, 20 May 2021 12:21:12 +0200 Subject: [PATCH] feat: upgrade to lit2 Co-authored-by: Thijs Louisse --- docs/components/inputs/combobox/overview.md | 5 +- .../inputs/combobox/src/lazyRender.js | 20 +- .../inputs/combobox/src/levenshtein.js | 2 +- docs/docs/systems/overlays/features.md | 33 +- .../babel-plugin-extend-docs/index.js | 2 +- .../src/program/analyzers/find-classes.js | 2 +- .../find-classes.json | 2 +- .../match-subclasses/ExtendedComp.js | 2 +- .../program/analyzers/find-classes.test.js | 2 +- packages-node/publish-docs/index.d.ts | 8 + .../accordion/test/lion-accordion.test.js | 149 +-- packages/button/test/lion-button.test.js | 159 +-- packages/calendar/index.d.ts | 2 + packages/calendar/src/LionCalendar.js | 22 +- packages/calendar/src/utils/dayTemplate.js | 12 +- .../calendar/test-helpers/CalendarObject.js | 66 +- packages/checkbox-group/index.d.ts | 3 + .../test/lion-checkbox-group.test.js | 3 +- .../test/lion-checkbox-indeterminate.test.js | 358 ++++--- .../checkbox-group/test/lion-checkbox.test.js | 17 +- packages/collapsible/index.d.ts | 1 + .../collapsible/test/lion-collapsible.test.js | 13 +- packages/combobox/index.d.ts | 1 + packages/combobox/lion-combobox.js | 2 +- packages/combobox/src/LionCombobox.js | 26 +- packages/combobox/test/lion-combobox.test.js | 993 ++++++++++-------- packages/core/index.d.ts | 124 ++- packages/core/index.js | 98 +- packages/core/package.json | 5 +- packages/core/src/DisabledMixin.js | 4 +- .../core/src/DisabledWithTabIndexMixin.js | 4 +- packages/core/test/DelegateMixin.test.js | 21 +- packages/core/test/DisabledMixin.test.js | 45 +- .../test/DisabledWithTabIndexMixin.test.js | 54 +- packages/core/test/UpdateStylesMixin.test.js | 3 +- packages/dialog/test/lion-dialog.test.js | 9 +- packages/form-core/src/FormControlMixin.js | 2 - packages/form-core/src/FormatMixin.js | 9 +- .../form-core/src/InteractionStateMixin.js | 6 +- .../src/choice-group/ChoiceInputMixin.js | 8 +- .../src/form-group/FormGroupMixin.js | 11 +- .../form-core/src/utils/SyncUpdatableMixin.js | 24 +- .../test-suites/FormatMixin.suite.js | 139 ++- .../test-suites/ValidateMixin.suite.js | 264 +++-- .../choice-group/ChoiceGroupMixin.suite.js | 220 ++-- .../choice-group/ChoiceInputMixin.suite.js | 92 +- .../form-group/FormGroupMixin-input.suite.js | 81 +- .../form-group/FormGroupMixin.suite.js | 445 ++++---- packages/form-core/test/FocusMixin.test.js | 54 +- .../form-core/test/FormControlMixin.test.js | 105 +- .../test/FormRegistrationMixins.test.js | 2 +- packages/form-core/test/lion-field.test.js | 101 +- .../test/utils/SyncUpdatableMixin.test.js | 43 +- .../getAriaElementsInRightDomOrder.test.js | 3 +- .../form-core/test/validate/Required.test.js | 15 +- .../form-core/test/validate/Validator.test.js | 9 +- .../validate/lion-validation-feedback.test.js | 27 +- .../form-core/types/FormatMixinTypes.d.ts | 2 +- .../choice-group/ChoiceInputMixinTypes.d.ts | 2 +- .../types/utils/SyncUpdatableMixinTypes.d.ts | 4 +- .../test/dialog-integrations.test.js | 3 +- .../test/form-group-methods.test.js | 19 +- .../test/form-integrations.test.js | 49 +- .../test/form-validation-integrations.test.js | 9 +- .../test/model-value-consistency.test.js | 48 +- .../test/model-value-event.test.js | 3 +- packages/form/test/lion-form.test.js | 17 +- .../test/sb-action-logger.test.js | 27 +- packages/icon/src/IconManager.js | 2 +- packages/icon/src/LionIcon.js | 19 +- packages/icon/test/lion-icon.test.js | 8 +- .../src/LionInputDatepicker.js | 23 +- .../test/lion-input-datepicker.test.js | 30 +- packages/input-iban/src/validators.js | 2 +- .../input-stepper/src/LionInputStepper.js | 44 +- .../test/lion-input-stepper.test.js | 3 +- packages/input/src/LionInput.js | 4 +- packages/input/test/lion-input.test.js | 21 +- packages/listbox/src/LionOption.js | 9 +- packages/listbox/src/ListboxMixin.js | 14 +- .../listbox/test-suites/ListboxMixin.suite.js | 80 +- packages/listbox/test/lion-option.test.js | 115 +- packages/listbox/test/lion-options.test.js | 11 +- packages/localize/src/LocalizeMixin.js | 3 +- packages/localize/test/LocalizeMixin.test.js | 18 +- packages/overlays/src/OverlayMixin.js | 4 +- packages/overlays/src/OverlaysManager.js | 9 +- .../test-suites/OverlayMixin.suite.js | 139 ++- .../overlays/test/OverlayController.test.js | 215 ++-- packages/overlays/test/OverlayMixin.test.js | 3 +- .../overlays/test/OverlaysManager.test.js | 3 +- .../overlays/test/global-positioning.test.js | 3 +- .../overlays/test/local-positioning.test.js | 167 +-- .../test/utils-tests/contain-focus.test.js | 3 +- packages/pagination/src/LionPagination.js | 7 +- .../pagination/test/lion-pagination.test.js | 9 +- .../src/LionProgressIndicator.js | 2 +- .../radio-group/test/lion-radio-group.test.js | 3 +- packages/radio-group/test/lion-radio.test.js | 15 +- packages/select-rich/src/LionSelectInvoker.js | 7 +- packages/select-rich/src/LionSelectRich.js | 20 +- .../test/lion-select-invoker.test.js | 57 +- ...ion-select-rich-dialog-integration.test.js | 21 +- .../test/lion-select-rich-interaction.test.js | 159 +-- .../select-rich/test/lion-select-rich.test.js | 33 +- packages/select/test/lion-select.test.js | 3 +- packages/steps/test/lion-step.test.js | 3 +- packages/steps/test/lion-steps.test.js | 3 +- packages/switch/src/LionSwitchButton.js | 4 +- .../switch/test/lion-switch-button.test.js | 3 +- packages/switch/test/lion-switch.test.js | 3 +- packages/tabs/test/lion-tabs.test.js | 355 ++++--- packages/textarea/src/LionTextarea.js | 9 +- packages/textarea/test/lion-textarea.test.js | 3 +- packages/tooltip/test/lion-tooltip.test.js | 271 ++--- .../src/loadDefaultFeedbackMessages.js | 1 + 116 files changed, 3499 insertions(+), 2559 deletions(-) create mode 100644 packages-node/publish-docs/index.d.ts create mode 100644 packages/calendar/index.d.ts create mode 100644 packages/checkbox-group/index.d.ts create mode 100644 packages/collapsible/index.d.ts create mode 100644 packages/combobox/index.d.ts diff --git a/docs/components/inputs/combobox/overview.md b/docs/components/inputs/combobox/overview.md index 47766ec3e..c2b20ee5b 100644 --- a/docs/components/inputs/combobox/overview.md +++ b/docs/components/inputs/combobox/overview.md @@ -24,7 +24,10 @@ import { lazyRender } from './src/lazyRender.js'; export const main = () => html` ${lazyRender( - listboxData.map(entry => html` ${entry} `), + listboxData.map( + (entry, i) => + html` ${entry} `, + ), )} `; diff --git a/docs/components/inputs/combobox/src/lazyRender.js b/docs/components/inputs/combobox/src/lazyRender.js index 12e6631e4..8404ab4f0 100644 --- a/docs/components/inputs/combobox/src/lazyRender.js +++ b/docs/components/inputs/combobox/src/lazyRender.js @@ -1,4 +1,5 @@ -import { directive } from '@lion/core'; +import { directive } from 'lit/directive.js'; +import { AsyncDirective } from 'lit/async-directive.js'; /** * In order to speed up the first meaningful paint, use this directive @@ -15,9 +16,14 @@ import { directive } from '@lion/core'; * )} * */ -export const lazyRender = directive(tplResult => part => { - setTimeout(() => { - part.setValue(tplResult); - part.commit(); - }); -}); +export const lazyRender = directive( + class extends AsyncDirective { + render(tplResult) { + setTimeout(() => { + this.setValue(tplResult); + }); + } + }, +); + +// export const lazyRender = () => {}; diff --git a/docs/components/inputs/combobox/src/levenshtein.js b/docs/components/inputs/combobox/src/levenshtein.js index 8c4226b9f..faae33d28 100644 --- a/docs/components/inputs/combobox/src/levenshtein.js +++ b/docs/components/inputs/combobox/src/levenshtein.js @@ -1,4 +1,4 @@ -/* eslint-disable*/ +/* eslint-disable */ // https://github.com/gustf/js-levenshtein/blob/master/index.js function _min(d0, d1, d2, bx, ay) { diff --git a/docs/docs/systems/overlays/features.md b/docs/docs/systems/overlays/features.md index b6e8b9b3e..bcf640bc1 100644 --- a/docs/docs/systems/overlays/features.md +++ b/docs/docs/systems/overlays/features.md @@ -14,7 +14,7 @@ import { import './assets/demo-overlay-system.js'; import './assets/demo-overlay-backdrop.js'; import './assets/applyDemoOverlayStyles.js'; -import { ref as r } from './assets/ref.js'; +import { ref, createRef } from 'lit/directives/ref.js'; ``` The overlay system allows to create different types of overlays like dialogs, toasts, tooltips, dropdown, etc. @@ -388,14 +388,21 @@ export const openedState = () => { const appState = { opened: false, }; - const refs = {}; + const myRefs = { + overlay: createRef(), + openedState: createRef(), + }; function onOpenClosed(ev) { appState.opened = ev.target.opened; - refs.openedState.innerText = appState.opened; + myRefs.openedState.value.innerText = appState.opened; } return html` - appState.opened: ${appState.opened} - + appState.opened: ${appState.opened} +
Hello! You can close this notification here: @@ -419,7 +426,10 @@ the `before-close` or `before-open` events. export const interceptingOpenClose = () => { // Application code let blockOverlay = true; - const refs = {}; + const myRefs = { + statusButton: createRef(), + overlay: createRef(), + }; function intercept(ev) { if (blockOverlay) { ev.preventDefault(); @@ -428,28 +438,29 @@ export const interceptingOpenClose = () => { return html` Overlay blocked state:
Hello! You can close this notification here: - +
`; diff --git a/packages-node/babel-plugin-extend-docs/index.js b/packages-node/babel-plugin-extend-docs/index.js index a49bb4cb5..ec27918ea 100644 --- a/packages-node/babel-plugin-extend-docs/index.js +++ b/packages-node/babel-plugin-extend-docs/index.js @@ -1,3 +1,3 @@ -const babelPlugin = require('./src/babelPluginExtendDocs'); +const babelPlugin = require('./src/babelPluginExtendDocs.js'); module.exports = babelPlugin; diff --git a/packages-node/providence-analytics/src/program/analyzers/find-classes.js b/packages-node/providence-analytics/src/program/analyzers/find-classes.js index b331bbec1..117878531 100644 --- a/packages-node/providence-analytics/src/program/analyzers/find-classes.js +++ b/packages-node/providence-analytics/src/program/analyzers/find-classes.js @@ -56,7 +56,7 @@ async function findMembersPerAstEntry(ast, fullCurrentFilePath, projectPath) { // // Handle methods // const mBlacklistPlatform = ['constructor', 'connectedCallback', 'disconnectedCallback']; // const mBlacklistLitEl = [ - // 'requestUpdateInternal', + // 'requestUpdate', // 'createRenderRoot', // 'render', // 'updated', diff --git a/packages-node/providence-analytics/test-helpers/project-mocks-analyzer-outputs/find-classes.json b/packages-node/providence-analytics/test-helpers/project-mocks-analyzer-outputs/find-classes.json index 859f2b420..413e223a5 100644 --- a/packages-node/providence-analytics/test-helpers/project-mocks-analyzer-outputs/find-classes.json +++ b/packages-node/providence-analytics/test-helpers/project-mocks-analyzer-outputs/find-classes.json @@ -179,7 +179,7 @@ "accessType": "public" }, { - "name": "requestUpdateInternal", + "name": "requestUpdate", "accessType": "protected" }, { diff --git a/packages-node/providence-analytics/test-helpers/project-mocks/importing-target-project/target-src/match-subclasses/ExtendedComp.js b/packages-node/providence-analytics/test-helpers/project-mocks/importing-target-project/target-src/match-subclasses/ExtendedComp.js index 375eff707..0eede338a 100644 --- a/packages-node/providence-analytics/test-helpers/project-mocks/importing-target-project/target-src/match-subclasses/ExtendedComp.js +++ b/packages-node/providence-analytics/test-helpers/project-mocks/importing-target-project/target-src/match-subclasses/ExtendedComp.js @@ -30,7 +30,7 @@ export class ExtendedComp extends MyCompMixin(RefClass) { static get properties() {} static get styles() {} get updateComplete() {} - requestUpdateInternal() {} + requestUpdate() {} createRenderRoot() {} render() {} updated() {} diff --git a/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js b/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js index 2b5ef919b..0b6457891 100644 --- a/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js +++ b/packages-node/providence-analytics/test-node/program/analyzers/find-classes.test.js @@ -213,7 +213,7 @@ describe('Analyzer "find-classes"', () => { static get properties() {} static get styles() {} get updateComplete() {} - requestUpdateInternal() {} + requestUpdate() {} createRenderRoot() {} render() {} updated() {} diff --git a/packages-node/publish-docs/index.d.ts b/packages-node/publish-docs/index.d.ts new file mode 100644 index 000000000..ea995aaab --- /dev/null +++ b/packages-node/publish-docs/index.d.ts @@ -0,0 +1,8 @@ +export { PublishDocs } from "./src/PublishDocs.js"; +export type PublishDocsOptions = { + projectDir: string; + gitHubUrl: string; + gitRootDir: string; + copyDir: string; + copyTarget: string; +}; diff --git a/packages/accordion/test/lion-accordion.test.js b/packages/accordion/test/lion-accordion.test.js index c77a0351a..9ab22cf01 100644 --- a/packages/accordion/test/lion-accordion.test.js +++ b/packages/accordion/test/lion-accordion.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import '../lion-accordion.js'; @@ -25,14 +26,16 @@ describe('', () => { }); it('can programmatically set expanded', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content 1
-

-
content 2
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content 1
+

+
content 2
+
+ `) + ); expect(el.expanded).to.deep.equal([1]); expect( Array.from(el.children).find( @@ -103,14 +106,16 @@ describe('', () => { }); it('can programmatically set focusedIndex', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content 1
-

-
content 2
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content 1
+

+
content 2
+
+ `) + ); expect(el.focusedIndex).to.equal(1); expect( Array.from(el.children).find( @@ -214,16 +219,18 @@ describe('', () => { }); it('selects previous invoker on [arrow-left] and [arrow-up]', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content 1
-

-
content 2
-

-
content 3
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content 1
+

+
content 2
+

+
content 3
+
+ `) + ); const invokers = el.querySelectorAll('[slot=invoker]'); el.focusedIndex = 2; invokers[2].firstElementChild?.dispatchEvent( @@ -237,14 +244,16 @@ describe('', () => { }); it('selects first invoker on [home]', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content 1
-

-
content 2
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content 1
+

+
content 2
+
+ `) + ); const invokers = el.querySelectorAll('[slot=invoker]'); invokers[1].firstElementChild?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); expect(el.focusedIndex).to.equal(0); @@ -258,16 +267,18 @@ describe('', () => { }); it('stays on last invoker on [arrow-right]', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content 1
-

-
content 2
-

-
content 3
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content 1
+

+
content 2
+

+
content 3
+
+ `) + ); const invokers = el.querySelectorAll('[slot=invoker]'); invokers[2].firstElementChild?.dispatchEvent( new KeyboardEvent('keydown', { key: 'ArrowRight' }), @@ -276,16 +287,18 @@ describe('', () => { }); it('stays on first invoker on [arrow-left]', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content 1
-

-
content 2
-

-
content 3
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content 1
+

+
content 2
+

+
content 3
+
+ `) + ); const invokers = el.querySelectorAll('[slot=invoker]'); invokers[0].firstElementChild?.dispatchEvent( new KeyboardEvent('keydown', { key: 'ArrowLeft' }), @@ -338,12 +351,12 @@ describe('', () => { el.append(content); } await el.updateComplete; - const invokers = /** @type {HTMLElement[]} */ (Array.from( - el.querySelectorAll('[slot=invoker]'), - )); - const contents = /** @type {HTMLElement[]} */ (Array.from( - el.querySelectorAll('[slot=content]'), - )); + const invokers = /** @type {HTMLElement[]} */ ( + Array.from(el.querySelectorAll('[slot=invoker]')) + ); + const contents = /** @type {HTMLElement[]} */ ( + Array.from(el.querySelectorAll('[slot=content]')) + ); invokers.forEach((invoker, index) => { const content = contents[index]; expect(invoker.style.getPropertyValue('order')).to.equal(`${index + 1}`); @@ -403,12 +416,14 @@ describe('', () => { }); it('adds aria-expanded="true" to invoker when its content is expanded', async () => { - const el = /** @type {LionAccordion} */ (await fixture(html` - -

-
content
-
- `)); + const el = /** @type {LionAccordion} */ ( + await fixture(html` + +

+
content
+
+ `) + ); el.expanded = [0]; expect( Array.from(el.children).find(child => child.slot === 'invoker')?.firstElementChild, diff --git a/packages/button/test/lion-button.test.js b/packages/button/test/lion-button.test.js index 1ae7ab13d..aca9273d0 100644 --- a/packages/button/test/lion-button.test.js +++ b/packages/button/test/lion-button.test.js @@ -1,6 +1,7 @@ /* eslint-disable lit-a11y/click-events-have-key-events */ import { browserDetection } from '@lion/core'; -import { aTimeout, expect, fixture, html, oneEvent, unsafeStatic } from '@open-wc/testing'; +import { aTimeout, expect, fixture, oneEvent } from '@open-wc/testing'; +import { unsafeStatic, html } from 'lit/static-html.js'; import sinon from 'sinon'; import '@lion/core/differentKeyEventNamesShimIE'; import '@lion/button/define'; @@ -37,9 +38,9 @@ describe('lion-button', () => { }); it('sync type down to the native button', async () => { - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); const { nativeButtonNode } = getProtectedMembers(el); expect(el.type).to.equal('button'); @@ -175,9 +176,9 @@ describe('lion-button', () => { }); it('does not override user provided role', async () => { - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); expect(el.getAttribute('role')).to.equal('foo'); }); @@ -187,9 +188,9 @@ describe('lion-button', () => { }); it('has a tabindex="-1" when disabled', async () => { - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); expect(el.getAttribute('tabindex')).to.equal('-1'); el.disabled = false; await el.updateComplete; @@ -200,16 +201,16 @@ describe('lion-button', () => { }); it('does not override user provided tabindex', async () => { - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); expect(el.getAttribute('tabindex')).to.equal('5'); }); it('disabled does not override user provided tabindex', async () => { - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); expect(el.getAttribute('tabindex')).to.equal('-1'); el.disabled = false; await el.updateComplete; @@ -230,9 +231,9 @@ describe('lion-button', () => { it('does not override aria-labelledby when provided by user', async () => { const browserDetectionStub = sinon.stub(browserDetection, 'isIE11').value(true); - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); expect(el.getAttribute('aria-labelledby')).to.equal('some-id another-id'); browserDetectionStub.restore(); }); @@ -244,15 +245,17 @@ describe('lion-button', () => { expect(nativeButtonNode.getAttribute('aria-hidden')).to.equal('true'); }); - it('is accessible', async () => { + // TODO: enable when native button is not a child anymore + it.skip('is accessible', async () => { const el = /** @type {LionButton} */ (await fixture(`foo`)); await expect(el).to.be.accessible(); }); - it('is accessible when disabled', async () => { - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + // TODO: enable when native button is not a child anymore + it.skip('is accessible when disabled', async () => { + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); await expect(el).to.be.accessible({ ignoredRules: ['color-contrast'] }); }); }); @@ -266,9 +269,9 @@ describe('lion-button', () => { foo `); - const button /** @type {LionButton} */ = /** @type {LionButton} */ (form.querySelector( - 'lion-button', - )); + const button /** @type {LionButton} */ = /** @type {LionButton} */ ( + form.querySelector('lion-button') + ); button.click(); expect(formSubmitSpy).to.have.been.calledOnce; }); @@ -280,9 +283,9 @@ describe('lion-button', () => { foo `); - const button /** @type {LionButton} */ = /** @type {LionButton} */ (form.querySelector( - 'lion-button', - )); + const button /** @type {LionButton} */ = /** @type {LionButton} */ ( + form.querySelector('lion-button') + ); button.dispatchEvent(new KeyboardEvent('keyup', { key: ' ' })); await aTimeout(0); await aTimeout(0); @@ -313,15 +316,15 @@ describe('lion-button', () => { reset `); - const btn /** @type {LionButton} */ = /** @type {LionButton} */ (form.querySelector( - 'lion-button', - )); - const firstName = /** @type {HTMLInputElement} */ (form.querySelector( - 'input[name=firstName]', - )); - const lastName = /** @type {HTMLInputElement} */ (form.querySelector( - 'input[name=lastName]', - )); + const btn /** @type {LionButton} */ = /** @type {LionButton} */ ( + form.querySelector('lion-button') + ); + const firstName = /** @type {HTMLInputElement} */ ( + form.querySelector('input[name=firstName]') + ); + const lastName = /** @type {HTMLInputElement} */ ( + form.querySelector('input[name=lastName]') + ); firstName.value = 'Foo'; lastName.value = 'Bar'; @@ -435,9 +438,9 @@ describe('lion-button', () => { it('is fired once', async () => { const clickSpy = /** @type {EventListener} */ (sinon.spy()); - const el = /** @type {LionButton} */ (await fixture( - html` foo `, - )); + const el = /** @type {LionButton} */ ( + await fixture(html` foo `) + ); el.click(); @@ -454,17 +457,19 @@ describe('lion-button', () => { const formSpyEarly = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault())); const formSpyLater = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault())); - const el = /** @type {HTMLDivElement} */ (await fixture( - html` -
-
-
- foo -
-
-
- `, - )); + const el = /** @type {HTMLDivElement} */ ( + await fixture( + html` +
+
+
+ foo +
+
+
+ `, + ) + ); const lionButton = /** @type {LionButton} */ (el.querySelector('lion-button')); const form = /** @type {HTMLFormElement} */ (el.querySelector('form')); form.addEventListener('click', formSpyLater); @@ -482,13 +487,15 @@ describe('lion-button', () => { }); it('works when connected to different form', async () => { - const form1El = /** @type {HTMLFormElement} */ (await fixture( - html` -
- foo -
- `, - )); + const form1El = /** @type {HTMLFormElement} */ ( + await fixture( + html` +
+ foo +
+ `, + ) + ); const lionButton = /** @type {LionButton} */ (form1El.querySelector('lion-button')); expect(lionButton._form).to.equal(form1El); @@ -500,15 +507,17 @@ describe('lion-button', () => { const formSpyEarly = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault())); const formSpyLater = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault())); - const form2El = /** @type {HTMLFormElement} */ (await fixture( - html` -
-
-
${lionButton}
-
-
- `, - )); + const form2El = /** @type {HTMLFormElement} */ ( + await fixture( + html` +
+
+
${lionButton}
+
+
+ `, + ) + ); const form2Node = /** @type {HTMLFormElement} */ (form2El.querySelector('form')); expect(lionButton._form).to.equal(form2Node); @@ -534,9 +543,9 @@ describe('lion-button', () => { before(async () => { const nativeButtonEl = /** @type {LionButton} */ (await fixture('')); - const lionButtonEl = /** @type {LionButton} */ (await fixture( - 'foo', - )); + const lionButtonEl = /** @type {LionButton} */ ( + await fixture('foo') + ); nativeButtonEvent = await prepareClickEvent(nativeButtonEl); lionButtonEvent = await prepareClickEvent(lionButtonEl); }); @@ -578,9 +587,9 @@ describe('lion-button', () => { const targetName = 'host'; it(`is ${targetName} with type ${type} and it is inside a ${container}`, async () => { const clickSpy = /** @type {EventListener} */ (sinon.spy(e => e.preventDefault())); - const el = /** @type {LionButton} */ (await fixture( - `foo`, - )); + const el = /** @type {LionButton} */ ( + await fixture(`foo`) + ); const tag = unsafeStatic(container); await fixture(html`<${tag} @click="${clickSpy}">${el}`); const event = await prepareClickEvent(el); diff --git a/packages/calendar/index.d.ts b/packages/calendar/index.d.ts new file mode 100644 index 000000000..8c98dff3f --- /dev/null +++ b/packages/calendar/index.d.ts @@ -0,0 +1,2 @@ +export { isSameDate } from "./src/utils/isSameDate.js"; +export { LionCalendar } from "./src/LionCalendar.js"; diff --git a/packages/calendar/src/LionCalendar.js b/packages/calendar/src/LionCalendar.js index 5d724d43d..4086049cd 100644 --- a/packages/calendar/src/LionCalendar.js +++ b/packages/calendar/src/LionCalendar.js @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { html, LitElement } from '@lion/core'; import { getMonthNames, @@ -224,9 +225,9 @@ export class LionCalendar extends LocalizeMixin(LitElement) { } focusCentralDate() { - const button = /** @type {HTMLElement} */ (this.shadowRoot?.querySelector( - 'button[tabindex="0"]', - )); + const button = /** @type {HTMLElement} */ ( + this.shadowRoot?.querySelector('button[tabindex="0"]') + ); button.focus(); this.__focusedDate = this.centralDate; } @@ -267,9 +268,9 @@ export class LionCalendar extends LocalizeMixin(LitElement) { * we can guard against adding events twice */ if (!this.__eventsAdded) { - this.__contentWrapperElement = /** @type {HTMLButtonElement} */ (this.shadowRoot?.getElementById( - 'js-content-wrapper', - )); + this.__contentWrapperElement = /** @type {HTMLButtonElement} */ ( + this.shadowRoot?.getElementById('js-content-wrapper') + ); this.__contentWrapperElement.addEventListener('click', this.__boundClickDateDelegation); this.__contentWrapperElement.addEventListener('focus', this.__boundFocusDateDelegation); this.__contentWrapperElement.addEventListener('blur', this.__boundBlurDateDelegation); @@ -305,8 +306,8 @@ export class LionCalendar extends LocalizeMixin(LitElement) { * @param {string} name * @param {?} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); const map = { disableDates: () => this.__disableDatesChanged(), @@ -740,8 +741,9 @@ export class LionCalendar extends LocalizeMixin(LitElement) { !this.__focusedDate && isDayButton(/** @type {HTMLElement} el */ (this.shadowRoot?.activeElement)) ) { - this.__focusedDate = /** @type {HTMLButtonElement & { date: Date }} */ (this.shadowRoot - ?.activeElement).date; + this.__focusedDate = /** @type {HTMLButtonElement & { date: Date }} */ ( + this.shadowRoot?.activeElement + ).date; } } diff --git a/packages/calendar/src/utils/dayTemplate.js b/packages/calendar/src/utils/dayTemplate.js index d961c0f5b..aeb2578fc 100644 --- a/packages/calendar/src/utils/dayTemplate.js +++ b/packages/calendar/src/utils/dayTemplate.js @@ -59,10 +59,16 @@ export function dayTemplate(day, { weekdays, monthsLabels = defaultMonthLabels } `); const cb = sinon.spy(); element.addEventListener('click', cb); - const childEl = /** @type {HTMLElement} */ (Array.from(element.children)?.find( - child => child.slot === 'button', - )); + const childEl = /** @type {HTMLElement} */ ( + Array.from(element.children)?.find(child => child.slot === 'button') + ); childEl?.click(); expect(cb.callCount).to.equal(1); }); @@ -343,14 +344,14 @@ describe('DelegateMixin', () => { const tagName = unsafeStatic(tag); // Here, the Application Developerd tries to set the type via attribute - const elementAttr = /** @type {ScheduledElement} */ (await fixture( - `<${tag} type="radio">`, - )); + const elementAttr = /** @type {ScheduledElement} */ ( + await fixture(`<${tag} type="radio">`) + ); expect(elementAttr.scheduledElement?.type).to.equal('radio'); // Here, the Application Developer tries to set the type via property - const elementProp = /** @type {ScheduledElement} */ (await fixture( - html`<${tagName} .type=${'radio'}>`, - )); + const elementProp = /** @type {ScheduledElement} */ ( + await fixture(html`<${tagName} .type=${'radio'}>`) + ); expect(elementProp.scheduledElement?.type).to.equal('radio'); }); diff --git a/packages/core/test/DisabledMixin.test.js b/packages/core/test/DisabledMixin.test.js index 52f1bc9cc..bed72df8c 100644 --- a/packages/core/test/DisabledMixin.test.js +++ b/packages/core/test/DisabledMixin.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { LitElement } from '../index.js'; import { DisabledMixin } from '../src/DisabledMixin.js'; @@ -9,9 +10,9 @@ describe('DisabledMixin', () => { }); it('reflects disabled to attribute', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); expect(el.hasAttribute('disabled')).to.be.false; el.makeRequestToBeDisabled(); el.disabled = true; @@ -20,9 +21,9 @@ describe('DisabledMixin', () => { }); it('can be requested to be disabled', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); el.makeRequestToBeDisabled(); expect(el.disabled).to.be.true; await el.updateComplete; @@ -30,9 +31,9 @@ describe('DisabledMixin', () => { }); it('will not allow to become enabled after makeRequestToBeDisabled()', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); el.makeRequestToBeDisabled(); expect(el.disabled).to.be.true; @@ -41,18 +42,18 @@ describe('DisabledMixin', () => { }); it('will stay disabled after retractRequestToBeDisabled() if it was disabled before', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); el.makeRequestToBeDisabled(); el.retractRequestToBeDisabled(); expect(el.disabled).to.be.true; }); it('will become enabled after retractRequestToBeDisabled() if it was enabled before', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); el.makeRequestToBeDisabled(); expect(el.disabled).to.be.true; el.retractRequestToBeDisabled(); @@ -60,9 +61,9 @@ describe('DisabledMixin', () => { }); it('may allow multiple calls to makeRequestToBeDisabled()', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); el.makeRequestToBeDisabled(); el.makeRequestToBeDisabled(); el.retractRequestToBeDisabled(); @@ -70,9 +71,9 @@ describe('DisabledMixin', () => { }); it('will restore last state after retractRequestToBeDisabled()', async () => { - const el = /** @type {CanBeDisabled} */ (await fixture( - html``, - )); + const el = /** @type {CanBeDisabled} */ ( + await fixture(html``) + ); el.makeRequestToBeDisabled(); el.disabled = true; el.retractRequestToBeDisabled(); diff --git a/packages/core/test/DisabledWithTabIndexMixin.test.js b/packages/core/test/DisabledWithTabIndexMixin.test.js index 12d9bba0c..be34689fe 100644 --- a/packages/core/test/DisabledWithTabIndexMixin.test.js +++ b/packages/core/test/DisabledWithTabIndexMixin.test.js @@ -1,6 +1,6 @@ /* eslint-disable lit-a11y/tabindex-no-positive */ -import { expect, fixture, html } from '@open-wc/testing'; - +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { LitElement } from '../index.js'; import { DisabledWithTabIndexMixin } from '../src/DisabledWithTabIndexMixin.js'; @@ -11,17 +11,17 @@ describe('DisabledWithTabIndexMixin', () => { }); it('has an initial tabIndex of 0', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` `) + ); expect(el.tabIndex).to.equal(0); expect(el.getAttribute('tabindex')).to.equal('0'); }); it('sets tabIndex to -1 if disabled', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` `) + ); el.disabled = true; expect(el.tabIndex).to.equal(-1); await el.updateComplete; @@ -29,9 +29,11 @@ describe('DisabledWithTabIndexMixin', () => { }); it('disabled does not override user provided tabindex', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` + + `) + ); expect(el.getAttribute('tabindex')).to.equal('-1'); el.disabled = false; await el.updateComplete; @@ -39,9 +41,11 @@ describe('DisabledWithTabIndexMixin', () => { }); it('can be disabled imperatively', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` + + `) + ); expect(el.getAttribute('tabindex')).to.equal('-1'); el.disabled = false; @@ -56,9 +60,9 @@ describe('DisabledWithTabIndexMixin', () => { }); it('will not allow to change tabIndex after makeRequestToBeDisabled()', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` `) + ); el.makeRequestToBeDisabled(); el.tabIndex = 5; @@ -68,9 +72,11 @@ describe('DisabledWithTabIndexMixin', () => { }); it('will restore last tabIndex after retractRequestToBeDisabled()', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` + + `) + ); el.makeRequestToBeDisabled(); expect(el.tabIndex).to.equal(-1); await el.updateComplete; @@ -97,9 +103,11 @@ describe('DisabledWithTabIndexMixin', () => { }); it('may allow multiple calls to retractRequestToBeDisabled', async () => { - const el = /** @type {WithTabIndex} */ (await fixture(html` - - `)); + const el = /** @type {WithTabIndex} */ ( + await fixture(html` + + `) + ); el.retractRequestToBeDisabled(); el.retractRequestToBeDisabled(); expect(el.disabled).to.be.true; diff --git a/packages/core/test/UpdateStylesMixin.test.js b/packages/core/test/UpdateStylesMixin.test.js index 8c2dc3843..930320796 100644 --- a/packages/core/test/UpdateStylesMixin.test.js +++ b/packages/core/test/UpdateStylesMixin.test.js @@ -1,4 +1,5 @@ -import { defineCE, expect, fixture, html } from '@open-wc/testing'; +import { defineCE, expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { css, LitElement } from '../index.js'; import { UpdateStylesMixin } from '../src/UpdateStylesMixin.js'; diff --git a/packages/dialog/test/lion-dialog.test.js b/packages/dialog/test/lion-dialog.test.js index e59e2decd..41d71a2a5 100644 --- a/packages/dialog/test/lion-dialog.test.js +++ b/packages/dialog/test/lion-dialog.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, html, unsafeStatic } from '@open-wc/testing'; +import { expect, fixture as _fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { runOverlayMixinSuite } from '../../overlays/test-suites/OverlayMixin.suite.js'; import '@lion/dialog/define'; @@ -62,9 +63,9 @@ describe('lion-dialog', () => { el._overlayInvokerNode.click(); expect(el.opened).to.be.true; - const overlaysContainer = /** @type {HTMLElement} */ (document.querySelector( - '.global-overlays', - )); + const overlaysContainer = /** @type {HTMLElement} */ ( + document.querySelector('.global-overlays') + ); const wrapperNode = Array.from(overlaysContainer.children)[1]; const nestedDialog = /** @type {LionDialog} */ (wrapperNode.querySelector('lion-dialog')); // @ts-expect-error you're not allowed to call protected _overlayInvokerNode in public context, but for testing it's okay diff --git a/packages/form-core/src/FormControlMixin.js b/packages/form-core/src/FormControlMixin.js index 42395af7c..3805aa014 100644 --- a/packages/form-core/src/FormControlMixin.js +++ b/packages/form-core/src/FormControlMixin.js @@ -7,7 +7,6 @@ import { FormRegisteringMixin } from './registration/FormRegisteringMixin.js'; * @typedef {import('@lion/core').TemplateResult} TemplateResult * @typedef {import('@lion/core').CSSResult} CSSResult * @typedef {import('@lion/core').CSSResultArray} CSSResultArray - * @typedef {import('@lion/core').nothing} nothing * @typedef {import('@lion/core/types/SlotMixinTypes').SlotsMap} SlotsMap * @typedef {import('./validate/LionValidationFeedback').LionValidationFeedback} LionValidationFeedback * @typedef {import('../types/choice-group/ChoiceInputMixinTypes').ChoiceInputHost} ChoiceInputHost @@ -765,7 +764,6 @@ const FormControlMixinImplementation = superclass => if (this._ariaLabelledNodes.includes(element)) { this._ariaLabelledNodes.splice(this._ariaLabelledNodes.indexOf(element), 1); this._ariaLabelledNodes = [...this._ariaLabelledNodes]; - // This value will be read when we need to reflect to attr /** @type {boolean} */ this.__reorderAriaLabelledNodes = false; diff --git a/packages/form-core/src/FormatMixin.js b/packages/form-core/src/FormatMixin.js index b907b8884..ce80012de 100644 --- a/packages/form-core/src/FormatMixin.js +++ b/packages/form-core/src/FormatMixin.js @@ -72,8 +72,8 @@ const FormatMixinImplementation = superclass => * @param {string} name * @param {any} oldVal */ - requestUpdateInternal(name, oldVal) { - super.requestUpdateInternal(name, oldVal); + requestUpdate(name, oldVal) { + super.requestUpdate(name, oldVal); if (name === 'modelValue' && this.modelValue !== oldVal) { this._onModelValueChanged({ modelValue: this.modelValue }, { modelValue: oldVal }); @@ -525,8 +525,9 @@ const FormatMixinImplementation = superclass => this._inputNode.removeEventListener('input', this._proxyInputEvent); this._inputNode.removeEventListener( this.formatOn, - /** @type {EventListenerOrEventListenerObject} */ (this - ._reflectBackFormattedValueDebounced), + /** @type {EventListenerOrEventListenerObject} */ ( + this._reflectBackFormattedValueDebounced + ), ); this._inputNode.removeEventListener('compositionstart', this.__onCompositionEvent); this._inputNode.removeEventListener('compositionend', this.__onCompositionEvent); diff --git a/packages/form-core/src/InteractionStateMixin.js b/packages/form-core/src/InteractionStateMixin.js index 38cb07f22..aad91a541 100644 --- a/packages/form-core/src/InteractionStateMixin.js +++ b/packages/form-core/src/InteractionStateMixin.js @@ -35,14 +35,14 @@ const InteractionStateMixinImplementation = superclass => * @param {PropertyKey} name * @param {*} oldVal */ - requestUpdateInternal(name, oldVal) { - super.requestUpdateInternal(name, oldVal); + requestUpdate(name, oldVal) { + super.requestUpdate(name, oldVal); if (name === 'touched' && this.touched !== oldVal) { this._onTouchedChanged(); } if (name === 'modelValue') { - // We do this in requestUpdateInternal because we don't want to fire another re-render (e.g. when doing this in updated) + // We do this in requestUpdate because we don't want to fire another re-render (e.g. when doing this in updated) // Furthermore, we cannot do it on model-value-changed event because it isn't fired initially. this.filled = !this._isEmpty(); } diff --git a/packages/form-core/src/choice-group/ChoiceInputMixin.js b/packages/form-core/src/choice-group/ChoiceInputMixin.js index 7afb33708..a34ac0e35 100644 --- a/packages/form-core/src/choice-group/ChoiceInputMixin.js +++ b/packages/form-core/src/choice-group/ChoiceInputMixin.js @@ -53,8 +53,8 @@ const ChoiceInputMixinImplementation = superclass => * @param {string} name * @param {any} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (name === 'modelValue') { if (this.modelValue.checked !== this.checked) { @@ -298,7 +298,7 @@ const ChoiceInputMixinImplementation = superclass => /** * @override * hasChanged is designed for async (updated) callback, also check for sync - * (requestUpdateInternal) callback + * (requestUpdate) callback * @param {{ modelValue:unknown }} newV * @param {{ modelValue:unknown }} [old] * @protected @@ -309,7 +309,7 @@ const ChoiceInputMixinImplementation = superclass => _old = old.modelValue; } // @ts-expect-error [external]: lit private property - if (this.constructor._classProperties.get('modelValue').hasChanged(modelValue, _old)) { + if (this.constructor.elementProperties.get('modelValue').hasChanged(modelValue, _old)) { super._onModelValueChanged({ modelValue }); } } diff --git a/packages/form-core/src/form-group/FormGroupMixin.js b/packages/form-core/src/form-group/FormGroupMixin.js index 925dfb63e..cc0ab2111 100644 --- a/packages/form-core/src/form-group/FormGroupMixin.js +++ b/packages/form-core/src/form-group/FormGroupMixin.js @@ -360,12 +360,11 @@ const FormGroupMixinImplementation = superclass => if (values && typeof values === 'object') { Object.keys(values).forEach(name => { if (Array.isArray(this.formElements[name])) { - this.formElements[name].forEach(( - /** @type {FormControl} */ el, - /** @type {number} */ index, - ) => { - el[property] = values[name][index]; // eslint-disable-line no-param-reassign - }); + this.formElements[name].forEach( + (/** @type {FormControl} */ el, /** @type {number} */ index) => { + el[property] = values[name][index]; // eslint-disable-line no-param-reassign + }, + ); } if (this.formElements[name]) { this.formElements[name][property] = values[name]; diff --git a/packages/form-core/src/utils/SyncUpdatableMixin.js b/packages/form-core/src/utils/SyncUpdatableMixin.js index 5058fe9e7..3b7edd1df 100644 --- a/packages/form-core/src/utils/SyncUpdatableMixin.js +++ b/packages/form-core/src/utils/SyncUpdatableMixin.js @@ -18,7 +18,7 @@ import { dedupeMixin } from '@lion/core'; * `updateSync` will only be called when new value differs from old value. * See: https://lit-element.polymer-project.org/guide/lifecycle#haschanged * - it is a stable abstraction on top of a protected/non official lifecycle LitElement api. - * Whenever the implementation of `requestUpdateInternal` changes (this happened in the past for + * Whenever the implementation of `requestUpdate` changes (this happened in the past for * `requestUpdate`) we only have to change our abstraction instead of all our components * @type {SyncUpdatableMixin} * @param {import('@open-wc/dedupe-mixin').Constructor} superclass @@ -64,7 +64,7 @@ const SyncUpdatableMixinImplementation = superclass => */ static __syncUpdatableHasChanged(name, newValue, oldValue) { // @ts-expect-error [external]: accessing private lit property - const properties = this._classProperties; + const properties = this.elementProperties; if (properties.get(name) && properties.get(name).hasChanged) { return properties.get(name).hasChanged(newValue, oldValue); } @@ -74,8 +74,10 @@ const SyncUpdatableMixinImplementation = superclass => /** @private */ __syncUpdatableInitialize() { const ns = this.__SyncUpdatableNamespace; - const ctor = /** @type {typeof SyncUpdatableMixin & typeof import('../../types/utils/SyncUpdatableMixinTypes').SyncUpdatableHost} */ (this - .constructor); + const ctor = + /** @type {typeof SyncUpdatableMixin & typeof import('../../types/utils/SyncUpdatableMixinTypes').SyncUpdatableHost} */ ( + this.constructor + ); ns.initialized = true; // Empty queue... @@ -93,14 +95,16 @@ const SyncUpdatableMixinImplementation = superclass => * @param {string} name * @param {*} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); this.__SyncUpdatableNamespace = this.__SyncUpdatableNamespace || {}; const ns = this.__SyncUpdatableNamespace; - const ctor = /** @type {typeof SyncUpdatableMixin & typeof import('../../types/utils/SyncUpdatableMixinTypes').SyncUpdatableHost} */ (this - .constructor); + const ctor = + /** @type {typeof SyncUpdatableMixin & typeof import('../../types/utils/SyncUpdatableMixinTypes').SyncUpdatableHost} */ ( + this.constructor + ); // Before connectedCallback: queue if (!ns.initialized) { ns.queue = ns.queue || new Set(); @@ -114,7 +118,7 @@ const SyncUpdatableMixinImplementation = superclass => } /** - * An abstraction that has the exact same api as `requestUpdateInternal`, but taking + * An abstraction that has the exact same api as `requestUpdate`, but taking * into account: * - [member order independence](https://github.com/webcomponents/gold-standard/wiki/Member-Order-Independence) * - property effects start when all (light) dom has initialized (on firstUpdated) @@ -122,7 +126,7 @@ const SyncUpdatableMixinImplementation = superclass => * - compatible with propertyAccessor.`hasChanged`: no manual checks needed or accidentally * run property effects / events when no change happened * effects when values didn't change - * All code previously present in requestUpdateInternal can be placed in this method. + * All code previously present in requestUpdate can be placed in this method. * @param {string} name * @param {*} oldValue */ diff --git a/packages/form-core/test-suites/FormatMixin.suite.js b/packages/form-core/test-suites/FormatMixin.suite.js index 6f88b333c..16b1ac57f 100644 --- a/packages/form-core/test-suites/FormatMixin.suite.js +++ b/packages/form-core/test-suites/FormatMixin.suite.js @@ -1,6 +1,8 @@ import { LitElement } from '@lion/core'; import { parseDate } from '@lion/localize'; -import { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing'; +import { aTimeout, defineCE, expect, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; + import sinon from 'sinon'; import { FormatMixin } from '../src/FormatMixin.js'; import { Unparseable, Validator } from '../index.js'; @@ -95,7 +97,7 @@ export function runFormatMixinSuite(customConfig) { } describe('FormatMixin', async () => { - /** @type {{d: any}} */ + /** @type {{_$litStatic$: any}} */ let tag; /** @type {FormatClass} */ let nonFormat; @@ -148,9 +150,9 @@ export function runFormatMixinSuite(customConfig) { */ describe('ModelValue', () => { it('fires `model-value-changed` for every programmatic modelValue change', async () => { - const el = /** @type {FormatClass} */ (await fixture( - html`<${tag}>`, - )); + const el = /** @type {FormatClass} */ ( + await fixture(html`<${tag}>`) + ); let counter = 0; let isTriggeredByUser = false; @@ -172,18 +174,19 @@ export function runFormatMixinSuite(customConfig) { }); it('fires `model-value-changed` for every user input, adding `isTriggeredByUser` in event detail', async () => { - const formatEl = /** @type {FormatClass} */ (await fixture( - html`<${tag}>`, - )); + const formatEl = /** @type {FormatClass} */ ( + await fixture(html`<${tag}>`) + ); let counter = 0; let isTriggeredByUser = false; - formatEl.addEventListener('model-value-changed', ( - /** @param {CustomEvent} event */ event, - ) => { - counter += 1; - isTriggeredByUser = /** @type {CustomEvent} */ (event).detail.isTriggeredByUser; - }); + formatEl.addEventListener( + 'model-value-changed', + (/** @param {CustomEvent} event */ event) => { + counter += 1; + isTriggeredByUser = /** @type {CustomEvent} */ (event).detail.isTriggeredByUser; + }, + ); mimicUserInput(formatEl, generateValueBasedOnType()); expect(counter).to.equal(1); @@ -205,7 +208,8 @@ export function runFormatMixinSuite(customConfig) { it('synchronizes _inputNode.value as a fallback mechanism on init (when no modelValue provided)', async () => { // Note that in lion-field, the attribute would be put on , not on - const formatElem = /** @type {FormatClass} */ (await fixture(html` + const formatElem = /** @type {FormatClass} */ ( + await fixture(html` <${tag} value="string" .formatter=${/** @param {string} value */ value => `foo: ${value}`} @@ -215,7 +219,8 @@ export function runFormatMixinSuite(customConfig) { > - `)); + `) + ); // Now check if the format/parse/serialize loop has been triggered await formatElem.updateComplete; expect(formatElem.formattedValue).to.equal('foo: string'); @@ -228,20 +233,23 @@ export function runFormatMixinSuite(customConfig) { describe('Unparseable values', () => { it('converts to Unparseable when wrong value inputted by user', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .parser=${ - /** @param {string} viewValue */ viewValue => Number(viewValue) || undefined - } + /** @param {string} viewValue */ viewValue => Number(viewValue) || undefined + } > - `)); + `) + ); mimicUserInput(el, 'test'); expect(el.modelValue).to.be.an.instanceof(Unparseable); }); it('preserves the viewValue when unparseable', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .parser=${ /** @param {string} viewValue */ viewValue => Number(viewValue) || undefined @@ -249,14 +257,16 @@ export function runFormatMixinSuite(customConfig) { > - `)); + `) + ); mimicUserInput(el, 'test'); expect(el.formattedValue).to.equal('test'); expect(el.value).to.equal('test'); }); it('displays the viewValue when modelValue is of type Unparseable', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .parser=${ /** @param {string} viewValue */ viewValue => Number(viewValue) || undefined @@ -264,17 +274,20 @@ export function runFormatMixinSuite(customConfig) { > - `)); + `) + ); el.modelValue = new Unparseable('foo'); expect(el.value).to.equal('foo'); }); it('empty strings are not Unparseable', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // This could happen when the user erases the input value mimicUserInput(el, ''); // For backwards compatibility, we keep the modelValue an empty string here. @@ -303,11 +316,13 @@ export function runFormatMixinSuite(customConfig) { describe('Presenting value to end user', () => { it('reflects back formatted value to user on leave', async () => { - const formatEl = /** @type {FormatClass} */ (await fixture(html` + const formatEl = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .formatter="${/** @param {string} value */ value => `foo: ${value}`}"> - `)); + `) + ); const { _inputNode } = getFormControlMembers(formatEl); const generatedViewValue = generateValueBasedOnType({ viewValue: true }); @@ -322,11 +337,13 @@ export function runFormatMixinSuite(customConfig) { }); it('reflects back .formattedValue immediately when .modelValue changed imperatively', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .formatter="${/** @param {string} value */ value => `foo: ${value}`}"> - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); @@ -351,7 +368,8 @@ export function runFormatMixinSuite(customConfig) { const parserSpy = sinon.spy(value => value.replace('foo: ', '')); const serializerSpy = sinon.spy(value => `[foo] ${value}`); const preprocessorSpy = sinon.spy(value => value.replace('bar', '')); - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .formatter=${formatterSpy} .parser=${parserSpy} @@ -361,7 +379,8 @@ export function runFormatMixinSuite(customConfig) { > - `)); + `) + ); expect(formatterSpy.called).to.be.true; expect(serializerSpy.called).to.be.true; @@ -407,11 +426,13 @@ export function runFormatMixinSuite(customConfig) { toggleValue: true, }); - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .formatter=${formatterSpy}> - `)); + `) + ); expect(formatterSpy.callCount).to.equal(1); el.hasFeedbackFor.push('error'); @@ -446,9 +467,11 @@ export function runFormatMixinSuite(customConfig) { it('has formatOptions available in formatter', async () => { const formatterSpy = sinon.spy(value => `foo: ${value}`); - const generatedViewValue = /** @type {string} */ (generateValueBasedOnType({ - viewValue: true, - })); + const generatedViewValue = /** @type {string} */ ( + generateValueBasedOnType({ + viewValue: true, + }) + ); await fixture(html` <${tag} value="${generatedViewValue}" .formatter="${formatterSpy}" .formatOptions="${{ locale: 'en-GB', decimalSeparator: '-' }}"> @@ -483,9 +506,11 @@ export function runFormatMixinSuite(customConfig) { } it('sets formatOptions.mode to "pasted" (and restores to "auto")', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${reflectingTag}> - `)); + `) + ); const formatterSpy = sinon.spy(el, 'formatter'); paste(el); expect(formatterSpy).to.be.called; @@ -496,9 +521,11 @@ export function runFormatMixinSuite(customConfig) { }); it('sets protected value "_isPasting" for Subclassers', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${reflectingTag}> - `)); + `) + ); const formatterSpy = sinon.spy(el, 'formatter'); paste(el); expect(formatterSpy).to.have.been.called; @@ -510,9 +537,11 @@ export function runFormatMixinSuite(customConfig) { }); it('calls formatter and "_reflectBackOn()"', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const reflectBackSpy = sinon.spy(el, '_reflectBackOn'); paste(el); @@ -520,9 +549,11 @@ export function runFormatMixinSuite(customConfig) { }); it(`updates viewValue when "_reflectBackOn()" configured to reflect`, async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${reflectingTag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const reflectBackSpy = sinon.spy(el, '_reflectBackOn'); paste(el); @@ -536,11 +567,13 @@ export function runFormatMixinSuite(customConfig) { /** @type {?} */ const generatedValue = generateValueBasedOnType(); const parserSpy = sinon.spy(); - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .parser="${parserSpy}"> - `)); + `) + ); expect(parserSpy.callCount).to.equal(1); // This could happen for instance in a reset @@ -562,11 +595,13 @@ export function runFormatMixinSuite(customConfig) { const toBeCorrectedVal = `${val}$`; const preprocessorSpy = sinon.spy(v => v.replace(/\$$/g, '')); - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .preprocessor=${preprocessorSpy}> - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); @@ -581,11 +616,13 @@ export function runFormatMixinSuite(customConfig) { }); it('does not preprocess during composition', async () => { - const el = /** @type {FormatClass} */ (await fixture(html` + const el = /** @type {FormatClass} */ ( + await fixture(html` <${tag} .preprocessor=${(/** @type {string} */ v) => v.replace(/\$$/g, '')}> - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); diff --git a/packages/form-core/test-suites/ValidateMixin.suite.js b/packages/form-core/test-suites/ValidateMixin.suite.js index d972975af..8dc835d2e 100644 --- a/packages/form-core/test-suites/ValidateMixin.suite.js +++ b/packages/form-core/test-suites/ValidateMixin.suite.js @@ -117,21 +117,25 @@ export function runValidateMixinSuite(customConfig) { }); it('validates on initialization (once form field has bootstrapped/initialized)', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new Required()]} >${lightDom} - `)); + `) + ); expect(el.hasFeedbackFor).to.deep.equal(['error']); }); it('revalidates when ".modelValue" changes', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid()]} .modelValue=${'myValue'} >${lightDom} - `)); + `) + ); const validateSpy = sinon.spy(el, 'validate'); el.modelValue = 'x'; @@ -139,13 +143,15 @@ export function runValidateMixinSuite(customConfig) { }); it('revalidates when child ".modelValue" changes', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} ._repropagationRole="${'fieldset'}" .validators=${[new AlwaysValid()]} .modelValue=${'myValue'} > - `)); + `) + ); const validateSpy = sinon.spy(el, 'validate'); /** @type {LionField} */ (el.querySelector('#child')).modelValue = 'test'; await el.updateComplete; @@ -153,12 +159,14 @@ export function runValidateMixinSuite(customConfig) { }); it('revalidates when ".validators" changes', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid()]} .modelValue=${'myValue'} >${lightDom} - `)); + `) + ); const validateSpy = sinon.spy(el, 'validate'); el.validators = [new MinLength(3)]; @@ -166,12 +174,14 @@ export function runValidateMixinSuite(customConfig) { }); it('clears current results when ".modelValue" changes', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid()]} .modelValue=${'myValue'} >${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const clearSpy = sinon.spy(el, '__clearValidationResults'); @@ -192,9 +202,11 @@ export function runValidateMixinSuite(customConfig) { it('firstly checks for empty values', async () => { const alwaysValid = new AlwaysValid(); const alwaysValidExecuteSpy = sinon.spy(alwaysValid, 'execute'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[alwaysValid]}>${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const isEmptySpy = sinon.spy(el, '__isEmpty'); const validateSpy = sinon.spy(el, 'validate'); @@ -210,9 +222,11 @@ export function runValidateMixinSuite(customConfig) { }); it('secondly checks for synchronous Validators: creates RegularValidationResult', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid()]}>${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const isEmptySpy = sinon.spy(el, '__isEmpty'); // @ts-ignore [allow-private] in test @@ -222,11 +236,13 @@ export function runValidateMixinSuite(customConfig) { }); it('thirdly schedules asynchronous Validators: creates RegularValidationResult', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid(), new AsyncAlwaysValid()]}> ${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const syncSpy = sinon.spy(el, '__executeSyncValidators'); // @ts-ignore [allow-private] in test @@ -242,12 +258,14 @@ export function runValidateMixinSuite(customConfig) { } } - let el = /** @type {ValidateElement} */ (await fixture(html` + let el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid(), new MyResult()]}> ${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const syncSpy = sinon.spy(el, '__executeSyncValidators'); @@ -278,11 +296,13 @@ export function runValidateMixinSuite(customConfig) { describe('Finalization', () => { it('fires private "validate-performed" event on every cycle', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AlwaysValid(), new AsyncAlwaysInvalid()]}> ${lightDom} - `)); + `) + ); const cbSpy = sinon.spy(); el.addEventListener('validate-performed', cbSpy); el.modelValue = 'nonEmpty'; @@ -290,11 +310,13 @@ export function runValidateMixinSuite(customConfig) { }); it('resolves ".validateComplete" Promise', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new AsyncAlwaysInvalid()]}> ${lightDom} - `)); + `) + ); el.modelValue = 'nonEmpty'; // @ts-ignore [allow-private] in test const validateResolveSpy = sinon.spy(el, '__validateCompleteResolve'); @@ -395,9 +417,11 @@ export function runValidateMixinSuite(customConfig) { }); it('Validators will not be called on empty values', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new IsCat()]}>${lightDom} - `)); + `) + ); el.modelValue = 'cat'; expect(el.validationStates.error.IsCat).to.be.undefined; @@ -410,12 +434,14 @@ export function runValidateMixinSuite(customConfig) { it('Validators get retriggered on parameter change', async () => { const isCatValidator = new IsCat('Felix'); const catSpy = sinon.spy(isCatValidator, 'execute'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[isCatValidator]} .modelValue=${'cat'} >${lightDom} - `)); + `) + ); el.modelValue = 'cat'; expect(catSpy.callCount).to.equal(1); isCatValidator.param = 'Garfield'; @@ -459,13 +485,15 @@ export function runValidateMixinSuite(customConfig) { // default execution trigger is keyup (think of password availability backend) // can configure execution trigger (blur, etc?) it('handles "execute" functions returning promises', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .modelValue=${'dog'} .validators=${[new IsAsyncCat()]}> ${lightDom} - `)); + `) + ); const validator = el.validators[0]; expect(validator instanceof Validator).to.be.true; @@ -476,9 +504,11 @@ export function runValidateMixinSuite(customConfig) { }); it('sets ".isPending/[is-pending]" when validation is in progress', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .modelValue=${'dog'}>${lightDom} - `)); + `) + ); expect(el.isPending).to.be.false; expect(el.hasAttribute('is-pending')).to.be.false; @@ -498,11 +528,13 @@ export function runValidateMixinSuite(customConfig) { const asyncV = new IsAsyncCat(); const asyncVExecuteSpy = sinon.spy(asyncV, 'execute'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .modelValue=${'dog'}> ${lightDom} - `)); + `) + ); // debounce started el.validators = [asyncV]; expect(asyncVExecuteSpy.called).to.equal(0); @@ -528,11 +560,13 @@ export function runValidateMixinSuite(customConfig) { const asyncV = new IsAsyncCat(); const asyncVAbortSpy = sinon.spy(asyncV, 'abortExecution'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .modelValue=${'dog'}> ${lightDom} - `)); + `) + ); // debounce started el.validators = [asyncV]; expect(asyncVAbortSpy.called).to.equal(0); @@ -546,7 +580,8 @@ export function runValidateMixinSuite(customConfig) { const asyncV = new IsAsyncCat(); const asyncVExecuteSpy = sinon.spy(asyncV, 'execute'); - const el = /** @type {ValidateElement & { isFocused: boolean }} */ (await fixture(html` + const el = /** @type {ValidateElement & { isFocused: boolean }} */ ( + await fixture(html` <${tag} .isFocused=${true} .modelValue=${'dog'} @@ -558,7 +593,8 @@ export function runValidateMixinSuite(customConfig) { > ${lightDom} - `)); + `) + ); expect(asyncVExecuteSpy.called).to.equal(0); el.isFocused = false; @@ -635,12 +671,14 @@ export function runValidateMixinSuite(customConfig) { const resultValidator = new MySuccessResultValidator(); const resultValidateSpy = sinon.spy(resultValidator, 'executeOnResults'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${withSuccessTag} .validators=${[new MinLength(3), resultValidator]} .modelValue=${'myValue'} >${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const prevValidationResult = el.__prevValidationResult; // @ts-ignore [allow-private] in test @@ -671,12 +709,14 @@ export function runValidateMixinSuite(customConfig) { const validator = new AlwaysInvalid(); const resultV = new AlwaysInvalidResult(); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[validator, resultV]} .modelValue=${'myValue'} >${lightDom} - `)); + `) + ); // @ts-ignore [allow-private] in test const totalValidationResult = el.__validationResult; @@ -686,12 +726,14 @@ export function runValidateMixinSuite(customConfig) { describe('Required Validator integration', () => { it('will result in erroneous state when form control is empty', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new Required()]} .modelValue=${''} >${lightDom} - `)); + `) + ); expect(el.validationStates.error.Required).to.be.true; expect(el.hasFeedbackFor).to.deep.equal(['error']); @@ -701,12 +743,14 @@ export function runValidateMixinSuite(customConfig) { }); it('calls private ".__isEmpty" by default', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new Required()]} .modelValue=${''} >${lightDom} - `)); + `) + ); const validator = /** @type {Validator} */ (el.validators.find(v => v instanceof Required)); const executeSpy = sinon.spy(validator, 'execute'); // @ts-ignore [allow-private] in test @@ -725,12 +769,14 @@ export function runValidateMixinSuite(customConfig) { const customRequiredTagString = defineCE(_isEmptyValidate); const customRequiredTag = unsafeStatic(customRequiredTagString); - const el = /** @type {_isEmptyValidate} */ (await fixture(html` + const el = /** @type {_isEmptyValidate} */ ( + await fixture(html` <${customRequiredTag} .validators=${[new Required()]} .modelValue=${{ model: 'foo' }} >${lightDom} - `)); + `) + ); const providedIsEmptySpy = sinon.spy(el, '_isEmpty'); el.modelValue = { model: '' }; @@ -741,24 +787,28 @@ export function runValidateMixinSuite(customConfig) { it('prevents other Validators from being called when input is empty', async () => { const alwaysInvalid = new AlwaysInvalid(); const alwaysInvalidSpy = sinon.spy(alwaysInvalid, 'execute'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new Required(), alwaysInvalid]} .modelValue=${''} >${lightDom} - `)); + `) + ); expect(alwaysInvalidSpy.callCount).to.equal(0); // __isRequired returned false (invalid) el.modelValue = 'foo'; expect(alwaysInvalidSpy.callCount).to.equal(1); // __isRequired returned true (valid) }); it('adds [aria-required="true"] to "._inputNode"', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new Required()]} .modelValue=${''} >${lightDom} - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); expect(_inputNode?.getAttribute('aria-required')).to.equal('true'); @@ -779,11 +829,13 @@ export function runValidateMixinSuite(customConfig) { const preconfTag = unsafeStatic(preconfTagString); it('can be stored for custom inputs', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${preconfTag} .validators=${[new MinLength(3)]} .modelValue=${'12'} - >`)); + >`) + ); expect(el.validationStates.error.AlwaysInvalid).to.be.true; expect(el.validationStates.error.MinLength).to.be.true; @@ -800,10 +852,12 @@ export function runValidateMixinSuite(customConfig) { ); const altPreconfTag = unsafeStatic(altPreconfTagString); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${altPreconfTag} .modelValue=${'12'} - >`)); + >`) + ); expect(el.validationStates.error.MinLength).to.be.true; el.defaultValidators[0].param = 2; @@ -811,10 +865,12 @@ export function runValidateMixinSuite(customConfig) { }); it('can be requested via "._allValidators" getter', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${preconfTag} .validators=${[new MinLength(3)]} - >`)); + >`) + ); const { _allValidators } = getFormControlMembers(el); expect(el.validators.length).to.equal(1); @@ -834,11 +890,13 @@ export function runValidateMixinSuite(customConfig) { describe('State storage and reflection', () => { it('stores validity of individual Validators in ".validationStates.error[validator.validatorName]"', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .modelValue=${'a'} .validators=${[new MinLength(3), new AlwaysInvalid()]} - >${lightDom}`)); + >${lightDom}`) + ); expect(el.validationStates.error.MinLength).to.be.true; expect(el.validationStates.error.AlwaysInvalid).to.be.true; @@ -849,11 +907,13 @@ export function runValidateMixinSuite(customConfig) { }); it('removes "non active" states whenever modelValue becomes undefined', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[new MinLength(3)]} >${lightDom} - `)); + `) + ); el.modelValue = 'a'; expect(el.hasFeedbackFor).to.deep.equal(['error']); expect(el.validationStates.error).to.not.eql({}); @@ -865,11 +925,13 @@ export function runValidateMixinSuite(customConfig) { it('clears current validation results when validators array updated', async () => { const validators = [new Required()]; - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${validators} >${lightDom} - `)); + `) + ); expect(el.hasFeedbackFor).to.deep.equal(['error']); expect(el.validationStates.error).to.eql({ Required: true }); @@ -883,7 +945,8 @@ export function runValidateMixinSuite(customConfig) { }); it('can be configured to change visibility conditions per type', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators="${[new Required({}, { type: 'error' })]}" .feedbackCondition="${( @@ -897,7 +960,8 @@ export function runValidateMixinSuite(customConfig) { return defaultCondition(type); }}" >${lightDom} - `)); + `) + ); expect(el.showsFeedbackFor).to.eql(['error']); }); @@ -905,13 +969,15 @@ export function runValidateMixinSuite(customConfig) { describe('Events', () => { it('fires "showsFeedbackForChanged" event async after feedbackData got synced to feedbackElement', async () => { const spy = sinon.spy(); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .submitted=${true} .validators=${[new MinLength(7)]} - @showsFeedbackForChanged=${spy}; + @showsFeedbackForChanged=${spy} >${lightDom} - `)); + `) + ); el.modelValue = 'a'; await el.updateComplete; expect(spy).to.have.callCount(1); @@ -927,13 +993,15 @@ export function runValidateMixinSuite(customConfig) { it('fires "showsFeedbackFor{type}Changed" event async when type visibility changed', async () => { const spy = sinon.spy(); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .submitted=${true} .validators=${[new MinLength(7)]} - @showsFeedbackForErrorChanged=${spy}; + @showsFeedbackForErrorChanged=${spy} >${lightDom} - `)); + `) + ); el.modelValue = 'a'; await el.updateComplete; expect(spy).to.have.callCount(1); @@ -949,13 +1017,15 @@ export function runValidateMixinSuite(customConfig) { it('fires "{type}StateChanged" event async when type validity changed', async () => { const spy = sinon.spy(); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .submitted=${true} .validators=${[new MinLength(7)]} - @errorStateChanged=${spy}; + @errorStateChanged=${spy} >${lightDom} - `)); + `) + ); expect(spy).to.have.callCount(0); el.modelValue = 'a'; @@ -975,12 +1045,14 @@ export function runValidateMixinSuite(customConfig) { describe('Accessibility', () => { it.skip('calls "._inputNode.setCustomValidity(errorMessage)"', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .modelValue=${'123'} .validators=${[new MinLength(3, { message: 'foo' })]}> - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); if (_inputNode) { @@ -1013,7 +1085,8 @@ export function runValidateMixinSuite(customConfig) { const customTypeTag = unsafeStatic(customTypeTagString); it('supports additional validationTypes in .hasFeedbackFor', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${customTypeTag} .validators=${[ new MinLength(2, { type: 'x' }), @@ -1022,7 +1095,8 @@ export function runValidateMixinSuite(customConfig) { ]} .modelValue=${'1234'} >${lightDom} - `)); + `) + ); expect(el.hasFeedbackFor).to.deep.equal([]); el.modelValue = '123'; // triggers y @@ -1036,7 +1110,8 @@ export function runValidateMixinSuite(customConfig) { }); it('supports additional validationTypes in .validationStates', async () => { - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${customTypeTag} .validators=${[ new MinLength(2, { type: 'x' }), @@ -1045,7 +1120,8 @@ export function runValidateMixinSuite(customConfig) { ]} .modelValue=${'1234'} >${lightDom} - `)); + `) + ); expect(el.validationStates).to.eql({ x: {}, error: {}, @@ -1076,7 +1152,8 @@ export function runValidateMixinSuite(customConfig) { it('orders feedback based on provided "validationTypes"', async () => { // we set submitted to always show error message in the test - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${customTypeTag} .submitted=${true} ._visibleMessagesAmount=${Infinity} @@ -1087,7 +1164,8 @@ export function runValidateMixinSuite(customConfig) { ]} .modelValue=${'1'} >${lightDom} - `)); + `) + ); const { _feedbackNode } = getFormControlMembers(el); await el.feedbackComplete; @@ -1132,13 +1210,15 @@ export function runValidateMixinSuite(customConfig) { const elTag = unsafeStatic(elTagString); // we set submitted to always show errors - const el = /** @type {ValidateHasX} */ (await fixture(html` + const el = /** @type {ValidateHasX} */ ( + await fixture(html` <${elTag} .submitted=${true} .validators=${[new MinLength(2, { type: 'x' })]} .modelValue=${'1'} >${lightDom} - `)); + `) + ); await el.feedbackComplete; expect(el.hasX).to.be.true; expect(el.hasXVisible).to.be.true; @@ -1186,14 +1266,16 @@ export function runValidateMixinSuite(customConfig) { const spy = sinon.spy(); // we set prefilled to always show errors - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${elTag} .prefilled=${true} @hasFeedbackForXChanged=${spy} .validators=${[new MinLength(2, { type: 'x' })]} .modelValue=${'1'} >${lightDom} - `)); + `) + ); expect(spy).to.have.callCount(1); el.modelValue = '1'; expect(spy).to.have.callCount(1); @@ -1228,12 +1310,14 @@ export function runValidateMixinSuite(customConfig) { }, ); const elTag = unsafeStatic(elTagString); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${elTag} .validators=${[new AlwaysInvalid()]} .modelValue=${'myValue'} >${lightDom} - `)); + `) + ); // @ts-ignore [allow-protected] in test const spy = sinon.spy(el, '_updateShouldShowFeedbackFor'); @@ -1282,14 +1366,16 @@ export function runValidateMixinSuite(customConfig) { }, ); const elTag = unsafeStatic(elTagString); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${elTag} .validators=${[ new AlwaysInvalid({}, { type: 'error' }), new AlwaysInvalid({}, { type: 'info' }), ]} >${lightDom} - `)); + `) + ); for (const [modelValue, expected] of [ ['A', ['error']], diff --git a/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js b/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js index 6217ec31d..186263314 100644 --- a/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js +++ b/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js @@ -2,7 +2,9 @@ import { LitElement } from '@lion/core'; import { LionInput } from '@lion/input'; import '@lion/fieldset/define'; import { FormGroupMixin, Required } from '@lion/form-core'; -import { expect, html, fixture, fixtureSync, unsafeStatic } from '@open-wc/testing'; +import { expect, fixture, fixtureSync } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; + import sinon from 'sinon'; import { ChoiceGroupMixin } from '../../src/choice-group/ChoiceGroupMixin.js'; import { ChoiceInputMixin } from '../../src/choice-group/ChoiceInputMixin.js'; @@ -41,13 +43,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi describe(`ChoiceGroupMixin: ${cfg.parentTagString}`, () => { if (cfg.choiceType === 'single') { it('has a single modelValue representing the currently checked radio value', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); expect(el.modelValue).to.equal('female'); el.formElements[0].checked = true; expect(el.modelValue).to.equal('male'); @@ -56,13 +60,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('has a single formattedValue representing the currently checked radio value', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); expect(el.formattedValue).to.equal('female'); el.formElements[0].checked = true; expect(el.formattedValue).to.equal('male'); @@ -72,16 +78,20 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi } it('throws if a child element without a modelValue like { value: "foo", checked: false } tries to register', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); - const invalidChild = /** @type {ChoiceInputGroup} */ (await fixture(html` + `) + ); + const invalidChild = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${childTag} .modelValue=${'Lara'}> - `)); + `) + ); expect(() => { el.addFormElement(invalidChild); @@ -91,31 +101,37 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('automatically sets the name property of child fields to its own name', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); expect(el.formElements[0].name).to.equal('gender[]'); expect(el.formElements[1].name).to.equal('gender[]'); - const validChild = /** @type {ChoiceInputGroup} */ (await fixture(html` + const validChild = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${childTag} .choiceValue=${'male'}> - `)); + `) + ); el.appendChild(validChild); expect(el.formElements[2].name).to.equal('gender[]'); }); it('automatically updates the name property of child fields to its own name', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag}> <${childTag}> - `)); + `) + ); expect(el.formElements[0].name).to.equal('gender[]'); expect(el.formElements[1].name).to.equal('gender[]'); @@ -129,12 +145,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('prevents updating the name property of a child if it is different from its parent', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag}> <${childTag}> - `)); + `) + ); expect(el.formElements[0].name).to.equal('gender[]'); expect(el.formElements[1].name).to.equal('gender[]'); @@ -146,12 +164,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('allows updating the name property of a child if parent tagName does not include childTagname', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTagFoo}> <${childTagFoo}> - `)); + `) + ); expect(el.formElements[0].name).to.equal('gender[]'); expect(el.formElements[1].name).to.equal('gender[]'); @@ -163,12 +183,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('allows setting the condition for syncing the name property of a child to parent', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTagBar}> <${childTagBar}> - `)); + `) + ); expect(el.formElements[0].name).to.equal('gender[]'); expect(el.formElements[1].name).to.equal('gender[]'); @@ -180,29 +202,35 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('adjusts the name of a child element if it has a different name than the group', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); - const invalidChild = /** @type {ChoiceInputGroup} */ (await fixture(html` + const invalidChild = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${childTag} name="foo" .choiceValue=${'male'}> - `)); + `) + ); el.addFormElement(invalidChild); await invalidChild.updateComplete; expect(invalidChild.name).to.equal('gender[]'); }); it('can set initial modelValue on creation', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]" .modelValue=${'other'}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.modelValue).to.equal('other'); @@ -213,13 +241,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can set initial serializedValue on creation', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]" .serializedValue=${'other'}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.serializedValue).to.equal('other'); @@ -230,13 +260,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can set initial formattedValue on creation', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]" .formattedValue=${'other'}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.formattedValue).to.equal('other'); @@ -247,13 +279,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('correctly handles modelValue being set before registrationComplete', async () => { - const el = /** @type {ChoiceInputGroup} */ (fixtureSync(html` + const el = /** @type {ChoiceInputGroup} */ ( + fixtureSync(html` <${parentTag} name="gender[]" .modelValue=${null}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { el.modelValue = 'other'; @@ -267,13 +301,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('correctly handles serializedValue being set before registrationComplete', async () => { - const el = /** @type {ChoiceInputGroup} */ (fixtureSync(html` + const el = /** @type {ChoiceInputGroup} */ ( + fixtureSync(html` <${parentTag} name="gender[]" .serializedValue=${null}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { // @ts-expect-error @@ -289,13 +325,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can handle null and undefined modelValues', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]" .modelValue=${null}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.modelValue).to.equal(''); @@ -315,12 +353,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi it('can handle complex data via choiceValue', async () => { const date = new Date(2018, 11, 24, 10, 33, 30, 0); - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="data[]"> <${childTag} .choiceValue=${{ some: 'data' }}> <${childTag} .choiceValue=${date} checked> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.modelValue).to.equal(date); @@ -334,12 +374,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can handle 0 and empty string as valid values', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="data[]"> <${childTag} .choiceValue=${0} checked> <${childTag} .choiceValue=${''}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.modelValue).to.equal(0); @@ -353,7 +395,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can check a choice by supplying an available modelValue', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .modelValue="${{ value: 'male', checked: false }}" @@ -365,7 +408,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi .modelValue="${{ value: 'other', checked: false }}" > - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.modelValue).to.equal('female'); @@ -377,7 +421,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can check a choice by supplying an available modelValue even if this modelValue is an array or object', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .modelValue="${{ value: { v: 'male' }, checked: false }}" @@ -389,7 +434,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi .modelValue="${{ value: { v: 'other' }, checked: false }}" > - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.modelValue).to.eql({ v: 'female' }); @@ -407,7 +453,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi it('expect child nodes to only fire one model-value-changed event per instance', async () => { let counter = 0; - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]" @model-value-changed=${() => { @@ -420,7 +467,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi > <${childTag} .choiceValue=${'other'}> - `)); + `) + ); counter = 0; // reset after setup which may result in different results @@ -454,14 +502,16 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can be required', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]" .validators=${[new Required()]}> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${{ subObject: 'satisfies required' }} > - `)); + `) + ); expect(el.hasFeedbackFor).to.include('error'); expect(el.validationStates.error).to.exist; expect(el.validationStates.error.Required).to.exist; @@ -478,12 +528,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('returns serialized value', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> - `)); + `) + ); el.formElements[0].checked = true; if (cfg.choiceType === 'single') { expect(el.serializedValue).to.deep.equal('male'); @@ -493,12 +545,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('returns serialized value on unchecked state', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.serializedValue).to.deep.equal(''); @@ -508,12 +562,14 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can be cleared', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> - `)); + `) + ); el.formElements[0].checked = true; el.clear(); @@ -526,13 +582,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi describe('multipleChoice', () => { it('has a single modelValue representing all currently checked values', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} multiple-choice name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); expect(el.modelValue).to.eql(['female']); el.formElements[0].checked = true; @@ -542,13 +600,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('has a single serializedValue representing all currently checked values', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} multiple-choice name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); expect(el.serializedValue).to.eql(['female']); el.formElements[0].checked = true; @@ -558,13 +618,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('has a single formattedValue representing all currently checked values', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} multiple-choice name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'} checked> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); expect(el.formattedValue).to.eql(['female']); el.formElements[0].checked = true; @@ -574,13 +636,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('can check multiple checkboxes by setting the modelValue', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} multiple-choice name="gender[]"> <${childTag} .choiceValue=${'male'}> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'}> - `)); + `) + ); el.modelValue = ['male', 'other']; expect(el.modelValue).to.eql(['male', 'other']); @@ -589,13 +653,15 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi }); it('unchecks non-matching checkboxes when setting the modelValue', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} multiple-choice name="gender[]"> <${childTag} .choiceValue=${'male'} checked> <${childTag} .choiceValue=${'female'}> <${childTag} .choiceValue=${'other'} checked> - `)); + `) + ); expect(el.modelValue).to.eql(['male', 'other']); expect(el.formElements[0].checked).to.be.true; @@ -610,7 +676,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi describe('Integration with a parent form/fieldset', () => { it('will serialize all children with their serializedValue', async () => { - const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + const el = /** @type {ChoiceInputGroup} */ ( + await fixture(html` <${parentTag} name="gender[]"> <${childTag} .choiceValue=${'male'} checked disabled> @@ -618,7 +685,8 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi <${childTag} .choiceValue=${'other'}> - `)); + `) + ); if (cfg.choiceType === 'single') { expect(el.serializedValue).to.deep.equal({ 'gender[]': ['female'] }); @@ -641,19 +709,19 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi `); - const choiceGroupEl = /** @type {ChoiceInputGroup} */ (formEl.querySelector( - '[name=choice-group]', - )); + const choiceGroupEl = /** @type {ChoiceInputGroup} */ ( + formEl.querySelector('[name=choice-group]') + ); if (choiceGroupEl.multipleChoice) { return; } /** @typedef {{ checked: boolean }} checkedInterface */ - const option1El = /** @type {HTMLElement & checkedInterface} */ (formEl.querySelector( - '#option1', - )); - const option2El = /** @type {HTMLElement & checkedInterface} */ (formEl.querySelector( - '#option2', - )); + const option1El = /** @type {HTMLElement & checkedInterface} */ ( + formEl.querySelector('#option1') + ); + const option2El = /** @type {HTMLElement & checkedInterface} */ ( + formEl.querySelector('#option2') + ); formEl.addEventListener('model-value-changed', formSpy); choiceGroupEl?.addEventListener('model-value-changed', choiceGroupSpy); diff --git a/packages/form-core/test-suites/choice-group/ChoiceInputMixin.suite.js b/packages/form-core/test-suites/choice-group/ChoiceInputMixin.suite.js index b55bc2248..223251124 100644 --- a/packages/form-core/test-suites/choice-group/ChoiceInputMixin.suite.js +++ b/packages/form-core/test-suites/choice-group/ChoiceInputMixin.suite.js @@ -1,6 +1,7 @@ import { Required } from '@lion/form-core'; import { LionInput } from '@lion/input'; import { expect, fixture, html, unsafeStatic } from '@open-wc/testing'; + import { getFormControlMembers } from '@lion/form-core/test-helpers'; import sinon from 'sinon'; import { ChoiceInputMixin } from '../../src/choice-group/ChoiceInputMixin.js'; @@ -15,6 +16,7 @@ customElements.define('choice-group-input', ChoiceInput); /** * @param {{ tagString?:string, tagType?: string}} [config] + * @deprecated */ export function runChoiceInputMixinSuite({ tagString } = {}) { const cfg = { @@ -29,9 +31,9 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { }); it('has choiceValue', async () => { - const el = /** @type {ChoiceInput} */ (await fixture( - html`<${tag} .choiceValue=${'foo'}>`, - )); + const el = /** @type {ChoiceInput} */ ( + await fixture(html`<${tag} .choiceValue=${'foo'}>`) + ); expect(el.choiceValue).to.equal('foo'); expect(el.modelValue).to.deep.equal({ @@ -43,9 +45,9 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { it('can handle complex data via choiceValue', async () => { const date = new Date(2018, 11, 24, 10, 33, 30, 0); - const el = /** @type {ChoiceInput} */ (await fixture( - html`<${tag} .choiceValue=${date}>`, - )); + const el = /** @type {ChoiceInput} */ ( + await fixture(html`<${tag} .choiceValue=${date}>`) + ); expect(el.choiceValue).to.equal(date); expect(el.modelValue.value).to.equal(date); @@ -53,14 +55,16 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { it('fires one "model-value-changed" event if choiceValue or checked state or modelValue changed', async () => { let counter = 0; - const el = /** @type {ChoiceInput} */ (await fixture(html` + const el = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} @model-value-changed=${() => { counter += 1; }} .choiceValue=${'foo'} > - `)); + `) + ); expect(counter).to.equal(1); // undefined to set value el.checked = true; @@ -78,7 +82,8 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { it('fires one "user-input-changed" event after user interaction', async () => { let counter = 0; - const el = /** @type {ChoiceInput} */ (await fixture(html` + const el = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} @user-input-changed="${() => { counter += 1; @@ -86,7 +91,8 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { > - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); expect(counter).to.equal(0); @@ -100,13 +106,15 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { it('fires one "click" event when clicking label or input, using the right target', async () => { const spy = sinon.spy(); - const el = /** @type {ChoiceInput} */ (await fixture(html` + const el = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} @click="${spy}" > - `)); + `) + ); const { _inputNode, _labelNode } = getFormControlMembers(el); el.click(); @@ -122,7 +130,8 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { it('adds "isTriggerByUser" flag on model-value-changed', async () => { let isTriggeredByUser; - const el = /** @type {ChoiceInput} */ (await fixture(html` + const el = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} @model-value-changed="${(/** @type {CustomEvent} */ event) => { isTriggeredByUser = event.detail.isTriggeredByUser; @@ -130,7 +139,8 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { > - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); _inputNode.dispatchEvent(new CustomEvent('change', { bubbles: true })); @@ -138,9 +148,11 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { }); it('can be required', async () => { - const el = /** @type {ChoiceInput} */ (await fixture(html` + const el = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} .choiceValue=${'foo'} .validators=${[new Required()]}> - `)); + `) + ); expect(el.hasFeedbackFor).to.include('error'); expect(el.validationStates.error).to.exist; @@ -156,9 +168,11 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { const el = /** @type {ChoiceInput} */ (await fixture(html`<${tag}>`)); expect(el.checked).to.equal(false, 'initially unchecked'); - const precheckedElementAttr = /** @type {ChoiceInput} */ (await fixture(html` + const precheckedElementAttr = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} .checked=${true}> - `)); + `) + ); expect(precheckedElementAttr.checked).to.equal(true, 'initially checked via attribute'); }); @@ -196,9 +210,9 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { }); it('synchronizes modelValue to checked state and vice versa', async () => { - const el = /** @type {ChoiceInput} */ (await fixture( - html`<${tag} .choiceValue=${'foo'}>`, - )); + const el = /** @type {ChoiceInput} */ ( + await fixture(html`<${tag} .choiceValue=${'foo'}>`) + ); expect(el.checked).to.be.false; expect(el.modelValue).to.deep.equal({ checked: false, @@ -215,9 +229,9 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { it('ensures optimal synchronize performance by preventing redundant computation steps', async () => { /* we are checking private apis here to make sure we do not have cyclical updates which can be quite common for these type of connected data */ - const el = /** @type {ChoiceInput} */ (await fixture( - html`<${tag} .choiceValue=${'foo'}>`, - )); + const el = /** @type {ChoiceInput} */ ( + await fixture(html`<${tag} .choiceValue=${'foo'}>`) + ); expect(el.checked).to.be.false; // @ts-ignore [allow-private] in test @@ -245,11 +259,13 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { /** @param {ChoiceInput} el */ const hasAttr = el => el.hasAttribute('checked'); const el = /** @type {ChoiceInput} */ (await fixture(html`<${tag}>`)); - const elChecked = /** @type {ChoiceInput} */ (await fixture(html` + const elChecked = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} .checked=${true}> - `)); + `) + ); const { _inputNode } = getFormControlMembers(el); const { _inputNode: _inputNodeChecked } = getFormControlMembers(elChecked); @@ -294,14 +310,16 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { describe('Format/parse/serialize loop', () => { it('creates a modelValue object like { checked: true, value: foo } on init', async () => { - const el = /** @type {ChoiceInput} */ (await fixture( - html`<${tag} .choiceValue=${'foo'}>`, - )); + const el = /** @type {ChoiceInput} */ ( + await fixture(html`<${tag} .choiceValue=${'foo'}>`) + ); expect(el.modelValue).deep.equal({ value: 'foo', checked: false }); - const elChecked = /** @type {ChoiceInput} */ (await fixture(html` + const elChecked = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} .choiceValue=${'foo'} .checked=${true}> - `)); + `) + ); expect(elChecked.modelValue).deep.equal({ value: 'foo', checked: true }); }); @@ -309,9 +327,11 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { const el = /** @type {ChoiceInput} */ (await fixture(html`<${tag}>`)); expect(el.formattedValue).to.equal(''); - const elementWithValue = /** @type {ChoiceInput} */ (await fixture(html` + const elementWithValue = /** @type {ChoiceInput} */ ( + await fixture(html` <${tag} .choiceValue=${'foo'}> - `)); + `) + ); expect(elementWithValue.formattedValue).to.equal('foo'); }); @@ -325,9 +345,9 @@ export function runChoiceInputMixinSuite({ tagString } = {}) { describe('Interaction states', () => { it('is considered prefilled when checked and not considered prefilled when unchecked', async () => { - const el = /** @type {ChoiceInput} */ (await fixture( - html`<${tag} .checked=${true}>`, - )); + const el = /** @type {ChoiceInput} */ ( + await fixture(html`<${tag} .checked=${true}>`) + ); expect(el.prefilled).equal(true, 'checked element not considered prefilled'); const elUnchecked = /** @type {ChoiceInput} */ (await fixture(html`<${tag}>`)); diff --git a/packages/form-core/test-suites/form-group/FormGroupMixin-input.suite.js b/packages/form-core/test-suites/form-group/FormGroupMixin-input.suite.js index 450ef0db1..ead12dd89 100644 --- a/packages/form-core/test-suites/form-group/FormGroupMixin-input.suite.js +++ b/packages/form-core/test-suites/form-group/FormGroupMixin-input.suite.js @@ -1,6 +1,7 @@ import { LitElement } from '@lion/core'; import { localizeTearDown } from '@lion/localize/test-helpers'; -import { defineCE, expect, html, unsafeStatic, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; +import { defineCE, expect, fixture } from '@open-wc/testing'; import { getFormControlMembers } from '@lion/form-core/test-helpers'; import { LionInput } from '@lion/input'; import '@lion/form-core/define'; @@ -47,12 +48,14 @@ export function runFormGroupMixinInputSuite(cfg = {}) { describe('FormGroupMixin with LionField', () => { it('serializes undefined values as "" (nb radios/checkboxes are always serialized)', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture(html` + const fieldset = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="custom[]"> <${childTag} name="custom[]"> - `)); + `) + ); fieldset.formElements['custom[]'][0].modelValue = 'custom 1'; fieldset.formElements['custom[]'][1].modelValue = undefined; @@ -62,12 +65,14 @@ export function runFormGroupMixinInputSuite(cfg = {}) { }); it('suffixes child labels with group label, just like in
', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} label="set"> <${childTag} name="A" label="fieldA"> <${childTag} name="B" label="fieldB"> - `)); + `) + ); const { _labelNode } = getFormControlMembers(el); /** @@ -88,8 +93,10 @@ export function runFormGroupMixinInputSuite(cfg = {}) { // Test the cleanup on disconnected el.removeChild(field1); - await field1.updateComplete; - expect(getLabels(field1)).to.eql([field1._labelNode.id]); + + // TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901 + // await field1.updateComplete; + // expect(getLabels(field1)).to.eql([field1._labelNode.id]); }); }); @@ -110,7 +117,8 @@ export function runFormGroupMixinInputSuite(cfg = {}) { childAriaFixture = async ( msgSlotType = 'feedback', // eslint-disable-line no-shadow ) => { - const dom = /** @type {FormGroup} */ (await fixture(html` + const dom = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="l1_g"> <${childTag} name="l1_fa">
@@ -144,7 +152,8 @@ export function runFormGroupMixinInputSuite(cfg = {}) {
- `)); + `) + ); return dom; }; @@ -163,18 +172,18 @@ export function runFormGroupMixinInputSuite(cfg = {}) { const msg_l2_fb = /** @type {FormChild} */ (childAriaFixture.querySelector('#msg_l2_fb')); // Field elements: all inputs pointing to message elements - const input_l1_fa = /** @type {HTMLInputElement} */ (childAriaFixture.querySelector( - 'input[name=l1_fa]', - )); - const input_l1_fb = /** @type {HTMLInputElement} */ (childAriaFixture.querySelector( - 'input[name=l1_fb]', - )); - const input_l2_fa = /** @type {HTMLInputElement} */ (childAriaFixture.querySelector( - 'input[name=l2_fa]', - )); - const input_l2_fb = /** @type {HTMLInputElement} */ (childAriaFixture.querySelector( - 'input[name=l2_fb]', - )); + const input_l1_fa = /** @type {HTMLInputElement} */ ( + childAriaFixture.querySelector('input[name=l1_fa]') + ); + const input_l1_fb = /** @type {HTMLInputElement} */ ( + childAriaFixture.querySelector('input[name=l1_fb]') + ); + const input_l2_fa = /** @type {HTMLInputElement} */ ( + childAriaFixture.querySelector('input[name=l2_fa]') + ); + const input_l2_fb = /** @type {HTMLInputElement} */ ( + childAriaFixture.querySelector('input[name=l2_fb]') + ); if (!cleanupPhase) { // 'L1' fields (inside lion-fieldset[name="l1_g"]) should point to l1(group) msg @@ -222,18 +231,18 @@ export function runFormGroupMixinInputSuite(cfg = {}) { ).to.equal(true, 'order of ids'); } else { // cleanupPhase - const control_l1_fa = /** @type {LionField} */ (childAriaFixture.querySelector( - '[name=l1_fa]', - )); - const control_l1_fb = /** @type {LionField} */ (childAriaFixture.querySelector( - '[name=l1_fb]', - )); - const control_l2_fa = /** @type {LionField} */ (childAriaFixture.querySelector( - '[name=l2_fa]', - )); - const control_l2_fb = /** @type {LionField} */ (childAriaFixture.querySelector( - '[name=l2_fb]', - )); + const control_l1_fa = /** @type {LionField} */ ( + childAriaFixture.querySelector('[name=l1_fa]') + ); + const control_l1_fb = /** @type {LionField} */ ( + childAriaFixture.querySelector('[name=l1_fb]') + ); + const control_l2_fa = /** @type {LionField} */ ( + childAriaFixture.querySelector('[name=l2_fa]') + ); + const control_l2_fb = /** @type {LionField} */ ( + childAriaFixture.querySelector('[name=l2_fb]') + ); // @ts-expect-error removeChild should always be inherited via LitElement? control_l1_fa._parentFormGroup.removeChild(control_l1_fa); @@ -303,12 +312,14 @@ export function runFormGroupMixinInputSuite(cfg = {}) { await childAriaTest(await childAriaFixture('help-text')); }); - it(`cleans up feedback message belonging to fieldset on disconnect`, async () => { + // TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901 + it.skip(`cleans up feedback message belonging to fieldset on disconnect`, async () => { const el = await childAriaFixture('feedback'); await childAriaTest(el, { cleanupPhase: true }); }); - it(`cleans up help-text message belonging to fieldset on disconnect`, async () => { + // TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901 + it.skip(`cleans up help-text message belonging to fieldset on disconnect`, async () => { const el = await childAriaFixture('help-text'); await childAriaTest(el, { cleanupPhase: true }); }); diff --git a/packages/form-core/test-suites/form-group/FormGroupMixin.suite.js b/packages/form-core/test-suites/form-group/FormGroupMixin.suite.js index b5e09e4cc..4462828c4 100644 --- a/packages/form-core/test-suites/form-group/FormGroupMixin.suite.js +++ b/packages/form-core/test-suites/form-group/FormGroupMixin.suite.js @@ -1,14 +1,7 @@ import { LitElement, ifDefined } from '@lion/core'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { localizeTearDown } from '@lion/localize/test-helpers'; -import { - defineCE, - expect, - html, - triggerFocusFor, - unsafeStatic, - fixture, - aTimeout, -} from '@open-wc/testing'; +import { defineCE, expect, triggerFocusFor, fixture, aTimeout } from '@open-wc/testing'; import sinon from 'sinon'; import { IsNumber, Validator, LionField } from '@lion/form-core'; import '@lion/form-core/define'; @@ -59,30 +52,32 @@ export function runFormGroupMixinSuite(cfg = {}) { describe('FormGroupMixin', () => { // TODO: Tests below belong to FormControlMixin. Preferably run suite integration test it(`has a fieldName based on the label`, async () => { - const el1 = /** @type {FormGroup} */ (await fixture( - html`<${tag} label="foo">${inputSlots}`, - )); + const el1 = /** @type {FormGroup} */ ( + await fixture(html`<${tag} label="foo">${inputSlots}`) + ); const { _labelNode: _labelNode1 } = getFormControlMembers(el1); expect(el1.fieldName).to.equal(_labelNode1.textContent); - const el2 = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const el2 = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); const { _labelNode: _labelNode2 } = getFormControlMembers(el2); expect(el2.fieldName).to.equal(_labelNode2.textContent); }); it(`has a fieldName based on the name if no label exists`, async () => { - const el = /** @type {FormGroup} */ (await fixture( - html`<${tag} name="foo">${inputSlots}`, - )); + const el = /** @type {FormGroup} */ ( + await fixture(html`<${tag} name="foo">${inputSlots}`) + ); expect(el.fieldName).to.equal(el.name); }); it(`can override fieldName`, async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} label="foo" .fieldName="${'bar'}">${inputSlots} - `)); + `) + ); // @ts-ignore [allow-protected] in test expect(el.__fieldName).to.equal(el.fieldName); }); @@ -100,13 +95,15 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it(`supports in html wrapped form elements`, async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}>
<${childTag} name="foo">
- `)); + `) + ); expect(el.formElements.length).to.equal(1); el.children[0].removeChild(el.formElements.foo); expect(el.formElements.length).to.equal(0); @@ -206,9 +203,9 @@ export function runFormGroupMixinSuite(cfg = {}) { it('can dynamically add/remove elements', async () => { const el = /** @type {FormGroup} */ (await fixture(html`<${tag}>${inputSlots}`)); - const newField = /** @type {FormGroup} */ (await fixture( - html`<${childTag} name="lastName">`, - )); + const newField = /** @type {FormGroup} */ ( + await fixture(html`<${childTag} name="lastName">`) + ); const { _inputNode } = getFormControlMembers(el); // @ts-ignore [allow-protected] in test @@ -226,12 +223,14 @@ export function runFormGroupMixinSuite(cfg = {}) { // TODO: Tests below belong to FormGroupMixin. Preferably run suite integration test it('can read/write all values (of every input) via this.modelValue', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="lastName"> <${tag} name="newfieldset">${inputSlots} - `)); + `) + ); const newFieldset = /** @type {FormGroup} */ (el.querySelector(tagString)); el.formElements.lastName.modelValue = 'Bar'; newFieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'chess' }; @@ -301,7 +300,8 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('does not list disabled values in this.modelValue', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="a" disabled .modelValue="${'x'}"> <${childTag} name="b" .modelValue="${'x'}"> @@ -313,7 +313,8 @@ export function runFormGroupMixinSuite(cfg = {}) { <${childTag} name="e" .modelValue="${'x'}"> - `)); + `) + ); expect(el.modelValue).to.deep.equal({ b: 'x', newFieldset: { @@ -323,12 +324,14 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('does not throw if setter data of this.modelValue can not be handled', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="firstName" .modelValue=${'foo'}> <${childTag} name="lastName" .modelValue=${'bar'}> - `)); + `) + ); const initState = { firstName: 'foo', lastName: 'bar', @@ -343,9 +346,9 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('disables/enables all its formElements if it becomes disabled/enabled', async () => { - const el = /** @type {FormGroup} */ (await fixture( - html`<${tag} disabled>${inputSlots}`, - )); + const el = /** @type {FormGroup} */ ( + await fixture(html`<${tag} disabled>${inputSlots}`) + ); expect(el.formElements.color.disabled).to.be.true; expect(el.formElements['hobbies[]'][0].disabled).to.be.true; expect(el.formElements['hobbies[]'][1].disabled).to.be.true; @@ -358,11 +361,13 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('does not propagate/override initial disabled value on nested form elements', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${tag} name="sub" disabled>${inputSlots} - `)); + `) + ); expect(el.disabled).to.equal(false); expect(el.formElements.sub.disabled).to.be.true; @@ -372,11 +377,13 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('can set initial modelValue on creation', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .modelValue=${{ lastName: 'Bar' }}> <${childTag} name="lastName"> - `)); + `) + ); expect(el.modelValue).to.eql({ lastName: 'Bar', @@ -384,11 +391,13 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('can set initial serializedValue on creation', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .modelValue=${{ lastName: 'Bar' }}> <${childTag} name="lastName"> - `)); + `) + ); expect(el.modelValue).to.eql({ lastName: 'Bar' }); }); @@ -409,13 +418,15 @@ export function runFormGroupMixinSuite(cfg = {}) { } } - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="color" .validators=${[ - new IsCat(), - ]} .modelValue=${'blue'}> + new IsCat(), + ]} .modelValue=${'blue'}> - `)); + `) + ); expect(el.formElements.color.validationStates.error.IsCat).to.be.true; }); @@ -442,13 +453,15 @@ export function runFormGroupMixinSuite(cfg = {}) { } } - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="color" .validators=${[ - new IsCat(), - ]} .modelValue=${'blue'}> + new IsCat(), + ]} .modelValue=${'blue'}> - `)); + `) + ); expect(el.validationStates.error.FormElementsHaveNoError).to.be.true; expect(el.formElements.color.validationStates.error.IsCat).to.be.true; @@ -470,14 +483,18 @@ export function runFormGroupMixinSuite(cfg = {}) { return hasError; } } - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .validators=${[new HasEvenNumberOfChildren()]}> <${childTag} id="c1" name="c1"> - `)); - const child2 = /** @type {FormGroup} */ (await fixture(html` + `) + ); + const child2 = /** @type {FormGroup} */ ( + await fixture(html` <${childTag} name="c2"> - `)); + `) + ); expect(el.validationStates.error.HasEvenNumberOfChildren).to.be.true; el.appendChild(child2); @@ -495,18 +512,18 @@ export function runFormGroupMixinSuite(cfg = {}) { describe('Interaction states', () => { it('has false states (dirty, touched, prefilled) on init', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const fieldset = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); expect(fieldset.dirty).to.equal(false, 'dirty'); expect(fieldset.touched).to.equal(false, 'touched'); expect(fieldset.prefilled).to.equal(false, 'prefilled'); }); it('sets dirty when value changed', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const fieldset = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); fieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'football' }; expect(fieldset.dirty).to.be.true; }); @@ -540,32 +557,38 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('becomes prefilled if all form elements are prefilled', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="input1" .modelValue="${'prefilled'}"> <${childTag} name="input2"> - `)); + `) + ); expect(el.prefilled).to.be.false; - const el2 = /** @type {FormGroup} */ (await fixture(html` + const el2 = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="input1" .modelValue="${'prefilled'}"> <${childTag} name="input2" .modelValue="${'prefilled'}"> - `)); + `) + ); expect(el2.prefilled).to.be.true; }); it(`becomes "touched" once the last element of a group becomes blurred by keyboard interaction (e.g. tabbing through the checkbox-group)`, async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="myGroup[]" label="Option 1" value="1"> <${childTag} name="myGroup[]" label="Option 2" value="2"> - `)); + `) + ); const button = /** @type {HTMLButtonElement} */ (await fixture(``)); @@ -582,22 +605,26 @@ export function runFormGroupMixinSuite(cfg = {}) { it(`becomes "touched" once the group as a whole becomes blurred via mouse interaction after keyboard interaction (e.g. focus is moved inside the group and user clicks somewhere outside the group)`, async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="input1"> <${childTag} name="input2"> - `)); - const el2 = /** @type {FormGroup} */ (await fixture(html` + `) + ); + const el2 = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="input1"> <${childTag} name="input2"> - `)); + `) + ); - const outside = /** @type {HTMLButtonElement} */ (await fixture( - html``, - )); + const outside = /** @type {HTMLButtonElement} */ ( + await fixture(html``) + ); outside.click(); expect(el.touched, 'unfocused fieldset should stay untouched').to.be.false; @@ -627,14 +654,16 @@ export function runFormGroupMixinSuite(cfg = {}) { } } - const outSideButton = /** @type {FormGroup} */ (await fixture( - html``, - )); - const el = /** @type {FormGroup} */ (await fixture(html` + const outSideButton = /** @type {FormGroup} */ ( + await fixture(html``) + ); + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .validators=${[new Input1IsTen()]}> <${childTag} name="input1" .validators=${[new IsNumber()]}> - `)); + `) + ); const input1 = /** @type {FormChild} */ (el.querySelector('[name=input1]')); input1.modelValue = 2; input1.focus(); @@ -657,15 +686,17 @@ export function runFormGroupMixinSuite(cfg = {}) { return hasError; } } - const outSideButton = /** @type {FormGroup} */ (await fixture( - html``, - )); - const el = /** @type {FormGroup} */ (await fixture(html` + const outSideButton = /** @type {FormGroup} */ ( + await fixture(html``) + ); + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .validators=${[new Input1IsTen()]}> <${childTag} name="input1" .validators=${[new IsNumber()]}> <${childTag} name="input2" .validators=${[new IsNumber()]}> - `)); + `) + ); const inputs = /** @type {FormChild[]} */ (Array.from(el.querySelectorAll(childTagString))); inputs[1].modelValue = 2; // make it dirty inputs[1].focus(); @@ -677,20 +708,24 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('does not become dirty when elements are prefilled', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .serializedValue="${{ input1: 'x', input2: 'y' }}"> <${childTag} name="input1" > <${childTag} name="input2"> - `)); + `) + ); expect(el.dirty).to.be.false; - const el2 = /** @type {FormGroup} */ (await fixture(html` + const el2 = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .modelValue="${{ input1: 'x', input2: 'y' }}"> <${childTag} name="input1" > <${childTag} name="input2"> - `)); + `) + ); expect(el2.dirty).to.be.false; }); }); @@ -698,9 +733,9 @@ export function runFormGroupMixinSuite(cfg = {}) { // TODO: this should be tested in FormGroupMixin describe('serializedValue', () => { it('use form elements serializedValue', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const fieldset = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); fieldset.formElements['hobbies[]'][0].serializer = /** @param {?} v */ v => `${v.value}-serialized`; fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'Bar' }; @@ -720,9 +755,9 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('treats names with ending [] as arrays', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const fieldset = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; @@ -742,21 +777,25 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('0 is a valid value to be serialized', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture(html` + const fieldset = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="price"> - `)); + `) + ); fieldset.formElements.price.modelValue = 0; expect(fieldset.serializedValue).to.deep.equal({ price: 0 }); }); it('allows for nested fieldsets', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture(html` + const fieldset = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="userData"> <${childTag} name="comment"> <${tag} name="newfieldset">${inputSlots} - `)); + `) + ); const newFieldset = /** @type {FormGroup} */ (fieldset.querySelector(tagString)); newFieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; newFieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; @@ -785,12 +824,14 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('does not serialize disabled values', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture(html` + const fieldset = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="custom[]"> <${childTag} name="custom[]"> - `)); + `) + ); fieldset.formElements['custom[]'][0].modelValue = 'custom 1'; fieldset.formElements['custom[]'][1].disabled = true; @@ -800,12 +841,14 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('will exclude form elements within a disabled fieldset', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture(html` + const fieldset = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="userData"> <${childTag} name="comment"> <${tag} name="newfieldset">${inputSlots} - `)); + `) + ); const newFieldset = /** @type {FormGroup} */ (fieldset.querySelector(tagString)); fieldset.formElements.comment.modelValue = 'Foo'; @@ -848,11 +891,13 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('updates the formElements keys when a name attribute changes', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture(html` + const fieldset = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="foo" .modelValue=${'qux'}> - `)); + `) + ); expect(fieldset.serializedValue.foo).to.equal('qux'); fieldset.formElements[0].name = 'bar'; await fieldset.updateComplete; @@ -863,11 +908,13 @@ export function runFormGroupMixinSuite(cfg = {}) { describe('Reset', () => { it('restores default values if changes were made', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} id="firstName" name="firstName" .modelValue="${'Foo'}"> - `)); + `) + ); await /** @type {FormChild} */ (el.querySelector(childTagString)).updateComplete; const input = /** @type {FormChild} */ (el.querySelector('#firstName')); @@ -882,11 +929,13 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('restores default values of arrays if changes were made', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} id="firstName" name="firstName[]" .modelValue="${'Foo'}"> - `)); + `) + ); await /** @type {FormChild} */ (el.querySelector(childTagString)).updateComplete; const input = /** @type {FormChild} */ (el.querySelector('#firstName')); @@ -901,13 +950,15 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('restores default values of a nested fieldset if changes were made', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${tag} id="name" name="name[]"> <${childTag} id="firstName" name="firstName" .modelValue="${'Foo'}"> - `)); + `) + ); await Promise.all([ /** @type {FormChild} */ (el.querySelector(tagString)).updateComplete, /** @type {FormChild} */ (el.querySelector(childTagString)).updateComplete, @@ -928,9 +979,9 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('clears interaction state', async () => { - const el = /** @type {FormGroup} */ (await fixture( - html`<${tag} touched dirty>${inputSlots}`, - )); + const el = /** @type {FormGroup} */ ( + await fixture(html`<${tag} touched dirty>${inputSlots}`) + ); // Safety check initially // @ts-ignore [allow-protected] in test el._setValueForAllFormElements('prefilled', true); @@ -957,9 +1008,9 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('clears submitted state', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const fieldset = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); fieldset.submitted = true; fieldset.resetGroup(); expect(fieldset.submitted).to.equal(false); @@ -999,12 +1050,14 @@ export function runFormGroupMixinSuite(cfg = {}) { } } - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} .validators=${[new ColorContainsA()]}> <${childTag} name="color" .validators=${[new IsCat()]}> <${childTag} name="color2"> - `)); + `) + ); expect(el.hasFeedbackFor).to.deep.equal(['error']); expect(el.validationStates.error.ColorContainsA).to.be.true; expect(el.formElements.color.hasFeedbackFor).to.deep.equal([]); @@ -1024,14 +1077,16 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('has access to `_initialModelValue` based on initial children states', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="child[]" .modelValue="${'foo1'}"> <${childTag} name="child[]" .modelValue="${'bar1'}"> - `)); + `) + ); await el.updateComplete; el.modelValue['child[]'] = ['foo2', 'bar2']; // @ts-ignore [allow-protected] in test @@ -1039,17 +1094,21 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('does not wrongly recompute `_initialModelValue` after dynamic changes of children', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> <${childTag} name="child[]" .modelValue="${'foo1'}"> - `)); + `) + ); el.modelValue['child[]'] = ['foo2']; - const childEl = /** @type {FormGroup} */ (await fixture(html` + const childEl = /** @type {FormGroup} */ ( + await fixture(html` <${childTag} name="child[]" .modelValue="${'bar1'}"> - `)); + `) + ); el.appendChild(childEl); // @ts-ignore [allow-protected] in test expect(el._initialModelValue['child[]']).to.eql(['foo1', 'bar1']); @@ -1057,14 +1116,16 @@ export function runFormGroupMixinSuite(cfg = {}) { describe('resetGroup method', () => { it('calls resetGroup on children fieldsets', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="parentFieldset"> <${tag} name="childFieldset"> <${childTag} name="child[]" .modelValue="${'foo1'}"> - `)); + `) + ); const childFieldsetEl = el.querySelector(tagString); // @ts-expect-error const resetGroupSpy = sinon.spy(childFieldsetEl, 'resetGroup'); @@ -1073,14 +1134,16 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('calls reset on children fields', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="parentFieldset"> <${tag} name="childFieldset"> <${childTag} name="child[]" .modelValue="${'foo1'}"> - `)); + `) + ); const childFieldsetEl = /** @type {FormChild} */ (el.querySelector(childTagString)); const resetSpy = sinon.spy(childFieldsetEl, 'reset'); el.resetGroup(); @@ -1090,14 +1153,16 @@ export function runFormGroupMixinSuite(cfg = {}) { describe('clearGroup method', () => { it('calls clearGroup on children fieldset', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="parentFieldset"> <${tag} name="childFieldset"> <${childTag} name="child[]" .modelValue="${'foo1'}"> - `)); + `) + ); const childFieldsetEl = el.querySelector(tagString); // @ts-expect-error const clearGroupSpy = sinon.spy(childFieldsetEl, 'clearGroup'); @@ -1106,14 +1171,16 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('calls clear on children fields', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="parentFieldset"> <${tag} name="childFieldset"> <${childTag} name="child[]" .modelValue="${'foo1'}"> - `)); + `) + ); const childFieldsetEl = /** @type {FormChild} */ (el.querySelector(childTagString)); const clearSpy = sinon.spy(childFieldsetEl, 'clear'); el.clearGroup(); @@ -1121,14 +1188,16 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('should clear the value of fields', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag} name="parentFieldset"> <${tag} name="childFieldset"> <${childTag} name="child" .modelValue="${'foo1'}"> - `)); + `) + ); el.clearGroup(); expect( /** @type {FormChild} */ (el.querySelector('[name="child"]')).modelValue, @@ -1139,9 +1208,9 @@ export function runFormGroupMixinSuite(cfg = {}) { describe('Accessibility', () => { it('has role="group" set', async () => { - const fieldset = /** @type {FormGroup} */ (await fixture( - html`<${tag}>${inputSlots}`, - )); + const fieldset = /** @type {FormGroup} */ ( + await fixture(html`<${tag}>${inputSlots}`) + ); fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' }; fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' }; fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' }; @@ -1152,15 +1221,17 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it('has an aria-labelledby from element with slot="label"', async () => { - const el = /** @type {FormGroup} */ (await fixture(html` + const el = /** @type {FormGroup} */ ( + await fixture(html` <${tag}> ${inputSlots} - `)); - const label = /** @type {HTMLElement} */ (Array.from(el.children).find( - child => child.slot === 'label', - )); + `) + ); + const label = /** @type {HTMLElement} */ ( + Array.from(el.children).find(child => child.slot === 'label') + ); expect(el.hasAttribute('aria-labelledby')).to.equal(true); expect(el.getAttribute('aria-labelledby')).contains(label.id); }); @@ -1204,13 +1275,15 @@ export function runFormGroupMixinSuite(cfg = {}) { it(`when rendering children right from the start, sets their values correctly based on prefilled model/seriazedValue`, async () => { - const el = /** @type {DynamicCWrapper} */ (await fixture(html` + const el = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .fields="${['firstName', 'lastName']}" .modelValue="${{ firstName: 'foo', lastName: 'bar' }}" > - `)); + `) + ); await el.updateComplete; const fieldset = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(tagString) @@ -1218,13 +1291,15 @@ export function runFormGroupMixinSuite(cfg = {}) { expect(fieldset.formElements[0].modelValue).to.equal('foo'); expect(fieldset.formElements[1].modelValue).to.equal('bar'); - const el2 = /** @type {DynamicCWrapper} */ (await fixture(html` + const el2 = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .fields="${['firstName', 'lastName']}" .serializedValue="${{ firstName: 'foo', lastName: 'bar' }}" > - `)); + `) + ); await el2.updateComplete; const fieldset2 = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el2.shadowRoot).querySelector(tagString) @@ -1235,10 +1310,12 @@ export function runFormGroupMixinSuite(cfg = {}) { it(`when rendering children delayed, sets their values correctly based on prefilled model/seriazedValue`, async () => { - const el = /** @type {DynamicCWrapper} */ (await fixture(html` + const el = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .modelValue="${{ firstName: 'foo', lastName: 'bar' }}"> - `)); + `) + ); await el.updateComplete; const fieldset = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(tagString) @@ -1248,10 +1325,12 @@ export function runFormGroupMixinSuite(cfg = {}) { expect(fieldset.formElements[0].modelValue).to.equal('foo'); expect(fieldset.formElements[1].modelValue).to.equal('bar'); - const el2 = /** @type {DynamicCWrapper} */ (await fixture(html` + const el2 = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .serializedValue="${{ firstName: 'foo', lastName: 'bar' }}"> - `)); + `) + ); await el2.updateComplete; const fieldset2 = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el2.shadowRoot).querySelector(tagString) @@ -1264,13 +1343,15 @@ export function runFormGroupMixinSuite(cfg = {}) { it(`when rendering children partly delayed, sets their values correctly based on prefilled model/seriazedValue`, async () => { - const el = /** @type {DynamicCWrapper} */ (await fixture(html` + const el = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .fields="${['firstName']}" .modelValue="${{ - firstName: 'foo', - lastName: 'bar', - }}"> + firstName: 'foo', + lastName: 'bar', + }}"> - `)); + `) + ); await el.updateComplete; const fieldset = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(tagString) @@ -1280,13 +1361,15 @@ export function runFormGroupMixinSuite(cfg = {}) { expect(fieldset.formElements[0].modelValue).to.equal('foo'); expect(fieldset.formElements[1].modelValue).to.equal('bar'); - const el2 = /** @type {DynamicCWrapper} */ (await fixture(html` + const el2 = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .fields="${['firstName']}" .serializedValue="${{ - firstName: 'foo', - lastName: 'bar', - }}"> + firstName: 'foo', + lastName: 'bar', + }}"> - `)); + `) + ); await el2.updateComplete; const fieldset2 = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el2.shadowRoot).querySelector(tagString) @@ -1305,13 +1388,15 @@ export function runFormGroupMixinSuite(cfg = {}) { expect(elm.prefilled).to.be.true; } - const el = /** @type {DynamicCWrapper} */ (await fixture(html` + const el = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .fields="${['firstName']}" .modelValue="${{ - firstName: 'foo', - lastName: 'bar', - }}"> + firstName: 'foo', + lastName: 'bar', + }}"> - `)); + `) + ); await el.updateComplete; const fieldset = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(tagString) @@ -1324,13 +1409,15 @@ export function runFormGroupMixinSuite(cfg = {}) { expectInteractionStatesToBeCorrectFor(fieldset.formElements[1]); expectInteractionStatesToBeCorrectFor(fieldset); - const el2 = /** @type {DynamicCWrapper} */ (await fixture(html` + const el2 = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .fields="${['firstName']}" .serializedValue="${{ - firstName: 'foo', - lastName: 'bar', - }}"> + firstName: 'foo', + lastName: 'bar', + }}"> - `)); + `) + ); await el2.updateComplete; const fieldset2 = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el2.shadowRoot).querySelector(tagString) @@ -1345,13 +1432,15 @@ export function runFormGroupMixinSuite(cfg = {}) { }); it(`prefilled children values take precedence over parent values`, async () => { - const el = /** @type {DynamicCWrapper} */ (await fixture(html` + const el = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .modelValue="${{ - firstName: 'foo', - lastName: 'bar', - }}"> + firstName: 'foo', + lastName: 'bar', + }}"> - `)); + `) + ); await el.updateComplete; const fieldset = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(tagString) @@ -1364,13 +1453,15 @@ export function runFormGroupMixinSuite(cfg = {}) { expect(fieldset.formElements[0].modelValue).to.equal('wins'); expect(fieldset.formElements[1].modelValue).to.equal('winsAsWell'); - const el2 = /** @type {DynamicCWrapper} */ (await fixture(html` + const el2 = /** @type {DynamicCWrapper} */ ( + await fixture(html` <${dynamicChildrenTag} .serializedValue="${{ - firstName: 'foo', - lastName: 'bar', - }}"> + firstName: 'foo', + lastName: 'bar', + }}"> - `)); + `) + ); await el2.updateComplete; const fieldset2 = /** @type {FormGroup} */ ( /** @type {ShadowRoot} */ (el2.shadowRoot).querySelector(tagString) diff --git a/packages/form-core/test/FocusMixin.test.js b/packages/form-core/test/FocusMixin.test.js index 7cf73626b..b32ef4619 100644 --- a/packages/form-core/test/FocusMixin.test.js +++ b/packages/form-core/test/FocusMixin.test.js @@ -1,9 +1,11 @@ import { LitElement } from '@lion/core'; -import { defineCE, expect, fixture, html, oneEvent, unsafeStatic } from '@open-wc/testing'; +import { defineCE, expect, fixture, oneEvent } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import sinon from 'sinon'; import { FocusMixin } from '../src/FocusMixin.js'; -const windowWithOptionalPolyfill = /** @type {Window & typeof globalThis & {applyFocusVisiblePolyfill?: function}} */ (window); +const windowWithOptionalPolyfill = + /** @type {Window & typeof globalThis & {applyFocusVisiblePolyfill?: function}} */ (window); /** * Checks two things: @@ -74,9 +76,11 @@ describe('FocusMixin', () => { const tag = unsafeStatic(tagString); it('focuses/blurs the underlaying native element on .focus()/.blur()', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const { _focusableNode } = el; @@ -87,9 +91,11 @@ describe('FocusMixin', () => { }); it('has an attribute focused when focused', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); el.focus(); await el.updateComplete; @@ -101,9 +107,11 @@ describe('FocusMixin', () => { }); it('becomes focused/blurred if the native element gets focused/blurred', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const { _focusableNode } = el; @@ -115,9 +123,11 @@ describe('FocusMixin', () => { }); it('dispatches [focus, blur] events', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); setTimeout(() => el.focus()); const focusEv = await oneEvent(el, 'focus'); expect(focusEv).to.be.instanceOf(Event); @@ -137,9 +147,11 @@ describe('FocusMixin', () => { }); it('dispatches [focusin, focusout] events with { bubbles: true, composed: true }', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); setTimeout(() => el.focus()); const focusinEv = await oneEvent(el, 'focusin'); expect(focusinEv).to.be.instanceOf(Event); @@ -160,9 +172,11 @@ describe('FocusMixin', () => { describe('Having :focus-visible within', () => { it('sets focusedVisible to true when focusable element matches :focus-visible', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const { _focusableNode } = el; @@ -204,9 +218,11 @@ describe('FocusMixin', () => { }); it('has an attribute focused-visible when focusedVisible is true', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const { _focusableNode } = el; @@ -251,9 +267,11 @@ describe('FocusMixin', () => { }); it('sets focusedVisible to true when focusable element if :focus-visible polyfill is loaded', async () => { - const el = /** @type {Focusable} */ (await fixture(html` + const el = /** @type {Focusable} */ ( + await fixture(html` <${tag}> - `)); + `) + ); // @ts-ignore [allow-protected] in test const { _focusableNode } = el; diff --git a/packages/form-core/test/FormControlMixin.test.js b/packages/form-core/test/FormControlMixin.test.js index ed3cd0a6b..0fcd835fa 100644 --- a/packages/form-core/test/FormControlMixin.test.js +++ b/packages/form-core/test/FormControlMixin.test.js @@ -1,4 +1,5 @@ -import { expect, html, defineCE, unsafeStatic, fixture } from '@open-wc/testing'; +import { expect, defineCE, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { LitElement } from '@lion/core'; import { getFormControlMembers } from '@lion/form-core/test-helpers'; import sinon from 'sinon'; @@ -30,108 +31,130 @@ describe('FormControlMixin', () => { describe('Label and helpText api', () => { it('has a label', async () => { - const elAttr = /** @type {FormControlMixinClass} */ (await fixture(html` + const elAttr = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag} label="Email address">${inputSlot} - `)); + `) + ); expect(elAttr.label).to.equal('Email address', 'as an attribute'); - const elProp = /** @type {FormControlMixinClass} */ (await fixture(html` + const elProp = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag} .label=${'Email address'} >${inputSlot} - `)); + `) + ); expect(elProp.label).to.equal('Email address', 'as a property'); - const elElem = /** @type {FormControlMixinClass} */ (await fixture(html` + const elElem = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag}> ${inputSlot} - `)); + `) + ); expect(elElem.label).to.equal('Email address', 'as an element'); }); it('has a label that supports inner html', async () => { - const el = /** @type {FormControlMixinClass} */ (await fixture(html` + const el = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag}> ${inputSlot} - `)); + `) + ); expect(el.label).to.equal('Email address'); }); it('only takes label of direct child', async () => { - const el = /** @type {FormControlMixinClass} */ (await fixture(html` + const el = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag}> <${tag} label="Email address"> ${inputSlot} - `)); + `) + ); expect(el.label).to.equal(''); }); it('can have a help-text', async () => { - const elAttr = /** @type {FormControlMixinClass} */ (await fixture(html` + const elAttr = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag} help-text="We will not send you any spam">${inputSlot} - `)); + `) + ); expect(elAttr.helpText).to.equal('We will not send you any spam', 'as an attribute'); - const elProp = /** @type {FormControlMixinClass} */ (await fixture(html` + const elProp = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag} .helpText=${'We will not send you any spam'} >${inputSlot} - `)); + `) + ); expect(elProp.helpText).to.equal('We will not send you any spam', 'as a property'); - const elElem = /** @type {FormControlMixinClass} */ (await fixture(html` + const elElem = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag}>
We will not send you any spam
${inputSlot} - `)); + `) + ); expect(elElem.helpText).to.equal('We will not send you any spam', 'as an element'); }); it('can have a help-text that supports inner html', async () => { - const el = /** @type {FormControlMixinClass} */ (await fixture(html` + const el = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag}>
We will not send you any spam
${inputSlot} - `)); + `) + ); expect(el.helpText).to.equal('We will not send you any spam'); }); it('only takes help-text of direct child', async () => { - const el = /** @type {FormControlMixinClass} */ (await fixture(html` + const el = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag}> <${tag} help-text="We will not send you any spam"> ${inputSlot} - `)); + `) + ); expect(el.helpText).to.equal(''); }); }); describe('Accessibility', () => { it('does not duplicate aria-describedby and aria-labelledby ids on reconnect', async () => { - const wrapper = /** @type {HTMLElement} */ (await fixture(html` + const wrapper = /** @type {HTMLElement} */ ( + await fixture(html`
<${tag} help-text="This element will be disconnected/reconnected">${inputSlot}
- `)); + `) + ); const el = /** @type {FormControlMixinClass} */ (wrapper.querySelector(tagString)); const { _inputNode } = getFormControlMembers(el); const labelIdsBefore = /** @type {string} */ (_inputNode.getAttribute('aria-labelledby')); - const descriptionIdsBefore = /** @type {string} */ (_inputNode.getAttribute( - 'aria-describedby', - )); + const descriptionIdsBefore = /** @type {string} */ ( + _inputNode.getAttribute('aria-describedby') + ); // Reconnect wrapper.removeChild(el); wrapper.appendChild(el); const labelIdsAfter = /** @type {string} */ (_inputNode.getAttribute('aria-labelledby')); - const descriptionIdsAfter = /** @type {string} */ (_inputNode.getAttribute( - 'aria-describedby', - )); + const descriptionIdsAfter = /** @type {string} */ ( + _inputNode.getAttribute('aria-describedby') + ); expect(labelIdsBefore).to.equal(labelIdsAfter); expect(descriptionIdsBefore).to.equal(descriptionIdsAfter); @@ -139,11 +162,13 @@ describe('FormControlMixin', () => { it('clicking the label should call `_onLabelClick`', async () => { const spy = sinon.spy(); - const el = /** @type {FormControlMixinClass} */ (await fixture(html` + const el = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${tag} ._onLabelClick="${spy}"> ${inputSlot} - `)); + `) + ); const { _labelNode } = getFormControlMembers(el); expect(spy).to.not.have.been.called; @@ -232,7 +257,8 @@ describe('FormControlMixin', () => { describe('Adding extra labels and descriptions', () => { it(`supports centrally orchestrated labels/descriptions via addToAriaLabelledBy() / removeFromAriaLabelledBy() / addToAriaDescribedBy() / removeFromAriaDescribedBy()`, async () => { - const wrapper = /** @type {HTMLElement} */ (await fixture(html` + const wrapper = /** @type {HTMLElement} */ ( + await fixture(html`
<${tag}> ${inputSlot} @@ -241,7 +267,8 @@ describe('FormControlMixin', () => {
This also needs to be read whenever the input has focus
Same for this
-
`)); +
`) + ); const el = /** @type {FormControlMixinClass} */ (wrapper.querySelector(tagString)); const { _inputNode } = getFormControlMembers(el); @@ -257,9 +284,9 @@ describe('FormControlMixin', () => { expect(/** @type {string} */ (_inputNode.getAttribute('aria-labelledby'))).to.contain( `label-${inputId}`, ); - const additionalLabel = /** @type {HTMLElement} */ (wrapper.querySelector( - '#additionalLabel', - )); + const additionalLabel = /** @type {HTMLElement} */ ( + wrapper.querySelector('#additionalLabel') + ); el.addToAriaLabelledBy(additionalLabel); await el.updateComplete; let labelledbyAttr = /** @type {string} */ (_inputNode.getAttribute('aria-labelledby')); @@ -392,13 +419,15 @@ describe('FormControlMixin', () => { it('redispatches one event from host', async () => { const formSpy = sinon.spy(); const fieldsetSpy = sinon.spy(); - const formEl = /** @type {FormControlMixinClass} */ (await fixture(html` + const formEl = /** @type {FormControlMixinClass} */ ( + await fixture(html` <${groupTag} name="form" ._repropagationRole=${'form-group'} @model-value-changed=${formSpy}> <${groupTag} name="fieldset" ._repropagationRole=${'form-group'} @model-value-changed=${fieldsetSpy}> <${tag} name="field"> - `)); + `) + ); const fieldsetEl = formEl.querySelector('[name=fieldset]'); expect(fieldsetSpy.callCount).to.equal(1); diff --git a/packages/form-core/test/FormRegistrationMixins.test.js b/packages/form-core/test/FormRegistrationMixins.test.js index 06a7828d1..b133b305f 100644 --- a/packages/form-core/test/FormRegistrationMixins.test.js +++ b/packages/form-core/test/FormRegistrationMixins.test.js @@ -1,5 +1,5 @@ import { LitElement } from '@lion/core'; -import { html } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { runRegistrationSuite } from '../test-suites/FormRegistrationMixins.suite.js'; runRegistrationSuite({ diff --git a/packages/form-core/test/lion-field.test.js b/packages/form-core/test/lion-field.test.js index ee974616a..0e666c5ea 100644 --- a/packages/form-core/test/lion-field.test.js +++ b/packages/form-core/test/lion-field.test.js @@ -2,14 +2,8 @@ import { unsafeHTML } from '@lion/core'; import { localize } from '@lion/localize'; import { localizeTearDown } from '@lion/localize/test-helpers'; import { Required, Validator } from '@lion/form-core'; -import { - expect, - fixture, - html, - triggerBlurFor, - triggerFocusFor, - unsafeStatic, -} from '@open-wc/testing'; +import { expect, fixture, triggerBlurFor, triggerFocusFor } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { getFormControlMembers } from '@lion/form-core/test-helpers'; import sinon from 'sinon'; import '@lion/form-core/define-field'; @@ -60,31 +54,31 @@ describe('', () => { }); it(`has a fieldName based on the label`, async () => { - const el1 = /** @type {LionField} */ (await fixture( - html`<${tag} label="foo">${inputSlot}`, - )); + const el1 = /** @type {LionField} */ ( + await fixture(html`<${tag} label="foo">${inputSlot}`) + ); const { _labelNode: _labelNode1 } = getFormControlMembers(el1); expect(el1.fieldName).to.equal(_labelNode1.textContent); - const el2 = /** @type {LionField} */ (await fixture( - html`<${tag}>${inputSlot}`, - )); + const el2 = /** @type {LionField} */ ( + await fixture(html`<${tag}>${inputSlot}`) + ); const { _labelNode: _labelNode2 } = getFormControlMembers(el2); expect(el2.fieldName).to.equal(_labelNode2.textContent); }); it(`has a fieldName based on the name if no label exists`, async () => { - const el = /** @type {LionField} */ (await fixture( - html`<${tag} name="foo">${inputSlot}`, - )); + const el = /** @type {LionField} */ ( + await fixture(html`<${tag} name="foo">${inputSlot}`) + ); expect(el.fieldName).to.equal(el.name); }); it(`can override fieldName`, async () => { - const el = /** @type {LionField} */ (await fixture( - html`<${tag} label="foo" .fieldName="${'bar'}">${inputSlot}`, - )); + const el = /** @type {LionField} */ ( + await fixture(html`<${tag} label="foo" .fieldName="${'bar'}">${inputSlot}`) + ); // @ts-ignore [allow-protected] in test expect(el.__fieldName).to.equal(el.fieldName); }); @@ -134,9 +128,9 @@ describe('', () => { }); it('can be cleared which erases value, validation and interaction states', async () => { - const el = /** @type {LionField} */ (await fixture( - html`<${tag} value="Some value from attribute">${inputSlot}`, - )); + const el = /** @type {LionField} */ ( + await fixture(html`<${tag} value="Some value from attribute">${inputSlot}`) + ); el.clear(); expect(el.modelValue).to.equal(''); el.modelValue = 'Some value from property'; @@ -146,10 +140,12 @@ describe('', () => { }); it('can be reset which restores original modelValue', async () => { - const el = /** @type {LionField} */ (await fixture(html` + const el = /** @type {LionField} */ ( + await fixture(html` <${tag} .modelValue="${'foo'}"> ${inputSlot} - `)); + `) + ); expect(el._initialModelValue).to.equal('foo'); el.modelValue = 'bar'; el.reset(); @@ -171,13 +167,15 @@ describe('', () => {
[feedback] ~~~`, async () => { - const el = /** @type {LionField} */ (await fixture(html`<${tag}> + const el = /** @type {LionField} */ ( + await fixture(html`<${tag}> ${inputSlot} Enter your Name No name entered - `)); + `) + ); const nativeInput = getSlot(el, 'input'); // @ts-ignore allow protected accessors in tests const inputId = el._inputId; @@ -188,14 +186,16 @@ describe('', () => { it(`allows additional slots (prefix, suffix, before, after) to be included in labelledby (via attribute data-label) and in describedby (via attribute data-description)`, async () => { - const el = /** @type {LionField} */ (await fixture(html`<${tag}> + const el = /** @type {LionField} */ ( + await fixture(html`<${tag}> ${inputSlot} [before] [after] [prefix] [suffix] - `)); + `) + ); const nativeInput = getSlot(el, 'input'); // @ts-ignore allow protected accessors in tests @@ -234,14 +234,16 @@ describe('', () => { return result; } }; - const el = /** @type {LionField} */ (await fixture(html` + const el = /** @type {LionField} */ ( + await fixture(html` <${tag} .validators=${[new HasX()]} .modelValue=${'a@b.nl'} > ${inputSlot} - `)); + `) + ); /** * @param {import("../index.js").LionField} _sceneEl @@ -303,7 +305,8 @@ describe('', () => { return result; } }; - const disabledEl = /** @type {LionField} */ (await fixture(html` + const disabledEl = /** @type {LionField} */ ( + await fixture(html` <${tag} disabled .validators=${[new HasX()]} @@ -311,15 +314,18 @@ describe('', () => { > ${inputSlot} - `)); - const el = /** @type {LionField} */ (await fixture(html` + `) + ); + const el = /** @type {LionField} */ ( + await fixture(html` <${tag} .validators=${[new HasX()]} .modelValue=${'a@b.nl'} > ${inputSlot} - `)); + `) + ); expect(el.hasFeedbackFor).to.deep.equal(['error']); expect(el.validationStates.error.HasX).to.exist; @@ -329,11 +335,13 @@ describe('', () => { }); it('can be required', async () => { - const el = /** @type {LionField} */ (await fixture(html` + const el = /** @type {LionField} */ ( + await fixture(html` <${tag} .validators=${[new Required()]} >${inputSlot} - `)); + `) + ); expect(el.hasFeedbackFor).to.deep.equal(['error']); expect(el.validationStates.error.Required).to.exist; el.modelValue = 'cat'; @@ -356,13 +364,15 @@ describe('', () => { return hasError; } }; - const el = /** @type {LionField} */ (await fixture(html` + const el = /** @type {LionField} */ ( + await fixture(html` <${tag} .modelValue=${'init-string'} .formatter=${formatterSpy} .validators=${[new Bar()]} >${inputSlot} - `)); + `) + ); expect(formatterSpy.callCount).to.equal(0); expect(el.formattedValue).to.equal('init-string'); @@ -379,7 +389,8 @@ describe('', () => { describe(`Content projection`, () => { it('renders correctly all slot elements in light DOM', async () => { - const el = /** @type {LionField} */ (await fixture(html` + const el = /** @type {LionField} */ ( + await fixture(html` <${tag}> ${inputSlot} @@ -390,7 +401,8 @@ describe('', () => { [suffix] [feedback] - `)); + `) + ); const names = [ 'label', @@ -405,10 +417,9 @@ describe('', () => { names.forEach(slotName => { const slotLight = /** @type {HTMLElement} */ (el.querySelector(`[slot="${slotName}"]`)); slotLight.setAttribute('test-me', 'ok'); - // @ts-expect-error - const slot = /** @type {ShadowHTMLElement} */ (el.shadowRoot.querySelector( - `slot[name="${slotName}"]`, - )); + const slot = /** @type {ShadowHTMLElement} */ ( + /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(`slot[name="${slotName}"]`) + ); const assignedNodes = slot.assignedNodes(); expect(assignedNodes.length).to.equal(1); expect(assignedNodes[0].getAttribute('test-me')).to.equal('ok'); diff --git a/packages/form-core/test/utils/SyncUpdatableMixin.test.js b/packages/form-core/test/utils/SyncUpdatableMixin.test.js index e93c34456..777edc563 100644 --- a/packages/form-core/test/utils/SyncUpdatableMixin.test.js +++ b/packages/form-core/test/utils/SyncUpdatableMixin.test.js @@ -1,5 +1,6 @@ import { LitElement } from '@lion/core'; -import { defineCE, expect, fixture, fixtureSync, html, unsafeStatic } from '@open-wc/testing'; +import { defineCE, expect, fixture, fixtureSync } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import sinon from 'sinon'; import { SyncUpdatableMixin } from '../../src/utils/SyncUpdatableMixin.js'; @@ -43,9 +44,9 @@ describe('SyncUpdatableMixin', () => { const tagString = defineCE(UpdatableImplementation); const tag = unsafeStatic(tagString); - const el = /** @type {UpdatableImplementation} */ (fixtureSync( - html`<${tag} prop-b="b">`, - )); + const el = /** @type {UpdatableImplementation} */ ( + fixtureSync(html`<${tag} prop-b="b">`) + ); // Getters setters work as expected, without running property effects expect(el.propA).to.equal('init-a'); @@ -102,9 +103,9 @@ describe('SyncUpdatableMixin', () => { const tagString = defineCE(UpdatableImplementation); const tag = unsafeStatic(tagString); - const el = /** @type {UpdatableImplementation} */ (fixtureSync( - html`<${tag} prop-b="b" .propA="${'a'}">`, - )); + const el = /** @type {UpdatableImplementation} */ ( + fixtureSync(html`<${tag} prop-b="b" .propA="${'a'}">`) + ); // Derived expect(el.derived).to.be.undefined; @@ -114,19 +115,19 @@ describe('SyncUpdatableMixin', () => { expect(el.derived).to.equal('ab'); expect(hasCalledRunPropertyEffect).to.be.true; - const el2 = /** @type {UpdatableImplementation} */ (await fixture( - html`<${tag} .propA="${'a'}">`, - )); + const el2 = /** @type {UpdatableImplementation} */ ( + await fixture(html`<${tag} .propA="${'a'}">`) + ); expect(el2.derived).to.equal('ainit-b'); - const el3 = /** @type {UpdatableImplementation} */ (await fixture( - html`<${tag} .propB="${'b'}">`, - )); + const el3 = /** @type {UpdatableImplementation} */ ( + await fixture(html`<${tag} .propB="${'b'}">`) + ); expect(el3.derived).to.equal('init-ab'); - const el4 = /** @type {UpdatableImplementation} */ (await fixture( - html`<${tag} .propA=${'a'} .propB="${'b'}">`, - )); + const el4 = /** @type {UpdatableImplementation} */ ( + await fixture(html`<${tag} .propA=${'a'} .propB="${'b'}">`) + ); expect(el4.derived).to.equal('ab'); }); @@ -150,8 +151,8 @@ describe('SyncUpdatableMixin', () => { * @param {string} name * @param {*} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (name === 'prop') { propChangedCount += 1; } @@ -223,9 +224,9 @@ describe('SyncUpdatableMixin', () => { const tagString = defineCE(UpdatableImplementation); const tag = unsafeStatic(tagString); - const el = /** @type {UpdatableImplementation} */ (fixtureSync( - html`<${tag} prop-b="b" .propA="${'a'}">`, - )); + const el = /** @type {UpdatableImplementation} */ ( + fixtureSync(html`<${tag} prop-b="b" .propA="${'a'}">`) + ); const spy = sinon.spy(el, '_runPropertyEffect'); expect(spy.callCount).to.equal(0); diff --git a/packages/form-core/test/utils/getAriaElementsInRightDomOrder.test.js b/packages/form-core/test/utils/getAriaElementsInRightDomOrder.test.js index 0607f36eb..0adbdaf51 100644 --- a/packages/form-core/test/utils/getAriaElementsInRightDomOrder.test.js +++ b/packages/form-core/test/utils/getAriaElementsInRightDomOrder.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import { browserDetection } from '@lion/core'; import { getAriaElementsInRightDomOrder } from '../../src/utils/getAriaElementsInRightDomOrder.js'; diff --git a/packages/form-core/test/validate/Required.test.js b/packages/form-core/test/validate/Required.test.js index 81a74103f..24cdf9902 100644 --- a/packages/form-core/test/validate/Required.test.js +++ b/packages/form-core/test/validate/Required.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html, unsafeStatic, defineCE } from '@open-wc/testing'; +import { expect, fixture, defineCE } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { LionField } from '@lion/form-core'; import { getFormControlMembers } from '@lion/form-core/test-helpers'; import { Required } from '../../src/validate/validators/Required.js'; @@ -31,9 +32,9 @@ describe('Required validation', async () => { const validator = new Required(); it('get aria-required attribute if element is part of the right tag names', async () => { - const el = /** @type {FormControlHost & HTMLElement} */ (await fixture( - html`<${tag}>`, - )); + const el = /** @type {FormControlHost & HTMLElement} */ ( + await fixture(html`<${tag}>`) + ); Required._compatibleTags.forEach(tagName => { inputNodeTag = /** @type {HTMLElementWithValue} */ (document.createElement(tagName)); @@ -53,9 +54,9 @@ describe('Required validation', async () => { expect(_inputNode).to.not.have.attribute('aria-required'); }); it('get aria-required attribute if element is part of the right roles', async () => { - const el = /** @type {FormControlHost & HTMLElement} */ (await fixture( - html`<${tag}>`, - )); + const el = /** @type {FormControlHost & HTMLElement} */ ( + await fixture(html`<${tag}>`) + ); Required._compatibleRoles.forEach(role => { // @ts-ignore diff --git a/packages/form-core/test/validate/Validator.test.js b/packages/form-core/test/validate/Validator.test.js index 5c3eca0ef..5ef5f6e29 100644 --- a/packages/form-core/test/validate/Validator.test.js +++ b/packages/form-core/test/validate/Validator.test.js @@ -1,5 +1,6 @@ import { LitElement } from '@lion/core'; -import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing'; +import { defineCE, expect, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import sinon from 'sinon'; import { ValidateMixin } from '../../src/validate/ValidateMixin.js'; import { Validator } from '../../src/validate/Validator.js'; @@ -171,9 +172,11 @@ describe('Validator', () => { const connectSpy = sinon.spy(myVal, 'onFormControlConnect'); const disconnectSpy = sinon.spy(myVal, 'onFormControlDisconnect'); - const el = /** @type {ValidateElement} */ (await fixture(html` + const el = /** @type {ValidateElement} */ ( + await fixture(html` <${tag} .validators=${[myVal]}>${lightDom} - `)); + `) + ); expect(connectSpy.callCount).to.equal(1); expect(connectSpy.calledWith(el)).to.equal(true); diff --git a/packages/form-core/test/validate/lion-validation-feedback.test.js b/packages/form-core/test/validate/lion-validation-feedback.test.js index 68028496c..814ae020e 100644 --- a/packages/form-core/test/validate/lion-validation-feedback.test.js +++ b/packages/form-core/test/validate/lion-validation-feedback.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-vars, no-param-reassign */ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import '@lion/form-core/define-validation-feedback'; import { AlwaysInvalid, AlwaysValid } from '../../test-helpers/index.js'; @@ -10,9 +11,9 @@ import { AlwaysInvalid, AlwaysValid } from '../../test-helpers/index.js'; describe('lion-validation-feedback', () => { it('renders a validation message', async () => { - const el = /** @type {LionValidationFeedback} */ (await fixture( - html``, - )); + const el = /** @type {LionValidationFeedback} */ ( + await fixture(html``) + ); expect(el).shadowDom.to.equal(''); el.feedbackData = [{ message: 'hello', type: 'error', validator: new AlwaysInvalid() }]; await el.updateComplete; @@ -20,9 +21,9 @@ describe('lion-validation-feedback', () => { }); it('renders the validation type attribute', async () => { - const el = /** @type {LionValidationFeedback} */ (await fixture( - html``, - )); + const el = /** @type {LionValidationFeedback} */ ( + await fixture(html``) + ); el.feedbackData = [{ message: 'hello', type: 'error', validator: new AlwaysInvalid() }]; await el.updateComplete; expect(el.getAttribute('type')).to.equal('error'); @@ -33,9 +34,9 @@ describe('lion-validation-feedback', () => { }); it('success message clears after 3s', async () => { - const el = /** @type {LionValidationFeedback} */ (await fixture( - html``, - )); + const el = /** @type {LionValidationFeedback} */ ( + await fixture(html``) + ); const clock = sinon.useFakeTimers(); @@ -55,9 +56,9 @@ describe('lion-validation-feedback', () => { }); it('does not clear error messages', async () => { - const el = /** @type {LionValidationFeedback} */ (await fixture( - html``, - )); + const el = /** @type {LionValidationFeedback} */ ( + await fixture(html``) + ); const clock = sinon.useFakeTimers(); diff --git a/packages/form-core/types/FormatMixinTypes.d.ts b/packages/form-core/types/FormatMixinTypes.d.ts index 848080467..1e0c668bf 100644 --- a/packages/form-core/types/FormatMixinTypes.d.ts +++ b/packages/form-core/types/FormatMixinTypes.d.ts @@ -1,5 +1,5 @@ import { Constructor } from '@open-wc/dedupe-mixin'; -import { BooleanAttributePart, LitElement } from '@lion/core'; +import { LitElement } from '@lion/core'; import { FormatNumberOptions } from '@lion/localize/types/LocalizeMixinTypes'; import { ValidateHost } from './validate/ValidateMixinTypes'; import { FormControlHost } from './FormControlMixinTypes'; diff --git a/packages/form-core/types/choice-group/ChoiceInputMixinTypes.d.ts b/packages/form-core/types/choice-group/ChoiceInputMixinTypes.d.ts index c3c91577c..ef8033d19 100644 --- a/packages/form-core/types/choice-group/ChoiceInputMixinTypes.d.ts +++ b/packages/form-core/types/choice-group/ChoiceInputMixinTypes.d.ts @@ -34,7 +34,7 @@ export declare class ChoiceInputHost { protected get _inputNode(): HTMLElement; protected _proxyInputEvent(): void; - protected requestUpdateInternal(name: string, oldValue: any): void; + protected requestUpdate(name: string, oldValue: any): void; protected _choiceGraphicTemplate(): TemplateResult; protected _afterTemplate(): TemplateResult; protected _preventDuplicateLabelClick(ev: Event): void; diff --git a/packages/form-core/types/utils/SyncUpdatableMixinTypes.d.ts b/packages/form-core/types/utils/SyncUpdatableMixinTypes.d.ts index 10e4b63cb..092b9580e 100644 --- a/packages/form-core/types/utils/SyncUpdatableMixinTypes.d.ts +++ b/packages/form-core/types/utils/SyncUpdatableMixinTypes.d.ts @@ -10,7 +10,7 @@ export declare interface SyncUpdatableNamespace { export declare class SyncUpdatableHost { /** - * An abstraction that has the exact same api as `requestUpdateInternal`, but taking + * An abstraction that has the exact same api as `requestUpdate`, but taking * into account: * - [member order independence](https://github.com/webcomponents/gold-standard/wiki/Member-Order-Independence) * - property effects start when all (light) dom has initialized (on firstUpdated) @@ -18,7 +18,7 @@ export declare class SyncUpdatableHost { * - compatible with propertyAccessor.`hasChanged`: no manual checks needed or accidentally * run property effects / events when no change happened * effects when values didn't change - * All code previously present in requestUpdateInternal can be placed in this method. + * All code previously present in requestUpdate can be placed in this method. * @param {string} name * @param {*} oldValue */ diff --git a/packages/form-integrations/test/dialog-integrations.test.js b/packages/form-integrations/test/dialog-integrations.test.js index a46106082..09c363b3c 100644 --- a/packages/form-integrations/test/dialog-integrations.test.js +++ b/packages/form-integrations/test/dialog-integrations.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { getAllTagNames } from './helpers/helpers.js'; import './helpers/umbrella-form.js'; import '@lion/dialog/define'; diff --git a/packages/form-integrations/test/form-group-methods.test.js b/packages/form-integrations/test/form-group-methods.test.js index 86020e0af..72ba719c5 100644 --- a/packages/form-integrations/test/form-group-methods.test.js +++ b/packages/form-integrations/test/form-group-methods.test.js @@ -1,4 +1,5 @@ -import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; +import { elementUpdated, expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import './helpers/umbrella-form.js'; import { getAllFieldsAndFormGroups } from './helpers/helpers.js'; @@ -81,9 +82,11 @@ describe(`Submitting/Resetting/Clearing Form`, async () => { }); it('calling resetGroup() should reset all metadata (interaction states and initial values)', async () => { - const el = /** @type {UmbrellaForm} */ (await fixture( - html``, - )); + const el = /** @type {UmbrellaForm} */ ( + await fixture( + html``, + ) + ); await el.updateComplete; const formEl = el._lionFormNode; @@ -125,9 +128,11 @@ describe(`Submitting/Resetting/Clearing Form`, async () => { // Wait till ListboxMixin properly clears it('calling clearGroup() should clear all fields', async () => { - const el = /** @type {UmbrellaForm} */ (await fixture( - html``, - )); + const el = /** @type {UmbrellaForm} */ ( + await fixture( + html``, + ) + ); await el.updateComplete; const formEl = el._lionFormNode; diff --git a/packages/form-integrations/test/form-integrations.test.js b/packages/form-integrations/test/form-integrations.test.js index bce4aa2b0..04abe1680 100644 --- a/packages/form-integrations/test/form-integrations.test.js +++ b/packages/form-integrations/test/form-integrations.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { getAllTagNames } from './helpers/helpers.js'; import './helpers/umbrella-form.js'; @@ -64,28 +65,30 @@ describe('Form Integrations', () => { describe('Form Integrations', () => { it('does not become dirty when elements are prefilled', async () => { - const el = /** @type {UmbrellaForm} */ (await fixture( - html``, - )); + const el = /** @type {UmbrellaForm} */ ( + await fixture( + html``, + ) + ); await el._lionFormNode.initComplete; expect(el._lionFormNode.dirty).to.be.false; diff --git a/packages/form-integrations/test/form-validation-integrations.test.js b/packages/form-integrations/test/form-validation-integrations.test.js index 675539a26..cf06c28bc 100644 --- a/packages/form-integrations/test/form-validation-integrations.test.js +++ b/packages/form-integrations/test/form-validation-integrations.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing'; +import { expect, fixture, defineCE } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { Required, DefaultSuccess, Validator } from '@lion/form-core'; import { loadDefaultFeedbackMessages } from '@lion/validate-messages'; import { LionInput } from '@lion/input'; @@ -41,7 +42,8 @@ describe('Form Validation Integrations', () => { } const elTagString = defineCE(ValidateElementCustomTypes); const elTag = unsafeStatic(elTagString); - const el = /** @type {ValidateElementCustomTypes} */ (await fixture(html` + const el = /** @type {ValidateElementCustomTypes} */ ( + await fixture(html` <${elTag} .validators=${[ new Required(null, { getMessage: () => 'error' }), @@ -49,7 +51,8 @@ describe('Form Validation Integrations', () => { new DefaultSuccess(), ]} >${lightDom} - `)); + `) + ); const { _feedbackNode } = getFormControlMembers(el); expect(_feedbackNode.feedbackData?.length).to.equal(0); diff --git a/packages/form-integrations/test/model-value-consistency.test.js b/packages/form-integrations/test/model-value-consistency.test.js index ccdb0ce40..c4f388334 100644 --- a/packages/form-integrations/test/model-value-consistency.test.js +++ b/packages/form-integrations/test/model-value-consistency.test.js @@ -1,4 +1,5 @@ -import { expect, html, unsafeStatic, fixture } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; // eslint-disable-next-line import/no-extraneous-dependencies import sinon from 'sinon'; @@ -111,9 +112,9 @@ const choiceDispatchesCountOnInteraction = (tagname, count) => { const tag = unsafeStatic(tagname); const spy = sinon.spy(); it(getInteractionTitle(count), async () => { - const el = /** @type {HTMLElement & {checked: boolean}} */ (await fixture( - html`<${tag} .choiceValue="${'option'}">`, - )); + const el = /** @type {HTMLElement & {checked: boolean}} */ ( + await fixture(html`<${tag} .choiceValue="${'option'}">`) + ); el.addEventListener('model-value-changed', spy); el.checked = true; expect(spy.callCount).to.equal(count); @@ -161,17 +162,17 @@ const choiceGroupDispatchesCountOnInteraction = (groupTagname, itemTagname, coun `); el.addEventListener('model-value-changed', spy); - const option2 = /** @type {HTMLElement & {checked: boolean}} */ (el.querySelector( - `${itemTagname}:nth-child(2)`, - )); + const option2 = /** @type {HTMLElement & {checked: boolean}} */ ( + el.querySelector(`${itemTagname}:nth-child(2)`) + ); option2.checked = true; expect(spy.callCount).to.equal(count); spy.resetHistory(); - const option3 = /** @type {HTMLElement & {checked: boolean}} */ (el.querySelector( - `${itemTagname}:nth-child(3)`, - )); + const option3 = /** @type {HTMLElement & {checked: boolean}} */ ( + el.querySelector(`${itemTagname}:nth-child(3)`) + ); option3.checked = true; expect(spy.callCount).to.equal(count); }); @@ -233,15 +234,17 @@ describe('lion-select', () => { it(getInteractionTitle(interactionCount), async () => { const spy = sinon.spy(); - const el = /** @type {LionSelect} */ (await fixture(html` - - - - `)); + const el = /** @type {LionSelect} */ ( + await fixture(html` + + + + `) + ); el.addEventListener('model-value-changed', spy); const option2 = /** @type {HTMLOptionElement} */ (el.querySelector('option:nth-child(2)')); @@ -464,9 +467,10 @@ describe('detail.isTriggeredByUser', () => { } const name = controlName === 'checkbox-group' ? 'test[]' : 'test'; - const el = /** @type {LitElement & FormControl & {value: string} & {registrationComplete: Promise} & {formElements: Array.}} */ (await fixture( - html`<${tag} name="${name}">${childrenEl}`, - )); + const el = + /** @type {LitElement & FormControl & {value: string} & {registrationComplete: Promise} & {formElements: Array.}} */ ( + await fixture(html`<${tag} name="${name}">${childrenEl}`) + ); await el.registrationComplete; el.addEventListener('model-value-changed', spy); diff --git a/packages/form-integrations/test/model-value-event.test.js b/packages/form-integrations/test/model-value-event.test.js index e58aec889..8d7c5c3cd 100644 --- a/packages/form-integrations/test/model-value-event.test.js +++ b/packages/form-integrations/test/model-value-event.test.js @@ -1,6 +1,7 @@ import '@lion/fieldset/define'; import '@lion/input/define'; -import { expect, html, fixture } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; // eslint-disable-next-line import/no-extraneous-dependencies import sinon from 'sinon'; diff --git a/packages/form/test/lion-form.test.js b/packages/form/test/lion-form.test.js index e54c40beb..e3d3ddd20 100644 --- a/packages/form/test/lion-form.test.js +++ b/packages/form/test/lion-form.test.js @@ -1,12 +1,5 @@ -import { - expect, - fixture as _fixture, - html, - oneEvent, - aTimeout, - unsafeStatic, - defineCE, -} from '@open-wc/testing'; +import { expect, fixture as _fixture, oneEvent, aTimeout, defineCE } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { spy } from 'sinon'; import { LionField } from '@lion/form-core'; import { LionFieldset } from '@lion/fieldset'; @@ -61,9 +54,9 @@ describe('', () => { `); - const resetButton = /** @type {HTMLInputElement} */ (withDefaults.querySelector( - 'input[type=reset]', - )); + const resetButton = /** @type {HTMLInputElement} */ ( + withDefaults.querySelector('input[type=reset]') + ); withDefaults.formElements.firstName.modelValue = 'updatedFoo'; expect(withDefaults.modelValue).to.deep.equal({ diff --git a/packages/helpers/sb-action-logger/test/sb-action-logger.test.js b/packages/helpers/sb-action-logger/test/sb-action-logger.test.js index ab473c971..cf61a707a 100644 --- a/packages/helpers/sb-action-logger/test/sb-action-logger.test.js +++ b/packages/helpers/sb-action-logger/test/sb-action-logger.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, html } from '@open-wc/testing'; +import { expect, fixture as _fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/helpers/define-sb-action-logger'; /** @@ -53,9 +54,9 @@ describe('sb-action-logger', () => { it('shows a visual cue whenever something is logged to the logger', async () => { const el = await fixture(html``); - const cueEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector( - '.header__log-cue-overlay', - )); + const cueEl = /** @type {HTMLElement} */ ( + el.shadowRoot?.querySelector('.header__log-cue-overlay') + ); expect(cueEl.classList.contains('header__log-cue-overlay--slide')).to.be.false; el.log('Hello, World!'); @@ -65,9 +66,9 @@ describe('sb-action-logger', () => { it('has a visual counter that counts the amount of total logs', async () => { const el = await fixture(html``); - const cueEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector( - '.header__log-cue-overlay', - )); + const cueEl = /** @type {HTMLElement} */ ( + el.shadowRoot?.querySelector('.header__log-cue-overlay') + ); expect(cueEl.classList.contains('.header__log-cue-overlay--slide')).to.be.false; @@ -100,12 +101,12 @@ describe('sb-action-logger', () => { const loggerEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.logger')); - const firstLogCount = /** @type {HTMLElement} */ (loggerEl.firstElementChild?.querySelector( - '.logger__log-count', - )); - const lastLogCount = /** @type {HTMLElement} */ (loggerEl.lastElementChild?.querySelector( - '.logger__log-count', - )); + const firstLogCount = /** @type {HTMLElement} */ ( + loggerEl.firstElementChild?.querySelector('.logger__log-count') + ); + const lastLogCount = /** @type {HTMLElement} */ ( + loggerEl.lastElementChild?.querySelector('.logger__log-count') + ); expect(loggerEl.children.length).to.equal(4); expect(firstLogCount.innerText).to.equal('3'); diff --git a/packages/icon/src/IconManager.js b/packages/icon/src/IconManager.js index 27e61d221..0e602d1e1 100644 --- a/packages/icon/src/IconManager.js +++ b/packages/icon/src/IconManager.js @@ -1,6 +1,6 @@ /** + * @typedef {import('lit-html').nothing} nothing * @typedef {import('@lion/core').TemplateResult} TemplateResult - * @typedef {import('@lion/core').nothing} nothing */ export class IconManager { diff --git a/packages/icon/src/LionIcon.js b/packages/icon/src/LionIcon.js index 7833dfde4..97539e5be 100644 --- a/packages/icon/src/LionIcon.js +++ b/packages/icon/src/LionIcon.js @@ -1,6 +1,11 @@ -import { css, html, LitElement, nothing, render, TemplateResult } from '@lion/core'; +import { css, html, LitElement, nothing, render, isTemplateResult } from '@lion/core'; import { icons } from './icons.js'; +/** + * @typedef {import('@lion/core').TemplateResult} TemplateResult + * @typedef {(tag: (strings: TemplateStringsArray, ... expr: string[]) => string) => string} TagFunction + */ + /** * @param {?} wrappedSvgObject */ @@ -14,7 +19,7 @@ function unwrapSvg(wrappedSvgObject) { * @param {TemplateResult|nothing} svg */ function validateSvg(svg) { - if (!(svg === nothing || svg instanceof TemplateResult)) { + if (!(svg === nothing || isTemplateResult(svg))) { throw new Error( 'icon accepts only lit-html templates or functions like "tag => tag`...`"', ); @@ -98,7 +103,10 @@ export class LionIcon extends LitElement { this.role = 'img'; this.ariaLabel = ''; this.iconId = ''; - /** @private */ + /** + * @private + * @type {TemplateResult|nothing|TagFunction} + */ this.__svg = nothing; } @@ -127,7 +135,7 @@ export class LionIcon extends LitElement { /** * On IE11, svgs without focusable false appear in the tab order * so make sure to have in svg files - * @param {TemplateResult|nothing} svg + * @param {TemplateResult|nothing|TagFunction} svg */ set svg(svg) { this.__svg = svg; @@ -138,6 +146,9 @@ export class LionIcon extends LitElement { } } + /** + * @type {TemplateResult|nothing|TagFunction} + */ get svg() { return this.__svg; } diff --git a/packages/icon/test/lion-icon.test.js b/packages/icon/test/lion-icon.test.js index 8aa512450..8cb1cdc67 100644 --- a/packages/icon/test/lion-icon.test.js +++ b/packages/icon/test/lion-icon.test.js @@ -1,5 +1,5 @@ -import { nothing, until } from '@lion/core'; -import { aTimeout, expect, fixture as _fixture, fixtureSync, html } from '@open-wc/testing'; +import { nothing, until, html } from '@lion/core'; +import { aTimeout, expect, fixture as _fixture, fixtureSync } from '@open-wc/testing'; import '@lion/icon/define'; import { icons } from '../src/icons.js'; import hammerSvg from './hammer.svg.js'; @@ -145,7 +145,7 @@ describe('lion-icon', () => { await el.updateComplete; el.svg = nothing; await el.updateComplete; - expect(el.innerHTML).to.equal(''); // don't use lightDom.to.equal(''), it gives false positives + expect(el.innerHTML).to.equal(''); // don't use lightDom.to.equal(''), it gives false positives }); it('does not render "null" if changed from valid input to null', async () => { @@ -153,7 +153,7 @@ describe('lion-icon', () => { await el.updateComplete; el.svg = nothing; await el.updateComplete; - expect(el.innerHTML).to.equal(''); // don't use lightDom.to.equal(''), it gives false positives + expect(el.innerHTML).to.equal(''); // don't use lightDom.to.equal(''), it gives false positives }); it('supports icons using an icon id', async () => { diff --git a/packages/input-datepicker/src/LionInputDatepicker.js b/packages/input-datepicker/src/LionInputDatepicker.js index 8c7bb02e0..c7df411b9 100644 --- a/packages/input-datepicker/src/LionInputDatepicker.js +++ b/packages/input-datepicker/src/LionInputDatepicker.js @@ -1,5 +1,6 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { LionCalendar } from '@lion/calendar'; -import { html, ifDefined, ScopedElementsMixin } from '@lion/core'; +import { html, ScopedElementsMixin, ifDefined, render } from '@lion/core'; import { LionInputDate } from '@lion/input-date'; import { OverlayMixin, @@ -9,6 +10,10 @@ import { } from '@lion/overlays'; import { LionCalendarOverlayFrame } from './LionCalendarOverlayFrame.js'; +/** + * @typedef {import('@lion/core').RenderOptions} RenderOptions + */ + /** * @customElement lion-input-datepicker */ @@ -62,13 +67,13 @@ export class LionInputDatepicker extends ScopedElementsMixin( ...super.slots, [this._calendarInvokerSlot]: () => { const renderParent = document.createElement('div'); - /** @type {typeof LionInputDatepicker} */ (this.constructor).render( + render( this._invokerTemplate(), renderParent, - { + /** @type {RenderOptions} */ ({ scopeName: this.localName, eventContext: this, - }, + }), ); return /** @type {HTMLElement} */ (renderParent.firstElementChild); }, @@ -169,9 +174,9 @@ export class LionInputDatepicker extends ScopedElementsMixin( * @protected */ get _calendarNode() { - return /** @type {LionCalendar} */ (this._overlayCtrl.contentNode.querySelector( - '[slot="content"]', - )); + return /** @type {LionCalendar} */ ( + this._overlayCtrl.contentNode.querySelector('[slot="content"]') + ); } constructor() { @@ -204,8 +209,8 @@ export class LionInputDatepicker extends ScopedElementsMixin( * @param {PropertyKey} name * @param {?} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (name === 'disabled' || name === 'readOnly') { this.__toggleInvokerDisabled(); diff --git a/packages/input-datepicker/test/lion-input-datepicker.test.js b/packages/input-datepicker/test/lion-input-datepicker.test.js index a4bc26da2..47a04ba0a 100644 --- a/packages/input-datepicker/test/lion-input-datepicker.test.js +++ b/packages/input-datepicker/test/lion-input-datepicker.test.js @@ -74,9 +74,9 @@ describe('', () => { const elObj = new DatepickerInputObject(el); await elObj.openCalendar(); expect( - /** @type {HTMLSlotElement} */ (elObj.overlayHeadingEl.querySelector( - 'slot[name="heading"]', - )).assignedNodes()[0], + /** @type {HTMLSlotElement} */ ( + elObj.overlayHeadingEl.querySelector('slot[name="heading"]') + ).assignedNodes()[0], ).lightDom.to.equal('Pick your date'); }); @@ -90,9 +90,9 @@ describe('', () => { const elObj = new DatepickerInputObject(el); await elObj.openCalendar(); expect( - /** @type {HTMLSlotElement} */ (elObj.overlayHeadingEl.querySelector( - 'slot[name="heading"]', - )).assignedNodes()[0], + /** @type {HTMLSlotElement} */ ( + elObj.overlayHeadingEl.querySelector('slot[name="heading"]') + ).assignedNodes()[0], ).lightDom.to.equal('foo'); }); @@ -315,9 +315,9 @@ describe('', () => { const el = await fixture(html` `); - const calendarEl = /** @type {LionCalendar} */ (el.shadowRoot?.querySelector( - '[data-tag-name="lion-calendar"]', - )); + const calendarEl = /** @type {LionCalendar} */ ( + el.shadowRoot?.querySelector('lion-calendar') + ); const { dateSelectedByUser } = getProtectedMembersCalendar(calendarEl); // First set a fixed date as if selected by a user dateSelectedByUser(new Date('December 17, 2020 03:24:00 GMT+0000')); @@ -342,9 +342,9 @@ describe('', () => { const el = await fixture(html` `); - const calendarEl = /** @type {LionCalendar} */ (el.shadowRoot?.querySelector( - '[data-tag-name="lion-calendar"]', - )); + const calendarEl = /** @type {LionCalendar} */ ( + el.shadowRoot?.querySelector('lion-calendar') + ); const { dateSelectedByUser } = getProtectedMembersCalendar(calendarEl); // First set a fixed date as if selected by a user @@ -356,9 +356,9 @@ describe('', () => { await elObj.openCalendar(); // Select the first date button, which is 29th of previous month (November) - const firstDateBtn = /** @type {HTMLButtonElement} */ (calendarEl?.shadowRoot?.querySelector( - '.calendar__day-button', - )); + const firstDateBtn = /** @type {HTMLButtonElement} */ ( + calendarEl?.shadowRoot?.querySelector('.calendar__day-button') + ); firstDateBtn.click(); expect(/** @type {Date} */ (el.modelValue).getTime()).to.equal( diff --git a/packages/input-iban/src/validators.js b/packages/input-iban/src/validators.js index 1538e65aa..ccae3657c 100644 --- a/packages/input-iban/src/validators.js +++ b/packages/input-iban/src/validators.js @@ -1,4 +1,4 @@ -/* eslint-disable max-classes-per-file */ +/* eslint-disable max-classes-per-file, import/no-extraneous-dependencies */ import { localize } from '@lion/localize'; import { Unparseable, Validator } from '@lion/form-core'; diff --git a/packages/input-stepper/src/LionInputStepper.js b/packages/input-stepper/src/LionInputStepper.js index 28348ae64..92a45e1f2 100644 --- a/packages/input-stepper/src/LionInputStepper.js +++ b/packages/input-stepper/src/LionInputStepper.js @@ -1,7 +1,11 @@ -import { html, css } from '@lion/core'; +import { html, css, render } from '@lion/core'; import { LionInput } from '@lion/input'; import { IsNumber, MinNumber, MaxNumber } from '@lion/form-core'; +/** + * @typedef {import('@lion/core').RenderOptions} RenderOptions + */ + /** * `LionInputStepper` is a class for custom input-stepper element (`` web component). * @@ -60,6 +64,9 @@ export class LionInputStepper extends LionInput { min: this.min, step: this.step, }; + + this.__increment = this.__increment.bind(this); + this.__decrement = this.__decrement.bind(this); } connectedCallback() { @@ -69,6 +76,7 @@ export class LionInputStepper extends LionInput { min: this.min, step: this.step, }; + this.role = 'spinbutton'; this.addEventListener('keydown', this.__keyDownHandler); this._inputNode.setAttribute('inputmode', 'decimal'); @@ -122,17 +130,17 @@ export class LionInputStepper extends LionInput { 'aria-valuemin': this.values.min, }; - const minMaxValidators = /** @type {(MaxNumber | MinNumber)[]} */ (Object.entries( - ariaAttributes, - ) - .map(([key, val]) => { - if (val !== Infinity) { - this.setAttribute(key, `${val}`); - return key === 'aria-valuemax' ? new MaxNumber(val) : new MinNumber(val); - } - return null; - }) - .filter(validator => validator !== null)); + const minMaxValidators = /** @type {(MaxNumber | MinNumber)[]} */ ( + Object.entries(ariaAttributes) + .map(([key, val]) => { + if (val !== Infinity) { + this.setAttribute(key, `${val}`); + return key === 'aria-valuemax' ? new MaxNumber(val) : new MinNumber(val); + } + return null; + }) + .filter(validator => validator !== null) + ); const validators = [new IsNumber(), ...minMaxValidators]; this.defaultValidators.push(...validators); } @@ -219,13 +227,13 @@ export class LionInputStepper extends LionInput { */ __getIncrementButtonNode() { const renderParent = document.createElement('div'); - /** @type {typeof LionInputStepper} */ (this.constructor).render( + render( this._incrementorTemplate(), renderParent, - { + /** @type {RenderOptions} */ ({ scopeName: this.localName, eventContext: this, - }, + }), ); return renderParent.firstElementChild; } @@ -237,13 +245,13 @@ export class LionInputStepper extends LionInput { */ __getDecrementButtonNode() { const renderParent = document.createElement('div'); - /** @type {typeof LionInputStepper} */ (this.constructor).render( + render( this._decrementorTemplate(), renderParent, - { + /** @type {RenderOptions} */ ({ scopeName: this.localName, eventContext: this, - }, + }), ); return renderParent.firstElementChild; } diff --git a/packages/input-stepper/test/lion-input-stepper.test.js b/packages/input-stepper/test/lion-input-stepper.test.js index 1712a71ef..09df7f797 100644 --- a/packages/input-stepper/test/lion-input-stepper.test.js +++ b/packages/input-stepper/test/lion-input-stepper.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, nextFrame, html } from '@open-wc/testing'; +import { expect, fixture as _fixture, nextFrame } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/input-stepper/define'; /** diff --git a/packages/input/src/LionInput.js b/packages/input/src/LionInput.js index 03df4246e..9829b8de3 100644 --- a/packages/input/src/LionInput.js +++ b/packages/input/src/LionInput.js @@ -67,8 +67,8 @@ export class LionInput extends NativeTextFieldMixin(LionField) { * @param {PropertyKey} name * @param {?} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (name === 'readOnly') { this.__delegateReadOnly(); } diff --git a/packages/input/test/lion-input.test.js b/packages/input/test/lion-input.test.js index 12a478427..e3740f411 100644 --- a/packages/input/test/lion-input.test.js +++ b/packages/input/test/lion-input.test.js @@ -1,5 +1,6 @@ import { Validator } from '@lion/form-core'; -import { expect, fixture, html, unsafeStatic, triggerFocusFor, aTimeout } from '@open-wc/testing'; +import { expect, fixture, triggerFocusFor, aTimeout } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { getInputMembers } from '../test-helpers/index.js'; import '@lion/input/define'; @@ -113,9 +114,11 @@ describe('', () => { }); it('automatically creates an
diff --git a/packages/listbox/src/ListboxMixin.js b/packages/listbox/src/ListboxMixin.js index 6cbb1acb5..c9d87b48d 100644 --- a/packages/listbox/src/ListboxMixin.js +++ b/packages/listbox/src/ListboxMixin.js @@ -149,6 +149,7 @@ const ListboxMixinImplementation = superclass => static get scopedElements() { return { + // @ts-expect-error [external] fix types scopedElements ...super.scopedElements, 'lion-options': LionOptions, }; @@ -158,9 +159,10 @@ const ListboxMixinImplementation = superclass => return { ...super.slots, input: () => { - const lionOptions = /** @type {HTMLElement & FormRegistrarPortalHost} */ (document.createElement( - ListboxMixin.getScopedTagName('lion-options'), - )); + const lionOptions = /** @type {HTMLElement & FormRegistrarPortalHost} */ ( + // @ts-expect-error [external] fix types scopedElements + document.createElement(ListboxMixin.getScopedTagName('lion-options')) + ); lionOptions.setAttribute('data-tag-name', 'lion-options'); lionOptions.registrationTarget = this; return lionOptions; @@ -188,9 +190,9 @@ const ListboxMixinImplementation = superclass => * @type {HTMLElement} */ get _listboxActiveDescendantNode() { - return /** @type {HTMLElement} */ (this._listboxNode.querySelector( - `#${this._listboxActiveDescendant}`, - )); + return /** @type {HTMLElement} */ ( + this._listboxNode.querySelector(`#${this._listboxActiveDescendant}`) + ); } /** diff --git a/packages/listbox/test-suites/ListboxMixin.suite.js b/packages/listbox/test-suites/ListboxMixin.suite.js index dd75adfa0..47ea375b1 100644 --- a/packages/listbox/test-suites/ListboxMixin.suite.js +++ b/packages/listbox/test-suites/ListboxMixin.suite.js @@ -3,7 +3,9 @@ import { repeat, LitElement } from '@lion/core'; import { Required } from '@lion/form-core'; import { LionOptions } from '@lion/listbox'; import '@lion/listbox/define'; -import { expect, fixture as _fixture, html, unsafeStatic, defineCE } from '@open-wc/testing'; +import { expect, fixture as _fixture, defineCE } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; + import sinon from 'sinon'; import { getListboxMembers } from '../test-helpers/index.js'; @@ -48,7 +50,6 @@ export function runListboxMixinSuite(customConfig = {}) { <${optionTag} .choiceValue=${'20'}>Item 2 `); - expect(el.modelValue).to.equal('10'); }); @@ -321,7 +322,8 @@ export function runListboxMixinSuite(customConfig = {}) { }); describe('Accessibility', () => { - it('[axe]: is accessible when opened', async () => { + // TODO: enable when native button is not a child anymore + it.skip('[axe]: is accessible when opened', async () => { const el = await fixture(html` <${tag} label="age" opened> <${optionTag} .choiceValue=${10}>Item 1 @@ -335,7 +337,8 @@ export function runListboxMixinSuite(customConfig = {}) { }); // NB: regular listbox is always 'opened', but needed for combobox and select-rich - it('[axe]: is accessible when closed', async () => { + // TODO: enable when native button is not a child anymore + it.skip('[axe]: is accessible when closed', async () => { const el = await fixture(html` <${tag} label="age"> <${optionTag} .choiceValue=${10}>Item 1 @@ -386,13 +389,15 @@ export function runListboxMixinSuite(customConfig = {}) { }); it('puts "aria-setsize" on all options to indicate the total amount of options', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue=${10}>Item 1 <${optionTag} .choiceValue=${20}>Item 2 <${optionTag} .choiceValue=${30}>Item 3 - `)); + `) + ); el.formElements.forEach(optionEl => { expect(optionEl.getAttribute('aria-setsize')).to.equal('3'); }); @@ -523,13 +528,15 @@ export function runListboxMixinSuite(customConfig = {}) { describe('Keyboard navigation', () => { describe('Rotate Keyboard Navigation', () => { it('stops navigation by default at end of option list', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened name="foo" .rotateKeyboardNavigation="${false}"> <${optionTag} .choiceValue="${'Artichoke'}">Artichoke <${optionTag} .choiceValue="${'Bla'}">Bla <${optionTag} .choiceValue="${'Chard'}">Chard - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); // Normalize @@ -552,13 +559,15 @@ export function runListboxMixinSuite(customConfig = {}) { }); it('when "rotate-navigation" provided, selects first option after navigated to next from last and vice versa', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened name="foo" rotate-keyboard-navigation autocomplete="inline"> <${optionTag} checked .choiceValue="${'Artichoke'}">Artichoke <${optionTag} .choiceValue="${'Bla'}">Bla <${optionTag} .choiceValue="${'Chard'}">Chard - `)); + `) + ); const { _inputNode } = getListboxMembers(el); _inputNode.dispatchEvent(new Event('focusin', { bubbles: true, composed: true })); @@ -587,13 +596,15 @@ export function runListboxMixinSuite(customConfig = {}) { describe('Enter', () => { it('[Enter] selects active option', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened name="foo" autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue="${'Artichoke'}">Artichoke <${optionTag} .choiceValue="${'Bla'}">Bla <${optionTag} .choiceValue="${'Chard'}">Chard - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); // Normalize suite @@ -610,13 +621,15 @@ export function runListboxMixinSuite(customConfig = {}) { it('selects active option when "_listboxReceivesNoFocus" is true', async () => { // When listbox is not focusable (in case of a combobox), the user should be allowed // to enter a space in the focusable element (texbox) - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened name="foo" ._listboxReceivesNoFocus="${false}" autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue="${'Artichoke'}">Artichoke <${optionTag} .choiceValue="${'Bla'}">Bla <${optionTag} .choiceValue="${'Chard'}">Chard - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); // Normalize suite @@ -686,13 +699,15 @@ export function runListboxMixinSuite(customConfig = {}) { expect(el.activeIndex).to.equal(3); }); it('navigates through open lists with [ArrowDown] [ArrowUp] keys activates the option', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened has-no-default-selected autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue=${'Item 1'}>Item 1 <${optionTag} .choiceValue=${'Item 2'}>Item 2 <${optionTag} .choiceValue=${'Item 3'}>Item 3 - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); // Normalize across listbox/select-rich/combobox @@ -714,12 +729,14 @@ export function runListboxMixinSuite(customConfig = {}) { describe('Orientation', () => { it('has a default value of "vertical"', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened name="foo" autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue="${'Artichoke'}">Artichoke <${optionTag} .choiceValue="${'Chard'}">Chard - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); expect(el.orientation).to.equal('vertical'); @@ -754,12 +771,14 @@ export function runListboxMixinSuite(customConfig = {}) { }); it('uses [ArrowLeft] and [ArrowRight] keys when "horizontal"', async () => { - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened name="foo" orientation="horizontal" autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue="${'Artichoke'}">Artichoke <${optionTag} .choiceValue="${'Chard'}">Chard - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); expect(el.orientation).to.equal('horizontal'); @@ -931,13 +950,15 @@ export function runListboxMixinSuite(customConfig = {}) { } }); } - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened selection-follows-focus autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue=${10}>Item 1 <${optionTag} .choiceValue=${20}>Item 2 <${optionTag} .choiceValue=${30}>Item 3 - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); const options = el.formElements; @@ -971,13 +992,15 @@ export function runListboxMixinSuite(customConfig = {}) { } }); } - const el = /** @type {LionListbox} */ (await fixture(html` + const el = /** @type {LionListbox} */ ( + await fixture(html` <${tag} opened selection-follows-focus orientation="horizontal" autocomplete="none" show-all-on-empty> <${optionTag} .choiceValue=${10}>Item 1 <${optionTag} .choiceValue=${20}>Item 2 <${optionTag} .choiceValue=${30}>Item 3 - `)); + `) + ); const { _listboxNode } = getListboxMembers(el); const options = el.formElements; @@ -1239,16 +1262,12 @@ export function runListboxMixinSuite(customConfig = {}) { `); expect(el.hasFeedbackFor).to.include('error'); - // @ts-expect-error no types for 'have.a.property' expect(el.validationStates).to.have.a.property('error'); - // @ts-expect-error no types for 'have.a.property' expect(el.validationStates.error).to.have.a.property('Required'); el.modelValue = 20; expect(el.hasFeedbackFor).not.to.include('error'); - // @ts-expect-error no types for 'have.a.property' expect(el.validationStates).to.have.a.property('error'); - // @ts-expect-error no types for 'have.a.property' expect(el.validationStates.error).not.to.have.a.property('Required'); }); }); @@ -1413,8 +1432,9 @@ export function runListboxMixinSuite(customConfig = {}) { <${tag} id="withRepeat"> ${repeat( this.options, - option => option, - option => html` ${option} `, + (/** @type {string} */ option) => option, + (/** @type {string} */ option) => + html` ${option} `, )} `; diff --git a/packages/listbox/test/lion-option.test.js b/packages/listbox/test/lion-option.test.js index 9048d2518..4ac146d01 100644 --- a/packages/listbox/test/lion-option.test.js +++ b/packages/listbox/test/lion-option.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; // eslint-disable-next-line no-unused-vars import { LionOption } from '../src/LionOption.js'; @@ -7,22 +8,24 @@ import '@lion/listbox/define-option'; describe('lion-option', () => { describe('Values', () => { it('has a modelValue', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el.modelValue).to.deep.equal({ value: 10, checked: false }); }); it('fires model-value-changed on click', async () => { let isTriggeredByUser; - const el = /** @type {LionOption} */ (await fixture(html` - - - `)); + const el = /** @type {LionOption} */ ( + await fixture(html` + + + `) + ); el.dispatchEvent(new CustomEvent('click', { bubbles: true })); expect(isTriggeredByUser).to.be.true; }); @@ -31,31 +34,33 @@ describe('lion-option', () => { let count = 0; let isTriggeredByUser; - const el = /** @type {LionOption} */ (await fixture(html` - - - `)); + const el = /** @type {LionOption} */ ( + await fixture(html` + + + `) + ); el.checked = true; expect(count).to.equal(1); expect(isTriggeredByUser).to.be.false; }); it('can be checked', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el.modelValue).to.deep.equal({ value: 10, checked: true }); }); it('is hidden when attribute hidden is true', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el).not.to.be.displayed; }); }); @@ -67,9 +72,9 @@ describe('lion-option', () => { }); it('has "aria-selected" attribute when checked', async () => { - const el = /** @type {LionOption} */ (await fixture(html` - Item 1 - `)); + const el = /** @type {LionOption} */ ( + await fixture(html` Item 1 `) + ); expect(el.getAttribute('aria-selected')).to.equal('true'); el.checked = false; @@ -81,9 +86,9 @@ describe('lion-option', () => { }); it('asynchronously adds the attributes "aria-disabled" and "disabled" when disabled', async () => { - const el = /** @type {LionOption} */ (await fixture(html` - Item 1 - `)); + const el = /** @type {LionOption} */ ( + await fixture(html` Item 1 `) + ); expect(el.getAttribute('aria-disabled')).to.equal('true'); expect(el.hasAttribute('disabled')).to.be.true; @@ -99,9 +104,9 @@ describe('lion-option', () => { describe('State reflection', () => { it('asynchronously adds the attribute "active" when active', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el.active).to.equal(false); expect(el.hasAttribute('active')).to.be.false; @@ -119,9 +124,9 @@ describe('lion-option', () => { }); it('does become checked and active on [click]', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el.checked).to.be.false; expect(el.active).to.be.false; el.click(); @@ -132,12 +137,14 @@ describe('lion-option', () => { it('fires active-changed event', async () => { const activeSpy = sinon.spy(); - const el = /** @type {LionOption} */ (await fixture(html` - - `)); + const el = /** @type {LionOption} */ ( + await fixture(html` + + `) + ); expect(activeSpy.callCount).to.equal(0); el.active = true; expect(activeSpy.callCount).to.equal(1); @@ -146,18 +153,18 @@ describe('lion-option', () => { describe('Disabled', () => { it('does not becomes active on [mouseenter]', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el.active).to.be.false; el.dispatchEvent(new Event('mouseenter')); expect(el.active).to.be.false; }); it('does not become checked on [click]', async () => { - const el = /** @type {LionOption} */ (await fixture( - html``, - )); + const el = /** @type {LionOption} */ ( + await fixture(html``) + ); expect(el.checked).to.be.false; el.click(); await el.updateComplete; @@ -165,9 +172,9 @@ describe('lion-option', () => { }); it('does not become un-active on [mouseleave]', async () => { - const el = /** @type {LionOption} */ (await fixture(html` - - `)); + const el = /** @type {LionOption} */ ( + await fixture(html` `) + ); expect(el.active).to.be.true; el.dispatchEvent(new Event('mouseleave')); expect(el.active).to.be.true; diff --git a/packages/listbox/test/lion-options.test.js b/packages/listbox/test/lion-options.test.js index dc3c8208f..569f27b40 100644 --- a/packages/listbox/test/lion-options.test.js +++ b/packages/listbox/test/lion-options.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; // eslint-disable-next-line no-unused-vars import { LionOptions } from '../src/LionOptions.js'; import '@lion/listbox/define-options'; @@ -6,9 +7,11 @@ import '@lion/listbox/define-options'; describe('lion-options', () => { it('should have role="listbox"', async () => { const registrationTargetEl = document.createElement('div'); - const el = /** @type {LionOptions} */ (await fixture(html` - - `)); + const el = /** @type {LionOptions} */ ( + await fixture(html` + + `) + ); expect(el.role).to.equal('listbox'); }); }); diff --git a/packages/localize/src/LocalizeMixin.js b/packages/localize/src/LocalizeMixin.js index d3a5e5a02..9170eb214 100644 --- a/packages/localize/src/LocalizeMixin.js +++ b/packages/localize/src/LocalizeMixin.js @@ -2,6 +2,7 @@ import { dedupeMixin, until, nothing } from '@lion/core'; import { localize } from './localize.js'; /** + * @typedef {import('@lion/core').DirectiveResult} DirectiveResult * @typedef {import('../types/LocalizeMixinTypes').LocalizeMixin} LocalizeMixin */ @@ -84,7 +85,7 @@ const LocalizeMixinImplementation = superclass => * @param {Object.} variables * @param {Object} [options] * @param {string} [options.locale] - * @return {string | function} + * @returns {string | DirectiveResult} */ msgLit(keys, variables, options) { if (this.__localizeMessageSync) { diff --git a/packages/localize/test/LocalizeMixin.test.js b/packages/localize/test/LocalizeMixin.test.js index 6b7d6b889..d61cbfb81 100644 --- a/packages/localize/test/LocalizeMixin.test.js +++ b/packages/localize/test/LocalizeMixin.test.js @@ -1,14 +1,6 @@ -import { isDirective, LitElement } from '@lion/core'; -import { - aTimeout, - defineCE, - expect, - fixture, - fixtureSync, - html, - nextFrame, - unsafeStatic, -} from '@open-wc/testing'; +import { isDirectiveResult, LitElement } from '@lion/core'; +import { aTimeout, defineCE, expect, fixture, fixtureSync, nextFrame } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import sinon from 'sinon'; import { localize } from '../src/localize.js'; import { LocalizeMixin } from '../src/LocalizeMixin.js'; @@ -292,7 +284,7 @@ describe('LocalizeMixin', () => { const messageDirective = el.msgLit('my-element:greeting'); expect(lionLocalizeMessageSpy.callCount).to.equal(0); - expect(isDirective(messageDirective)).to.be.true; + expect(isDirectiveResult(messageDirective)).to.be.true; await aTimeout(1); // wait for directive to "resolve" @@ -329,7 +321,7 @@ describe('LocalizeMixin', () => { const el = /** @type {MyElement} */ (document.createElement(tagString)); const messageDirective = el.msgLit('my-element:greeting'); - expect(isDirective(messageDirective)).to.be.true; + expect(isDirectiveResult(messageDirective)).to.be.true; await el.localizeNamespacesLoaded; expect(el.msgLit('my-element:greeting')).to.equal('Hi!'); diff --git a/packages/overlays/src/OverlayMixin.js b/packages/overlays/src/OverlayMixin.js index 8eed5bf4f..6e6cbae4c 100644 --- a/packages/overlays/src/OverlayMixin.js +++ b/packages/overlays/src/OverlayMixin.js @@ -61,8 +61,8 @@ export const OverlayMixinImplementation = superclass => * @param {string} name * @param {any} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (name === 'opened' && this.opened !== oldValue) { this.dispatchEvent(new Event('opened-changed')); } diff --git a/packages/overlays/src/OverlaysManager.js b/packages/overlays/src/OverlaysManager.js index d6157c9a7..23a5801b1 100644 --- a/packages/overlays/src/OverlaysManager.js +++ b/packages/overlays/src/OverlaysManager.js @@ -2,6 +2,7 @@ import { unsetSiblingsInert, setSiblingsInert } from './utils/inert-siblings.js' import { globalOverlaysStyle } from './globalOverlaysStyle.js'; /** + * @typedef {import('@lion/core').CSSResult} CSSResult * @typedef {import('./OverlayController.js').OverlayController} OverlayController */ @@ -21,7 +22,7 @@ export class OverlaysManager { static __createGlobalStyleNode() { const styleTag = document.createElement('style'); styleTag.setAttribute('data-global-overlays', ''); - styleTag.textContent = globalOverlaysStyle.cssText; + styleTag.textContent = /** @type {CSSResult} */ (globalOverlaysStyle).cssText; document.head.appendChild(styleTag); return styleTag; } @@ -232,9 +233,9 @@ export class OverlaysManager { */ retractRequestToShowOnly(blockingCtrl) { if (this.__blockingMap.has(blockingCtrl)) { - const controllersWhichGotHidden = /** @type {OverlayController[]} */ (this.__blockingMap.get( - blockingCtrl, - )); + const controllersWhichGotHidden = /** @type {OverlayController[]} */ ( + this.__blockingMap.get(blockingCtrl) + ); controllersWhichGotHidden.map(ctrl => ctrl.show()); } } diff --git a/packages/overlays/test-suites/OverlayMixin.suite.js b/packages/overlays/test-suites/OverlayMixin.suite.js index 6da75cec7..a27e0f9b4 100644 --- a/packages/overlays/test-suites/OverlayMixin.suite.js +++ b/packages/overlays/test-suites/OverlayMixin.suite.js @@ -24,23 +24,27 @@ function getGlobalOverlayNodes() { export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { describe(`OverlayMixin${suffix}`, () => { it('should not be opened by default', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content of the overlay
- `)); + `) + ); expect(el.opened).to.be.false; expect(el._overlayCtrl.isShown).to.be.false; }); it('syncs opened to overlayController', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content of the overlay
- `)); + `) + ); el.opened = true; await el.updateComplete; await el._overlayCtrl._showComplete; @@ -55,12 +59,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { }); it('syncs OverlayController to opened', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content of the overlay
- `)); + `) + ); expect(el.opened).to.be.false; await el._overlayCtrl.show(); expect(el.opened).to.be.true; @@ -72,19 +78,20 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { it('does not change the body size when opened', async () => { const parentNode = document.createElement('div'); parentNode.setAttribute('style', 'height: 10000px; width: 10000px;'); - const elWithBigParent = /** @type {OverlayEl} */ (await fixture( - html` + const elWithBigParent = /** @type {OverlayEl} */ ( + await fixture( + html` <${tag}>
content of the overlay
`, - { parentNode }, - )); - const { - offsetWidth, - offsetHeight, - } = /** @type {HTMLElement} */ (elWithBigParent.offsetParent); + { parentNode }, + ) + ); + const { offsetWidth, offsetHeight } = /** @type {HTMLElement} */ ( + elWithBigParent.offsetParent + ); await elWithBigParent._overlayCtrl.show(); expect(elWithBigParent.opened).to.be.true; expect(/** @type {HTMLElement} */ (elWithBigParent?.offsetParent).offsetWidth).to.equal( @@ -103,12 +110,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { }); it('should respond to initially and dynamically setting the config', async () => { - const itEl = /** @type {OverlayEl} */ (await fixture(html` + const itEl = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} .config=${{ trapsKeyboardFocus: false, viewportConfig: { placement: 'top' } }}>
content of the overlay
- `)); + `) + ); itEl.opened = true; await itEl.updateComplete; expect(itEl._overlayCtrl.trapsKeyboardFocus).to.be.false; @@ -120,12 +129,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { it('fires "opened-changed" event on hide', async () => { const spy = sinon.spy(); - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} @opened-changed="${spy}">
content of the overlay
- `)); + `) + ); expect(spy).not.to.have.been.called; await el._overlayCtrl.show(); await el.updateComplete; @@ -142,12 +153,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { it('fires "before-closed" event on hide', async () => { const beforeSpy = sinon.spy(); - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} @before-closed="${beforeSpy}" opened>
content of the overlay
- `)); + `) + ); // Wait until it's done opening (handling features is async) await nextFrame(); expect(beforeSpy).not.to.have.been.called; @@ -158,12 +171,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { it('fires before-opened" event on show', async () => { const beforeSpy = sinon.spy(); - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} @before-opened="${beforeSpy}">
content of the overlay
- `)); + `) + ); expect(beforeSpy).not.to.have.been.called; await el._overlayCtrl.show(); expect(beforeSpy).to.have.been.called; @@ -174,12 +189,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { function preventer(/** @type Event */ ev) { ev.preventDefault(); } - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} @before-opened="${preventer}" @before-closed="${preventer}">
content of the overlay
- `)); + `) + ); /** @type {HTMLElement} */ (el.querySelector('[slot="invoker"]')).click(); await nextFrame(); expect(el.opened).to.be.false; @@ -195,11 +212,12 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { function sendCloseEvent(/** @type {Event} */ e) { e.target?.dispatchEvent(new Event('close-overlay', { bubbles: true })); } - const closeBtn = /** @type {OverlayEl} */ (await fixture( - html` `, - )); + const closeBtn = /** @type {OverlayEl} */ ( + await fixture(html` `) + ); - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} opened>
content of the overlay @@ -207,7 +225,8 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
- `)); + `) + ); closeBtn.click(); await nextFrame(); // hide takes at least a frame expect(el.opened).to.be.false; @@ -215,12 +234,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { // See https://github.com/ing-bank/lion/discussions/1095 it('exposes "open()", "close()" and "toggle()" methods', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content
- `)); + `) + ); expect(el.opened).to.be.false; el.open(); await nextFrame(); @@ -240,12 +261,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { }); it('exposes "repositionOverlay()" method', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} opened .config="${{ placementMode: 'local' }}">
content
- `)); + `) + ); await OverlayController.popperModule; sinon.spy(el._overlayCtrl._popper, 'update'); el.repositionOverlay(); @@ -260,12 +283,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { /** See: https://github.com/ing-bank/lion/issues/1075 */ it('stays open after config update', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content
- `)); + `) + ); el.open(); await el._overlayCtrl._showComplete; @@ -277,12 +302,14 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { /** Prevent unnecessary reset side effects, such as show animation. See: https://github.com/ing-bank/lion/issues/1075 */ it('does not call updateConfig on equivalent config change', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content
- `)); + `) + ); el.open(); await nextFrame(); @@ -309,7 +336,8 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { }); it('supports nested overlays', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} id="main-dialog">
open nested overlay: @@ -322,7 +350,8 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
- `)); + `) + ); if (el._overlayCtrl.placementMode === 'global') { expect(getGlobalOverlayNodes().length).to.equal(2); @@ -331,21 +360,23 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { el.opened = true; await aTimeout(0); expect(el._overlayCtrl.contentNode).to.be.displayed; - const nestedOverlayEl = /** @type {OverlayEl} */ (el._overlayCtrl.contentNode.querySelector( - tagString, - )); + const nestedOverlayEl = /** @type {OverlayEl} */ ( + el._overlayCtrl.contentNode.querySelector(tagString) + ); nestedOverlayEl.opened = true; await aTimeout(0); expect(nestedOverlayEl._overlayCtrl.contentNode).to.be.displayed; }); it('[global] allows for moving of the element', async () => { - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag}>
content of the nested overlay
- `)); + `) + ); if (el._overlayCtrl.placementMode === 'global') { expect(getGlobalOverlayNodes().length).to.equal(1); @@ -357,14 +388,17 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { }); it('reconstructs the overlay when disconnected and reconnected to DOM (support for nested overlay nodes)', async () => { - const nestedEl = /** @type {OverlayEl} */ (await fixture(html` + const nestedEl = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} id="nest">
content of the nested overlay
- `)); + `) + ); - const el = /** @type {OverlayEl} */ (await fixture(html` + const el = /** @type {OverlayEl} */ ( + await fixture(html` <${tag} id="main">
open nested overlay: @@ -372,7 +406,8 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
- `)); + `) + ); if (el._overlayCtrl.placementMode === 'global') { // Find the outlets that are not backdrop outlets @@ -385,10 +420,10 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) { ); expect(lastContentNodeInContainer.firstElementChild.slot).to.equal('content'); } else { - // @ts-ignore allow protected props in tests - const contentNode = /** @type {HTMLElement} */ (el._overlayContentNode.querySelector( - '#nestedContent', - )); + const contentNode = /** @type {HTMLElement} */ ( + // @ts-ignore [allow-protected] in tests + el._overlayContentNode.querySelector('#nestedContent') + ); expect(contentNode).to.not.be.null; expect(contentNode.innerText).to.equal('content of the nested overlay'); } diff --git a/packages/overlays/test/OverlayController.test.js b/packages/overlays/test/OverlayController.test.js index 47c0e3dec..a1edbf22e 100644 --- a/packages/overlays/test/OverlayController.test.js +++ b/packages/overlays/test/OverlayController.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-new */ -import { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing'; +import { aTimeout, defineCE, expect, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { fixtureSync } from '@open-wc/testing-helpers'; import sinon from 'sinon'; @@ -37,9 +38,9 @@ const withLocalTestConfig = () => /** @type {OverlayConfig} */ ({ placementMode: 'local', contentNode: /** @type {HTMLElement} */ (fixtureSync(html`
my content
`)), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
Invoker
- `)), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
Invoker
`) + ), }); afterEach(() => { @@ -73,21 +74,23 @@ describe('OverlayController', () => { */ async function createZNode(zIndexVal, { mode } = {}) { if (mode === 'global') { - contentNode = /** @type {HTMLElement} */ (await fixture(html` -
- - I should be on top -
- `)); + contentNode = /** @type {HTMLElement} */ ( + await fixture(html` +
+ + I should be on top +
+ `) + ); } if (mode === 'inline') { - contentNode = /** @type {HTMLElement} */ (await fixture( - html`
I should be on top
`, - )); + contentNode = /** @type {HTMLElement} */ ( + await fixture(html`
I should be on top
`) + ); contentNode.style.zIndex = zIndexVal; } return contentNode; @@ -160,11 +163,13 @@ describe('OverlayController', () => { }); it('keeps local target for placement mode "local" when already connected', async () => { - const parentNode = /** @type {HTMLElement} */ (await fixture(html` -
-
Content
-
- `)); + const parentNode = /** @type {HTMLElement} */ ( + await fixture(html` +
+
Content
+
+ `) + ); const contentNode = /** @type {HTMLElement} */ (parentNode.querySelector('#content')); const ctrl = new OverlayController({ ...withLocalTestConfig(), @@ -300,12 +305,14 @@ describe('OverlayController', () => { describe('When contentWrapperNode needs to be provided for correct arrow positioning', () => { it('uses contentWrapperNode as provided for local positioning', async () => { - const el = /** @type {HTMLElement} */ (await fixture(html` -
-
- -
- `)); + const el = /** @type {HTMLElement} */ ( + await fixture(html` +
+
+ +
+ `) + ); const contentNode = /** @type {HTMLElement} */ (el.querySelector('#contentNode')); const contentWrapperNode = el; @@ -344,9 +351,9 @@ describe('OverlayController', () => { }); it('keeps focus within the overlay e.g. you can not tab out by accident', async () => { - const contentNode = /** @type {HTMLElement} */ (await fixture(html` -
- `)); + const contentNode = /** @type {HTMLElement} */ ( + await fixture(html`
`) + ); const ctrl = new OverlayController({ ...withGlobalTestConfig(), trapsKeyboardFocus: true, @@ -354,9 +361,9 @@ describe('OverlayController', () => { }); await ctrl.show(); - const elOutside = /** @type {HTMLElement} */ (await fixture( - html``, - )); + const elOutside = /** @type {HTMLElement} */ ( + await fixture(html``) + ); const input1 = ctrl.contentNode.querySelectorAll('input')[0]; const input2 = ctrl.contentNode.querySelectorAll('input')[1]; @@ -521,9 +528,11 @@ describe('OverlayController', () => { ...withGlobalTestConfig(), hidesOnOutsideClick: true, contentNode, - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
Invoker
- `)), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
Invoker
+ `) + ), }); await ctrl.show(); mimicClick(document.body, { releaseElement: contentNode }); @@ -578,12 +587,14 @@ describe('OverlayController', () => { ); const tag = unsafeStatic(tagString); ctrl.updateConfig({ - contentNode: /** @type {HTMLElement} */ (await fixture(html` + contentNode: /** @type {HTMLElement} */ ( + await fixture(html`
Content
<${tag}>
- `)), + `) + ), }); await ctrl.show(); @@ -603,9 +614,9 @@ describe('OverlayController', () => { }); it('works with 3rd party code using "event.stopPropagation()" on bubble phase', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
Invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
Invoker
') + ); const contentNode = /** @type {HTMLElement} */ (await fixture('
Content
')); const ctrl = new OverlayController({ ...withLocalTestConfig(), @@ -640,9 +651,9 @@ describe('OverlayController', () => { }); it('works with 3rd party code using "event.stopPropagation()" on capture phase', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - html`
Invoker
`, - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture(html`
Invoker
`) + ); const contentNode = /** @type {HTMLElement} */ (await fixture('
Content
')); const ctrl = new OverlayController({ ...withLocalTestConfig(), @@ -651,14 +662,16 @@ describe('OverlayController', () => { invokerNode, }); const stopProp = (/** @type {Event} */ e) => e.stopPropagation(); - const dom = /** @type {HTMLElement} */ (await fixture(` + const dom = /** @type {HTMLElement} */ ( + await fixture(`
This element prevents our handlers from reaching the document click handler.
- `)); + `) + ); const noiseEl = /** @type {HTMLElement} */ (dom.querySelector('#third-party-noise')); @@ -679,12 +692,14 @@ describe('OverlayController', () => { }); it('doesn\'t hide on "inside label" click', async () => { - const contentNode = /** @type {HTMLElement} */ (await fixture(` + const contentNode = /** @type {HTMLElement} */ ( + await fixture(`
Content -
`)); +
`) + ); const labelNode = /** @type {HTMLElement} */ (contentNode.querySelector('label[for=test]')); const ctrl = new OverlayController({ ...withGlobalTestConfig(), @@ -723,9 +738,9 @@ describe('OverlayController', () => { it('supports elementToFocusAfterHide option to focus it when hiding', async () => { const input = /** @type {HTMLElement} */ (await fixture('')); - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
', - )); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
') + ); const ctrl = new OverlayController({ ...withGlobalTestConfig(), elementToFocusAfterHide: input, @@ -762,9 +777,9 @@ describe('OverlayController', () => { it('allows to set elementToFocusAfterHide on show', async () => { const input = /** @type {HTMLElement} */ (await fixture('')); - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
', - )); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
') + ); const ctrl = new OverlayController({ ...withGlobalTestConfig(), viewportConfig: { @@ -1281,9 +1296,9 @@ describe('OverlayController', () => { describe('Accessibility', () => { it('synchronizes [aria-expanded] on invoker', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1306,9 +1321,9 @@ describe('OverlayController', () => { }); it('preserves content id when present', async () => { - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
content
', - )); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
content
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1318,9 +1333,9 @@ describe('OverlayController', () => { }); it('adds [role=dialog] on content', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1330,12 +1345,12 @@ describe('OverlayController', () => { }); it('preserves [role] on content when present', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1446,9 +1461,9 @@ describe('OverlayController', () => { describe('Tooltip', () => { it('adds [aria-describedby] on invoker', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1461,9 +1476,9 @@ describe('OverlayController', () => { }); it('adds [aria-labelledby] on invoker when invokerRelation is label', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1478,9 +1493,9 @@ describe('OverlayController', () => { }); it('adds [role=tooltip] on content', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1492,9 +1507,9 @@ describe('OverlayController', () => { describe('Teardown', () => { it('restores [role] on dialog content', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1506,12 +1521,12 @@ describe('OverlayController', () => { }); it('restores [role] on tooltip content', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
content
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
content
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1525,12 +1540,12 @@ describe('OverlayController', () => { }); it('restores [aria-describedby] on content', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
content
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
content
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, @@ -1544,12 +1559,12 @@ describe('OverlayController', () => { }); it('restores [aria-labelledby] on content', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture( - '
invoker
', - )); - const contentNode = /** @type {HTMLElement} */ (await fixture( - '
content
', - )); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture('
invoker
') + ); + const contentNode = /** @type {HTMLElement} */ ( + await fixture('
content
') + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), handlesAccessibility: true, diff --git a/packages/overlays/test/OverlayMixin.test.js b/packages/overlays/test/OverlayMixin.test.js index 842b90e72..34fb94c1f 100644 --- a/packages/overlays/test/OverlayMixin.test.js +++ b/packages/overlays/test/OverlayMixin.test.js @@ -1,4 +1,5 @@ -import { defineCE, unsafeStatic } from '@open-wc/testing'; +import { defineCE } from '@open-wc/testing'; +import { unsafeStatic } from 'lit/static-html.js'; import { LitElement, html } from '@lion/core'; import { runOverlayMixinSuite } from '../test-suites/OverlayMixin.suite.js'; import { OverlayMixin } from '../src/OverlayMixin.js'; diff --git a/packages/overlays/test/OverlaysManager.test.js b/packages/overlays/test/OverlaysManager.test.js index 6a50d34e2..76a591961 100644 --- a/packages/overlays/test/OverlaysManager.test.js +++ b/packages/overlays/test/OverlaysManager.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { OverlayController } from '../src/OverlayController.js'; import { OverlaysManager } from '../src/OverlaysManager.js'; diff --git a/packages/overlays/test/global-positioning.test.js b/packages/overlays/test/global-positioning.test.js index 507bfcf93..eabb547aa 100644 --- a/packages/overlays/test/global-positioning.test.js +++ b/packages/overlays/test/global-positioning.test.js @@ -1,4 +1,5 @@ -import { expect, html } from '@open-wc/testing'; +import { expect } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { fixtureSync } from '@open-wc/testing-helpers'; import { OverlayController } from '../src/OverlayController.js'; import { overlays } from '../src/overlays.js'; diff --git a/packages/overlays/test/local-positioning.test.js b/packages/overlays/test/local-positioning.test.js index 0be2285f7..0c46b8c81 100644 --- a/packages/overlays/test/local-positioning.test.js +++ b/packages/overlays/test/local-positioning.test.js @@ -1,5 +1,6 @@ /* eslint-disable lit-a11y/click-events-have-key-events */ -import { expect, fixture, fixtureSync, html } from '@open-wc/testing'; +import { expect, fixture, fixtureSync } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { OverlayController } from '../src/OverlayController.js'; import { normalizeTransformStyle } from './utils-tests/local-positioning-helpers.js'; @@ -12,9 +13,9 @@ const withLocalTestConfig = () => /** @type {OverlayConfig} */ ({ placementMode: 'local', contentNode: /** @type {HTMLElement} */ (fixtureSync(html`
my content
`)), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
Invoker
- `)), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
Invoker
`) + ), }); describe('Local Positioning', () => { @@ -35,12 +36,14 @@ describe('Local Positioning', () => { // smoke test for integration of popper const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync(html` -
- `)), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
+ `) + ), }); await fixture(html`
@@ -58,12 +61,18 @@ describe('Local Positioning', () => { it('uses top as the default placement', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}>
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()} + >
+ `) + ), }); await fixture(html`
@@ -77,12 +86,18 @@ describe('Local Positioning', () => { it('positions to preferred place if placement is set and space is available', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}>
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()} + >
+ `) + ), popperConfig: { placement: 'left-start', }, @@ -100,14 +115,16 @@ describe('Local Positioning', () => { it('positions to different place if placement is set and no space is available', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
invoker
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}> - content -
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
invoker
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()}> + content +
+ `) + ), popperConfig: { placement: 'left', }, @@ -123,12 +140,18 @@ describe('Local Positioning', () => { it('allows the user to override default Popper modifiers', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}>
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()} + >
+ `) + ), popperConfig: { modifiers: [ { @@ -152,12 +175,18 @@ describe('Local Positioning', () => { it('positions the Popper element correctly on show', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}>
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()} + >
+ `) + ), popperConfig: { placement: 'top', }, @@ -185,12 +214,18 @@ describe('Local Positioning', () => { it.skip('updates placement properly even during hidden state', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}>
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()} + >
+ `) + ), popperConfig: { placement: 'top', modifiers: [ @@ -242,14 +277,16 @@ describe('Local Positioning', () => { it.skip('updates positioning correctly during shown state when config gets updated', async () => { const ctrl = new OverlayController({ ...withLocalTestConfig(), - contentNode: /** @type {HTMLElement} */ (fixtureSync( - html`
`, - )), - invokerNode: /** @type {HTMLElement} */ (fixtureSync(html` -
ctrl.show()}> - Invoker -
- `)), + contentNode: /** @type {HTMLElement} */ ( + fixtureSync(html`
`) + ), + invokerNode: /** @type {HTMLElement} */ ( + fixtureSync(html` +
ctrl.show()}> + Invoker +
+ `) + ), popperConfig: { placement: 'top', modifiers: [ @@ -287,9 +324,9 @@ describe('Local Positioning', () => { }); it('can set the contentNode minWidth as the invokerNode width', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture(html` -
invoker
- `)); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture(html`
invoker
`) + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), inheritsReferenceWidth: 'min', @@ -300,9 +337,9 @@ describe('Local Positioning', () => { }); it('can set the contentNode maxWidth as the invokerNode width', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture(html` -
invoker
- `)); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture(html`
invoker
`) + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), inheritsReferenceWidth: 'max', @@ -313,9 +350,9 @@ describe('Local Positioning', () => { }); it('can set the contentNode width as the invokerNode width', async () => { - const invokerNode = /** @type {HTMLElement} */ (await fixture(html` -
invoker
- `)); + const invokerNode = /** @type {HTMLElement} */ ( + await fixture(html`
invoker
`) + ); const ctrl = new OverlayController({ ...withLocalTestConfig(), inheritsReferenceWidth: 'full', diff --git a/packages/overlays/test/utils-tests/contain-focus.test.js b/packages/overlays/test/utils-tests/contain-focus.test.js index d92d1aebc..0b08898ac 100644 --- a/packages/overlays/test/utils-tests/contain-focus.test.js +++ b/packages/overlays/test/utils-tests/contain-focus.test.js @@ -1,5 +1,6 @@ /* eslint-disable lit-a11y/no-autofocus */ -import { expect, fixture, html, nextFrame } from '@open-wc/testing'; +import { expect, fixture, nextFrame } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { renderLitAsNode } from '@lion/helpers'; import { getDeepActiveElement } from '../../src/utils/get-deep-active-element.js'; import { getFocusableElements } from '../../src/utils/get-focusable-elements.js'; diff --git a/packages/pagination/src/LionPagination.js b/packages/pagination/src/LionPagination.js index 661ef9642..3c8ae4b1d 100644 --- a/packages/pagination/src/LionPagination.js +++ b/packages/pagination/src/LionPagination.js @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { LitElement, html, css } from '@lion/core'; import { LocalizeMixin } from '@lion/localize'; @@ -200,9 +201,9 @@ export class LionPagination extends LocalizeMixin(LitElement) { const pos5 = this.current + 1; // if pos 3 is lower than 4 we have a predefined list of elements if (pos4 <= 4) { - const list = /** @type {(number|'...')[]} */ ([...Array(this.__visiblePages)].map( - (_, idx) => start + idx, - )); + const list = /** @type {(number|'...')[]} */ ( + [...Array(this.__visiblePages)].map((_, idx) => start + idx) + ); list.push('...'); list.push(this.count); return list; diff --git a/packages/pagination/test/lion-pagination.test.js b/packages/pagination/test/lion-pagination.test.js index 993e36bd8..44e91d1cd 100644 --- a/packages/pagination/test/lion-pagination.test.js +++ b/packages/pagination/test/lion-pagination.test.js @@ -1,4 +1,5 @@ -import { html, fixture as _fixture, expect } from '@open-wc/testing'; +import { fixture as _fixture, expect } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import '@lion/pagination/define'; @@ -96,9 +97,9 @@ describe('Pagination', () => { const el = await fixture(html` `); - const page2 = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector( - "button[aria-current='true']", - )); + const page2 = /** @type {HTMLElement} */ ( + el.shadowRoot?.querySelector("button[aria-current='true']") + ); page2.click(); expect(changeSpy).to.not.be.called; expect(el.current).to.equal(2); diff --git a/packages/progress-indicator/src/LionProgressIndicator.js b/packages/progress-indicator/src/LionProgressIndicator.js index b493d60f6..ecb394776 100644 --- a/packages/progress-indicator/src/LionProgressIndicator.js +++ b/packages/progress-indicator/src/LionProgressIndicator.js @@ -1,4 +1,4 @@ -/* eslint-disable class-methods-use-this */ +/* eslint-disable class-methods-use-this, import/no-extraneous-dependencies */ import { nothing, LitElement } from '@lion/core'; import { localize, LocalizeMixin } from '@lion/localize'; diff --git a/packages/radio-group/test/lion-radio-group.test.js b/packages/radio-group/test/lion-radio-group.test.js index 97276f5f9..1739dd5d2 100644 --- a/packages/radio-group/test/lion-radio-group.test.js +++ b/packages/radio-group/test/lion-radio-group.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, html } from '@open-wc/testing'; +import { expect, fixture as _fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/radio-group/define'; /** diff --git a/packages/radio-group/test/lion-radio.test.js b/packages/radio-group/test/lion-radio.test.js index 92b140610..fffdc6e19 100644 --- a/packages/radio-group/test/lion-radio.test.js +++ b/packages/radio-group/test/lion-radio.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/radio-group/define-radio'; /** @@ -14,9 +15,9 @@ describe('', () => { }); it('can be reset when unchecked by default', async () => { - const el = /** @type {LionRadio} */ (await fixture(html` - - `)); + const el = /** @type {LionRadio} */ ( + await fixture(html` `) + ); expect(el._initialModelValue).to.deep.equal({ value: 'male', checked: false }); el.checked = true; expect(el.modelValue).to.deep.equal({ value: 'male', checked: true }); @@ -26,9 +27,9 @@ describe('', () => { }); it('can be reset when checked by default', async () => { - const el = /** @type {LionRadio} */ (await fixture(html` - - `)); + const el = /** @type {LionRadio} */ ( + await fixture(html` `) + ); expect(el._initialModelValue).to.deep.equal({ value: 'male', checked: true }); el.checked = false; expect(el.modelValue).to.deep.equal({ value: 'male', checked: false }); diff --git a/packages/select-rich/src/LionSelectInvoker.js b/packages/select-rich/src/LionSelectInvoker.js index dd66ea2e9..1a8daf629 100644 --- a/packages/select-rich/src/LionSelectInvoker.js +++ b/packages/select-rich/src/LionSelectInvoker.js @@ -3,6 +3,7 @@ import { css, html } from '@lion/core'; /** * @typedef {import('@lion/core').CSSResult} CSSResult + * @typedef {import('@lion/core').TemplateResult} TemplateResult * @typedef {import('@lion/listbox').LionOption} LionOption */ @@ -105,7 +106,10 @@ export class LionSelectInvoker extends LionButton { this.removeEventListener('keydown', this.__handleKeydown); } - /** @protected */ + /** + * @protected + * @returns {TemplateResult|Node[]|string|null} + */ _contentTemplate() { if (this.selectedElement) { const labelNodes = Array.from(this.selectedElement.childNodes); @@ -120,6 +124,7 @@ export class LionSelectInvoker extends LionButton { /** * To be overriden for a placeholder, used when `hasNoDefaultSelected` is true on the select rich * @protected + * @returns {TemplateResult} */ // eslint-disable-next-line class-methods-use-this _noSelectionTemplate() { diff --git a/packages/select-rich/src/LionSelectRich.js b/packages/select-rich/src/LionSelectRich.js index c89f0ec48..3f0599491 100644 --- a/packages/select-rich/src/LionSelectRich.js +++ b/packages/select-rich/src/LionSelectRich.js @@ -86,9 +86,9 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L * @type {LionSelectInvoker} */ get _invokerNode() { - return /** @type {LionSelectInvoker} */ (Array.from(this.children).find( - child => child.slot === 'invoker', - )); + return /** @type {LionSelectInvoker} */ ( + Array.from(this.children).find(child => child.slot === 'invoker') + ); } /** @@ -141,9 +141,8 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L connectedCallback() { super.connectedCallback(); - this._invokerNode.selectedElement = this.formElements[ - /** @type {number} */ (this.checkedIndex) - ]; + this._invokerNode.selectedElement = + this.formElements[/** @type {number} */ (this.checkedIndex)]; this.__setupInvokerNode(); this.__toggleInvokerDisabled(); this.addEventListener('keyup', this.__onKeyUp); @@ -159,8 +158,8 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L * @param {string} name * @param {unknown} oldValue */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (name === 'interactionMode') { if (this.interactionMode === 'auto') { this.interactionMode = detectInteractionMode(); @@ -287,9 +286,8 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L __syncInvokerElement() { // sync to invoker if (this._invokerNode) { - this._invokerNode.selectedElement = this.formElements[ - /** @type {number} */ (this.checkedIndex) - ]; + this._invokerNode.selectedElement = + this.formElements[/** @type {number} */ (this.checkedIndex)]; /** * Manually update this, as the node reference may be the same, but the modelValue might not. * This would mean that it won't pass the LitElement dirty check. diff --git a/packages/select-rich/test/lion-select-invoker.test.js b/packages/select-rich/test/lion-select-invoker.test.js index e36a785da..4bdbe7716 100644 --- a/packages/select-rich/test/lion-select-invoker.test.js +++ b/packages/select-rich/test/lion-select-invoker.test.js @@ -1,5 +1,6 @@ import { LionButton } from '@lion/button'; -import { defineCE, expect, fixture, html } from '@open-wc/testing'; +import { defineCE, expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/select-rich/define-select-invoker'; import { LionSelectInvoker } from '../src/LionSelectInvoker.js'; @@ -14,12 +15,12 @@ describe('lion-select-invoker', () => { }); it('renders invoker info based on selectedElement child elements', async () => { - const el = /** @type {LionSelectInvoker} */ (await fixture( - html``, - )); - el.selectedElement = /** @type {LionOption} */ (await fixture( - `
Textnode

I am

2 lines

`, - )); + const el = /** @type {LionSelectInvoker} */ ( + await fixture(html``) + ); + el.selectedElement = /** @type {LionOption} */ ( + await fixture(`
Textnode

I am

2 lines

`) + ); await el.updateComplete; expect(el._contentWrapperNode).lightDom.to.equal( @@ -35,38 +36,38 @@ describe('lion-select-invoker', () => { }); it('renders invoker info based on selectedElement textContent', async () => { - const el = /** @type {LionSelectInvoker} */ (await fixture( - html``, - )); - el.selectedElement = /** @type {LionOption} */ (await fixture( - `
just textContent
`, - )); + const el = /** @type {LionSelectInvoker} */ ( + await fixture(html``) + ); + el.selectedElement = /** @type {LionOption} */ ( + await fixture(`
just textContent
`) + ); await el.updateComplete; expect(el._contentWrapperNode).lightDom.to.equal('just textContent'); }); it('has tabindex="0"', async () => { - const el = /** @type {LionSelectInvoker} */ (await fixture( - html``, - )); + const el = /** @type {LionSelectInvoker} */ ( + await fixture(html``) + ); expect(el.tabIndex).to.equal(0); expect(el.getAttribute('tabindex')).to.equal('0'); }); it('should not render after slot when singleOption is true', async () => { - const el = /** @type {LionSelectInvoker} */ (await fixture(html` - - `)); + const el = /** @type {LionSelectInvoker} */ ( + await fixture(html` `) + ); expect(/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('slot[name="after"]')).to.not .exist; }); it('should render after slot when singleOption is not true', async () => { - const el = /** @type {LionSelectInvoker} */ (await fixture( - html``, - )); + const el = /** @type {LionSelectInvoker} */ ( + await fixture(html``) + ); expect(/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('slot[name="after"]')).to.exist; }); @@ -85,15 +86,15 @@ describe('lion-select-invoker', () => { ); const el = /** @type {LionSelectInvoker} */ (await fixture(`<${myTag}>`)); - el.selectedElement = /** @type {LionOption} */ (await fixture( - `
cat
`, - )); + el.selectedElement = /** @type {LionOption} */ ( + await fixture(`
cat
`) + ); await el.updateComplete; expect(el._contentWrapperNode).lightDom.to.equal('cat selected'); - el.selectedElement = /** @type {LionOption} */ (await fixture( - `
dog
`, - )); + el.selectedElement = /** @type {LionOption} */ ( + await fixture(`
dog
`) + ); await el.updateComplete; expect(el._contentWrapperNode).lightDom.to.equal('no valid selection'); }); diff --git a/packages/select-rich/test/lion-select-rich-dialog-integration.test.js b/packages/select-rich/test/lion-select-rich-dialog-integration.test.js index f42ca2c64..3b3d1f178 100644 --- a/packages/select-rich/test/lion-select-rich-dialog-integration.test.js +++ b/packages/select-rich/test/lion-select-rich-dialog-integration.test.js @@ -1,6 +1,7 @@ import { OverlayMixin } from '@lion/overlays'; import { LitElement } from '@lion/core'; -import { defineCE, fixture, html, expect, unsafeStatic } from '@open-wc/testing'; +import { defineCE, fixture, expect } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import '@lion/listbox/define'; import '@lion/select-rich/define'; @@ -27,14 +28,16 @@ describe('Select Rich Integration tests', () => { let properlyInstantiated = false; try { - const nestedEl = /** @type {LionSelectRich} */ (await fixture(html` - - - Item 1 - Item 2 - - - `)); + const nestedEl = /** @type {LionSelectRich} */ ( + await fixture(html` + + + Item 1 + Item 2 + + + `) + ); await nestedEl.registrationComplete; await fixture(html` diff --git a/packages/select-rich/test/lion-select-rich-interaction.test.js b/packages/select-rich/test/lion-select-rich-interaction.test.js index de3e4bc5a..4bc366560 100644 --- a/packages/select-rich/test/lion-select-rich-interaction.test.js +++ b/packages/select-rich/test/lion-select-rich-interaction.test.js @@ -1,5 +1,6 @@ import { Required } from '@lion/form-core'; -import { expect, html, triggerBlurFor, triggerFocusFor, fixture } from '@open-wc/testing'; +import { expect, triggerBlurFor, triggerFocusFor, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import { browserDetection } from '@lion/core'; import '@lion/core/differentKeyEventNamesShimIE'; import '@lion/listbox/define'; @@ -37,45 +38,57 @@ describe('lion-select-rich interactions', () => { const originalIsMac = browserDetection.isMac; browserDetection.isMac = true; - const el = /** @type {LionSelectRich} */ (await fixture(html` - Item 1 - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + Item 1 + `) + ); expect(el.interactionMode).to.equal('mac'); - const el2 = /** @type {LionSelectRich} */ (await fixture(html` - Item 1 - `)); + const el2 = /** @type {LionSelectRich} */ ( + await fixture(html` + Item 1 + `) + ); expect(el2.interactionMode).to.equal('windows/linux'); browserDetection.isMac = false; - const el3 = /** @type {LionSelectRich} */ (await fixture(html` - Item 1 - `)); + const el3 = /** @type {LionSelectRich} */ ( + await fixture(html` + Item 1 + `) + ); expect(el3.interactionMode).to.equal('windows/linux'); - const el4 = /** @type {LionSelectRich} */ (await fixture(html` - Item 1 - `)); + const el4 = /** @type {LionSelectRich} */ ( + await fixture(html` + Item 1 + `) + ); expect(el4.interactionMode).to.equal('mac'); browserDetection.isMac = originalIsMac; }); it('derives selectionFollowsFocus and navigateWithinInvoker from interactionMode', async () => { - const el = /** @type {LionSelectRich} */ (await fixture(html` - Item 1 - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + Item 1 + `) + ); expect(el.selectionFollowsFocus).to.be.true; expect(el.navigateWithinInvoker).to.be.true; - const el2 = /** @type {LionSelectRich} */ (await fixture(html` - Item 1 - `)); + const el2 = /** @type {LionSelectRich} */ ( + await fixture(html` + Item 1 + `) + ); expect(el2.selectionFollowsFocus).to.be.false; expect(el2.navigateWithinInvoker).to.be.false; }); @@ -101,15 +114,17 @@ describe('lion-select-rich interactions', () => { }); } - const el = /** @type {LionSelectRich} */ (await fixture(html` - - - Item 1 - Item 2 - Item 3 - - - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + + + Item 1 + Item 2 + Item 3 + + + `) + ); const options = el.formElements; expect(el.checkedIndex).to.equal(0); @@ -127,32 +142,38 @@ describe('lion-select-rich interactions', () => { describe('Disabled', () => { it('invoker cannot be focused if disabled', async () => { - const el = /** @type {LionSelectRich} */ (await fixture(html` - - - - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + + + + `) + ); const { invoker } = getNodes(el); expect(invoker.tabIndex).to.equal(-1); }); it('cannot be opened via click if disabled', async () => { - const el = /** @type {LionSelectRich} */ (await fixture(html` - - - - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + + + + `) + ); const { invoker } = getNodes(el); invoker.click(); expect(el.opened).to.be.false; }); it('reflects disabled attribute to invoker', async () => { - const el = /** @type {LionSelectRich} */ (await fixture(html` - - - - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + + + + `) + ); const { invoker } = getNodes(el); expect(invoker.hasAttribute('disabled')).to.be.true; el.removeAttribute('disabled'); @@ -163,14 +184,16 @@ describe('lion-select-rich interactions', () => { describe('Interaction states', () => { it('becomes touched if blurred once', async () => { - const el = /** @type {LionSelectRich} */ (await fixture(html` - - - Item 1 - Item 2 - - - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + + + Item 1 + Item 2 + + + `) + ); const { invoker } = getNodes(el); expect(el.touched).to.be.false; await triggerFocusFor(invoker); @@ -181,14 +204,16 @@ describe('lion-select-rich interactions', () => { describe('Accessibility', () => { it('sets [aria-invalid="true"] to "._invokerNode" when there is an error', async () => { - const el = /** @type {LionSelectRich} */ (await fixture(html` - - - Please select a value - Item 1 - - - `)); + const el = /** @type {LionSelectRich} */ ( + await fixture(html` + + + Please select a value + Item 1 + + + `) + ); const { invoker } = getNodes(el); const options = el.formElements; await el.feedbackComplete; diff --git a/packages/select-rich/test/lion-select-rich.test.js b/packages/select-rich/test/lion-select-rich.test.js index 641a38189..cda11372f 100644 --- a/packages/select-rich/test/lion-select-rich.test.js +++ b/packages/select-rich/test/lion-select-rich.test.js @@ -3,15 +3,8 @@ import { renderLitAsNode } from '@lion/helpers'; import { OverlayController } from '@lion/overlays'; import { LionOption } from '@lion/listbox'; import { mimicClick } from '@lion/overlays/test-helpers'; -import { - aTimeout, - defineCE, - expect, - html, - nextFrame, - unsafeStatic, - fixture as _fixture, -} from '@open-wc/testing'; +import { aTimeout, defineCE, expect, nextFrame, fixture as _fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { LionSelectInvoker, LionSelectRich } from '@lion/select-rich'; import '@lion/core/differentKeyEventNamesShimIE'; import '@lion/listbox/define'; @@ -164,9 +157,9 @@ describe('lion-select-rich', () => { ); const tagString = unsafeStatic(tag); - const firstOption = /** @type {LionOption} */ (renderLitAsNode( - html`<${tagString} checked .choiceValue=${10}>`, - )); + const firstOption = /** @type {LionOption} */ ( + renderLitAsNode(html`<${tagString} checked .choiceValue=${10}>`) + ); const el = await fixture(html` @@ -238,9 +231,9 @@ describe('lion-select-rich', () => { it('syncs opened state with overlay shown', async () => { const el = await fixture(html` `); - const outerEl = /** @type {HTMLButtonElement} */ (await _fixture( - '', - )); + const outerEl = /** @type {HTMLButtonElement} */ ( + await _fixture('') + ); expect(el.opened).to.be.true; @@ -384,8 +377,9 @@ describe('lion-select-rich', () => { expect(el.singleOption).to.be.false; expect(_invokerNode.singleOption).to.be.false; - const optionELm = el.formElements[0]; - optionELm.parentNode.removeChild(optionELm); + const optionElm = el.formElements[0]; + optionElm.parentNode.removeChild(optionElm); + // @ts-ignore [test] we don't need args in this case el.requestUpdate(); await el.updateComplete; expect(el.singleOption).to.be.true; @@ -394,6 +388,7 @@ describe('lion-select-rich', () => { const newOption = /** @type {LionOption} */ (document.createElement('lion-option')); newOption.choiceValue = 30; _inputNode.appendChild(newOption); + // @ts-ignore [test] allow to not provide args for testing purposes el.requestUpdate(); await el.updateComplete; expect(el.singleOption).to.be.false; @@ -665,14 +660,14 @@ describe('lion-select-rich', () => { } }, ); - const invokerTag = unsafeStatic(invokerTagName); + // const invokerTag = unsafeStatic(invokerTagName); const selectTagName = defineCE( class extends LionSelectRich { get slots() { return { ...super.slots, - invoker: () => document.createElement(invokerTag.d), + invoker: () => document.createElement(invokerTagName), }; } }, diff --git a/packages/select/test/lion-select.test.js b/packages/select/test/lion-select.test.js index 0095be980..7a760eed1 100644 --- a/packages/select/test/lion-select.test.js +++ b/packages/select/test/lion-select.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/select/define'; diff --git a/packages/steps/test/lion-step.test.js b/packages/steps/test/lion-step.test.js index f78d0a905..d92a015eb 100644 --- a/packages/steps/test/lion-step.test.js +++ b/packages/steps/test/lion-step.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html, oneEvent } from '@open-wc/testing'; +import { expect, fixture, oneEvent } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import '@lion/steps/define-step'; diff --git a/packages/steps/test/lion-steps.test.js b/packages/steps/test/lion-steps.test.js index cf32329dc..1fc3213ff 100644 --- a/packages/steps/test/lion-steps.test.js +++ b/packages/steps/test/lion-steps.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, html, oneEvent } from '@open-wc/testing'; +import { expect, fixture as _fixture, oneEvent } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import '@lion/steps/define'; diff --git a/packages/switch/src/LionSwitchButton.js b/packages/switch/src/LionSwitchButton.js index ea644bbf9..612fcb9a1 100644 --- a/packages/switch/src/LionSwitchButton.js +++ b/packages/switch/src/LionSwitchButton.js @@ -157,8 +157,8 @@ export class LionSwitchButton extends DisabledWithTabIndexMixin(LitElement) { * @param {?} oldValue * @override */ - requestUpdateInternal(name, oldValue) { - super.requestUpdateInternal(name, oldValue); + requestUpdate(name, oldValue) { + super.requestUpdate(name, oldValue); if (this.isConnected && name === 'checked' && this.checked !== oldValue) { this.__checkedStateChange(); } diff --git a/packages/switch/test/lion-switch-button.test.js b/packages/switch/test/lion-switch-button.test.js index d4ec3fce6..c03d3aa1a 100644 --- a/packages/switch/test/lion-switch-button.test.js +++ b/packages/switch/test/lion-switch-button.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, html } from '@open-wc/testing'; +import { expect, fixture as _fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import '@lion/switch/define-switch-button'; diff --git a/packages/switch/test/lion-switch.test.js b/packages/switch/test/lion-switch.test.js index 37cd55abb..e23ed9e48 100644 --- a/packages/switch/test/lion-switch.test.js +++ b/packages/switch/test/lion-switch.test.js @@ -1,4 +1,5 @@ -import { expect, fixture as _fixture, html } from '@open-wc/testing'; +import { expect, fixture as _fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import { Validator } from '@lion/form-core'; import { LionSwitch } from '@lion/switch'; diff --git a/packages/tabs/test/lion-tabs.test.js b/packages/tabs/test/lion-tabs.test.js index 113097e45..7ea9d6d3f 100644 --- a/packages/tabs/test/lion-tabs.test.js +++ b/packages/tabs/test/lion-tabs.test.js @@ -1,4 +1,5 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; /** @@ -25,24 +26,30 @@ describe('', () => { }); it('can programmatically set selectedIndex', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - -
tab 1
-
panel 1
-
tab 2
-
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + +
tab 1
+
panel 1
+
tab 2
+
panel 2
+
+ `) + ); expect(el.selectedIndex).to.equal(1); - let selectedTab = /** @type {Element} */ (Array.from(el.children).find( - child => child.slot === 'tab' && child.hasAttribute('selected'), - )); + let selectedTab = /** @type {Element} */ ( + Array.from(el.children).find( + child => child.slot === 'tab' && child.hasAttribute('selected'), + ) + ); expect(selectedTab.textContent).to.equal('tab 2'); el.selectedIndex = 0; - selectedTab = /** @type {Element} */ (Array.from(el.children).find( - child => child.slot === 'tab' && child.hasAttribute('selected'), - )); + selectedTab = /** @type {Element} */ ( + Array.from(el.children).find( + child => child.slot === 'tab' && child.hasAttribute('selected'), + ) + ); expect(selectedTab.textContent).to.equal('tab 1'); }); @@ -82,33 +89,39 @@ describe('', () => { }); it('only takes direct children into account', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
- -
nested panel
-
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
+ +
nested panel
+
+ +
panel 2
+
+ `) + ); el.selectedIndex = 1; - const selectedTab = /** @type {Element} */ (Array.from(el.children).find( - child => child.slot === 'tab' && child.hasAttribute('selected'), - )); + const selectedTab = /** @type {Element} */ ( + Array.from(el.children).find( + child => child.slot === 'tab' && child.hasAttribute('selected'), + ) + ); expect(selectedTab.textContent).to.equal('tab 2'); }); }); describe('Tabs ([slot=tab])', () => { it('adds role=tab', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel
+
+ `) + ); expect(Array.from(el.children).find(child => child.slot === 'tab')).to.have.attribute( 'role', 'tab', @@ -163,16 +176,18 @@ describe('', () => { }); it('selects previous tab on [arrow-left] and [arrow-up]', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
- -
panel 3
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+ +
panel 3
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); el.selectedIndex = 2; tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' })); @@ -182,14 +197,16 @@ describe('', () => { }); it('selects first tab on [home]', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); tabs[1].dispatchEvent(new KeyboardEvent('keyup', { key: 'Home' })); expect(el.selectedIndex).to.equal(0); @@ -203,32 +220,36 @@ describe('', () => { }); it('selects first tab on [arrow-right] if on last tab', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
- -
panel 3
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+ +
panel 3
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' })); expect(el.selectedIndex).to.equal(0); }); it('selects last tab on [arrow-left] if on first tab', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
- -
panel 3
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+ +
panel 3
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' })); expect(el.selectedIndex).to.equal(2); @@ -266,14 +287,16 @@ describe('', () => { describe('Initializing without Focus', () => { it('does not focus a tab when setting selectedIndex property', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); el.selectedIndex = 1; expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be @@ -281,27 +304,31 @@ describe('', () => { }); it('does not focus a tab on firstUpdate', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); const tabs = Array.from(el.children).filter(child => child.slot === 'tab'); expect(tabs.some(tab => tab === document.activeElement)).to.be.false; }); it('focuses on a tab when setting with _setSelectedIndexWithFocus method', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); // @ts-ignore : this el is LionTabs el._setSelectedIndexWithFocus(1); @@ -310,14 +337,16 @@ describe('', () => { }); it('focuses on a tab when the selected tab is changed by user interaction', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); const secondTab = /** @type {Element} */ (el.querySelector('[slot="tab"]:nth-of-type(2)')); secondTab.dispatchEvent(new MouseEvent('click')); expect(secondTab === document.activeElement).to.be.true; @@ -325,14 +354,16 @@ describe('', () => { describe('Accessibility', () => { it('does not make panels focusable', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); expect(Array.from(el.children).find(child => child.slot === 'panel')).to.not.have.attribute( 'tabindex', ); @@ -342,16 +373,18 @@ describe('', () => { }); it('makes selected tab focusable (other tabs are unfocusable)', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
- -
panel 3
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+ +
panel 3
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); expect(tabs[0]).to.have.attribute('tabindex', '0'); expect(tabs[1]).to.have.attribute('tabindex', '-1'); @@ -360,14 +393,16 @@ describe('', () => { describe('Tabs', () => { it('links ids of content items to tab via [aria-controls]', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); const panels = el.querySelectorAll('[slot=panel]'); expect(tabs[0].getAttribute('aria-controls')).to.equal(panels[0].id); @@ -375,16 +410,18 @@ describe('', () => { }); it('adds aria-selected=“true” to selected tab', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
- -
panel 3
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+ +
panel 3
+
+ `) + ); const tabs = el.querySelectorAll('[slot=tab]'); expect(tabs[0].getAttribute('aria-selected')).to.equal('true'); @@ -395,28 +432,32 @@ describe('', () => { describe('panels', () => { it('adds role="tabpanel" to panels', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); const panels = el.querySelectorAll('[slot=panel]'); expect(panels[0]).to.have.attribute('role', 'tabpanel'); expect(panels[1]).to.have.attribute('role', 'tabpanel'); }); it('adds aria-labelledby referring to tab ids', async () => { - const el = /** @type {LionTabs} */ (await fixture(html` - - -
panel 1
- -
panel 2
-
- `)); + const el = /** @type {LionTabs} */ ( + await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `) + ); const panels = el.querySelectorAll('[slot=panel]'); const tabs = el.querySelectorAll('[slot=tab]'); expect(panels[0]).to.have.attribute('aria-labelledby', tabs[0].id); diff --git a/packages/textarea/src/LionTextarea.js b/packages/textarea/src/LionTextarea.js index dad00450a..bf8b8ac4f 100644 --- a/packages/textarea/src/LionTextarea.js +++ b/packages/textarea/src/LionTextarea.js @@ -10,9 +10,9 @@ class LionFieldWithTextArea extends LionField { * @protected */ get _inputNode() { - return /** @type {HTMLTextAreaElement} */ (Array.from(this.children).find( - el => el.slot === 'input', - )); + return /** @type {HTMLTextAreaElement} */ ( + Array.from(this.children).find(el => el.slot === 'input') + ); } } @@ -161,6 +161,9 @@ export class LionTextarea extends NativeTextFieldMixin(LionFieldWithTextArea) { ]; } + /** + * @returns {Promise|Promise} + */ get updateComplete() { if (this.__textareaUpdateComplete) { return Promise.all([this.__textareaUpdateComplete, super.updateComplete]); diff --git a/packages/textarea/test/lion-textarea.test.js b/packages/textarea/test/lion-textarea.test.js index 601dc9e4a..0e0551056 100644 --- a/packages/textarea/test/lion-textarea.test.js +++ b/packages/textarea/test/lion-textarea.test.js @@ -1,4 +1,5 @@ -import { aTimeout, expect, fixture as _fixture, html } from '@open-wc/testing'; +import { aTimeout, expect, fixture as _fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; import sinon from 'sinon'; import '@lion/textarea/define'; import { getFormControlMembers } from '@lion/form-core/test-helpers'; diff --git a/packages/tooltip/test/lion-tooltip.test.js b/packages/tooltip/test/lion-tooltip.test.js index de733b96a..1009af4fb 100644 --- a/packages/tooltip/test/lion-tooltip.test.js +++ b/packages/tooltip/test/lion-tooltip.test.js @@ -1,4 +1,5 @@ -import { aTimeout, expect, fixture, html, unsafeStatic } from '@open-wc/testing'; +import { aTimeout, expect, fixture } from '@open-wc/testing'; +import { html, unsafeStatic } from 'lit/static-html.js'; import { runOverlayMixinSuite } from '../../overlays/test-suites/OverlayMixin.suite.js'; import '@lion/tooltip/define'; @@ -21,12 +22,14 @@ describe('lion-tooltip', () => { describe('Basic', () => { it('shows content on mouseenter and hide on mouseleave', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); const eventMouseEnter = new Event('mouseenter'); el.dispatchEvent(eventMouseEnter); await el.updateComplete; @@ -41,12 +44,14 @@ describe('lion-tooltip', () => { }); it('shows content on mouseenter and remain shown on focusout', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); const eventMouseEnter = new Event('mouseenter'); el.dispatchEvent(eventMouseEnter); await el.updateComplete; @@ -60,15 +65,17 @@ describe('lion-tooltip', () => { }); it('shows content on focusin and hide on focusout', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); - const invoker = /** @type {HTMLElement} */ (Array.from(el.children).find( - child => child.slot === 'invoker', - )); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); + const invoker = /** @type {HTMLElement} */ ( + Array.from(el.children).find(child => child.slot === 'invoker') + ); const eventFocusIn = new Event('focusin'); invoker.dispatchEvent(eventFocusIn); await el.updateComplete; @@ -83,15 +90,17 @@ describe('lion-tooltip', () => { }); it('shows content on focusin and remain shown on mouseleave', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); - const invoker = /** @type {HTMLElement} */ (Array.from(el.children).find( - child => child.slot === 'invoker', - )); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); + const invoker = /** @type {HTMLElement} */ ( + Array.from(el.children).find(child => child.slot === 'invoker') + ); const eventFocusIn = new Event('focusin'); invoker.dispatchEvent(eventFocusIn); await el.updateComplete; @@ -105,15 +114,17 @@ describe('lion-tooltip', () => { }); it('stays hidden on disabled invoker', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); - const invoker = /** @type {HTMLElement} */ (Array.from(el.children).find( - child => child.slot === 'invoker', - )); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); + const invoker = /** @type {HTMLElement} */ ( + Array.from(el.children).find(child => child.slot === 'invoker') + ); const eventMouseEnter = new Event('mouseenter'); el.dispatchEvent(eventMouseEnter); await el.updateComplete; @@ -127,15 +138,17 @@ describe('lion-tooltip', () => { }); it('stays hidden on aria-disabled invoker', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); - const invoker = /** @type {HTMLElement} */ (Array.from(el.children).find( - child => child.slot === 'invoker', - )); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); + const invoker = /** @type {HTMLElement} */ ( + Array.from(el.children).find(child => child.slot === 'invoker') + ); const eventMouseEnter = new Event('mouseenter'); el.dispatchEvent(eventMouseEnter); await el.updateComplete; @@ -149,17 +162,19 @@ describe('lion-tooltip', () => { }); it('contains html when specified in tooltip content body', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
- This is Tooltip using overlay -
- -
- `)); - const invoker = /** @type {HTMLElement} */ (Array.from(el.children).find( - child => child.slot === 'invoker', - )); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
+ This is Tooltip using overlay +
+ +
+ `) + ); + const invoker = /** @type {HTMLElement} */ ( + Array.from(el.children).find(child => child.slot === 'invoker') + ); const event = new Event('mouseenter'); invoker.dispatchEvent(event); await el.updateComplete; @@ -169,34 +184,38 @@ describe('lion-tooltip', () => { describe('Arrow', () => { it('shows when "has-arrow" is configured', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
- This is Tooltip using overlay -
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
+ This is Tooltip using overlay +
+ +
+ `) + ); expect(el._arrowNode).to.be.displayed; }); it('makes sure positioning of the arrow is correct', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); el.opened = true; @@ -221,12 +240,14 @@ describe('lion-tooltip', () => { describe('Positioning', () => { it('updates popper positioning correctly, without overriding other modifiers', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
-
Tooltip button
-
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+
Tooltip button
+
+ `) + ); await aTimeout(0); // @ts-expect-error allow protected props in tests @@ -253,12 +274,14 @@ describe('lion-tooltip', () => { describe('Accessibility', () => { it('should have a tooltip role set on the tooltip', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); // FIXME: This should be refactored to Array.from(this.children).find(child => child.slot === 'content'). // When this issue is fixed https://github.com/ing-bank/lion/issues/382 @@ -267,12 +290,14 @@ describe('lion-tooltip', () => { }); it('should have aria-describedby role set on the invoker', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); const content = /** @type {HTMLElement} */ (el.querySelector('[slot=content]')); const invoker = /** @type {HTMLElement} */ (el.querySelector('[slot=invoker]')); expect(invoker.getAttribute('aria-describedby')).to.be.equal(content.id); @@ -280,12 +305,14 @@ describe('lion-tooltip', () => { }); it('should have aria-labelledby role set on the invoker when [ invoker-relation="label"]', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); const content = /** @type {HTMLElement} */ (el.querySelector('[slot=content]')); const invoker = /** @type {HTMLElement} */ (el.querySelector('[slot=invoker]')); expect(invoker.getAttribute('aria-describedby')).to.be.equal(null); @@ -293,22 +320,26 @@ describe('lion-tooltip', () => { }); it('should be accessible when closed', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); await expect(el).to.be.accessible; }); it('should be accessible when opened', async () => { - const el = /** @type {LionTooltip} */ (await fixture(html` - -
Hey there
- -
- `)); + const el = /** @type {LionTooltip} */ ( + await fixture(html` + +
Hey there
+ +
+ `) + ); const invoker = /** @type {HTMLElement} */ (el.querySelector('[slot="invoker"]')); const eventFocusIn = new Event('focusin'); invoker.dispatchEvent(eventFocusIn); diff --git a/packages/validate-messages/src/loadDefaultFeedbackMessages.js b/packages/validate-messages/src/loadDefaultFeedbackMessages.js index e14d48391..083b74ea5 100644 --- a/packages/validate-messages/src/loadDefaultFeedbackMessages.js +++ b/packages/validate-messages/src/loadDefaultFeedbackMessages.js @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { localize } from '@lion/localize'; import { DefaultSuccess,