@lion/ui 0.18.0-menu-system.6 → 0.18.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.
Files changed (245) hide show
  1. package/collapsible.js +0 -1
  2. package/components/button/src/LionButton.js +0 -4
  3. package/components/button/test-suites/LionButton.suite.js +0 -1
  4. package/components/calendar/src/LionCalendar.js +1 -3
  5. package/components/combobox/test/lion-combobox.test.js +0 -1
  6. package/components/form-core/src/form-group/FormGroupMixin.js +0 -7
  7. package/components/form-core/src/registration/FormRegistrarMixin.js +0 -11
  8. package/components/form-core/test/FormControlMixin.test.js +0 -1
  9. package/components/form-core/test/validate/Validator.test.js +0 -2
  10. package/components/form-core/test-suites/ValidateMixin.suite.js +0 -2
  11. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +0 -1
  12. package/components/input-datepicker/src/localizeNamespaceLoader.js +0 -1
  13. package/components/input-datepicker/test/lion-input-datepicker.test.js +6 -5
  14. package/components/listbox/test-suites/ListboxMixin.suite.js +1 -7
  15. package/components/localize/src/date/utils/normalizeIntlDate.js +2 -3
  16. package/components/localize/test/date/formatDate.test.js +2 -3
  17. package/components/overlays/src/OverlayController.js +110 -278
  18. package/components/overlays/src/OverlayMixin.js +11 -58
  19. package/components/overlays/src/OverlaysManager.js +2 -2
  20. package/components/overlays/src/configurations/withDropdownConfig.js +2 -22
  21. package/components/overlays/src/configurations/withPopoverConfig.js +32 -0
  22. package/components/overlays/src/utils/adopt-styles.js +5 -4
  23. package/components/overlays/src/utils/get-focusable-elements.js +0 -4
  24. package/components/overlays/src/utils/is-equal-config.js +6 -10
  25. package/components/overlays/test/OverlayController.test.js +21 -128
  26. package/components/overlays/test/utils-tests/is-equal-config.test.js +1 -1
  27. package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
  28. package/components/overlays/types/ArrowMixinTypes.ts +1 -3
  29. package/components/overlays/types/OverlayConfig.ts +5 -20
  30. package/components/select-rich/test/lion-select-rich.test.js +0 -3
  31. package/components/switch/src/LionSwitchButton.js +8 -3
  32. package/components/switch/test/lion-switch-button.test.js +38 -0
  33. package/custom-elements.json +17566 -30468
  34. package/dist-types/components/button/src/LionButton.d.ts +0 -1
  35. package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
  36. package/dist-types/components/button/test-suites/LionButton.suite.d.ts.map +1 -1
  37. package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
  38. package/dist-types/components/dialog/src/LionDialog.d.ts +1 -5
  39. package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
  40. package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts.map +1 -1
  41. package/dist-types/components/form-core/src/registration/FormRegistrarMixin.d.ts.map +1 -1
  42. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  43. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  44. package/dist-types/components/input-datepicker/src/localizeNamespaceLoader.d.ts.map +1 -1
  45. package/dist-types/components/listbox/test-suites/ListboxMixin.suite.d.ts.map +1 -1
  46. package/dist-types/components/localize/src/date/utils/normalizeIntlDate.d.ts.map +1 -1
  47. package/dist-types/components/overlays/src/OverlayController.d.ts +12 -24
  48. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  49. package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
  50. package/dist-types/components/overlays/src/configurations/withDropdownConfig.d.ts +0 -1
  51. package/dist-types/components/overlays/src/configurations/withDropdownConfig.d.ts.map +1 -1
  52. package/dist-types/components/overlays/src/configurations/withPopoverConfig.d.ts +6 -0
  53. package/dist-types/components/overlays/src/configurations/withPopoverConfig.d.ts.map +1 -0
  54. package/dist-types/components/overlays/src/utils/get-focusable-elements.d.ts.map +1 -1
  55. package/dist-types/components/overlays/src/utils/is-equal-config.d.ts.map +1 -1
  56. package/dist-types/components/overlays/types/ArrowMixinTypes.d.ts.map +1 -1
  57. package/dist-types/components/overlays/types/OverlayConfig.d.ts +2 -12
  58. package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
  59. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +2 -10
  60. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  61. package/dist-types/components/switch/src/LionSwitchButton.d.ts +6 -0
  62. package/dist-types/components/switch/src/LionSwitchButton.d.ts.map +1 -1
  63. package/dist-types/exports/overlays.d.ts +1 -3
  64. package/dist-types/exports/overlays.d.ts.map +1 -1
  65. package/dist-types/tsconfig.tsbuildinfo +1 -1
  66. package/docs/components/checkbox-group/overview.md +2 -2
  67. package/docs/components/collapsible/overview.md +1 -1
  68. package/docs/components/combobox/api-table.md +8 -21
  69. package/docs/components/combobox/overview.md +1 -1
  70. package/docs/components/combobox/use-cases.md +1 -1
  71. package/docs/components/dialog/api-table.md +6 -31
  72. package/docs/components/dialog/overview.md +1 -1
  73. package/docs/components/fieldset/overview.md +5 -5
  74. package/docs/components/form/overview.md +3 -3
  75. package/docs/components/form/use-cases.md +2 -2
  76. package/docs/components/icon/use-cases.md +1 -1
  77. package/docs/components/input/overview.md +1 -1
  78. package/docs/components/input-amount/overview.md +3 -3
  79. package/docs/components/input-date/overview.md +3 -3
  80. package/docs/components/input-datepicker/api-table.md +4 -21
  81. package/docs/components/input-datepicker/overview.md +5 -5
  82. package/docs/components/input-email/overview.md +2 -2
  83. package/docs/components/input-file/overview.md +1 -1
  84. package/docs/components/input-iban/overview.md +2 -2
  85. package/docs/components/input-range/overview.md +2 -2
  86. package/docs/components/input-stepper/overview.md +1 -1
  87. package/docs/components/input-tel/overview.md +1 -1
  88. package/docs/components/input-tel/use-cases.md +6 -6
  89. package/docs/components/input-tel-dropdown/overview.md +1 -1
  90. package/docs/components/listbox/use-cases.md +1 -1
  91. package/docs/components/radio-group/overview.md +3 -3
  92. package/docs/components/select/use-cases.md +1 -1
  93. package/docs/components/select-rich/api-table.md +5 -21
  94. package/docs/components/select-rich/use-cases.md +2 -2
  95. package/docs/components/switch/api-table.md +2 -0
  96. package/docs/components/tabs/examples.md +1 -1
  97. package/docs/components/tooltip/api-table.md +11 -36
  98. package/docs/fundamentals/index.md +4 -4
  99. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  100. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  101. package/docs/fundamentals/rationales/accessibility.md +1 -1
  102. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  103. package/docs/fundamentals/systems/core/overview.md +1 -1
  104. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  105. package/docs/fundamentals/systems/form/model-value.md +1 -1
  106. package/docs/fundamentals/systems/form/overview.md +28 -28
  107. package/docs/fundamentals/systems/form/validate.md +2 -2
  108. package/docs/fundamentals/systems/localize/overview.md +4 -4
  109. package/docs/fundamentals/systems/overlays/api-table.md +34 -36
  110. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  111. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  112. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  113. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  114. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  115. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  116. package/docs/guides/how-to/create-a-form.md +4 -4
  117. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  118. package/docs/guides/how-to/index.md +1 -1
  119. package/docs/guides/index.md +8 -8
  120. package/docs/guides/principles/index.md +5 -5
  121. package/docs/guides/principles/styling.md +1 -1
  122. package/exports/collapsible.js +0 -1
  123. package/exports/overlays.js +1 -6
  124. package/overlays.js +1 -6
  125. package/package.json +1 -1
  126. package/components/collapsible/src/DisclosureMixin.js +0 -313
  127. package/components/collapsible/test-suites/DisclosureMixin.suite.js +0 -30
  128. package/components/collapsible/types/DisclosureMixinTypes.ts +0 -48
  129. package/components/menu/CHANGELOG.md +0 -37
  130. package/components/menu/README.md +0 -335
  131. package/components/menu/index.js +0 -2
  132. package/components/menu/lion-menu-overlay.js +0 -3
  133. package/components/menu/lion-menu.js +0 -3
  134. package/components/menu/lion-spinbutton.js +0 -3
  135. package/components/menu/lion-toolbar.js +0 -3
  136. package/components/menu/lion-tree.js +0 -3
  137. package/components/menu/package.json +0 -46
  138. package/components/menu/requirements.temp.md +0 -12
  139. package/components/menu/src/InteractiveListMixin.js +0 -841
  140. package/components/menu/src/LionMenu.js +0 -250
  141. package/components/menu/src/LionMenuHybrid.js +0 -46
  142. package/components/menu/src/LionMenuOverlay.js +0 -155
  143. package/components/menu/src/LionSpinbutton.js +0 -213
  144. package/components/menu/src/LionToolbar.js +0 -94
  145. package/components/menu/src/LionTree.js +0 -52
  146. package/components/menu/src/MoreButtonMenuMixin.js +0 -278
  147. package/components/menu/src/MultiLevelListMixin.js +0 -274
  148. package/components/menu/src/utils/isInView.js +0 -26
  149. package/components/menu/src/utils/listItemInteractions.js +0 -124
  150. package/components/menu/src/utils/uuid.js +0 -3
  151. package/components/menu/test/lion-menu.test.js +0 -6
  152. package/components/menu/test-suites/InteractiveListMixin.suite.js +0 -642
  153. package/components/menu/test-suites/LionMenuHybrid.suite.js +0 -583
  154. package/components/menu/types/InteractiveListMixinTypes.d.ts +0 -82
  155. package/components/navigation-bar/README.md +0 -1
  156. package/components/navigation-bar/src/LionNavigationBar.js +0 -439
  157. package/components/navigation-bar/src/types.js +0 -41
  158. package/components/overlays/NEXT.md +0 -49
  159. package/dist-types/components/collapsible/src/DisclosureMixin.d.ts +0 -3
  160. package/dist-types/components/collapsible/src/DisclosureMixin.d.ts.map +0 -1
  161. package/dist-types/components/collapsible/test-suites/DisclosureMixin.suite.d.ts +0 -2
  162. package/dist-types/components/collapsible/test-suites/DisclosureMixin.suite.d.ts.map +0 -1
  163. package/dist-types/components/collapsible/types/DisclosureMixinTypes.d.ts +0 -24
  164. package/dist-types/components/collapsible/types/DisclosureMixinTypes.d.ts.map +0 -1
  165. package/dist-types/components/menu/index.d.ts +0 -3
  166. package/dist-types/components/menu/index.d.ts.map +0 -1
  167. package/dist-types/components/menu/lion-menu-overlay.d.ts +0 -2
  168. package/dist-types/components/menu/lion-menu-overlay.d.ts.map +0 -1
  169. package/dist-types/components/menu/lion-menu.d.ts +0 -2
  170. package/dist-types/components/menu/lion-menu.d.ts.map +0 -1
  171. package/dist-types/components/menu/lion-spinbutton.d.ts +0 -2
  172. package/dist-types/components/menu/lion-spinbutton.d.ts.map +0 -1
  173. package/dist-types/components/menu/lion-toolbar.d.ts +0 -2
  174. package/dist-types/components/menu/lion-toolbar.d.ts.map +0 -1
  175. package/dist-types/components/menu/lion-tree.d.ts +0 -2
  176. package/dist-types/components/menu/lion-tree.d.ts.map +0 -1
  177. package/dist-types/components/menu/src/InteractiveListMixin.d.ts +0 -617
  178. package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +0 -1
  179. package/dist-types/components/menu/src/LionMenu.d.ts +0 -565
  180. package/dist-types/components/menu/src/LionMenu.d.ts.map +0 -1
  181. package/dist-types/components/menu/src/LionMenuHybrid.d.ts +0 -19
  182. package/dist-types/components/menu/src/LionMenuHybrid.d.ts.map +0 -1
  183. package/dist-types/components/menu/src/LionMenuOverlay.d.ts +0 -1013
  184. package/dist-types/components/menu/src/LionMenuOverlay.d.ts.map +0 -1
  185. package/dist-types/components/menu/src/LionSpinbutton.d.ts +0 -58
  186. package/dist-types/components/menu/src/LionSpinbutton.d.ts.map +0 -1
  187. package/dist-types/components/menu/src/LionToolbar.d.ts +0 -8
  188. package/dist-types/components/menu/src/LionToolbar.d.ts.map +0 -1
  189. package/dist-types/components/menu/src/LionTree.d.ts +0 -447
  190. package/dist-types/components/menu/src/LionTree.d.ts.map +0 -1
  191. package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +0 -39
  192. package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +0 -1
  193. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +0 -495
  194. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +0 -1
  195. package/dist-types/components/menu/src/utils/isInView.d.ts +0 -9
  196. package/dist-types/components/menu/src/utils/isInView.d.ts.map +0 -1
  197. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +0 -48
  198. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +0 -1
  199. package/dist-types/components/menu/src/utils/uuid.d.ts +0 -2
  200. package/dist-types/components/menu/src/utils/uuid.d.ts.map +0 -1
  201. package/dist-types/components/menu/test/lion-menu.test.d.ts +0 -2
  202. package/dist-types/components/menu/test/lion-menu.test.d.ts.map +0 -1
  203. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +0 -14
  204. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +0 -1
  205. package/dist-types/components/menu/test-suites/LionMenuHybrid.suite.d.ts +0 -5
  206. package/dist-types/components/menu/test-suites/LionMenuHybrid.suite.d.ts.map +0 -1
  207. package/dist-types/components/navigation-bar/src/LionNavigationBar.d.ts +0 -238
  208. package/dist-types/components/navigation-bar/src/LionNavigationBar.d.ts.map +0 -1
  209. package/dist-types/components/navigation-bar/src/types.d.ts +0 -85
  210. package/dist-types/components/navigation-bar/src/types.d.ts.map +0 -1
  211. package/dist-types/exports/define/lion-item.d.ts +0 -2
  212. package/dist-types/exports/define/lion-item.d.ts.map +0 -1
  213. package/dist-types/exports/define/lion-menu-overlay.d.ts +0 -2
  214. package/dist-types/exports/define/lion-menu-overlay.d.ts.map +0 -1
  215. package/dist-types/exports/define/lion-menu.d.ts +0 -2
  216. package/dist-types/exports/define/lion-menu.d.ts.map +0 -1
  217. package/dist-types/exports/define/lion-navigation-bar.d.ts +0 -2
  218. package/dist-types/exports/define/lion-navigation-bar.d.ts.map +0 -1
  219. package/dist-types/exports/define/lion-toolbar.d.ts +0 -2
  220. package/dist-types/exports/define/lion-toolbar.d.ts.map +0 -1
  221. package/dist-types/exports/define/lion-tree.d.ts +0 -2
  222. package/dist-types/exports/define/lion-tree.d.ts.map +0 -1
  223. package/dist-types/exports/menu-test-suites.d.ts +0 -2
  224. package/dist-types/exports/menu-test-suites.d.ts.map +0 -1
  225. package/dist-types/exports/menu.d.ts +0 -7
  226. package/dist-types/exports/menu.d.ts.map +0 -1
  227. package/dist-types/exports/navigation-bar.d.ts +0 -2
  228. package/dist-types/exports/navigation-bar.d.ts.map +0 -1
  229. package/docs/components/menu/api-table.md +0 -455
  230. package/docs/components/menu/applyMenuDemoStyles.js +0 -173
  231. package/docs/components/menu/extensions.md +0 -262
  232. package/docs/components/menu/index.md +0 -13
  233. package/docs/components/menu/overview.md +0 -422
  234. package/exports/define/lion-item.js +0 -3
  235. package/exports/define/lion-menu-overlay.js +0 -3
  236. package/exports/define/lion-menu.js +0 -3
  237. package/exports/define/lion-navigation-bar.js +0 -3
  238. package/exports/define/lion-toolbar.js +0 -3
  239. package/exports/define/lion-tree.js +0 -3
  240. package/exports/menu-test-suites.js +0 -2
  241. package/exports/menu.js +0 -7
  242. package/exports/navigation-bar.js +0 -1
  243. package/menu-test-suites.js +0 -2
  244. package/menu.js +0 -7
  245. package/navigation-bar.js +0 -1
