@lion/ui 0.12.0 → 0.13.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 (196) hide show
  1. package/components/accordion/src/LionAccordion.js +3 -2
  2. package/components/button/src/LionButton.js +6 -10
  3. package/components/button/src/LionButtonReset.js +2 -0
  4. package/components/button/src/LionButtonSubmit.js +2 -0
  5. package/components/button/test-suites/LionButton.suite.js +0 -14
  6. package/components/checkbox-group/src/LionCheckbox.js +3 -0
  7. package/components/checkbox-group/src/LionCheckboxGroup.js +2 -0
  8. package/components/checkbox-group/src/LionCheckboxIndeterminate.js +3 -0
  9. package/components/collapsible/src/LionCollapsible.js +4 -1
  10. package/components/combobox/src/LionCombobox.js +31 -8
  11. package/components/combobox/test/lion-combobox.test.js +206 -57
  12. package/components/dialog/src/LionDialog.js +3 -0
  13. package/components/drawer/src/LionDrawer.js +9 -0
  14. package/components/fieldset/src/LionFieldset.js +1 -1
  15. package/components/form/src/LionForm.js +0 -1
  16. package/components/form-core/src/FocusMixin.js +1 -0
  17. package/components/form-core/src/FormControlMixin.js +33 -18
  18. package/components/form-core/src/FormatMixin.js +1 -1
  19. package/components/form-core/src/validate/LionValidationFeedback.js +8 -10
  20. package/components/form-core/src/validate/ValidateMixin.js +40 -4
  21. package/components/form-core/test/FormControlMixin.test.js +68 -0
  22. package/components/form-core/test/validate/lion-validation-feedback.test.js +0 -22
  23. package/components/form-core/test-suites/ValidateMixin.suite.js +19 -3
  24. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +119 -0
  25. package/components/form-core/types/validate/validate.ts +1 -0
  26. package/components/icon/src/LionIcon.js +2 -0
  27. package/components/input-file/src/LionInputFile.js +9 -0
  28. package/components/input-tel/src/LionInputTel.js +3 -0
  29. package/components/input-tel-dropdown/src/LionInputTelDropdown.js +2 -0
  30. package/components/listbox/src/LionListbox.js +2 -0
  31. package/components/overlays/src/OverlayController.js +32 -5
  32. package/components/overlays/src/OverlayMixin.js +5 -0
  33. package/components/overlays/src/configurations/withDropdownConfig.js +0 -1
  34. package/components/overlays/src/configurations/withTooltipConfig.js +0 -1
  35. package/components/overlays/test/OverlayController.test.js +16 -4
  36. package/components/overlays/test-suites/OverlayMixin.suite.js +71 -0
  37. package/components/progress-indicator/src/LionProgressIndicator.js +4 -0
  38. package/components/radio-group/src/LionRadioGroup.js +3 -1
  39. package/components/select/src/LionSelect.js +4 -0
  40. package/components/select-rich/src/LionSelectInvoker.js +2 -0
  41. package/components/select-rich/src/LionSelectRich.js +2 -0
  42. package/components/steps/src/LionStep.js +0 -1
  43. package/components/steps/src/LionSteps.js +0 -1
  44. package/components/switch/src/LionSwitch.js +3 -0
  45. package/components/switch/src/LionSwitchButton.js +5 -0
  46. package/components/tabs/src/LionTabs.js +8 -0
  47. package/custom-elements.json +1271 -406
  48. package/dist-types/components/accordion/src/LionAccordion.d.ts +3 -2
  49. package/dist-types/components/accordion/src/LionAccordion.d.ts.map +1 -1
  50. package/dist-types/components/button/src/LionButton.d.ts +2 -1
  51. package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
  52. package/dist-types/components/button/src/LionButtonReset.d.ts +2 -0
  53. package/dist-types/components/button/src/LionButtonReset.d.ts.map +1 -1
  54. package/dist-types/components/button/src/LionButtonSubmit.d.ts +2 -0
  55. package/dist-types/components/button/src/LionButtonSubmit.d.ts.map +1 -1
  56. package/dist-types/components/button/test-suites/LionButton.suite.d.ts.map +1 -1
  57. package/dist-types/components/checkbox-group/src/LionCheckbox.d.ts +3 -0
  58. package/dist-types/components/checkbox-group/src/LionCheckbox.d.ts.map +1 -1
  59. package/dist-types/components/checkbox-group/src/LionCheckboxGroup.d.ts +2 -0
  60. package/dist-types/components/checkbox-group/src/LionCheckboxGroup.d.ts.map +1 -1
  61. package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts +3 -0
  62. package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts.map +1 -1
  63. package/dist-types/components/collapsible/src/LionCollapsible.d.ts +3 -1
  64. package/dist-types/components/collapsible/src/LionCollapsible.d.ts.map +1 -1
  65. package/dist-types/components/combobox/src/LionCombobox.d.ts +5 -0
  66. package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
  67. package/dist-types/components/core/src/ScopedElementsMixin.d.ts.map +1 -1
  68. package/dist-types/components/dialog/src/LionDialog.d.ts +3 -0
  69. package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
  70. package/dist-types/components/drawer/src/LionDrawer.d.ts +9 -0
  71. package/dist-types/components/drawer/src/LionDrawer.d.ts.map +1 -1
  72. package/dist-types/components/fieldset/src/LionFieldset.d.ts +1 -1
  73. package/dist-types/components/form/src/LionForm.d.ts.map +1 -1
  74. package/dist-types/components/form-core/src/FocusMixin.d.ts.map +1 -1
  75. package/dist-types/components/form-core/src/FormControlMixin.d.ts.map +1 -1
  76. package/dist-types/components/form-core/src/validate/LionValidationFeedback.d.ts +4 -3
  77. package/dist-types/components/form-core/src/validate/LionValidationFeedback.d.ts.map +1 -1
  78. package/dist-types/components/form-core/src/validate/ValidateMixin.d.ts.map +1 -1
  79. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  80. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  81. package/dist-types/components/form-core/types/validate/validate.d.ts +1 -0
  82. package/dist-types/components/form-core/types/validate/validate.d.ts.map +1 -1
  83. package/dist-types/components/icon/src/LionIcon.d.ts +2 -0
  84. package/dist-types/components/icon/src/LionIcon.d.ts.map +1 -1
  85. package/dist-types/components/input-file/src/LionInputFile.d.ts +9 -0
  86. package/dist-types/components/input-file/src/LionInputFile.d.ts.map +1 -1
  87. package/dist-types/components/input-tel/src/LionInputTel.d.ts +3 -0
  88. package/dist-types/components/input-tel/src/LionInputTel.d.ts.map +1 -1
  89. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts +2 -0
  90. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
  91. package/dist-types/components/listbox/src/LionListbox.d.ts +2 -0
  92. package/dist-types/components/listbox/src/LionListbox.d.ts.map +1 -1
  93. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  94. package/dist-types/components/overlays/src/OverlayMixin.d.ts +4 -0
  95. package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
  96. package/dist-types/components/overlays/src/configurations/withDropdownConfig.d.ts.map +1 -1
  97. package/dist-types/components/overlays/src/configurations/withTooltipConfig.d.ts.map +1 -1
  98. package/dist-types/components/overlays/test/OverlayController.test.d.ts.map +1 -1
  99. package/dist-types/components/overlays/test-suites/OverlayMixin.suite.d.ts.map +1 -1
  100. package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts +3 -0
  101. package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts.map +1 -1
  102. package/dist-types/components/radio-group/src/LionRadioGroup.d.ts +3 -1
  103. package/dist-types/components/radio-group/src/LionRadioGroup.d.ts.map +1 -1
  104. package/dist-types/components/select/src/LionSelect.d.ts +3 -0
  105. package/dist-types/components/select/src/LionSelect.d.ts.map +1 -1
  106. package/dist-types/components/select-rich/src/LionSelectInvoker.d.ts +2 -0
  107. package/dist-types/components/select-rich/src/LionSelectInvoker.d.ts.map +1 -1
  108. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +3 -1
  109. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  110. package/dist-types/components/steps/src/LionStep.d.ts +0 -1
  111. package/dist-types/components/steps/src/LionStep.d.ts.map +1 -1
  112. package/dist-types/components/steps/src/LionSteps.d.ts +0 -1
  113. package/dist-types/components/steps/src/LionSteps.d.ts.map +1 -1
  114. package/dist-types/components/switch/src/LionSwitch.d.ts +3 -0
  115. package/dist-types/components/switch/src/LionSwitch.d.ts.map +1 -1
  116. package/dist-types/components/switch/src/LionSwitchButton.d.ts +5 -0
  117. package/dist-types/components/switch/src/LionSwitchButton.d.ts.map +1 -1
  118. package/dist-types/components/tabs/src/LionTabs.d.ts +8 -0
  119. package/dist-types/components/tabs/src/LionTabs.d.ts.map +1 -1
  120. package/dist-types/tsconfig.tsbuildinfo +1 -1
  121. package/docs/components/accordion/api-table.md +8 -0
  122. package/docs/components/button/api-table.md +0 -3
  123. package/docs/components/checkbox-group/api-table.md +6 -6
  124. package/docs/components/checkbox-group/overview.md +2 -2
  125. package/docs/components/collapsible/api-table.md +7 -0
  126. package/docs/components/collapsible/overview.md +1 -1
  127. package/docs/components/combobox/api-table.md +9 -2
  128. package/docs/components/combobox/overview.md +1 -1
  129. package/docs/components/combobox/use-cases.md +90 -1
  130. package/docs/components/dialog/overview.md +1 -1
  131. package/docs/components/drawer/api-table.md +8 -0
  132. package/docs/components/fieldset/api-table.md +2 -2
  133. package/docs/components/fieldset/overview.md +5 -5
  134. package/docs/components/form/overview.md +3 -3
  135. package/docs/components/form/use-cases.md +2 -2
  136. package/docs/components/icon/use-cases.md +1 -1
  137. package/docs/components/input/api-table.md +2 -2
  138. package/docs/components/input/overview.md +1 -1
  139. package/docs/components/input-amount/api-table.md +2 -2
  140. package/docs/components/input-amount/overview.md +3 -3
  141. package/docs/components/input-date/api-table.md +2 -2
  142. package/docs/components/input-date/overview.md +3 -3
  143. package/docs/components/input-datepicker/api-table.md +2 -2
  144. package/docs/components/input-datepicker/overview.md +5 -5
  145. package/docs/components/input-email/api-table.md +2 -2
  146. package/docs/components/input-email/overview.md +2 -2
  147. package/docs/components/input-file/api-table.md +9 -2
  148. package/docs/components/input-file/overview.md +1 -1
  149. package/docs/components/input-iban/api-table.md +2 -2
  150. package/docs/components/input-iban/overview.md +2 -2
  151. package/docs/components/input-range/api-table.md +2 -2
  152. package/docs/components/input-range/overview.md +2 -2
  153. package/docs/components/input-stepper/api-table.md +2 -2
  154. package/docs/components/input-stepper/overview.md +1 -1
  155. package/docs/components/input-tel/api-table.md +2 -2
  156. package/docs/components/input-tel/overview.md +1 -1
  157. package/docs/components/input-tel/use-cases.md +6 -6
  158. package/docs/components/input-tel-dropdown/api-table.md +2 -2
  159. package/docs/components/input-tel-dropdown/overview.md +1 -1
  160. package/docs/components/listbox/api-table.md +2 -2
  161. package/docs/components/listbox/use-cases.md +1 -1
  162. package/docs/components/radio-group/api-table.md +4 -4
  163. package/docs/components/radio-group/overview.md +3 -3
  164. package/docs/components/select/api-table.md +2 -2
  165. package/docs/components/select/use-cases.md +1 -1
  166. package/docs/components/select-rich/api-table.md +2 -3
  167. package/docs/components/select-rich/use-cases.md +1 -1
  168. package/docs/components/switch/api-table.md +2 -2
  169. package/docs/components/tabs/api-table.md +7 -0
  170. package/docs/components/tabs/examples.md +1 -1
  171. package/docs/components/textarea/api-table.md +2 -2
  172. package/docs/fundamentals/index.md +4 -4
  173. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  174. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  175. package/docs/fundamentals/rationales/accessibility.md +1 -1
  176. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  177. package/docs/fundamentals/systems/core/overview.md +1 -1
  178. package/docs/fundamentals/systems/form/api-table.md +2 -2
  179. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  180. package/docs/fundamentals/systems/form/model-value.md +1 -1
  181. package/docs/fundamentals/systems/form/overview.md +28 -28
  182. package/docs/fundamentals/systems/form/styling.md +3 -20
  183. package/docs/fundamentals/systems/form/validate.md +19 -3
  184. package/docs/fundamentals/systems/localize/overview.md +4 -4
  185. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  186. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  187. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  188. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  189. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  190. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  191. package/docs/guides/how-to/create-a-form.md +4 -4
  192. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  193. package/docs/guides/index.md +8 -8
  194. package/docs/guides/principles/index.md +5 -5
  195. package/docs/guides/principles/styling.md +1 -1
  196. package/package.json +3 -3
