@lion/ui 0.15.0 → 0.15.1

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 (208) hide show
  1. package/README.md +38 -12
  2. package/components/combobox/test/lion-combobox.test.js +50 -2
  3. package/components/core/src/SlotMixin.js +52 -0
  4. package/components/core/test/SlotMixin.test.js +85 -0
  5. package/components/listbox/src/ListboxMixin.js +3 -35
  6. package/components/overlays/src/ArrowMixin.js +10 -4
  7. package/components/overlays/test/OverlayController.test.js +4 -1
  8. package/custom-elements.json +2959 -207
  9. package/dist-types/components/core/src/ScopedElementsMixin.d.ts.map +1 -1
  10. package/dist-types/components/core/src/SlotMixin.d.ts +35 -0
  11. package/dist-types/components/core/src/SlotMixin.d.ts.map +1 -1
  12. package/dist-types/components/core/test/SlotMixin.test.d.ts.map +1 -1
  13. package/dist-types/components/listbox/src/ListboxMixin.d.ts.map +1 -1
  14. package/dist-types/components/overlays/src/ArrowMixin.d.ts.map +1 -1
  15. package/dist-types/tsconfig.tsbuildinfo +1 -1
  16. package/docs/components/accordion/index.md +1 -1
  17. package/docs/components/accordion/overview.md +1 -1
  18. package/docs/components/accordion/reference.md +1 -1
  19. package/docs/components/accordion/use-cases.md +1 -1
  20. package/docs/components/button/{extensions/bootstrap-button.mjs → assets/bootstrap-button.js} +10 -5
  21. package/docs/components/button/examples.md +1 -1
  22. package/docs/components/button/extensions.md +2 -2
  23. package/docs/components/button/index.md +2 -2
  24. package/docs/components/button/overview.md +4 -4
  25. package/docs/components/button/use-cases.md +6 -6
  26. package/docs/components/calendar/index.md +1 -1
  27. package/docs/components/calendar/overview.md +1 -1
  28. package/docs/components/calendar/use-cases.md +2 -2
  29. package/docs/components/checkbox-group/index.md +1 -1
  30. package/docs/components/checkbox-group/overview.md +3 -3
  31. package/docs/components/checkbox-group/use-cases.md +1 -1
  32. package/docs/components/collapsible/examples.md +1 -1
  33. package/docs/components/collapsible/index.md +1 -1
  34. package/docs/components/collapsible/overview.md +2 -2
  35. package/docs/components/collapsible/use-cases.md +1 -1
  36. package/docs/components/combobox/extensions.md +2 -1
  37. package/docs/components/combobox/index.md +1 -1
  38. package/docs/components/combobox/overview.md +2 -2
  39. package/docs/components/combobox/use-cases.md +2 -2
  40. package/docs/components/dialog/index.md +1 -1
  41. package/docs/components/dialog/overview.md +2 -2
  42. package/docs/components/dialog/use-cases.md +1 -1
  43. package/docs/components/drawer/index.md +1 -1
  44. package/docs/components/drawer/overview.md +1 -1
  45. package/docs/components/drawer/use-cases.md +1 -1
  46. package/docs/components/fieldset/index.md +1 -1
  47. package/docs/components/fieldset/overview.md +6 -6
  48. package/docs/components/fieldset/use-cases.md +1 -1
  49. package/docs/components/form/index.md +1 -1
  50. package/docs/components/form/overview.md +4 -4
  51. package/docs/components/form/use-cases.md +3 -3
  52. package/docs/components/icon/index.md +1 -1
  53. package/docs/components/icon/overview.md +1 -1
  54. package/docs/components/icon/use-cases.md +2 -15
  55. package/docs/components/index.md +1 -1
  56. package/docs/components/input/index.md +1 -1
  57. package/docs/components/input/overview.md +2 -2
  58. package/docs/components/input/use-cases.md +1 -1
  59. package/docs/components/input-amount/index.md +1 -1
  60. package/docs/components/input-amount/overview.md +4 -4
  61. package/docs/components/input-amount/use-cases.md +1 -1
  62. package/docs/components/input-amount-dropdown/index.md +1 -1
  63. package/docs/components/input-amount-dropdown/use-cases.md +1 -1
  64. package/docs/components/input-date/index.md +1 -1
  65. package/docs/components/input-date/overview.md +4 -4
  66. package/docs/components/input-date/use-cases.md +1 -1
  67. package/docs/components/input-datepicker/index.md +1 -1
  68. package/docs/components/input-datepicker/overview.md +6 -6
  69. package/docs/components/input-datepicker/use-cases.md +1 -1
  70. package/docs/components/input-email/index.md +1 -1
  71. package/docs/components/input-email/overview.md +3 -3
  72. package/docs/components/input-email/use-cases.md +1 -1
  73. package/docs/components/input-file/index.md +1 -1
  74. package/docs/components/input-file/overview.md +2 -2
  75. package/docs/components/input-file/use-cases.md +1 -1
  76. package/docs/components/input-iban/index.md +1 -1
  77. package/docs/components/input-iban/overview.md +3 -3
  78. package/docs/components/input-iban/use-cases.md +1 -1
  79. package/docs/components/input-range/index.md +1 -1
  80. package/docs/components/input-range/overview.md +3 -3
  81. package/docs/components/input-range/use-cases.md +1 -1
  82. package/docs/components/input-stepper/index.md +1 -1
  83. package/docs/components/input-stepper/overview.md +2 -2
  84. package/docs/components/input-stepper/use-cases.md +1 -1
  85. package/docs/components/input-tel/index.md +1 -1
  86. package/docs/components/input-tel/overview.md +2 -2
  87. package/docs/components/input-tel/use-cases.md +7 -7
  88. package/docs/components/input-tel-dropdown/extensions.md +1 -1
  89. package/docs/components/input-tel-dropdown/index.md +1 -1
  90. package/docs/components/input-tel-dropdown/overview.md +2 -2
  91. package/docs/components/input-tel-dropdown/use-cases.md +1 -1
  92. package/docs/components/listbox/index.md +1 -1
  93. package/docs/components/listbox/overview.md +1 -1
  94. package/docs/components/listbox/use-cases.md +2 -2
  95. package/docs/components/pagination/index.md +1 -1
  96. package/docs/components/pagination/overview.md +1 -1
  97. package/docs/components/pagination/use-cases.md +1 -1
  98. package/docs/components/progress-indicator/examples.md +1 -1
  99. package/docs/components/progress-indicator/index.md +1 -1
  100. package/docs/components/progress-indicator/overview.md +1 -1
  101. package/docs/components/radio-group/index.md +1 -1
  102. package/docs/components/radio-group/overview.md +4 -4
  103. package/docs/components/radio-group/use-cases.md +1 -1
  104. package/docs/components/select/index.md +1 -1
  105. package/docs/components/select/overview.md +1 -1
  106. package/docs/components/select/use-cases.md +2 -2
  107. package/docs/components/select-rich/examples.md +1 -1
  108. package/docs/components/select-rich/index.md +1 -1
  109. package/docs/components/select-rich/overview.md +1 -1
  110. package/docs/components/select-rich/use-cases.md +3 -3
  111. package/docs/components/steps/index.md +1 -1
  112. package/docs/components/steps/overview.md +1 -1
  113. package/docs/components/steps/use-cases.md +1 -1
  114. package/docs/components/switch/index.md +1 -1
  115. package/docs/components/switch/overview.md +1 -1
  116. package/docs/components/switch/reference.md +1 -1
  117. package/docs/components/switch/use-cases.md +1 -1
  118. package/docs/components/tabs/examples.md +2 -2
  119. package/docs/components/tabs/index.md +1 -1
  120. package/docs/components/tabs/overview.md +1 -1
  121. package/docs/components/tabs/use-cases.md +1 -1
  122. package/docs/components/textarea/index.md +1 -1
  123. package/docs/components/textarea/overview.md +1 -1
  124. package/docs/components/textarea/use-cases.md +1 -1
  125. package/docs/components/tooltip/examples.md +1 -1
  126. package/docs/components/tooltip/index.md +1 -1
  127. package/docs/components/tooltip/overview.md +1 -1
  128. package/docs/components/tooltip/use-cases.md +1 -1
  129. package/docs/fundamentals/index.md +4 -4
  130. package/docs/fundamentals/node-tools/babel-plugin-extend-docs/index.md +1 -1
  131. package/docs/fundamentals/node-tools/babel-plugin-extend-docs/overview.md +1 -1
  132. package/docs/fundamentals/node-tools/index.md +1 -1
  133. package/docs/fundamentals/node-tools/providence-analytics/index.md +1 -1
  134. package/docs/fundamentals/node-tools/providence-analytics/overview.md +1 -1
  135. package/docs/fundamentals/node-tools/publish-docs/index.md +1 -1
  136. package/docs/fundamentals/node-tools/publish-docs/overview.md +4 -4
  137. package/docs/fundamentals/node-tools/remark-extend/index.md +1 -1
  138. package/docs/fundamentals/node-tools/remark-extend/overview.md +1 -1
  139. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/index.md +1 -1
  140. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +3 -3
  141. package/docs/fundamentals/rationales/TypeScript.md +1 -1
  142. package/docs/fundamentals/rationales/accessibility.md +2 -2
  143. package/docs/fundamentals/rationales/index.md +1 -1
  144. package/docs/fundamentals/rationales/side-effects.md +1 -1
  145. package/docs/fundamentals/rationales/versioning.md +1 -1
  146. package/docs/fundamentals/systems/core/SlotMixin.md +2 -2
  147. package/docs/fundamentals/systems/core/index.md +1 -1
  148. package/docs/fundamentals/systems/core/overview.md +2 -2
  149. package/docs/fundamentals/systems/form/formatting-and-parsing.md +5 -5
  150. package/docs/fundamentals/systems/form/index.md +1 -1
  151. package/docs/fundamentals/systems/form/interaction-states.md +1 -1
  152. package/docs/fundamentals/systems/form/model-value.md +2 -2
  153. package/docs/fundamentals/systems/form/overview.md +29 -29
  154. package/docs/fundamentals/systems/form/styling.md +1 -1
  155. package/docs/fundamentals/systems/form/use-cases.md +1 -1
  156. package/docs/fundamentals/systems/form/validate.md +3 -3
  157. package/docs/fundamentals/systems/icon/index.md +1 -1
  158. package/docs/fundamentals/systems/icon/overview.md +1 -1
  159. package/docs/fundamentals/systems/index.md +1 -1
  160. package/docs/fundamentals/systems/localize/api-table.md +3 -0
  161. package/docs/fundamentals/systems/localize/dates.md +1 -1
  162. package/docs/fundamentals/systems/localize/index.md +1 -1
  163. package/docs/fundamentals/systems/localize/numbers.md +1 -1
  164. package/docs/fundamentals/systems/localize/overview.md +5 -5
  165. package/docs/fundamentals/systems/localize/rationale.md +1 -1
  166. package/docs/fundamentals/systems/localize/text.md +5 -4
  167. package/docs/fundamentals/systems/localize/use-cases.md +1 -1
  168. package/docs/fundamentals/systems/overlays/_configuration-positioning.md +3 -3
  169. package/docs/fundamentals/systems/overlays/_edge-cases.md +3 -3
  170. package/docs/fundamentals/systems/overlays/api-table.md +3 -0
  171. package/docs/fundamentals/systems/overlays/assets/demo-overlay-positioning.mjs +1 -1
  172. package/docs/fundamentals/systems/overlays/configuration.md +5 -5
  173. package/docs/fundamentals/systems/overlays/index.md +1 -1
  174. package/docs/fundamentals/systems/overlays/overview.md +6 -6
  175. package/docs/fundamentals/systems/overlays/rationale.md +1 -1
  176. package/docs/fundamentals/systems/overlays/use-cases.md +6 -6
  177. package/docs/fundamentals/tools/ajax/index.md +1 -1
  178. package/docs/fundamentals/tools/ajax/overview.md +2 -2
  179. package/docs/fundamentals/tools/ajax/use-cases.md +3 -3
  180. package/docs/fundamentals/tools/helpers/action-logger.md +1 -1
  181. package/docs/fundamentals/tools/helpers/index.md +2 -2
  182. package/docs/fundamentals/tools/helpers/overview.md +2 -2
  183. package/docs/fundamentals/tools/index.md +1 -1
  184. package/docs/fundamentals/tools/singleton-manager/example-complex/index.md +1 -1
  185. package/docs/fundamentals/tools/singleton-manager/example-fail/demo-app.js +4 -4
  186. package/docs/fundamentals/tools/singleton-manager/example-fail/index.md +2 -2
  187. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/{page-a/page-a.js → page-c/page-c.js} +2 -2
  188. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/{page-b/page-b.js → page-d/page-d.js} +2 -2
  189. package/docs/fundamentals/tools/singleton-manager/example-success/demo-app.js +4 -4
  190. package/docs/fundamentals/tools/singleton-manager/example-success/index.md +2 -2
  191. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/{page-a/page-a.js → page-e/page-e.js} +2 -3
  192. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/{page-b/page-b.js → page-f/page-f.js} +2 -2
  193. package/docs/fundamentals/tools/singleton-manager/index.md +2 -2
  194. package/docs/fundamentals/tools/singleton-manager/overview.md +5 -5
  195. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  196. package/docs/guides/how-to/create-a-form.md +4 -4
  197. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  198. package/docs/guides/index.md +8 -8
  199. package/docs/guides/principles/index.md +5 -5
  200. package/docs/guides/principles/styling.md +1 -1
  201. package/package.json +1 -1
  202. /package/docs/fundamentals/systems/overlays/assets/{applyDemoOverlayStyles.mjs → applyDemoOverlayStyles.js} +0 -0
  203. /package/docs/fundamentals/systems/overlays/assets/{demo-el-using-overlaymixin.mjs → demo-el-using-overlaymixin.js} +0 -0
  204. /package/docs/fundamentals/systems/overlays/assets/{demo-overlay-backdrop.mjs → demo-overlay-backdrop.js} +0 -0
  205. /package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/{page-b → page-d}/node_modules/overlays/index.js +0 -0
  206. /package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/{page-b → page-d}/node_modules/overlays/instance.js +0 -0
  207. /package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/{page-b → page-f}/node_modules/overlays/index.js +0 -0
  208. /package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/{page-b → page-f}/node_modules/overlays/instance.js +0 -0
