@lion/ui 0.4.2 → 0.5.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 (175) hide show
  1. package/components/button/src/LionButton.js +2 -2
  2. package/components/calendar/src/LionCalendar.js +1 -1
  3. package/components/calendar/test-helpers/DayObject.js +2 -2
  4. package/components/collapsible/src/LionCollapsible.js +1 -0
  5. package/components/combobox/src/LionCombobox.js +79 -28
  6. package/components/combobox/test/lion-combobox-integrations.test.js +5 -0
  7. package/components/combobox/test/lion-combobox.test.js +290 -23
  8. package/components/combobox/test-helpers/combobox-helpers.js +5 -2
  9. package/components/core/src/browserDetection.js +8 -0
  10. package/components/drawer/src/LionDrawer.js +2 -0
  11. package/components/form-core/src/FocusMixin.js +2 -0
  12. package/components/form-core/src/FormControlMixin.js +1 -1
  13. package/components/form-core/src/choice-group/ChoiceGroupMixin.js +1 -0
  14. package/components/form-core/src/choice-group/CustomChoiceGroupMixin.js +173 -0
  15. package/components/form-core/src/utils/getAriaElementsInRightDomOrder.js +7 -14
  16. package/components/form-core/src/validate/validators/Required.js +2 -2
  17. package/components/form-core/test/choice-group/CustomChoiceGroupMixin.integrations.test.js +55 -0
  18. package/components/form-core/test/choice-group/CustomChoiceGroupMixin.test.js +16 -0
  19. package/components/form-core/test/utils/getAriaElementsInRightDomOrder.test.js +0 -25
  20. package/components/form-core/test/validate/StringValidators.test.js +1 -1
  21. package/components/form-core/test-helpers/ExampleValidators.js +1 -3
  22. package/components/form-core/test-suites/FormRegistrationMixins.suite.js +2 -0
  23. package/components/form-core/test-suites/NativeTextFieldMixin.suite.js +4 -4
  24. package/components/form-core/test-suites/ValidateMixin.suite.js +27 -0
  25. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +11 -2
  26. package/components/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js +1 -0
  27. package/components/form-core/test-suites/choice-group/CustomChoiceGroupMixin.suite.js +265 -0
  28. package/components/form-core/types/choice-group/ChoiceGroupMixinTypes.ts +1 -1
  29. package/components/form-core/types/choice-group/CustomChoiceGroupMixinTypes.ts +30 -0
  30. package/components/form-core/types/choice-group/index.ts +1 -0
  31. package/components/helpers/src/SbActionLogger.js +1 -0
  32. package/components/icon/src/LionIcon.js +1 -9
  33. package/components/icon/test/lion-icon.test.js +2 -0
  34. package/components/input-datepicker/src/LionInputDatepicker.js +2 -6
  35. package/components/input-range/test/lion-input-range.test.js +12 -6
  36. package/components/listbox/src/LionOptions.js +5 -5
  37. package/components/listbox/src/ListboxMixin.js +1 -16
  38. package/components/listbox/test/lion-options.test.js +1 -1
  39. package/components/listbox/test-suites/ListboxMixin.suite.js +1 -1
  40. package/components/localize/src/date/getMonthNames.js +3 -0
  41. package/components/localize/src/date/parseDate.js +5 -1
  42. package/components/localize/src/date/utils/forceDotsForNlnl.js +11 -0
  43. package/components/localize/src/number/utils/normalize-format-number-to-parts/forceAddGroupSeparators.js +3 -1
  44. package/components/localize/src/number/utils/normalize-format-number-to-parts/forceSymbols.js +2 -0
  45. package/components/localize/src/number/utils/normalize-format-number-to-parts/normalizeIntl.js +1 -0
  46. package/components/localize/test/date/getMonthNames.test.js +1 -0
  47. package/components/localize/test/date/parseDate.test.js +6 -0
  48. package/components/localize/test-helpers/fake-imports.js +1 -0
  49. package/components/overlays/src/OverlaysManager.js +4 -15
  50. package/components/overlays/src/utils/adopt-styles.js +6 -1
  51. package/components/overlays/test/OverlaysManager.test.js +12 -11
  52. package/components/overlays/test/utils-tests/adopt-styles.test.js +61 -0
  53. package/components/overlays/test-suites/OverlayMixin.suite.js +2 -2
  54. package/components/radio-group/src/LionRadioGroup.js +1 -1
  55. package/components/radio-group/test/lion-radio-group.test.js +29 -0
  56. package/components/select/test/lion-select.test.js +1 -1
  57. package/components/switch/src/LionSwitch.js +3 -1
  58. package/components/switch/src/LionSwitchButton.js +2 -5
  59. package/components/tabs/src/LionTabs.js +7 -7
  60. package/components/textarea/src/LionTextarea.js +1 -1
  61. package/components/validate-messages/translations/de.js +3 -3
  62. package/custom-elements.json +103123 -61806
  63. package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
  64. package/dist-types/components/collapsible/src/LionCollapsible.d.ts.map +1 -1
  65. package/dist-types/components/combobox/src/LionCombobox.d.ts +13 -8
  66. package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
  67. package/dist-types/components/combobox/test-helpers/combobox-helpers.d.ts.map +1 -1
  68. package/dist-types/components/core/src/browserDetection.d.ts +2 -0
  69. package/dist-types/components/drawer/src/LionDrawer.d.ts.map +1 -1
  70. package/dist-types/components/form-core/src/FocusMixin.d.ts.map +1 -1
  71. package/dist-types/components/form-core/src/choice-group/ChoiceGroupMixin.d.ts.map +1 -1
  72. package/dist-types/components/form-core/src/choice-group/CustomChoiceGroupMixin.d.ts +4 -0
  73. package/dist-types/components/form-core/src/choice-group/CustomChoiceGroupMixin.d.ts.map +1 -0
  74. package/dist-types/components/form-core/src/utils/getAriaElementsInRightDomOrder.d.ts +5 -5
  75. package/dist-types/components/form-core/src/utils/getAriaElementsInRightDomOrder.d.ts.map +1 -1
  76. package/dist-types/components/form-core/test/choice-group/CustomChoiceGroupMixin.integrations.test.d.ts +2 -0
  77. package/dist-types/components/form-core/test/choice-group/CustomChoiceGroupMixin.integrations.test.d.ts.map +1 -0
  78. package/dist-types/components/form-core/test/choice-group/CustomChoiceGroupMixin.test.d.ts +6 -0
  79. package/dist-types/components/form-core/test/choice-group/CustomChoiceGroupMixin.test.d.ts.map +1 -0
  80. package/dist-types/components/form-core/test-helpers/ExampleValidators.d.ts +0 -1
  81. package/dist-types/components/form-core/test-helpers/ExampleValidators.d.ts.map +1 -1
  82. package/dist-types/components/form-core/test-suites/FormRegistrationMixins.suite.d.ts.map +1 -1
  83. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  84. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  85. package/dist-types/components/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.d.ts.map +1 -1
  86. package/dist-types/components/form-core/test-suites/choice-group/CustomChoiceGroupMixin.suite.d.ts +14 -0
  87. package/dist-types/components/form-core/test-suites/choice-group/CustomChoiceGroupMixin.suite.d.ts.map +1 -0
  88. package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts +1 -1
  89. package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts.map +1 -1
  90. package/dist-types/components/form-core/types/choice-group/CustomChoiceGroupMixinTypes.d.ts +18 -0
  91. package/dist-types/components/form-core/types/choice-group/CustomChoiceGroupMixinTypes.d.ts.map +1 -0
  92. package/dist-types/components/form-core/types/choice-group/index.d.ts +1 -0
  93. package/dist-types/components/form-core/types/choice-group/index.d.ts.map +1 -1
  94. package/dist-types/components/helpers/src/SbActionLogger.d.ts.map +1 -1
  95. package/dist-types/components/icon/src/LionIcon.d.ts +0 -9
  96. package/dist-types/components/icon/src/LionIcon.d.ts.map +1 -1
  97. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +0 -2
  98. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts.map +1 -1
  99. package/dist-types/components/listbox/src/LionOptions.d.ts +0 -5
  100. package/dist-types/components/listbox/src/LionOptions.d.ts.map +1 -1
  101. package/dist-types/components/listbox/src/ListboxMixin.d.ts.map +1 -1
  102. package/dist-types/components/localize/src/date/getMonthNames.d.ts.map +1 -1
  103. package/dist-types/components/localize/src/date/parseDate.d.ts.map +1 -1
  104. package/dist-types/components/localize/src/date/utils/forceDotsForNlnl.d.ts +5 -0
  105. package/dist-types/components/localize/src/date/utils/forceDotsForNlnl.d.ts.map +1 -0
  106. package/dist-types/components/localize/src/number/utils/normalize-format-number-to-parts/forceAddGroupSeparators.d.ts.map +1 -1
  107. package/dist-types/components/localize/src/number/utils/normalize-format-number-to-parts/forceSymbols.d.ts.map +1 -1
  108. package/dist-types/components/localize/src/number/utils/normalize-format-number-to-parts/normalizeIntl.d.ts.map +1 -1
  109. package/dist-types/components/localize/test-helpers/fake-imports.d.ts.map +1 -1
  110. package/dist-types/components/overlays/src/OverlaysManager.d.ts +0 -4
  111. package/dist-types/components/overlays/src/OverlaysManager.d.ts.map +1 -1
  112. package/dist-types/components/overlays/src/utils/adopt-styles.d.ts.map +1 -1
  113. package/dist-types/components/overlays/test/OverlaysManager.test.d.ts.map +1 -1
  114. package/dist-types/components/switch/src/LionSwitch.d.ts.map +1 -1
  115. package/dist-types/components/switch/src/LionSwitchButton.d.ts +0 -5
  116. package/dist-types/components/switch/src/LionSwitchButton.d.ts.map +1 -1
  117. package/dist-types/components/tabs/src/LionTabs.d.ts.map +1 -1
  118. package/dist-types/exports/types/form-core.d.ts +1 -1
  119. package/dist-types/exports/types/form-core.d.ts.map +1 -1
  120. package/dist-types/tsconfig.tsbuildinfo +1 -1
  121. package/docs/components/checkbox-group/overview.md +2 -2
  122. package/docs/components/collapsible/assets/CustomCollapsible.js +4 -0
  123. package/docs/components/collapsible/overview.md +1 -1
  124. package/docs/components/combobox/overview.md +1 -1
  125. package/docs/components/combobox/src/demo-selection-display.js +8 -35
  126. package/docs/components/combobox/use-cases.md +24 -1
  127. package/docs/components/dialog/overview.md +1 -1
  128. package/docs/components/fieldset/overview.md +5 -5
  129. package/docs/components/form/overview.md +3 -3
  130. package/docs/components/icon/use-cases.md +1 -1
  131. package/docs/components/input/overview.md +1 -1
  132. package/docs/components/input-amount/overview.md +3 -3
  133. package/docs/components/input-date/overview.md +3 -3
  134. package/docs/components/input-datepicker/overview.md +5 -5
  135. package/docs/components/input-email/overview.md +2 -2
  136. package/docs/components/input-file/overview.md +1 -1
  137. package/docs/components/input-iban/overview.md +2 -2
  138. package/docs/components/input-range/overview.md +2 -2
  139. package/docs/components/input-stepper/overview.md +1 -1
  140. package/docs/components/input-tel/overview.md +1 -1
  141. package/docs/components/input-tel/use-cases.md +6 -6
  142. package/docs/components/input-tel-dropdown/overview.md +1 -1
  143. package/docs/components/radio-group/overview.md +3 -3
  144. package/docs/components/select-rich/use-cases.md +1 -1
  145. package/docs/components/tabs/examples.md +1 -1
  146. package/docs/fundamentals/index.md +4 -4
  147. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  148. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  149. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  150. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  151. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  152. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  153. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  154. package/docs/fundamentals/rationales/accessibility.md +3 -3
  155. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  156. package/docs/fundamentals/systems/core/overview.md +1 -1
  157. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  158. package/docs/fundamentals/systems/form/model-value.md +1 -1
  159. package/docs/fundamentals/systems/form/overview.md +28 -28
  160. package/docs/fundamentals/systems/form/validate.md +18 -2
  161. package/docs/fundamentals/systems/localize/overview.md +4 -4
  162. package/docs/fundamentals/systems/overlays/configuration.md +3 -2
  163. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  164. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  165. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  166. package/docs/fundamentals/tools/singleton-manager/example-complex/overlayCompatibility.js +1 -2
  167. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  168. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  169. package/docs/guides/how-to/create-a-form.md +4 -4
  170. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  171. package/docs/guides/index.md +8 -8
  172. package/docs/guides/principles/index.md +5 -5
  173. package/docs/guides/principles/styling.md +1 -1
  174. package/exports/types/form-core.ts +5 -1
  175. package/package.json +2 -2
