@lion/ui 0.18.0-menu-system.5 → 0.18.0-menu-system.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/menu/src/InteractiveListMixin.js +11 -9
- package/components/menu/src/LionMenu.js +1 -1
- package/components/menu/src/LionTree.js +1 -1
- package/components/menu/src/MoreButtonMenuMixin.js +18 -27
- package/components/menu/src/MultiLevelListMixin.js +46 -9
- package/components/menu/src/utils/listItemInteractions.js +2 -1
- package/components/menu/test/InteractiveListMixin.test.js +3 -0
- package/components/menu/test/MoreButtonMenuMixin.test.js +4 -0
- package/components/menu/test/MultiLevelListMixin.test.js +3 -0
- package/components/menu/test/lion-menu-hybrid.test.js +3 -0
- package/components/menu/test/lion-menu.test.js +7 -0
- package/components/menu/test/lion-toolbar.test.js +8 -0
- package/components/menu/test/lion-tree.test.js +7 -0
- package/components/menu/test-helpers/mimicKeyPress.js +9 -0
- package/components/menu/test-suites/InteractiveListMixin.suite.js +155 -5
- package/components/menu/test-suites/LionMenu.suite.js +113 -0
- package/components/menu/test-suites/LionMenuHybrid.suite.js +583 -0
- package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +372 -0
- package/components/menu/test-suites/MultiLevelListMixin.suite.js +303 -0
- package/components/menu/types/InteractiveListMixinTypes.d.ts +2 -0
- package/components/overlays/src/OverlayController.js +1 -1
- package/components/overlays/types/OverlayMixinTypes.ts +2 -0
- package/custom-elements.json +245 -153
- package/dist-types/components/combobox/src/LionCombobox.d.ts +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +1 -1
- package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts +1 -1
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -1
- package/dist-types/components/menu/src/LionMenu.d.ts +75 -5
- package/dist-types/components/menu/src/LionMenu.d.ts.map +1 -1
- package/dist-types/components/menu/src/LionMenuOverlay.d.ts +4 -2
- package/dist-types/components/menu/src/LionMenuOverlay.d.ts.map +1 -1
- package/dist-types/components/menu/src/LionTree.d.ts +2 -2
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +0 -1
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +1 -1
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +10 -2
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +1 -1
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +1 -1
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-menu-hybrid.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-menu-hybrid.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-toolbar.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-toolbar.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-tree.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-tree.test.d.ts.map +1 -0
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts +7 -0
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -1
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +9 -0
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/LionMenuHybrid.suite.d.ts +5 -0
- package/dist-types/components/menu/test-suites/LionMenuHybrid.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +14 -0
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +1 -0
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +1 -0
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +1 -1
- package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
- package/dist-types/exports/menu-test-suites.d.ts +2 -0
- 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 +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/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/menu/api-table.md +44 -39
- 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 +2 -2
- package/docs/components/tabs/examples.md +1 -1
- 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/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/menu-test-suites.js +2 -0
- package/menu-test-suites.js +2 -0
- package/package.json +1 -1
- package/components/menu/src/utils/uuid.js +0 -3
- package/dist-types/components/menu/src/utils/uuid.d.ts +0 -2
- package/dist-types/components/menu/src/utils/uuid.d.ts.map +0 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/* eslint-disable max-classes-per-file */
|
|
2
2
|
/* eslint-disable import/no-extraneous-dependencies */
|
|
3
3
|
import { html, css, LitElement } from 'lit';
|
|
4
|
-
import { SlotMixin, DisabledMixin } from '@lion/ui/core.js';
|
|
4
|
+
import { SlotMixin, DisabledMixin, uuid } from '@lion/ui/core.js';
|
|
5
5
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
6
6
|
import { MoreButtonMenuMixin } from './MoreButtonMenuMixin.js';
|
|
7
|
-
import { uuid } from './utils/uuid.js';
|
|
8
7
|
import { isInView } from './utils/isInView.js';
|
|
9
8
|
import {
|
|
10
9
|
isDisabled,
|
|
@@ -167,7 +166,7 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
167
166
|
multipleChoice: { type: Boolean, attribute: 'multiple-choice' },
|
|
168
167
|
// TODO: align with name of open-ui
|
|
169
168
|
selectionFollowsFocus: { type: Boolean, attribute: 'selection-follows-focus' },
|
|
170
|
-
// TODO: align with name of open-ui => "wrap" in
|
|
169
|
+
// TODO: align with name of open-ui => "wrap" in focusgroup
|
|
171
170
|
rotateKeyboardNavigation: { type: Boolean, attribute: 'rotate-keyboard-navigation' },
|
|
172
171
|
// TODO: align with name of open-ui
|
|
173
172
|
noPreselect: { type: Boolean, attribute: 'no-preselect' },
|
|
@@ -293,7 +292,7 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
293
292
|
}
|
|
294
293
|
|
|
295
294
|
// @ts-ignore - focusableEl is safely used as HTMLElement here
|
|
296
|
-
if (!isInView(this._scrollTargetNode, focusableEl)) {
|
|
295
|
+
if (this.orientation === 'vertical' && !isInView(this._scrollTargetNode, focusableEl)) {
|
|
297
296
|
focusableEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
298
297
|
}
|
|
299
298
|
|
|
@@ -556,7 +555,7 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
556
555
|
// having hideVisually as hide mechanism (opening on focus, closing on blur).
|
|
557
556
|
// N.B. this._listNode has `display:flex`. This means we need to add `text-wrap: nowrap;`
|
|
558
557
|
// to avoid elems going over two lines, or give them a max-height based on measuring the height of one item.
|
|
559
|
-
// 2. N.B. hideVisually needs to be
|
|
558
|
+
// 2. N.B. hideVisually needs to be implemented in OverlayController.
|
|
560
559
|
// 3. when the more menu opens a next level, MultiLevelListMixin will be applied to the host as well.
|
|
561
560
|
// That means it knows when a child menu is open. If _subListMap of one of the items is open, we should hide more menu like this:
|
|
562
561
|
// https://jsfiddle.net/a4ocktfp/19/
|
|
@@ -588,7 +587,7 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
588
587
|
}
|
|
589
588
|
});
|
|
590
589
|
|
|
591
|
-
this.
|
|
590
|
+
this._syncCurrentPageWithLocationHref(window.location);
|
|
592
591
|
}
|
|
593
592
|
|
|
594
593
|
/**
|
|
@@ -817,10 +816,13 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
817
816
|
/**
|
|
818
817
|
* @param {Location} location
|
|
819
818
|
*/
|
|
820
|
-
|
|
819
|
+
_syncCurrentPageWithLocationHref(location) {
|
|
821
820
|
this.listItems.forEach(item => {
|
|
822
821
|
if (/** @type {HTMLAnchorElement} */ (item).href) {
|
|
823
|
-
|
|
822
|
+
const pathname = location.href.endsWith('/')
|
|
823
|
+
? location.href.substring(0, location.href.length - 1)
|
|
824
|
+
: location.href;
|
|
825
|
+
if (pathname === /** @type {HTMLAnchorElement} */ (item).href) {
|
|
824
826
|
setChecked(item);
|
|
825
827
|
} else {
|
|
826
828
|
setChecked(item, true);
|
|
@@ -831,7 +833,7 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
831
833
|
|
|
832
834
|
/** @protected */
|
|
833
835
|
_onPopState = () => {
|
|
834
|
-
this.
|
|
836
|
+
this._syncCurrentPageWithLocationHref(document.location);
|
|
835
837
|
};
|
|
836
838
|
};
|
|
837
839
|
// @ts-ignore
|
|
@@ -22,7 +22,7 @@ export class LionTree extends MultiLevelListMixin(LitElement) {
|
|
|
22
22
|
super.updated(changedProperties);
|
|
23
23
|
|
|
24
24
|
if (changedProperties.has('level')) {
|
|
25
|
-
if (this.level >
|
|
25
|
+
if (this.level > 1) {
|
|
26
26
|
this._listNode.setAttribute('role', 'group');
|
|
27
27
|
}
|
|
28
28
|
this.__setAriaLevelForListItems();
|
|
@@ -18,8 +18,8 @@ const styles = css`
|
|
|
18
18
|
/** N.B. this is a simplified version of sr-only. when we move to overlayctrl, we should build this into overlayctrl */
|
|
19
19
|
[data-more-button-menu] {
|
|
20
20
|
overflow: hidden;
|
|
21
|
-
height:
|
|
22
|
-
width:
|
|
21
|
+
height: 0;
|
|
22
|
+
width: 0;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
[role='list']:has(> [data-more-button-wrapper]) > [role='listitem'] > :is(button, a) {
|
|
@@ -75,11 +75,13 @@ export const MoreButtonMenuMixin = superclass =>
|
|
|
75
75
|
moreButton?.setAttribute('aria-expanded', 'false');
|
|
76
76
|
const moreButtonMenu = document.createElement('div');
|
|
77
77
|
moreButtonMenu.setAttribute('data-more-button-menu', '');
|
|
78
|
+
moreButtonMenu.setAttribute('tabindex', '-1');
|
|
78
79
|
moreButtonWrapper.appendChild(moreButtonMenu);
|
|
79
80
|
this._listNode.appendChild(moreButtonWrapper);
|
|
80
81
|
|
|
81
82
|
const isElementDirectFocusableItemUnderMoreButtonMenu = (/** @type {Element} */ element) =>
|
|
82
|
-
this.listItems.includes(element) && moreButtonMenu.contains(element)
|
|
83
|
+
(this.listItems.includes(element) && moreButtonMenu.contains(element)) ||
|
|
84
|
+
element === moreButtonMenu;
|
|
83
85
|
|
|
84
86
|
moreButtonMenu.addEventListener(
|
|
85
87
|
'focusin',
|
|
@@ -87,6 +89,9 @@ export const MoreButtonMenuMixin = superclass =>
|
|
|
87
89
|
const target = /** @type {HTMLElement} */ (event?.target);
|
|
88
90
|
if (!isElementDirectFocusableItemUnderMoreButtonMenu(target)) return;
|
|
89
91
|
|
|
92
|
+
// If applicable hide any other L2 menus
|
|
93
|
+
this.querySelector('[level="2"]:has([data-open])')?._overlayCtrl?.hide();
|
|
94
|
+
|
|
90
95
|
// A user did `Shift + Tab` from L2 menu to More menu
|
|
91
96
|
if (target?.getAttribute('aria-expanded') === 'true') {
|
|
92
97
|
// close L2 menu
|
|
@@ -110,31 +115,11 @@ export const MoreButtonMenuMixin = superclass =>
|
|
|
110
115
|
}, 100);
|
|
111
116
|
});
|
|
112
117
|
|
|
113
|
-
this.hasMoreButtonMenuAnyFocusedFirstLevelItems = false;
|
|
114
|
-
|
|
115
|
-
moreButton?.addEventListener('mousedown', () => {
|
|
116
|
-
this.hasMoreButtonMenuAnyFocusedFirstLevelItems = false;
|
|
117
|
-
const focusableFirstLevelItems = moreButtonMenu.querySelectorAll(
|
|
118
|
-
':scope > [role="listitem"] > :is(a, button)',
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// @ts-ignore Type 'NodeListOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.
|
|
122
|
-
for (const focusableFirstLevelItem of focusableFirstLevelItems) {
|
|
123
|
-
if (this._getDeepActiveElement() === focusableFirstLevelItem) {
|
|
124
|
-
this.hasMoreButtonMenuAnyFocusedFirstLevelItems = true;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
118
|
moreButton?.addEventListener('click', () => {
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
firstInteractiveItem?.focus();
|
|
137
|
-
moreButtonMenu.setAttribute('data-open', '');
|
|
119
|
+
if (moreButtonMenu.getAttribute('data-open') === null) {
|
|
120
|
+
moreButtonMenu?.focus({ preventScroll: true });
|
|
121
|
+
moreButtonMenu.setAttribute('data-open', '');
|
|
122
|
+
}
|
|
138
123
|
});
|
|
139
124
|
}
|
|
140
125
|
|
|
@@ -186,6 +171,9 @@ export const MoreButtonMenuMixin = superclass =>
|
|
|
186
171
|
const previousNodes = [];
|
|
187
172
|
let { previousSibling } = listItem;
|
|
188
173
|
while (previousSibling) {
|
|
174
|
+
if (previousSibling.nodeType === Node.ELEMENT_NODE) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
189
177
|
previousNodes.unshift(previousSibling);
|
|
190
178
|
if (isMarkerComment(previousSibling)) {
|
|
191
179
|
break;
|
|
@@ -196,6 +184,9 @@ export const MoreButtonMenuMixin = superclass =>
|
|
|
196
184
|
const nextNodes = [];
|
|
197
185
|
let { nextSibling } = listItem;
|
|
198
186
|
while (nextSibling) {
|
|
187
|
+
if (nextSibling.nodeType === Node.ELEMENT_NODE) {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
199
190
|
nextNodes.push(nextSibling);
|
|
200
191
|
|
|
201
192
|
if (isMarkerComment(nextSibling)) {
|
|
@@ -3,10 +3,12 @@ import { html } from 'lit';
|
|
|
3
3
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
4
4
|
// import { DisclosureMixin } from '@lion/ui/collapsible.js';
|
|
5
5
|
import { InteractiveListMixin } from './InteractiveListMixin.js';
|
|
6
|
-
|
|
6
|
+
import { isChecked, setChecked } from './utils/listItemInteractions.js';
|
|
7
7
|
/**
|
|
8
8
|
* @typedef {import('./InteractiveListMixin.js').LionItem} LionItem
|
|
9
9
|
* @typedef {import('../types/InteractiveListMixinTypes.js').InteractiveListHost} InteractiveList
|
|
10
|
+
* @typedef {typeof import('../../overlays/types/OverlayMixinTypes.js').OverlayHost} OverlayHost
|
|
11
|
+
* @typedef {import('./LionMenuOverlay.js').LionMenuOverlay} LionMenuOverlay
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -37,8 +39,13 @@ const MultiLevelListMixinImplementation = superclass =>
|
|
|
37
39
|
* @configure DisclosureMixin
|
|
38
40
|
*/
|
|
39
41
|
get _invokerNode() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const ctor = /** @type {OverlayHost} */ (this.constructor);
|
|
43
|
+
if (ctor._getFocusableInvokerEl && this.invokerNode) {
|
|
44
|
+
// @ts-ignore - accessing static method and super property
|
|
45
|
+
return ctor._getFocusableInvokerEl(this.invokerNode);
|
|
46
|
+
}
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
return super._invokerNode;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
/**
|
|
@@ -84,6 +91,7 @@ const MultiLevelListMixinImplementation = superclass =>
|
|
|
84
91
|
this.noPreselect = true;
|
|
85
92
|
/**
|
|
86
93
|
* @configure InteractiveListMixin
|
|
94
|
+
* @type {'activedescendant'|'roving-tabindex'|'tabbable-disclosure'}
|
|
87
95
|
*/
|
|
88
96
|
this._activeMode = 'roving-tabindex';
|
|
89
97
|
/**
|
|
@@ -184,12 +192,12 @@ const MultiLevelListMixinImplementation = superclass =>
|
|
|
184
192
|
* @param {*} ev
|
|
185
193
|
*/
|
|
186
194
|
_onListKeyDown(ev) {
|
|
187
|
-
const
|
|
195
|
+
const targetFromSubList = Array.from(this._subListMap).find(
|
|
188
196
|
// @ts-ignore - contains method on InteractiveListHost
|
|
189
197
|
([, s]) => s === ev.target || s.contains(ev.target),
|
|
190
198
|
);
|
|
191
199
|
|
|
192
|
-
if (
|
|
200
|
+
if (targetFromSubList) {
|
|
193
201
|
return;
|
|
194
202
|
}
|
|
195
203
|
|
|
@@ -228,22 +236,51 @@ const MultiLevelListMixinImplementation = superclass =>
|
|
|
228
236
|
if (this.orientation === 'horizontal' && parentListOfActiveItem) {
|
|
229
237
|
// @ts-ignore - close method
|
|
230
238
|
this.close();
|
|
231
|
-
|
|
232
|
-
|
|
239
|
+
if (
|
|
240
|
+
/** @type {InteractiveList & LionMenuOverlay} */ (parentListOfActiveItem)
|
|
241
|
+
._onOverlayShow
|
|
242
|
+
) {
|
|
243
|
+
// @ts-ignore - _onOverlayShow method
|
|
244
|
+
/** @type {OverlayHost} */ (parentListOfActiveItem)._onOverlayShow();
|
|
245
|
+
}
|
|
233
246
|
}
|
|
234
247
|
break;
|
|
235
248
|
case 'ArrowLeft':
|
|
236
249
|
if (this.orientation === 'vertical' && parentListOfActiveItem) {
|
|
237
250
|
// @ts-ignore - close method
|
|
238
251
|
this.close();
|
|
239
|
-
|
|
240
|
-
|
|
252
|
+
if (
|
|
253
|
+
/** @type {InteractiveList & LionMenuOverlay} */ (parentListOfActiveItem)
|
|
254
|
+
._onOverlayShow
|
|
255
|
+
) {
|
|
256
|
+
// @ts-ignore - _onOverlayShow method
|
|
257
|
+
/** @type {OverlayHost} */ (parentListOfActiveItem)._onOverlayShow();
|
|
258
|
+
}
|
|
241
259
|
}
|
|
242
260
|
break;
|
|
243
261
|
/* no default */
|
|
244
262
|
}
|
|
245
263
|
}
|
|
246
264
|
|
|
265
|
+
/**
|
|
266
|
+
* @param {Location} location
|
|
267
|
+
*/
|
|
268
|
+
_syncCurrentPageWithLocationHref(location) {
|
|
269
|
+
super._syncCurrentPageWithLocationHref(location);
|
|
270
|
+
|
|
271
|
+
this._subListMap.forEach(subList => {
|
|
272
|
+
subList.listItems.forEach(
|
|
273
|
+
/** @param {LionItem|HTMLElement} item */ item => {
|
|
274
|
+
// @ts-ignore
|
|
275
|
+
if (isChecked(item) && subList._invokerNode) {
|
|
276
|
+
// @ts-ignore
|
|
277
|
+
setChecked(subList._invokerNode);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
247
284
|
// /**
|
|
248
285
|
// * @enhance DisclosureMixin
|
|
249
286
|
// */
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { runInteractiveListMixinSuite } from '../test-suites/InteractiveListMixin.suite.js';
|
|
2
|
+
import { runMultiLevelListMixinSuite } from '../test-suites/MultiLevelListMixin.suite.js';
|
|
3
|
+
import { runMoreButtonMenuMixinSuite } from '../test-suites/MoreButtonMenuMixin.suite.js';
|
|
4
|
+
import { runLionMenuSuite } from '../test-suites/LionMenu.suite.js';
|
|
2
5
|
import '@lion/ui/define/lion-menu.js';
|
|
6
|
+
import '@lion/ui/define/lion-menu-overlay.js';
|
|
3
7
|
|
|
4
8
|
runInteractiveListMixinSuite({ tagString: 'lion-menu' });
|
|
9
|
+
runMultiLevelListMixinSuite({ tagString: 'lion-menu', tagChildString: 'lion-menu-overlay' });
|
|
10
|
+
runMoreButtonMenuMixinSuite({ tagString: 'lion-menu' });
|
|
11
|
+
runLionMenuSuite({ tagString: 'lion-menu' });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// import { runInteractiveListMixinSuite } from '../test-suites/InteractiveListMixin.suite.js';
|
|
2
|
+
// import { runMultiLevelListMixinSuite } from '../test-suites/MultiLevelListMixin.suite.js';
|
|
3
|
+
import { runMoreButtonMenuMixinSuite } from '../test-suites/MoreButtonMenuMixin.suite.js';
|
|
4
|
+
import '@lion/ui/define/lion-toolbar.js';
|
|
5
|
+
|
|
6
|
+
// runInteractiveListMixinSuite({ tagString: 'lion-toolbar' }); // 1 test fails
|
|
7
|
+
// runMultiLevelListMixinSuite({ tagString: 'lion-toolbar', tagChildString: 'lion-toolbar' }); // hangs
|
|
8
|
+
runMoreButtonMenuMixinSuite({ tagString: 'lion-toolbar' });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { runInteractiveListMixinSuite } from '../test-suites/InteractiveListMixin.suite.js';
|
|
2
|
+
// import { runMultiLevelListMixinSuite } from '../test-suites/MultiLevelListMixin.suite.js';
|
|
3
|
+
import '@lion/ui/define/lion-tree.js';
|
|
4
|
+
|
|
5
|
+
runInteractiveListMixinSuite({ tagString: 'lion-tree' });
|
|
6
|
+
// TODO: fix failing tests. LionTree is missing a close function, should this be coming from the DisclosureMixin?
|
|
7
|
+
// runMultiLevelListMixinSuite({ tagString: 'lion-tree', tagChildString: 'lion-tree' });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {HTMLElement} el
|
|
3
|
+
* @param {string} key
|
|
4
|
+
* @param {string} code
|
|
5
|
+
*/
|
|
6
|
+
export function mimicKeyPress(el, key, code = '') {
|
|
7
|
+
el.dispatchEvent(new KeyboardEvent('keydown', { key, code }));
|
|
8
|
+
el.dispatchEvent(new KeyboardEvent('keyup', { key, code }));
|
|
9
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
2
|
import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
|
|
3
3
|
import { InteractiveListMixin } from '../src/InteractiveListMixin.js';
|
|
4
|
+
import { mimicKeyPress } from '../test-helpers/mimicKeyPress.js';
|
|
4
5
|
|
|
5
6
|
class InteractiveListClass extends InteractiveListMixin(LitElement) {}
|
|
6
7
|
|
|
@@ -127,6 +128,28 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
127
128
|
});
|
|
128
129
|
});
|
|
129
130
|
|
|
131
|
+
describe('preselect', () => {
|
|
132
|
+
it('sets the checkedIndex to 0 by default when noPreselect is false', async () => {
|
|
133
|
+
const el = await fixture(html`
|
|
134
|
+
<${tag}>
|
|
135
|
+
<div role="menuitem" id="item1">Item 1</div>
|
|
136
|
+
</${tag}>
|
|
137
|
+
`);
|
|
138
|
+
if (!el.noPreselect) {
|
|
139
|
+
expect(el.checkedIndex).to.equal(0);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('sets the checkedIndex to -1 when noPreselect is true', async () => {
|
|
144
|
+
const el = await fixture(html`
|
|
145
|
+
<${tag} no-preselect>
|
|
146
|
+
<div role="menuitem" id="item1">Item 1</div>
|
|
147
|
+
</${tag}>
|
|
148
|
+
`);
|
|
149
|
+
expect(el.checkedIndex).to.equal(-1);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
130
153
|
describe('Programmatic interaction', () => {
|
|
131
154
|
// If we have the item line directly in the fixture the linting rule lit-a11y/role-has-required-aria-attrs
|
|
132
155
|
// will fail. Inside our code we set the required aria attributes, but the linter doesn't know that.
|
|
@@ -155,7 +178,6 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
155
178
|
${item(2, 'option')}
|
|
156
179
|
</${tag}>
|
|
157
180
|
`);
|
|
158
|
-
console.debug('listItems', el.listItems);
|
|
159
181
|
const item1 = el.listItems[1];
|
|
160
182
|
expect(item1.getAttribute('aria-selected')).to.equal('false');
|
|
161
183
|
|
|
@@ -211,7 +233,7 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
211
233
|
|
|
212
234
|
it('set checked state sets aria-checked attribute with role="menuitemcheckbox"', async () => {
|
|
213
235
|
const el = await fixture(html`
|
|
214
|
-
<${tag}>
|
|
236
|
+
<${tag} multiple-choice>
|
|
215
237
|
${item(1, 'menuitemcheckbox')}
|
|
216
238
|
${item(2, 'menuitemcheckbox')}
|
|
217
239
|
</${tag}>
|
|
@@ -240,7 +262,7 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
240
262
|
|
|
241
263
|
it('can set checked state to "mixed" for role="menuitemcheckbox"', async () => {
|
|
242
264
|
const el = await fixture(html`
|
|
243
|
-
<${tag}>
|
|
265
|
+
<${tag} multiple-choice>
|
|
244
266
|
${item(1, 'menuitemcheckbox')}
|
|
245
267
|
${item(2, 'menuitemcheckbox')}
|
|
246
268
|
</${tag}>
|
|
@@ -256,7 +278,7 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
256
278
|
|
|
257
279
|
it('does not allow to set checkedIndex or activeIndex to be out of bound', async () => {
|
|
258
280
|
const el = await fixture(html`
|
|
259
|
-
<${tag}
|
|
281
|
+
<${tag} no-preselect>
|
|
260
282
|
<div role="menuitem" id="item1">Item 1</div>
|
|
261
283
|
</${tag}>
|
|
262
284
|
`);
|
|
@@ -284,15 +306,143 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
284
306
|
});
|
|
285
307
|
});
|
|
286
308
|
|
|
309
|
+
it('sets aria-current="page" when the location href matches the item href', async () => {
|
|
310
|
+
const el = await fixture(html`
|
|
311
|
+
<${tag}>
|
|
312
|
+
<div role="menuitem" id="item0">
|
|
313
|
+
<a href="${window.location.href}">Item 0</a>
|
|
314
|
+
</div>
|
|
315
|
+
<div role="menuitem" id="item1">
|
|
316
|
+
<a href="#bar">Item 1</a>
|
|
317
|
+
</div>
|
|
318
|
+
</${tag}>
|
|
319
|
+
`);
|
|
320
|
+
const item0 = el.listItems[0];
|
|
321
|
+
expect(item0.getAttribute('aria-current')).to.equal('page');
|
|
322
|
+
});
|
|
323
|
+
|
|
287
324
|
// Copy tests from ListboxMixin. Later, make this mixin a fundament of the ListboxMixin
|
|
288
325
|
|
|
289
326
|
// orientation
|
|
290
327
|
// multipleChoice
|
|
291
328
|
// selectionFollowsFocus
|
|
292
329
|
// rotateKeyboardNavigation
|
|
293
|
-
// noPreselect
|
|
294
330
|
// _scrollTargetNode
|
|
295
331
|
|
|
332
|
+
describe('Interactions', () => {
|
|
333
|
+
describe('Keyboard navigation', () => {
|
|
334
|
+
it('navigates between items on [ArrowDown] [ArrowUp] keys', async () => {
|
|
335
|
+
const el = await fixture(html`
|
|
336
|
+
<${tag}>
|
|
337
|
+
<div role="listitem" id="item-0">
|
|
338
|
+
<button data-invoker>Item 0</button>
|
|
339
|
+
</div>
|
|
340
|
+
<div role="listitem" id="item-1">
|
|
341
|
+
<a href="#baz">Item 1</a>
|
|
342
|
+
</div>
|
|
343
|
+
<div role="listitem" id="item-2">
|
|
344
|
+
<a href="#foobar">Item 2</a>
|
|
345
|
+
</div>
|
|
346
|
+
</${tag}>
|
|
347
|
+
`);
|
|
348
|
+
|
|
349
|
+
const { _listNode } = el;
|
|
350
|
+
|
|
351
|
+
// Normalize
|
|
352
|
+
el.activeIndex = 0;
|
|
353
|
+
|
|
354
|
+
mimicKeyPress(_listNode, 'ArrowDown');
|
|
355
|
+
expect(el.activeIndex).to.be.equal(1);
|
|
356
|
+
mimicKeyPress(_listNode, 'ArrowDown');
|
|
357
|
+
expect(el.activeIndex).to.be.equal(2);
|
|
358
|
+
mimicKeyPress(_listNode, 'ArrowUp');
|
|
359
|
+
expect(el.activeIndex).to.be.equal(1);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('navigates between items on [ArrowRight] [ArrowLeft] keys when orientation is "horizontal" and _activeMode="tabbable-disclosure"', async () => {
|
|
363
|
+
const el = await fixture(html`
|
|
364
|
+
<${tag} orientation="horizontal">
|
|
365
|
+
<div role="listitem" id="item-0">
|
|
366
|
+
<button data-invoker>Item 0</button>
|
|
367
|
+
</div>
|
|
368
|
+
<div role="listitem" id="item-1">
|
|
369
|
+
<a href="#baz">Item 1</a>
|
|
370
|
+
</div>
|
|
371
|
+
<div role="listitem" id="item-2">
|
|
372
|
+
<a href="#foobar">Item 2</a>
|
|
373
|
+
</div>
|
|
374
|
+
</${tag}>
|
|
375
|
+
`);
|
|
376
|
+
el._activeMode = 'tabbable-disclosure';
|
|
377
|
+
const { _listNode } = el;
|
|
378
|
+
|
|
379
|
+
// Normalize
|
|
380
|
+
el.activeIndex = 0;
|
|
381
|
+
|
|
382
|
+
mimicKeyPress(_listNode, 'ArrowRight');
|
|
383
|
+
expect(el.activeIndex).to.be.equal(1);
|
|
384
|
+
mimicKeyPress(_listNode, 'ArrowRight');
|
|
385
|
+
expect(el.activeIndex).to.be.equal(2);
|
|
386
|
+
mimicKeyPress(_listNode, 'ArrowLeft');
|
|
387
|
+
expect(el.activeIndex).to.be.equal(1);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('stops navigation by default at end of option list', async () => {
|
|
391
|
+
const el = await fixture(html`
|
|
392
|
+
<${tag}>
|
|
393
|
+
<div role="listitem" id="item-0">
|
|
394
|
+
<button data-invoker>Item 0</button>
|
|
395
|
+
</div>
|
|
396
|
+
<div role="listitem" id="item-1">
|
|
397
|
+
<a href="#baz">Item 1</a>
|
|
398
|
+
</div>
|
|
399
|
+
<div role="listitem" id="item-2">
|
|
400
|
+
<a href="#foobar">Item 2</a>
|
|
401
|
+
</div>
|
|
402
|
+
</${tag}>
|
|
403
|
+
`);
|
|
404
|
+
|
|
405
|
+
const { _listNode } = el;
|
|
406
|
+
|
|
407
|
+
// Normalize
|
|
408
|
+
el.activeIndex = 0;
|
|
409
|
+
|
|
410
|
+
mimicKeyPress(_listNode, 'ArrowUp');
|
|
411
|
+
expect(el.activeIndex).to.be.equal(0);
|
|
412
|
+
|
|
413
|
+
el.activeIndex = 2;
|
|
414
|
+
mimicKeyPress(_listNode, 'ArrowDown');
|
|
415
|
+
expect(el.activeIndex).to.be.equal(2);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('navigates to first and last option with [Home] and [End] keys', async () => {
|
|
419
|
+
const el = await fixture(html`
|
|
420
|
+
<${tag} orientation="horizontal">
|
|
421
|
+
<div role="listitem" id="item-0">
|
|
422
|
+
<button data-invoker>Item 0</button>
|
|
423
|
+
</div>
|
|
424
|
+
<div role="listitem" id="item-1">
|
|
425
|
+
<a href="#baz">Item 1</a>
|
|
426
|
+
</div>
|
|
427
|
+
<div role="listitem" id="item-2">
|
|
428
|
+
<a href="#foobar">Item 2</a>
|
|
429
|
+
</div>
|
|
430
|
+
</${tag}>
|
|
431
|
+
`);
|
|
432
|
+
|
|
433
|
+
const { _listNode } = el;
|
|
434
|
+
|
|
435
|
+
// Normalize
|
|
436
|
+
el.activeIndex = 2;
|
|
437
|
+
|
|
438
|
+
mimicKeyPress(_listNode, 'Home');
|
|
439
|
+
expect(el.activeIndex).to.equal(0);
|
|
440
|
+
mimicKeyPress(_listNode, 'End');
|
|
441
|
+
expect(el.activeIndex).to.equal(2);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
296
446
|
describe('Widget extensions', () => {
|
|
297
447
|
it('can be extended to [role=listbox]', async () => {});
|
|
298
448
|
it('can be extended to [role=menu]', async () => {});
|