package/README.md CHANGED
@@ -26,33 +26,59 @@
26
26
  </p>
27
27
 
28
28
  <p align="center">
29
- <a href="https://lion.js.org">Website</a>
29
+ <a href="https://lion-web.netlify.app">Website</a>
30
30
  ·
31
- <a href="https://lion.js.org/fundamentals/">Fundamentals</a>
31
+ <a href="https://lion-web.netlify.app/fundamentals/">Fundamentals</a>
32
32
  ·
33
- <a href="https://lion.js.org/guides/">Guides</a>
33
+ <a href="https://lion-web.netlify.app/guides/">Guides</a>
34
34
  ·
35
- <a href="https://lion.js.org/components/">Components</a>
35
+ <a href="https://lion-web.netlify.app/components/">Components</a>
36
36
  ·
37
- <a href="https://lion.js.org/blog/">Blog</a>
37
+ <a href="https://lion-web.netlify.app/blog/">Blog</a>
38
38
  </p>
39
39
 
40
- **Lion is a set of highly performant, accessible, and flexible Web Components.**
40
+ **Lion is a set of highly performant, accessible and flexible Web Components.!**
41
41
 
42
42
  They provide an unopinionated, white-label layer that can be extended to your own layer of components.
43
43
 
44
44
  - **High Performance:** Focused on great performance in all relevant browsers with a minimal number of dependencies.
