@lion/ui 0.15.3 → 0.15.4

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 (138) hide show
  1. package/README.md +2 -2
  2. package/components/combobox/src/LionCombobox.js +4 -1
  3. package/components/combobox/test/lion-combobox-integrations.test.js +23 -0
  4. package/components/combobox/test/lion-combobox.test.js +20 -0
  5. package/components/core/src/ScopedStylesController.js +7 -10
  6. package/components/core/test-helpers/getCachedFixture.js +55 -0
  7. package/components/input-amount/src/parsers.js +3 -2
  8. package/components/input-amount/test/parsers.test.js +6 -0
  9. package/components/input-amount-dropdown/src/currencyUtil.js +1 -1
  10. package/components/input-file/src/localizeNamespaceLoader.js +4 -0
  11. package/components/input-file/translations/bg.js +5 -0
  12. package/components/input-file/translations/cs.js +5 -0
  13. package/components/input-file/translations/es.js +5 -0
  14. package/components/input-file/translations/fr.js +5 -0
  15. package/components/input-file/translations/hu.js +5 -0
  16. package/components/input-file/translations/id-ID.js +5 -0
  17. package/components/input-file/translations/id.js +19 -0
  18. package/components/input-file/translations/it.js +5 -0
  19. package/components/input-file/translations/pl.js +5 -0
  20. package/components/input-file/translations/ro.js +5 -0
  21. package/components/input-file/translations/sk.js +5 -0
  22. package/components/input-file/translations/uk.js +5 -0
  23. package/components/input-file/translations/zh.js +5 -0
  24. package/components/input-stepper/test/lion-input-stepper-integrations.test.js +2 -19
  25. package/components/input-stepper/test/lion-input-stepper.test.js +2 -688
  26. package/components/input-stepper/test-suites/lion-input-stepper.integration.suite.js +19 -0
  27. package/components/input-stepper/test-suites/lion-input-stepper.suite.js +673 -0
  28. package/components/listbox/src/ListboxMixin.js +0 -1
  29. package/components/listbox/test-suites/ListboxMixin.suite.js +71 -2
  30. package/components/overlays/src/OverlayController.js +3 -0
  31. package/components/overlays/src/OverlaysManager.js +9 -2
  32. package/components/overlays/test/OverlayController.test.js +22 -9
  33. package/components/overlays/test/OverlaysManager.test.js +16 -0
  34. package/custom-elements.json +133 -9
  35. package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
  36. package/dist-types/components/core/src/ScopedStylesController.d.ts +1 -2
  37. package/dist-types/components/core/src/ScopedStylesController.d.ts.map +1 -1
  38. package/dist-types/components/core/test-helpers/getCachedFixture.d.ts +14 -0
  39. package/dist-types/components/core/test-helpers/getCachedFixture.d.ts.map +1 -0
  40. package/dist-types/components/form-integrations/test/helpers/helpers.d.ts +3 -3
  41. package/dist-types/components/form-integrations/test/helpers/helpers.d.ts.map +1 -1
  42. package/dist-types/components/input-amount/src/parsers.d.ts.map +1 -1
  43. package/dist-types/components/input-file/src/localizeNamespaceLoader.d.ts +1 -1
  44. package/dist-types/components/input-file/src/localizeNamespaceLoader.d.ts.map +1 -1
  45. package/dist-types/components/input-file/translations/bg-BG.d.ts +5 -0
  46. package/dist-types/components/input-file/translations/bg.d.ts +5 -0
  47. package/dist-types/components/input-file/translations/cs-CZ.d.ts +5 -0
  48. package/dist-types/components/input-file/translations/cs.d.ts +5 -0
  49. package/dist-types/components/input-file/translations/es-ES.d.ts +5 -0
  50. package/dist-types/components/input-file/translations/es.d.ts +5 -0
  51. package/dist-types/components/input-file/translations/fr-BE.d.ts +5 -0
  52. package/dist-types/components/input-file/translations/fr-FR.d.ts +5 -0
  53. package/dist-types/components/input-file/translations/fr.d.ts +5 -0
  54. package/dist-types/components/input-file/translations/hu-HU.d.ts +5 -0
  55. package/dist-types/components/input-file/translations/hu.d.ts +5 -0
  56. package/dist-types/components/input-file/translations/id-ID.d.ts +20 -0
  57. package/dist-types/components/input-file/translations/id-ID.d.ts.map +1 -0
  58. package/dist-types/components/input-file/translations/id.d.ts +20 -0
  59. package/dist-types/components/input-file/translations/id.d.ts.map +1 -0
  60. package/dist-types/components/input-file/translations/it-IT.d.ts +5 -0
  61. package/dist-types/components/input-file/translations/it.d.ts +5 -0
  62. package/dist-types/components/input-file/translations/pl-PL.d.ts +5 -0
  63. package/dist-types/components/input-file/translations/pl.d.ts +5 -0
  64. package/dist-types/components/input-file/translations/ro-RO.d.ts +5 -0
  65. package/dist-types/components/input-file/translations/ro.d.ts +5 -0
  66. package/dist-types/components/input-file/translations/sk-SK.d.ts +5 -0
  67. package/dist-types/components/input-file/translations/sk.d.ts +5 -0
  68. package/dist-types/components/input-file/translations/uk-UA.d.ts +5 -0
  69. package/dist-types/components/input-file/translations/uk.d.ts +5 -0
  70. package/dist-types/components/input-file/translations/zh-CN.d.ts +5 -0
  71. package/dist-types/components/input-file/translations/zh.d.ts +5 -0
  72. package/dist-types/components/input-stepper/test/lion-input-stepper.test.d.ts +1 -2
  73. package/dist-types/components/input-stepper/test/lion-input-stepper.test.d.ts.map +1 -1
  74. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.integration.suite.d.ts +3 -0
  75. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.integration.suite.d.ts.map +1 -0
  76. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.suite.d.ts +4 -0
  77. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.suite.d.ts.map +1 -0
  78. package/dist-types/components/listbox/src/ListboxMixin.d.ts.map +1 -1
  79. package/dist-types/components/listbox/test-suites/ListboxMixin.suite.d.ts.map +1 -1
  80. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  81. package/dist-types/components/overlays/src/OverlaysManager.d.ts +4 -1
  82. package/dist-types/components/overlays/src/OverlaysManager.d.ts.map +1 -1
  83. package/dist-types/exports/input-stepper-suites.d.ts +6 -0
  84. package/dist-types/exports/input-stepper-suites.d.ts.map +1 -0
  85. package/dist-types/tsconfig.tsbuildinfo +1 -1
  86. package/docs/components/checkbox-group/overview.md +2 -2
  87. package/docs/components/collapsible/overview.md +1 -1
  88. package/docs/components/combobox/overview.md +1 -1
  89. package/docs/components/combobox/use-cases.md +1 -1
  90. package/docs/components/dialog/overview.md +1 -1
  91. package/docs/components/fieldset/overview.md +5 -5
  92. package/docs/components/form/overview.md +3 -3
  93. package/docs/components/form/use-cases.md +2 -2
  94. package/docs/components/icon/use-cases.md +1 -1
  95. package/docs/components/input/overview.md +1 -1
  96. package/docs/components/input-amount/overview.md +3 -3
  97. package/docs/components/input-date/overview.md +3 -3
  98. package/docs/components/input-datepicker/overview.md +5 -5
  99. package/docs/components/input-email/overview.md +2 -2
  100. package/docs/components/input-file/overview.md +1 -1
  101. package/docs/components/input-iban/overview.md +2 -2
  102. package/docs/components/input-range/overview.md +2 -2
  103. package/docs/components/input-stepper/overview.md +1 -1
  104. package/docs/components/input-tel/overview.md +1 -1
  105. package/docs/components/input-tel/use-cases.md +6 -6
  106. package/docs/components/input-tel-dropdown/overview.md +1 -1
  107. package/docs/components/listbox/use-cases.md +1 -1
  108. package/docs/components/radio-group/overview.md +3 -3
  109. package/docs/components/select/use-cases.md +1 -1
  110. package/docs/components/select-rich/use-cases.md +2 -2
  111. package/docs/components/tabs/examples.md +1 -1
  112. package/docs/fundamentals/index.md +4 -4
  113. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  114. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  115. package/docs/fundamentals/rationales/accessibility.md +1 -1
  116. package/docs/fundamentals/systems/core/SlotMixin.md +2 -2
  117. package/docs/fundamentals/systems/core/api-table.md +3 -4
  118. package/docs/fundamentals/systems/core/overview.md +1 -1
  119. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  120. package/docs/fundamentals/systems/form/model-value.md +1 -1
  121. package/docs/fundamentals/systems/form/overview.md +28 -28
  122. package/docs/fundamentals/systems/form/validate.md +38 -30
  123. package/docs/fundamentals/systems/localize/overview.md +4 -4
  124. package/docs/fundamentals/systems/overlays/api-table.md +1 -1
  125. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  126. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  127. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  128. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  129. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  130. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  131. package/docs/guides/how-to/create-a-form.md +4 -4
  132. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  133. package/docs/guides/index.md +8 -8
  134. package/docs/guides/principles/index.md +5 -5
  135. package/docs/guides/principles/styling.md +1 -1
  136. package/exports/input-stepper-suites.js +11 -0
  137. package/input-stepper-suites.js +11 -0
  138. package/package.json +1 -1