@@ -0,0 +1,265 @@
1
+ import '@lion/ui/define/lion-fieldset.js';
2
+ import '@lion/ui/define/lion-checkbox-group.js';
3
+ import '@lion/ui/define/lion-checkbox.js';
4
+ import { expect, fixture, fixtureSync, html, unsafeStatic } from '@open-wc/testing';
5
+
6
+ /**
7
+ *
8
+ * @typedef {import('../../test/choice-group/CustomChoiceGroupMixin.test.js').CustomChoiceGroup} CustomChoiceGroup
9
+ */
10
+
11
+ /**
12
+ * @param {{ parentTagString?:string, childTagString?: string, choiceType?: string}} config
13
+ */
14
+ export function runCustomChoiceGroupMixinSuite({
15
+ parentTagString,
16
+ childTagString,
17
+ choiceType,
18
+ } = {}) {
19
+ const cfg = {
20
+ parentTagString: parentTagString || 'custom-choice-input-group',
21
+ childTagString: childTagString || 'custom-choice-input',
22
+ choiceType: choiceType || 'single',
23
+ };
24
+
25
+ const parentTag = unsafeStatic(cfg.parentTagString);
26
+ const childTag = unsafeStatic(cfg.childTagString);
27
+
28
+ describe(`CustomChoiceGroupMixin: ${cfg.parentTagString}`, () => {
29
+ if (cfg.choiceType === 'single') {
30
+ it('has a single modelValue representing a custom value', async () => {
31
+ const el = /** @type {CustomChoiceGroup} */ (
32
+ await fixture(html`
33
+ <${parentTag} allow-custom-choice name="gender[]">
34
+ <${childTag} .choiceValue=${'male'}></${childTag}>
35
+ <${childTag} checked .choiceValue=${'female'}></${childTag}>
36
+ </${parentTag}>
37
+ `)
38
+ );
39
+
40
+ await el.registrationComplete;
41
+
42
+ expect(el.modelValue).to.equal('female');
43
+ el.modelValue = 'male';
44
+ expect(el.modelValue).to.equal('male');
45
+
46
+ el.modelValue = 'other';
47
+ expect(el.modelValue).to.equal('other');
48
+
49
+ expect(el.formElements[0].checked).to.be.false;
50
+ expect(el.formElements[1].checked).to.be.false;
51
+ });
52
+
53
+ it('has a single formattedValue representing a custom value', async () => {
54
+ const el = /** @type {CustomChoiceGroup} */ (
55
+ await fixture(html`
56
+ <${parentTag} allow-custom-choice name="gender">
57
+ <${childTag} .choiceValue=${'male'}></${childTag}>
58
+ <${childTag} .choiceValue=${'female'} checked></${childTag}>
59
+ </${parentTag}>
60
+ `)
61
+ );
62
+
63
+ el.modelValue = 'other';
64
+ expect(el.formattedValue).to.equal('other');
65
+ });
66
+ }
67
+
68
+ it('can set initial custom modelValue on creation', async () => {
69
+ const el = /** @type {CustomChoiceGroup} */ (
70
+ await fixture(html`
71
+ <${parentTag} allow-custom-choice name="gender[]" .modelValue=${'other'}>
72
+ <${childTag} .choiceValue=${'male'}></${childTag}>
73
+ <${childTag} .choiceValue=${'female'}></${childTag}>
74
+ </${parentTag}>
75
+ `)
76
+ );
77
+
78
+ if (cfg.choiceType === 'single') {
79
+ expect(el.modelValue).to.equal('other');
80
+ } else {
81
+ expect(el.modelValue).to.deep.equal(['other']);
82
+ }
83
+ expect(el.formElements[0].checked).to.be.false;
84
+ expect(el.formElements[1].checked).to.be.false;
85
+ });
86
+
87
+ it('can set initial custom serializedValue on creation', async () => {
88
+ const el = /** @type {CustomChoiceGroup} */ (
89
+ await fixture(html`
90
+ <${parentTag} allow-custom-choice name="gender[]" .serializedValue=${'other'}>
91
+ <${childTag} .choiceValue=${'male'}></${childTag}>
92
+ <${childTag} .choiceValue=${'female'}></${childTag}>
93
+ </${parentTag}>
94
+ `)
95
+ );
96
+
97
+ if (cfg.choiceType === 'single') {
98
+ expect(el.serializedValue).to.equal('other');
99
+ } else {
100
+ expect(el.serializedValue).to.deep.equal(['other']);
101
+ }
102
+ expect(el.formElements[0].checked).to.be.false;
103
+ expect(el.formElements[1].checked).to.be.false;
104
+ });
105
+
106
+ it('can set initial custom formattedValue on creation', async () => {
107
+ const el = /** @type {CustomChoiceGroup} */ (
108
+ await fixture(html`
109
+ <${parentTag} allow-custom-choice name="gender[]" .formattedValue=${'other'}>
110
+ <${childTag} .choiceValue=${'male'}></${childTag}>
111
+ <${childTag} .choiceValue=${'female'}></${childTag}>
112
+ </${parentTag}>
113
+ `)
114
+ );
115
+
116
+ if (cfg.choiceType === 'single') {
117
+ expect(el.formattedValue).to.equal('other');
118
+ } else {
119
+ expect(el.formattedValue).to.deep.equal(['other']);
120
+ }
121
+ expect(el.formElements[0].checked).to.be.false;
122
+ expect(el.formElements[1].checked).to.be.false;
123
+ });
124
+
125
+ it('correctly handles custom modelValue being set before registrationComplete', async () => {
126
+ const el = /** @type {CustomChoiceGroup} */ (
127
+ fixtureSync(html`
128
+ <${parentTag} allow-custom-choice name="gender[]" .modelValue=${null}>
129
+ <${childTag} .choiceValue=${'male'}></${childTag}>
130
+ <${childTag} .choiceValue=${'female'}></${childTag}>
131
+ </${parentTag}>
132
+ `)
133
+ );
134
+
135
+ if (cfg.choiceType === 'single') {
136
+ el.modelValue = 'other';
137
+ await el.registrationComplete;
138
+ expect(el.modelValue).to.equal('other');
139
+ } else {
140
+ el.modelValue = ['other'];
141
+ await el.registrationComplete;
142
+ expect(el.modelValue).to.deep.equal(['other']);
143
+ }
144
+ });
145
+
146
+ it('correctly handles custom serializedValue being set before registrationComplete', async () => {
147
+ const el = /** @type {CustomChoiceGroup} */ (
148
+ fixtureSync(html`
149
+ <${parentTag} allow-custom-choice name="gender[]" .serializedValue=${null}>
150
+ <${childTag} .choiceValue=${'male'}></${childTag}>
151
+ <${childTag} .choiceValue=${'female'}></${childTag}>
152
+ </${parentTag}>
153
+ `)
154
+ );
155
+
156
+ if (cfg.choiceType === 'single') {
157
+ // @ts-expect-error
158
+ el.serializedValue = 'other';
159
+ await el.registrationComplete;
160
+ expect(el.serializedValue).to.equal('other');
161
+ } else {
162
+ // @ts-expect-error
163
+ el.serializedValue = ['other'];
164
+ await el.registrationComplete;
165
+ expect(el.serializedValue).to.deep.equal(['other']);
166
+ }
167
+ });
168
+
169
+ it('can be cleared, even when a custom value is selected', async () => {
170
+ const el = /** @type {CustomChoiceGroup} */ (
171
+ await fixture(html`
172
+ <${parentTag} allow-custom-choice name="gender[]">
173
+ <${childTag} .choiceValue=${'male'}></${childTag}>
174
+ <${childTag} .choiceValue=${'female'}></${childTag}>
175
+ </${parentTag}>
176
+ `)
177
+ );
178
+ if (cfg.choiceType === 'single') {
179
+ el.modelValue = 'other';
180
+ } else {
181
+ el.modelValue = ['other'];
182
+ }
183
+
184
+ el.clear();
185
+
186
+ if (cfg.choiceType === 'single') {
187
+ expect(el.serializedValue).to.deep.equal('');
188
+ } else {
189
+ expect(el.serializedValue).to.deep.equal([]);
190
+ }
191
+ });
192
+
193
+ describe('multipleChoice', () => {
194
+ it('has a single modelValue representing all currently checked values, including custom values', async () => {
195
+ const el = /** @type {CustomChoiceGroup} */ (
196
+ await fixture(html`
197
+ <${parentTag} allow-custom-choice multiple-choice name="gender[]">
198
+ <${childTag} .choiceValue=${'male'}></${childTag}>
199
+ <${childTag} .choiceValue=${'female'} checked></${childTag}>
200
+ </${parentTag}>
201
+ `)
202
+ );
203
+
204
+ expect(el.modelValue).to.eql(['female']);
205
+
206
+ el.modelValue = ['female', 'male'];
207
+ expect(el.modelValue).to.eql(['male', 'female']);
208
+
209
+ el.modelValue = ['female', 'male', 'other'];
210
+ expect(el.modelValue).to.eql(['male', 'female', 'other']);
211
+ });
212
+
213
+ it('has a single serializedValue representing all currently checked values, including custom values', async () => {
214
+ const el = /** @type {CustomChoiceGroup} */ (
215
+ await fixture(html`
216
+ <${parentTag} allow-custom-choice multiple-choice name="gender[]">
217
+ <${childTag} .choiceValue=${'male'}></${childTag}>
218
+ <${childTag} .choiceValue=${'female'} checked></${childTag}>
219
+ </${parentTag}>
220
+ `)
221
+ );
222
+
223
+ expect(el.serializedValue).to.eql(['female']);
224
+
225
+ el.modelValue = ['female', 'male', 'other'];
226
+ expect(el.serializedValue).to.eql(['male', 'female', 'other']);
227
+ });
228
+
229
+ it('has a single formattedValue representing all currently checked values', async () => {
230
+ const el = /** @type {CustomChoiceGroup} */ (
231
+ await fixture(html`
232
+ <${parentTag} allow-custom-choice multiple-choice name="gender[]">
233
+ <${childTag} .choiceValue=${'male'}></${childTag}>
234
+ <${childTag} .choiceValue=${'female'} checked></${childTag}>
235
+ </${parentTag}>
236
+ `)
237
+ );
238
+
239
+ expect(el.formattedValue).to.eql(['female']);
240
+
241
+ el.modelValue = ['female', 'male', 'other'];
242
+ expect(el.formattedValue).to.eql(['male', 'female', 'other']);
243
+ });
244
+
245
+ it('unchecks non-matching checkboxes when setting the modelValue', async () => {
246
+ const el = /** @type {CustomChoiceGroup} */ (
247
+ await fixture(html`
248
+ <${parentTag} allow-custom-choice multiple-choice name="gender[]">
249
+ <${childTag} .choiceValue=${'male'} checked></${childTag}>
250
+ <${childTag} .choiceValue=${'female'} checked></${childTag}>
251
+ </${parentTag}>
252
+ `)
253
+ );
254
+
255
+ expect(el.modelValue).to.eql(['male', 'female']);
256
+ expect(el.formElements[0].checked).to.be.true;
257
+ expect(el.formElements[1].checked).to.be.true;
258
+
259
+ el.modelValue = ['other'];
260
+ expect(el.formElements[0].checked).to.be.false;
261
+ expect(el.formElements[1].checked).to.be.false;
262
+ });
263
+ });
264
+ });
265
+ }
@@ -25,7 +25,7 @@ export declare class ChoiceGroupHost {
25
25
  filterFn?: (el: FormControl, property?: string) => boolean,
26
26
  ): void;
