@lion/ui 0.11.0 → 0.11.2

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 (238) hide show
  1. package/README.md +4 -4
  2. package/components/checkbox-group/src/LionCheckboxIndeterminate.js +39 -16
  3. package/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.js +189 -1
  4. package/components/combobox/test/lion-combobox.test.js +75 -1
  5. package/components/form-core/src/FocusMixin.js +4 -4
  6. package/components/form-core/src/LionField.js +1 -1
  7. package/components/localize/src/date/parseDate.js +6 -1
  8. package/components/localize/src/date/utils/checkYearFormat.js +8 -0
  9. package/components/localize/test/date/parseDate.test.js +6 -0
  10. package/components/localize/types/LocalizeMixinTypes.ts +0 -5
  11. package/components/select-rich/src/LionSelectRich.js +1 -1
  12. package/components/select-rich/test/lion-select-rich.test.js +13 -1
  13. package/custom-elements.json +47758 -47690
  14. package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts +10 -0
  15. package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts.map +1 -1
  16. package/dist-types/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.d.ts.map +1 -1
  17. package/dist-types/components/localize/src/date/parseDate.d.ts.map +1 -1
  18. package/dist-types/components/localize/src/date/utils/checkYearFormat.d.ts +7 -0
  19. package/dist-types/components/localize/src/date/utils/checkYearFormat.d.ts.map +1 -0
  20. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts +0 -4
  21. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts.map +1 -1
  22. package/dist-types/tsconfig.tsbuildinfo +1 -1
  23. package/docs/components/accordion/api-table.md +19 -7
  24. package/docs/components/accordion/index.md +10 -0
  25. package/docs/components/accordion/overview.md +13 -1
  26. package/docs/components/accordion/reference.md +13 -1
  27. package/docs/components/accordion/use-cases.md +13 -1
  28. package/docs/components/button/api-table.md +40 -31
  29. package/docs/components/button/examples.md +13 -1
  30. package/docs/components/button/extensions.md +13 -1
  31. package/docs/components/button/index.md +11 -1
  32. package/docs/components/button/overview.md +13 -1
  33. package/docs/components/button/use-cases.md +13 -1
  34. package/docs/components/calendar/api-table.md +26 -14
  35. package/docs/components/calendar/index.md +11 -1
  36. package/docs/components/calendar/overview.md +13 -1
  37. package/docs/components/calendar/use-cases.md +13 -1
  38. package/docs/components/checkbox-group/api-table.md +318 -330
  39. package/docs/components/checkbox-group/index.md +11 -1
  40. package/docs/components/checkbox-group/overview.md +15 -3
  41. package/docs/components/checkbox-group/use-cases.md +13 -1
  42. package/docs/components/collapsible/api-table.md +18 -6
  43. package/docs/components/collapsible/examples.md +13 -1
  44. package/docs/components/collapsible/index.md +11 -1
  45. package/docs/components/collapsible/overview.md +14 -2
  46. package/docs/components/collapsible/use-cases.md +13 -1
  47. package/docs/components/combobox/api-table.md +163 -155
  48. package/docs/components/combobox/extensions.md +13 -1
  49. package/docs/components/combobox/index.md +11 -1
  50. package/docs/components/combobox/overview.md +14 -2
  51. package/docs/components/combobox/use-cases.md +14 -2
  52. package/docs/components/dialog/api-table.md +25 -13
  53. package/docs/components/dialog/index.md +11 -1
  54. package/docs/components/dialog/overview.md +14 -2
  55. package/docs/components/dialog/use-cases.md +13 -1
  56. package/docs/components/drawer/api-table.md +22 -11
  57. package/docs/components/drawer/index.md +11 -1
  58. package/docs/components/drawer/overview.md +13 -1
  59. package/docs/components/drawer/use-cases.md +13 -1
  60. package/docs/components/fieldset/api-table.md +117 -119
  61. package/docs/components/fieldset/index.md +11 -1
  62. package/docs/components/fieldset/overview.md +18 -6
  63. package/docs/components/fieldset/use-cases.md +13 -1
  64. package/docs/components/form/api-table.md +12 -1
  65. package/docs/components/form/index.md +11 -1
  66. package/docs/components/form/overview.md +16 -4
  67. package/docs/components/form/use-cases.md +15 -3
  68. package/docs/components/icon/api-table.md +16 -4
  69. package/docs/components/icon/index.md +11 -1
  70. package/docs/components/icon/overview.md +13 -1
  71. package/docs/components/icon/use-cases.md +14 -2
  72. package/docs/components/index.md +11 -1
  73. package/docs/components/input/api-table.md +123 -121
  74. package/docs/components/input/index.md +11 -1
  75. package/docs/components/input/overview.md +14 -2
  76. package/docs/components/input/use-cases.md +13 -1
  77. package/docs/components/input-amount/api-table.md +142 -143
  78. package/docs/components/input-amount/index.md +11 -1
  79. package/docs/components/input-amount/overview.md +16 -4
  80. package/docs/components/input-amount/use-cases.md +13 -1
  81. package/docs/components/input-date/api-table.md +123 -121
  82. package/docs/components/input-date/index.md +11 -1
  83. package/docs/components/input-date/overview.md +16 -4
  84. package/docs/components/input-date/use-cases.md +13 -1
  85. package/docs/components/input-datepicker/api-table.md +161 -161
  86. package/docs/components/input-datepicker/index.md +11 -1
  87. package/docs/components/input-datepicker/overview.md +18 -6
  88. package/docs/components/input-datepicker/use-cases.md +13 -1
  89. package/docs/components/input-email/api-table.md +123 -121
  90. package/docs/components/input-email/index.md +11 -1
  91. package/docs/components/input-email/overview.md +15 -3
  92. package/docs/components/input-email/use-cases.md +13 -1
  93. package/docs/components/input-file/api-table.md +145 -144
  94. package/docs/components/input-file/index.md +11 -1
  95. package/docs/components/input-file/overview.md +14 -2
  96. package/docs/components/input-file/use-cases.md +13 -1
  97. package/docs/components/input-iban/api-table.md +123 -121
  98. package/docs/components/input-iban/index.md +11 -1
  99. package/docs/components/input-iban/overview.md +15 -3
  100. package/docs/components/input-iban/use-cases.md +13 -1
  101. package/docs/components/input-range/api-table.md +129 -127
  102. package/docs/components/input-range/index.md +11 -1
  103. package/docs/components/input-range/overview.md +15 -3
  104. package/docs/components/input-range/use-cases.md +13 -1
  105. package/docs/components/input-stepper/api-table.md +136 -134
  106. package/docs/components/input-stepper/index.md +11 -1
  107. package/docs/components/input-stepper/overview.md +14 -2
  108. package/docs/components/input-stepper/use-cases.md +13 -1
  109. package/docs/components/input-tel/api-table.md +131 -129
  110. package/docs/components/input-tel/index.md +11 -1
  111. package/docs/components/input-tel/overview.md +14 -2
  112. package/docs/components/input-tel/use-cases.md +19 -7
  113. package/docs/components/input-tel-dropdown/api-table.md +138 -136
  114. package/docs/components/input-tel-dropdown/extensions.md +13 -1
  115. package/docs/components/input-tel-dropdown/index.md +11 -1
  116. package/docs/components/input-tel-dropdown/overview.md +14 -2
  117. package/docs/components/input-tel-dropdown/use-cases.md +13 -1
  118. package/docs/components/listbox/api-table.md +139 -132
  119. package/docs/components/listbox/index.md +11 -1
  120. package/docs/components/listbox/overview.md +13 -1
  121. package/docs/components/listbox/use-cases.md +14 -2
  122. package/docs/components/pagination/api-table.md +18 -6
  123. package/docs/components/pagination/index.md +11 -1
  124. package/docs/components/pagination/overview.md +13 -1
  125. package/docs/components/pagination/use-cases.md +13 -1
  126. package/docs/components/progress-indicator/api-table.md +22 -11
  127. package/docs/components/progress-indicator/examples.md +13 -1
  128. package/docs/components/progress-indicator/index.md +11 -1
  129. package/docs/components/progress-indicator/overview.md +13 -1
  130. package/docs/components/radio-group/api-table.md +247 -259
  131. package/docs/components/radio-group/index.md +11 -1
  132. package/docs/components/radio-group/overview.md +16 -4
  133. package/docs/components/radio-group/use-cases.md +13 -1
  134. package/docs/components/select/api-table.md +119 -117
  135. package/docs/components/select/index.md +11 -1
  136. package/docs/components/select/overview.md +13 -1
  137. package/docs/components/select/use-cases.md +14 -2
  138. package/docs/components/select-rich/api-table.md +182 -179
  139. package/docs/components/select-rich/examples.md +13 -1
  140. package/docs/components/select-rich/index.md +11 -1
  141. package/docs/components/select-rich/overview.md +13 -1
  142. package/docs/components/select-rich/use-cases.md +14 -2
  143. package/docs/components/steps/api-table.md +19 -7
  144. package/docs/components/steps/index.md +11 -1
  145. package/docs/components/steps/overview.md +13 -1
  146. package/docs/components/steps/use-cases.md +13 -1
  147. package/docs/components/switch/api-table.md +130 -129
  148. package/docs/components/switch/index.md +11 -1
  149. package/docs/components/switch/overview.md +13 -1
  150. package/docs/components/switch/reference.md +13 -1
  151. package/docs/components/switch/use-cases.md +13 -1
  152. package/docs/components/tabs/api-table.md +14 -2
  153. package/docs/components/tabs/examples.md +14 -2
  154. package/docs/components/tabs/index.md +11 -1
  155. package/docs/components/tabs/overview.md +13 -1
  156. package/docs/components/tabs/use-cases.md +13 -1
  157. package/docs/components/textarea/api-table.md +127 -125
  158. package/docs/components/textarea/index.md +11 -1
  159. package/docs/components/textarea/overview.md +13 -1
  160. package/docs/components/textarea/use-cases.md +13 -1
  161. package/docs/components/tooltip/api-table.md +26 -14
  162. package/docs/components/tooltip/examples.md +13 -1
  163. package/docs/components/tooltip/index.md +11 -1
  164. package/docs/components/tooltip/overview.md +13 -1
  165. package/docs/components/tooltip/use-cases.md +13 -1
  166. package/docs/fundamentals/index.md +4 -4
  167. package/docs/fundamentals/node-tools/babel-plugin-extend-docs/index.md +12 -0
  168. package/docs/fundamentals/node-tools/babel-plugin-extend-docs/overview.md +13 -0
  169. package/docs/fundamentals/node-tools/index.md +10 -0
  170. package/docs/fundamentals/node-tools/providence-analytics/index.md +12 -0
  171. package/docs/fundamentals/node-tools/providence-analytics/overview.md +13 -0
  172. package/docs/fundamentals/node-tools/publish-docs/index.md +12 -0
  173. package/docs/fundamentals/node-tools/publish-docs/overview.md +16 -3
  174. package/docs/fundamentals/node-tools/remark-extend/index.md +12 -0
  175. package/docs/fundamentals/node-tools/remark-extend/overview.md +13 -0
  176. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/index.md +12 -0
  177. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +15 -2
  178. package/docs/fundamentals/rationales/TypeScript.md +12 -0
  179. package/docs/fundamentals/rationales/accessibility.md +13 -1
  180. package/docs/fundamentals/rationales/index.md +10 -0
  181. package/docs/fundamentals/rationales/side-effects.md +12 -0
  182. package/docs/fundamentals/rationales/versioning.md +12 -0
  183. package/docs/fundamentals/systems/core/SlotMixin.md +14 -1
  184. package/docs/fundamentals/systems/core/api-table.md +22 -12
  185. package/docs/fundamentals/systems/core/index.md +12 -0
  186. package/docs/fundamentals/systems/core/overview.md +14 -1
  187. package/docs/fundamentals/systems/form/api-table.md +91 -83
  188. package/docs/fundamentals/systems/form/formatting-and-parsing.md +17 -4
  189. package/docs/fundamentals/systems/form/index.md +12 -0
  190. package/docs/fundamentals/systems/form/interaction-states.md +13 -0
  191. package/docs/fundamentals/systems/form/model-value.md +14 -1
  192. package/docs/fundamentals/systems/form/overview.md +41 -28
  193. package/docs/fundamentals/systems/form/styling.md +13 -0
  194. package/docs/fundamentals/systems/form/use-cases.md +13 -0
  195. package/docs/fundamentals/systems/form/validate.md +15 -2
  196. package/docs/fundamentals/systems/icon/index.md +12 -0
  197. package/docs/fundamentals/systems/icon/overview.md +13 -0
  198. package/docs/fundamentals/systems/index.md +10 -0
  199. package/docs/fundamentals/systems/localize/api-table.md +33 -20
  200. package/docs/fundamentals/systems/localize/dates.md +13 -0
  201. package/docs/fundamentals/systems/localize/index.md +12 -0
  202. package/docs/fundamentals/systems/localize/numbers.md +13 -0
  203. package/docs/fundamentals/systems/localize/overview.md +17 -4
  204. package/docs/fundamentals/systems/localize/rationale.md +13 -0
  205. package/docs/fundamentals/systems/localize/text.md +13 -0
  206. package/docs/fundamentals/systems/localize/use-cases.md +13 -0
  207. package/docs/fundamentals/systems/overlays/_configuration-positioning.md +11 -0
  208. package/docs/fundamentals/systems/overlays/_edge-cases.md +10 -0
  209. package/docs/fundamentals/systems/overlays/api-table.md +59 -47
  210. package/docs/fundamentals/systems/overlays/configuration.md +15 -2
  211. package/docs/fundamentals/systems/overlays/index.md +12 -0
  212. package/docs/fundamentals/systems/overlays/overview.md +18 -5
  213. package/docs/fundamentals/systems/overlays/rationale.md +13 -0
  214. package/docs/fundamentals/systems/overlays/use-cases.md +15 -2
  215. package/docs/fundamentals/tools/ajax/index.md +12 -0
  216. package/docs/fundamentals/tools/ajax/overview.md +13 -0
  217. package/docs/fundamentals/tools/ajax/use-cases.md +13 -0
  218. package/docs/fundamentals/tools/helpers/action-logger.md +13 -0
  219. package/docs/fundamentals/tools/helpers/index.md +12 -0
  220. package/docs/fundamentals/tools/helpers/overview.md +14 -1
  221. package/docs/fundamentals/tools/index.md +10 -0
  222. package/docs/fundamentals/tools/singleton-manager/example-complex/index.md +13 -0
  223. package/docs/fundamentals/tools/singleton-manager/example-fail/index.md +13 -0
  224. package/docs/fundamentals/tools/singleton-manager/example-success/index.md +13 -0
  225. package/docs/fundamentals/tools/singleton-manager/index.md +12 -0
  226. package/docs/fundamentals/tools/singleton-manager/overview.md +17 -4
  227. package/docs/guides/how-to/create-a-custom-field.md +18 -6
  228. package/docs/guides/how-to/create-a-form.md +17 -5
  229. package/docs/guides/how-to/extend-a-native-input.md +15 -3
  230. package/docs/guides/how-to/get-started.md +13 -1
  231. package/docs/guides/how-to/index.md +11 -1
  232. package/docs/guides/index.md +19 -9
  233. package/docs/guides/principles/definitions-and-terms.md +13 -1
  234. package/docs/guides/principles/index.md +16 -6
  235. package/docs/guides/principles/scoped-elements.md +13 -1
  236. package/docs/guides/principles/styling.md +14 -2
  237. package/docs/guides/principles/subclasser-apis.md +13 -1
  238. package/package.json +1 -1
