@lion/ui 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/calendar/src/LionCalendar.js +5 -1
- package/components/combobox/test/lion-combobox.test.js +13 -11
- package/components/core/src/ScopedElementsMixin.js +2 -2
- package/components/core/test/ScopedElementsMixin.test.js +34 -1
- package/components/core/test/SlotMixin.test.js +19 -33
- package/components/core/test-helpers/isActiveElement.js +14 -0
- package/components/dialog/test/lion-dialog.test.js +6 -5
- package/components/form/test/lion-form.test.js +20 -18
- package/components/form-core/test/lion-field.test.js +15 -14
- package/components/form-core/test-suites/NativeTextFieldMixin.suite.js +8 -6
- package/components/form-core/test-suites/ValidateMixin.suite.js +7 -7
- package/components/form-integrations/test/dialog-integrations.test.js +6 -5
- package/components/input-datepicker/test/lion-input-datepicker.test.js +25 -1
- package/components/overlays/src/OverlayController.js +25 -1
- package/components/overlays/src/OverlayMixin.js +17 -4
- package/components/overlays/src/utils/adopt-styles.js +3 -3
- package/components/overlays/src/utils/contain-focus.js +1 -1
- package/components/overlays/test/OverlayController.test.js +46 -13
- package/components/overlays/test/utils-tests/contain-focus.test.js +21 -16
- package/components/select-rich/test/lion-select-rich.test.js +15 -17
- package/components/switch/test/lion-switch.test.js +7 -6
- package/custom-elements.json +157 -7
- package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
- package/dist-types/components/combobox/test/lion-combobox.test.d.ts.map +1 -1
- package/dist-types/components/{overlays/src/utils/get-deep-active-element.d.ts → core/src/getDeepActiveElement.d.ts} +1 -1
- package/dist-types/components/core/src/getDeepActiveElement.d.ts.map +1 -0
- package/dist-types/components/core/test/SlotMixin.test.d.ts.map +1 -1
- package/dist-types/components/core/test-helpers/isActiveElement.d.ts +13 -0
- package/dist-types/components/core/test-helpers/isActiveElement.d.ts.map +1 -0
- package/dist-types/components/dialog/test/lion-dialog.test.d.ts.map +1 -1
- package/dist-types/components/form/test/lion-form.test.d.ts.map +1 -1
- package/dist-types/components/form-core/test/lion-field.test.d.ts +2 -2
- package/dist-types/components/form-core/test/lion-field.test.d.ts.map +1 -1
- package/dist-types/components/form-core/test-suites/NativeTextFieldMixin.suite.d.ts +2 -2
- package/dist-types/components/form-core/test-suites/NativeTextFieldMixin.suite.d.ts.map +1 -1
- package/dist-types/components/form-integrations/test/dialog-integrations.test.d.ts.map +1 -1
- package/dist-types/components/overlays/src/OverlayController.d.ts +2 -0
- package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
- package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
- package/dist-types/components/overlays/test/OverlayController.test.d.ts.map +1 -1
- package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts +2 -2
- package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts.map +1 -1
- package/dist-types/components/switch/test/lion-switch.test.d.ts +1 -1
- package/dist-types/components/switch/test/lion-switch.test.d.ts.map +1 -1
- package/dist-types/exports/overlays.d.ts +2 -2
- package/dist-types/tsconfig.tsbuildinfo +1 -1
- package/docs/components/checkbox-group/overview.md +2 -2
- package/docs/components/collapsible/overview.md +1 -1
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/overview.md +1 -1
- package/docs/components/fieldset/overview.md +5 -5
- package/docs/components/form/overview.md +3 -3
- package/docs/components/form/use-cases.md +157 -8
- package/docs/components/icon/use-cases.md +1 -1
- package/docs/components/input/overview.md +1 -1
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/overview.md +5 -5
- package/docs/components/input-email/overview.md +2 -2
- package/docs/components/input-file/overview.md +1 -1
- package/docs/components/input-iban/overview.md +2 -2
- package/docs/components/input-range/overview.md +2 -2
- package/docs/components/input-stepper/overview.md +1 -1
- package/docs/components/input-tel/overview.md +1 -1
- package/docs/components/input-tel/use-cases.md +6 -6
- package/docs/components/input-tel-dropdown/overview.md +1 -1
- package/docs/components/listbox/use-cases.md +1 -1
- package/docs/components/radio-group/overview.md +3 -3
- package/docs/components/select/use-cases.md +1 -1
- package/docs/components/select-rich/use-cases.md +1 -1
- package/docs/components/tabs/examples.md +1 -1
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/providence-analytics/overview.md +42 -139
- package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
- package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
- package/docs/fundamentals/rationales/accessibility.md +1 -1
- package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
- package/docs/fundamentals/systems/core/overview.md +1 -1
- package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
- package/docs/fundamentals/systems/form/model-value.md +1 -1
- package/docs/fundamentals/systems/form/overview.md +28 -28
- package/docs/fundamentals/systems/form/validate.md +2 -2
- package/docs/fundamentals/systems/localize/overview.md +4 -4
- package/docs/fundamentals/systems/overlays/configuration.md +2 -2
- package/docs/fundamentals/systems/overlays/overview.md +5 -5
- package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
- package/docs/fundamentals/tools/helpers/overview.md +1 -1
- package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
- package/docs/guides/how-to/create-a-custom-field.md +5 -5
- package/docs/guides/how-to/create-a-form.md +4 -4
- package/docs/guides/how-to/extend-a-native-input.md +2 -2
- package/docs/guides/index.md +8 -8
- package/docs/guides/principles/index.md +5 -5
- package/docs/guides/principles/styling.md +1 -1
- package/exports/overlays.js +3 -2
- package/overlays.js +3 -2
- package/package.json +1 -1
- package/dist-types/components/overlays/src/utils/get-deep-active-element.d.ts.map +0 -1
- package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +0 -68
- package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +0 -111
- package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +0 -69
- package/docs/fundamentals/node-tools/providence-analytics/assets/_mermaid.svg.js +0 -1226
- package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +0 -22
- /package/components/{overlays/src/utils/get-deep-active-element.js → core/src/getDeepActiveElement.js} +0 -0
|
@@ -364,7 +364,11 @@ export class LionCalendar extends LocalizeMixin(LitElement) {
|
|
|
364
364
|
if (this.selectedDate) {
|
|
365
365
|
this.focusSelectedDate();
|
|
366
366
|
} else {
|
|
367
|
-
this.
|
|
367
|
+
if (!this.__isEnabledDate(/** @type {Date} */ (this.__initialCentralDate))) {
|
|
368
|
+
this.centralDate = this.findNearestEnabledDate(this.__initialCentralDate);
|
|
369
|
+
} else {
|
|
370
|
+
this.centralDate = /** @type {Date} */ (this.__initialCentralDate);
|
|
371
|
+
}
|
|
368
372
|
this.focusCentralDate();
|
|
369
373
|
}
|
|
370
374
|
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
mimicKeyPress,
|
|
5
|
-
mimicUserTyping,
|
|
6
|
-
mimicUserTypingAdvanced,
|
|
7
|
-
} from '@lion/ui/combobox-test-helpers.js';
|
|
1
|
+
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
|
2
|
+
import { Required, Unparseable } from '@lion/ui/form-core.js';
|
|
3
|
+
import { sendKeys } from '@web/test-runner-commands';
|
|
8
4
|
import { LionCombobox } from '@lion/ui/combobox.js';
|
|
9
5
|
import { browserDetection } from '@lion/ui/core.js';
|
|
10
6
|
import '@lion/ui/define/lion-combobox.js';
|
|
11
7
|
import '@lion/ui/define/lion-listbox.js';
|
|
12
8
|
import '@lion/ui/define/lion-option.js';
|
|
13
|
-
import { Required, Unparseable } from '@lion/ui/form-core.js';
|
|
14
|
-
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
|
15
|
-
import { sendKeys } from '@web/test-runner-commands';
|
|
16
9
|
import { LitElement } from 'lit';
|
|
17
10
|
import sinon from 'sinon';
|
|
11
|
+
import {
|
|
12
|
+
getFilteredOptionValues,
|
|
13
|
+
mimicUserTypingAdvanced,
|
|
14
|
+
getComboboxMembers,
|
|
15
|
+
mimicUserTyping,
|
|
16
|
+
mimicKeyPress,
|
|
17
|
+
} from '@lion/ui/combobox-test-helpers.js';
|
|
18
|
+
|
|
19
|
+
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* @typedef {import('../types/SelectionDisplay.js').SelectionDisplay} SelectionDisplay
|
|
@@ -986,7 +988,7 @@ describe('lion-combobox', () => {
|
|
|
986
988
|
options[0].click();
|
|
987
989
|
await el.updateComplete;
|
|
988
990
|
expect(el.opened).to.equal(false);
|
|
989
|
-
expect(
|
|
991
|
+
expect(isActiveElement(_inputNode)).to.be.true;
|
|
990
992
|
|
|
991
993
|
// step [4]
|
|
992
994
|
await el.updateComplete;
|
|
@@ -89,8 +89,8 @@ const ScopedElementsMixinImplementation = superclass =>
|
|
|
89
89
|
*/
|
|
90
90
|
defineScopedElement(tagName, classToBeRegistered) {
|
|
91
91
|
const registeredClass = this.registry.get(tagName);
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
92
|
+
const isNewClassWithSameName = registeredClass && registeredClass !== classToBeRegistered;
|
|
93
|
+
if (!supportsScopedRegistry() && isNewClassWithSameName) {
|
|
94
94
|
// eslint-disable-next-line no-console
|
|
95
95
|
console.error(
|
|
96
96
|
[
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
} from '@lit-labs/testing/fixtures.js';
|
|
7
7
|
import { LitElement, html } from 'lit';
|
|
8
8
|
import sinon from 'sinon';
|
|
9
|
-
import { browserDetection } from '../src/browserDetection.js';
|
|
10
9
|
|
|
11
10
|
import { ScopedElementsMixin, supportsScopedRegistry } from '../src/ScopedElementsMixin.js';
|
|
11
|
+
import { browserDetection } from '../src/browserDetection.js';
|
|
12
12
|
|
|
13
13
|
const hasRealScopedRegistrySupport = supportsScopedRegistry();
|
|
14
14
|
const originalShadowRootProps = {
|
|
@@ -78,6 +78,8 @@ describe('ScopedElementsMixin', () => {
|
|
|
78
78
|
|
|
79
79
|
describe('When scoped registries are supported', () => {
|
|
80
80
|
it('registers elements on local registry', async () => {
|
|
81
|
+
if (!hasRealScopedRegistrySupport) return;
|
|
82
|
+
|
|
81
83
|
const ceDefineSpy = sinon.spy(customElements, 'define');
|
|
82
84
|
|
|
83
85
|
const el = /** @type {ScopedElementsHost} */ (
|
|
@@ -127,5 +129,36 @@ describe('ScopedElementsMixin', () => {
|
|
|
127
129
|
expect(ceDefineSpy.calledWith('scoped-elements-child-no-reg')).to.be.true;
|
|
128
130
|
ceDefineSpy.restore();
|
|
129
131
|
});
|
|
132
|
+
|
|
133
|
+
it('fails when different classes are registered under different name', async () => {
|
|
134
|
+
class ScopedElementsHostNoReg2 extends ScopedElementsMixin(LitElement) {
|
|
135
|
+
static scopedElements = { 'scoped-elements-child-no-reg': class extends HTMLElement {} };
|
|
136
|
+
|
|
137
|
+
render() {
|
|
138
|
+
return html`<scoped-elements-child-no-reg></scoped-elements-child-no-reg>`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
customElements.define('scoped-elements-host-no-reg-2', ScopedElementsHostNoReg2);
|
|
142
|
+
|
|
143
|
+
const errorSpy = sinon.spy(console, 'error');
|
|
144
|
+
/** @type {ScopedElementsHostNoReg2} */ (
|
|
145
|
+
await fixture(html`<scoped-elements-host-no-reg></scoped-elements-host-no-reg>`)
|
|
146
|
+
);
|
|
147
|
+
/** @type {ScopedElementsHostNoReg2} */ (
|
|
148
|
+
await fixture(html`<scoped-elements-host-no-reg-2></scoped-elements-host-no-reg-2>`)
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(errorSpy.args[0][0]).to.equal(
|
|
152
|
+
[
|
|
153
|
+
'You are trying to re-register the "scoped-elements-child-no-reg" custom element with a different class via ScopedElementsMixin.',
|
|
154
|
+
'This is only possible with a CustomElementRegistry.',
|
|
155
|
+
'Your browser does not support this feature so you will need to load a polyfill for it.',
|
|
156
|
+
'Load "@webcomponents/scoped-custom-element-registry" before you register ANY web component to the global customElements registry.',
|
|
157
|
+
'e.g. add "<script src="/node_modules/@webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min.js"></script>" as your first script tag.',
|
|
158
|
+
'For more details you can visit https://open-wc.org/docs/development/scoped-elements/',
|
|
159
|
+
].join('\n'),
|
|
160
|
+
);
|
|
161
|
+
errorSpy.restore();
|
|
162
|
+
});
|
|
130
163
|
});
|
|
131
164
|
});
|
|
@@ -1,31 +1,15 @@
|
|
|
1
|
-
import sinon from 'sinon';
|
|
2
1
|
import { defineCE, expect, fixture, fixtureSync, unsafeStatic, html } from '@open-wc/testing';
|
|
3
|
-
import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
|
|
4
2
|
import { SlotMixin } from '@lion/ui/core.js';
|
|
5
3
|
import { LitElement } from 'lit';
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
|
|
6
|
+
import { ScopedElementsMixin, supportsScopedRegistry } from '../src/ScopedElementsMixin.js';
|
|
7
|
+
import { isActiveElement } from '../test-helpers/isActiveElement.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* @typedef {import('../types/SlotMixinTypes.js').SlotHost} SlotHost
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
const createElementNative = ShadowRoot.prototype.createElement;
|
|
13
|
-
function mockScopedRegistry() {
|
|
14
|
-
const outputObj = { createElementCallCount: 0 };
|
|
15
|
-
// @ts-expect-error wait for browser support
|
|
16
|
-
ShadowRoot.prototype.createElement = (tagName, options) => {
|
|
17
|
-
outputObj.createElementCallCount += 1;
|
|
18
|
-
// Return an element that lit can use as render target
|
|
19
|
-
return createElementNative(tagName, options);
|
|
20
|
-
};
|
|
21
|
-
return outputObj;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function unMockScopedRegistry() {
|
|
25
|
-
// @ts-expect-error wait for browser support
|
|
26
|
-
ShadowRoot.prototype.createElement = createElementNative;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
13
|
describe('SlotMixin', () => {
|
|
30
14
|
it('inserts provided element into light dom and sets slot', async () => {
|
|
31
15
|
const tag = defineCE(
|
|
@@ -227,12 +211,12 @@ describe('SlotMixin', () => {
|
|
|
227
211
|
);
|
|
228
212
|
const el = /** @type {* & SlotHost} */ (await fixture(`<${tag}></${tag}>`));
|
|
229
213
|
el._focusableNode.focus();
|
|
230
|
-
expect(
|
|
214
|
+
expect(isActiveElement(el._focusableNode)).to.be.true;
|
|
231
215
|
|
|
232
216
|
el.currentValue = 1;
|
|
233
217
|
await el.updateComplete;
|
|
234
218
|
|
|
235
|
-
expect(
|
|
219
|
+
expect(isActiveElement(el._focusableNode)).to.be.true;
|
|
236
220
|
});
|
|
237
221
|
|
|
238
222
|
it('keeps focus after rerendering complex shadow root into slot', async () => {
|
|
@@ -294,7 +278,7 @@ describe('SlotMixin', () => {
|
|
|
294
278
|
el.currentValue = 1;
|
|
295
279
|
await el.updateComplete;
|
|
296
280
|
|
|
297
|
-
expect(
|
|
281
|
+
expect(isActiveElement(el._focusableNode)).to.be.true;
|
|
298
282
|
expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
|
|
299
283
|
});
|
|
300
284
|
|
|
@@ -352,12 +336,12 @@ describe('SlotMixin', () => {
|
|
|
352
336
|
|
|
353
337
|
el._focusableNode._buttonNode.focus();
|
|
354
338
|
|
|
355
|
-
expect(el._focusableNode.
|
|
339
|
+
expect(isActiveElement(el._focusableNode._buttonNode, { deep: true })).to.be.true;
|
|
356
340
|
|
|
357
341
|
el.currentValue = 1;
|
|
358
342
|
await el.updateComplete;
|
|
359
343
|
|
|
360
|
-
expect(
|
|
344
|
+
expect(isActiveElement(el._focusableNode)).to.be.true;
|
|
361
345
|
expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
|
|
362
346
|
});
|
|
363
347
|
|
|
@@ -568,8 +552,11 @@ describe('SlotMixin', () => {
|
|
|
568
552
|
});
|
|
569
553
|
|
|
570
554
|
describe('Scoped Registries', () => {
|
|
571
|
-
it('supports scoped elements when polyfill loaded', async () => {
|
|
572
|
-
|
|
555
|
+
it('supports scoped elements when scoped registries supported (or polyfill loaded)', async () => {
|
|
556
|
+
if (!supportsScopedRegistry()) return;
|
|
557
|
+
|
|
558
|
+
// @ts-expect-error
|
|
559
|
+
const createElSpy = sinon.spy(ShadowRoot.prototype, 'createElement');
|
|
573
560
|
|
|
574
561
|
class ScopedEl extends LitElement {}
|
|
575
562
|
|
|
@@ -600,14 +587,14 @@ describe('SlotMixin', () => {
|
|
|
600
587
|
const tag = unsafeStatic(tagName);
|
|
601
588
|
await fixture(html`<${tag}></${tag}>`);
|
|
602
589
|
|
|
603
|
-
expect(
|
|
590
|
+
expect(createElSpy.callCount).to.equal(1);
|
|
604
591
|
|
|
605
|
-
|
|
592
|
+
createElSpy.restore();
|
|
606
593
|
});
|
|
607
594
|
|
|
608
|
-
it('does not scope elements when polyfill not loaded', async () => {
|
|
609
|
-
|
|
610
|
-
|
|
595
|
+
it('does not scope elements when scoped registries not supported (or polyfill not loaded)', async () => {
|
|
596
|
+
if (supportsScopedRegistry()) return;
|
|
597
|
+
|
|
611
598
|
class ScopedEl extends LitElement {}
|
|
612
599
|
|
|
613
600
|
const tagName = defineCE(
|
|
@@ -645,7 +632,6 @@ describe('SlotMixin', () => {
|
|
|
645
632
|
|
|
646
633
|
document.body.removeChild(renderTarget);
|
|
647
634
|
docSpy.restore();
|
|
648
|
-
unMockScopedRegistry();
|
|
649
635
|
});
|
|
650
636
|
});
|
|
651
637
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getDeepActiveElement } from '../src/getDeepActiveElement.js';
|
|
2
|
+
/**
|
|
3
|
+
* Readable alternative for `expect(el).to.equal(document.activeElement);`.
|
|
4
|
+
* While this is readable by itself, it makes Web Test Runner hang completely in many occasions.
|
|
5
|
+
* Therefore it's better to write:
|
|
6
|
+
* `expect(isActiveElement(el)).to.be.true;`
|
|
7
|
+
* @param {Element} el
|
|
8
|
+
* @param {{deep?: boolean}} opts
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
export function isActiveElement(el, { deep = false } = {}) {
|
|
12
|
+
const activeEl = deep ? getDeepActiveElement() : document.activeElement;
|
|
13
|
+
return el === activeEl;
|
|
14
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable lit-a11y/no-autofocus */
|
|
2
2
|
import { expect, fixture as _fixture, html, unsafeStatic, aTimeout } from '@open-wc/testing';
|
|
3
3
|
import { runOverlayMixinSuite } from '../../overlays/test-suites/OverlayMixin.suite.js';
|
|
4
|
+
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
|
4
5
|
import '@lion/ui/define/lion-dialog.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -89,8 +90,8 @@ describe('lion-dialog', () => {
|
|
|
89
90
|
const invokerNode = el._overlayInvokerNode;
|
|
90
91
|
invokerNode.focus();
|
|
91
92
|
invokerNode.click();
|
|
92
|
-
const contentNode = el.querySelector('[slot="content"]');
|
|
93
|
-
expect(
|
|
93
|
+
const contentNode = /** @type {Element} */ (el.querySelector('[slot="content"]'));
|
|
94
|
+
expect(isActiveElement(contentNode)).to.be.true;
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
it('sets focus on autofocused element', async () => {
|
|
@@ -107,8 +108,8 @@ describe('lion-dialog', () => {
|
|
|
107
108
|
const invokerNode = el._overlayInvokerNode;
|
|
108
109
|
invokerNode.focus();
|
|
109
110
|
invokerNode.click();
|
|
110
|
-
const input = el.querySelector('input');
|
|
111
|
-
expect(
|
|
111
|
+
const input = /** @type {Element} */ (el.querySelector('input'));
|
|
112
|
+
expect(isActiveElement(input)).to.be.true;
|
|
112
113
|
});
|
|
113
114
|
|
|
114
115
|
it('with trapsKeyboardFocus set to false the focus stays on the invoker', async () => {
|
|
@@ -125,7 +126,7 @@ describe('lion-dialog', () => {
|
|
|
125
126
|
const invokerNode = el._overlayInvokerNode;
|
|
126
127
|
invokerNode.focus();
|
|
127
128
|
invokerNode.click();
|
|
128
|
-
expect(
|
|
129
|
+
expect(isActiveElement(invokerNode)).to.be.true;
|
|
129
130
|
});
|
|
130
131
|
|
|
131
132
|
it('opened-changed event should send detail object with opened state', async () => {
|
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { LionFieldset } from '@lion/ui/fieldset.js';
|
|
2
|
-
import '@lion/ui/define/lion-fieldset.js';
|
|
3
1
|
import { LionField, Required } from '@lion/ui/form-core.js';
|
|
4
|
-
import '@lion/ui/define/lion-field.js';
|
|
5
2
|
import '@lion/ui/define/lion-validation-feedback.js';
|
|
6
|
-
import '@lion/ui/
|
|
7
|
-
import '@lion/ui/define/lion-option.js';
|
|
3
|
+
import { LionFieldset } from '@lion/ui/fieldset.js';
|
|
8
4
|
import '@lion/ui/define/lion-checkbox-group.js';
|
|
9
|
-
import '@lion/ui/define/lion-checkbox.js';
|
|
10
5
|
import '@lion/ui/define/lion-radio-group.js';
|
|
6
|
+
import '@lion/ui/define/lion-fieldset.js';
|
|
7
|
+
import '@lion/ui/define/lion-checkbox.js';
|
|
8
|
+
import '@lion/ui/define/lion-listbox.js';
|
|
9
|
+
import '@lion/ui/define/lion-option.js';
|
|
10
|
+
import '@lion/ui/define/lion-field.js';
|
|
11
11
|
import '@lion/ui/define/lion-radio.js';
|
|
12
12
|
import '@lion/ui/define/lion-form.js';
|
|
13
|
+
import { spy } from 'sinon';
|
|
13
14
|
import {
|
|
15
|
+
fixture as _fixture,
|
|
16
|
+
unsafeStatic,
|
|
14
17
|
aTimeout,
|
|
15
18
|
defineCE,
|
|
19
|
+
oneEvent,
|
|
16
20
|
expect,
|
|
17
|
-
fixture as _fixture,
|
|
18
21
|
html,
|
|
19
|
-
oneEvent,
|
|
20
|
-
unsafeStatic,
|
|
21
22
|
} from '@open-wc/testing';
|
|
22
|
-
|
|
23
|
+
|
|
24
|
+
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* @typedef {import('../src/LionForm.js').LionForm} LionForm
|
|
@@ -225,7 +227,7 @@ describe('<lion-form>', () => {
|
|
|
225
227
|
button.click();
|
|
226
228
|
expect(dispatchSpy.args[0][0].type).to.equal('submit');
|
|
227
229
|
// @ts-ignore [allow-protected] in test
|
|
228
|
-
expect(
|
|
230
|
+
expect(isActiveElement(el.formElements[1]._inputNode)).to.be.true;
|
|
229
231
|
});
|
|
230
232
|
|
|
231
233
|
it('sets focus on submit to the first erroneous form element within a fieldset', async () => {
|
|
@@ -248,7 +250,7 @@ describe('<lion-form>', () => {
|
|
|
248
250
|
expect(dispatchSpy.args[0][0].type).to.equal('submit');
|
|
249
251
|
const fieldset = el.formElements[0];
|
|
250
252
|
// @ts-ignore [allow-protected] in test
|
|
251
|
-
expect(
|
|
253
|
+
expect(isActiveElement(fieldset.formElements[1]._inputNode)).to.be.true;
|
|
252
254
|
});
|
|
253
255
|
|
|
254
256
|
it('sets focus on submit to the first form element within a erroneous fieldset', async () => {
|
|
@@ -268,7 +270,7 @@ describe('<lion-form>', () => {
|
|
|
268
270
|
button.click();
|
|
269
271
|
expect(dispatchSpy.args[0][0].type).to.equal('submit');
|
|
270
272
|
const fieldset = el.formElements[0];
|
|
271
|
-
expect(
|
|
273
|
+
expect(isActiveElement(fieldset.formElements[0]._inputNode)).to.be.true;
|
|
272
274
|
});
|
|
273
275
|
|
|
274
276
|
it('sets focus on submit to the first form element within a erroneous fieldset within another fieldset', async () => {
|
|
@@ -290,7 +292,7 @@ describe('<lion-form>', () => {
|
|
|
290
292
|
const childFieldsetEl = parentFieldSetEl.formElements[0];
|
|
291
293
|
const inputEl = childFieldsetEl.formElements[0];
|
|
292
294
|
button.click();
|
|
293
|
-
expect(
|
|
295
|
+
expect(isActiveElement(inputEl._focusableNode)).to.be.true;
|
|
294
296
|
});
|
|
295
297
|
|
|
296
298
|
it('sets focus on submit to the first form element within a erroneous listbox', async () => {
|
|
@@ -308,7 +310,7 @@ describe('<lion-form>', () => {
|
|
|
308
310
|
const button = /** @type {HTMLButtonElement} */ (el.querySelector('button'));
|
|
309
311
|
button.click();
|
|
310
312
|
const listboxEl = el.formElements[0];
|
|
311
|
-
expect(
|
|
313
|
+
expect(isActiveElement(listboxEl._inputNode)).to.be.true;
|
|
312
314
|
});
|
|
313
315
|
|
|
314
316
|
it('sets focus on submit to the first form element within a erroneous listbox within a fieldset', async () => {
|
|
@@ -329,7 +331,7 @@ describe('<lion-form>', () => {
|
|
|
329
331
|
button.click();
|
|
330
332
|
const fieldsetEl = el.formElements[0];
|
|
331
333
|
const listboxEl = fieldsetEl.formElements[0];
|
|
332
|
-
expect(
|
|
334
|
+
expect(isActiveElement(listboxEl._inputNode)).to.be.true;
|
|
333
335
|
});
|
|
334
336
|
|
|
335
337
|
it('sets focus on submit to the first form element within a erroneous checkbox-group', async () => {
|
|
@@ -348,7 +350,7 @@ describe('<lion-form>', () => {
|
|
|
348
350
|
const checkboxGroupEl = el.formElements[0];
|
|
349
351
|
const checkboxEl = checkboxGroupEl.formElements[0];
|
|
350
352
|
button.click();
|
|
351
|
-
expect(
|
|
353
|
+
expect(isActiveElement(checkboxEl._focusableNode)).to.be.true;
|
|
352
354
|
});
|
|
353
355
|
|
|
354
356
|
it('sets focus on submit to the first form element within a erroneous radio-group', async () => {
|
|
@@ -370,6 +372,6 @@ describe('<lion-form>', () => {
|
|
|
370
372
|
const radioGroupEl = el.formElements[0];
|
|
371
373
|
const radioEl = radioGroupEl.formElements[0];
|
|
372
374
|
button.click();
|
|
373
|
-
expect(
|
|
375
|
+
expect(isActiveElement(radioEl._focusableNode)).to.be.true;
|
|
374
376
|
});
|
|
375
377
|
});
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
2
|
-
import { Required, Validator } from '@lion/ui/form-core.js';
|
|
3
|
-
import '@lion/ui/define/lion-field.js';
|
|
4
1
|
import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
|
|
5
2
|
import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
|
|
6
3
|
import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
|
|
4
|
+
import { Required, Validator } from '@lion/ui/form-core.js';
|
|
5
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
6
|
+
import '@lion/ui/define/lion-field.js';
|
|
7
|
+
import sinon from 'sinon';
|
|
7
8
|
import {
|
|
8
|
-
expect,
|
|
9
|
-
fixture,
|
|
10
|
-
html,
|
|
11
|
-
triggerBlurFor,
|
|
12
9
|
triggerFocusFor,
|
|
10
|
+
triggerBlurFor,
|
|
13
11
|
unsafeStatic,
|
|
12
|
+
fixture,
|
|
13
|
+
expect,
|
|
14
|
+
html,
|
|
14
15
|
} from '@open-wc/testing';
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
|
-
* @typedef {import('../src/LionField.js').LionField} LionField
|
|
19
|
-
* @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
|
|
20
20
|
* @typedef {FormControlHost & HTMLElement & {_parentFormGroup?:HTMLElement, checked?:boolean}} FormControl
|
|
21
|
+
* @typedef {HTMLElement & {shadowRoot: HTMLElement, assignedNodes: Function}} ShadowHTMLElement
|
|
22
|
+
* @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
|
|
23
|
+
* @typedef {import('../src/LionField.js').LionField} LionField
|
|
21
24
|
*/
|
|
22
25
|
|
|
23
|
-
/** @typedef {HTMLElement & {shadowRoot: HTMLElement, assignedNodes: Function}} ShadowHTMLElement */
|
|
24
|
-
|
|
25
26
|
const tagString = 'lion-field';
|
|
26
27
|
const tag = unsafeStatic(tagString);
|
|
27
28
|
const inputSlotString = '<input slot="input" />';
|
|
@@ -106,7 +107,7 @@ describe('<lion-field>', () => {
|
|
|
106
107
|
|
|
107
108
|
await triggerFocusFor(el);
|
|
108
109
|
|
|
109
|
-
expect(
|
|
110
|
+
expect(isActiveElement(_inputNode)).to.be.true;
|
|
110
111
|
expect(cbFocusHost.callCount).to.equal(1);
|
|
111
112
|
expect(cbFocusNativeInput.callCount).to.equal(1);
|
|
112
113
|
expect(cbBlurHost.callCount).to.equal(0);
|
|
@@ -117,7 +118,7 @@ describe('<lion-field>', () => {
|
|
|
117
118
|
expect(cbBlurNativeInput.callCount).to.equal(1);
|
|
118
119
|
|
|
119
120
|
await triggerFocusFor(el);
|
|
120
|
-
expect(
|
|
121
|
+
expect(isActiveElement(_inputNode)).to.be.true;
|
|
121
122
|
expect(cbFocusHost.callCount).to.equal(2);
|
|
122
123
|
expect(cbFocusNativeInput.callCount).to.equal(2);
|
|
123
124
|
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { LitElement } from 'lit';
|
|
2
|
-
import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
|
|
3
1
|
import { defineCE, expect, fixture, html, triggerFocusFor, unsafeStatic } from '@open-wc/testing';
|
|
4
|
-
import {
|
|
5
|
-
import { spy } from 'sinon';
|
|
2
|
+
import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
|
|
6
3
|
import { NativeTextFieldMixin } from '@lion/ui/form-core.js';
|
|
4
|
+
import { sendKeys } from '@web/test-runner-commands';
|
|
7
5
|
import { browserDetection } from '@lion/ui/core.js';
|
|
6
|
+
import { LitElement } from 'lit';
|
|
7
|
+
import { spy } from 'sinon';
|
|
8
|
+
|
|
9
|
+
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
|
-
* @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
|
|
11
12
|
* @typedef {ArrayConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | StringConstructor | DateConstructor | 'iban' | 'email'} modelValueType
|
|
13
|
+
* @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -56,7 +58,7 @@ export function runNativeTextFieldMixinSuite(customConfig) {
|
|
|
56
58
|
const { _inputNode } = getFormControlMembers(el);
|
|
57
59
|
await triggerFocusFor(el);
|
|
58
60
|
await el.updateComplete;
|
|
59
|
-
expect(
|
|
61
|
+
expect(isActiveElement(_inputNode)).to.be.true;
|
|
60
62
|
await sendKeys({
|
|
61
63
|
press: 'h',
|
|
62
64
|
});
|
|
@@ -2,25 +2,25 @@ import { LitElement } from 'lit';
|
|
|
2
2
|
import { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
|
3
3
|
import {
|
|
4
4
|
getFormControlMembers,
|
|
5
|
-
AlwaysInvalid,
|
|
6
|
-
AlwaysValid,
|
|
7
5
|
AsyncAlwaysInvalid,
|
|
8
6
|
AsyncAlwaysValid,
|
|
7
|
+
AlwaysInvalid,
|
|
8
|
+
AlwaysValid,
|
|
9
9
|
} from '@lion/ui/form-core-test-helpers.js';
|
|
10
10
|
import sinon from 'sinon';
|
|
11
11
|
import {
|
|
12
|
+
ResultValidator,
|
|
13
|
+
ValidateMixin,
|
|
12
14
|
EqualsLength,
|
|
15
|
+
Unparseable,
|
|
13
16
|
MaxLength,
|
|
14
17
|
MinLength,
|
|
15
|
-
Required,
|
|
16
|
-
ResultValidator,
|
|
17
|
-
Unparseable,
|
|
18
|
-
ValidateMixin,
|
|
19
18
|
Validator,
|
|
19
|
+
Required,
|
|
20
20
|
} from '@lion/ui/form-core.js';
|
|
21
21
|
|
|
22
|
-
import '@lion/ui/define/lion-field.js';
|
|
23
22
|
import '@lion/ui/define/lion-validation-feedback.js';
|
|
23
|
+
import '@lion/ui/define/lion-field.js';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* @typedef {import('@lion/ui/form-core.js').LionField} LionField
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/* eslint-disable lit-a11y/no-autofocus */
|
|
2
2
|
import { expect, fixture } from '@open-wc/testing';
|
|
3
|
-
import { html } from 'lit';
|
|
4
|
-
import { getAllTagNames } from './helpers/helpers.js';
|
|
5
|
-
import './helpers/umbrella-form.js';
|
|
6
|
-
import '@lion/ui/define/lion-dialog.js';
|
|
7
3
|
import '@lion/ui/define/lion-checkbox.js';
|
|
4
|
+
import '@lion/ui/define/lion-dialog.js';
|
|
8
5
|
import '@lion/ui/define/lion-option.js';
|
|
9
6
|
import '@lion/ui/define/lion-radio.js';
|
|
7
|
+
import { html } from 'lit';
|
|
10
8
|
|
|
9
|
+
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
|
10
|
+
import { getAllTagNames } from './helpers/helpers.js';
|
|
11
|
+
import './helpers/umbrella-form.js';
|
|
11
12
|
/**
|
|
12
13
|
* @typedef {import('./helpers/umbrella-form.js').UmbrellaForm} UmbrellaForm
|
|
13
14
|
* @typedef {import('../../dialog/src/LionDialog.js').LionDialog} LionDialog
|
|
@@ -89,6 +90,6 @@ describe('Form inside dialog Integrations', () => {
|
|
|
89
90
|
el._overlayInvokerNode.click();
|
|
90
91
|
const lionInput = el.querySelector('[name="input"]');
|
|
91
92
|
// @ts-expect-error [allow-protected-in-tests]
|
|
92
|
-
expect(
|
|
93
|
+
expect(isActiveElement(lionInput._focusableNode)).to.be.true;
|
|
93
94
|
});
|
|
94
95
|
});
|
|
@@ -19,9 +19,10 @@ import '@lion/ui/define/lion-input-datepicker.js';
|
|
|
19
19
|
*/
|
|
20
20
|
function getProtectedMembersDatepicker(datepickerEl) {
|
|
21
21
|
// @ts-ignore
|
|
22
|
-
const { __invokerId: invokerId } = datepickerEl;
|
|
22
|
+
const { __invokerId: invokerId, _calendarNode: calendarNode } = datepickerEl;
|
|
23
23
|
return {
|
|
24
24
|
invokerId,
|
|
25
|
+
calendarNode,
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -410,6 +411,29 @@ describe('<lion-input-datepicker>', () => {
|
|
|
410
411
|
expect(el.__calendarMaxDate.toString()).to.equal(new Date('2019/07/15').toString());
|
|
411
412
|
});
|
|
412
413
|
|
|
414
|
+
it('should update the central date of Calendar if updated validator made the current central date disabled', async () => {
|
|
415
|
+
const initialValidators = [new MaxDate(new Date('2000/01/01'))];
|
|
416
|
+
const updatedValidators = [new MaxDate(new Date('2020/01/01'))];
|
|
417
|
+
|
|
418
|
+
const el = await fixture(html`
|
|
419
|
+
<lion-input-datepicker .validators="${initialValidators}"></lion-input-datepicker>
|
|
420
|
+
`);
|
|
421
|
+
const obj = new DatepickerInputObject(el);
|
|
422
|
+
await obj.openCalendar();
|
|
423
|
+
|
|
424
|
+
expect(getProtectedMembersDatepicker(el).calendarNode.centralDate.toString()).to.equal(
|
|
425
|
+
new Date('2000/01/01').toString(),
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
await obj.closeCalendar();
|
|
429
|
+
el.validators = updatedValidators;
|
|
430
|
+
await obj.openCalendar();
|
|
431
|
+
|
|
432
|
+
expect(getProtectedMembersDatepicker(el).calendarNode.centralDate.toString()).to.equal(
|
|
433
|
+
new Date('2020/01/01').toString(),
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
413
437
|
it('should show error on invalid date passed to modelValue', async () => {
|
|
414
438
|
const myDate = new Date('foo');
|
|
415
439
|
const el = await fixture(html`
|
|
@@ -192,7 +192,6 @@ export class OverlayController extends EventTarget {
|
|
|
192
192
|
zIndex: 9999,
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
-
this.manager.add(this);
|
|
196
195
|
/** @protected */
|
|
197
196
|
this._contentId = `overlay-content--${Math.random().toString(36).slice(2, 10)}`;
|
|
198
197
|
/** @private */
|
|
@@ -475,6 +474,14 @@ export class OverlayController extends EventTarget {
|
|
|
475
474
|
this._init();
|
|
476
475
|
/** @private */
|
|
477
476
|
this.__elementToFocusAfterHide = undefined;
|
|
477
|
+
|
|
478
|
+
if (!this.#isRegisteredOnManager()) {
|
|
479
|
+
this.manager.add(this);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
#isRegisteredOnManager() {
|
|
484
|
+
return Boolean(this.manager.list.find(ctrl => this === ctrl));
|
|
478
485
|
}
|
|
479
486
|
|
|
480
487
|
/**
|
|
@@ -1285,6 +1292,14 @@ export class OverlayController extends EventTarget {
|
|
|
1285
1292
|
wasMouseUpInside = false;
|
|
1286
1293
|
});
|
|
1287
1294
|
};
|
|
1295
|
+
|
|
1296
|
+
/** @type {EventListenerOrEventListenerObject} */
|
|
1297
|
+
this.__onWindowBlur = () => {
|
|
1298
|
+
// When the current window loses the focus (clicking outside iframe) the overlay gets hidden
|
|
1299
|
+
setTimeout(() => {
|
|
1300
|
+
this.hide();
|
|
1301
|
+
});
|
|
1302
|
+
};
|
|
1288
1303
|
}
|
|
1289
1304
|
|
|
1290
1305
|
this.contentWrapperNode[addOrRemoveListener](
|
|
@@ -1321,6 +1336,11 @@ export class OverlayController extends EventTarget {
|
|
|
1321
1336
|
(this.__onDocumentMouseUp),
|
|
1322
1337
|
true,
|
|
1323
1338
|
);
|
|
1339
|
+
window[addOrRemoveListener](
|
|
1340
|
+
'blur',
|
|
1341
|
+
/** @type {EventListenerOrEventListenerObject} */
|
|
1342
|
+
(this.__onWindowBlur),
|
|
1343
|
+
);
|
|
1324
1344
|
}
|
|
1325
1345
|
|
|
1326
1346
|
/**
|
|
@@ -1341,6 +1361,10 @@ export class OverlayController extends EventTarget {
|
|
|
1341
1361
|
this.__handleOverlayStyles({ phase: 'teardown' });
|
|
1342
1362
|
this._handleFeatures({ phase: 'teardown' });
|
|
1343
1363
|
this.__wrappingDialogNode?.removeEventListener('cancel', this.__cancelHandler);
|
|
1364
|
+
|
|
1365
|
+
if (this.#isRegisteredOnManager()) {
|
|
1366
|
+
this.manager.remove(this);
|
|
1367
|
+
}
|
|
1344
1368
|
}
|
|
1345
1369
|
|
|
1346
1370
|
/** @private */
|