@@ -1,642 +0,0 @@
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 { useFakeTimers } 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
- /** @type {InteractiveListItemRole[]} */
15
- const supportedRoles = [
16
- 'menuitem',
17
- 'menuitemcheckbox',
18
- 'menuitemradio',
19
- 'option',
20
- 'treeitem',
21
- 'radio',
22
- 'checkbox',
23
- ];
24
-
25
- /**
26
- * @typedef {import('../src/LionMenu.js').LionMenu} LionMenu
27
- * @typedef {import('lit').TemplateResult} TemplateResult
28
- *
29
- * * @typedef {Object} TestConfig
30
- * @property {string} tagString
31
- */
32
-
33
- const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
34
-
35
- const l1Config = {
36
- isBar: true,
37
- hasFullWidthFlyout: true,
38
- openableConfig: {
39
- // We want disclosure behavior
40
- placementMode: 'custom',
41
- // N.B. we don't have an invoker...
42
- isActivated: false,
43
- inheritsReferenceWidth: 'none',
44
- focusContentOnOpen: true,
45
- },
46
- };
47
-
48
- /**
49
- * @param {TestConfig} [customConfig]
50
- */
51
- export function runInteractiveListMixinSuite(customConfig) {
52
- const cfg = {
53
- tagString: customConfig?.tagString || defineCE(InteractiveListClass),
54
- };
55
-
56
- const { tagString } = cfg;
57
- const tag = unsafeStatic(tagString);
58
-
59
- describe('InteractiveListMixin', () => {
60
- it(`supports items with role="${supportedRoles.join('|')}"`, async () => {
61
- const el = await fixture(html`
62
- <${tag} name="foo">
63
- ${supportedRoles.map(role => html` <div role="${role}"></div> `)}
64
- </${tag}>
65
- `);
66
- // @ts-ignore - test element with InteractiveListMixin properties
67
- expect(el.listItems.length).to.equal(supportedRoles.length);
68
- supportedRoles.forEach((role, i) => {
69
- // @ts-ignore - test element with InteractiveListMixin properties
70
- expect(el.listItems[i].getAttribute('role')).to.equal(role);
71
- });
72
- });
73
-
74
- it('moves elements to a private [slot=list] for optimal DX and a11y', async () => {
75
- const el = await fixture(html`
76
- <${tag} name="foo">
77
- <div role="menuitem" id="item1">Item 1</div>
78
- <div role="menuitem" id="item2">Item 2</div>
79
- </${tag}>
80
- `);
81
- // @ts-ignore - test element with InteractiveListMixin properties
82
- const listSlotNode = Array.from(el.children).find(c => c.slot === 'list');
83
- // @ts-ignore - accessing children property
84
- const listItemEls = Array.from(listSlotNode.children);
85
- // @ts-ignore - test element with InteractiveListMixin properties
86
- expect(listItemEls.includes(el.listItems[0])).to.be.true;
87
- // @ts-ignore - test element with InteractiveListMixin properties
88
- expect(listItemEls.includes(el.listItems[1])).to.be.true;
89
- });
90
-
91
- it('inherits from DisclosureMixin', async () => {});
92
-
93
- describe('Active mode', async () => {
94
- it('supports "activedescendant" pattern', async () => {
95
- const el = await fixture(html`
96
- <${tag} name="foo" ._activeMode="${'activedescendant'}">
97
- <div role="menuitem" id="item1">Item 1</div>
98
- <div role="menuitem" id="item2">Item 2</div>
99
- </${tag}>
100
- `);
101
- // @ts-ignore - test element with InteractiveListMixin properties
102
- el.checkedIndex = 0;
103
- // @ts-ignore - test element with InteractiveListMixin properties
104
- const listSlotNode = Array.from(el.children).find(c => c.slot === 'list');
105
-
106
- // @ts-ignore - test element with InteractiveListMixin properties
107
- // @ts-ignore - test element with InteractiveListMixin properties
108
- expect(el.listItems[0].hasAttribute('id')).to.be.true;
109
- // @ts-ignore - test element with InteractiveListMixin properties
110
- expect(el.listItems[1].hasAttribute('id')).to.be.true;
111
- expect(listSlotNode?.hasAttribute('aria-activedescendant')).to.be.false;
112
- // @ts-ignore - test element with InteractiveListMixin properties
113
- el.activeIndex = 1;
114
- expect(listSlotNode?.hasAttribute('aria-activedescendant')).to.be.true;
115
- });
116
-
117
- it('supports "roving-tabindex" pattern', async () => {
118
- const el = await fixture(html`
119
- <${tag} name="foo" ._activeMode="${'roving-tabindex'}">
120
- <div role="menuitem" id="item1">Item 1</div>
121
- <div role="menuitem" id="item2">Item 2</div>
122
- </${tag}>
123
- `);
124
-
125
- // @ts-ignore - test element with InteractiveListMixin properties
126
- el.activeIndex = 0;
127
- // @ts-ignore - test element with InteractiveListMixin properties
128
- expect(el.listItems[0].getAttribute('tabindex')).to.equal('0');
129
- // @ts-ignore - test element with InteractiveListMixin properties
130
- expect(el.listItems[1].getAttribute('tabindex')).to.equal('-1');
131
-
132
- // @ts-ignore - test element with InteractiveListMixin properties
133
- el.activeIndex = 1;
134
- // @ts-ignore - test element with InteractiveListMixin properties
135
- expect(el.listItems[0].getAttribute('tabindex')).to.equal('-1');
136
- // @ts-ignore - test element with InteractiveListMixin properties
137
- expect(el.listItems[1].getAttribute('tabindex')).to.equal('0');
138
-
139
- // @ts-ignore - test element with InteractiveListMixin properties
140
- el.activeIndex = 1;
141
- // @ts-ignore - test element with InteractiveListMixin properties
142
- expect(document.activeElement).to.equal(el.listItems[1]);
143
- });
144
- });
145
-
146
- describe('Programmatic interaction', () => {
147
- // If we have the item line directly in the fixture the linting rule lit-a11y/role-has-required-aria-attrs
148
- // will fail. Inside our code we set the required aria attributes, but the linter doesn't know that.
149
- const item = /** @param {number} i, @param {string} role */ (i, role) => html`
150
- <div role="${role}" id="item${i}">Item ${i}</div>
151
- `;
152
-
153
- it('set checked state sets aria-current attribute with role="listitem"', async () => {
154
- const el = await fixture(html`
155
- <${tag}>
156
- ${item(1, 'listitem')}
157
- ${item(2, 'listitem')}
158
- </${tag}>
159
- `);
160
- el.setCheckedIndex(1);
161
- const item1 = el.listItems[1];
162
- expect(item1.getAttribute('aria-current')).to.equal('true');
163
- el.setCheckedIndex(0);
164
- expect(item1.getAttribute('aria-current')).to.equal('false');
165
- });
166
-
167
- it('set checked state sets aria-selected attribute with role="option"', async () => {
168
- const el = await fixture(html`
169
- <${tag}>
170
- ${item(1, 'option')}
171
- ${item(2, 'option')}
172
- </${tag}>
173
- `);
174
- const item1 = el.listItems[1];
175
- expect(item1.getAttribute('aria-selected')).to.equal('false');
176
-
177
- el.setCheckedIndex(1);
178
- expect(item1.getAttribute('aria-selected')).to.equal('true');
179
-
180
- el.setCheckedIndex(0);
181
- expect(item1.getAttribute('aria-selected')).to.equal('false');
182
- });
183
-
184
- it('set checked state sets aria-selected attribute with role="treeitem"', async () => {
185
- const el = await fixture(html`
186
- <${tag}>
187
- ${item(1, 'treeitem')}
188
- ${item(2, 'treeitem')}
189
- </${tag}>
190
- `);
191
- el.setCheckedIndex(1);
192
- // @ts-ignore - test element with InteractiveListMixin properties
193
- const item1 = el.listItems[1];
194
- expect(item1.getAttribute('aria-selected')).to.equal('true');
195
- el.setCheckedIndex(0);
196
- expect(item1.getAttribute('aria-selected')).to.equal('false');
197
- });
198
-
199
- it('set checked state sets aria-current attribute with role="menuitem"', async () => {
200
- const el = await fixture(html`
201
- <${tag}>
202
- ${item(1, 'menuitem')}
203
- ${item(2, 'menuitem')}
204
- </${tag}>
205
- `);
206
- el.setCheckedIndex(1);
207
- const item1 = el.listItems[1];
208
- expect(item1.getAttribute('aria-current')).to.equal('true');
209
- el.setCheckedIndex(0);
210
- expect(item1.getAttribute('aria-current')).to.equal('false');
211
- });
212
-
213
- it('set checked state sets aria-checked attribute with role="menuitemradio"', async () => {
214
- const el = await fixture(html`
215
- <${tag}>
216
- ${item(1, 'menuitemradio')}
217
- ${item(2, 'menuitemradio')}
218
- </${tag}>
219
- `);
220
- el.setCheckedIndex(1);
221
- const item1 = el.listItems[1];
222
- expect(item1.getAttribute('aria-checked')).to.equal('true');
223
- el.setCheckedIndex(0);
224
- expect(item1.getAttribute('aria-checked')).to.equal('false');
225
- });
226
-
227
- it('set checked state sets aria-checked attribute with role="menuitemcheckbox"', async () => {
228
- const el = await fixture(html`
229
- <${tag}>
230
- ${item(1, 'menuitemcheckbox')}
231
- ${item(2, 'menuitemcheckbox')}
232
- </${tag}>
233
- `);
234
- el.setCheckedIndex(1);
235
- const item1 = el.listItems[1];
236
- expect(item1.getAttribute('aria-checked')).to.equal('true');
237
- el.setCheckedIndex(0);
238
- // As this is multiple choice it doesn't unset the checked state
239
- expect(item1.getAttribute('aria-checked')).to.equal('true');
240
- });
241
-
242
- it('can set checked state to "page" for role="listitem"', async () => {
243
- const el = await fixture(html`
244
- <${tag}>
245
- <a role="listitem" id="item1" href="#">Item 1</a>
246
- <a role="listitem" id="item2" href="#">Item 2</a>
247
- </${tag}>
248
- `);
249
- el.setCheckedIndex(1);
250
- const item1 = el.listItems[1];
251
- expect(item1.getAttribute('aria-current')).to.equal('page');
252
- el.setCheckedIndex(0);
253
- expect(item1.getAttribute('aria-current')).to.equal('false');
254
- });
255
-
256
- it('can set checked state to "mixed" for role="menuitemcheckbox"', async () => {
257
- const el = await fixture(html`
258
- <${tag}>
259
- ${item(1, 'menuitemcheckbox')}
260
- ${item(2, 'menuitemcheckbox')}
261
- </${tag}>
262
- `);
263
- const item1 = el.listItems[1];
264
- // @ts-ignore - not yet supported property
265
- item1.mixedState = true;
266
- el.setCheckedIndex(1);
267
- expect(item1.getAttribute('aria-checked')).to.equal('mixed');
268
- el.setCheckedIndex(0);
269
- expect(item1.getAttribute('aria-checked')).to.equal('mixed');
270
- });
271
-
272
- it('does not allow to set checkedIndex or activeIndex to be out of bound', async () => {
273
- const el = await fixture(html`
274
- <${tag} has-no-default-selected autocomplete="list">
275
- <div role="menuitem" id="item1">Item 1</div>
276
- </${tag}>
277
- `);
278
- expect(() => {
279
- el.checkedIndex = -1;
280
- el.checkedIndex = 1;
281
- }).to.not.throw();
282
- expect(el.checkedIndex).to.equal(-1);
283
- });
284
-
285
- it('unsets checked on other options when option becomes checked', async () => {
286
- const el = await fixture(html`
287
- <${tag}>
288
- <div role="menuitem" id="item1">Item 1</div>
289
- <div role="menuitem" id="item2">Item 2</div>
290
- </${tag}>
291
- `);
292
- const { listItems } = el;
293
- el.setCheckedIndex(0);
294
- expect(listItems[0].hasAttribute('checked')).to.be.true;
295
- expect(listItems[1].hasAttribute('checked')).to.be.false;
296
- el.setCheckedIndex(1);
297
- expect(listItems[0].hasAttribute('checked')).to.be.false;
298
- expect(listItems[1].hasAttribute('checked')).to.be.true;
299
- });
300
- });
301
-
302
- // Copy tests from ListboxMixin. Later, make this mixin a fundament of the ListboxMixin
303
-
304
- // orientation
305
- // multipleChoice
306
- // selectionFollowsFocus
307
- // rotateKeyboardNavigation
308
- // noPreselect
309
- // _scrollTargetNode
310
-
311
- describe('Widget extensions', () => {
312
- it('can be extended to [role=listbox]', async () => {});
313
- it('can be extended to [role=menu]', async () => {});
314
- it('can be extended to [role=menubar]', async () => {});
315
- it('can be extended to [role=toolbar]', async () => {});
316
- it('can be extended to [role=tree]', async () => {});
317
- });
318
-
319
- describe('Overflow handling', () => {
320
- /** @type {import('sinon').SinonFakeTimers | null} */
321
- let clock = null;
322
-
323
- /** @param {HTMLElement} el */
324
- const isMoreButtonShown = el => {
325
- const wrapper = /** @type {HTMLElement | null} */ (
326
- el.querySelector('[data-more-button-wrapper]')
327
- );
328
- return wrapper?.style.display !== 'none';
329
- };
330
-
331
- /** @param {HTMLElement} el */
332
- const isMoreButtonMenuShown = el => {
333
- const menu = el.querySelector('[data-more-button-menu]');
334
- return menu ? getComputedStyle(menu).width !== '0px' : false;
335
- };
336
-
337
- /** @param {HTMLElement} el */
338
- const getMoreButton = el =>
339
- /** @type {HTMLElement | null} */ (el.querySelector('[data-more-button]'));
340
- /** @param {HTMLElement} el */
341
- const getMoreButtonMenu = el => el.querySelector('[data-more-button-menu]');
342
-
343
- /**
344
- * @param {HTMLElement} element
345
- */
346
- function getCoordinates(element) {
347
- const { x, y, height } = element.getBoundingClientRect();
348
-
349
- return {
350
- x: Math.floor(x + window.pageXOffset),
351
- y: Math.floor(y + window.pageYOffset + height / 2),
352
- };
353
- }
354
- /** @param {HTMLElement} el */
355
- const clickOnMoreButton = async el => {
356
- const moreButton = getMoreButton(el);
357
- if (!moreButton) {
358
- return;
359
- }
360
- const { x, y } = getCoordinates(moreButton);
361
- await sendMouse({ type: 'click', position: [x, y] });
362
- clock?.tick(100);
363
- };
364
-
365
- /** @param {HTMLElement} el */
366
- const getDirectListItemsUnderMoreButtonMenu = el => [
367
- ...Array.from(
368
- el.querySelectorAll('[data-more-button-menu] > [role="listitem"]'),
369
- item => /** @type {HTMLElement} */ (item),
370
- ),
371
- ];
372
-
373
- /** @param {HTMLElement} el */
374
- const getDirectListItemsUnderMainMenu = el => [
375
- ...Array.from(
376
- el.querySelectorAll('[slot="list"] > [role="listitem"]'),
377
- item => /** @type {HTMLElement} */ (item),
378
- ),
379
- ];
380
-
381
- /** @param {HTMLElement & { updateComplete: Promise<unknown> }} el */
382
- const waitResizeEventDebounce = async el => {
383
- await el.updateComplete;
384
- // wait for resize event debouncer
385
- clock?.tick(55);
386
- };
387
-
388
- beforeEach(() => {
389
- clock = useFakeTimers({
390
- shouldAdvanceTime: true,
391
- });
392
- });
393
- afterEach(async () => {
394
- clock?.restore();
395
- await resetMouse();
396
- });
397
-
398
- it('should show no More button when all items fit', async () => {
399
- const el = await fixture(html`
400
- <${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
401
- data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
402
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
403
- <a href="#">Item 1</a>
404
- </div>
405
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
406
- <a href="#">Item 2</a>
407
- </div>
408
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
409
- <a href="#">Item 3</a>
410
- </div>
411
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
412
- <button>More</button>
413
- </div>
414
- </${tag}>
415
- `);
416
- await waitResizeEventDebounce(el);
417
- // 3 items is 150px. They fit into 170px parent
418
- expect(isMoreButtonShown(el)).to.equal(false);
419
- });
420
-
421
- it('should show More button when not all items fit', async () => {
422
- const el = await fixture(html`
423
- <${tag}
424
- .itemWrap="${true}"
425
- ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
426
- .config="${l1Config.openableConfig}"
427
- .bar="${l1Config.isBar}"
428
- ._activeMode="${'tabbable-disclosure'}"
429
- style="min-width: 170px; max-width: 170px;"
430
- >
431
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
432
- <a href="#">Item 1</a>
433
- </div>
434
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
435
- <a href="#">Item 2</a>
436
- </div>
437
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
438
- <a href="#">Item 3</a>
439
- </div>
440
- <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
441
- <a href="#">Item 4</a>
442
- </div>
443
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
444
- <button>More</button>
445
- </div>
446
- </${tag}>
447
- <style>
448
- [orientation='horizontal'] ::slotted([slot='list']) {
449
- display: flex;
450
- }
451
- </style>
452
- `);
453
-
454
- await waitResizeEventDebounce(el);
455
- // 4 items is 200px. They don't fit into 170px parent. So More button should be shown
456
- expect(isMoreButtonShown(el)).to.equal(true);
457
- });
458
-
459
- it('should show 2 items when clicking on `More` button', async () => {
460
- const el = await fixture(html`
461
- <${tag}
462
- .itemWrap="${true}"
463
- ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
464
- .config="${l1Config.openableConfig}"
465
- .bar="${l1Config.isBar}"
466
- ._activeMode="${'tabbable-disclosure'}"
467
- style="min-width: 170px; max-width: 170px;"
468
- >
469
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
470
- <a href="#">Item 1</a>
471
- </div>
472
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
473
- <a href="#">Item 2</a>
474
- </div>
475
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
476
- <a href="#">Item 3</a>
477
- </div>
478
- <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
479
- <a href="#">Item 4</a>
480
- </div>
481
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
482
- <button>More</button>
483
- </div>
484
- </${tag}>
485
- `);
486
-
487
- await waitResizeEventDebounce(el);
488
- await clickOnMoreButton(el);
489
-
490
- expect(isMoreButtonMenuShown(el)).to.equal(true);
491
- const labels = getDirectListItemsUnderMoreButtonMenu(el).map(
492
- item => item.textContent?.trim() ?? '',
493
- );
494
- expect(labels).to.deep.equal(['Item 3', 'Item 4']);
495
- });
496
-
497
- it('should focus more button menu container when clicking on `More` button', async () => {
498
- const el = await fixture(html`
499
- <${tag}
500
- .itemWrap="${true}"
501
- ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
502
- .config="${l1Config.openableConfig}"
503
- .bar="${l1Config.isBar}"
504
- ._activeMode="${'tabbable-disclosure'}"
505
- style="min-width: 170px; max-width: 170px;"
506
- >
507
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
508
- <a href="#">Item 1</a>
509
- </div>
510
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
511
- <a href="#">Item 2</a>
512
- </div>
513
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
514
- <a href="#">Item 3</a>
515
- </div>
516
- <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
517
- <a href="#">Item 4</a>
518
- </div>
519
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
520
- <button>More</button>
521
- </div>
522
- </${tag}>
523
- `);
524
-
525
- await waitResizeEventDebounce(el);
526
- await clickOnMoreButton(el);
527
-
528
- expect(isMoreButtonMenuShown(el)).to.equal(true);
529
- expect(getDeepActiveElement() === getMoreButtonMenu(el)).to.equal(true);
530
- });
531
-
532
- it('should open and then close More button menu when clicking 2 times on the More button', async () => {
533
- const el = await fixture(html`
534
- <${tag}
535
- .itemWrap="${true}"
536
- ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
537
- .config="${l1Config.openableConfig}"
538
- .bar="${l1Config.isBar}"
539
- ._activeMode="${'tabbable-disclosure'}"
540
- style="min-width: 170px; max-width: 170px;"
541
- >
542
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
543
- <a href="#">Item 1</a>
544
- </div>
545
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
546
- <a href="#">Item 2</a>
547
- </div>
548
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
549
- <a href="#">Item 3</a>
550
- </div>
551
- <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
552
- <a href="#">Item 4</a>
553
- </div>
554
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
555
- <button>More</button>
556
- </div>
557
- </${tag}>
558
- `);
559
-
560
- await waitResizeEventDebounce(el);
561
- expect(isMoreButtonMenuShown(el)).to.equal(false);
562
- await clickOnMoreButton(el);
563
- expect(isMoreButtonMenuShown(el)).to.equal(true);
564
- await clickOnMoreButton(el);
565
- expect(isMoreButtonMenuShown(el)).to.equal(false);
566
- });
567
-
568
- it('should remove `More` button when making font smaller', async () => {
569
- const el = await fixture(html`
570
- <${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
571
- data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
572
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
573
- <a href="#">Item 1</a>
574
- </div>
575
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
576
- <a href="#">Item 2</a>
577
- </div>
578
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
579
- <a href="#">Item 3</a>
580
- </div>
581
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
582
- <button>More</button>
583
- </div>
584
- </${tag}>
585
- `);
586
- await waitResizeEventDebounce(el);
587
-
588
- /**
589
- * Make font smaller for first level items to force
590
- * the menu render all 4 items and no `More` button
591
- */
592
- getDirectListItemsUnderMainMenu(el).forEach(item => {
593
- // eslint-disable-next-line no-param-reassign
594
- item.style.fontSize = '12px';
595
- });
596
- el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
597
- await waitResizeEventDebounce(el);
598
- expect(isMoreButtonShown(el)).to.equal(false);
599
- });
600
-
601
- it('should add `More` button when making font larger', async () => {
602
- const el = await fixture(html`
603
- <${tag}
604
- .itemWrap="${true}"
605
- ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
606
- .config="${l1Config.openableConfig}"
607
- .bar="${l1Config.isBar}"
608
- ._activeMode="${'tabbable-disclosure'}"
609
- style="min-width: 170px; max-width: 170px;"
610
- >
611
- <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
612
- <a href="#">Item 1</a>
613
- </div>
614
- <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
615
- <a href="#">Item 2</a>
616
- </div>
617
- <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
618
- <a href="#">Item 3</a>
619
- </div>
620
- <div slot="more-button" style="min-width: 50px; max-width: 50px;">
621
- <button>More</button>
622
- </div>
623
- </${tag}>
624
- `);
625
- await waitResizeEventDebounce(el);
626
-
627
- expect(getDirectListItemsUnderMainMenu(el).length).to.equal(3);
628
- /**
629
- * Make font larger for first level items to force
630
- * the More button to appear
631
- */
632
- getDirectListItemsUnderMainMenu(el).forEach(item => {
633
- // eslint-disable-next-line no-param-reassign
634
- item.style.fontSize = '40px';
635
- });
636
- el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
637
- await waitResizeEventDebounce(el);
638
- expect(isMoreButtonShown(el)).to.equal(true);
639
- });
640
- });
641
- });
642
- }