27
27
  protected _throwWhenInvalidChildModelValue(child: FormControlHost): void;
28
- protected _isEmpty(): void;
28
+ protected _isEmpty(): boolean;
29
29
  protected _checkSingleChoiceElements(ev: Event): void;
30
30
  protected _getCheckedElements(): ChoiceInputHost[];
31
31
  protected _setCheckedElements(value: any, check: boolean): void;
@@ -0,0 +1,30 @@
1
+ import { Constructor } from '@open-wc/dedupe-mixin';
2
+ import { LitElement } from 'lit';
3
+
4
+ import { ChoiceGroupHost } from './ChoiceGroupMixinTypes.js';
5
+
6
+ export declare class CustomChoiceGroupHost {
7
+ allowCustomChoice: boolean;
8
+ get modelValue(): any;
9
+ set modelValue(value: any);
10
+ get serializedValue(): string;
11
+ set serializedValue(value: string);
12
+ get formattedValue(): string;
13
+ set formattedValue(value: string);
14
+
15
+ clear(): void;
16
+ parser(value: string | string[]): string | string[];
17
+
18
+ protected _isEmpty(): boolean;
19
+ }
20
+
21
+ export declare function CustomChoiceGroupImplementation<T extends Constructor<LitElement>>(
22
+ superclass: T,
23
+ ): T &
24
+ Constructor<CustomChoiceGroupHost> &
25
+ Pick<typeof CustomChoiceGroupHost, keyof typeof CustomChoiceGroupHost> &
26
+ Constructor<ChoiceGroupHost> &
27
+ Pick<typeof ChoiceGroupHost, keyof typeof ChoiceGroupHost> &
28
+ Pick<typeof LitElement, keyof typeof LitElement>;
29
+
30
+ export type CustomChoiceGroupMixin = typeof CustomChoiceGroupImplementation;
@@ -1,2 +1,3 @@
1
1
  export * from './ChoiceInputMixinTypes.js';