package/README.md CHANGED
@@ -37,15 +37,15 @@
37
37
  <a href="https://lion.js.org/blog/">Blog</a>
38
38
  </p>
39
39
 
40
- **Lion is a set of highly performant, accessible and flexible Web Components.!**
40
+ **Lion is a set of highly performant, accessible, and flexible Web Components.**
41
41
 
42
42
  They provide an unopinionated, white-label layer that can be extended to your own layer of components.
43
43
 
44
44
  - **High Performance:** Focused on great performance in all relevant browsers with a minimal number of dependencies.
45
45
  - **Accessibility:** Aimed at compliance with the WCAG 2.2 AA standard to create components that are accessible for everybody.
46
46
  - **Flexibility:** Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs.
47
- - **Modern Code:** Lion is distributes as pure es modules.
48
- - **Exposes functions/classes and Web Components:** Ships a functionality in it's most appropriate form.
47
+ - **Modern Code:** Lion is distributed as pure es modules.
48
+ - **Exposes functions/classes and Web Components:** Ships a functionality in its most appropriate form.
49
49
 
50
50
  > Note: Our demo examples might look simple and not very stylish. This is on purpose. They are designed to be basic so you can easily add your own styles to them to match your intended design, without dealing with styles that are already there.
51
51
 
@@ -113,7 +113,7 @@ You can also use the lion elements directly, although this is likely not a commo
113
113
 
