@lion/ui 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/components/calendar/src/LionCalendar.js +10 -4
  2. package/components/combobox/src/LionCombobox.js +6 -4
  3. package/components/core/src/DisabledMixin.js +6 -4
  4. package/components/core/src/DisabledWithTabIndexMixin.js +6 -4
  5. package/components/core/src/SlotMixin.js +180 -42
  6. package/components/core/test/SlotMixin.test.js +341 -138
  7. package/components/core/types/SlotMixinTypes.ts +13 -2
  8. package/components/form-core/src/FormatMixin.js +10 -8
  9. package/components/form-core/src/InteractionStateMixin.js +8 -6
  10. package/components/form-core/src/choice-group/ChoiceInputMixin.js +6 -4
  11. package/components/form-core/src/utils/SyncUpdatableMixin.js +9 -4
  12. package/components/form-core/test/utils/SyncUpdatableMixin.test.js +6 -4
  13. package/components/form-core/test-suites/ValidateMixin.suite.js +1 -1
  14. package/components/form-core/test-suites/form-group/FormGroupMixin.suite.js +2 -2
  15. package/components/input/src/LionInput.js +6 -4
  16. package/components/input-datepicker/src/LionInputDatepicker.js +6 -4
  17. package/components/input-tel/src/LionInputTel.js +7 -22
  18. package/components/input-tel/src/PhoneUtilManager.js +9 -5
  19. package/components/input-tel/src/formatters.js +8 -10
  20. package/components/input-tel/src/parsers.js +3 -4
  21. package/components/input-tel/src/preprocessors.js +2 -4
  22. package/components/input-tel/src/validators.js +4 -6
  23. package/components/input-tel/test/validators.test.js +8 -5
  24. package/components/input-tel-dropdown/src/LionInputTelDropdown.js +41 -32
  25. package/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js +5 -6
  26. package/components/listbox/src/LionOption.js +6 -4
  27. package/components/localize/types/LocalizeMixinTypes.ts +1 -1
  28. package/components/overlays/src/OverlayMixin.js +6 -5
  29. package/components/overlays/src/OverlaysManager.js +21 -4
  30. package/components/overlays/src/globalOverlaysStyle.js +10 -0
  31. package/components/overlays/test/OverlaysManager.test.js +76 -0
  32. package/components/overlays/test-suites/OverlayMixin.suite.js +20 -14
  33. package/components/select-rich/src/LionSelectRich.js +6 -4
  34. package/components/switch/src/LionSwitchButton.js +6 -7
  35. package/custom-elements.json +2002 -291
  36. package/dist-types/components/accordion/src/LionAccordion.d.ts +1 -1
  37. package/dist-types/components/button/src/LionButton.d.ts +1 -1
  38. package/dist-types/components/calendar/src/LionCalendar.d.ts +7 -41
  39. package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
  40. package/dist-types/components/checkbox-group/src/LionCheckboxGroup.d.ts +1 -1
  41. package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts +0 -4
  42. package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts.map +1 -1
  43. package/dist-types/components/collapsible/src/LionCollapsible.d.ts +1 -1
  44. package/dist-types/components/combobox/src/LionCombobox.d.ts +5 -3
  45. package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
  46. package/dist-types/components/core/src/DisabledMixin.d.ts.map +1 -1
  47. package/dist-types/components/core/src/DisabledWithTabIndexMixin.d.ts.map +1 -1
  48. package/dist-types/components/core/src/SlotMixin.d.ts +3 -1
  49. package/dist-types/components/core/src/SlotMixin.d.ts.map +1 -1
  50. package/dist-types/components/core/test/SlotMixin.test.d.ts +1 -1
  51. package/dist-types/components/core/test/SlotMixin.test.d.ts.map +1 -1
  52. package/dist-types/components/core/types/SlotMixinTypes.d.ts +10 -2
  53. package/dist-types/components/core/types/SlotMixinTypes.d.ts.map +1 -1
  54. package/dist-types/components/dialog/src/LionDialog.d.ts +1 -1
  55. package/dist-types/components/fieldset/src/LionFieldset.d.ts +1 -1
  56. package/dist-types/components/form-core/src/FormatMixin.d.ts.map +1 -1
  57. package/dist-types/components/form-core/src/InteractionStateMixin.d.ts.map +1 -1
  58. package/dist-types/components/form-core/src/LionField.d.ts +1 -1
  59. package/dist-types/components/form-core/src/choice-group/ChoiceInputMixin.d.ts.map +1 -1
  60. package/dist-types/components/form-core/src/utils/SyncUpdatableMixin.d.ts.map +1 -1
  61. package/dist-types/components/form-core/src/validate/LionValidationFeedback.d.ts +1 -1
  62. package/dist-types/components/form-core/test-suites/FormRegistrationMixins.suite.d.ts +1 -1
  63. package/dist-types/components/form-integrations/test/helpers/umbrella-form.d.ts +1 -1
  64. package/dist-types/components/helpers/src/SbActionLogger.d.ts +1 -1
  65. package/dist-types/components/helpers/src/SbLocaleSwitcher.d.ts +1 -1
  66. package/dist-types/components/icon/src/LionIcon.d.ts +1 -1
  67. package/dist-types/components/input/src/LionInput.d.ts +5 -3
  68. package/dist-types/components/input/src/LionInput.d.ts.map +1 -1
  69. package/dist-types/components/input-amount/src/LionInputAmount.d.ts +1 -37
  70. package/dist-types/components/input-amount/src/LionInputAmount.d.ts.map +1 -1
  71. package/dist-types/components/input-date/src/LionInputDate.d.ts +1 -37
  72. package/dist-types/components/input-date/src/LionInputDate.d.ts.map +1 -1
  73. package/dist-types/components/input-datepicker/src/LionCalendarOverlayFrame.d.ts +2 -38
  74. package/dist-types/components/input-datepicker/src/LionCalendarOverlayFrame.d.ts.map +1 -1
  75. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +0 -5
  76. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts.map +1 -1
  77. package/dist-types/components/input-email/src/LionInputEmail.d.ts +1 -37
  78. package/dist-types/components/input-email/src/LionInputEmail.d.ts.map +1 -1
  79. package/dist-types/components/input-iban/src/LionInputIban.d.ts +1 -37
  80. package/dist-types/components/input-iban/src/LionInputIban.d.ts.map +1 -1
  81. package/dist-types/components/input-tel/src/LionInputTel.d.ts +3 -63
  82. package/dist-types/components/input-tel/src/LionInputTel.d.ts.map +1 -1
  83. package/dist-types/components/input-tel/src/PhoneUtilManager.d.ts +8 -4
  84. package/dist-types/components/input-tel/src/PhoneUtilManager.d.ts.map +1 -1
  85. package/dist-types/components/input-tel/src/formatters.d.ts +0 -2
  86. package/dist-types/components/input-tel/src/formatters.d.ts.map +1 -1
  87. package/dist-types/components/input-tel/src/parsers.d.ts +0 -2
  88. package/dist-types/components/input-tel/src/parsers.d.ts.map +1 -1
  89. package/dist-types/components/input-tel/src/preprocessors.d.ts +0 -2
  90. package/dist-types/components/input-tel/src/preprocessors.d.ts.map +1 -1
  91. package/dist-types/components/input-tel/src/validators.d.ts +0 -1
  92. package/dist-types/components/input-tel/src/validators.d.ts.map +1 -1
  93. package/dist-types/components/input-tel/test/validators.test.d.ts +1 -1
  94. package/dist-types/components/input-tel/test/validators.test.d.ts.map +1 -1
  95. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts +20 -16
  96. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
  97. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.d.ts.map +1 -1
  98. package/dist-types/components/listbox/src/LionListbox.d.ts +1 -1
  99. package/dist-types/components/listbox/src/LionOption.d.ts +6 -4
  100. package/dist-types/components/listbox/src/LionOption.d.ts.map +1 -1
  101. package/dist-types/components/listbox/src/LionOptions.d.ts +1 -1
  102. package/dist-types/components/localize/src/LocalizeMixin.d.ts +1 -37
  103. package/dist-types/components/localize/src/LocalizeMixin.d.ts.map +1 -1
  104. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts +1 -1
  105. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts.map +1 -1
  106. package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
  107. package/dist-types/components/overlays/src/OverlaysManager.d.ts +4 -0
  108. package/dist-types/components/overlays/src/OverlaysManager.d.ts.map +1 -1
  109. package/dist-types/components/overlays/src/globalOverlaysStyle.d.ts.map +1 -1
  110. package/dist-types/components/overlays/test/OverlaysManager.test.d.ts.map +1 -1
  111. package/dist-types/components/overlays/test-suites/OverlayMixin.suite.d.ts.map +1 -1
  112. package/dist-types/components/pagination/src/LionPagination.d.ts +2 -38
  113. package/dist-types/components/pagination/src/LionPagination.d.ts.map +1 -1
  114. package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts +2 -38
  115. package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts.map +1 -1
  116. package/dist-types/components/radio-group/src/LionRadioGroup.d.ts +1 -1
  117. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +5 -3
  118. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  119. package/dist-types/components/steps/src/LionStep.d.ts +1 -1
  120. package/dist-types/components/steps/src/LionSteps.d.ts +1 -1
  121. package/dist-types/components/switch/src/LionSwitchButton.d.ts +6 -7
  122. package/dist-types/components/switch/src/LionSwitchButton.d.ts.map +1 -1
  123. package/dist-types/components/tabs/src/LionTabs.d.ts +1 -1
  124. package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
  125. package/dist-types/exports/types/core.d.ts +2 -0
  126. package/dist-types/exports/types/core.d.ts.map +1 -0
  127. package/dist-types/exports/types/form-core.d.ts +3 -0
  128. package/dist-types/exports/types/form-core.d.ts.map +1 -0
  129. package/dist-types/exports/types/input-tel-dropdown.d.ts +3 -0
  130. package/dist-types/exports/types/input-tel-dropdown.d.ts.map +1 -0
  131. package/dist-types/exports/types/input-tel.d.ts +2 -0
  132. package/dist-types/exports/types/input-tel.d.ts.map +1 -0
  133. package/dist-types/exports/types/localize.d.ts +4 -0
  134. package/dist-types/exports/types/localize.d.ts.map +1 -0
  135. package/dist-types/exports/types/overlays.d.ts +5 -0
  136. package/dist-types/exports/types/overlays.d.ts.map +1 -0
  137. package/dist-types/tsconfig.tsbuildinfo +1 -1
  138. package/docs/components/checkbox-group/overview.md +2 -2
  139. package/docs/components/collapsible/overview.md +1 -1
  140. package/docs/components/combobox/overview.md +1 -1
  141. package/docs/components/combobox/use-cases.md +1 -1
  142. package/docs/components/dialog/overview.md +1 -1
  143. package/docs/components/fieldset/overview.md +5 -5
  144. package/docs/components/form/overview.md +3 -3
  145. package/docs/components/icon/use-cases.md +1 -1
  146. package/docs/components/input/overview.md +1 -1
  147. package/docs/components/input-amount/overview.md +3 -3
  148. package/docs/components/input-date/overview.md +3 -3
  149. package/docs/components/input-datepicker/overview.md +5 -5
  150. package/docs/components/input-email/overview.md +2 -2
  151. package/docs/components/input-iban/overview.md +2 -2
  152. package/docs/components/input-range/overview.md +2 -2
  153. package/docs/components/input-stepper/overview.md +1 -1
  154. package/docs/components/input-tel/overview.md +1 -1
  155. package/docs/components/input-tel/use-cases.md +6 -6
  156. package/docs/components/input-tel-dropdown/overview.md +1 -1
  157. package/docs/components/radio-group/overview.md +3 -3
  158. package/docs/components/select-rich/use-cases.md +1 -1
  159. package/docs/components/tabs/examples.md +1 -1
  160. package/docs/fundamentals/index.md +4 -4
  161. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  162. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  163. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  164. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  165. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  166. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  167. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  168. package/docs/fundamentals/systems/core/overview.md +1 -1
  169. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  170. package/docs/fundamentals/systems/form/model-value.md +1 -1
  171. package/docs/fundamentals/systems/form/overview.md +28 -28
  172. package/docs/fundamentals/systems/form/validate.md +1 -1
  173. package/docs/fundamentals/systems/localize/overview.md +4 -4
  174. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  175. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  176. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  177. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  178. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  179. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  180. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  181. package/docs/guides/index.md +8 -8
  182. package/docs/guides/principles/index.md +5 -5
  183. package/docs/guides/principles/styling.md +1 -1
  184. package/exports/types/core.ts +1 -0
  185. package/exports/types/form-core.ts +2 -0
  186. package/exports/types/input-tel-dropdown.ts +2 -0
  187. package/exports/types/input-tel.ts +1 -0
  188. package/exports/types/localize.ts +3 -0
  189. package/exports/types/overlays.ts +4 -0
  190. package/package.json +8 -5
