@lion/ui 0.18.0-menu-system.4 → 0.18.0-menu-system.5
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/collapsible/test-suites/DisclosureMixin.suite.js +30 -0
- package/components/menu/src/InteractiveListMixin.js +56 -23
- package/components/menu/src/LionMenu.js +1 -1
- package/components/menu/src/LionToolbar.js +1 -1
- package/components/menu/src/utils/listItemInteractions.js +25 -6
- package/components/menu/test/lion-menu.test.js +2 -1
- package/components/menu/test-suites/InteractiveListMixin.suite.js +164 -2
- package/custom-elements.json +263 -19
- package/dist-types/components/collapsible/test-suites/DisclosureMixin.suite.d.ts +2 -0
- package/dist-types/components/collapsible/test-suites/DisclosureMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts +11 -5
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -1
- package/dist-types/components/menu/src/LionMenu.d.ts +4 -2
- package/dist-types/components/menu/src/LionMenu.d.ts.map +1 -1
- package/dist-types/components/menu/src/LionMenuOverlay.d.ts +8 -4
- package/dist-types/components/menu/src/LionMenuOverlay.d.ts.map +1 -1
- package/dist-types/components/menu/src/LionTree.d.ts +4 -2
- package/dist-types/components/menu/src/LionTree.d.ts.map +1 -1
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +4 -2
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +1 -1
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +8 -2
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +1 -1
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +6 -5
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -1
- package/dist-types/exports/menu-test-suites.d.ts +2 -0
- package/dist-types/exports/menu-test-suites.d.ts.map +1 -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 +197 -192
- package/docs/components/menu/overview.md +39 -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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// import { LitElement } from 'lit';
|
|
2
|
+
// import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
|
|
3
|
+
// import { DisclosureMixin } from '../src/DisclosureMixin.js';
|
|
4
|
+
|
|
5
|
+
// import '@lion/ui/define/lion-menu.js';
|
|
6
|
+
// import '@lion/ui/define/lion-item.js';
|
|
7
|
+
|
|
8
|
+
// class DisclosureClass extends DisclosureMixin(LitElement) {}
|
|
9
|
+
|
|
10
|
+
// /**
|
|
11
|
+
// * @typedef {import('lit').TemplateResult} TemplateResult
|
|
12
|
+
// */
|
|
13
|
+
|
|
14
|
+
// const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
|
|
15
|
+
|
|
16
|
+
// /**
|
|
17
|
+
// * @param { {tagString?:string, itemTagString?:string} } [customConfig]
|
|
18
|
+
// */
|
|
19
|
+
// export function runDisclosureMixinSuite(customConfig = {}) {
|
|
20
|
+
// const cfg = {
|
|
21
|
+
// tagString: 'lion-menu',
|
|
22
|
+
// ...customConfig,
|
|
23
|
+
// };
|
|
24
|
+
|
|
25
|
+
// const tagString = cfg.tagString || defineCE(DisclosureClass);
|
|
26
|
+
// const tag = unsafeStatic(tagString);
|
|
27
|
+
|
|
28
|
+
// describe('DisclosureMixin', () => {
|
|
29
|
+
// });
|
|
30
|
+
// }
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
|
|
23
23
|
// TODO: consider renaming to FocusGroupMixin
|
|
24
24
|
|
|
25
|
-
// TODO: make all available in controller/
|
|
25
|
+
// TODO: make all available in controller/directive (same logic with an elegant prop-to-host-mapping)
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* @param {Element} potentialFocusable
|
|
@@ -55,13 +55,25 @@ function isFocusableElement(potentialFocusable) {
|
|
|
55
55
|
export class LionItem extends LitElement {
|
|
56
56
|
/** @type {any} */
|
|
57
57
|
static get properties() {
|
|
58
|
-
return {
|
|
58
|
+
return {
|
|
59
|
+
type: { type: String },
|
|
60
|
+
};
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
get _invokerNode() {
|
|
62
|
-
return Array.from(this.children).find(
|
|
64
|
+
return /** @type {HTMLElement[]} */ (Array.from(this.children)).find(
|
|
65
|
+
child => child.slot === 'invoker',
|
|
66
|
+
);
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
static styles = [
|
|
70
|
+
css`
|
|
71
|
+
:host {
|
|
72
|
+
display: block;
|
|
73
|
+
}
|
|
74
|
+
`,
|
|
75
|
+
];
|
|
76
|
+
|
|
65
77
|
constructor() {
|
|
66
78
|
super();
|
|
67
79
|
/** @type {InteractiveListItemRole |''} */
|
|
@@ -81,14 +93,6 @@ export class LionItem extends LitElement {
|
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
|
|
84
|
-
static styles = [
|
|
85
|
-
css`
|
|
86
|
-
:host {
|
|
87
|
-
display: block;
|
|
88
|
-
}
|
|
89
|
-
`,
|
|
90
|
-
];
|
|
91
|
-
|
|
92
96
|
render() {
|
|
93
97
|
return html`
|
|
94
98
|
<slot name="invoker"></slot>
|
|
@@ -332,11 +336,10 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
332
336
|
setCheckedIndex(index) {
|
|
333
337
|
const item = this.listItems[index];
|
|
334
338
|
if (!item) return;
|
|
335
|
-
|
|
336
339
|
if (!this.multipleChoice) {
|
|
337
340
|
// Uncheck all
|
|
338
341
|
this.listItems.forEach(listItem => {
|
|
339
|
-
setChecked(listItem,
|
|
342
|
+
setChecked(listItem, true);
|
|
340
343
|
});
|
|
341
344
|
setChecked(this.listItems[index]);
|
|
342
345
|
} else {
|
|
@@ -377,7 +380,7 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
377
380
|
* - [role="menubar"](https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-1/menubar-1.html)
|
|
378
381
|
* - [role="tree"](https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-1/treeview-1b.html)
|
|
379
382
|
* For disclosure menus, we need keyboard navigation, but treat every list item
|
|
380
|
-
* (more precisely its anchor child) as a tab stop.
|
|
383
|
+
* (more precisely its anchor child) as a tab stop.
|
|
381
384
|
* @type {'activedescendant'|'roving-tabindex'|'tabbable-disclosure'|'none'}
|
|
382
385
|
*/
|
|
383
386
|
this._activeMode = 'activedescendant';
|
|
@@ -426,6 +429,16 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
426
429
|
this._onListFocusIn = this._onListFocusIn.bind(this);
|
|
427
430
|
}
|
|
428
431
|
|
|
432
|
+
connectedCallback() {
|
|
433
|
+
super.connectedCallback();
|
|
434
|
+
window.addEventListener('popstate', this._onPopState);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
disconnectedCallback() {
|
|
438
|
+
super.disconnectedCallback();
|
|
439
|
+
window.removeEventListener('popstate', this._onPopState);
|
|
440
|
+
}
|
|
441
|
+
|
|
429
442
|
/**
|
|
430
443
|
* @param {import('lit').PropertyValues } changedProperties
|
|
431
444
|
*/
|
|
@@ -461,7 +474,6 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
461
474
|
* we search for a[href]
|
|
462
475
|
*/
|
|
463
476
|
if (node.getAttribute('role') === 'group') {
|
|
464
|
-
// console.debug('checking children of', node, node.children);
|
|
465
477
|
// @ts-ignore - HTMLCollection is iterable
|
|
466
478
|
for (const child of node.children) {
|
|
467
479
|
// @ts-ignore
|
|
@@ -568,6 +580,15 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
568
580
|
if (!this.noPreselect && this.checkedIndex === -1) {
|
|
569
581
|
this.checkedIndex = 0;
|
|
570
582
|
}
|
|
583
|
+
newItems.forEach(item => {
|
|
584
|
+
if (!item.hasAttribute('checked')) {
|
|
585
|
+
setChecked(item, true);
|
|
586
|
+
} else {
|
|
587
|
+
setChecked(item);
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
this.__syncCurrentPageWithLocationHref(window.location);
|
|
571
592
|
}
|
|
572
593
|
|
|
573
594
|
/**
|
|
@@ -706,16 +727,8 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
706
727
|
// const item = /** @type {HTMLElement} */ (target.closest('[role]'));
|
|
707
728
|
// @ts-ignore - target is HTMLElement
|
|
708
729
|
const foundIndex = this.listItems.indexOf(target);
|
|
709
|
-
// console.debug('focusin', target, foundIndex, this.listItems);
|
|
710
|
-
const previousActiveIndex = this.activeIndex;
|
|
711
730
|
if (foundIndex > -1 && foundIndex !== this.activeIndex) {
|
|
712
731
|
this.activeIndex = foundIndex;
|
|
713
|
-
if (this._activeMode === 'tabbable-disclosure') {
|
|
714
|
-
const previousActiveItem = this.listItems[previousActiveIndex];
|
|
715
|
-
previousActiveItem?.removeAttribute('aria-current');
|
|
716
|
-
const activeItem = this.listItems[this.activeIndex];
|
|
717
|
-
activeItem.setAttribute('aria-current', 'page');
|
|
718
|
-
}
|
|
719
732
|
}
|
|
720
733
|
}
|
|
721
734
|
|
|
@@ -800,6 +813,26 @@ const InteractiveListMixinImplementation = superclass =>
|
|
|
800
813
|
_getPreviousEnabledOption(currentIndex, offset = -1) {
|
|
801
814
|
return this.__getNextOption(currentIndex, offset);
|
|
802
815
|
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* @param {Location} location
|
|
819
|
+
*/
|
|
820
|
+
__syncCurrentPageWithLocationHref(location) {
|
|
821
|
+
this.listItems.forEach(item => {
|
|
822
|
+
if (/** @type {HTMLAnchorElement} */ (item).href) {
|
|
823
|
+
if (location.href.includes(/** @type {HTMLAnchorElement} */ (item).href)) {
|
|
824
|
+
setChecked(item);
|
|
825
|
+
} else {
|
|
826
|
+
setChecked(item, true);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/** @protected */
|
|
833
|
+
_onPopState = () => {
|
|
834
|
+
this.__syncCurrentPageWithLocationHref(document.location);
|
|
835
|
+
};
|
|
803
836
|
};
|
|
804
837
|
// @ts-ignore
|
|
805
838
|
export const InteractiveListMixin = dedupeMixin(InteractiveListMixinImplementation);
|
|
@@ -189,7 +189,7 @@ export class LionMenu extends MultiLevelListMixin(LitElement) {
|
|
|
189
189
|
if (!multiple) {
|
|
190
190
|
// Uncheck all
|
|
191
191
|
listItemsWithinGroup.forEach(listItem => {
|
|
192
|
-
setChecked(listItem,
|
|
192
|
+
setChecked(listItem, true);
|
|
193
193
|
});
|
|
194
194
|
setChecked(this.listItems[index]);
|
|
195
195
|
} else {
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* 'smart' (LionOption) and 'regular' items
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('@lion/ui/checkbox-group.js').LionCheckboxIndeterminate} LionCheckboxIndeterminate
|
|
8
|
+
*/
|
|
9
|
+
|
|
6
10
|
/**
|
|
7
11
|
* @param {HTMLElement} item
|
|
8
12
|
*/
|
|
@@ -34,18 +38,33 @@ export function toggleDisabled(item) {
|
|
|
34
38
|
* @param {HTMLElement} item
|
|
35
39
|
*/
|
|
36
40
|
export function isChecked(item) {
|
|
37
|
-
return item.hasAttribute('checked')
|
|
41
|
+
return item.hasAttribute('checked');
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
/**
|
|
41
45
|
* @param {HTMLElement} item
|
|
42
|
-
* @param {
|
|
46
|
+
* @param {Object} [options]
|
|
47
|
+
* @param {boolean} [options.unset=false]
|
|
48
|
+
* @param {'page'|'mixed'} [options.text] - optional text to set for aria-checked/aria-selected/aria-current (e.g. "mixed")
|
|
43
49
|
*/
|
|
44
|
-
export function setChecked(item,
|
|
45
|
-
const
|
|
46
|
-
|
|
50
|
+
export function setChecked(item, unset = false) {
|
|
51
|
+
const role = item.getAttribute('role');
|
|
52
|
+
let ariaAttr = 'current';
|
|
53
|
+
if (role?.endsWith('checkbox') || role?.endsWith('radio')) {
|
|
54
|
+
ariaAttr = 'checked';
|
|
55
|
+
} else if (role === 'option' || role === 'treeitem') {
|
|
56
|
+
ariaAttr = 'selected';
|
|
57
|
+
}
|
|
58
|
+
let text;
|
|
59
|
+
if (/** @type {HTMLAnchorElement} */ (item).href) {
|
|
60
|
+
text = 'page';
|
|
61
|
+
}
|
|
62
|
+
if (/** @type {LionCheckboxIndeterminate} */ (item).mixedState) {
|
|
63
|
+
text = 'mixed';
|
|
64
|
+
}
|
|
65
|
+
if (!unset) {
|
|
47
66
|
item.setAttribute('checked', '');
|
|
48
|
-
item.setAttribute(`aria-${ariaAttr}`, 'true');
|
|
67
|
+
item.setAttribute(`aria-${ariaAttr}`, text || 'true');
|
|
49
68
|
} else {
|
|
50
69
|
item.removeAttribute('checked');
|
|
51
70
|
item.setAttribute(`aria-${ariaAttr}`, 'false');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
-
import { expect, html, fixture, unsafeStatic, defineCE } from '@open-wc/testing';
|
|
2
|
+
import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
|
|
3
3
|
import { InteractiveListMixin } from '../src/InteractiveListMixin.js';
|
|
4
4
|
|
|
5
5
|
class InteractiveListClass extends InteractiveListMixin(LitElement) {}
|
|
@@ -20,10 +20,15 @@ const supportedRoles = [
|
|
|
20
20
|
];
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* @typedef {
|
|
23
|
+
* @typedef {import('../src/LionMenu.js').LionMenu} LionMenu
|
|
24
|
+
* @typedef {import('lit').TemplateResult} TemplateResult
|
|
25
|
+
*
|
|
26
|
+
* * @typedef {Object} TestConfig
|
|
24
27
|
* @property {string} tagString
|
|
25
28
|
*/
|
|
26
29
|
|
|
30
|
+
const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
|
|
31
|
+
|
|
27
32
|
/**
|
|
28
33
|
* @param {TestConfig} [customConfig]
|
|
29
34
|
*/
|
|
@@ -122,6 +127,163 @@ export function runInteractiveListMixinSuite(customConfig) {
|
|
|
122
127
|
});
|
|
123
128
|
});
|
|
124
129
|
|
|
130
|
+
describe('Programmatic interaction', () => {
|
|
131
|
+
// If we have the item line directly in the fixture the linting rule lit-a11y/role-has-required-aria-attrs
|
|
132
|
+
// will fail. Inside our code we set the required aria attributes, but the linter doesn't know that.
|
|
133
|
+
const item = /** @param {number} i, @param {string} role */ (i, role) => html`
|
|
134
|
+
<div role="${role}" id="item${i}">Item ${i}</div>
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
it('set checked state sets aria-current attribute with role="listitem"', async () => {
|
|
138
|
+
const el = await fixture(html`
|
|
139
|
+
<${tag}>
|
|
140
|
+
${item(1, 'listitem')}
|
|
141
|
+
${item(2, 'listitem')}
|
|
142
|
+
</${tag}>
|
|
143
|
+
`);
|
|
144
|
+
el.setCheckedIndex(1);
|
|
145
|
+
const item1 = el.listItems[1];
|
|
146
|
+
expect(item1.getAttribute('aria-current')).to.equal('true');
|
|
147
|
+
el.setCheckedIndex(0);
|
|
148
|
+
expect(item1.getAttribute('aria-current')).to.equal('false');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('set checked state sets aria-selected attribute with role="option"', async () => {
|
|
152
|
+
const el = await fixture(html`
|
|
153
|
+
<${tag}>
|
|
154
|
+
${item(1, 'option')}
|
|
155
|
+
${item(2, 'option')}
|
|
156
|
+
</${tag}>
|
|
157
|
+
`);
|
|
158
|
+
console.debug('listItems', el.listItems);
|
|
159
|
+
const item1 = el.listItems[1];
|
|
160
|
+
expect(item1.getAttribute('aria-selected')).to.equal('false');
|
|
161
|
+
|
|
162
|
+
el.setCheckedIndex(1);
|
|
163
|
+
expect(item1.getAttribute('aria-selected')).to.equal('true');
|
|
164
|
+
|
|
165
|
+
el.setCheckedIndex(0);
|
|
166
|
+
expect(item1.getAttribute('aria-selected')).to.equal('false');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('set checked state sets aria-selected attribute with role="treeitem"', async () => {
|
|
170
|
+
const el = await fixture(html`
|
|
171
|
+
<${tag}>
|
|
172
|
+
${item(1, 'treeitem')}
|
|
173
|
+
${item(2, 'treeitem')}
|
|
174
|
+
</${tag}>
|
|
175
|
+
`);
|
|
176
|
+
el.setCheckedIndex(1);
|
|
177
|
+
// @ts-ignore - test element with InteractiveListMixin properties
|
|
178
|
+
const item1 = el.listItems[1];
|
|
179
|
+
expect(item1.getAttribute('aria-selected')).to.equal('true');
|
|
180
|
+
el.setCheckedIndex(0);
|
|
181
|
+
expect(item1.getAttribute('aria-selected')).to.equal('false');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('set checked state sets aria-current attribute with role="menuitem"', async () => {
|
|
185
|
+
const el = await fixture(html`
|
|
186
|
+
<${tag}>
|
|
187
|
+
${item(1, 'menuitem')}
|
|
188
|
+
${item(2, 'menuitem')}
|
|
189
|
+
</${tag}>
|
|
190
|
+
`);
|
|
191
|
+
el.setCheckedIndex(1);
|
|
192
|
+
const item1 = el.listItems[1];
|
|
193
|
+
expect(item1.getAttribute('aria-current')).to.equal('true');
|
|
194
|
+
el.setCheckedIndex(0);
|
|
195
|
+
expect(item1.getAttribute('aria-current')).to.equal('false');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('set checked state sets aria-checked attribute with role="menuitemradio"', async () => {
|
|
199
|
+
const el = await fixture(html`
|
|
200
|
+
<${tag}>
|
|
201
|
+
${item(1, 'menuitemradio')}
|
|
202
|
+
${item(2, 'menuitemradio')}
|
|
203
|
+
</${tag}>
|
|
204
|
+
`);
|
|
205
|
+
el.setCheckedIndex(1);
|
|
206
|
+
const item1 = el.listItems[1];
|
|
207
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
208
|
+
el.setCheckedIndex(0);
|
|
209
|
+
expect(item1.getAttribute('aria-checked')).to.equal('false');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('set checked state sets aria-checked attribute with role="menuitemcheckbox"', async () => {
|
|
213
|
+
const el = await fixture(html`
|
|
214
|
+
<${tag}>
|
|
215
|
+
${item(1, 'menuitemcheckbox')}
|
|
216
|
+
${item(2, 'menuitemcheckbox')}
|
|
217
|
+
</${tag}>
|
|
218
|
+
`);
|
|
219
|
+
el.setCheckedIndex(1);
|
|
220
|
+
const item1 = el.listItems[1];
|
|
221
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
222
|
+
el.setCheckedIndex(0);
|
|
223
|
+
// As this is multiple choice it doesn't unset the checked state
|
|
224
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('can set checked state to "page" for role="listitem"', async () => {
|
|
228
|
+
const el = await fixture(html`
|
|
229
|
+
<${tag}>
|
|
230
|
+
<a role="listitem" id="item1" href="#">Item 1</a>
|
|
231
|
+
<a role="listitem" id="item2" href="#">Item 2</a>
|
|
232
|
+
</${tag}>
|
|
233
|
+
`);
|
|
234
|
+
el.setCheckedIndex(1);
|
|
235
|
+
const item1 = el.listItems[1];
|
|
236
|
+
expect(item1.getAttribute('aria-current')).to.equal('page');
|
|
237
|
+
el.setCheckedIndex(0);
|
|
238
|
+
expect(item1.getAttribute('aria-current')).to.equal('false');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('can set checked state to "mixed" for role="menuitemcheckbox"', async () => {
|
|
242
|
+
const el = await fixture(html`
|
|
243
|
+
<${tag}>
|
|
244
|
+
${item(1, 'menuitemcheckbox')}
|
|
245
|
+
${item(2, 'menuitemcheckbox')}
|
|
246
|
+
</${tag}>
|
|
247
|
+
`);
|
|
248
|
+
const item1 = el.listItems[1];
|
|
249
|
+
// @ts-ignore - not yet supported property
|
|
250
|
+
item1.mixedState = true;
|
|
251
|
+
el.setCheckedIndex(1);
|
|
252
|
+
expect(item1.getAttribute('aria-checked')).to.equal('mixed');
|
|
253
|
+
el.setCheckedIndex(0);
|
|
254
|
+
expect(item1.getAttribute('aria-checked')).to.equal('mixed');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('does not allow to set checkedIndex or activeIndex to be out of bound', async () => {
|
|
258
|
+
const el = await fixture(html`
|
|
259
|
+
<${tag} has-no-default-selected autocomplete="list">
|
|
260
|
+
<div role="menuitem" id="item1">Item 1</div>
|
|
261
|
+
</${tag}>
|
|
262
|
+
`);
|
|
263
|
+
expect(() => {
|
|
264
|
+
el.checkedIndex = -1;
|
|
265
|
+
el.checkedIndex = 1;
|
|
266
|
+
}).to.not.throw();
|
|
267
|
+
expect(el.checkedIndex).to.equal(-1);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('unsets checked on other options when option becomes checked', async () => {
|
|
271
|
+
const el = await fixture(html`
|
|
272
|
+
<${tag}>
|
|
273
|
+
<div role="menuitem" id="item1">Item 1</div>
|
|
274
|
+
<div role="menuitem" id="item2">Item 2</div>
|
|
275
|
+
</${tag}>
|
|
276
|
+
`);
|
|
277
|
+
const { listItems } = el;
|
|
278
|
+
el.setCheckedIndex(0);
|
|
279
|
+
expect(listItems[0].hasAttribute('checked')).to.be.true;
|
|
280
|
+
expect(listItems[1].hasAttribute('checked')).to.be.false;
|
|
281
|
+
el.setCheckedIndex(1);
|
|
282
|
+
expect(listItems[0].hasAttribute('checked')).to.be.false;
|
|
283
|
+
expect(listItems[1].hasAttribute('checked')).to.be.true;
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
125
287
|
// Copy tests from ListboxMixin. Later, make this mixin a fundament of the ListboxMixin
|
|
126
288
|
|
|
127
289
|
// orientation
|