@lion/ui 0.21.1-menu-system.0 → 0.22.0
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/button/src/LionButton.js +0 -4
- package/components/form/test/lion-form.test.js +4 -3
- package/components/form-core/src/form-group/FormGroupMixin.js +37 -0
- package/components/form-core/test-suites/form-group/FormGroupMixin.suite.js +25 -0
- package/components/input-amount-dropdown/src/LionInputAmountDropdown.js +2 -2
- package/components/input-amount-dropdown/test/lion-input-amount-dropdown-integrations.test.js +12 -3
- package/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.js +10 -3
- package/components/overlays/src/OverlayController.js +111 -296
- package/components/overlays/src/OverlayMixin.js +17 -76
- package/components/overlays/src/utils/adopt-styles.js +1 -1
- package/components/overlays/src/utils/is-equal-config.js +6 -15
- package/components/overlays/test/OverlayController.test.js +24 -144
- package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
- package/components/overlays/types/ArrowMixinTypes.ts +3 -1
- package/components/overlays/types/OverlayConfig.ts +5 -20
- package/components/overlays/types/OverlayMixinTypes.ts +0 -2
- package/custom-elements.json +6260 -13587
- package/dist-types/components/button/src/LionButton.d.ts +0 -1
- package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
- package/dist-types/components/combobox/src/LionCombobox.d.ts +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +2 -6
- package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
- package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts +1 -0
- package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts.map +1 -1
- package/dist-types/components/form-core/test-suites/form-group/FormGroupMixin.suite.d.ts.map +1 -1
- package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts.map +1 -1
- package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
- package/dist-types/components/overlays/src/OverlayController.d.ts +5 -22
- 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/src/utils/is-equal-config.d.ts.map +1 -1
- package/dist-types/components/overlays/types/ArrowMixinTypes.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayConfig.d.ts +2 -12
- package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +0 -1
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +2 -6
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
- package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
- package/dist-types/exports/overlays.d.ts +0 -6
- package/dist-types/exports/overlays.d.ts.map +1 -1
- 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/api-table.md +39 -53
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/api-table.md +6 -32
- 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 +2 -2
- 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/api-table.md +45 -63
- 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/api-table.md +33 -50
- package/docs/components/select-rich/use-cases.md +2 -2
- package/docs/components/tabs/examples.md +1 -1
- package/docs/components/tooltip/api-table.md +7 -33
- package/docs/fundamentals/index.md +4 -4
- 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/api-table.md +43 -46
- 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/how-to/index.md +1 -1
- 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 +0 -6
- package/overlays.js +0 -6
- package/package.json +6 -2
- package/vscode.css-custom-data.json +5 -0
- package/vscode.html-custom-data.json +789 -0
- package/web-types.json +2016 -0
- package/components/menu/CHANGELOG.md +0 -37
- package/components/menu/README.md +0 -334
- package/components/menu/index.js +0 -2
- package/components/menu/lion-menu.js +0 -3
- package/components/menu/package.json +0 -46
- package/components/menu/requirements.temp.md +0 -12
- package/components/menu/src/InteractiveListMixin.js +0 -840
- package/components/menu/src/LionMenu.js +0 -421
- package/components/menu/src/MoreButtonMenuMixin.js +0 -278
- package/components/menu/src/MultiLevelListMixin.js +0 -305
- package/components/menu/src/utils/isInView.js +0 -26
- package/components/menu/src/utils/listItemInteractions.js +0 -124
- package/components/menu/test/InteractiveListMixin.test.js +0 -3
- package/components/menu/test/MoreButtonMenuMixin.test.js +0 -4
- package/components/menu/test/MultiLevelListMixin.test.js +0 -3
- package/components/menu/test/lion-menu-interactions.test.js +0 -3
- package/components/menu/test/lion-menu.test.js +0 -14
- package/components/menu/test-helpers/mimicKeyPress.js +0 -9
- package/components/menu/test-suites/InteractiveListMixin.suite.js +0 -454
- package/components/menu/test-suites/LionMenu.suite.js +0 -114
- package/components/menu/test-suites/LionMenuInteractions.suite.js +0 -583
- package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +0 -372
- package/components/menu/test-suites/MultiLevelListMixin.suite.js +0 -303
- package/components/menu/types/InteractiveListMixinTypes.d.ts +0 -84
- package/components/overlays/NEXT.md +0 -49
- package/dist-types/components/menu/index.d.ts +0 -3
- package/dist-types/components/menu/index.d.ts.map +0 -1
- package/dist-types/components/menu/lion-menu.d.ts +0 -2
- package/dist-types/components/menu/lion-menu.d.ts.map +0 -1
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +0 -39
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +0 -1
- package/dist-types/components/menu/src/utils/isInView.d.ts +0 -9
- package/dist-types/components/menu/src/utils/isInView.d.ts.map +0 -1
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +0 -48
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +0 -1
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts +0 -2
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts +0 -2
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts +0 -2
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts +0 -2
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/lion-menu.test.d.ts +0 -2
- package/dist-types/components/menu/test/lion-menu.test.d.ts.map +0 -1
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts +0 -7
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +0 -11
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +0 -9
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts +0 -5
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +0 -11
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +0 -11
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +0 -1
- package/dist-types/exports/define/lion-item.d.ts +0 -2
- package/dist-types/exports/define/lion-item.d.ts.map +0 -1
- package/dist-types/exports/define/lion-menu.d.ts +0 -2
- package/dist-types/exports/define/lion-menu.d.ts.map +0 -1
- package/dist-types/exports/menu-test-suites.d.ts +0 -4
- package/dist-types/exports/menu-test-suites.d.ts.map +0 -1
- package/dist-types/exports/menu.d.ts +0 -3
- package/dist-types/exports/menu.d.ts.map +0 -1
- package/docs/components/menu/api-table.md +0 -121
- package/docs/components/menu/applyMenuDemoStyles.js +0 -173
- package/docs/components/menu/extensions.md +0 -262
- package/docs/components/menu/index.md +0 -13
- package/docs/components/menu/overview.md +0 -422
- package/docs/components/menu/src/DemoNavigationBar.js +0 -441
- package/docs/components/menu/src/DemoSpinButton.js +0 -215
- package/docs/components/menu/src/DemoToolbar.js +0 -101
- package/docs/components/menu/src/DemoTree.js +0 -55
- package/docs/components/menu/src/navigation-bar-types.js +0 -41
- package/docs/components/menu/use-cases.md +0 -74
- package/exports/define/lion-item.js +0 -3
- package/exports/define/lion-menu.js +0 -3
- package/exports/menu-test-suites.js +0 -4
- package/exports/menu.js +0 -3
- package/menu-test-suites.js +0 -4
- package/menu.js +0 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
2
2
|
import { OverlayController } from './OverlayController.js';
|
|
3
|
+
import { isEqualConfig } from './utils/is-equal-config.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @typedef {<T>(ingOverlayHost:T) => void} PostProcessor
|
|
@@ -31,7 +32,10 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
31
32
|
class OverlayMixin extends superclass {
|
|
32
33
|
static get properties() {
|
|
33
34
|
return {
|
|
34
|
-
opened: {
|
|
35
|
+
opened: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
reflect: true,
|
|
38
|
+
},
|
|
35
39
|
};
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -65,13 +69,13 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
65
69
|
|
|
66
70
|
/** @param {OverlayConfig} value */
|
|
67
71
|
set config(value) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (this._overlayCtrl) {
|
|
72
|
+
const shouldUpdate = !isEqualConfig(this.config, value);
|
|
73
|
+
|
|
74
|
+
if (this._overlayCtrl && shouldUpdate) {
|
|
71
75
|
this._overlayCtrl.updateConfig(value);
|
|
72
76
|
}
|
|
73
77
|
this.__config = value;
|
|
74
|
-
if (this._overlayCtrl) {
|
|
78
|
+
if (this._overlayCtrl && shouldUpdate) {
|
|
75
79
|
this.__syncToOverlayController();
|
|
76
80
|
}
|
|
77
81
|
}
|
|
@@ -156,8 +160,6 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
156
160
|
*/
|
|
157
161
|
// eslint-disable-next-line class-methods-use-this
|
|
158
162
|
_setupOpenCloseListeners() {
|
|
159
|
-
// Keeps the close-overlay event private for the controller.
|
|
160
|
-
// (we already have )
|
|
161
163
|
/**
|
|
162
164
|
* @param {{ stopPropagation: () => void; }} ev
|
|
163
165
|
*/
|
|
@@ -215,6 +217,12 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
215
217
|
// @ts-expect-error
|
|
216
218
|
static enabledWarnings = super.enabledWarnings?.filter(w => w !== 'change-in-update') || [];
|
|
217
219
|
|
|
220
|
+
get _overlayInvokerNode() {
|
|
221
|
+
return /** @type {HTMLElement | undefined} */ (
|
|
222
|
+
Array.from(this.children).find(child => child.slot === 'invoker')
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
218
226
|
/**
|
|
219
227
|
* @overridable
|
|
220
228
|
*/
|
|
@@ -247,77 +255,13 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
247
255
|
);
|
|
248
256
|
}
|
|
249
257
|
|
|
250
|
-
/**
|
|
251
|
-
* Returns the element that can actually receive focus inside the passed
|
|
252
|
-
* invoker (wrapper) element: the element itself when it is focusable,
|
|
253
|
-
* otherwise the first focusable descendant.
|
|
254
|
-
* @param {Element|null|undefined} focusableElOrWrapper
|
|
255
|
-
* @returns {Element|null}
|
|
256
|
-
*/
|
|
257
|
-
static _getFocusableInvokerEl(focusableElOrWrapper) {
|
|
258
|
-
if (!focusableElOrWrapper) return null;
|
|
259
|
-
const focusableSelector = '[tabindex], button, a[href], [role=button]';
|
|
260
|
-
return focusableElOrWrapper.matches(focusableSelector)
|
|
261
|
-
? focusableElOrWrapper
|
|
262
|
-
: focusableElOrWrapper.querySelector(focusableSelector);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* The node that opens/closes this overlay.
|
|
267
|
-
*
|
|
268
|
-
* Resolved in a stable, declarative way (independent of the exact DOM order)
|
|
269
|
-
* by looking, in order, for:
|
|
270
|
-
* 1. a child with `[slot="invoker"]`
|
|
271
|
-
* 2. any element in the same root that opts in via `[data-invoker]` and
|
|
272
|
-
* references this host by id (`for="<id>"`)
|
|
273
|
-
* 3. a preceding sibling that opts in via `[data-invoker]`
|
|
274
|
-
* @protected
|
|
275
|
-
*/
|
|
276
|
-
get _overlayInvokerNode() {
|
|
277
|
-
if (!this.__invokerNode) {
|
|
278
|
-
this.__invokerNode = this._getInvokerNode();
|
|
279
|
-
}
|
|
280
|
-
return this.__invokerNode;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* @protected
|
|
285
|
-
* @returns {Element|null}
|
|
286
|
-
*/
|
|
287
|
-
_getInvokerNode() {
|
|
288
|
-
const ctor = /** @type {typeof OverlayMixin} */ (this.constructor);
|
|
289
|
-
|
|
290
|
-
const slottedNode = Array.from(this.children).find(child => child.slot === 'invoker');
|
|
291
|
-
if (slottedNode) {
|
|
292
|
-
return ctor._getFocusableInvokerEl(slottedNode);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (this.id) {
|
|
296
|
-
// Reference the invoker declaratively: `<button data-invoker for="my-menu">`
|
|
297
|
-
const root = /** @type {Document|ShadowRoot} */ (this.getRootNode());
|
|
298
|
-
const explicitInvoker = root.querySelector?.(`[data-invoker][for="${this.id}"]`);
|
|
299
|
-
if (explicitInvoker) {
|
|
300
|
-
return ctor._getFocusableInvokerEl(explicitInvoker);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Fall back to a preceding sibling that opted in via [data-invoker]
|
|
305
|
-
const { previousElementSibling } = this;
|
|
306
|
-
return previousElementSibling && previousElementSibling.hasAttribute('data-invoker')
|
|
307
|
-
? ctor._getFocusableInvokerEl(previousElementSibling)
|
|
308
|
-
: null;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
258
|
/** @protected */
|
|
312
259
|
_setupOverlayCtrl() {
|
|
313
260
|
if (this.#hasSetup) return;
|
|
314
|
-
|
|
315
|
-
const invokerNode = this._overlayInvokerNode;
|
|
316
|
-
|
|
317
261
|
const config = {
|
|
318
262
|
contentNode: this._overlayContentNode,
|
|
319
263
|
contentWrapperNode: this._overlayContentWrapperNode,
|
|
320
|
-
invokerNode:
|
|
264
|
+
invokerNode: this._overlayInvokerNode,
|
|
321
265
|
referenceNode: this._overlayReferenceNode,
|
|
322
266
|
backdropNode: this._overlayBackdropNode,
|
|
323
267
|
};
|
|
@@ -338,8 +282,6 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
338
282
|
|
|
339
283
|
/** @protected */
|
|
340
284
|
_teardownOverlayCtrl() {
|
|
341
|
-
// Make sure that dynamic behavior (e.g. responsive) is possible by allowing multiple setups and teardowns of the overlay controller.
|
|
342
|
-
this.#hasSetup = false;
|
|
343
285
|
if (!this._overlayCtrl) return;
|
|
344
286
|
|
|
345
287
|
this._teardownOpenCloseListeners();
|
|
@@ -492,7 +434,6 @@ export const OverlayMixinImplementation = superclass => {
|
|
|
492
434
|
for (const postProcessor of overlayMixinPostProcessors) {
|
|
493
435
|
postProcessor(OverlayMixin);
|
|
494
436
|
}
|
|
495
|
-
|
|
496
|
-
return OverlayMixin;
|
|
437
|
+
return /** @type {* & OverlayMixin} */ (OverlayMixin);
|
|
497
438
|
};
|
|
498
439
|
export const OverlayMixin = dedupeMixin(OverlayMixinImplementation);
|
|
@@ -124,7 +124,7 @@ export function adoptStyle(renderRoot, style, { teardown = false } = {}) {
|
|
|
124
124
|
if (!teardown) {
|
|
125
125
|
// @ts-ignore
|
|
126
126
|
// eslint-disable-next-line no-param-reassign
|
|
127
|
-
renderRoot.adoptedStyleSheets = [...
|
|
127
|
+
renderRoot.adoptedStyleSheets = [...renderRoot.adoptedStyleSheets, sheet];
|
|
128
128
|
} else if (renderRoot.adoptedStyleSheets.includes(sheet)) {
|
|
129
129
|
// @ts-ignore
|
|
130
130
|
// eslint-disable-next-line no-param-reassign
|
|
@@ -12,24 +12,15 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export function isEqualConfig(a, b) {
|
|
14
14
|
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
|
|
15
|
-
if (typeof a === 'function' && typeof b === 'function') {
|
|
16
|
-
return /** @type {Function} */ (a).toString() === /** @type {Function} */ (b).toString();
|
|
17
|
-
}
|
|
18
15
|
return a === b;
|
|
19
16
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const aProps = Object.keys(a);
|
|
25
|
-
const bProps = Object.keys(b);
|
|
17
|
+
const aObj = /** @type {Record<string, any>} */ (a);
|
|
18
|
+
const bObj = /** @type {Record<string, any>} */ (b);
|
|
19
|
+
const aProps = Object.keys(aObj);
|
|
20
|
+
const bProps = Object.keys(bObj);
|
|
26
21
|
if (aProps.length !== bProps.length) {
|
|
27
22
|
return false;
|
|
28
23
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Object.prototype.hasOwnProperty.call(b, prop) &&
|
|
32
|
-
// @ts-ignore - dynamic property access
|
|
33
|
-
isEqualConfig(a[prop], b[prop]),
|
|
34
|
-
);
|
|
24
|
+
const isEqual = /** @param {string} prop */ prop => isEqualConfig(aObj[prop], bObj[prop]);
|
|
25
|
+
return aProps.every(isEqual);
|
|
35
26
|
}
|
|
@@ -115,35 +115,26 @@ const withLocalTestConfig = () =>
|
|
|
115
115
|
*/
|
|
116
116
|
async function createNestedEscControllers(parentContent) {
|
|
117
117
|
const childContent = /** @type {HTMLDivElement} */ (parentContent.querySelector('div[id]'));
|
|
118
|
+
// Assert valid fixure
|
|
119
|
+
const isValidFixture =
|
|
120
|
+
(parentContent.id.startsWith('parent-overlay--hidesOnEsc') ||
|
|
121
|
+
parentContent.id.startsWith('parent-overlay--hidesOnOutsideEsc')) &&
|
|
122
|
+
(childContent.id.startsWith('child-overlay--hidesOnEsc') ||
|
|
123
|
+
childContent.id.startsWith('child-overlay--hidesOnOutsideEsc'));
|
|
124
|
+
if (!isValidFixture) {
|
|
125
|
+
throw new Error('Provide a valid fixture');
|
|
126
|
+
}
|
|
118
127
|
|
|
119
128
|
if (parentContent.hasAttribute('data-convert-to-shadow-root')) {
|
|
120
129
|
const shadowRootParent = parentContent.attachShadow({ mode: 'open' });
|
|
121
130
|
shadowRootParent.appendChild(childContent);
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
const parentConfig = {};
|
|
125
|
-
const childConfig = {};
|
|
126
|
-
|
|
127
133
|
const parentHasOutsideOnEsc = parentContent.id.startsWith('parent-overlay--hidesOnOutsideEsc');
|
|
128
134
|
const childHasOutsideOnEsc = childContent.id.startsWith('child-overlay--hidesOnOutsideEsc');
|
|
129
135
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
} else {
|
|
133
|
-
parentConfig.hidesOnEsc = true;
|
|
134
|
-
}
|
|
135
|
-
if (childHasOutsideOnEsc) {
|
|
136
|
-
childConfig.hidesOnOutsideEsc = true;
|
|
137
|
-
} else {
|
|
138
|
-
childConfig.hidesOnEsc = true;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const parentHasSyncChildrenCloseState =
|
|
142
|
-
parentContent.dataset.syncChildrenCloseState !== undefined;
|
|
143
|
-
|
|
144
|
-
if (parentHasSyncChildrenCloseState) {
|
|
145
|
-
parentConfig.syncChildrenCloseState = true;
|
|
146
|
-
}
|
|
136
|
+
const parentConfig = parentHasOutsideOnEsc ? { hidesOnOutsideEsc: true } : { hidesOnEsc: true };
|
|
137
|
+
const childConfig = childHasOutsideOnEsc ? { hidesOnOutsideEsc: true } : { hidesOnEsc: true };
|
|
147
138
|
|
|
148
139
|
const parentOverlay = new OverlayController({
|
|
149
140
|
...withGlobalTestConfig(),
|
|
@@ -292,9 +283,8 @@ describe('OverlayController', () => {
|
|
|
292
283
|
contentNode,
|
|
293
284
|
});
|
|
294
285
|
};
|
|
295
|
-
|
|
296
286
|
expect(createOverlayController).to.throw(
|
|
297
|
-
'[OverlayController] Could not find a render target,
|
|
287
|
+
'[OverlayController] Could not find a render target, since the provided contentNode is not connected to the DOM. Make sure that it is connected, e.g. by doing "document.body.appendChild(contentNode)", before passing it on.',
|
|
298
288
|
);
|
|
299
289
|
});
|
|
300
290
|
|
|
@@ -356,21 +346,6 @@ describe('OverlayController', () => {
|
|
|
356
346
|
await ctrl.hide();
|
|
357
347
|
expect(ctrl.isShown).to.be.false;
|
|
358
348
|
});
|
|
359
|
-
|
|
360
|
-
it('cleans up the dom structure it created', async () => {
|
|
361
|
-
const contentNode = /** @type {HTMLElement} */ (fixtureSync(html`<div>my content</div>`));
|
|
362
|
-
const ctrl = new OverlayController({
|
|
363
|
-
placementMode: 'global',
|
|
364
|
-
contentNode,
|
|
365
|
-
});
|
|
366
|
-
await ctrl.show();
|
|
367
|
-
expect(ctrl.contentWrapperNode).to.exist;
|
|
368
|
-
expect(ctrl.__wrappingDialogNode).to.exist;
|
|
369
|
-
|
|
370
|
-
await ctrl.teardown();
|
|
371
|
-
expect(ctrl.contentWrapperNode).to.not.exist;
|
|
372
|
-
expect(ctrl.__wrappingDialogNode).to.not.exist;
|
|
373
|
-
});
|
|
374
349
|
});
|
|
375
350
|
|
|
376
351
|
describe('Node Configuration', () => {
|
|
@@ -428,7 +403,7 @@ describe('OverlayController', () => {
|
|
|
428
403
|
{ ignoreAttributes: ['closedby'] },
|
|
429
404
|
);
|
|
430
405
|
|
|
431
|
-
expect(el).lightDom.to.equal(`<div
|
|
406
|
+
expect(el).lightDom.to.equal(`<div slot="content">projected</div>`);
|
|
432
407
|
});
|
|
433
408
|
});
|
|
434
409
|
|
|
@@ -450,7 +425,7 @@ describe('OverlayController', () => {
|
|
|
450
425
|
`
|
|
451
426
|
<dialog data-overlay-outer-wrapper="" open="" role="none" style="${wrappingDialogNodeStyle}">
|
|
452
427
|
<div data-id="content-wrapper">
|
|
453
|
-
<div
|
|
428
|
+
<div id="content">non projected</div>
|
|
454
429
|
</div>
|
|
455
430
|
</dialog>
|
|
456
431
|
`,
|
|
@@ -645,7 +620,6 @@ describe('OverlayController', () => {
|
|
|
645
620
|
});
|
|
646
621
|
});
|
|
647
622
|
|
|
648
|
-
// TODO: make features modular (so they can be treeshaken) and test their lifecycle within this desrcibe
|
|
649
623
|
describe('Feature Configuration', () => {
|
|
650
624
|
describe('trapsKeyboardFocus', () => {
|
|
651
625
|
it('focuses the overlay on show', async () => {
|
|
@@ -889,7 +863,10 @@ describe('OverlayController', () => {
|
|
|
889
863
|
|
|
890
864
|
describe('Nested hidesOnEsc / hidesOnOutsideEsc', () => {
|
|
891
865
|
describe('Parent has hidesOnEsc and child has hidesOnOutsideEsc', () => {
|
|
892
|
-
|
|
866
|
+
// TODO: This test is flaky. We need to investigate why the child overlay
|
|
867
|
+
// is not staying shown after the parent overlay is hidden. The failing line
|
|
868
|
+
// is highlighted below.
|
|
869
|
+
it.skip('on [Escape] press in child overlay: parent hides, child stays shown', async () => {
|
|
893
870
|
const parentContent = /** @type {HTMLDivElement} */ (
|
|
894
871
|
await fixture(
|
|
895
872
|
html` <!-- -->
|
|
@@ -900,16 +877,12 @@ describe('OverlayController', () => {
|
|
|
900
877
|
);
|
|
901
878
|
const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
|
|
902
879
|
await mimicEscapePress(childOverlay.contentNode);
|
|
903
|
-
await
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
if (parentOverlay.isShown) {
|
|
911
|
-
await waitUntil(() => !parentOverlay.isShown);
|
|
912
|
-
}
|
|
880
|
+
await waitUntil(() => !parentOverlay.isShown);
|
|
881
|
+
|
|
882
|
+
// TODO: This is the failing line ("sometimes").
|
|
883
|
+
// The child overlay is not staying shown after the parent overlay is hidden.
|
|
884
|
+
await waitUntil(() => childOverlay.isShown);
|
|
885
|
+
|
|
913
886
|
await childOverlay.teardown();
|
|
914
887
|
await parentOverlay.teardown();
|
|
915
888
|
});
|
|
@@ -1015,7 +988,6 @@ describe('OverlayController', () => {
|
|
|
1015
988
|
});
|
|
1016
989
|
});
|
|
1017
990
|
});
|
|
1018
|
-
|
|
1019
991
|
describe('hidesOnOutsideClick', () => {
|
|
1020
992
|
it('hides on outside click', async () => {
|
|
1021
993
|
const contentNode = /** @type {HTMLElement} */ (await fixture('<div>Content</div>'));
|
|
@@ -1736,97 +1708,6 @@ describe('OverlayController', () => {
|
|
|
1736
1708
|
expect(ctrl1.hasActiveBackdrop).to.be.true;
|
|
1737
1709
|
});
|
|
1738
1710
|
});
|
|
1739
|
-
|
|
1740
|
-
describe('elementToFocusOnShow', () => {
|
|
1741
|
-
it('adds tabindex="-1" to the content node when elementToFocusOnShow is true', async () => {
|
|
1742
|
-
const ctrl = new OverlayController({
|
|
1743
|
-
...withGlobalTestConfig(),
|
|
1744
|
-
isBlocking: false,
|
|
1745
|
-
elementToFocusOnShow: true,
|
|
1746
|
-
});
|
|
1747
|
-
const contentNode = /** @type {HTMLElement} */ (await fixture('<div>Content</div>'));
|
|
1748
|
-
ctrl.updateConfig({ contentNode });
|
|
1749
|
-
await ctrl.show();
|
|
1750
|
-
expect(contentNode.getAttribute('tabindex')).to.equal('-1');
|
|
1751
|
-
});
|
|
1752
|
-
|
|
1753
|
-
it('makes contentNode the root of "next tab flow"', async () => {
|
|
1754
|
-
const ctrl = new OverlayController({
|
|
1755
|
-
...withGlobalTestConfig(),
|
|
1756
|
-
isBlocking: false,
|
|
1757
|
-
elementToFocusOnShow: true,
|
|
1758
|
-
});
|
|
1759
|
-
const contentNode = /** @type {HTMLElement} */ (
|
|
1760
|
-
await fixture('<div><button>Button</button></div>')
|
|
1761
|
-
);
|
|
1762
|
-
ctrl.updateConfig({ contentNode });
|
|
1763
|
-
await ctrl.show();
|
|
1764
|
-
const button = /** @type {HTMLButtonElement} */ (contentNode.querySelector('button'));
|
|
1765
|
-
button.focus();
|
|
1766
|
-
expect(isActiveElement(button)).to.be.true;
|
|
1767
|
-
});
|
|
1768
|
-
});
|
|
1769
|
-
|
|
1770
|
-
describe('syncChildrenCloseState', () => {
|
|
1771
|
-
it('does not close all children of nested controllers when syncChildrenCloseState is false', async () => {
|
|
1772
|
-
const parentContent = /** @type {HTMLDivElement} */ (
|
|
1773
|
-
await fixture(
|
|
1774
|
-
html` <!-- -->
|
|
1775
|
-
<div data-sync-children-close-state id="parent-overlay">
|
|
1776
|
-
<div id="child-overlay">we open our child and it will close when parent does</div>
|
|
1777
|
-
</div>`,
|
|
1778
|
-
)
|
|
1779
|
-
);
|
|
1780
|
-
const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
|
|
1781
|
-
expect(parentOverlay.isShown).to.be.true;
|
|
1782
|
-
expect(childOverlay.isShown).to.be.true;
|
|
1783
|
-
|
|
1784
|
-
await parentOverlay.hide();
|
|
1785
|
-
await childOverlay._hideComplete;
|
|
1786
|
-
expect(parentOverlay.isShown).to.be.false;
|
|
1787
|
-
expect(childOverlay.isShown).to.be.false;
|
|
1788
|
-
});
|
|
1789
|
-
|
|
1790
|
-
it('does not close all children of nested controllers when syncChildrenCloseState is false', async () => {
|
|
1791
|
-
const parentContent = /** @type {HTMLDivElement} */ (
|
|
1792
|
-
await fixture(
|
|
1793
|
-
html` <!-- -->
|
|
1794
|
-
<div id="parent-overlay">
|
|
1795
|
-
<div id="child-overlay">
|
|
1796
|
-
we open our child and it will not close when parent does
|
|
1797
|
-
</div>
|
|
1798
|
-
</div>`,
|
|
1799
|
-
)
|
|
1800
|
-
);
|
|
1801
|
-
const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
|
|
1802
|
-
expect(parentOverlay.isShown).to.be.true;
|
|
1803
|
-
expect(childOverlay.isShown).to.be.true;
|
|
1804
|
-
|
|
1805
|
-
await parentOverlay.hide();
|
|
1806
|
-
expect(parentOverlay.isShown).to.be.false;
|
|
1807
|
-
expect(childOverlay.isShown).to.be.true;
|
|
1808
|
-
});
|
|
1809
|
-
});
|
|
1810
|
-
|
|
1811
|
-
describe('isActivated', () => {
|
|
1812
|
-
it('allows to conditionally disable overlay/openable functionality (for responsive context like nav menus, accordions/collapsibles vs plain headings/content etc.)', async () => {
|
|
1813
|
-
const ctrl = new OverlayController({
|
|
1814
|
-
...withGlobalTestConfig(),
|
|
1815
|
-
isActivated: false,
|
|
1816
|
-
});
|
|
1817
|
-
expect(ctrl.__hasSetup).to.be.false;
|
|
1818
|
-
ctrl.updateConfig({
|
|
1819
|
-
isActivated: true,
|
|
1820
|
-
});
|
|
1821
|
-
expect(ctrl.__hasSetup).to.be.true;
|
|
1822
|
-
});
|
|
1823
|
-
});
|
|
1824
|
-
|
|
1825
|
-
// TODO
|
|
1826
|
-
describe('hideVisually', () => {});
|
|
1827
|
-
|
|
1828
|
-
// TODO
|
|
1829
|
-
describe('requireConnectedNodes', () => {});
|
|
1830
1711
|
});
|
|
1831
1712
|
|
|
1832
1713
|
describe('Show / Hide / Toggle', () => {
|
|
@@ -2417,7 +2298,6 @@ describe('OverlayController', () => {
|
|
|
2417
2298
|
});
|
|
2418
2299
|
|
|
2419
2300
|
it('should run with scroll prevention', async () => {
|
|
2420
|
-
await overlayControllerPreventsScroll.hide();
|
|
2421
2301
|
await overlayControllerPreventsScroll.show();
|
|
2422
2302
|
|
|
2423
2303
|
expect(
|
|
@@ -540,13 +540,13 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
|
|
|
540
540
|
it('works with cache directive when reconnected', async () => {
|
|
541
541
|
class CachingContext extends LitElement {
|
|
542
542
|
static properties = {
|
|
543
|
-
|
|
543
|
+
switched: { type: Boolean },
|
|
544
544
|
};
|
|
545
545
|
|
|
546
546
|
constructor() {
|
|
547
547
|
super();
|
|
548
548
|
|
|
549
|
-
this.
|
|
549
|
+
this.switched = false;
|
|
550
550
|
}
|
|
551
551
|
|
|
552
552
|
get overlayEl() {
|
|
@@ -555,12 +555,12 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
|
|
|
555
555
|
|
|
556
556
|
render() {
|
|
557
557
|
return html`${cache(
|
|
558
|
-
this.
|
|
559
|
-
? html
|
|
558
|
+
this.switched
|
|
559
|
+
? html`something else`
|
|
560
|
+
: html`<${tag} id="myOverlay">
|
|
560
561
|
<div slot="content">content b</div>
|
|
561
562
|
<button slot="invoker">invoker button b</button>
|
|
562
|
-
</${tag}
|
|
563
|
-
: html`something else`,
|
|
563
|
+
</${tag}>`,
|
|
564
564
|
)}`;
|
|
565
565
|
}
|
|
566
566
|
}
|
|
@@ -581,7 +581,7 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
|
|
|
581
581
|
// @ts-expect-error [allow-protected] in tests
|
|
582
582
|
const setupSpy = sinon.spy(el.overlayEl, '_setupOverlayCtrl');
|
|
583
583
|
|
|
584
|
-
el.
|
|
584
|
+
el.switched = true;
|
|
585
585
|
// render the new content
|
|
586
586
|
await el.updateComplete;
|
|
587
587
|
// wait for the teardown to complete
|
|
@@ -591,7 +591,7 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
|
|
|
591
591
|
expect(teardownSpy.callCount).to.equal(1);
|
|
592
592
|
expect(setupSpy.callCount).to.equal(0);
|
|
593
593
|
|
|
594
|
-
el.
|
|
594
|
+
el.switched = false;
|
|
595
595
|
await el.updateComplete;
|
|
596
596
|
expect(setupSpy.callCount).to.equal(1);
|
|
597
597
|
|
|
@@ -26,7 +26,9 @@ export declare class ArrowHost {
|
|
|
26
26
|
|
|
27
27
|
protected _defineOverlayConfig(): OverlayConfig;
|
|
28
28
|
|
|
29
|
-
protected _getPopperArrowConfig(
|
|
29
|
+
protected _getPopperArrowConfig(
|
|
30
|
+
popperConfigToExtendFrom: Partial<PopperOptions>,
|
|
31
|
+
): Partial<PopperOptions>;
|
|
30
32
|
|
|
31
33
|
private __setupRepositionCompletePromise(): void;
|
|
32
34
|
|
|
@@ -21,6 +21,7 @@ export type OverlayPhase =
|
|
|
21
21
|
| 'add'
|
|
22
22
|
| 'remove';
|
|
23
23
|
|
|
24
|
+
|
|
24
25
|
export interface ViewportConfig {
|
|
25
26
|
placement: ViewportPlacement;
|
|
26
27
|
}
|
|
@@ -96,30 +97,14 @@ export interface OverlayConfig {
|
|
|
96
97
|
/** By default, the tooltip content is a 'description' for the invoker (uses aria-describedby) Setting this property to 'label' makes the content function as a label (via aria-labelledby) */
|
|
97
98
|
invokerRelation?: 'label' | 'description';
|
|
98
99
|
|
|
100
|
+
/** render a div instead of dialog */
|
|
101
|
+
_noDialogEl?: Boolean;
|
|
102
|
+
|
|
99
103
|
/**
|
|
100
104
|
* Determines the conditions hiding/showing should be based on. It gets the OverlayController as input and returns an object with
|
|
101
105
|
* functions with Overlay phases as keys
|
|
102
106
|
*/
|
|
103
107
|
visibilityTriggerFunction?: Function;
|
|
108
|
+
}
|
|
104
109
|
|
|
105
|
-
/**
|
|
106
|
-
* Whether the overlay content should receive focus when shown.
|
|
107
|
-
*/
|
|
108
|
-
elementToFocusOnShow?: boolean;
|
|
109
|
-
|
|
110
|
-
/*
|
|
111
|
-
* By default, we function either as overlay or disclosure. In some cases, this should be dynamically set (think of responsive changes).
|
|
112
|
-
* or it depends on the context (a top level menu is not openable (unless it's a context menu), its children menus are).
|
|
113
|
-
*/
|
|
114
|
-
isActivated?: boolean;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* By default, an error is thrown when contentNode is not connected to the DOM.
|
|
118
|
-
* This allows to prepare nodes and later connect them to dom.
|
|
119
|
-
*/
|
|
120
|
-
requireConnectedNodes?: boolean;
|
|
121
|
-
|
|
122
|
-
syncChildrenCloseState?: boolean;
|
|
123
110
|
|
|
124
|
-
_shouldTeardownDomStructure?: boolean;
|
|
125
|
-
}
|
|
@@ -17,8 +17,6 @@ export interface DefineOverlayConfig {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export declare class OverlayHost {
|
|
20
|
-
static _getFocusableInvokerEl(focusableElOrWrapper: Element): Element | null;
|
|
21
|
-
|
|
22
20
|
opened: Boolean;
|
|
23
21
|
get config(): Partial<OverlayConfig>;
|
|
24
22
|
set config(value: Partial<OverlayConfig>);
|