@lion/ui 0.18.0-menu-system.6 → 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 +9 -10
- package/components/menu/src/LionMenu.js +1 -1
- package/components/menu/src/LionTree.js +1 -1
- package/components/menu/src/MultiLevelListMixin.js +46 -9
- 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 -2
- 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 +154 -342
- package/components/menu/test-suites/LionMenu.suite.js +113 -0
- package/components/menu/test-suites/LionMenuHybrid.suite.js +1 -1
- 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/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 +3 -3
- 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/MultiLevelListMixin.d.ts +10 -2
- package/dist-types/components/menu/src/MultiLevelListMixin.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/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
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* eslint-disable lit-a11y/click-events-have-key-events */
|
|
2
|
+
import { defineCE, expect, fixture as _fixture, html, unsafeStatic } from '@open-wc/testing';
|
|
3
|
+
import { LionMenu } from '../src/LionMenu.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {import('lit').TemplateResult} TemplateResult
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Object} TestConfig
|
|
9
|
+
* @property {string} tagString
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {TestConfig} [customConfig]
|
|
16
|
+
*/
|
|
17
|
+
export function runLionMenuSuite(customConfig) {
|
|
18
|
+
const cfg = {
|
|
19
|
+
tagString: customConfig?.tagString || defineCE(LionMenu),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const { tagString } = cfg;
|
|
23
|
+
const tag = unsafeStatic(tagString);
|
|
24
|
+
|
|
25
|
+
describe('LionMenu', () => {
|
|
26
|
+
it('sets the correct role to the listNode', async () => {
|
|
27
|
+
const el = await fixture(html`
|
|
28
|
+
<${tag}>
|
|
29
|
+
<div role="listitem" id="item-0">
|
|
30
|
+
<a href="#foo">Item 0</a>
|
|
31
|
+
</div>
|
|
32
|
+
<div role="listitem" id="item-1">
|
|
33
|
+
<a href="#bar">Item 1</a>
|
|
34
|
+
</div>
|
|
35
|
+
</${tag}>
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
const listNode = el.querySelector('[slot="list"]');
|
|
39
|
+
expect(listNode?.getAttribute('role')).to.equal('menu');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('sets the correct role to the listNode when activeMode is tabbable-disclosure', async () => {
|
|
43
|
+
const el = await fixture(html`
|
|
44
|
+
<${tag} ._activeMode=${'tabbable-disclosure'}>
|
|
45
|
+
<div role="listitem" id="item-0">
|
|
46
|
+
<a href="#foo">Item 0</a>
|
|
47
|
+
</div>
|
|
48
|
+
<div role="listitem" id="item-1">
|
|
49
|
+
<a href="#bar">Item 1</a>
|
|
50
|
+
</div>
|
|
51
|
+
</${tag}>
|
|
52
|
+
`);
|
|
53
|
+
const listNode = el.querySelector('[slot="list"]');
|
|
54
|
+
expect(listNode?.getAttribute('role')).to.equal('list');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('sets the correct roles when it is a bar', async () => {
|
|
58
|
+
const el = await fixture(html`
|
|
59
|
+
<${tag}>
|
|
60
|
+
<div role="listitem" id="item-0">
|
|
61
|
+
<a href="#foo">Item 0</a>
|
|
62
|
+
</div>
|
|
63
|
+
<div role="listitem" id="item-1">
|
|
64
|
+
<a href="#bar">Item 1</a>
|
|
65
|
+
</div>
|
|
66
|
+
</${tag}>
|
|
67
|
+
`);
|
|
68
|
+
|
|
69
|
+
el._listRole = 'menu';
|
|
70
|
+
expect(el._listRole).to.equal('menu');
|
|
71
|
+
expect(el.orientation).to.equal('vertical');
|
|
72
|
+
el.bar = true;
|
|
73
|
+
await el.updateComplete;
|
|
74
|
+
expect(el._listRole).to.equal('menubar');
|
|
75
|
+
expect(el.orientation).to.equal('horizontal');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Programmatic interaction', () => {
|
|
79
|
+
it('allows groups within one menu level', async () => {
|
|
80
|
+
const el = await fixture(html`
|
|
81
|
+
<${tag}>
|
|
82
|
+
<div role="menuitemcheckbox" aria-checked="false">Bold</div>
|
|
83
|
+
<div role="menuitemcheckbox" aria-checked="false">Italic</div>
|
|
84
|
+
<div role="separator"></div>
|
|
85
|
+
<div role="group" aria-label="Text Color">
|
|
86
|
+
<div role="menuitemradio" aria-checked="false">Blue</div>
|
|
87
|
+
<div role="menuitemradio" aria-checked="false">Red</div>
|
|
88
|
+
<div role="menuitemradio" aria-checked="false">Green</div>
|
|
89
|
+
</div>
|
|
90
|
+
</${tag}>
|
|
91
|
+
`);
|
|
92
|
+
el.setCheckedIndex(1);
|
|
93
|
+
const item1 = el.listItems[1];
|
|
94
|
+
const item3 = el.listItems[3];
|
|
95
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
96
|
+
|
|
97
|
+
// change within checkbox group doesn't trigger a change on checkbox
|
|
98
|
+
el.setCheckedIndex(0);
|
|
99
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
100
|
+
|
|
101
|
+
// update outside checkbox group doesn't trigger a change on checkbox
|
|
102
|
+
el.setCheckedIndex(3);
|
|
103
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
104
|
+
expect(item3.getAttribute('aria-checked')).to.equal('true');
|
|
105
|
+
|
|
106
|
+
// change within radio group triggers a change on radio button and not on checkbox
|
|
107
|
+
el.setCheckedIndex(4);
|
|
108
|
+
expect(item1.getAttribute('aria-checked')).to.equal('true');
|
|
109
|
+
expect(item3.getAttribute('aria-checked')).to.equal('false');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -129,7 +129,7 @@ export function runLionMenuHybridSuite({ klass = LionMenuHybrid } = {}) {
|
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* We need actual 100 ms timeout when More button menu is getting hidden,
|
|
132
|
-
* `useFakeTimers` is not an option.
|
|
132
|
+
* `useFakeTimers` is not an option. Otherwise Safari fails.
|
|
133
133
|
* See more info in the description of `_createMoreButtonWrapper` method in `MoreButtonMenuMixin`
|
|
134
134
|
*/
|
|
135
135
|
const waitAfterMoreButtonMenuHides = async () => {
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
|
|
3
|
+
import { sendMouse, resetMouse } from '@web/test-runner-commands';
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { getDeepActiveElement } from '@lion/ui/overlays.js';
|
|
6
|
+
import { InteractiveListMixin } from '../src/InteractiveListMixin.js';
|
|
7
|
+
|
|
8
|
+
class InteractiveListClass extends InteractiveListMixin(LitElement) {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('../types/InteractiveListMixinTypes.js').InteractiveListItemRole} InteractiveListItemRole
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {import('../src/LionMenu.js').LionMenu} LionMenu
|
|
16
|
+
* @typedef {import('lit').TemplateResult} TemplateResult
|
|
17
|
+
*
|
|
18
|
+
* * @typedef {Object} TestConfig
|
|
19
|
+
* @property {string} tagString
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
|
|
23
|
+
|
|
24
|
+
const l1Config = {
|
|
25
|
+
isBar: true,
|
|
26
|
+
hasFullWidthFlyout: true,
|
|
27
|
+
openableConfig: {
|
|
28
|
+
// We want disclosure behavior
|
|
29
|
+
placementMode: 'custom',
|
|
30
|
+
// N.B. we don't have an invoker...
|
|
31
|
+
isActivated: false,
|
|
32
|
+
inheritsReferenceWidth: 'none',
|
|
33
|
+
focusContentOnOpen: true,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {TestConfig} [customConfig]
|
|
39
|
+
*/
|
|
40
|
+
export function runMoreButtonMenuMixinSuite(customConfig) {
|
|
41
|
+
const cfg = {
|
|
42
|
+
tagString: customConfig?.tagString || defineCE(InteractiveListClass),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const { tagString } = cfg;
|
|
46
|
+
const tag = unsafeStatic(tagString);
|
|
47
|
+
|
|
48
|
+
describe('MoreButtonMenuMixin', () => {
|
|
49
|
+
describe('Overflow handling', () => {
|
|
50
|
+
/** @type {import('sinon').SinonFakeTimers | null} */
|
|
51
|
+
let clock = null;
|
|
52
|
+
|
|
53
|
+
/** @param {HTMLElement} el */
|
|
54
|
+
const isMoreButtonShown = el => {
|
|
55
|
+
const wrapper = /** @type {HTMLElement | null} */ (
|
|
56
|
+
el.querySelector('[data-more-button-wrapper]')
|
|
57
|
+
);
|
|
58
|
+
return wrapper?.style.display !== 'none';
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** @param {HTMLElement} el */
|
|
62
|
+
const isMoreButtonMenuShown = el => {
|
|
63
|
+
const menu = el.querySelector('[data-more-button-menu]');
|
|
64
|
+
return menu ? getComputedStyle(menu).width !== '0px' : false;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** @param {HTMLElement} el */
|
|
68
|
+
const getMoreButton = el =>
|
|
69
|
+
/** @type {HTMLElement | null} */ (el.querySelector('[data-more-button]'));
|
|
70
|
+
/** @param {HTMLElement} el */
|
|
71
|
+
const getMoreButtonMenu = el => el.querySelector('[data-more-button-menu]');
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {HTMLElement} element
|
|
75
|
+
*/
|
|
76
|
+
function getCoordinates(element) {
|
|
77
|
+
const { x, y, height } = element.getBoundingClientRect();
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
x: Math.floor(x + window.pageXOffset),
|
|
81
|
+
y: Math.floor(y + window.pageYOffset + height / 2),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** @param {HTMLElement} el */
|
|
85
|
+
const clickOnMoreButton = async el => {
|
|
86
|
+
const moreButton = getMoreButton(el);
|
|
87
|
+
if (!moreButton) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { x, y } = getCoordinates(moreButton);
|
|
91
|
+
await sendMouse({ type: 'click', position: [x, y] });
|
|
92
|
+
clock?.tick(100);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/** @param {HTMLElement} el */
|
|
96
|
+
const getDirectListItemsUnderMoreButtonMenu = el => [
|
|
97
|
+
...Array.from(
|
|
98
|
+
el.querySelectorAll('[data-more-button-menu] > [role="listitem"]'),
|
|
99
|
+
item => /** @type {HTMLElement} */ (item),
|
|
100
|
+
),
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
/** @param {HTMLElement} el */
|
|
104
|
+
const getDirectListItemsUnderMainMenu = el => [
|
|
105
|
+
...Array.from(
|
|
106
|
+
el.querySelectorAll('[slot="list"] > [role="listitem"]'),
|
|
107
|
+
item => /** @type {HTMLElement} */ (item),
|
|
108
|
+
),
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/** @param {HTMLElement & { updateComplete: Promise<unknown> }} el */
|
|
112
|
+
const waitResizeEventDebounce = async el => {
|
|
113
|
+
await el.updateComplete;
|
|
114
|
+
// wait for resize event debouncer
|
|
115
|
+
clock?.tick(55);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
beforeEach(() => {
|
|
119
|
+
clock = sinon.useFakeTimers({
|
|
120
|
+
shouldAdvanceTime: true,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
afterEach(async () => {
|
|
124
|
+
clock?.restore();
|
|
125
|
+
await resetMouse();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should show no More button when all items fit', async () => {
|
|
129
|
+
const el = await fixture(html`
|
|
130
|
+
<${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
|
|
131
|
+
data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
|
|
132
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
133
|
+
<a href="#">Item 1</a>
|
|
134
|
+
</div>
|
|
135
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
136
|
+
<a href="#">Item 2</a>
|
|
137
|
+
</div>
|
|
138
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
139
|
+
<a href="#">Item 3</a>
|
|
140
|
+
</div>
|
|
141
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
142
|
+
<button>More</button>
|
|
143
|
+
</div>
|
|
144
|
+
</${tag}>
|
|
145
|
+
`);
|
|
146
|
+
await waitResizeEventDebounce(el);
|
|
147
|
+
// 3 items is 150px. They fit into 170px parent
|
|
148
|
+
expect(isMoreButtonShown(el)).to.equal(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should show More button when not all items fit', async () => {
|
|
152
|
+
const el = await fixture(html`
|
|
153
|
+
<${tag}
|
|
154
|
+
.itemWrap="${true}"
|
|
155
|
+
?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
|
|
156
|
+
.config="${l1Config.openableConfig}"
|
|
157
|
+
.bar="${l1Config.isBar}"
|
|
158
|
+
._activeMode="${'tabbable-disclosure'}"
|
|
159
|
+
style="min-width: 170px; max-width: 170px;"
|
|
160
|
+
>
|
|
161
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
162
|
+
<a href="#">Item 1</a>
|
|
163
|
+
</div>
|
|
164
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
165
|
+
<a href="#">Item 2</a>
|
|
166
|
+
</div>
|
|
167
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
168
|
+
<a href="#">Item 3</a>
|
|
169
|
+
</div>
|
|
170
|
+
<div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
|
|
171
|
+
<a href="#">Item 4</a>
|
|
172
|
+
</div>
|
|
173
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
174
|
+
<button>More</button>
|
|
175
|
+
</div>
|
|
176
|
+
</${tag}>
|
|
177
|
+
<style>
|
|
178
|
+
[orientation='horizontal'] ::slotted([slot='list']) {
|
|
179
|
+
display: flex;
|
|
180
|
+
}
|
|
181
|
+
</style>
|
|
182
|
+
`);
|
|
183
|
+
|
|
184
|
+
await waitResizeEventDebounce(el);
|
|
185
|
+
// 4 items is 200px. They don't fit into 170px parent. So More button should be shown
|
|
186
|
+
expect(isMoreButtonShown(el)).to.equal(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should show 2 items when clicking on `More` button', async () => {
|
|
190
|
+
const el = await fixture(html`
|
|
191
|
+
<${tag}
|
|
192
|
+
.itemWrap="${true}"
|
|
193
|
+
?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
|
|
194
|
+
.config="${l1Config.openableConfig}"
|
|
195
|
+
.bar="${l1Config.isBar}"
|
|
196
|
+
._activeMode="${'tabbable-disclosure'}"
|
|
197
|
+
style="min-width: 170px; max-width: 170px;"
|
|
198
|
+
>
|
|
199
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
200
|
+
<a href="#">Item 1</a>
|
|
201
|
+
</div>
|
|
202
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
203
|
+
<a href="#">Item 2</a>
|
|
204
|
+
</div>
|
|
205
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
206
|
+
<a href="#">Item 3</a>
|
|
207
|
+
</div>
|
|
208
|
+
<div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
|
|
209
|
+
<a href="#">Item 4</a>
|
|
210
|
+
</div>
|
|
211
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
212
|
+
<button>More</button>
|
|
213
|
+
</div>
|
|
214
|
+
</${tag}>
|
|
215
|
+
`);
|
|
216
|
+
|
|
217
|
+
await waitResizeEventDebounce(el);
|
|
218
|
+
await clickOnMoreButton(el);
|
|
219
|
+
|
|
220
|
+
expect(isMoreButtonMenuShown(el)).to.equal(true);
|
|
221
|
+
const labels = getDirectListItemsUnderMoreButtonMenu(el).map(
|
|
222
|
+
item => item.textContent?.trim() ?? '',
|
|
223
|
+
);
|
|
224
|
+
expect(labels).to.deep.equal(['Item 3', 'Item 4']);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should focus more button menu container when clicking on `More` button', async () => {
|
|
228
|
+
const el = await fixture(html`
|
|
229
|
+
<${tag}
|
|
230
|
+
.itemWrap="${true}"
|
|
231
|
+
?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
|
|
232
|
+
.config="${l1Config.openableConfig}"
|
|
233
|
+
.bar="${l1Config.isBar}"
|
|
234
|
+
._activeMode="${'tabbable-disclosure'}"
|
|
235
|
+
style="min-width: 170px; max-width: 170px;"
|
|
236
|
+
>
|
|
237
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
238
|
+
<a href="#">Item 1</a>
|
|
239
|
+
</div>
|
|
240
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
241
|
+
<a href="#">Item 2</a>
|
|
242
|
+
</div>
|
|
243
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
244
|
+
<a href="#">Item 3</a>
|
|
245
|
+
</div>
|
|
246
|
+
<div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
|
|
247
|
+
<a href="#">Item 4</a>
|
|
248
|
+
</div>
|
|
249
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
250
|
+
<button>More</button>
|
|
251
|
+
</div>
|
|
252
|
+
</${tag}>
|
|
253
|
+
`);
|
|
254
|
+
|
|
255
|
+
await waitResizeEventDebounce(el);
|
|
256
|
+
await clickOnMoreButton(el);
|
|
257
|
+
|
|
258
|
+
expect(isMoreButtonMenuShown(el)).to.equal(true);
|
|
259
|
+
expect(getDeepActiveElement() === getMoreButtonMenu(el)).to.equal(true);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should open and then close More button menu when clicking 2 times on the More button', async () => {
|
|
263
|
+
const el = await fixture(html`
|
|
264
|
+
<${tag}
|
|
265
|
+
.itemWrap="${true}"
|
|
266
|
+
?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
|
|
267
|
+
.config="${l1Config.openableConfig}"
|
|
268
|
+
.bar="${l1Config.isBar}"
|
|
269
|
+
._activeMode="${'tabbable-disclosure'}"
|
|
270
|
+
style="min-width: 170px; max-width: 170px;"
|
|
271
|
+
>
|
|
272
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
273
|
+
<a href="#">Item 1</a>
|
|
274
|
+
</div>
|
|
275
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
276
|
+
<a href="#">Item 2</a>
|
|
277
|
+
</div>
|
|
278
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
279
|
+
<a href="#">Item 3</a>
|
|
280
|
+
</div>
|
|
281
|
+
<div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
|
|
282
|
+
<a href="#">Item 4</a>
|
|
283
|
+
</div>
|
|
284
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
285
|
+
<button>More</button>
|
|
286
|
+
</div>
|
|
287
|
+
</${tag}>
|
|
288
|
+
`);
|
|
289
|
+
|
|
290
|
+
await waitResizeEventDebounce(el);
|
|
291
|
+
expect(isMoreButtonMenuShown(el)).to.equal(false);
|
|
292
|
+
await clickOnMoreButton(el);
|
|
293
|
+
expect(isMoreButtonMenuShown(el)).to.equal(true);
|
|
294
|
+
await clickOnMoreButton(el);
|
|
295
|
+
expect(isMoreButtonMenuShown(el)).to.equal(false);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should remove `More` button when making font smaller', async () => {
|
|
299
|
+
const el = await fixture(html`
|
|
300
|
+
<${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
|
|
301
|
+
data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
|
|
302
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
303
|
+
<a href="#">Item 1</a>
|
|
304
|
+
</div>
|
|
305
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
306
|
+
<a href="#">Item 2</a>
|
|
307
|
+
</div>
|
|
308
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
309
|
+
<a href="#">Item 3</a>
|
|
310
|
+
</div>
|
|
311
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
312
|
+
<button>More</button>
|
|
313
|
+
</div>
|
|
314
|
+
</${tag}>
|
|
315
|
+
`);
|
|
316
|
+
await waitResizeEventDebounce(el);
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Make font smaller for first level items to force
|
|
320
|
+
* the menu render all 4 items and no `More` button
|
|
321
|
+
*/
|
|
322
|
+
getDirectListItemsUnderMainMenu(el).forEach(item => {
|
|
323
|
+
// eslint-disable-next-line no-param-reassign
|
|
324
|
+
item.style.fontSize = '12px';
|
|
325
|
+
});
|
|
326
|
+
el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
|
|
327
|
+
await waitResizeEventDebounce(el);
|
|
328
|
+
expect(isMoreButtonShown(el)).to.equal(false);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should add `More` button when making font larger', async () => {
|
|
332
|
+
const el = await fixture(html`
|
|
333
|
+
<${tag}
|
|
334
|
+
.itemWrap="${true}"
|
|
335
|
+
?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
|
|
336
|
+
.config="${l1Config.openableConfig}"
|
|
337
|
+
.bar="${l1Config.isBar}"
|
|
338
|
+
._activeMode="${'tabbable-disclosure'}"
|
|
339
|
+
style="min-width: 170px; max-width: 170px;"
|
|
340
|
+
>
|
|
341
|
+
<div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
|
|
342
|
+
<a href="#">Item 1</a>
|
|
343
|
+
</div>
|
|
344
|
+
<div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
|
|
345
|
+
<a href="#">Item 2</a>
|
|
346
|
+
</div>
|
|
347
|
+
<div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
|
|
348
|
+
<a href="#">Item 3</a>
|
|
349
|
+
</div>
|
|
350
|
+
<div slot="more-button" style="min-width: 50px; max-width: 50px;">
|
|
351
|
+
<button>More</button>
|
|
352
|
+
</div>
|
|
353
|
+
</${tag}>
|
|
354
|
+
`);
|
|
355
|
+
await waitResizeEventDebounce(el);
|
|
356
|
+
|
|
357
|
+
expect(getDirectListItemsUnderMainMenu(el).length).to.equal(3);
|
|
358
|
+
/**
|
|
359
|
+
* Make font larger for first level items to force
|
|
360
|
+
* the More button to appear
|
|
361
|
+
*/
|
|
362
|
+
getDirectListItemsUnderMainMenu(el).forEach(item => {
|
|
363
|
+
// eslint-disable-next-line no-param-reassign
|
|
364
|
+
item.style.fontSize = '40px';
|
|
365
|
+
});
|
|
366
|
+
el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
|
|
367
|
+
await waitResizeEventDebounce(el);
|
|
368
|
+
expect(isMoreButtonShown(el)).to.equal(true);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
}
|