114
114
  ## Issues
115
115
 
116
- If you encounter an issue with any of the packages we are offering please open a [new bug issue](https://github.com/ing-bank/lion/issues/new?assignees=&labels=&template=bug_report.md&title=). Be sure to include a description of the expected and the current behavior - additional adding a [reproduction](https://webcomponents.dev/edit/kpZmz1CJN580OaXsk56f?pm=1) always helps.
116
+ If you encounter an issue with any of the packages we are offering please open a [new bug issue](https://github.com/ing-bank/lion/issues/new?assignees=&labels=&template=bug_report.md&title=). Be sure to include a description of the expected and the current behavior - additionally adding a [reproduction](https://webcomponents.dev/edit/kpZmz1CJN580OaXsk56f?pm=1) always helps.
117
117
 
118
118
  ## Feature requests
119
119
 
@@ -49,17 +49,7 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
49
49
  * @protected
50
50
  */
51
51
  get _subCheckboxes() {
52
- let checkboxes = [];
53
- if (
54
- this._checkboxGroupNode &&
55
- this._checkboxGroupNode.formElements &&
56
- this._checkboxGroupNode.formElements.length > 0
57
- ) {
58
- checkboxes = this._checkboxGroupNode.formElements.filter(
59
- checkbox => checkbox !== this && this.contains(checkbox),
60
- );
61
- }
62
- return /** @type LionCheckbox[] */ (checkboxes);
52
+ return /** @type LionCheckbox[] */ (this.__subCheckboxes);
63
53
  }
64
54
 
65
55
  _storeIndeterminateState() {
@@ -97,9 +87,14 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
97
87
  this.indeterminate = false;
98
88
  this.checked = false;
99
89
  break;
100
- default:
90
+ default: {
101
91
  this.indeterminate = true;
102
- this.checked = false;
92
+ const disabledUncheckedElements = subCheckboxes.filter(
93
+ checkbox => checkbox.disabled && checkbox.checked === false,
94
+ );
95
+ this.checked =
96
+ subCheckboxes.length - checkedElements.length - disabledUncheckedElements.length === 0;
97
+ }
103
98
  }
104
99
  this.updateComplete.then(() => {
105
100
  this.__settingOwnChecked = false;
@@ -155,13 +150,12 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
155
150
  subCheckboxes.length > 0 && subCheckboxes.length === checkedElements.length;
156
151
  const allDisabled =
157
152
  subCheckboxes.length > 0 && subCheckboxes.length === disabledElements.length;
158
- const hasDisabledElements = disabledElements.length > 0;
159
153
 
160
154
  if (allDisabled) {
161
155
  this.checked = allChecked;
162
156
  }
163
157
 
164
- if (this.indeterminate && (this.mixedState || hasDisabledElements)) {
158
+ if (this.indeterminate && this.mixedState) {
165
159
  this._subCheckboxes.forEach((checkbox, i) => {
166
160
  // eslint-disable-next-line no-param-reassign
167
161
  checkbox.checked = this._indeterminateSubStates[i];
@@ -211,6 +205,7 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
211
205
  if (!(/** @type {HTMLElement} */ (ev.target).hasAttribute('role'))) {
212
206
  /** @type {HTMLElement} */ (ev.target)?.setAttribute('role', 'listitem');
213
207
  }
208
+ this.__addToSubCheckboxes(/** @type {CustomEvent} */ (ev).detail.element);
214
209
  this._setOwnCheckedState();
215
210
  }
216
211
 
@@ -223,6 +218,26 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
223
218
  if (/** @type {HTMLElement} */ (ev.target).getAttribute('role') === 'listitem') {
224
219
  /** @type {HTMLElement} */ (ev.target)?.removeAttribute('role');
225
220
  }
221
+ this.__removeFromSubCheckboxes(/** @type {CustomEvent} */ (ev).detail.element);
222
+ }
223
+
224
+ /**
225
+ * @param {HTMLElement} element
226
+ */
227
+ __addToSubCheckboxes(element) {
228
+ if (element !== this && this.contains(element)) {
229
+ this.__subCheckboxes.push(element);
230
+ }
231
+ }
232
+
233
+ /**
234
+ * @param {HTMLElement} element
235
+ */
236
+ __removeFromSubCheckboxes(element) {
237
+ const index = this.__subCheckboxes.indexOf(element);
238
+ if (index !== -1) {
239
+ this.__subCheckboxes.splice(index, 1);
240
+ }
226
241
  }
227
242
 
228
243
  constructor() {
@@ -230,6 +245,8 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
230
245
  this.indeterminate = false;
231
246
  this._onRequestToAddFormElement = this._onRequestToAddFormElement.bind(this);
232
247
  this.__onModelValueChanged = this.__onModelValueChanged.bind(this);
248
+ /** @type {HTMLElement[]} */
249
+ this.__subCheckboxes = [];
233
250
  /** @type {boolean[]} */
234
251
  this._indeterminateSubStates = [];
235
252
  this.mixedState = false;
@@ -259,7 +276,13 @@ export class LionCheckboxIndeterminate extends LionCheckbox {
259
276
  /** @param {import('lit-element').PropertyValues } changedProperties */
260
277
  updated(changedProperties) {
261
278
  super.updated(changedProperties);
262
- if (changedProperties.has('indeterminate')) {
279
+
280
+ // When 1. sub checkboxes have disabled elements and 2. some elements are checked while the others are unchecked
281
+ // both this._inputNode.indeterminate and this.indeterminate are already true.
282
+ // If user clicks the input node, this._inputNode.indeterminate is turned to false by the browser
283
+ // while this.indeterminate is still true and the 'indeterminate' is not in the changedProperties
284
+ // because it hasn't been updated (true -> true) but checked would have been updated (false -> true).
285
+ if (changedProperties.has('indeterminate') || changedProperties.has('checked')) {
263
286
  this._inputNode.indeterminate = this.indeterminate;
264
287
  }
265
288
  }
@@ -205,7 +205,84 @@ export function runCheckboxIndeterminateSuite(customConfig) {
205
205
 
206
206
  // Assert
207
207
  expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.true;
208
- expect(elIndeterminate?.checked).to.be.false;
208
+ expect(elIndeterminate?.checked).to.be.true;
209
+ });
210
+
211
+ it('should be checked when all children are prechecked', async () => {
212
+ // Arrange
213
+ const el = /** @type {LionCheckboxGroup} */ await fixture(html`
214
+ <${groupTag} name="scientists[]">
215
+ <${tag} label="Favorite scientists">
216
+ <${childTag} checked label="Archimedes"></${childTag}>
217
+ <${childTag} checked label="Francis Bacon"></${childTag}>
218
+ <${childTag} checked label="Marie Curie"></${childTag}>
219
+ </${tag}>
220
+ </${groupTag}>
221
+ `);
222
+
223
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
224
+ el.querySelector(`${cfg.tagString}`)
225
+ );
226
+
227
+ // Assert
228
+ expect(elIndeterminate?.hasAttribute('checked')).to.be.true;
229
+ });
230
+
231
+ it('should be checked when it has one prechecked child', async () => {
232
+ // Arrange
233
+ const el = /** @type {LionCheckboxGroup} */ await fixture(html`
234
+ <${groupTag} name="scientists[]">
235
+ <${tag} label="Favorite scientists">
236
+ <${childTag} checked label="Archimedes"></${childTag}>
237
+ </${tag}>
238
+ </${groupTag}>
239
+ `);
240
+
241
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
242
+ el.querySelector(`${cfg.tagString}`)
243
+ );
244
+
245
+ // Assert
246
+ expect(elIndeterminate?.hasAttribute('checked')).to.be.true;
247
+ });
248
+
249
+ it('should be unchecked when it has an disabled checked child and an unchecked child', async () => {
250
+ // Arrange
251
+ const el = /** @type {LionCheckboxGroup} */ await fixture(html`
252
+ <${groupTag} name="scientists[]">
253
+ <${tag} label="Favorite scientists">
254
+ <${childTag} disabled checked label="Archimedes"></${childTag}>
255
+ <${childTag} label="Francis Bacon"></${childTag}>
256
+ </${tag}>
257
+ </${groupTag}>
258
+ `);
259
+
260
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
261
+ el.querySelector(`${cfg.tagString}`)
262
+ );
263
+
264
+ // Assert
265
+ expect(elIndeterminate?.hasAttribute('checked')).to.be.false;
266
+ });
267
+
268
+ it('should be indeterminated when some of the children are prechecked', async () => {
269
+ // Arrange
270
+ const el = /** @type {LionCheckboxGroup} */ await fixture(html`
271
+ <${groupTag} name="scientists[]">
272
+ <${tag} label="Favorite scientists">
273
+ <${childTag} checked label="Archimedes"></${childTag}>
274
+ <${childTag} checked label="Francis Bacon"></${childTag}>
275
+ <${childTag} label="Marie Curie"></${childTag}>
276
+ </${tag}>
277
+ </${groupTag}>
278
+ `);
279
+
280
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
281
+ el.querySelector(`${cfg.tagString}`)
282
+ );
283
+
284
+ // Assert
285
+ expect(elIndeterminate.hasAttribute('indeterminate')).to.be.true;
209
286
  });
210
287
 
211
288
  it('should sync all children when parent is checked (from indeterminate to checked)', async () => {
@@ -384,6 +461,65 @@ export function runCheckboxIndeterminateSuite(customConfig) {
384
461
  expect(_subCheckboxes[2].hasAttribute('checked')).to.be.false;
385
462
  });
386
463
 
464
+ it('should sync all prechecked children when parent is indeterminate and some of the children are disabled (from checked to unchecked)', async () => {
465
+ // Arrange
466
+ const el = /** @type {LionCheckboxGroup} */ (
467
+ await fixture(html`
468
+ <${groupTag} name="scientists[]">
469
+ <${tag} label="Favorite scientists">
470
+ <${childTag} checked label="Archimedes"></${childTag}>
471
+ <${childTag} disabled label="Francis Bacon"></${childTag}>
472
+ <${childTag} checked label="Marie Curie"></${childTag}>
473
+ </${tag}>
474
+ </${groupTag}>
475
+ `)
476
+ );
477
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
478
+ el.querySelector(`${cfg.tagString}`)
479
+ );
480
+ const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate);
481
+
482
+ // Act
483
+ _inputNode.click();
484
+ await elIndeterminate.updateComplete;
485
+
486
+ // Assert
487
+ expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.false;
488
+ expect(_inputNode?.hasAttribute('indeterminate')).to.be.false;
489
+ expect(_subCheckboxes[0].hasAttribute('checked')).to.be.false;
490
+ expect(_subCheckboxes[1].hasAttribute('checked')).to.be.false;
491
+ expect(_subCheckboxes[2].hasAttribute('checked')).to.be.false;
492
+ });
493
+
494
+ it('should stay as indeterminated after it is clicked, when it is interminated already and some children are disabled', async () => {
495
+ // Arrange
496
+ const el = /** @type {LionCheckboxGroup} */ (
497
+ await fixture(html`
498
+ <${groupTag} name="scientists[]">
499
+ <${tag} label="Favorite scientists">
500
+ <${childTag} checked label="Archimedes"></${childTag}>
501
+ <${childTag} disabled label="Francis Bacon"></${childTag}>
502
+ <${childTag} label="Marie Curie"></${childTag}>
503
+ </${tag}>
504
+ </${groupTag}>
505
+ `)
506
+ );
507
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
508
+ el.querySelector(`${cfg.tagString}`)
509
+ );
510
+ const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate);
511
+
512
+ // Act
513
+ _inputNode.click();
514
+ await elIndeterminate.updateComplete;
515
+
516
+ // Assert
517
+ expect(elIndeterminate.hasAttribute('indeterminate')).to.be.true;
518
+ expect(_inputNode.indeterminate).to.be.true;
519
+ expect(_subCheckboxes[0].hasAttribute('checked')).to.be.true;
520
+ expect(_subCheckboxes[2].hasAttribute('checked')).to.be.true;
521
+ });
522
+
387
523
  it('should work as expected with siblings checkbox-indeterminate', async () => {
388
524
  // Arrange
389
525
  const el = /** @type {LionCheckboxGroup} */ (
@@ -450,6 +586,58 @@ export function runCheckboxIndeterminateSuite(customConfig) {
450
586
  expect(elSecondSubCheckboxes._subCheckboxes[1].hasAttribute('checked')).to.be.false;
451
587
  });
452
588
 
589
+ it('should work as expected when new checkbox was added', async () => {
590
+ // Arrange
591
+ const el = /** @type {LionCheckboxGroup} */ await fixture(html`
592
+ <${groupTag} name="scientists[]">
593
+ <${tag} label="Favorite scientists">
594
+ <${childTag} checked label="Archimedes"></${childTag}>
595
+ <${childTag} checked label="Francis Bacon" checked></${childTag}>
596
+ <${childTag} checked label="Marie Curie"></${childTag}>
597
+ </${tag}>
598
+ </${groupTag}>
599
+ `);
600
+
601
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
602
+ el.querySelector(`${cfg.tagString}`)
603
+ );
604
+ expect(elIndeterminate.hasAttribute('indeterminate')).to.be.false;
605
+
606
+ // Act
607
+ const newChild = document.createElement(/** @type {string} */ (cfg.childTagString));
608
+ elIndeterminate.appendChild(newChild);
609
+ await elIndeterminate.updateComplete;
610
+
611
+ // Assert
612
+ expect(elIndeterminate.hasAttribute('indeterminate')).to.be.true;
613
+ });
614
+
615
+ it('should work as expected when an existing checkbox was removed', async () => {
616
+ // Arrange
617
+ const el = /** @type {LionCheckboxGroup} */ await fixture(html`
618
+ <${groupTag} name="scientists[]">
619
+ <${tag} label="Favorite scientists">
620
+ <${childTag} checked label="Archimedes"></${childTag}>
621
+ <${childTag} checked label="Francis Bacon" checked></${childTag}>
622
+ <${childTag} label="Marie Curie"></${childTag}>
623
+ </${tag}>
624
+ </${groupTag}>
625
+ `);
626
+
627
+ const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ (
628
+ el.querySelector(`${cfg.tagString}`)
629
+ );
630
+ expect(elIndeterminate.hasAttribute('indeterminate')).to.be.true;
631
+ expect(elIndeterminate.checked).to.be.false;
632
+
633
+ // Act
634
+ elIndeterminate.removeChild(/** @type {ChildNode} */ (elIndeterminate.lastChild));
635
+ await elIndeterminate.updateComplete;
636
+
637
+ // Assert
638
+ expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.true;
639
+ });
640
+
453
641
  it('should work as expected with nested indeterminate checkboxes', async () => {
454
642
  // Arrange
455
643
  const el = /** @type {LionCheckboxGroup} */ (
@@ -1,4 +1,4 @@
1
- import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
1
+ import { defineCE, expect, fixture, html, unsafeStatic, waitUntil } from '@open-wc/testing';
2
2
  import { Required, Unparseable } from '@lion/ui/form-core.js';
3
3
  import { sendKeys } from '@web/test-runner-commands';
4
4
  import { LionCombobox } from '@lion/ui/combobox.js';
@@ -48,6 +48,80 @@ async function fruitFixture({ autocomplete, matchMode } = {}) {
48
48
  }
49
49
 
50
50
  describe('lion-combobox', () => {
51
+ describe('Rendering on API change', () => {
52
+ it('renders when options changed', async () => {
53
+ const entries = [
54
+ {
55
+ id: '1',
56
+ label: 'Apple',
57
+ },
58
+ {
59
+ id: '2',
60
+ label: 'Artichoke',
61
+ },
62
+ {
63
+ id: '3',
64
+ label: 'Asparagus',
65
+ },
66
+ {
67
+ id: '4',
68
+ label: 'Banana',
69
+ },
70
+ {
71
+ id: '5',
72
+ label: 'Pineapple',
73
+ },
74
+ ];
75
+
76
+ class Wrapper extends LitElement {
77
+ static properties = {
78
+ ...super.properties,
79
+ entries: { type: Array },
80
+ };
81
+
82
+ constructor() {
83
+ super();
84
+ this.entries = entries;
85
+ }
86
+
87
+ render() {
88
+ return html`<lion-combobox name="foo">
89
+ ${this.entries.map(
90
+ entry => html`<lion-option .choiceValue="${entry.id}">${entry.label}</lion-option>`,
91
+ )}
92
+ </lion-combobox>`;
93
+ }
94
+ }
95
+
96
+ const tagString = defineCE(Wrapper);
97
+ const tag = unsafeStatic(tagString);
98
+ const wrapperElement = /** @type {Wrapper} */ (await fixture(html`<${tag}></${tag}>`));
99
+ await waitUntil(
100
+ () =>
101
+ wrapperElement?.shadowRoot?.querySelector('lion-options')?.querySelectorAll('lion-option')
102
+ .length === entries.length,
103
+ );
104
+
105
+ wrapperElement.entries = [
106
+ {
107
+ id: '4',
108
+ label: 'Banana',
109
+ },
110
+ ];
111
+
112
+ await waitUntil(
113
+ () =>
114
+ wrapperElement?.shadowRoot?.querySelector('lion-options')?.querySelectorAll('lion-option')
115
+ .length === wrapperElement.entries.length,
116
+ );
117
+
118
+ const optionElements = wrapperElement.shadowRoot?.querySelectorAll('lion-option');
119
+ expect(optionElements?.length).to.equal(wrapperElement.entries.length);
120
+ const optionElement = optionElements?.item(0);
121
+ expect(optionElement?.textContent).to.equal('Banana');
122
+ });
123
+ });
124
+
51
125
  describe('Options visibility', () => {
52
126
  it('hides options when text in input node is cleared after typing something by default', async () => {
53
127
  const el = /** @type {LionCombobox} */ (
@@ -198,19 +198,19 @@ const FocusMixinImplementation = superclass =>
198
198
  * @private
199
199
  */
200
200
  __teardownEventsForFocusMixin() {
201
- this._focusableNode.removeEventListener(
201
+ this._focusableNode?.removeEventListener(
202
202
  'focus',
203
203
  /** @type {EventListenerOrEventListenerObject} */ (this.__redispatchFocus),
204
204
  );
205
- this._focusableNode.removeEventListener(
205
+ this._focusableNode?.removeEventListener(
206
206
  'blur',
207
207
  /** @type {EventListenerOrEventListenerObject} */ (this.__redispatchBlur),
208
208
  );
209
- this._focusableNode.removeEventListener(
209
+ this._focusableNode?.removeEventListener(
210
210
  'focusin',
211
211
  /** @type {EventListenerOrEventListenerObject} */ (this.__redispatchFocusin),
212
212
  );
213
- this._focusableNode.removeEventListener(
213
+ this._focusableNode?.removeEventListener(
214
214
  'focusout',
215
215
  /** @type {EventListenerOrEventListenerObject} */ (this.__redispatchFocusout),
216
216
  );
@@ -44,7 +44,7 @@ export class LionField extends FormControlMixin(
44
44
 
45
45
  disconnectedCallback() {
46
46
  super.disconnectedCallback();
47
- this._inputNode.removeEventListener('change', this._onChange);
47
+ this._inputNode?.removeEventListener('change', this._onChange);
48
48
  }
49
49
 
50
50
  resetInteractionState() {
@@ -1,6 +1,7 @@
1
1
  import { getDateFormatBasedOnLocale } from './getDateFormatBasedOnLocale.js';
2
2
  import { getLocalizeManager } from '../getLocalizeManager.js';
3
3
  import { addLeadingZero } from './utils/addLeadingZero.js';
4
+ import { isCorrectYearFormat } from './utils/checkYearFormat.js';
4
5
 
5
6
  /**
6
7
  * @typedef {import('../../types/LocalizeMixinTypes.js').FormatDateOptions} FormatDateOptions
@@ -63,12 +64,16 @@ export function parseDate(dateString, options) {
63
64
  }
64
65
 
65
66
  const [year, month, day] = parsedString.split('/').map(Number);
67
+ const [yearString] = parsedString.split('/');
68
+ if (!isCorrectYearFormat(yearString)) {
69
+ return undefined;
70
+ }
66
71
  let correctedYear = year;
67
72
  if (year < 50) {
68
73
  correctedYear = 2000 + year;
69
74
  }
70
75
 
71
- const parsedDate = new Date(new Date(correctedYear, month - 1, day));
76
+ const parsedDate = new Date(correctedYear, month - 1, day);
72
77
  // Check if parsedDate is not `Invalid Date` or that the date has changed (e.g. the not existing 31.02.2020)
73
78
  if (
74
79
  year > 0 &&
@@ -0,0 +1,8 @@
1
+ /**
2
+ *
3
+ * @param {string} year
4
+ * @returns {boolean}
5
+ */
6
+ export function isCorrectYearFormat(year) {
7
+ return /^\d{2}(\d{2})?$/g.test(year);
8
+ }
@@ -91,4 +91,10 @@ describe('parseDate()', () => {
91
91
  expect(parseDate('02.31.2020')).to.equal(undefined); // non existing date
92
92
  expect(parseDate('31.12.2020')).to.equal(undefined); // day & month switched places
93
93
  });
94
+
95
+ it('returns undefined for incorrect year format', () => {
96
+ // TODO: we need a `> 0` check for day and month, but maybe consider `>= 0` for year?
97
+ expect(parseDate('12-12-0000')).to.equal(undefined);
98
+ expect(parseDate('12-12-999')).to.equal(undefined);
99
+ });
94
100
  });
@@ -67,11 +67,6 @@ export declare class LocalizeMixinHost {
67
67
 
68
68
  public localizeNamespacesLoaded: Promise<Object> | undefined;
69
69
 
70
- /**
71
- * Hook into LitElement to only render once all translations are loaded
72
- */
73
- public performUpdate(): Promise<void>;
74
-
75
70
  public onLocaleReady(): void;
76
71
  public onLocaleChanged(newLocale: string, oldLocale: string): void;
77
72
  public onLocaleUpdated(): void;
@@ -491,7 +491,7 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
491
491
  */
492
492
  // TODO: rename to #invokerOnKeyUp() (and move event listener to the invoker) in v1
493
493
  __onKeyUp(ev) {
494
- if (this.disabled || this.readOnly) {
494
+ if (this.disabled || this.readOnly || this.singleOption) {
495
495
  return;
496
496
  }
497
497
 
@@ -342,7 +342,7 @@ describe('lion-select-rich', () => {
342
342
  expect(elSingleoption.opened).to.be.false;
343
343
  });
344
344
 
345
- it('stays closed with [ArrowUp] or [ArrowDown] key in readonly mode', async () => {
345
+ it('stays closed with [ArrowUp] or [ArrowDown] key in readonly mode or has a single option', async () => {
346
346
  const elReadOnly = await fixture(html`
347
347
  <lion-select-rich readonly>
348
348
  <lion-option .choiceValue=${10}>Item 1</lion-option>
@@ -350,11 +350,23 @@ describe('lion-select-rich', () => {
350
350
  </lion-select-rich>
351
351
  `);
352
352
 
353
+ const elSingleOption = await fixture(html`
354
+ <lion-select-rich>
355
+ <lion-option .choiceValue=${10}>Item 1</lion-option>
356
+ </lion-select-rich>
357
+ `);
358
+
353
359
  elReadOnly.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
354
360
  expect(elReadOnly.opened).to.be.false;
355
361
 
356
362
  elReadOnly.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowUp' }));
357
363
  expect(elReadOnly.opened).to.be.false;
364
+
365
+ elSingleOption.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
366
+ expect(elSingleOption.opened).to.be.false;
367
+
368
+ elSingleOption.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowUp' }));
369
+ expect(elSingleOption.opened).to.be.false;
358
370
  });
359
371
 
360
372
  it('sets inheritsReferenceWidth to min by default', async () => {