45
45
  - **Accessibility:** Aimed at compliance with the WCAG 2.2 AA standard to create components that are accessible for everybody.
46
46
  - **Flexibility:** Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs.
47
- - **Modern Code:** Lion is distributed as pure ES modules.
48
- - **Exposes functions/classes and Web Components:** Ships a functionality in its most appropriate form.
47
+ - **Modern Code:** Lion is distributes as pure es modules.
48
+ - **Exposes functions/classes and Web Components:** Ships a functionality in it's most appropriate form.
49
49
 
50
- > Note: Our demo examples might look simple and not very stylish. This is on purpose. They are designed to be basic so you can easily add your own styles to them to match your intended design, without dealing with styles that are already there.
50
+ > Note: Our demos may look a little bland but that is on purpose. They only come with functional stylings.
51
+ > This makes sense as the main use case is to extend those components and if you do you do not want to override existing stylings.
51
52
 
52
53
  <p align="center">
53
- <a href="https://lion.js.org/guides/"><strong>Explore the Lion Guides&nbsp;&nbsp;▶</strong></a>
54
+ <a href="https://lion-web.netlify.app/guides/"><strong>Explore the Lion Guides&nbsp;&nbsp;▶</strong></a>
54
55
  </p>
55
56
 
57
+ ## Astro migration
58
+
59
+ - Keep using `/docs` on the root level as we used it in the `master` branch. The documentation is copied into Astro related directories on `npm run start` and when when anything in `/docs` is updated.
60
+ - Replace manually all references to assets in all `md` files so that we imply that the path is produced from the directory where the md file is located. F.e. `new URL('../src/wa-combobox/assets/obama.jpeg', import.meta.url).href;` should `new URL('./src/wa-combobox/assets/obama.jpeg', import.meta.url).href;`. Note double dot is replaced with one dot. See [this PR](https://github.com/ing-bank/lion/pull/2125/files#diff-403b1e0ab54d51dcfe54248e847498e492da00383d8b33a4087994ab9035a22c) for the reference.
61
+ - Rename all `*.mjs` files to `*.js` ones if they are used by `mdjs` examples
62
+
63
+ ### Astro how to
64
+
65
+ - To start in dev mode run `nmm run start`
66
+ - To start in production mode:
67
+ - Run `npm run build`
68
+ - Run `npm run preview`
69
+
70
+ ### Issues which are not caused by the migration (not to be fixed now)
71
+
72
+ - There is a browser console error on [collapsible page](http://localhost:4321/components/collapsible):
73
+
74
+ ```
75
+ __mdjs-stories--use-cases.js:40 Uncaught TypeError: shadowRoot.getElementById is not a function
76
+ ```
77
+
78
+ Note. There is the same error on master. This issue is not caused by the migration
79
+
80
+ ### TODO
81
+
56
82
  ## How to install
57
83
 
58
84
  ```bash
@@ -113,7 +139,7 @@ You can also use the lion elements directly, although this is likely not a commo
113
139
 
114
140
  ## Issues
115
141
 
116
- If you encounter an issue with any of the packages we are offering please open a [new bug issue](https://github.com/ing-bank/lion/issues/new?assignees=&labels=&template=bug_report.md&title=). Be sure to include a description of the expected and the current behavior - additionally adding a [reproduction](https://webcomponents.dev/edit/kpZmz1CJN580OaXsk56f?pm=1) always helps.
142
+ If you encounter an issue with any of the packages we are offering please open a [new bug issue](https://github.com/ing-bank/lion/issues/new?assignees=&labels=&template=bug_report.md&title=). Be sure to include a description of the expected and the current behavior - additional adding a [reproduction](https://webcomponents.dev/edit/kpZmz1CJN580OaXsk56f?pm=1) always helps.
117
143
 
118
144
  ## Feature requests
119
145
 
@@ -158,7 +184,7 @@ This means you only have to apply your own Design System: by delivering styles,
158
184
 
159
185
  ## Coding guidelines
160
186
 
161
- Check out our [coding guidelines](https://lion.js.org/guides/principles/definitions-and-terms/) for more detailed information.
187
+ Check out our [coding guidelines](https://lion-web.netlify.app/guides/principles/definitions-and-terms/) for more detailed information.
162
188
 
163
189
  ## How to contribute
164
190
 
@@ -2,11 +2,11 @@ import { defineCE, expect, fixture, html, unsafeStatic, waitUntil } from '@open-
2
2
  import { Required, Unparseable } from '@lion/ui/form-core.js';
3
3
  import { sendKeys } from '@web/test-runner-commands';
4
4
  import { LionCombobox } from '@lion/ui/combobox.js';
5
- import { browserDetection } from '@lion/ui/core.js';
5
+ import { browserDetection, SlotMixin } from '@lion/ui/core.js';
6
6
  import '@lion/ui/define/lion-combobox.js';
7
7
  import '@lion/ui/define/lion-listbox.js';
8
8
  import '@lion/ui/define/lion-option.js';
9
- import { LitElement } from 'lit';
9
+ import { LitElement, nothing } from 'lit';
10
10
  import sinon from 'sinon';
11
11
  import {
12
12
  getFilteredOptionValues,
@@ -413,6 +413,54 @@ describe('lion-combobox', () => {
413
413
  expect(el.validationStates).to.have.property('error');
414
414
  expect(el.validationStates.error).to.have.property('MatchesOption');
415
415
  });
416
+
417
+ it('keeps slottable provided in `slots` getter as direct host child', async () => {
418
+ class MyEl extends SlotMixin(LionCombobox) {
419
+ // @ts-ignore
420
+ get slots() {
421
+ return {
422
+ ...super.slots,
423
+ _lazyRenderedSlot: () => ({
424
+ template: this.renderSlot
425
+ ? html`<span id="lazyRenderedSlotId">(Optional)</span>`
426
+ : html`${nothing}`,
427
+ renderAsDirectHostChild: true,
428
+ }),
429
+ };
430
+ }
431
+
432
+ _labelTemplate() {
433
+ return html`
434
+ <div class="form-field__label">
435
+ <slot name="label"></slot>
436
+ <slot name="_lazyRenderedSlot"></slot>
437
+ <slot></slot>
438
+ </div>
439
+ `;
440
+ }
441
+
442
+ constructor() {
443
+ super();
444
+ this.renderSlot = false;
445
+ }
446
+ }
447
+ const tagName = defineCE(MyEl);
448
+ const wrappingTag = unsafeStatic(tagName);
449
+
450
+ const el = /** @type {MyEl} */ (
451
+ await fixture(html`
452
+ <${wrappingTag} label="my label">
453
+ <lion-option .choiceValue="${'1'}">${'one'}</lion-option>
454
+ </${wrappingTag}>
455
+ `)
456
+ );
457
+ await el.registrationComplete;
458
+
459
+ el.renderSlot = true;
460
+ await el.updateComplete;
461
+ const lazyRenderedSlot = el.querySelector('#lazyRenderedSlotId');
462
+ expect(lazyRenderedSlot?.parentElement === el).to.be.true;
463
+ });
416
464
  });
417
465
 
418
466
  describe('Values', () => {
@@ -12,6 +12,58 @@ import { render } from 'lit';
12
12
  * @typedef {import('lit').LitElement} LitElement
13
13
  */
14
14
 
15
+ /**
16
+ * Sometimes, we want to provide best DX (direct slottables) and be accessible
17
+ * at the same time.
18
+ * In the first example below, we need to wrap our options in light dom in an element with
19
+ * [role=listbox]. We could achieve this via the second example, but it would affect our
20
+ * public api negatively. not allowing us to be forward compatible with the AOM spec:
21
+ * https://wicg.github.io/aom/explainer.html
22
+ * With this method, it's possible to watch elements in the default slot and move them
23
+ * to the desired target (the element with [role=listbox]) in light dom.
24
+ *
25
+ * @example
26
+ * # desired api
27
+ * <sel-ect>
28
+ * <opt-ion></opt-ion>
29
+ * </sel-ect>
30
+ * # desired end state
31
+ * <sel-ect>
32
+ * <div role="listbox" slot="lisbox">
33
+ * <opt-ion></opt-ion>
34
+ * </div>
35
+ * </sel-ect>
36
+ *
37
+ * Note, the function does not move the nodes specified by a subclasser in the `slots` getter
38
+ * @param {HTMLElement} source host of ShadowRoot with default <slot>
39
+ * @param {HTMLElement} target the desired target in light dom
40
+ */
41
+ export function moveUserProvidedDefaultSlottablesToTarget(source, target) {
42
+ /**
43
+ * Nodes injected via `slots` getter are going to be added as host's children
44
+ * starting by a comment node like <!--_start_slot_*-->
45
+ * and ending by a comment node like <!--_end_slot_*-->
46
+ * So we ignore everything that comes between those `start_slot` and `end_slot` comments
47
+ */
48
+ let isInsideSlotSection = false;
49
+ Array.from(source.childNodes).forEach((/** @type {* & Element} */ c) => {
50
+ const isNamedSlottable = c.hasAttribute && c.hasAttribute('slot');
51
+ const isComment = c.nodeType === Node.COMMENT_NODE;
52
+ if (isComment && !isInsideSlotSection) {
53
+ isInsideSlotSection = c.textContent.includes('_start_slot_');
54
+ }
55
+ if (isInsideSlotSection) {
56
+ if (c.textContent.includes('_end_slot_')) {
57
+ isInsideSlotSection = false;
58
+ }
59
+ return;
60
+ }
61
+ if (!isNamedSlottable) {
62
+ target.appendChild(c);
63
+ }
64
+ });
65
+ }
66
+
15
67
  /**
16
68
  * @param {SlotFunctionResult} slotFunctionResult
17
69
  * @returns {'template-result'|'node'|'slot-rerender-object'|null}
@@ -2,6 +2,7 @@ import { defineCE, expect, fixture, fixtureSync, unsafeStatic, html } from '@ope
2
2
  import { SlotMixin } from '@lion/ui/core.js';
3
3
  import { LitElement } from 'lit';
4
4
  import sinon from 'sinon';
5
+ import { moveUserProvidedDefaultSlottablesToTarget } from '../src/SlotMixin.js';
5
6
 
6
7
  import { ScopedElementsMixin, supportsScopedRegistry } from '../src/ScopedElementsMixin.js';
7
8
  import { isActiveElement } from '../test-helpers/isActiveElement.js';
@@ -134,6 +135,90 @@ describe('SlotMixin', () => {
134
135
  expect(elNoSlot.didCreateConditionalSlot()).to.be.false;
135
136
  });
136
137
 
138
+ it('should move default slots to target', async () => {
139
+ const target = document.createElement('div');
140
+ const source = document.createElement('div');
141
+ /**
142
+ * Exmple of usage:
143
+ * get slots() {
144
+ * return {
145
+ * _nothing: () => ({
146
+ * template: html`${nothing}`,
147
+ * renderAsDirectHostChild: true,
148
+ * }),
149
+ * }
150
+ * }
151
+ */
152
+ const variableNothing = `
153
+ <!--_start_slot_lit-el-->
154
+ <!-- {lit-el} -->
155
+ <!--_end_slot_lit-el-->`;
156
+
157
+ /**
158
+ * Exmple of usage:
159
+ * get slots() {
160
+ * return {
161
+ * '': () => ({
162
+ * template: html`<div>text<div>`,
163
+ * renderAsDirectHostChild: true,
164
+ * }),
165
+ * }
166
+ * }
167
+ */
168
+ const defaultSlottableProvidedViaSlotsGetter = `
169
+ <!--_start_slot_-->
170
+ <div>text</div>
171
+ <!--_end_slot_-->
172
+ `;
173
+
174
+ /**
175
+ * Exmple of usage:
176
+ * get slots() {
177
+ * return {
178
+ * label: () => ({
179
+ * template: html`<div>text<div>`,
180
+ * renderAsDirectHostChild: true,
181
+ * }),
182
+ * }
183
+ * }
184
+ */
185
+ const namedSlottable = `
186
+ <!--_start_slot_label-->
187
+ <div slot="label">text</div>
188
+ <!--_end_slot_label-->
189
+ `;
190
+
191
+ /**
192
+ * Here we assume .test1, .test2 and .test3 are the ones provided as content projection f.e.:
193
+ * <my-comp>
194
+ * <div class="test1"><div>
195
+ * <div class="test2"><div>
196
+ * <div class="test3"><div>
197
+ * </my-comp>
198
+ *
199
+ * And the rest of content is added via `slots` getter by SlotMixin
200
+ * The function should move only content projection and ignore the rest
201
+ * */
202
+
203
+ source.innerHTML = `
204
+ <div class="test1"></div>
205
+ ${variableNothing}
206
+ <div class="test2"></div>
207
+ ${defaultSlottableProvidedViaSlotsGetter}
208
+ <div class="test3"></div>
209
+ ${namedSlottable}
210
+ `;
211
+
212
+ moveUserProvidedDefaultSlottablesToTarget(source, target);
213
+ expect(target.children.length).to.equal(3);
214
+ const test1Element = target.querySelector('.test1');
215
+ const test2Element = target.querySelector('.test2');
216
+ const test3Element = target.querySelector('.test3');
217
+ expect(test1Element?.parentElement === target).to.equal(true);
218
+ expect(test2Element?.parentElement === target).to.equal(true);
219
+ expect(test3Element?.parentElement === target).to.equal(true);
220
+ });
221
+
137
222
  describe('Rerender', () => {
138
223
  it('supports rerender when SlotRerenderObject provided', async () => {
139
224
  const tag = defineCE(
@@ -4,6 +4,7 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
4
4
  import { ChoiceGroupMixin, FormControlMixin, FormRegistrarMixin } from '@lion/ui/form-core.js';
5
5
  import { ScopedElementsMixin } from '../../core/src/ScopedElementsMixin.js';
6
6
  import { LionOptions } from './LionOptions.js';
7
+ import { moveUserProvidedDefaultSlottablesToTarget } from '../../core/src/SlotMixin.js';
7
8
 
8
9
  // TODO: extract ListNavigationWithActiveDescendantMixin that can be reused in [role="menu"]
9
10
  // having children with [role="menuitem|menuitemcheckbox|menuitemradio|option"] and
@@ -21,39 +22,6 @@ import { LionOptions } from './LionOptions.js';
21
22
 
22
23
  // TODO: consider adding methods below to @lion/helpers
23
24
 
24
- /**
25
- * Sometimes, we want to provide best DX (direct slottables) and be accessible
26
- * at the same time.
27
- * In the first example below, we need to wrap our options in light dom in an element with
28
- * [role=listbox]. We could achieve this via the second example, but it would affect our
29
- * public api negatively. not allowing us to be forward compatible with the AOM spec:
30
- * https://wicg.github.io/aom/explainer.html
31
- * With this method, it's possible to watch elements in the default slot and move them
32
- * to the desired target (the element with [role=listbox]) in light dom.
33
- *
34
- * @example
35
- * # desired api
36
- * <sel-ect>
37
- * <opt-ion></opt-ion>
38
- * </sel-ect>
39
- * # desired end state
40
- * <sel-ect>
41
- * <div role="listbox" slot="lisbox">
42
- * <opt-ion></opt-ion>
43
- * </div>
44
- * </sel-ect>
45
- * @param {HTMLElement} source host of ShadowRoot with default <slot>
46
- * @param {HTMLElement} target the desired target in light dom
47
- */
48
- function moveDefaultSlottablesToTarget(source, target) {
49
- Array.from(source.childNodes).forEach((/** @type {* & Element} */ c) => {
50
- const isNamedSlottable = c.hasAttribute && c.hasAttribute('slot');
51
- if (!isNamedSlottable) {
52
- target.appendChild(c);
53
- }
54
- });
55
- }
56
-
57
25
  /**
58
26
  * @type {ListboxMixin}
59
27
  * @param {import('@open-wc/dedupe-mixin').Constructor<import('lit').LitElement>} superclass
@@ -899,9 +867,9 @@ const ListboxMixinImplementation = superclass =>
899
867
  );
900
868
 
901
869
  if (slot) {
902
- moveDefaultSlottablesToTarget(this, this._listboxNode);
870
+ moveUserProvidedDefaultSlottablesToTarget(this, this._listboxNode);
903
871
  slot.addEventListener('slotchange', () => {
904
- moveDefaultSlottablesToTarget(this, this._listboxNode);
872
+ moveUserProvidedDefaultSlottablesToTarget(this, this._listboxNode);
905
873
  });
906
874
  }
907
875
  }
@@ -64,8 +64,11 @@ export const ArrowMixinImplementation = superclass =>
64
64
 
65
65
  [data-popper-placement^='left'] .arrow {
66
66
  right: calc(
67
- -1 * (var(--tooltip-arrow-height) +
68
- (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2)
67
+ -1 *
68
+ (
69
+ var(--tooltip-arrow-height) +
70
+ (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2
71
+ )
69
72
  );
70
73
  }
71
74
 
@@ -75,8 +78,11 @@ export const ArrowMixinImplementation = superclass =>
75
78
 
76
79
  [data-popper-placement^='right'] .arrow {
77
80
  left: calc(
78
- -1 * (var(--tooltip-arrow-height) +
79
- (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2)
81
+ -1 *
82
+ (
83
+ var(--tooltip-arrow-height) +
84
+ (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2
85
+ )
80
86
  );
81
87
  }
82
88
 
@@ -799,6 +799,9 @@ describe('OverlayController', () => {
799
799
  const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
800
800
  await mimicEscapePress(childOverlay.contentNode);
801
801
 
802
+ // without this line, the test is unstable on FF sometimes
803
+ await aTimeout(0);
804
+
802
805
  expect(parentOverlay.isShown).to.be.false;
803
806
  expect(childOverlay.isShown).to.be.true;
804
807
 
@@ -1040,7 +1043,7 @@ describe('OverlayController', () => {
1040
1043
  // Don't hide on inside shadowDom click
1041
1044
  /** @type {ShadowRoot} */
1042
1045
  // @ts-expect-error
1043
- (ctrl.contentNode.querySelector(tagString).shadowRoot).querySelector('button').click();
1046
+ ctrl.contentNode.querySelector(tagString).shadowRoot.querySelector('button').click();
1044
1047
 
1045
1048
  await aTimeout(0);
1046
1049
  expect(ctrl.isShown).to.be.true;