@@ -14,10 +14,11 @@ import { uuid } from '@lion/ui/core.js';
14
14
  */
15
15
 
16
16
  /**
17
- * # <lion-accordion> webcomponent
17
+ * @slot invoker - The invoker element for the accordion
18
+ * @slot content - The content element for the accordion
19
+ * @slot _accordion - The slot for the accordion, used to rearrange invokers and content
18
20
  *
19
21
  * @customElement lion-accordion
20
- * @extends LitElement
21
22
  */
22
23
  export class LionAccordion extends LitElement {
23
24
  static get properties() {
@@ -1,9 +1,11 @@
1
1
  import { html, LitElement, css } from 'lit';
2
- import { browserDetection, DisabledWithTabIndexMixin, uuid } from '@lion/ui/core.js';
2
+ import { DisabledWithTabIndexMixin } from '@lion/ui/core.js';
3
3
 
4
4
  const isKeyboardClickEvent = (/** @type {KeyboardEvent} */ e) => e.key === ' ' || e.key === 'Enter';
5
5
  const isSpaceKeyboardClickEvent = (/** @type {KeyboardEvent} */ e) => e.key === ' ';
6
6
 
7
+ // TODO: simplify html structure, as we got rid of the wrapper element for IE11
8
+
7
9
  /**
8
10
  * @typedef {import('lit').TemplateResult} TemplateResult
9
11
  */
@@ -25,6 +27,8 @@ const isSpaceKeyboardClickEvent = (/** @type {KeyboardEvent} */ e) => e.key ===
25
27
  * `<form>` support is needed:
26
28
  * - When type="reset|submit" should be supported, use LionButtonReset.
27
29
  * - When implicit form submission should be supported on top, use LionButtonSubmit.
30
+ *
31
+ * @customElement lion-button
28
32
  */
29
33
  export class LionButton extends DisabledWithTabIndexMixin(LitElement) {
30
34
  static get properties() {
@@ -35,7 +39,7 @@ export class LionButton extends DisabledWithTabIndexMixin(LitElement) {
35
39
  }
36
40
 
37
41
  render() {
38
- return html` <div class="button-content" id="${this._buttonId}"><slot></slot></div> `;
42
+ return html` <div class="button-content"><slot></slot></div> `;
39
43
  }
40
44
 
41
45
  static get styles() {
@@ -124,14 +128,6 @@ export class LionButton extends DisabledWithTabIndexMixin(LitElement) {
124
128
  this.type = 'button';
125
129
  this.active = false;
126
130
 
127
- this._buttonId = uuid('button');
128
- if (browserDetection.isIE11) {
129
- this.updateComplete.then(() => {
130
- if (!this.hasAttribute('aria-labelledby')) {
131
- this.setAttribute('aria-labelledby', this._buttonId);
132
- }
133
- });
134
- }
135
131
  this.__setupEvents();
136
132
  }
137
133
 
@@ -17,6 +17,8 @@ import { LionButton } from './LionButton.js';
17
17
  * implicit form submission logic), but LionButtonReset is an easier to grasp name for
18
18
  * Application Developers: for reset buttons, always use LionButtonReset, for submit
19
19
  * buttons always use LionButtonSubmit.
20
+ *
21
+ * @customElement lion-button-reset
20
22
  */
21
23
  export class LionButtonReset extends LionButton {
22
24
  constructor() {
@@ -39,6 +39,8 @@ function createImplicitSubmitHelperButton() {
39
39
  * - the Application Developer should be able to switch types between 'submit'|'reset'|'button'
40
40
  * (this is similar to how native HTMLButtonElement works)
41
41
  * - a submit button with native form support is needed
42
+ *
43
+ * @customElement lion-button-submit
42
44
  */
43
45
  export class LionButtonSubmit extends LionButtonReset {
44
46
  /**
@@ -203,20 +203,6 @@ export function LionButtonSuite({ klass = LionButton } = {}) {
203
203
  expect(el.getAttribute('tabindex')).to.equal('5');
204
204
  });
205
205
 
206
- it('has an aria-labelledby and wrapper element in IE11', async () => {
207
- const browserDetectionStub = sinon.stub(browserDetection, 'isIE11').value(true);
208
- const el = /** @type {LionButton} */ (
209
- await fixture(html`<${tagButton}>foo</${tagButton}>`)
210
- );
211
- expect(el.hasAttribute('aria-labelledby')).to.be.true;
212
- const wrapperId = el.getAttribute('aria-labelledby');
213
- expect(/** @type {ShadowRoot} */ (el.shadowRoot).querySelector(`#${wrapperId}`)).to.exist;
214
- expect(
215
- /** @type {ShadowRoot} */ (el.shadowRoot).querySelector(`#${wrapperId}`),
216
- ).dom.to.equal(`<div class="button-content" id="${wrapperId}"><slot></slot></div>`);
217
- browserDetectionStub.restore();
218
- });
219
-
220
206
  it('does not override aria-labelledby when provided by user', async () => {
221
207
  const browserDetectionStub = sinon.stub(browserDetection, 'isIE11').value(true);
222
208
  const el = /** @type {LionButton} */ (
@@ -1,6 +1,9 @@
1
1
  import { LionInput } from '@lion/ui/input.js';
2
2
  import { ChoiceInputMixin } from '@lion/ui/form-core.js';
3
3
 
4
+ /**
5
+ * @customElement lion-checkbox
6
+ */
4
7
  export class LionCheckbox extends ChoiceInputMixin(LionInput) {
5
8
  connectedCallback() {
6
9
  super.connectedCallback();
@@ -3,6 +3,8 @@ import { ChoiceGroupMixin, FormGroupMixin } from '@lion/ui/form-core.js';
3
3
 
4
4
  /**
5
5
  * A wrapper around multiple checkboxes
6
+ *
7
+ * @customElement lion-checkbox-group
6
8
  */
7
9
  export class LionCheckboxGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {
8
10
  constructor() {
@@ -5,6 +5,9 @@ import { LionCheckbox } from './LionCheckbox.js';
5
5
  * @typedef {import('./LionCheckboxGroup.js').LionCheckboxGroup} LionCheckboxGroup
6
6
  */
7
7
 
8
+ /**
9
+ * @customElement lion-checkbox-indeterminate
10
+ */
8
11
  export class LionCheckboxIndeterminate extends LionCheckbox {
9
12
  static get styles() {
10
13
  return [
@@ -1,10 +1,13 @@
1
1
  import { LitElement, html, css } from 'lit';
2
2
  import { uuid } from '@lion/ui/core.js';
3
+
3
4
  /**
4
5
  * `LionCollapsible` is a class for custom collapsible element (`<lion-collapsible>` web component).
5
6
  *
7
+ * @slot invoker - The invoker element for the collapsible
8
+ * @slot content - The content element for the collapsible
9
+ *
6
10
  * @customElement lion-collapsible
7
- * @extends LitElement
8
11
  */
9
12
  export class LionCollapsible extends LitElement {
10
13
  static get styles() {
@@ -28,6 +28,11 @@ const matchA11ySpanReverseFns = new WeakMap();
28
28
  /**
29
29
  * LionCombobox: implements the wai-aria combobox design pattern and integrates it as a Lion
30
30
  * FormControl
31
+ *
32
+ * @slot listbox - The listbox element for the combobox, e.g. <lion-options>
33
+ * @slot selection-display - The selection display element for the combobox, e.g. <lion-selection-display>
34
+ *
35
+ * @customElement lion-combobox
31
36
  */
32
37
  export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMixin(LionListbox))) {
33
38
  /** @type {any} */
@@ -502,6 +507,10 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
502
507
  if (this._selectionDisplayNode) {
503
508
  this._selectionDisplayNode.comboboxElement = this;
504
509
  }
510
+
511
+ if (this.disabled || this.readOnly) {
512
+ this.__setComboboxDisabledAndReadOnly();
513
+ }
505
514
  }
506
515
 
507
516
  /**
@@ -657,9 +666,11 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
657
666
  const alwaysHideOn = ['Tab', 'Escape'];
658
667
  const notMultipleChoiceHideOn = ['Enter'];
659
668
  if (
660
- lastKey &&
661
- (alwaysHideOn.includes(lastKey) ||
662
- (!this.multipleChoice && notMultipleChoiceHideOn.includes(lastKey)))
669
+ this.disabled ||
670
+ this.readOnly ||
671
+ (lastKey &&
672
+ (alwaysHideOn.includes(lastKey) ||
673
+ (!this.multipleChoice && notMultipleChoiceHideOn.includes(lastKey))))
663
674
  ) {
664
675
  return false;
665
676
  }
@@ -1174,9 +1185,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
1174
1185
  */
1175
1186
  // eslint-disable-next-line no-unused-vars
1176
1187
  _syncToTextboxCondition(modelValue, oldModelValue, { phase } = {}) {
1177
- return (
1178
- this.autocomplete === 'inline' || this.autocomplete === 'both' || phase === 'overlay-close'
1179
- );
1188
+ return this.autocomplete === 'both' || this.autocomplete === 'inline' || !this.focused;
1180
1189
  }
1181
1190
 
1182
1191
  /**
@@ -1213,8 +1222,22 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
1213
1222
  */
1214
1223
  __setComboboxDisabledAndReadOnly() {
1215
1224
  if (this._comboboxNode) {
1216
- this._comboboxNode.setAttribute('disabled', `${this.disabled}`);
1217
- this._comboboxNode.setAttribute('readonly', `${this.readOnly}`);
1225
+ // Since `._comboboxNode` can either be `<div "role=combobox">` or `<input>`,
1226
+ // we need to cater for both scenarios (aria for semantics, "native attr" for operability)
1227
+ this._comboboxNode.toggleAttribute('disabled', this.disabled);
1228
+ this._comboboxNode.setAttribute('aria-disabled', `${this.disabled}`);
1229
+ this._comboboxNode.toggleAttribute('readonly', this.readOnly);
1230
+ this._comboboxNode.setAttribute('aria-readonly', `${this.readOnly}`);
1231
+ }
1232
+
1233
+ if (this._inputNode) {
1234
+ // N.B. in case ._inputNode === ._comboboxNode (we have <input role="combobox">)
1235
+ // this value has already been set above. This is fine, as a toggle with boolean flag is idempotent.
1236
+ this._inputNode.toggleAttribute('disabled', this.disabled);
1237
+ this._inputNode.toggleAttribute('readOnly', this.readOnly);
1238
+
1239
+ this._inputNode.setAttribute('aria-readonly', `${this.readOnly}`);
1240
+ this._inputNode.tabIndex = this.disabled ? -1 : 0;
1218
1241
  }
1219
1242
  }
1220
1243
 
@@ -24,12 +24,12 @@ import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
24
24
  */
25
25
 
26
26
  /**
27
- * @param {{ autocomplete?:'none'|'list'|'both', matchMode?:'begin'|'all' }} config
27
+ * @param {{ autocomplete?:'none'|'list'|'both', matchMode?:'begin'|'all', disabled?: boolean, readonly?: boolean }} config
28
28
  */
29
- async function fruitFixture({ autocomplete, matchMode } = {}) {
29
+ async function fruitFixture({ autocomplete, matchMode, disabled, readonly } = {}) {
30
30
  const el = /** @type {LionCombobox} */ (
31
31
  await fixture(html`
32
- <lion-combobox name="foo">
32
+ <lion-combobox label="Search" name="foo">
33
33
  <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
34
34
  <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
35
35
  <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
@@ -43,6 +43,12 @@ async function fruitFixture({ autocomplete, matchMode } = {}) {
43
43
  if (matchMode) {
44
44
  el.matchMode = matchMode;
45
45
  }
46
+ if (disabled) {
47
+ el.disabled = disabled;
48
+ }
49
+ if (readonly) {
50
+ el.readOnly = readonly;
51
+ }
46
52
  await el.updateComplete;
47
53
  return [el, el.formElements];
48
54
  }
@@ -634,38 +640,39 @@ describe('lion-combobox', () => {
634
640
  });
635
641
 
636
642
  it('syncs textbox to modelValue', async () => {
637
- const el = /** @type {LionCombobox} */ (
638
- await fixture(html`
639
- <lion-combobox name="foo" show-all-on-empty>
640
- <lion-option .choiceValue="${'Aha'}" checked>Aha</lion-option>
641
- <lion-option .choiceValue="${'Bhb'}">Bhb</lion-option>
642
- </lion-combobox>
643
- `)
644
- );
645
- const { _inputNode } = getComboboxMembers(el);
643
+ for (const autocompleteMode of ['none', 'list', 'inline', 'both']) {
644
+ const el = /** @type {LionCombobox} */ (
645
+ await fixture(html`
646
+ <lion-combobox name="foo" show-all-on-empty .autocomplete="${autocompleteMode}">
647
+ <lion-option .choiceValue="${'Aa'}" checked>Aa</lion-option>
648
+ <lion-option .choiceValue="${'Bb'}">Bb</lion-option>
649
+ </lion-combobox>
650
+ `)
651
+ );
652
+ const { _inputNode } = getComboboxMembers(el);
653
+
654
+ expect(_inputNode.value).to.equal('Aa', `autocompleteMode is ${autocompleteMode}`);
646
655
 
647
- /** @param {string} autocompleteMode */
648
- async function performChecks(autocompleteMode) {
649
656
  el.formElements[0].click();
650
657
  await el.updateComplete;
651
658
 
652
- // FIXME: fix properly for Webkit
653
- // expect(_inputNode.value).to.equal('Aha', `autocompleteMode is ${autocompleteMode}`);
659
+ expect(_inputNode.value).to.equal('Aa', `autocompleteMode is ${autocompleteMode}`);
654
660
  expect(el.checkedIndex).to.equal(0, `autocompleteMode is ${autocompleteMode}`);
655
661
 
656
- await mimicUserTyping(el, 'Ah');
662
+ await mimicUserTyping(el, 'B');
657
663
  await el.updateComplete;
658
- expect(_inputNode.value).to.equal('Ah', `autocompleteMode is ${autocompleteMode}`);
659
-
660
- await el.updateComplete;
661
- expect(el.checkedIndex).to.equal(-1, `autocompleteMode is ${autocompleteMode}`);
664
+ if (autocompleteMode === 'none' || autocompleteMode === 'list') {
665
+ expect(el.modelValue).to.deep.equal(
666
+ { type: 'unparseable', viewValue: 'B' },
667
+ `autocompleteMode is ${autocompleteMode}`,
668
+ );
669
+ await el.updateComplete;
670
+ expect(el.checkedIndex).to.equal(-1, `autocompleteMode is ${autocompleteMode}`);
671
+ } else {
672
+ expect(el.modelValue).to.equal('Bb', `autocompleteMode is ${autocompleteMode}`);
673
+ expect(el.checkedIndex).to.equal(1, `autocompleteMode is ${autocompleteMode}`);
674
+ }
662
675
  }
663
-
664
- el.autocomplete = 'none';
665
- await performChecks('none');
666
-
667
- el.autocomplete = 'list';
668
- await performChecks('list');
669
676
  });
670
677
 
671
678
  it('works with Required validator', async () => {
@@ -986,6 +993,27 @@ describe('lion-combobox', () => {
986
993
  expect(el.modelValue).to.eql(['Art']);
987
994
  });
988
995
 
996
+ it('allows prefilling the combobox', async () => {
997
+ const autocompleteValues = ['inline', 'none', 'both', 'list'];
998
+
999
+ for (const autocompleteValue of autocompleteValues) {
1000
+ const el = /** @type {LionCombobox} */ (
1001
+ await fixture(html`
1002
+ <lion-combobox autocomplete="${autocompleteValue}" .modelValue="${'Chard'}" name="foo">
1003
+ <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1004
+ <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1005
+ <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1006
+ <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1007
+ </lion-combobox>
1008
+ `)
1009
+ );
1010
+
1011
+ expect(el.modelValue).to.equal('Chard');
1012
+ // @ts-expect-error
1013
+ expect(el._inputNode.value).to.equal('Chard');
1014
+ }
1015
+ });
1016
+
989
1017
  it('submits form on [Enter] when listbox is closed', async () => {
990
1018
  const submitSpy = sinon.spy(e => e.preventDefault());
991
1019
  const el = /** @type {HTMLFormElement} */ (
@@ -2312,8 +2340,7 @@ describe('lion-combobox', () => {
2312
2340
  </${tag}>
2313
2341
  `)
2314
2342
  );
2315
- // This ensures autocomplete would be off originally
2316
- el.autocomplete = 'list';
2343
+
2317
2344
  await mimicUserTypingAdvanced(el, ['v', 'i']); // so we have options ['Victoria Plum']
2318
2345
  await el.updateComplete;
2319
2346
  expect(el.checkedIndex).to.equal(3);
@@ -2440,23 +2467,26 @@ describe('lion-combobox', () => {
2440
2467
  expect(_inputNode.value).to.equal('');
2441
2468
 
2442
2469
  el.setCheckedIndex(-1);
2443
- el.autocomplete = 'none';
2444
- el.setCheckedIndex(0);
2445
- expect(_inputNode.value).to.equal('');
2470
+ el.autocomplete = 'list';
2471
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2472
+ expect(_inputNode.value).to.equal('art');
2446
2473
 
2447
2474
  el.setCheckedIndex(-1);
2448
- el.autocomplete = 'list';
2449
- el.setCheckedIndex(0);
2450
- expect(_inputNode.value).to.equal('');
2475
+ el.value = '';
2476
+ el.autocomplete = 'none';
2477
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2478
+ expect(_inputNode.value).to.equal('art');
2451
2479
 
2452
2480
  el.setCheckedIndex(-1);
2481
+ el.value = '';
2453
2482
  el.autocomplete = 'inline';
2454
- el.setCheckedIndex(0);
2483
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2455
2484
  expect(_inputNode.value).to.equal('Artichoke');
2456
2485
 
2457
2486
  el.setCheckedIndex(-1);
2487
+ el.value = '';
2458
2488
  el.autocomplete = 'both';
2459
- el.setCheckedIndex(0);
2489
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2460
2490
  expect(_inputNode.value).to.equal('Artichoke');
2461
2491
  });
2462
2492
 
@@ -2476,30 +2506,28 @@ describe('lion-combobox', () => {
2476
2506
  expect(_inputNode.value).to.eql('');
2477
2507
 
2478
2508
  el.setCheckedIndex(-1);
2479
- el.autocomplete = 'none';
2480
- el.setCheckedIndex([0]);
2481
- el.setCheckedIndex([1]);
2482
- expect(_inputNode.value).to.equal('');
2483
-
2484
2509
  el.setCheckedIndex(-1);
2485
2510
  el.autocomplete = 'list';
2486
- el.setCheckedIndex([0]);
2487
- el.setCheckedIndex([1]);
2488
- expect(_inputNode.value).to.equal('');
2511
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2512
+ expect(_inputNode.value).to.equal('art');
2513
+
2514
+ el.setCheckedIndex(-1);
2515
+ el.value = '';
2516
+ el.autocomplete = 'none';
2517
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2518
+ expect(_inputNode.value).to.equal('art');
2489
2519
 
2490
2520
  el.setCheckedIndex(-1);
2521
+ el.value = '';
2491
2522
  el.autocomplete = 'inline';
2492
- el.setCheckedIndex([0]);
2523
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2493
2524
  expect(_inputNode.value).to.equal('Artichoke');
2494
- el.setCheckedIndex([1]);
2495
- expect(_inputNode.value).to.equal('Chard');
2496
2525
 
2497
2526
  el.setCheckedIndex(-1);
2527
+ el.value = '';
2498
2528
  el.autocomplete = 'both';
2499
- el.setCheckedIndex([0]);
2529
+ await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2500
2530
  expect(_inputNode.value).to.equal('Artichoke');
2501
- el.setCheckedIndex([1]);
2502
- expect(_inputNode.value).to.equal('Chard');
2503
2531
  });
2504
2532
 
2505
2533
  describe('Subclassers', () => {
@@ -2748,12 +2776,6 @@ describe('lion-combobox', () => {
2748
2776
  async function performChecks(autocompleteMode) {
2749
2777
  await el.updateComplete;
2750
2778
 
2751
- el.formElements[0].click();
2752
-
2753
- // FIXME: fix properly for Webkit
2754
- // expect(_inputNode.value).to.equal('Aha', autocompleteMode);
2755
- expect(el.checkedIndex).to.equal(0, autocompleteMode);
2756
-
2757
2779
  await mimicUserTypingAdvanced(el, ['A', 'r', 't', 'i']);
2758
2780
  await el.updateComplete;
2759
2781
  expect(_inputNode.value).to.equal('Arti', `autocompleteMode is ${autocompleteMode}`);
@@ -3431,4 +3453,131 @@ describe('lion-combobox', () => {
3431
3453
  });
3432
3454
  });
3433
3455
  });
3456
+
3457
+ describe('Disabled state', () => {
3458
+ it('does not open overlay or allow input when disabled', async () => {
3459
+ const [el] = await fruitFixture({ disabled: true });
3460
+ expect(el.disabled).to.equal(true);
3461
+
3462
+ const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ (el));
3463
+
3464
+ expect(el.disabled).to.be.true;
3465
+ expect(_inputNode.disabled).to.be.true;
3466
+
3467
+ // Try to open overlay by clicking input
3468
+ _inputNode.dispatchEvent(new Event('click', { bubbles: true, composed: true }));
3469
+ await el.updateComplete;
3470
+ expect(el.opened).to.be.false;
3471
+ });
3472
+
3473
+ it('does open overlay or allow input when disabled state is removed after it was previously disabled', async () => {
3474
+ const [el] = await fruitFixture({ disabled: true });
3475
+ expect(el.disabled).to.equal(true);
3476
+
3477
+ const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ (el));
3478
+
3479
+ expect(el.disabled).to.be.true;
3480
+ expect(_inputNode.disabled).to.be.true;
3481
+
3482
+ // Try to open overlay by clicking input
3483
+ _inputNode.dispatchEvent(new Event('click', { bubbles: true, composed: true }));
3484
+ await el.updateComplete;
3485
+ expect(el.opened).to.be.false;
3486
+
3487
+ el.disabled = false;
3488
+ await el.updateComplete;
3489
+
3490
+ expect(el.disabled).to.be.false;
3491
+ expect(_inputNode.disabled).to.be.false;
3492
+
3493
+ // Try to open overlay by clicking input
3494
+ async function open() {
3495
+ await mimicUserTyping(/** @type {LionCombobox} */ (el), 'ch');
3496
+ return el.updateComplete;
3497
+ }
3498
+
3499
+ await open();
3500
+ expect(el.opened).to.be.true;
3501
+ });
3502
+
3503
+ it('sets aria-disabled and disables focus when combobox is disabled', async () => {
3504
+ const [el] = await fruitFixture({ disabled: true });
3505
+ const { _comboboxNode, _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ (el));
3506
+
3507
+ // input should not be focusable
3508
+ expect(_comboboxNode.tabIndex).to.equal(-1);
3509
+ expect(_inputNode.tabIndex).to.equal(-1);
3510
+
3511
+ // aria-disabled should be set
3512
+ expect(el.getAttribute('aria-disabled')).to.equal('true');
3513
+
3514
+ // aria-disabled should be set
3515
+ expect(_inputNode.getAttribute('aria-disabled')).to.equal('true');
3516
+
3517
+ await expect(el).to.be.accessible();
3518
+ });
3519
+
3520
+ it('ensure focus when combobox is readonly', async () => {
3521
+ const [el] = await fruitFixture({ readonly: true });
3522
+ const { _comboboxNode, _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ (el));
3523
+
3524
+ // input should be focusable
3525
+ expect(_inputNode.tabIndex).to.equal(0);
3526
+
3527
+ // aria-disabled should be set
3528
+ expect(_comboboxNode.getAttribute('aria-readonly')).to.equal('true');
3529
+
3530
+ // aria-disabled should be set
3531
+ expect(_inputNode.getAttribute('aria-readonly')).to.equal('true');
3532
+
3533
+ await expect(el).to.be.accessible();
3534
+ });
3535
+ });
3536
+
3537
+ describe('Readonly state', () => {
3538
+ it('does not open overlay or allow input when readonly', async () => {
3539
+ const [el] = await fruitFixture({ readonly: true });
3540
+
3541
+ const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ (el));
3542
+ expect(el.readOnly).to.equal(true);
3543
+
3544
+ expect(el.readOnly).to.be.true;
3545
+ expect(_inputNode.readOnly).to.be.true;
3546
+
3547
+ // Try to open overlay by clicking input
3548
+ _inputNode.dispatchEvent(new Event('click', { bubbles: true, composed: true }));
3549
+ await el.updateComplete;
3550
+ expect(el.opened).to.be.false;
3551
+ });
3552
+
3553
+ it('does open overlay or allow input when readonly state is removed after it was previously set', async () => {
3554
+ const [el] = await fruitFixture({ readonly: true });
3555
+ expect(el.readOnly).to.equal(true);
3556
+
3557
+ const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ (el));
3558
+
3559
+ expect(el.readOnly).to.be.true;
3560
+ expect(_inputNode.readOnly).to.be.true;
3561
+
3562
+ // Try to open overlay by clicking input
3563
+ _inputNode.dispatchEvent(new Event('click', { bubbles: true, composed: true }));
3564
+ await el.updateComplete;
3565
+ expect(el.opened).to.be.false;
3566
+
3567
+ el.readOnly = false;
3568
+ await el.updateComplete;
3569
+
3570
+ expect(el.readOnly).to.be.false;
3571
+ expect(_inputNode.readOnly).to.be.false;
3572
+
3573
+ // Try to open overlay by clicking input
3574
+ async function open() {
3575
+ await mimicUserTyping(/** @type {LionCombobox} */ (el), 'ch');
3576
+ return el.updateComplete;
3577
+ }
3578
+
3579
+ await open();
3580
+ expect(el.opened).to.be.true;
3581
+ });
3582
+ });
3434
3583
  });
@@ -1,6 +1,9 @@
1
1
  import { html, LitElement } from 'lit';
2
2
  import { OverlayMixin, withModalDialogConfig } from '@lion/ui/overlays.js';
3
3
 
4
+ /**
5
+ * @customElement lion-dialog
6
+ */
4
7
  export class LionDialog extends OverlayMixin(LitElement) {
5
8
  /** @type {any} */
6
9
  static get properties() {
@@ -7,6 +7,15 @@ const EVENT = {
7
7
  TRANSITION_START: 'transitionstart',
8
8
  };
9
9
 
10
+ /**
11
+ * LionDrawer: extension of lion-collapsible with drawer specific styles and animations
12
+ *
13
+ * @slot invoker - The invoker element for the drawer
14
+ * @slot content - The content element for the drawer
15
+ * @slot headline - The headline element for the drawer
16
+ *
17
+ * @customElement lion-drawer
18
+ */
10
19
  export class LionDrawer extends LionCollapsible {
11
20
  static get properties() {
12
21
  return {
@@ -2,7 +2,7 @@ import { LitElement } from 'lit';
2
2
  import { FormGroupMixin } from '@lion/ui/form-core.js';
3
3
 
4
4
  /**
5
- * @desc LionFieldset is basically a 'sub form' and can have its own nested sub forms.
5
+ * LionFieldset is basically a 'sub form' and can have its own nested sub forms.
6
6
  * It mimics the native <fieldset> element in this sense, but has all the functionality of
7
7
  * a FormControl (advanced styling, validation, interaction states etc.) Also see
8
8
  * FormGroupMixin it depends on.
@@ -15,7 +15,6 @@ const throwFormNodeError = () => {
15
15
  *
16
16
  * @customElement lion-form
17
17
  */
18
- // eslint-disable-next-line no-unused-vars
19
18
  export class LionForm extends LionFieldset {
20
19
  constructor() {
21
20
  super();
@@ -198,6 +198,7 @@ const FocusMixinImplementation = superclass =>
198
198
  * @private
199
199
  */
200
200
  __teardownEventsForFocusMixin() {
201
+ if (!this._focusableNode) return;
201
202
  this._focusableNode?.removeEventListener(
202
203
  'focus',
203
204
  /** @type {EventListenerOrEventListenerObject} */ (this.__redispatchFocus),