@@ -4,6 +4,10 @@ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
4
4
  import { SlotMixin } from '@lion/ui/core.js';
5
5
  import { LitElement } from 'lit';
6
6
 
7
+ /**
8
+ * @typedef {import('../types/SlotMixinTypes.js').SlotHost} SlotHost
9
+ */
10
+
7
11
  const mockedRenderTarget = document.createElement('div');
8
12
  function mockScopedRegistry() {
9
13
  const outputObj = { createElementCallCount: 0 };
@@ -26,7 +30,7 @@ function unMockScopedRegistry() {
26
30
  }
27
31
 
28
32
  describe('SlotMixin', () => {
29
- it('inserts provided element into lightdom and sets slot', async () => {
33
+ it('inserts provided element into light dom and sets slot', async () => {
30
34
  const tag = defineCE(
31
35
  class extends SlotMixin(LitElement) {
32
36
  get slots() {
@@ -41,7 +45,7 @@ describe('SlotMixin', () => {
41
45
  expect(el.children[0].slot).to.equal('feedback');
42
46
  });
43
47
 
44
- it("supports unnamed slot with ''", async () => {
48
+ it("supports default slot with ''", async () => {
45
49
  const tag = defineCE(
46
50
  class extends SlotMixin(LitElement) {
47
51
  get slots() {
@@ -57,7 +61,7 @@ describe('SlotMixin', () => {
57
61
  expect(el.children[0]).dom.to.equal('<div></div>');
58
62
  });
59
63
 
60
- it('supports unnamed slot in conjunction with named slots', async () => {
64
+ it('supports default slot in conjunction with named slots', async () => {
61
65
  const tag = defineCE(
62
66
  class extends SlotMixin(LitElement) {
63
67
  get slots() {
@@ -111,63 +115,6 @@ describe('SlotMixin', () => {
111
115
  expect(slot.innerText).to.equal('');
112
116
  });
113
117
 
114
- it('supports complex dom trees as element', async () => {
115
- const tag = defineCE(
116
- class extends SlotMixin(LitElement) {
117
- constructor() {
118
- super();
119
- this.foo = 'bar';
120
- }
121
-
122
- get slots() {
123
- return {
124
- ...super.slots,
125
- feedback: () => {
126
- const el = document.createElement('div');
127
- el.setAttribute('foo', this.foo);
128
- const subEl = document.createElement('p');
129
- subEl.innerText = 'cat';
130
- el.appendChild(subEl);
131
- return el;
132
- },
133
- };
134
- }
135
- },
136
- );
137
- const el = await fixture(`<${tag}></${tag}>`);
138
- expect(el.children[0].slot).to.equal('feedback');
139
- expect(el.children[0].getAttribute('foo')).to.equal('bar');
140
- expect(/** @type HTMLParagraphElement */ (el.children[0].children[0]).innerText).to.equal(
141
- 'cat',
142
- );
143
- });
144
-
145
- it('supports conditional slots', async () => {
146
- let renderSlot = true;
147
- const tag = defineCE(
148
- class extends SlotMixin(LitElement) {
149
- get slots() {
150
- return {
151
- ...super.slots,
152
- conditional: () => {
153
- if (renderSlot) {
154
- const el = document.createElement('div');
155
- el.id = 'someSlot';
156
- return el;
157
- }
158
- return undefined;
159
- },
160
- };
161
- }
162
- },
163
- );
164
- const elSlot = await fixture(`<${tag}><${tag}>`);
165
- expect(elSlot.querySelector('#someSlot')).to.exist;
166
- renderSlot = false;
167
- const elNoSlot = await fixture(`<${tag}><${tag}>`);
168
- expect(elNoSlot.querySelector('#someSlot')).to.not.exist;
169
- });
170
-
171
118
  it("allows to check which slots have been created via this._isPrivateSlot('slotname')", async () => {
172
119
  let renderSlot = true;
173
120
  class SlotPrivateText extends SlotMixin(LitElement) {
@@ -195,102 +142,358 @@ describe('SlotMixin', () => {
195
142
  expect(elNoSlot.didCreateConditionalSlot()).to.be.false;
196
143
  });
197
144
 
198
- it('supports templates', async () => {
199
- const tag = defineCE(
200
- class extends SlotMixin(LitElement) {
201
- get slots() {
202
- return {
203
- ...super.slots,
204
- template: () => html`<span>text</span>`,
205
- };
206
- }
145
+ describe('Rerender', () => {
146
+ it('supports rerender when SlotRerenderObject provided', async () => {
147
+ const tag = defineCE(
148
+ // @ts-expect-error
149
+ class extends SlotMixin(LitElement) {
150
+ static properties = { currentValue: Number };
207
151
 
208
- render() {
209
- return html`<slot name="template"></slot>`;
210
- }
211
- },
212
- );
213
- const el = await fixture(`<${tag}></${tag}>`);
214
- const slot = /** @type HTMLSpanElement */ (
215
- Array.from(el.children).find(elm => elm.slot === 'template')
216
- );
217
- expect(slot.slot).to.equal('template');
218
- expect(slot.tagName).to.equal('SPAN');
219
- });
152
+ constructor() {
153
+ super();
154
+ this.currentValue = 0;
155
+ }
220
156
 
221
- it('supports scoped elements when polyfill loaded', async () => {
222
- const outputObj = mockScopedRegistry();
157
+ get slots() {
158
+ return {
159
+ ...super.slots,
160
+ template: () => ({ template: html`<span>${this.currentValue}</span> ` }),
161
+ };
162
+ }
223
163
 
224
- class ScopedEl extends LitElement {}
164
+ render() {
165
+ return html`<slot name="template"></slot>`;
166
+ }
225
167
 
226
- const tagName = defineCE(
227
- class extends ScopedElementsMixin(SlotMixin(LitElement)) {
228
- static get scopedElements() {
229
- return {
168
+ get _templateNode() {
169
+ return /** @type HTMLSpanElement */ (
170
+ Array.from(this.children).find(elm => elm.slot === 'template')
171
+ );
172
+ }
173
+ },
174
+ );
175
+ const el = /** @type {* & SlotHost} */ (await fixture(`<${tag}></${tag}>`));
176
+ await el.updateComplete;
177
+
178
+ expect(el._templateNode.slot).to.equal('template');
179
+ expect(el._templateNode.textContent?.trim()).to.equal('0');
180
+
181
+ el.currentValue = 1;
182
+ await el.updateComplete;
183
+
184
+ expect(el._templateNode.textContent?.trim()).to.equal('1');
185
+ });
186
+
187
+ it('keeps focus after rerender', async () => {
188
+ const tag = defineCE(
189
+ // @ts-expect-error
190
+ class extends SlotMixin(LitElement) {
191
+ static properties = { currentValue: Number };
192
+
193
+ constructor() {
194
+ super();
195
+ this.currentValue = 0;
196
+ }
197
+
198
+ get slots() {
199
+ return {
200
+ ...super.slots,
201
+ 'focusable-node': () => ({
202
+ template: html`<input /> `,
203
+ }),
204
+ };
205
+ }
206
+
207
+ render() {
208
+ return html`<slot name="focusable-node"></slot>`;
209
+ }
210
+
211
+ get _focusableNode() {
212
+ return /** @type HTMLSpanElement */ (
213
+ Array.from(this.children).find(elm => elm.slot === 'focusable-node')
214
+ ?.firstElementChild
215
+ );
216
+ }
217
+ },
218
+ );
219
+ const el = /** @type {* & SlotHost} */ (await fixture(`<${tag}></${tag}>`));
220
+
221
+ el._focusableNode.focus();
222
+ expect(document.activeElement).to.equal(el._focusableNode);
223
+
224
+ el.currentValue = 1;
225
+ await el.updateComplete;
226
+
227
+ expect(document.activeElement).to.equal(el._focusableNode);
228
+ });
229
+
230
+ it('keeps focus after rerendering complex shadow root into slot', async () => {
231
+ const complexSlotTagName = defineCE(
232
+ class extends LitElement {
233
+ render() {
234
+ return html`
235
+ <input />
236
+ <button>I will be focused</button>
237
+ `;
238
+ }
239
+
240
+ get _buttonNode() {
230
241
  // @ts-expect-error
231
- ...super.scopedElements,
232
- 'scoped-el': ScopedEl,
233
- };
234
- }
242
+ return this.shadowRoot.querySelector('button');
243
+ }
244
+ },
245
+ );
235
246
 
236
- get slots() {
237
- return {
238
- ...super.slots,
239
- template: () => html`<scoped-el></scoped-el>`,
240
- };
241
- }
247
+ const complexSlotTag = unsafeStatic(complexSlotTagName);
242
248
 
243
- render() {
244
- return html`<slot name="template"></slot>`;
245
- }
246
- },
247
- );
249
+ const tagName = defineCE(
250
+ // @ts-expect-error
251
+ class extends SlotMixin(LitElement) {
252
+ static properties = { currentValue: Number };
248
253
 
249
- const tag = unsafeStatic(tagName);
250
- await fixture(html`<${tag}></${tag}>`);
254
+ constructor() {
255
+ super();
256
+ this.currentValue = 0;
257
+ }
251
258
 
252
- expect(outputObj.createElementCallCount).to.equal(1);
259
+ get slots() {
260
+ return {
261
+ ...super.slots,
262
+ 'focusable-node': () => ({
263
+ template: html`<${complexSlotTag}> </${complexSlotTag}> `,
264
+ }),
265
+ };
266
+ }
253
267
 
254
- unMockScopedRegistry();
268
+ render() {
269
+ return html`<slot name="focusable-node"></slot>`;
270
+ }
271
+
272
+ get _focusableNode() {
273
+ return /** @type HTMLSpanElement */ (
274
+ Array.from(this.children).find(elm => elm.slot === 'focusable-node')
275
+ ?.firstElementChild
276
+ );
277
+ }
278
+ },
279
+ );
280
+ const el = /** @type {* & SlotHost} */ (await fixture(`<${tagName}></${tagName}>`));
281
+
282
+ el._focusableNode._buttonNode.focus();
283
+
284
+ expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
285
+
286
+ el.currentValue = 1;
287
+ await el.updateComplete;
288
+
289
+ expect(document.activeElement).to.equal(el._focusableNode);
290
+ expect(el._focusableNode.shadowRoot.activeElement).to.equal(el._focusableNode._buttonNode);
291
+ });
255
292
  });
256
293
 
257
- it('does not scope elements when polyfill not loaded', async () => {
258
- class ScopedEl extends LitElement {}
294
+ describe('SlotFunctionResult types', () => {
295
+ it('supports complex dom trees as element (type "Element")', async () => {
296
+ const tag = defineCE(
297
+ class extends SlotMixin(LitElement) {
298
+ constructor() {
299
+ super();
300
+ this.foo = 'bar';
301
+ }
259
302
 
260
- const tagName = defineCE(
261
- class extends ScopedElementsMixin(SlotMixin(LitElement)) {
262
- static get scopedElements() {
263
- return {
264
- // @ts-expect-error
265
- ...super.scopedElements,
266
- 'scoped-el': ScopedEl,
267
- };
268
- }
303
+ get slots() {
304
+ return {
305
+ ...super.slots,
306
+ feedback: () => {
307
+ const el = document.createElement('div');
308
+ el.setAttribute('foo', this.foo);
309
+ const subEl = document.createElement('p');
310
+ subEl.innerText = 'cat';
311
+ el.appendChild(subEl);
312
+ return el;
313
+ },
314
+ };
315
+ }
316
+ },
317
+ );
318
+ const el = await fixture(`<${tag}></${tag}>`);
319
+ expect(el.children[0].slot).to.equal('feedback');
320
+ expect(el.children[0].getAttribute('foo')).to.equal('bar');
321
+ expect(/** @type HTMLParagraphElement */ (el.children[0].children[0]).innerText).to.equal(
322
+ 'cat',
323
+ );
324
+ });
269
325
 
270
- get slots() {
271
- return {
272
- ...super.slots,
273
- template: () => html`<scoped-el></scoped-el>`,
274
- };
275
- }
326
+ it('supports conditional slots (type "undefined")', async () => {
327
+ let renderSlot = true;
328
+ const tag = defineCE(
329
+ class extends SlotMixin(LitElement) {
330
+ get slots() {
331
+ return {
332
+ ...super.slots,
333
+ conditional: () => {
334
+ if (renderSlot) {
335
+ const el = document.createElement('div');
336
+ el.id = 'someSlot';
337
+ return el;
338
+ }
339
+ return undefined;
340
+ },
341
+ };
342
+ }
343
+ },
344
+ );
345
+ const elSlot = await fixture(`<${tag}><${tag}>`);
346
+ expect(elSlot.querySelector('#someSlot')).to.exist;
347
+ renderSlot = false;
348
+ const elNoSlot = await fixture(`<${tag}><${tag}>`);
349
+ expect(elNoSlot.querySelector('#someSlot')).to.not.exist;
350
+ });
276
351
 
277
- render() {
278
- return html`<slot name="template"></slot>`;
279
- }
280
- },
281
- );
352
+ it('supports templates (type "TemplateResult")', async () => {
353
+ const tag = defineCE(
354
+ class extends SlotMixin(LitElement) {
355
+ get slots() {
356
+ return {
357
+ ...super.slots,
358
+ template: () => html`<span>text</span>`,
359
+ };
360
+ }
361
+
362
+ render() {
363
+ return html`<slot name="template"></slot>`;
364
+ }
365
+ },
366
+ );
367
+ const el = await fixture(`<${tag}></${tag}>`);
368
+ const slot = /** @type HTMLSpanElement */ (
369
+ Array.from(el.children).find(elm => elm.slot === 'template')
370
+ );
371
+ expect(slot.slot).to.equal('template');
372
+ expect(slot.tagName).to.equal('SPAN');
373
+ });
374
+
375
+ it('supports afterRender logic (type "{ template:TemplateResults; afterRender: Function}" )', async () => {
376
+ let varThatProvesAfterRenderIsCalled = 'not called';
377
+
378
+ const tag = defineCE(
379
+ // @ts-expect-error
380
+ class extends SlotMixin(LitElement) {
381
+ static properties = { currentValue: Number };
382
+
383
+ constructor() {
384
+ super();
385
+ this.currentValue = 0;
386
+ }
387
+
388
+ get slots() {
389
+ return {
390
+ ...super.slots,
391
+ template: () => ({
392
+ template: html`<span>${this.currentValue}</span>, `,
393
+ afterRender: () => {
394
+ varThatProvesAfterRenderIsCalled = 'called';
395
+ },
396
+ }),
397
+ };
398
+ }
399
+
400
+ render() {
401
+ return html`<slot name="template"></slot>`;
402
+ }
403
+
404
+ get _templateNode() {
405
+ return /** @type HTMLSpanElement */ (
406
+ Array.from(this.children).find(elm => elm.slot === 'template')?.firstElementChild
407
+ );
408
+ }
409
+ },
410
+ );
411
+ const el = /** @type {* & SlotHost} */ (await fixture(`<${tag}></${tag}>`));
412
+ expect(el._templateNode.textContent).to.equal('0');
413
+
414
+ el.currentValue = 1;
415
+ await el.updateComplete;
416
+
417
+ expect(varThatProvesAfterRenderIsCalled).to.equal('called');
418
+ expect(el._templateNode.textContent).to.equal('1');
419
+ });
420
+ });
421
+
422
+ describe('Scoped Registries', () => {
423
+ it('supports scoped elements when polyfill loaded', async () => {
424
+ const outputObj = mockScopedRegistry();
425
+
426
+ class ScopedEl extends LitElement {}
427
+
428
+ const tagName = defineCE(
429
+ class extends ScopedElementsMixin(SlotMixin(LitElement)) {
430
+ static get scopedElements() {
431
+ return {
432
+ // @ts-expect-error
433
+ ...super.scopedElements,
434
+ 'scoped-elm': ScopedEl,
435
+ };
436
+ }
437
+
438
+ get slots() {
439
+ return {
440
+ ...super.slots,
441
+ template: () => html`<scoped-elm></scoped-elm>`,
442
+ };
443
+ }
444
+
445
+ render() {
446
+ return html`<slot name="template"></slot>`;
447
+ }
448
+ },
449
+ );
450
+
451
+ const tag = unsafeStatic(tagName);
452
+ await fixture(html`<${tag}></${tag}>`);
453
+
454
+ expect(outputObj.createElementCallCount).to.equal(1);
455
+
456
+ unMockScopedRegistry();
457
+ });
458
+
459
+ it('does not scope elements when polyfill not loaded', async () => {
460
+ class ScopedEl extends LitElement {}
461
+
462
+ const tagName = defineCE(
463
+ class extends ScopedElementsMixin(SlotMixin(LitElement)) {
464
+ static get scopedElements() {
465
+ return {
466
+ // @ts-expect-error
467
+ ...super.scopedElements,
468
+ 'scoped-el': ScopedEl,
469
+ };
470
+ }
471
+
472
+ get slots() {
473
+ return {
474
+ ...super.slots,
475
+ template: () => html`<scoped-el></scoped-el>`,
476
+ };
477
+ }
478
+
479
+ render() {
480
+ return html`<slot name="template"></slot>`;
481
+ }
482
+ },
483
+ );
282
484
 
283
- const renderTarget = document.createElement('div');
284
- const el = document.createElement(tagName);
485
+ const renderTarget = document.createElement('div');
486
+ const el = document.createElement(tagName);
285
487
 
286
- // We don't use fixture, so we limit the amount of calls to document.createElement
287
- const docSpy = sinon.spy(document, 'createElement');
288
- document.body.appendChild(renderTarget);
289
- renderTarget.appendChild(el);
488
+ // We don't use fixture, so we limit the amount of calls to document.createElement
489
+ const docSpy = sinon.spy(document, 'createElement');
490
+ document.body.appendChild(renderTarget);
491
+ renderTarget.appendChild(el);
290
492
 
291
- expect(docSpy.callCount).to.equal(2);
493
+ expect(docSpy.callCount).to.equal(2);
292
494
 
293
- document.body.removeChild(renderTarget);
294
- docSpy.restore();
495
+ document.body.removeChild(renderTarget);
496
+ docSpy.restore();
497
+ });
295
498
  });
296
499
  });
@@ -1,7 +1,18 @@
1
1
  import { Constructor } from '@open-wc/dedupe-mixin';
2
2
  import { TemplateResult, LitElement } from 'lit';
3
3
 
4
- declare function slotFunction(): HTMLElement | Node[] | TemplateResult | undefined;
4
+ /**
5
+ * Implicitly creates a wrapper node that allows for rerenders
6
+ */
7
+ export type SlotRerenderObject = {
8
+ template: TemplateResult;
9
+ /* Add logic that will be performed after the render */
10
+ afterRender?: Function;
11
+ };
12
+
13
+ export type SlotFunctionResult = TemplateResult | Element | SlotRerenderObject | undefined;
14
+
15
+ declare function slotFunction(): SlotFunctionResult;
5
16
 
6
17
  export type SlotsMap = {
7
18
  [key: string]: typeof slotFunction;
@@ -49,7 +60,7 @@ export declare class SlotHost {
49
60
  * };
50
61
  * }
51
62
  */
52
- export declare function SlotMixinImplementation<T extends Constructor<LitElement>>(
63
+ declare function SlotMixinImplementation<T extends Constructor<LitElement>>(
53
64
  superclass: T,
54
65
  ): T &
55
66
  Constructor<SlotHost> &
@@ -70,19 +70,21 @@ const FormatMixinImplementation = superclass =>
70
70
  }
71
71
 
72
72
  /**
73
- * @param {string} name
74
- * @param {any} oldVal
73
+ * @param {string} [name]
74
+ * @param {unknown} [oldValue]
75
+ * @param {import('lit').PropertyDeclaration} [options]
76
+ * @returns {void}
75
77
  */
76
- requestUpdate(name, oldVal) {
77
- super.requestUpdate(name, oldVal);
78
+ requestUpdate(name, oldValue, options) {
79
+ super.requestUpdate(name, oldValue, options);
78
80
 
79
- if (name === 'modelValue' && this.modelValue !== oldVal) {
80
- this._onModelValueChanged({ modelValue: this.modelValue }, { modelValue: oldVal });
81
+ if (name === 'modelValue' && this.modelValue !== oldValue) {
82
+ this._onModelValueChanged({ modelValue: this.modelValue }, { modelValue: oldValue });
81
83
  }
82
- if (name === 'serializedValue' && this.serializedValue !== oldVal) {
84
+ if (name === 'serializedValue' && this.serializedValue !== oldValue) {
83
85
  this._calculateValues({ source: 'serialized' });
84
86
  }
85
- if (name === 'formattedValue' && this.formattedValue !== oldVal) {
87
+ if (name === 'formattedValue' && this.formattedValue !== oldValue) {
86
88
  this._calculateValues({ source: 'formatted' });
87
89
  }
88
90
  }
@@ -33,12 +33,14 @@ const InteractionStateMixinImplementation = superclass =>
33
33
  }
34
34
 
35
35
  /**
36
- * @param {PropertyKey} name
37
- * @param {*} oldVal
36
+ * @param {string} [name]
37
+ * @param {unknown} [oldValue]
38
+ * @param {import('lit').PropertyDeclaration} [options]
39
+ * @returns {void}
38
40
  */
39
- requestUpdate(name, oldVal) {
40
- super.requestUpdate(name, oldVal);
41
- if (name === 'touched' && this.touched !== oldVal) {
41
+ requestUpdate(name, oldValue, options) {
42
+ super.requestUpdate(name, oldValue, options);
43
+ if (name === 'touched' && this.touched !== oldValue) {
42
44
  this._onTouchedChanged();
43
45
  }
44
46
 
@@ -48,7 +50,7 @@ const InteractionStateMixinImplementation = superclass =>
48
50
  this.filled = !this._isEmpty();
49
51
  }
50
52
 
51
- if (name === 'dirty' && this.dirty !== oldVal) {
53
+ if (name === 'dirty' && this.dirty !== oldValue) {
52
54
  this._onDirtyChanged();
53
55
  }
54
56
  }
@@ -52,11 +52,13 @@ const ChoiceInputMixinImplementation = superclass =>
52
52
  }
53
53
 
54
54
  /**
55
- * @param {string} name
56
- * @param {any} oldValue
55
+ * @param {string} [name]
56
+ * @param {unknown} [oldValue]
57
+ * @param {import('lit').PropertyDeclaration} [options]
58
+ * @returns {void}
57
59
  */
58
- requestUpdate(name, oldValue) {
59
- super.requestUpdate(name, oldValue);
60
+ requestUpdate(name, oldValue, options) {
61
+ super.requestUpdate(name, oldValue, options);
60
62
 
61
63
  if (name === 'modelValue') {
62
64
  if (this.modelValue.checked !== this.checked) {
@@ -93,11 +93,16 @@ const SyncUpdatableMixinImplementation = superclass =>
93
93
  }
94
94
 
95
95
  /**
96
- * @param {string} name
97
- * @param {*} oldValue
96
+ * @param {string} [name]
97
+ * @param {unknown} [oldValue]
98
+ * @param {import('lit').PropertyDeclaration} [options]
99
+ * @returns {void}
98
100
  */
99
- requestUpdate(name, oldValue) {
100
- super.requestUpdate(name, oldValue);
101
+ requestUpdate(name, oldValue, options) {
102
+ super.requestUpdate(name, oldValue, options);
103
+ if (name === undefined) {
104
+ return;
105
+ }
101
106
 
102
107
  this.__SyncUpdatableNamespace = this.__SyncUpdatableNamespace || {};
103
108
  const ns = this.__SyncUpdatableNamespace;
@@ -147,11 +147,13 @@ describe('SyncUpdatableMixin', () => {
147
147
  }
148
148
 
149
149
  /**
150
- * @param {string} name
151
- * @param {*} oldValue
150
+ * @param {string} [name]
151
+ * @param {unknown} [oldValue]
152
+ * @param {import('lit').PropertyDeclaration} [options]
153
+ * @returns {void}
152
154
  */
153
- requestUpdate(name, oldValue) {
154
- super.requestUpdate(name, oldValue);
155
+ requestUpdate(name, oldValue, options) {
156
+ super.requestUpdate(name, oldValue, options);
155
157
  if (name === 'prop') {
156
158
  propChangedCount += 1;
157
159
  }