@@ -1,689 +1,3 @@
1
- import { expect, fixture as _fixture, nextFrame } from '@open-wc/testing';
2
- import { nothing } from 'lit';
3
- import { html } from 'lit/static-html.js';
4
- import sinon from 'sinon';
5
- import { formatNumber } from '@lion/ui/localize-no-side-effects.js';
6
- import '@lion/ui/define/lion-input-stepper.js';
1
+ import { runInputStepperSuite } from '../test-suites/lion-input-stepper.suite.js';
7
2
 
8
- /**
9
- * @typedef {import('../src/LionInputStepper.js').LionInputStepper} LionInputStepper
10
- * @typedef {import('lit').TemplateResult} TemplateResult
11
- */
12
- const fixture = /** @type {(arg: TemplateResult|string) => Promise<LionInputStepper>} */ (_fixture);
13
-
14
- const defaultInputStepper = html`
15
- <lion-input-stepper name="year" label="Years"></lion-input-stepper>
16
- `;
17
- const inputStepperWithAttrs = html`<lion-input-stepper
18
- step="10"
19
- min="100"
20
- max="200"
21
- label="Label"
22
- ></lion-input-stepper>`;
23
-
24
- describe('<lion-input-stepper>', () => {
25
- describe('Stepper', () => {
26
- it('has a type text', async () => {
27
- const el = await fixture(defaultInputStepper);
28
- expect(el._inputNode.type).to.equal('text');
29
- });
30
-
31
- it('has a default min and max of Infinity', async () => {
32
- const el = await fixture(defaultInputStepper);
33
- expect(el.getAttribute('min')).to.equal('Infinity');
34
- expect(el.getAttribute('max')).to.equal('Infinity');
35
- });
36
-
37
- it('has a default step of 1', async () => {
38
- const el = await fixture(defaultInputStepper);
39
- expect(el.getAttribute('step')).to.equal('1');
40
- });
41
-
42
- it('can set a step with which the value increases', async () => {
43
- const el = await fixture(defaultInputStepper);
44
- el.step = 10;
45
- await el.updateComplete;
46
- expect(el.value).to.equal('');
47
- expect(el.getAttribute('step')).to.equal('10');
48
-
49
- const incrementButton = el.querySelector('[slot=suffix]');
50
- incrementButton?.dispatchEvent(new Event('click'));
51
- expect(el.value).to.equal('10');
52
- });
53
- });
54
-
55
- describe('Formatter', () => {
56
- it('uses formatNumber for formatting', async () => {
57
- const el = await fixture(defaultInputStepper);
58
- expect(el.formatter).to.equal(formatNumber);
59
- });
60
-
61
- it('formatNumber uses locale provided in formatOptions', async () => {
62
- let el = await fixture(html`
63
- <lion-input-stepper
64
- .formatOptions="${{ locale: 'en-GB' }}"
65
- .modelValue="${1234.56}"
66
- ></lion-input-stepper>
67
- `);
68
- expect(el.formattedValue).to.equal('1,234.56');
69
- el = await fixture(html`
70
- <lion-input-stepper
71
- .formatOptions="${{ locale: 'nl-NL' }}"
72
- .modelValue="${1234.56}"
73
- ></lion-input-stepper>
74
- `);
75
- expect(el.formattedValue).to.equal('1.234,56');
76
- });
77
-
78
- it('supports overriding decimalSeparator in formatOptions', async () => {
79
- const el = await fixture(
80
- html`<lion-input-stepper
81
- .formatOptions="${{ locale: 'nl-NL', decimalSeparator: '.' }}"
82
- .modelValue="${12.34}"
83
- ></lion-input-stepper>`,
84
- );
85
- expect(el.formattedValue).to.equal('12.34');
86
- });
87
-
88
- it('supports overriding groupSeparator in formatOptions', async () => {
89
- const el = await fixture(
90
- html`<lion-input-stepper
91
- .formatOptions="${{ locale: 'nl-NL', groupSeparator: ',', decimalSeparator: '.' }}"
92
- .modelValue="${1234.56}"
93
- ></lion-input-stepper>`,
94
- );
95
- expect(el.formattedValue).to.equal('1,234.56');
96
- });
97
- });
98
-
99
- describe('User interaction', () => {
100
- it('should increment the value to 1 on [ArrowUp]', async () => {
101
- const el = await fixture(defaultInputStepper);
102
- expect(el.value).to.equal('');
103
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
104
- await el.updateComplete;
105
- expect(el.value).to.equal('1');
106
- });
107
-
108
- it('should increment the value to minValue on [ArrowUp] if value is below min', async () => {
109
- const el = await fixture(inputStepperWithAttrs);
110
- expect(el.value).to.equal('');
111
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
112
- await el.updateComplete;
113
- expect(el.value).to.equal('100');
114
- });
115
-
116
- it('should decrement the value to -1 on [ArrowDown]', async () => {
117
- const el = await fixture(defaultInputStepper);
118
- expect(el.value).to.equal('');
119
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
120
- await el.updateComplete;
121
- expect(el.value).to.equal('−1');
122
- });
123
-
124
- it('should increment the value to minValue on [ArrowDown] if value is below min', async () => {
125
- const el = await fixture(inputStepperWithAttrs);
126
- el.modelValue = 600;
127
- el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
128
- await el.updateComplete;
129
- expect(el.value).to.equal('200');
130
- });
131
-
132
- it('should increment the value to 1 on + button click', async () => {
133
- const el = await fixture(defaultInputStepper);
134
- expect(el.value).to.equal('');
135
- const incrementButton = el.querySelector('[slot=suffix]');
136
- incrementButton?.dispatchEvent(new Event('click'));
137
- expect(el.value).to.equal('1');
138
- });
139
-
140
- it('should decrement the value to -1 on - button click', async () => {
141
- const el = await fixture(defaultInputStepper);
142
- expect(el.value).to.equal('');
143
- const decrementButton = el.querySelector('[slot=prefix]');
144
- decrementButton?.dispatchEvent(new Event('click'));
145
- expect(el.value).to.equal('−1');
146
- });
147
-
148
- it('fires one "user-input-changed" event on + button click', async () => {
149
- let counter = 0;
150
- const el = await fixture(html`
151
- <lion-input-stepper
152
- name="year"
153
- label="Years"
154
- @user-input-changed="${() => {
155
- counter += 1;
156
- }}"
157
- >
158
- </lion-input-stepper>
159
- `);
160
- const incrementButton = el.querySelector('[slot=suffix]');
161
- incrementButton?.dispatchEvent(new Event('click'));
162
- expect(counter).to.equal(1);
163
- });
164
-
165
- it('fires one "user-input-changed" event on - button click', async () => {
166
- let counter = 0;
167
- const el = await fixture(html`
168
- <lion-input-stepper
169
- name="year"
170
- label="Years"
171
- @user-input-changed="${() => {
172
- counter += 1;
173
- }}"
174
- >
175
- </lion-input-stepper>
176
- `);
177
- const decrementButton = el.querySelector('[slot=prefix]');
178
- decrementButton?.dispatchEvent(new Event('click'));
179
- expect(counter).to.equal(1);
180
- });
181
-
182
- it('should update min and max attributes when min and max property change', async () => {
183
- const el = await fixture(inputStepperWithAttrs);
184
- el.min = 100;
185
- el.max = 200;
186
- await nextFrame();
187
- expect(el._inputNode.min).to.equal(el.min.toString());
188
- expect(el._inputNode.max).to.equal(el.max.toString());
189
- });
190
-
191
- it('should remove the disabled attribute of the decrement button when the min property changes to below the modelvalue', async () => {
192
- const el = await fixture(inputStepperWithAttrs);
193
- const decrementButton = el.querySelector('[slot=prefix]');
194
- el.modelValue = 100;
195
- await nextFrame();
196
- expect(decrementButton?.getAttribute('disabled')).to.equal('true');
197
- el.min = 99;
198
- await nextFrame();
199
- expect(decrementButton?.getAttribute('disabled')).to.equal(null);
200
- });
201
-
202
- it('should add the disabled attribute of the decrement button when the min property changes to the modelvalue', async () => {
203
- const el = await fixture(inputStepperWithAttrs);
204
- const decrementButton = el.querySelector('[slot=prefix]');
205
- el.modelValue = 101;
206
- await nextFrame();
207
- expect(decrementButton?.getAttribute('disabled')).to.equal(null);
208
- el.min = 101;
209
- await nextFrame();
210
- expect(decrementButton?.getAttribute('disabled')).to.equal('true');
211
- });
212
-
213
- it('should remove the disabled attribute of the increment button when the max property changes to above the modelvalue', async () => {
214
- const el = await fixture(inputStepperWithAttrs);
215
- const incrementButton = el.querySelector('[slot=suffix]');
216
- el.modelValue = 200;
217
- await nextFrame();
218
- expect(incrementButton?.getAttribute('disabled')).to.equal('true');
219
- el.max = 201;
220
- await nextFrame();
221
- expect(incrementButton?.getAttribute('disabled')).to.equal(null);
222
- });
223
-
224
- it('should add the disabled attribute of the increment button when the max property changes to the modelvalue', async () => {
225
- const el = await fixture(inputStepperWithAttrs);
226
- const incrementButton = el.querySelector('[slot=suffix]');
227
- el.modelValue = 199;
228
- await nextFrame();
229
- expect(incrementButton?.getAttribute('disabled')).to.equal(null);
230
- el.max = 199;
231
- await nextFrame();
232
- expect(incrementButton?.getAttribute('disabled')).to.equal('true');
233
- });
234
-
235
- it('should react to changes in the modelValue by adjusting the disabled state of the button', async () => {
236
- const el = await fixture(inputStepperWithAttrs);
237
- const incrementButton = el.querySelector('[slot=suffix]');
238
- el.modelValue = 199;
239
- await nextFrame();
240
- expect(incrementButton?.getAttribute('disabled')).to.equal(null);
241
- el.modelValue = 200;
242
- await nextFrame();
243
- expect(incrementButton?.getAttribute('disabled')).to.equal('true');
244
- });
245
-
246
- describe('It align to steps', () => {
247
- it('aligns the value to the nearest step when incrementing', async () => {
248
- let el = await fixture(
249
- html`<lion-input-stepper step="10" min="0" max="100" value="55"></lion-input-stepper>`,
250
- );
251
- let incrementButton = el.querySelector('[slot=suffix]');
252
- incrementButton?.dispatchEvent(new Event('click'));
253
- await el.updateComplete;
254
- expect(el.modelValue).to.equal(60, 'Fail + : (0 > 100 by 10; val 55)');
255
-
256
- // min 1
257
- el = await fixture(
258
- html`<lion-input-stepper step="10" min="1" max="100" value="55"></lion-input-stepper>`,
259
- );
260
- incrementButton = el.querySelector('[slot=suffix]');
261
- incrementButton?.dispatchEvent(new Event('click'));
262
- await el.updateComplete;
263
- expect(el.modelValue).to.equal(61, 'Fail + : (1 > 100 by 10; val 55)');
264
-
265
- // min 34
266
- el = await fixture(
267
- html`<lion-input-stepper step="10" min="34" max="100" value="55"></lion-input-stepper>`,
268
- );
269
- incrementButton = el.querySelector('[slot=suffix]');
270
- incrementButton?.dispatchEvent(new Event('click'));
271
- await el.updateComplete;
272
- expect(el.modelValue).to.equal(64, 'Fail + : (34 > 100 by 10; val 55)');
273
-
274
- // min -23
275
- el = await fixture(
276
- html`<lion-input-stepper step="10" min="-23" max="100" value="55"></lion-input-stepper>`,
277
- );
278
- incrementButton = el.querySelector('[slot=suffix]');
279
- incrementButton?.dispatchEvent(new Event('click'));
280
- await el.updateComplete;
281
- expect(el.modelValue).to.equal(57, 'Fail + : (-23 > 100 by 10; val 55)'); // -23 > -13 > -3 > 7 > ... > 57
282
-
283
- // min -23
284
- el = await fixture(
285
- html`<lion-input-stepper step="10" min="-23" max="100" value="-9"></lion-input-stepper>`,
286
- );
287
- incrementButton = el.querySelector('[slot=suffix]');
288
- incrementButton?.dispatchEvent(new Event('click'));
289
- await el.updateComplete;
290
- expect(el.modelValue).to.equal(-3, 'Fail + : (-23 > 100 by 10; val 55)'); // -23 > -13 > -3 > 7
291
- });
292
-
293
- it('aligns the value to the nearest step when decrementing', async () => {
294
- let el = await fixture(
295
- html`<lion-input-stepper step="10" min="0" max="100" value="55"></lion-input-stepper>`,
296
- );
297
- let decrementButton = el.querySelector('[slot=prefix]');
298
- decrementButton?.dispatchEvent(new Event('click'));
299
- await el.updateComplete;
300
- expect(el.modelValue).to.equal(50, 'Fail - : (0 > 100 by 10; val 55)');
301
-
302
- // min 1
303
- el = await fixture(
304
- html`<lion-input-stepper step="10" min="1" max="100" value="55"></lion-input-stepper>`,
305
- );
306
- decrementButton = el.querySelector('[slot=prefix]');
307
- decrementButton?.dispatchEvent(new Event('click'));
308
- await el.updateComplete;
309
- expect(el.modelValue).to.equal(51, 'Fail - : (1 > 100 by 10; val 55)');
310
-
311
- // min 34
312
- el = await fixture(
313
- html`<lion-input-stepper step="10" min="34" max="100" value="55"></lion-input-stepper>`,
314
- );
315
- decrementButton = el.querySelector('[slot=prefix]');
316
- decrementButton?.dispatchEvent(new Event('click'));
317
- await el.updateComplete;
318
- expect(el.modelValue).to.equal(54, 'Fail - : (34 > 100 by 10; val 55)');
319
-
320
- // min -23
321
- el = await fixture(
322
- html`<lion-input-stepper step="10" min="-23" max="100" value="55"></lion-input-stepper>`,
323
- );
324
- decrementButton = el.querySelector('[slot=prefix]');
325
- decrementButton?.dispatchEvent(new Event('click'));
326
- await el.updateComplete;
327
- expect(el.modelValue).to.equal(47, 'Fail - : (-23 > 100 by 10; val 55)'); // -23 > -13 > -3 > 7 > ... > 47
328
-
329
- // min -23
330
- el = await fixture(
331
- html`<lion-input-stepper step="10" min="-23" max="100" value="-9"></lion-input-stepper>`,
332
- );
333
- decrementButton = el.querySelector('[slot=prefix]');
334
- decrementButton?.dispatchEvent(new Event('click'));
335
- await el.updateComplete;
336
- expect(el.modelValue).to.equal(-13, 'Fail - : (-23 > 100 by 10; val 55)'); // -23 > -13 > -3 > 7
337
- });
338
-
339
- it('handles decimal step values correctly', async () => {
340
- // Test with decimal step 0.1
341
- let el = await fixture(
342
- html`<lion-input-stepper step="0.1" min="0" max="9" value="5.55"></lion-input-stepper>`,
343
- );
344
-
345
- // Test increment with decimal step
346
- let incrementButton = el.querySelector('[slot=suffix]');
347
- incrementButton?.dispatchEvent(new Event('click'));
348
- await el.updateComplete;
349
- expect(el.modelValue).to.equal(5.6, 'Fail + : (0 > 9 by 0.1; val 5.55)');
350
-
351
- // Test decrement with decimal step
352
- let decrementButton = el.querySelector('[slot=prefix]');
353
- decrementButton?.dispatchEvent(new Event('click'));
354
- await el.updateComplete;
355
- expect(el.modelValue).to.equal(5.5, 'Fail - : (0 > 9 by 0.1; val 5.6)');
356
-
357
- // Test with value that needs alignment
358
- el = await fixture(
359
- html`<lion-input-stepper step="0.1" min="0" max="9" value="3.27"></lion-input-stepper>`,
360
- );
361
-
362
- // Should align to next step when incrementing
363
- incrementButton = el.querySelector('[slot=suffix]');
364
- incrementButton?.dispatchEvent(new Event('click'));
365
- await el.updateComplete;
366
- expect(el.modelValue).to.equal(3.3, 'Fail + alignment: (0 > 9 by 0.1; val 3.27)');
367
-
368
- // Reset and test decrement alignment
369
- el = await fixture(
370
- html`<lion-input-stepper step="0.1" min="0" max="9" value="3.27"></lion-input-stepper>`,
371
- );
372
-
373
- // Should align to previous step when decrementing
374
- decrementButton = el.querySelector('[slot=prefix]');
375
- decrementButton?.dispatchEvent(new Event('click'));
376
- await el.updateComplete;
377
- expect(el.modelValue).to.equal(3.2, 'Fail - alignment: (0 > 9 by 0.1; val 3.27)');
378
- });
379
- });
380
- });
381
-
382
- describe('_destroyOutputContent method', () => {
383
- beforeEach(() => {
384
- // Ensure clean state for each test
385
- sinon.restore();
386
- });
387
-
388
- it('should clear existing timer before setting a new one', async () => {
389
- const el = await fixture(defaultInputStepper);
390
- const clearTimeoutSpy = sinon.spy(window, 'clearTimeout');
391
-
392
- // Set an initial timer
393
- el.timer = setTimeout(() => {}, 1000);
394
- const initialTimer = el.timer;
395
-
396
- // Call _destroyOutputContent
397
- el._destroyOutputContent();
398
-
399
- expect(clearTimeoutSpy.calledWith(initialTimer)).to.be.true;
400
- clearTimeoutSpy.restore();
401
- });
402
-
403
- it('should set __valueText to nothing and request update after timeout', async () => {
404
- const el = await fixture(defaultInputStepper);
405
- const requestUpdateSpy = sinon.spy(el, 'requestUpdate');
406
- const clock = sinon.useFakeTimers();
407
-
408
- // Set initial value text
409
- el.__valueText = 'test value';
410
-
411
- // Call _destroyOutputContent
412
- el._destroyOutputContent();
413
-
414
- // Fast forward time by 2000ms (default timeout)
415
- clock.tick(2000);
416
-
417
- await expect(el.__valueText).to.equal(nothing);
418
- expect(requestUpdateSpy.calledOnce).to.be.true;
419
-
420
- clock.restore();
421
- requestUpdateSpy.restore();
422
- });
423
-
424
- it('should use custom timeout from data-self-destruct attribute', async () => {
425
- const el = await fixture(html`
426
- <lion-input-stepper name="test" label="Test"> </lion-input-stepper>
427
- `);
428
-
429
- await el.updateComplete;
430
-
431
- // Get the output element and set custom self-destruct value
432
- const outputElement = /** @type {HTMLElement} */ (
433
- el.shadowRoot?.querySelector('.input-stepper__value')
434
- );
435
- if (outputElement) {
436
- outputElement.dataset.selfDestruct = '5000';
437
- }
438
-
439
- const clock = sinon.useFakeTimers();
440
- const requestUpdateSpy = sinon.spy(el, 'requestUpdate');
441
-
442
- el.__valueText = 'test value';
443
- el._destroyOutputContent();
444
-
445
- // Should not trigger after default 2000ms
446
- clock.tick(2000);
447
- await expect(el.__valueText).to.equal('test value');
448
- expect(requestUpdateSpy.called).to.be.false;
449
-
450
- // Should trigger after custom 5000ms
451
- clock.tick(3000); // Total 5000ms
452
- await expect(el.__valueText).to.equal(nothing);
453
- expect(requestUpdateSpy.calledOnce).to.be.true;
454
-
455
- clock.restore();
456
- requestUpdateSpy.restore();
457
- });
458
-
459
- it('should handle invalid data-self-destruct value by using default timeout', async () => {
460
- const el = await fixture(html`
461
- <lion-input-stepper name="test" label="Test"> </lion-input-stepper>
462
- `);
463
-
464
- await el.updateComplete;
465
-
466
- // Get the output element and set invalid self-destruct value
467
- const outputElement = /** @type {HTMLElement} */ (
468
- el.shadowRoot?.querySelector('.input-stepper__value')
469
- );
470
- if (outputElement) {
471
- outputElement.dataset.selfDestruct = 'invalid';
472
- }
473
-
474
- const clock = sinon.useFakeTimers();
475
- const requestUpdateSpy = sinon.spy(el, 'requestUpdate');
476
-
477
- el.__valueText = 'test value';
478
- el._destroyOutputContent();
479
-
480
- // Should use default 2000ms when invalid value is provided
481
- clock.tick(2000);
482
- await expect(el.__valueText).to.equal(nothing);
483
- expect(requestUpdateSpy.calledOnce).to.be.true;
484
-
485
- clock.restore();
486
- requestUpdateSpy.restore();
487
- });
488
-
489
- it('should not set timeout if output element does not exist', async () => {
490
- const el = await fixture(defaultInputStepper);
491
- const setTimeoutSpy = sinon.spy(window, 'setTimeout');
492
-
493
- // Mock shadowRoot to return null for querySelector
494
- const originalQuerySelector = el.shadowRoot?.querySelector;
495
- if (el.shadowRoot) {
496
- el.shadowRoot.querySelector = () => null;
497
- }
498
-
499
- el._destroyOutputContent();
500
-
501
- expect(setTimeoutSpy.called).to.be.false;
502
-
503
- // Restore original querySelector
504
- if (el.shadowRoot && originalQuerySelector) {
505
- el.shadowRoot.querySelector = originalQuerySelector;
506
- }
507
- setTimeoutSpy.restore();
508
- });
509
-
510
- it('should only execute timeout callback if output element still has parent', async () => {
511
- const el = await fixture(defaultInputStepper);
512
- const clock = sinon.useFakeTimers();
513
- const requestUpdateSpy = sinon.spy(el, 'requestUpdate');
514
-
515
- el.__valueText = 'test value';
516
- el._destroyOutputContent();
517
-
518
- // Remove the output element from DOM before timeout
519
- const outputElement = el.shadowRoot?.querySelector('.input-stepper__value');
520
- if (outputElement && outputElement.parentNode) {
521
- outputElement.parentNode.removeChild(outputElement);
522
- }
523
-
524
- // Fast forward time
525
- clock.tick(2000);
526
-
527
- // Should not have updated since element was removed
528
- expect(requestUpdateSpy.called).to.be.false;
529
- expect(el.__valueText).to.equal('test value');
530
-
531
- clock.restore();
532
- requestUpdateSpy.restore();
533
- });
534
-
535
- it('should be called when increment button is clicked', async () => {
536
- const el = await fixture(defaultInputStepper);
537
- const destroyOutputSpy = sinon.spy(el, '_destroyOutputContent');
538
-
539
- const incrementButton = el.querySelector('[slot=suffix]');
540
- incrementButton?.dispatchEvent(new Event('click'));
541
-
542
- expect(destroyOutputSpy.calledOnce).to.be.true;
543
- destroyOutputSpy.restore();
544
- });
545
-
546
- it('should be called when decrement button is clicked', async () => {
547
- const el = await fixture(defaultInputStepper);
548
- const destroyOutputSpy = sinon.spy(el, '_destroyOutputContent');
549
-
550
- const decrementButton = el.querySelector('[slot=prefix]');
551
- decrementButton?.dispatchEvent(new Event('click'));
552
-
553
- expect(destroyOutputSpy.calledOnce).to.be.true;
554
- destroyOutputSpy.restore();
555
- });
556
-
557
- it('should handle multiple rapid calls by clearing previous timers', async () => {
558
- const el = await fixture(defaultInputStepper);
559
- const clock = sinon.useFakeTimers();
560
- const requestUpdateSpy = sinon.spy(el, 'requestUpdate');
561
-
562
- el.__valueText = 'test value';
563
-
564
- // Call _destroyOutputContent multiple times rapidly
565
- el._destroyOutputContent();
566
- clock.tick(1000); // 1 second
567
-
568
- el._destroyOutputContent(); // This should clear the previous timer
569
- clock.tick(1000); // Total 2 seconds from first call, 1 second from second call
570
-
571
- // Should not have triggered yet since second call reset the timer
572
- expect(el.__valueText).to.equal('test value');
573
- expect(requestUpdateSpy.called).to.be.false;
574
-
575
- clock.tick(1000); // Total 2 seconds from second call
576
-
577
- // Should trigger now
578
- await expect(el.__valueText).to.equal(nothing);
579
- expect(requestUpdateSpy.calledOnce).to.be.true;
580
-
581
- clock.restore();
582
- requestUpdateSpy.restore();
583
- });
584
- });
585
-
586
- describe('Accessibility', () => {
587
- it('is a11y AXE accessible', async () => {
588
- const el = await fixture(defaultInputStepper);
589
- await expect(el).to.be.accessible();
590
- });
591
-
592
- it('is accessible when disabled', async () => {
593
- const el = await fixture(`<lion-input-stepper label="rsvp" disabled></lion-input-stepper>`);
594
- await expect(el).to.be.accessible();
595
- });
596
-
597
- it('has role="spinbutton"', async () => {
598
- const el = await fixture(defaultInputStepper);
599
- expect(el._inputNode.hasAttribute('role')).to.be.true;
600
- expect(el._inputNode.getAttribute('role')).to.equal('spinbutton');
601
- });
602
-
603
- it('updates aria-valuenow when stepper is changed', async () => {
604
- const el = await fixture(defaultInputStepper);
605
- el.modelValue = 1;
606
-
607
- await el.updateComplete;
608
- expect(el._inputNode.hasAttribute('aria-valuenow')).to.be.true;
609
- expect(el._inputNode.getAttribute('aria-valuenow')).to.equal('1');
610
-
611
- el.modelValue = '';
612
- await el.updateComplete;
613
- expect(el._inputNode.hasAttribute('aria-valuenow')).to.be.false;
614
- });
615
-
616
- it('updates aria-valuetext when stepper is changed', async () => {
617
- // VoiceOver announces percentages once the valuemin or valuemax are used.
618
- // This can be fixed by setting valuetext to the same value as valuenow
619
- // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-valuenow
620
- const el = await fixture(defaultInputStepper);
621
- el.modelValue = 1;
622
- await el.updateComplete;
623
-
624
- expect(el._inputNode.hasAttribute('aria-valuetext')).to.be.true;
625
- expect(el._inputNode.getAttribute('aria-valuetext')).to.equal('1');
626
-
627
- el.modelValue = '';
628
- await el.updateComplete;
629
- expect(el._inputNode.hasAttribute('aria-valuetext')).to.be.false;
630
- });
631
-
632
- it('can give aria-valuetext to override default value as a human-readable text alternative', async () => {
633
- const values = {
634
- 1: 'first',
635
- 2: 'second',
636
- 3: 'third',
637
- };
638
- const el = await fixture(html`
639
- <lion-input-stepper min="1" max="3" .valueTextMapping="${values}"></lion-input-stepper>
640
- `);
641
- el.modelValue = 1;
642
- await el.updateComplete;
643
- expect(el._inputNode.hasAttribute('aria-valuetext')).to.be.true;
644
- expect(el._inputNode.getAttribute('aria-valuetext')).to.equal('first');
645
- });
646
-
647
- it('updates aria-valuemin when stepper is changed', async () => {
648
- const el = await fixture(inputStepperWithAttrs);
649
- const incrementButton = el.querySelector('[slot=suffix]');
650
- incrementButton?.dispatchEvent(new Event('click'));
651
- expect(el._inputNode.hasAttribute('aria-valuemin')).to.be.true;
652
- expect(el._inputNode.getAttribute('aria-valuemin')).to.equal('100');
653
-
654
- el.min = 0;
655
- await el.updateComplete;
656
- expect(el._inputNode.hasAttribute('aria-valuemin')).to.be.true;
657
- expect(el._inputNode.getAttribute('aria-valuemin')).to.equal('0');
658
- });
659
-
660
- it('updates aria-valuemax when stepper is changed', async () => {
661
- const el = await fixture(inputStepperWithAttrs);
662
- const incrementButton = el.querySelector('[slot=suffix]');
663
- incrementButton?.dispatchEvent(new Event('click'));
664
- expect(el._inputNode.hasAttribute('aria-valuemax')).to.be.true;
665
- expect(el._inputNode.getAttribute('aria-valuemax')).to.equal('200');
666
-
667
- el.max = 1000;
668
- await el.updateComplete;
669
- expect(el._inputNode.hasAttribute('aria-valuemax')).to.be.true;
670
- expect(el._inputNode.getAttribute('aria-valuemax')).to.equal('1000');
671
- });
672
-
673
- it('decrease button should have aria-label with the component label', async () => {
674
- const el = await fixture(inputStepperWithAttrs);
675
- const decrementButton = el.querySelector('[slot=prefix]');
676
-
677
- expect(decrementButton?.hasAttribute('aria-label')).to.be.true;
678
- expect(decrementButton?.getAttribute('aria-label')).to.equal('Decrease Label');
679
- });
680
-
681
- it('increase button should have aria-label with the component label', async () => {
682
- const el = await fixture(inputStepperWithAttrs);
683
- const incrementButton = el.querySelector('[slot=suffix]');
684
-
685
- expect(incrementButton?.hasAttribute('aria-label')).to.be.true;
686
- expect(incrementButton?.getAttribute('aria-label')).to.equal('Increase Label');
687
- });
688
- });
689
- });
3
+ runInputStepperSuite();