2
2
  export * from './ChoiceGroupMixinTypes.js';
3
+ export * from './CustomChoiceGroupMixinTypes.js';
@@ -5,6 +5,7 @@ import { css, html, LitElement, render } from 'lit';
5
5
  export class SbActionLogger extends LitElement {
6
6
  static get properties() {
7
7
  return {
8
+ // eslint-disable-next-line lit/no-native-attributes
8
9
  title: { type: String, reflect: true },
9
10
  simple: { type: Boolean, reflect: true },
10
11
  __logCounter: { type: Number },
@@ -57,14 +57,6 @@ export class LionIcon extends LitElement {
57
57
  type: String,
58
58
  attribute: 'icon-id',
59
59
  },
60
- /**
61
- * @private
62
- */
63
- role: {
64
- type: String,
65
- attribute: 'role',
66
- reflect: true,
67
- },
68
60
  };
69
61
  }
70
62
 
@@ -101,7 +93,6 @@ export class LionIcon extends LitElement {
101
93
 
102
94
  constructor() {
103
95
  super();
104
- this.role = 'img';
105
96
  this.ariaLabel = '';
106
97
  this.iconId = '';
107
98
  /**
@@ -131,6 +122,7 @@ export class LionIcon extends LitElement {
131
122
  // ensures that aria-hidden is set if there is no aria-label attribute
132
123
  this._onLabelChanged();
133
124
  super.connectedCallback();
125
+ this.setAttribute('role', 'img');
134
126
  }
135
127
 
136
128
  /**
@@ -185,10 +185,12 @@ describe('lion-icon', () => {
185
185
  try {
186
186
  icons.addIconResolver(
187
187
  'foo',
188
+ // eslint-disable-next-line no-promise-executor-return
188
189
  () => new Promise(resolve => setTimeout(() => resolve(heartSvg), 10)),
189
190
  );
190
191
  icons.addIconResolver(
191
192
  'bar',
193
+ // eslint-disable-next-line no-promise-executor-return
192
194
  () => new Promise(resolve => setTimeout(() => resolve(hammerSvg), 4)),
193
195
  );
194
196
  const el = await fixture(html`<lion-icon icon-id="foo:lorem:ipsum"></lion-icon>`);
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies */
2
2
  import { LionCalendar } from '@lion/ui/calendar.js';
3
+ import { uuid } from '@lion/ui/core.js';
3
4
  import { ScopedElementsMixin } from '@open-wc/scoped-elements';
4
5
  import { html, css } from 'lit';
5
6
  import { ifDefined } from 'lit/directives/if-defined.js';
@@ -149,7 +150,7 @@ export class LionInputDatepicker extends ScopedElementsMixin(
149
150
  constructor() {
150
151
  super();
151
152
  /** @private */
152
- this.__invokerId = this.__createUniqueIdForA11y();
153
+ this.__invokerId = uuid(this.localName);
153
154
  /** @protected */
154
155
  this._calendarInvokerSlot = 'suffix';
155
156
 
@@ -169,11 +170,6 @@ export class LionInputDatepicker extends ScopedElementsMixin(
169
170
  this._onCalendarUserSelectedChanged = this._onCalendarUserSelectedChanged.bind(this);
170
171
  }
171
172
 
172
- /** @private */
173
- __createUniqueIdForA11y() {
174
- return `${this.localName}-${Math.random().toString(36).substr(2, 10)}`;
175
- }
176
-
177
173
  /**
178
174
  * @param {string} [name]
179
175
  * @param {unknown} [oldValue]
@@ -19,10 +19,14 @@ describe('<lion-input-range>', () => {
19
19
  <lion-input-range .modelValue=${75} unit="${`%`}"></lion-input-range>
20
20
  `);
21
21
  expect(
22
- /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.input-range__value')).innerText,
22
+ /** @type {HTMLElement} */ (
23
+ /** @type {ShadowRoot} */ (el.shadowRoot).querySelector('.input-range__value')
24
+ ).innerText,
23
25
  ).to.equal('75');
24
26
  expect(
25
- /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.input-range__unit')).innerText,
27
+ /** @type {HTMLElement} */ (
28
+ /** @type {ShadowRoot} */ (el.shadowRoot).querySelector('.input-range__unit')
29
+ ).innerText,
26
30
  ).to.equal('%');
27
31
  });
28
32
 
@@ -30,12 +34,14 @@ describe('<lion-input-range>', () => {
30
34
  const el = await fixture(`<lion-input-range min="100" max="200"></lion-input-range>`);
31
35
  expect(el.shadowRoot?.querySelectorAll('.input-range__limits span').length).to.equal(2);
32
36
  expect(
33
- /** @type {HTMLElement} */ (el.shadowRoot?.querySelectorAll('.input-range__limits span')[0])
34
- .innerText,
37
+ /** @type {HTMLElement} */ (
38
+ /** @type {ShadowRoot} */ (el.shadowRoot).querySelectorAll('.input-range__limits span')[0]
39
+ ).innerText,
35
40
  ).to.equal(el.min.toString());
36
41
  expect(
37
- /** @type {HTMLElement} */ (el.shadowRoot?.querySelectorAll('.input-range__limits span')[1])
38
- .innerText,
42
+ /** @type {HTMLElement} */ (
43
+ /** @type {ShadowRoot} */ (el.shadowRoot).querySelectorAll('.input-range__limits span')[1]
44
+ ).innerText,
39
45
  ).to.equal(el.max.toString());
40
46
  });
41
47
 
@@ -7,10 +7,6 @@ import { FormRegistrarPortalMixin } from '@lion/ui/form-core.js';
7
7
  export class LionOptions extends FormRegistrarPortalMixin(LitElement) {
8
8
  static get properties() {
9
9
  return {
10
- role: {
11
- type: String,
12
- reflect: true,
13
- },
14
10
  tabIndex: {
15
11
  type: Number,
16
12
  reflect: true,
@@ -21,10 +17,14 @@ export class LionOptions extends FormRegistrarPortalMixin(LitElement) {
21
17
 
22
18
  constructor() {
23
19
  super();
24
- this.role = 'listbox';
25
20
  this.tabIndex = 0;
26
21
  }
27
22
 
23
+ connectedCallback() {
24
+ super.connectedCallback();
25
+ this.setAttribute('role', 'listbox');
26
+ }
27
+
28
28
  createRenderRoot() {
29
29
  return this;
30
30
  }
@@ -196,22 +196,6 @@ const ListboxMixinImplementation = superclass =>
196
196
  return this._listboxNode;
197
197
  }
198
198
 
199
- /**
200
- * @override ChoiceGroupMixin
201
- */
202
- get serializedValue() {
203
- return this.modelValue;
204
- }
205
-
206
- // Duplicating from FormGroupMixin, because you cannot independently inherit/override getter + setter.
207
- // If you override one, gotta override the other, they go in pairs.
208
- /**
209
- * @override ChoiceGroupMixin
210
- */
211
- set serializedValue(value) {
212
- super.serializedValue = value;
213
- }
214
-
215
199
  get activeIndex() {
216
200
  return this.formElements.findIndex(el => el.active === true);
217
201
  }
@@ -473,6 +457,7 @@ const ListboxMixinImplementation = superclass =>
473
457
  }
474
458
 
475
459
  clear() {
460
+ super.clear();
476
461
  this.setCheckedIndex(-1);
477
462
  this.resetInteractionState();
478
463
  }
@@ -12,6 +12,6 @@ describe('lion-options', () => {
12
12
  <lion-options .registrationTarget=${registrationTargetEl}></lion-options>
13
13
  `)
14
14
  );
15
- expect(el.role).to.equal('listbox');
15
+ expect(el.getAttribute('role')).to.equal('listbox');
16
16
  });
17
17
  });
@@ -163,7 +163,7 @@ export function runListboxMixinSuite(customConfig = {}) {
163
163
  });
164
164
 
165
165
  it('requests update for modelValue when checkedIndex changes', async () => {
166
- const el = await fixture(html`
166
+ const el = /** @type {LionListbox} */ await fixture(html`
167
167
  <${tag} name="gender" .modelValue=${'other'}>
168
168
  <${optionTag} .choiceValue=${'male'}></${optionTag}>
169
169
  <${optionTag} .choiceValue=${'female'}></${optionTag}>
@@ -1,5 +1,6 @@
1
1
  import { normalizeIntlDate } from './utils/normalizeIntlDate.js';
2
2
  import { forceShortMonthNamesForEnGb } from './utils/forceShortMonthNamesForEnGb.js';
3
+ import { forceDotsForNlnl } from './utils/forceDotsForNlnl.js';
3
4
 
4
5
  /** @type {Object.<string, Object.<string,string[]>>} */
5
6
  const monthsLocaleCache = {};
@@ -29,6 +30,8 @@ export function getMonthNames({ locale = 'en-GB', style = 'long' } = {}) {
29
30
  }
30
31
  if (locale === 'en-GB' && style === 'short') {
31
32
  months = forceShortMonthNamesForEnGb(months);
33
+ } else if (locale === 'nl-NL' && style === 'short') {
34
+ months = forceDotsForNlnl(months);
32
35
  }
33
36
  monthsLocaleCache[locale] = monthsLocaleCache[locale] || {};
34
37
  monthsLocaleCache[locale][style] = months;
@@ -58,8 +58,12 @@ export function parseDate(dateString) {
58
58
  }
59
59
 
60
60
  const [year, month, day] = parsedString.split('/').map(Number);
61
- const parsedDate = new Date(new Date(year, month - 1, day));
61
+ let correctedYear = year;
62
+ if (year < 50) {
63
+ correctedYear = 2000 + year;
64
+ }
62
65
 
66
+ const parsedDate = new Date(new Date(correctedYear, month - 1, day));
63
67
  // Check if parsedDate is not `Invalid Date` or that the date has changed (e.g. the not existing 31.02.2020)
64
68
  if (
65
69
  year > 0 &&
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @param {string[]} months
3
+ */
4
+ export function forceDotsForNlnl(months) {
5
+ return months.map(m => {
6
+ if (m !== 'mei' && !m.endsWith('.')) {
7
+ return `${m}.`;
8
+ }
9
+ return m;
10
+ });
11
+ }
@@ -13,7 +13,7 @@ export function forceAddGroupSeparators(formattedParts, groupSeparator) {
13
13
  let integerPart;
14
14
 
15
15
  for (let i = 0; i < formattedParts.length; i += 1) {
16
- if (formattedParts[i].type === 'integer') {
16
+ if (formattedParts[i].type === 'integer' && formattedParts[i].value.length > 3) {
17
17
  firstPart = formattedParts.splice(0, i);
18
18
  integerPart = formattedParts.splice(0, 1);
19
19
  }
@@ -66,6 +66,8 @@ export function forceAddGroupSeparators(formattedParts, groupSeparator) {
66
66
  if (firstPart) {
67
67
  concatArray = firstPart.concat(numberArray, formattedParts);
68
68
  }
69
+ } else {
70
+ return formattedParts;
69
71
  }
70
72
  return concatArray;
71
73
  }
@@ -17,6 +17,7 @@ const CURRENCY_CODE_SYMBOL_MAP = {
17
17
  * @param {string[]} currencyScope
18
18
  * @returns {FormatNumberPart[]}
19
19
  */
20
+ // eslint-disable-next-line default-param-last
20
21
  export function forceSymbols(formattedParts, { currency, currencyDisplay } = {}, currencyScope) {
21
22
  const result = formattedParts;
22
23
  if (
@@ -44,6 +45,7 @@ export function forceSymbols(formattedParts, { currency, currencyDisplay } = {},
44
45
  */
45
46
  export function forceFRBESymbols(
46
47
  formattedParts,
48
+ // eslint-disable-next-line default-param-last
47
49
  { currency, currencyDisplay } = {},
48
50
  currencyScope,
49
51
  ) {
@@ -17,6 +17,7 @@ import { forceFRBESymbols, forceSymbols } from './forceSymbols.js';
17
17
  * @param {string} _locale
18
18
  * @returns {FormatNumberPart[]}
19
19
  */
20
+ // eslint-disable-next-line default-param-last
20
21
  export function normalizeIntl(formattedParts, options = {}, _locale) {
21
22
  let normalize = forceNormalSpaces(formattedParts);
22
23
  // Dutch and Belgian currency must be moved to end of number
@@ -25,6 +25,7 @@ describe('getMonthNames', () => {
25
25
  // expect(getMonthNames({ locale: 'en-GB', style: 'short' })).to.deep.equal(
26
26
  // s`Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec`,
27
27
  // );
28
+ // Chromium default is leading
28
29
  expect(getMonthNames({ locale: 'nl-NL', style: 'short' })).to.deep.equal(
29
30
  s`jan. feb. mrt. apr. mei jun. jul. aug. sep. okt. nov. dec.`,
30
31
  );
@@ -23,6 +23,12 @@ describe('parseDate()', () => {
23
23
  localizeTearDown();
24
24
  });
25
25
 
26
+ it('handles year correctly', () => {
27
+ expect(equalsDate(parseDate('1/11/1'), new Date('2001/11/01'))).to.equal(true);
28
+ expect(equalsDate(parseDate('1/11/49'), new Date('2049/11/01'))).to.equal(true);
29
+ expect(equalsDate(parseDate('1/11/50'), new Date('1950/11/01'))).to.equal(true);
30
+ });
31
+
26
32
  it('adds leading zeros', () => {
27
33
  expect(equalsDate(parseDate('1-1-1979'), new Date('1979/01/01'))).to.equal(true);
28
34
  expect(equalsDate(parseDate('1-11-1979'), new Date('1979/11/01'))).to.equal(true);
@@ -56,5 +56,6 @@ export async function fakeImport(path, ms = 0) {
56
56
  setTimeout(() => resolveOrReject(result, resolve, reject), ms);
57
57
  });
58
58
  }
59
+ // eslint-disable-next-line no-promise-executor-return
59
60
  return new Promise((resolve, reject) => resolveOrReject(result, resolve, reject));
60
61
  }
@@ -1,3 +1,5 @@
1
+ import { browserDetection } from '@lion/ui/core.js';
2
+
1
3
  /**
2
4
  * @typedef {import('lit').CSSResult} CSSResult
3
5
  * @typedef {import('./OverlayController.js').OverlayController} OverlayController
@@ -5,19 +7,6 @@
5
7
 
6
8
  import { overlayDocumentStyle } from './overlayDocumentStyle.js';
7
9
 
8
- // Export this as protected var, so that we can easily mock it in tests
9
- // TODO: combine with browserDetection of core?
10
- export const _browserDetection = {
11
- isIOS: /iPhone|iPad|iPod/i.test(navigator.userAgent),
12
- isMacSafari:
13
- navigator.vendor &&
14
- navigator.vendor.indexOf('Apple') > -1 &&
15
- navigator.userAgent &&
16
- navigator.userAgent.indexOf('CriOS') === -1 &&
17
- navigator.userAgent.indexOf('FxiOS') === -1 &&
18
- navigator.appVersion.indexOf('Mac') !== -1,
19
- };
20
-
21
10
  /**
22
11
  * `OverlaysManager` which manages overlays which are rendered into the body
23
12
  */
@@ -182,7 +171,7 @@ export class OverlaysManager {
182
171
 
183
172
  // eslint-disable-next-line class-methods-use-this
184
173
  requestToPreventScroll() {
185
- const { isIOS, isMacSafari } = _browserDetection;
174
+ const { isIOS, isMacSafari } = browserDetection;
186
175
  // no check as classList will dedupe it anyways
187
176
  document.body.classList.add('overlays-scroll-lock');
188
177
  if (isIOS || isMacSafari) {
@@ -203,7 +192,7 @@ export class OverlaysManager {
203
192
  return;
204
193
  }
205
194
 
206
- const { isIOS, isMacSafari } = _browserDetection;
195
+ const { isIOS, isMacSafari } = browserDetection;
207
196
  document.body.classList.remove('overlays-scroll-lock');
208
197
  if (isIOS || isMacSafari) {
209
198
  document.body.classList.remove('overlays-scroll-lock-ios-fix');