@lion/ui 0.25.0 → 0.26.0

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 (771) hide show
  1. package/README.md +25 -7
  2. package/accordion-test-suites.js +2 -0
  3. package/collapsible-test-suites.js +1 -0
  4. package/combobox-test-suites.js +2 -0
  5. package/components/accordion/test/lion-accordion.test.js +2 -565
  6. package/components/accordion/test-suites/lion-accordion.suite.js +596 -0
  7. package/components/button/test-suites/LionButton.suite.js +13 -11
  8. package/components/button/test-suites/LionButtonReset.suite.js +11 -3
  9. package/components/button/test-suites/LionButtonSubmit.suite.js +11 -3
  10. package/components/calendar/test/lion-calendar.test.js +1 -1570
  11. package/components/calendar/test-suites/LionCalendar.suite.js +1617 -5
  12. package/components/checkbox-group/test/lion-checkbox-group-integrations.test.js +5 -2
  13. package/components/checkbox-group/test/lion-checkbox-indeterminate-integrations.test.js +13 -4
  14. package/components/checkbox-group/test/lion-checkbox-integrations.test.js +4 -1
  15. package/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.js +54 -54
  16. package/components/collapsible/test/lion-collapsible.test.js +2 -126
  17. package/components/collapsible/test-suites/LionCollapsible.suite.js +147 -0
  18. package/components/combobox/test/lion-combobox-integrations.test.js +11 -4
  19. package/components/combobox/test/lion-combobox.test.js +2 -3622
  20. package/components/combobox/test-suites/lion-combobox.suite.js +3665 -0
  21. package/components/core/src/ScopedElementsMixin.js +16 -1
  22. package/components/core/test-helpers/isElementVisible.js +21 -0
  23. package/components/core/test-helpers/resolveSuiteTag.js +97 -0
  24. package/components/core/types/TestSuiteTypes.ts +43 -0
  25. package/components/dialog/test/lion-dialog.integration.test.js +9 -10
  26. package/components/dialog/test/lion-dialog.test.js +2 -399
  27. package/components/dialog/test-suites/lion-dialog.suite.js +412 -0
  28. package/components/drawer/test/lion-drawer.test.js +2 -84
  29. package/components/drawer/test-suites/LionDrawer.suite.js +108 -0
  30. package/components/fieldset/test/lion-fieldset.test.js +7 -2
  31. package/components/form/test/lion-form.test.js +2 -348
  32. package/components/form/test-suites/lion-form.suite.js +398 -0
  33. package/components/form-core/test/FormatMixin.test.js +4 -1
  34. package/components/form-core/test/choice-group/CustomChoiceGroupMixin.integrations.test.js +13 -0
  35. package/components/form-core/test-suites/FormRegistrationMixins.suite.js +21 -26
  36. package/components/form-core/test-suites/FormatMixin.suite.js +45 -43
  37. package/components/form-core/test-suites/InteractionStateMixin.suite.js +19 -15
  38. package/components/form-core/test-suites/NativeTextFieldMixin.suite.js +8 -9
  39. package/components/form-core/test-suites/ValidateMixin.suite.js +14 -10
  40. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +15 -2
  41. package/components/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js +55 -33
  42. package/components/form-core/test-suites/choice-group/ChoiceInputMixin.suite.js +7 -6
  43. package/components/form-core/test-suites/choice-group/CustomChoiceGroupMixin.suite.js +19 -22
  44. package/components/form-core/test-suites/form-group/FormGroupMixin-input.suite.js +14 -5
  45. package/components/form-core/test-suites/form-group/FormGroupMixin.suite.js +13 -4
  46. package/components/icon/test/lion-icon.test.js +2 -188
  47. package/components/icon/test-suites/lion-icon.suite.js +206 -0
  48. package/components/input/test/input-integrations.test.js +18 -15
  49. package/components/input/test/lion-input-integrations.test.js +7 -3
  50. package/components/input/test/lion-input.test.js +2 -198
  51. package/components/input/test-suites/lion-input.suite.js +214 -0
  52. package/components/input-amount/src/parsers.js +5 -1
  53. package/components/input-amount/test/lion-input-amount-integrations.test.js +7 -3
  54. package/components/input-amount/test/lion-input-amount.test.js +2 -421
  55. package/components/input-amount/test/parsers.test.js +12 -0
  56. package/components/input-amount/test-suites/lion-input-amount.suite.js +418 -0
  57. package/components/input-amount-dropdown/test/LionInputAmountDropdown.test.js +3 -2
  58. package/components/input-amount-dropdown/test/lion-input-amount-dropdown-integrations.test.js +4 -2
  59. package/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.js +6 -4
  60. package/components/input-date/test/lion-input-date-integrations.test.js +7 -3
  61. package/components/input-date/test/lion-input-date.test.js +2 -177
  62. package/components/input-date/test-suites/lion-input-date.suite.js +195 -0
  63. package/components/input-datepicker/test/lion-input-datepicker-integrations.test.js +7 -3
  64. package/components/input-datepicker/test/lion-input-datepicker.test.js +1 -844
  65. package/components/input-datepicker/test-helpers/DatepickerInputObject.js +26 -0
  66. package/components/input-datepicker/test-suites/LionInputDatepicker.suite.js +899 -14
  67. package/components/input-email/test/lion-input-email-integrations.test.js +4 -2
  68. package/components/input-email/test/lion-input-email.test.js +2 -46
  69. package/components/input-email/test-suites/lion-input-email.suite.js +63 -0
  70. package/components/input-file/test/lion-input-file.test.js +2 -1274
  71. package/components/input-file/test-suites/lion-input-file.suite.js +1505 -0
  72. package/components/input-iban/test/lion-input-iban-integrations.test.js +4 -2
  73. package/components/input-iban/test/lion-input-iban.test.js +2 -73
  74. package/components/input-iban/test-suites/lion-input-iban.suite.js +88 -0
  75. package/components/input-range/test/lion-input-range.test.js +2 -176
  76. package/components/input-range/test-suites/lion-input-range.suite.js +195 -0
  77. package/components/input-stepper/test-suites/lion-input-stepper.integration.suite.js +14 -3
  78. package/components/input-stepper/test-suites/lion-input-stepper.suite.js +14 -10
  79. package/components/input-tel/test/LionInputTel.test.js +3 -1
  80. package/components/input-tel/test-suites/LionInputTel.suite.js +9 -4
  81. package/components/input-tel-dropdown/test/LionInputTelDropdown.test.js +6 -4
  82. package/components/input-tel-dropdown/test-suites/LionInputTelDropdown--parentheses.suite.js +10 -13
  83. package/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js +6 -5
  84. package/components/listbox/test-suites/ListboxMixin.suite.js +19 -19
  85. package/components/menu/test-suites/InteractiveListMixin.suite.js +6 -9
  86. package/components/menu/test-suites/LionMenu.suite.js +6 -10
  87. package/components/menu/test-suites/LionMenuInteractions.suite.js +13 -4
  88. package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +6 -9
  89. package/components/menu/test-suites/MultiLevelListMixin.suite.js +15 -11
  90. package/components/overlays/test/OverlayController.test.js +6 -1
  91. package/components/overlays/test-suites/OverlayMixin.suite.js +3 -5
  92. package/components/pagination/test/lion-pagination.test.js +2 -176
  93. package/components/pagination/test-suites/LionPagination.suite.js +201 -0
  94. package/components/progress-indicator/test/lion-progress-indicator.test.js +2 -203
  95. package/components/progress-indicator/test-suites/lion-progress-indicator.suite.js +214 -0
  96. package/components/radio-group/test/lion-radio-group.test.js +2 -162
  97. package/components/radio-group/test/lion-radio.test.js +2 -54
  98. package/components/radio-group/test-suites/lion-radio-group.suite.js +173 -0
  99. package/components/radio-group/test-suites/lion-radio.suite.js +70 -0
  100. package/components/select/test/lion-select.test.js +2 -79
  101. package/components/select/test-suites/LionSelect.suite.js +94 -0
  102. package/components/select-rich/test/lion-select-invoker.test.js +2 -86
  103. package/components/select-rich/test/lion-select-rich-interaction.test.js +2 -430
  104. package/components/select-rich/test/lion-select-rich.test.js +2 -1193
  105. package/components/select-rich/test-suites/lion-select-invoker.suite.js +98 -0
  106. package/components/select-rich/test-suites/lion-select-rich-interaction.suite.js +452 -0
  107. package/components/select-rich/test-suites/lion-select-rich.suite.js +1236 -0
  108. package/components/steps/test/lion-step.test.js +2 -97
  109. package/components/steps/test/lion-steps.test.js +2 -433
  110. package/components/steps/test-suites/lion-step.suite.js +113 -0
  111. package/components/steps/test-suites/lion-steps.suite.js +445 -0
  112. package/components/switch/test/lion-switch.test.js +2 -232
  113. package/components/switch/test-suites/lion-switch.suite.js +450 -0
  114. package/components/tabs/test/lion-tabs.test.js +2 -596
  115. package/components/tabs/test-suites/lion-tabs.suite.js +617 -0
  116. package/components/textarea/src/LionTextarea.js +5 -2
  117. package/components/textarea/test/lion-textarea.test.js +2 -159
  118. package/components/textarea/test-suites/lion-textarea.suite.js +192 -0
  119. package/components/tooltip/test/lion-tooltip.test.js +2 -393
  120. package/components/tooltip/test-suites/lion-tooltip.suite.js +409 -0
  121. package/custom-elements.json +14855 -10161
  122. package/dialog-test-suites.js +2 -0
  123. package/dist-types/components/accordion/test/lion-accordion.test.d.ts +1 -2
  124. package/dist-types/components/accordion/test/lion-accordion.test.d.ts.map +1 -1
  125. package/dist-types/components/accordion/test-suites/lion-accordion.suite.d.ts +18 -0
  126. package/dist-types/components/accordion/test-suites/lion-accordion.suite.d.ts.map +1 -0
  127. package/dist-types/components/button/test-suites/LionButton.suite.d.ts +10 -4
  128. package/dist-types/components/button/test-suites/LionButton.suite.d.ts.map +1 -1
  129. package/dist-types/components/button/test-suites/LionButtonReset.suite.d.ts +10 -4
  130. package/dist-types/components/button/test-suites/LionButtonReset.suite.d.ts.map +1 -1
  131. package/dist-types/components/button/test-suites/LionButtonSubmit.suite.d.ts +10 -4
  132. package/dist-types/components/button/test-suites/LionButtonSubmit.suite.d.ts.map +1 -1
  133. package/dist-types/components/calendar/test/lion-calendar.test.d.ts +1 -3
  134. package/dist-types/components/calendar/test/lion-calendar.test.d.ts.map +1 -1
  135. package/dist-types/components/calendar/test-suites/LionCalendar.suite.d.ts +13 -4
  136. package/dist-types/components/calendar/test-suites/LionCalendar.suite.d.ts.map +1 -1
  137. package/dist-types/components/checkbox-group/test/lion-checkbox-group-integrations.test.d.ts.map +1 -1
  138. package/dist-types/components/checkbox-group/test/lion-checkbox-integrations.test.d.ts.map +1 -1
  139. package/dist-types/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.d.ts +12 -9
  140. package/dist-types/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.d.ts.map +1 -1
  141. package/dist-types/components/collapsible/test/lion-collapsible.test.d.ts +1 -3
  142. package/dist-types/components/collapsible/test/lion-collapsible.test.d.ts.map +1 -1
  143. package/dist-types/components/collapsible/test-suites/LionCollapsible.suite.d.ts +18 -0
  144. package/dist-types/components/collapsible/test-suites/LionCollapsible.suite.d.ts.map +1 -0
  145. package/dist-types/components/combobox/test/lion-combobox.test.d.ts +1 -5
  146. package/dist-types/components/combobox/test/lion-combobox.test.d.ts.map +1 -1
  147. package/dist-types/components/combobox/test-suites/lion-combobox.suite.d.ts +17 -0
  148. package/dist-types/components/combobox/test-suites/lion-combobox.suite.d.ts.map +1 -0
  149. package/dist-types/components/core/src/ScopedElementsMixin.d.ts.map +1 -1
  150. package/dist-types/components/core/test-helpers/isElementVisible.d.ts +12 -0
  151. package/dist-types/components/core/test-helpers/isElementVisible.d.ts.map +1 -0
  152. package/dist-types/components/core/test-helpers/resolveSuiteTag.d.ts +29 -0
  153. package/dist-types/components/core/test-helpers/resolveSuiteTag.d.ts.map +1 -0
  154. package/dist-types/components/core/types/TestSuiteTypes.d.ts +40 -0
  155. package/dist-types/components/core/types/TestSuiteTypes.d.ts.map +1 -0
  156. package/dist-types/components/dialog/test/lion-dialog.integration.test.d.ts.map +1 -1
  157. package/dist-types/components/dialog/test/lion-dialog.test.d.ts +1 -5
  158. package/dist-types/components/dialog/test/lion-dialog.test.d.ts.map +1 -1
  159. package/dist-types/components/dialog/test-suites/lion-dialog.suite.d.ts +19 -0
  160. package/dist-types/components/dialog/test-suites/lion-dialog.suite.d.ts.map +1 -0
  161. package/dist-types/components/drawer/test/lion-drawer.test.d.ts +1 -3
  162. package/dist-types/components/drawer/test/lion-drawer.test.d.ts.map +1 -1
  163. package/dist-types/components/drawer/test-suites/LionDrawer.suite.d.ts +20 -0
  164. package/dist-types/components/drawer/test-suites/LionDrawer.suite.d.ts.map +1 -0
  165. package/dist-types/components/fieldset/test/lion-fieldset.test.d.ts.map +1 -1
  166. package/dist-types/components/form/test/lion-form.test.d.ts +1 -12
  167. package/dist-types/components/form/test/lion-form.test.d.ts.map +1 -1
  168. package/dist-types/components/form/test-suites/lion-form.suite.d.ts +39 -0
  169. package/dist-types/components/form/test-suites/lion-form.suite.d.ts.map +1 -0
  170. package/dist-types/components/form-core/test-suites/FormRegistrationMixins.suite.d.ts +14 -17
  171. package/dist-types/components/form-core/test-suites/FormRegistrationMixins.suite.d.ts.map +1 -1
  172. package/dist-types/components/form-core/test-suites/FormatMixin.suite.d.ts +22 -38
  173. package/dist-types/components/form-core/test-suites/FormatMixin.suite.d.ts.map +1 -1
  174. package/dist-types/components/form-core/test-suites/InteractionStateMixin.suite.d.ts +11 -5
  175. package/dist-types/components/form-core/test-suites/InteractionStateMixin.suite.d.ts.map +1 -1
  176. package/dist-types/components/form-core/test-suites/NativeTextFieldMixin.suite.d.ts +9 -4
  177. package/dist-types/components/form-core/test-suites/NativeTextFieldMixin.suite.d.ts.map +1 -1
  178. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts +9 -4
  179. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  180. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts +10 -1
  181. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  182. package/dist-types/components/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.d.ts +17 -6
  183. package/dist-types/components/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.d.ts.map +1 -1
  184. package/dist-types/components/form-core/test-suites/choice-group/ChoiceInputMixin.suite.d.ts +5 -2
  185. package/dist-types/components/form-core/test-suites/choice-group/ChoiceInputMixin.suite.d.ts.map +1 -1
  186. package/dist-types/components/form-core/test-suites/choice-group/CustomChoiceGroupMixin.suite.d.ts +10 -5
  187. package/dist-types/components/form-core/test-suites/choice-group/CustomChoiceGroupMixin.suite.d.ts.map +1 -1
  188. package/dist-types/components/form-core/test-suites/form-group/FormGroupMixin-input.suite.d.ts +10 -5
  189. package/dist-types/components/form-core/test-suites/form-group/FormGroupMixin-input.suite.d.ts.map +1 -1
  190. package/dist-types/components/form-core/test-suites/form-group/FormGroupMixin.suite.d.ts +10 -5
  191. package/dist-types/components/form-core/test-suites/form-group/FormGroupMixin.suite.d.ts.map +1 -1
  192. package/dist-types/components/icon/test/lion-icon.test.d.ts +1 -4
  193. package/dist-types/components/icon/test/lion-icon.test.d.ts.map +1 -1
  194. package/dist-types/components/icon/test-suites/lion-icon.suite.d.ts +16 -0
  195. package/dist-types/components/icon/test-suites/lion-icon.suite.d.ts.map +1 -0
  196. package/dist-types/components/input/test/lion-input-integrations.test.d.ts.map +1 -1
  197. package/dist-types/components/input/test/lion-input.test.d.ts +1 -2
  198. package/dist-types/components/input/test/lion-input.test.d.ts.map +1 -1
  199. package/dist-types/components/input/test-suites/lion-input.suite.d.ts +17 -0
  200. package/dist-types/components/input/test-suites/lion-input.suite.d.ts.map +1 -0
  201. package/dist-types/components/input-amount/src/parsers.d.ts.map +1 -1
  202. package/dist-types/components/input-amount/test/lion-input-amount-integrations.test.d.ts.map +1 -1
  203. package/dist-types/components/input-amount/test/lion-input-amount.test.d.ts +1 -2
  204. package/dist-types/components/input-amount/test/lion-input-amount.test.d.ts.map +1 -1
  205. package/dist-types/components/input-amount/test-suites/lion-input-amount.suite.d.ts +19 -0
  206. package/dist-types/components/input-amount/test-suites/lion-input-amount.suite.d.ts.map +1 -0
  207. package/dist-types/components/input-amount-dropdown/test/lion-input-amount-dropdown-integrations.test.d.ts.map +1 -1
  208. package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts +7 -3
  209. package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts.map +1 -1
  210. package/dist-types/components/input-date/test/lion-input-date-integrations.test.d.ts.map +1 -1
  211. package/dist-types/components/input-date/test/lion-input-date.test.d.ts +1 -3
  212. package/dist-types/components/input-date/test/lion-input-date.test.d.ts.map +1 -1
  213. package/dist-types/components/input-date/test-suites/lion-input-date.suite.d.ts +15 -0
  214. package/dist-types/components/input-date/test-suites/lion-input-date.suite.d.ts.map +1 -0
  215. package/dist-types/components/input-datepicker/test/lion-input-datepicker-integrations.test.d.ts.map +1 -1
  216. package/dist-types/components/input-datepicker/test/lion-input-datepicker.test.d.ts +1 -2
  217. package/dist-types/components/input-datepicker/test/lion-input-datepicker.test.d.ts.map +1 -1
  218. package/dist-types/components/input-datepicker/test-helpers/DatepickerInputObject.d.ts +8 -0
  219. package/dist-types/components/input-datepicker/test-helpers/DatepickerInputObject.d.ts.map +1 -1
  220. package/dist-types/components/input-datepicker/test-suites/LionInputDatepicker.suite.d.ts +15 -5
  221. package/dist-types/components/input-datepicker/test-suites/LionInputDatepicker.suite.d.ts.map +1 -1
  222. package/dist-types/components/input-email/test/lion-input-email-integrations.test.d.ts.map +1 -1
  223. package/dist-types/components/input-email/test/lion-input-email.test.d.ts +1 -3
  224. package/dist-types/components/input-email/test/lion-input-email.test.d.ts.map +1 -1
  225. package/dist-types/components/input-email/test-suites/lion-input-email.suite.d.ts +15 -0
  226. package/dist-types/components/input-email/test-suites/lion-input-email.suite.d.ts.map +1 -0
  227. package/dist-types/components/input-file/test/lion-input-file.test.d.ts +1 -5
  228. package/dist-types/components/input-file/test/lion-input-file.test.d.ts.map +1 -1
  229. package/dist-types/components/input-file/test-suites/lion-input-file.suite.d.ts +19 -0
  230. package/dist-types/components/input-file/test-suites/lion-input-file.suite.d.ts.map +1 -0
  231. package/dist-types/components/input-iban/test/lion-input-iban-integrations.test.d.ts.map +1 -1
  232. package/dist-types/components/input-iban/test/lion-input-iban.test.d.ts +1 -3
  233. package/dist-types/components/input-iban/test/lion-input-iban.test.d.ts.map +1 -1
  234. package/dist-types/components/input-iban/test-suites/lion-input-iban.suite.d.ts +15 -0
  235. package/dist-types/components/input-iban/test-suites/lion-input-iban.suite.d.ts.map +1 -0
  236. package/dist-types/components/input-range/test/lion-input-range.test.d.ts +1 -3
  237. package/dist-types/components/input-range/test/lion-input-range.test.d.ts.map +1 -1
  238. package/dist-types/components/input-range/test-suites/lion-input-range.suite.d.ts +15 -0
  239. package/dist-types/components/input-range/test-suites/lion-input-range.suite.d.ts.map +1 -0
  240. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.integration.suite.d.ts +10 -2
  241. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.integration.suite.d.ts.map +1 -1
  242. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.suite.d.ts +10 -2
  243. package/dist-types/components/input-stepper/test-suites/lion-input-stepper.suite.d.ts.map +1 -1
  244. package/dist-types/components/input-tel/test-suites/LionInputTel.suite.d.ts +6 -4
  245. package/dist-types/components/input-tel/test-suites/LionInputTel.suite.d.ts.map +1 -1
  246. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown--parentheses.suite.d.ts +7 -3
  247. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown--parentheses.suite.d.ts.map +1 -1
  248. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.d.ts +7 -3
  249. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.d.ts.map +1 -1
  250. package/dist-types/components/listbox/test-suites/ListboxMixin.suite.d.ts +3 -4
  251. package/dist-types/components/listbox/test-suites/ListboxMixin.suite.d.ts.map +1 -1
  252. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +6 -5
  253. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -1
  254. package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +6 -5
  255. package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +1 -1
  256. package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts +10 -4
  257. package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts.map +1 -1
  258. package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +6 -5
  259. package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +1 -1
  260. package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +7 -6
  261. package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +1 -1
  262. package/dist-types/components/overlays/test-suites/OverlayMixin.suite.d.ts.map +1 -1
  263. package/dist-types/components/pagination/test/lion-pagination.test.d.ts +1 -3
  264. package/dist-types/components/pagination/test/lion-pagination.test.d.ts.map +1 -1
  265. package/dist-types/components/pagination/test-suites/LionPagination.suite.d.ts +18 -0
  266. package/dist-types/components/pagination/test-suites/LionPagination.suite.d.ts.map +1 -0
  267. package/dist-types/components/progress-indicator/test/lion-progress-indicator.test.d.ts +1 -3
  268. package/dist-types/components/progress-indicator/test/lion-progress-indicator.test.d.ts.map +1 -1
  269. package/dist-types/components/progress-indicator/test-suites/lion-progress-indicator.suite.d.ts +15 -0
  270. package/dist-types/components/progress-indicator/test-suites/lion-progress-indicator.suite.d.ts.map +1 -0
  271. package/dist-types/components/radio-group/test/lion-radio-group.test.d.ts +1 -5
  272. package/dist-types/components/radio-group/test/lion-radio-group.test.d.ts.map +1 -1
  273. package/dist-types/components/radio-group/test/lion-radio.test.d.ts +1 -2
  274. package/dist-types/components/radio-group/test/lion-radio.test.d.ts.map +1 -1
  275. package/dist-types/components/radio-group/test-suites/lion-radio-group.suite.d.ts +19 -0
  276. package/dist-types/components/radio-group/test-suites/lion-radio-group.suite.d.ts.map +1 -0
  277. package/dist-types/components/radio-group/test-suites/lion-radio.suite.d.ts +17 -0
  278. package/dist-types/components/radio-group/test-suites/lion-radio.suite.d.ts.map +1 -0
  279. package/dist-types/components/select/test/lion-select.test.d.ts +1 -2
  280. package/dist-types/components/select/test/lion-select.test.d.ts.map +1 -1
  281. package/dist-types/components/select/test-suites/LionSelect.suite.d.ts +16 -0
  282. package/dist-types/components/select/test-suites/LionSelect.suite.d.ts.map +1 -0
  283. package/dist-types/components/select-rich/test/lion-select-invoker.test.d.ts +1 -2
  284. package/dist-types/components/select-rich/test/lion-select-invoker.test.d.ts.map +1 -1
  285. package/dist-types/components/select-rich/test/lion-select-rich-interaction.test.d.ts +1 -5
  286. package/dist-types/components/select-rich/test/lion-select-rich-interaction.test.d.ts.map +1 -1
  287. package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts +1 -8
  288. package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts.map +1 -1
  289. package/dist-types/components/select-rich/test-suites/lion-select-invoker.suite.d.ts +19 -0
  290. package/dist-types/components/select-rich/test-suites/lion-select-invoker.suite.d.ts.map +1 -0
  291. package/dist-types/components/select-rich/test-suites/lion-select-rich-interaction.suite.d.ts +19 -0
  292. package/dist-types/components/select-rich/test-suites/lion-select-rich-interaction.suite.d.ts.map +1 -0
  293. package/dist-types/components/select-rich/test-suites/lion-select-rich.suite.d.ts +21 -0
  294. package/dist-types/components/select-rich/test-suites/lion-select-rich.suite.d.ts.map +1 -0
  295. package/dist-types/components/steps/test/lion-step.test.d.ts +1 -2
  296. package/dist-types/components/steps/test/lion-step.test.d.ts.map +1 -1
  297. package/dist-types/components/steps/test/lion-steps.test.d.ts +1 -8
  298. package/dist-types/components/steps/test/lion-steps.test.d.ts.map +1 -1
  299. package/dist-types/components/steps/test-suites/lion-step.suite.d.ts +17 -0
  300. package/dist-types/components/steps/test-suites/lion-step.suite.d.ts.map +1 -0
  301. package/dist-types/components/steps/test-suites/lion-steps.suite.d.ts +22 -0
  302. package/dist-types/components/steps/test-suites/lion-steps.suite.d.ts.map +1 -0
  303. package/dist-types/components/switch/test/lion-switch.test.d.ts +1 -4
  304. package/dist-types/components/switch/test/lion-switch.test.d.ts.map +1 -1
  305. package/dist-types/components/switch/test-suites/lion-switch.suite.d.ts +17 -0
  306. package/dist-types/components/switch/test-suites/lion-switch.suite.d.ts.map +1 -0
  307. package/dist-types/components/tabs/test/lion-tabs.test.d.ts +1 -5
  308. package/dist-types/components/tabs/test/lion-tabs.test.d.ts.map +1 -1
  309. package/dist-types/components/tabs/test-suites/lion-tabs.suite.d.ts +17 -0
  310. package/dist-types/components/tabs/test-suites/lion-tabs.suite.d.ts.map +1 -0
  311. package/dist-types/components/textarea/src/LionTextarea.d.ts.map +1 -1
  312. package/dist-types/components/textarea/test/lion-textarea.test.d.ts +1 -3
  313. package/dist-types/components/textarea/test/lion-textarea.test.d.ts.map +1 -1
  314. package/dist-types/components/textarea/test-suites/lion-textarea.suite.d.ts +15 -0
  315. package/dist-types/components/textarea/test-suites/lion-textarea.suite.d.ts.map +1 -0
  316. package/dist-types/components/tooltip/test/lion-tooltip.test.d.ts +1 -3
  317. package/dist-types/components/tooltip/test/lion-tooltip.test.d.ts.map +1 -1
  318. package/dist-types/components/tooltip/test-suites/lion-tooltip.suite.d.ts +19 -0
  319. package/dist-types/components/tooltip/test-suites/lion-tooltip.suite.d.ts.map +1 -0
  320. package/dist-types/exports/accordion-test-suites.d.ts +2 -0
  321. package/dist-types/exports/accordion-test-suites.d.ts.map +1 -0
  322. package/dist-types/exports/collapsible-test-suites.d.ts +2 -0
  323. package/dist-types/exports/collapsible-test-suites.d.ts.map +1 -0
  324. package/dist-types/exports/combobox-test-suites.d.ts +2 -0
  325. package/dist-types/exports/combobox-test-suites.d.ts.map +1 -0
  326. package/dist-types/exports/dialog-test-suites.d.ts +2 -0
  327. package/dist-types/exports/dialog-test-suites.d.ts.map +1 -0
  328. package/dist-types/exports/drawer-test-suites.d.ts +2 -0
  329. package/dist-types/exports/drawer-test-suites.d.ts.map +1 -0
  330. package/dist-types/exports/form-test-suites.d.ts +2 -0
  331. package/dist-types/exports/form-test-suites.d.ts.map +1 -0
  332. package/dist-types/exports/icon-test-suites.d.ts +2 -0
  333. package/dist-types/exports/icon-test-suites.d.ts.map +1 -0
  334. package/dist-types/exports/input-amount-test-suites.d.ts +2 -0
  335. package/dist-types/exports/input-amount-test-suites.d.ts.map +1 -0
  336. package/dist-types/exports/input-date-test-suites.d.ts +2 -0
  337. package/dist-types/exports/input-date-test-suites.d.ts.map +1 -0
  338. package/dist-types/exports/input-email-test-suites.d.ts +2 -0
  339. package/dist-types/exports/input-email-test-suites.d.ts.map +1 -0
  340. package/dist-types/exports/input-file-test-suites.d.ts +2 -0
  341. package/dist-types/exports/input-file-test-suites.d.ts.map +1 -0
  342. package/dist-types/exports/input-iban-test-suites.d.ts +2 -0
  343. package/dist-types/exports/input-iban-test-suites.d.ts.map +1 -0
  344. package/dist-types/exports/input-range-test-suites.d.ts +2 -0
  345. package/dist-types/exports/input-range-test-suites.d.ts.map +1 -0
  346. package/dist-types/exports/input-stepper-suites.d.ts +7 -2
  347. package/dist-types/exports/input-stepper-suites.d.ts.map +1 -1
  348. package/dist-types/exports/input-test-suites.d.ts +2 -0
  349. package/dist-types/exports/input-test-suites.d.ts.map +1 -0
  350. package/dist-types/exports/menu-test-suites.d.ts.map +1 -1
  351. package/dist-types/exports/pagination-test-suites.d.ts +2 -0
  352. package/dist-types/exports/pagination-test-suites.d.ts.map +1 -0
  353. package/dist-types/exports/progress-indicator-test-suites.d.ts +2 -0
  354. package/dist-types/exports/progress-indicator-test-suites.d.ts.map +1 -0
  355. package/dist-types/exports/radio-group-test-suites.d.ts +3 -0
  356. package/dist-types/exports/radio-group-test-suites.d.ts.map +1 -0
  357. package/dist-types/exports/select-rich-test-suites.d.ts +4 -0
  358. package/dist-types/exports/select-rich-test-suites.d.ts.map +1 -0
  359. package/dist-types/exports/select-test-suites.d.ts +2 -0
  360. package/dist-types/exports/select-test-suites.d.ts.map +1 -0
  361. package/dist-types/exports/steps-test-suites.d.ts +3 -0
  362. package/dist-types/exports/steps-test-suites.d.ts.map +1 -0
  363. package/dist-types/exports/switch-test-suites.d.ts +2 -0
  364. package/dist-types/exports/switch-test-suites.d.ts.map +1 -0
  365. package/dist-types/exports/tabs-test-suites.d.ts +2 -0
  366. package/dist-types/exports/tabs-test-suites.d.ts.map +1 -0
  367. package/dist-types/exports/textarea-test-suites.d.ts +2 -0
  368. package/dist-types/exports/textarea-test-suites.d.ts.map +1 -0
  369. package/dist-types/exports/tooltip-test-suites.d.ts +2 -0
  370. package/dist-types/exports/tooltip-test-suites.d.ts.map +1 -0
  371. package/dist-types/exports/types/test-suites.d.ts +4 -0
  372. package/dist-types/exports/types/test-suites.d.ts.map +1 -0
  373. package/dist-types/tsconfig.tsbuildinfo +1 -1
  374. package/drawer-test-suites.js +1 -0
  375. package/exports/accordion-test-suites.js +2 -0
  376. package/exports/collapsible-test-suites.js +1 -0
  377. package/exports/combobox-test-suites.js +2 -0
  378. package/exports/dialog-test-suites.js +2 -0
  379. package/exports/drawer-test-suites.js +1 -0
  380. package/exports/form-test-suites.js +2 -0
  381. package/exports/icon-test-suites.js +2 -0
  382. package/exports/input-amount-test-suites.js +2 -0
  383. package/exports/input-date-test-suites.js +2 -0
  384. package/exports/input-email-test-suites.js +2 -0
  385. package/exports/input-file-test-suites.js +2 -0
  386. package/exports/input-iban-test-suites.js +2 -0
  387. package/exports/input-range-test-suites.js +2 -0
  388. package/exports/input-stepper-suites.js +18 -4
  389. package/exports/input-test-suites.js +2 -0
  390. package/exports/menu-test-suites.js +3 -0
  391. package/exports/pagination-test-suites.js +1 -0
  392. package/exports/progress-indicator-test-suites.js +2 -0
  393. package/exports/radio-group-test-suites.js +3 -0
  394. package/exports/select-rich-test-suites.js +4 -0
  395. package/exports/select-test-suites.js +1 -0
  396. package/exports/steps-test-suites.js +3 -0
  397. package/exports/switch-test-suites.js +2 -0
  398. package/exports/tabs-test-suites.js +2 -0
  399. package/exports/textarea-test-suites.js +2 -0
  400. package/exports/tooltip-test-suites.js +2 -0
  401. package/exports/types/test-suites.ts +3 -0
  402. package/form-test-suites.js +2 -0
  403. package/generated/awesome-phonenumber/provenance.json +1 -1
  404. package/icon-test-suites.js +2 -0
  405. package/input-amount-test-suites.js +2 -0
  406. package/input-date-test-suites.js +2 -0
  407. package/input-email-test-suites.js +2 -0
  408. package/input-file-test-suites.js +2 -0
  409. package/input-iban-test-suites.js +2 -0
  410. package/input-range-test-suites.js +2 -0
  411. package/input-stepper-suites.js +18 -4
  412. package/input-test-suites.js +2 -0
  413. package/menu-test-suites.js +3 -0
  414. package/package.json +4 -1
  415. package/pagination-test-suites.js +1 -0
  416. package/progress-indicator-test-suites.js +2 -0
  417. package/radio-group-test-suites.js +3 -0
  418. package/select-rich-test-suites.js +4 -0
  419. package/select-test-suites.js +1 -0
  420. package/steps-test-suites.js +3 -0
  421. package/switch-test-suites.js +2 -0
  422. package/tabs-test-suites.js +2 -0
  423. package/textarea-test-suites.js +2 -0
  424. package/tooltip-test-suites.js +2 -0
  425. package/vscode.html-custom-data.json +55 -0
  426. package/web-types.json +219 -1
  427. package/components/button/test/demos.screenshots-test.js +0 -100
  428. package/components/calendar/test/demos.screenshots-test.js +0 -118
  429. package/components/input-file/test/lion-selected-file-list.test.js +0 -199
  430. package/components/select-rich/test/demos.screenshots-test.js +0 -168
  431. package/components/switch/test/lion-switch-button.test.js +0 -204
  432. package/dist-types/components/button/test/demos.screenshots-test.d.ts +0 -2
  433. package/dist-types/components/button/test/demos.screenshots-test.d.ts.map +0 -1
  434. package/dist-types/components/calendar/test/demos.screenshots-test.d.ts +0 -2
  435. package/dist-types/components/calendar/test/demos.screenshots-test.d.ts.map +0 -1
  436. package/dist-types/components/input-file/test/lion-selected-file-list.test.d.ts +0 -5
  437. package/dist-types/components/input-file/test/lion-selected-file-list.test.d.ts.map +0 -1
  438. package/dist-types/components/select-rich/test/demos.screenshots-test.d.ts +0 -2
  439. package/dist-types/components/select-rich/test/demos.screenshots-test.d.ts.map +0 -1
  440. package/dist-types/components/switch/test/lion-switch-button.test.d.ts +0 -4
  441. package/dist-types/components/switch/test/lion-switch-button.test.d.ts.map +0 -1
  442. package/docs/components/accordion/api-table.md +0 -53
  443. package/docs/components/accordion/index.md +0 -13
  444. package/docs/components/accordion/overview.md +0 -141
  445. package/docs/components/accordion/reference.md +0 -59
  446. package/docs/components/accordion/use-cases.md +0 -303
  447. package/docs/components/button/api-table.md +0 -84
  448. package/docs/components/button/assets/bootstrap-button.js +0 -245
  449. package/docs/components/button/examples.md +0 -42
  450. package/docs/components/button/extensions.md +0 -42
  451. package/docs/components/button/index.md +0 -13
  452. package/docs/components/button/overview.md +0 -42
  453. package/docs/components/button/src/icon.svg.js +0 -5
  454. package/docs/components/button/use-cases.md +0 -121
  455. package/docs/components/calendar/api-table.md +0 -56
  456. package/docs/components/calendar/index.md +0 -13
  457. package/docs/components/calendar/overview.md +0 -61
  458. package/docs/components/calendar/use-cases.md +0 -243
  459. package/docs/components/checkbox-group/api-table.md +0 -117
  460. package/docs/components/checkbox-group/index.md +0 -13
  461. package/docs/components/checkbox-group/overview.md +0 -51
  462. package/docs/components/checkbox-group/use-cases.md +0 -192
  463. package/docs/components/collapsible/api-table.md +0 -55
  464. package/docs/components/collapsible/assets/CustomCollapsible.js +0 -117
  465. package/docs/components/collapsible/assets/applyDemoCollapsibleStyles.js +0 -41
  466. package/docs/components/collapsible/examples.md +0 -95
  467. package/docs/components/collapsible/index.md +0 -13
  468. package/docs/components/collapsible/overview.md +0 -51
  469. package/docs/components/collapsible/use-cases.md +0 -91
  470. package/docs/components/combobox/api-table.md +0 -122
  471. package/docs/components/combobox/extensions.md +0 -174
  472. package/docs/components/combobox/index.md +0 -13
  473. package/docs/components/combobox/overview.md +0 -74
  474. package/docs/components/combobox/src/LinkMixin.js +0 -60
  475. package/docs/components/combobox/src/demo-selection-display.js +0 -164
  476. package/docs/components/combobox/src/gh-combobox/gh-button.js +0 -71
  477. package/docs/components/combobox/src/gh-combobox/gh-combobox.js +0 -515
  478. package/docs/components/combobox/src/google-combobox/assets/google-clear-icon.js +0 -9
  479. package/docs/components/combobox/src/google-combobox/assets/google-search-icon.js +0 -9
  480. package/docs/components/combobox/src/google-combobox/assets/google-voice-search-icon.js +0 -19
  481. package/docs/components/combobox/src/google-combobox/google-combobox.js +0 -461
  482. package/docs/components/combobox/src/lazyRender.js +0 -29
  483. package/docs/components/combobox/src/levenshtein.js +0 -95
  484. package/docs/components/combobox/src/md-combobox/MdFieldMixin.js +0 -345
  485. package/docs/components/combobox/src/md-combobox/md-combobox.js +0 -91
  486. package/docs/components/combobox/src/md-combobox/md-input.js +0 -5
  487. package/docs/components/combobox/src/md-combobox/style/load-roboto.js +0 -6
  488. package/docs/components/combobox/src/md-combobox/style/md-ripple.js +0 -82
  489. package/docs/components/combobox/src/wa-combobox/wa-combobox.js +0 -375
  490. package/docs/components/combobox/use-cases.md +0 -561
  491. package/docs/components/dialog/api-table.md +0 -61
  492. package/docs/components/dialog/index.md +0 -13
  493. package/docs/components/dialog/overview.md +0 -71
  494. package/docs/components/dialog/src/demoStyle.js +0 -57
  495. package/docs/components/dialog/src/external-dialog.js +0 -32
  496. package/docs/components/dialog/src/slots-dialog-content.js +0 -32
  497. package/docs/components/dialog/src/styled-dialog-content.js +0 -38
  498. package/docs/components/dialog/use-cases.md +0 -199
  499. package/docs/components/drawer/api-table.md +0 -64
  500. package/docs/components/drawer/index.md +0 -13
  501. package/docs/components/drawer/overview.md +0 -80
  502. package/docs/components/drawer/src/demoStyle.js +0 -71
  503. package/docs/components/drawer/use-cases.md +0 -206
  504. package/docs/components/fieldset/api-table.md +0 -24
  505. package/docs/components/fieldset/index.md +0 -13
  506. package/docs/components/fieldset/overview.md +0 -61
  507. package/docs/components/fieldset/use-cases.md +0 -203
  508. package/docs/components/form/api-table.md +0 -12
  509. package/docs/components/form/index.md +0 -13
  510. package/docs/components/form/overview.md +0 -66
  511. package/docs/components/form/use-cases.md +0 -230
  512. package/docs/components/icon/api-table.md +0 -41
  513. package/docs/components/icon/assets/bugs/bug01.svg.js +0 -2
  514. package/docs/components/icon/assets/bugs/bug02.svg.js +0 -2
  515. package/docs/components/icon/assets/bugs/bug05.svg.js +0 -2
  516. package/docs/components/icon/assets/bugs/bug06.svg.js +0 -2
  517. package/docs/components/icon/assets/bugs/bug08.svg.js +0 -2
  518. package/docs/components/icon/assets/bugs/bug12.svg.js +0 -2
  519. package/docs/components/icon/assets/bugs/bug19.svg.js +0 -2
  520. package/docs/components/icon/assets/bugs/bug23.svg.js +0 -2
  521. package/docs/components/icon/assets/bugs/bug24.svg.js +0 -2
  522. package/docs/components/icon/assets/iconset-bugs.js +0 -11
  523. package/docs/components/icon/assets/iconset-misc.js +0 -3
  524. package/docs/components/icon/assets/iconset-space.js +0 -31
  525. package/docs/components/icon/assets/misc/arrowLeft.svg.js +0 -6
  526. package/docs/components/icon/assets/space/aliens-spaceship.svg.js +0 -2
  527. package/docs/components/icon/assets/space/meteor.svg.js +0 -2
  528. package/docs/components/icon/assets/space/moon-flag.svg.js +0 -2
  529. package/docs/components/icon/assets/space/moon.svg.js +0 -2
  530. package/docs/components/icon/assets/space/night.svg.js +0 -2
  531. package/docs/components/icon/assets/space/orbit.svg.js +0 -2
  532. package/docs/components/icon/assets/space/planet.svg.js +0 -2
  533. package/docs/components/icon/assets/space/robot.svg.js +0 -2
  534. package/docs/components/icon/assets/space/rocket.svg.js +0 -2
  535. package/docs/components/icon/assets/space/satellite.svg.js +0 -2
  536. package/docs/components/icon/assets/space/signal.svg.js +0 -2
  537. package/docs/components/icon/assets/space/space-helmet.svg.js +0 -2
  538. package/docs/components/icon/assets/space/sun.svg.js +0 -2
  539. package/docs/components/icon/assets/space/telescope.svg.js +0 -2
  540. package/docs/components/icon/index.md +0 -13
  541. package/docs/components/icon/overview.md +0 -52
  542. package/docs/components/icon/use-cases.md +0 -92
  543. package/docs/components/index.md +0 -17
  544. package/docs/components/input/api-table.md +0 -52
  545. package/docs/components/input/index.md +0 -13
  546. package/docs/components/input/overview.md +0 -48
  547. package/docs/components/input/use-cases.md +0 -90
  548. package/docs/components/input-amount/api-table.md +0 -66
  549. package/docs/components/input-amount/index.md +0 -13
  550. package/docs/components/input-amount/overview.md +0 -61
  551. package/docs/components/input-amount/use-cases.md +0 -147
  552. package/docs/components/input-amount-dropdown/api-table.md +0 -87
  553. package/docs/components/input-amount-dropdown/index.md +0 -13
  554. package/docs/components/input-amount-dropdown/use-cases.md +0 -103
  555. package/docs/components/input-date/api-table.md +0 -58
  556. package/docs/components/input-date/index.md +0 -13
  557. package/docs/components/input-date/overview.md +0 -51
  558. package/docs/components/input-date/use-cases.md +0 -96
  559. package/docs/components/input-datepicker/api-table.md +0 -99
  560. package/docs/components/input-datepicker/index.md +0 -13
  561. package/docs/components/input-datepicker/overview.md +0 -54
  562. package/docs/components/input-datepicker/use-cases.md +0 -117
  563. package/docs/components/input-email/api-table.md +0 -53
  564. package/docs/components/input-email/index.md +0 -13
  565. package/docs/components/input-email/overview.md +0 -44
  566. package/docs/components/input-email/use-cases.md +0 -60
  567. package/docs/components/input-file/api-table.md +0 -157
  568. package/docs/components/input-file/index.md +0 -13
  569. package/docs/components/input-file/overview.md +0 -44
  570. package/docs/components/input-file/use-cases.md +0 -454
  571. package/docs/components/input-iban/api-table.md +0 -54
  572. package/docs/components/input-iban/index.md +0 -13
  573. package/docs/components/input-iban/overview.md +0 -49
  574. package/docs/components/input-iban/use-cases.md +0 -114
  575. package/docs/components/input-range/api-table.md +0 -71
  576. package/docs/components/input-range/index.md +0 -13
  577. package/docs/components/input-range/overview.md +0 -44
  578. package/docs/components/input-range/use-cases.md +0 -126
  579. package/docs/components/input-stepper/api-table.md +0 -74
  580. package/docs/components/input-stepper/index.md +0 -13
  581. package/docs/components/input-stepper/overview.md +0 -48
  582. package/docs/components/input-stepper/use-cases.md +0 -155
  583. package/docs/components/input-tel/api-table.md +0 -77
  584. package/docs/components/input-tel/index.md +0 -13
  585. package/docs/components/input-tel/overview.md +0 -74
  586. package/docs/components/input-tel/src/h-region-code-table.js +0 -109
  587. package/docs/components/input-tel/use-cases.md +0 -319
  588. package/docs/components/input-tel-dropdown/api-table.md +0 -85
  589. package/docs/components/input-tel-dropdown/extensions.md +0 -44
  590. package/docs/components/input-tel-dropdown/index.md +0 -13
  591. package/docs/components/input-tel-dropdown/overview.md +0 -50
  592. package/docs/components/input-tel-dropdown/src/intl-input-tel-dropdown.js +0 -138
  593. package/docs/components/input-tel-dropdown/use-cases.md +0 -171
  594. package/docs/components/listbox/api-table.md +0 -65
  595. package/docs/components/listbox/index.md +0 -13
  596. package/docs/components/listbox/overview.md +0 -54
  597. package/docs/components/listbox/src/listboxData.js +0 -131
  598. package/docs/components/listbox/use-cases.md +0 -154
  599. package/docs/components/menu/api-table.md +0 -44
  600. package/docs/components/menu/applyMenuDemoStyles.js +0 -173
  601. package/docs/components/menu/extensions.md +0 -262
  602. package/docs/components/menu/index.md +0 -13
  603. package/docs/components/menu/migration.md +0 -129
  604. package/docs/components/menu/overview.md +0 -448
  605. package/docs/components/menu/src/DemoNavigationBar.js +0 -441
  606. package/docs/components/menu/src/DemoSpinButton.js +0 -215
  607. package/docs/components/menu/src/DemoToolbar.js +0 -101
  608. package/docs/components/menu/src/DemoTree.js +0 -55
  609. package/docs/components/menu/src/navigation-bar-types.js +0 -41
  610. package/docs/components/menu/use-cases.md +0 -74
  611. package/docs/components/pagination/api-table.md +0 -45
  612. package/docs/components/pagination/index.md +0 -13
  613. package/docs/components/pagination/overview.md +0 -41
  614. package/docs/components/pagination/use-cases.md +0 -113
  615. package/docs/components/progress-indicator/api-table.md +0 -48
  616. package/docs/components/progress-indicator/assets/my-determinate-progress-bar.js +0 -30
  617. package/docs/components/progress-indicator/assets/my-indeterminate-progress-spinner.js +0 -55
  618. package/docs/components/progress-indicator/examples.md +0 -137
  619. package/docs/components/progress-indicator/index.md +0 -13
  620. package/docs/components/progress-indicator/overview.md +0 -48
  621. package/docs/components/radio-group/api-table.md +0 -63
  622. package/docs/components/radio-group/index.md +0 -13
  623. package/docs/components/radio-group/overview.md +0 -58
  624. package/docs/components/radio-group/use-cases.md +0 -146
  625. package/docs/components/select/api-table.md +0 -51
  626. package/docs/components/select/index.md +0 -13
  627. package/docs/components/select/overview.md +0 -56
  628. package/docs/components/select/use-cases.md +0 -128
  629. package/docs/components/select-rich/api-table.md +0 -123
  630. package/docs/components/select-rich/examples.md +0 -43
  631. package/docs/components/select-rich/index.md +0 -13
  632. package/docs/components/select-rich/overview.md +0 -55
  633. package/docs/components/select-rich/src/flagStyles.js +0 -1250
  634. package/docs/components/select-rich/src/intl-select-rich.js +0 -194
  635. package/docs/components/select-rich/src/regionMetaList.js +0 -1717
  636. package/docs/components/select-rich/use-cases.md +0 -387
  637. package/docs/components/steps/api-table.md +0 -92
  638. package/docs/components/steps/index.md +0 -13
  639. package/docs/components/steps/overview.md +0 -77
  640. package/docs/components/steps/use-cases.md +0 -181
  641. package/docs/components/switch/api-table.md +0 -82
  642. package/docs/components/switch/index.md +0 -13
  643. package/docs/components/switch/overview.md +0 -87
  644. package/docs/components/switch/reference.md +0 -51
  645. package/docs/components/switch/use-cases.md +0 -88
  646. package/docs/components/tabs/api-table.md +0 -51
  647. package/docs/components/tabs/examples.md +0 -35
  648. package/docs/components/tabs/index.md +0 -13
  649. package/docs/components/tabs/overview.md +0 -60
  650. package/docs/components/tabs/src/lea-tab-panel.js +0 -26
  651. package/docs/components/tabs/src/lea-tab.js +0 -50
  652. package/docs/components/tabs/src/lea-tabs.js +0 -28
  653. package/docs/components/tabs/use-cases.md +0 -231
  654. package/docs/components/textarea/api-table.md +0 -57
  655. package/docs/components/textarea/index.md +0 -13
  656. package/docs/components/textarea/overview.md +0 -47
  657. package/docs/components/textarea/use-cases.md +0 -107
  658. package/docs/components/tooltip/api-table.md +0 -62
  659. package/docs/components/tooltip/examples.md +0 -51
  660. package/docs/components/tooltip/index.md +0 -13
  661. package/docs/components/tooltip/overview.md +0 -56
  662. package/docs/components/tooltip/use-cases.md +0 -275
  663. package/docs/fundamentals/index.md +0 -19
  664. package/docs/fundamentals/node-tools/babel-plugin-extend-docs/index.md +0 -13
  665. package/docs/fundamentals/node-tools/babel-plugin-extend-docs/overview.md +0 -246
  666. package/docs/fundamentals/node-tools/index.md +0 -11
  667. package/docs/fundamentals/node-tools/providence-analytics/index.md +0 -13
  668. package/docs/fundamentals/node-tools/providence-analytics/overview.md +0 -108
  669. package/docs/fundamentals/node-tools/publish-docs/index.md +0 -13
  670. package/docs/fundamentals/node-tools/publish-docs/overview.md +0 -149
  671. package/docs/fundamentals/node-tools/remark-extend/index.md +0 -13
  672. package/docs/fundamentals/node-tools/remark-extend/overview.md +0 -287
  673. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/index.md +0 -13
  674. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +0 -298
  675. package/docs/fundamentals/rationales/TypeScript.md +0 -123
  676. package/docs/fundamentals/rationales/accessibility.md +0 -40
  677. package/docs/fundamentals/rationales/index.md +0 -11
  678. package/docs/fundamentals/rationales/side-effects.md +0 -215
  679. package/docs/fundamentals/rationales/versioning.md +0 -47
  680. package/docs/fundamentals/systems/core/SlotMixin.md +0 -168
  681. package/docs/fundamentals/systems/core/api-table.md +0 -43
  682. package/docs/fundamentals/systems/core/index.md +0 -15
  683. package/docs/fundamentals/systems/core/overview.md +0 -98
  684. package/docs/fundamentals/systems/form/api-table.md +0 -40
  685. package/docs/fundamentals/systems/form/assets/h-output.js +0 -156
  686. package/docs/fundamentals/systems/form/formatting-and-parsing.md +0 -258
  687. package/docs/fundamentals/systems/form/index.md +0 -15
  688. package/docs/fundamentals/systems/form/interaction-states.md +0 -195
  689. package/docs/fundamentals/systems/form/model-value.md +0 -57
  690. package/docs/fundamentals/systems/form/overview.md +0 -95
  691. package/docs/fundamentals/systems/form/styling.md +0 -92
  692. package/docs/fundamentals/systems/form/use-cases.md +0 -187
  693. package/docs/fundamentals/systems/form/validate.md +0 -985
  694. package/docs/fundamentals/systems/icon/index.md +0 -15
  695. package/docs/fundamentals/systems/icon/overview.md +0 -137
  696. package/docs/fundamentals/systems/index.md +0 -11
  697. package/docs/fundamentals/systems/localize/api-table.md +0 -72
  698. package/docs/fundamentals/systems/localize/assets/all-locales.js +0 -437
  699. package/docs/fundamentals/systems/localize/assets/en-AU.js +0 -6
  700. package/docs/fundamentals/systems/localize/assets/en-GB.js +0 -6
  701. package/docs/fundamentals/systems/localize/assets/en-US.js +0 -6
  702. package/docs/fundamentals/systems/localize/assets/en.js +0 -4
  703. package/docs/fundamentals/systems/localize/assets/nl-BE.js +0 -6
  704. package/docs/fundamentals/systems/localize/assets/nl-NL.js +0 -6
  705. package/docs/fundamentals/systems/localize/assets/nl.js +0 -4
  706. package/docs/fundamentals/systems/localize/dates.md +0 -208
  707. package/docs/fundamentals/systems/localize/index.md +0 -15
  708. package/docs/fundamentals/systems/localize/numbers.md +0 -230
  709. package/docs/fundamentals/systems/localize/overview.md +0 -61
  710. package/docs/fundamentals/systems/localize/rationale.md +0 -75
  711. package/docs/fundamentals/systems/localize/text.md +0 -139
  712. package/docs/fundamentals/systems/localize/use-cases.md +0 -334
  713. package/docs/fundamentals/systems/overlays/_configuration-positioning.md +0 -118
  714. package/docs/fundamentals/systems/overlays/_edge-cases.md +0 -149
  715. package/docs/fundamentals/systems/overlays/api-table.md +0 -174
  716. package/docs/fundamentals/systems/overlays/assets/applyDemoOverlayStyles.js +0 -23
  717. package/docs/fundamentals/systems/overlays/assets/demo-el-using-overlaymixin.js +0 -104
  718. package/docs/fundamentals/systems/overlays/assets/demo-overlay-backdrop.js +0 -47
  719. package/docs/fundamentals/systems/overlays/assets/demo-overlay-positioning.mjs +0 -537
  720. package/docs/fundamentals/systems/overlays/assets/umbrella-form.js +0 -140
  721. package/docs/fundamentals/systems/overlays/configuration.md +0 -516
  722. package/docs/fundamentals/systems/overlays/index.md +0 -15
  723. package/docs/fundamentals/systems/overlays/overview.md +0 -126
  724. package/docs/fundamentals/systems/overlays/rationale.md +0 -242
  725. package/docs/fundamentals/systems/overlays/use-cases.md +0 -717
  726. package/docs/fundamentals/tools/ajax/index.md +0 -15
  727. package/docs/fundamentals/tools/ajax/overview.md +0 -269
  728. package/docs/fundamentals/tools/ajax/use-cases.md +0 -469
  729. package/docs/fundamentals/tools/helpers/action-logger.md +0 -175
  730. package/docs/fundamentals/tools/helpers/index.md +0 -15
  731. package/docs/fundamentals/tools/helpers/overview.md +0 -43
  732. package/docs/fundamentals/tools/index.md +0 -11
  733. package/docs/fundamentals/tools/singleton-manager/example-complex/demo-app.js +0 -95
  734. package/docs/fundamentals/tools/singleton-manager/example-complex/index.md +0 -34
  735. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/overlays/index.js +0 -30
  736. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/overlays/instance.js +0 -4
  737. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-a/page-a.js +0 -35
  738. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-b/node_modules/overlays/index.js +0 -30
  739. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-b/node_modules/overlays/instance.js +0 -4
  740. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-b/page-b.js +0 -49
  741. package/docs/fundamentals/tools/singleton-manager/example-complex/overlayCompatibility.js +0 -69
  742. package/docs/fundamentals/tools/singleton-manager/example-fail/demo-app.js +0 -93
  743. package/docs/fundamentals/tools/singleton-manager/example-fail/index.md +0 -32
  744. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/overlays/index.js +0 -30
  745. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/overlays/instance.js +0 -3
  746. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-c/page-c.js +0 -34
  747. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-d/node_modules/overlays/index.js +0 -30
  748. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-d/node_modules/overlays/instance.js +0 -3
  749. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-d/page-d.js +0 -34
  750. package/docs/fundamentals/tools/singleton-manager/example-success/demo-app.js +0 -95
  751. package/docs/fundamentals/tools/singleton-manager/example-success/index.md +0 -31
  752. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/overlays/index.js +0 -30
  753. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/overlays/instance.js +0 -4
  754. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-e/page-e.js +0 -33
  755. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-f/node_modules/overlays/index.js +0 -30
  756. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-f/node_modules/overlays/instance.js +0 -4
  757. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-f/page-f.js +0 -34
  758. package/docs/fundamentals/tools/singleton-manager/example-success/overlayCompatibility.js +0 -23
  759. package/docs/fundamentals/tools/singleton-manager/index.md +0 -15
  760. package/docs/fundamentals/tools/singleton-manager/overview.md +0 -317
  761. package/docs/guides/how-to/create-a-custom-field.md +0 -212
  762. package/docs/guides/how-to/create-a-form.md +0 -101
  763. package/docs/guides/how-to/extend-a-native-input.md +0 -78
  764. package/docs/guides/how-to/get-started.md +0 -116
  765. package/docs/guides/how-to/index.md +0 -25
  766. package/docs/guides/index.md +0 -44
  767. package/docs/guides/principles/definitions-and-terms.md +0 -53
  768. package/docs/guides/principles/index.md +0 -36
  769. package/docs/guides/principles/scoped-elements.md +0 -119
  770. package/docs/guides/principles/styling.md +0 -43
  771. package/docs/guides/principles/subclasser-apis.md +0 -40
@@ -1,3622 +1,2 @@
1
- import {
2
- getComboboxMembers,
3
- getFilteredOptionValues,
4
- mimicKeyPress,
5
- mimicUserTyping,
6
- mimicUserTypingAdvanced,
7
- } from '@lion/ui/combobox-test-helpers.js';
8
- import { LionCombobox } from '@lion/ui/combobox.js';
9
- import { browserDetection, SlotMixin } from '@lion/ui/core.js';
10
- import '@lion/ui/define/lion-combobox.js';
11
- import '@lion/ui/define/lion-listbox.js';
12
- import '@lion/ui/define/lion-option.js';
13
- import { Required, Unparseable } from '@lion/ui/form-core.js';
14
- import { defineCE, expect, fixture, html, unsafeStatic, waitUntil } from '@open-wc/testing';
15
- import { sendKeys } from '@web/test-runner-commands';
16
- import { LitElement, nothing } from 'lit';
17
- import sinon from 'sinon';
18
- import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
19
-
20
- /**
21
- * @typedef {import('../types/SelectionDisplay.js').SelectionDisplay} SelectionDisplay
22
- * @typedef {import('@lion/ui/listbox.js').LionOption} LionOption
23
- */
24
-
25
- /**
26
- * @param {{ autocomplete?:'none'|'list'|'both', matchMode?:'begin'|'all', disabled?: boolean, readonly?: boolean }} config
27
- */
28
- async function fruitFixture({ autocomplete, matchMode, disabled, readonly } = {}) {
29
- const el =
30
- /** @type {LionCombobox} */
31
- await fixture(html`
32
- <lion-combobox label="Search" name="foo">
33
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
34
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
35
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
36
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
37
- </lion-combobox>
38
- `);
39
- if (autocomplete) {
40
- el.autocomplete = autocomplete;
41
- }
42
- if (matchMode) {
43
- el.matchMode = matchMode;
44
- }
45
- if (disabled) {
46
- el.disabled = disabled;
47
- }
48
- if (readonly) {
49
- el.readOnly = readonly;
50
- }
51
- await el.updateComplete;
52
- return [el, el.formElements];
53
- }
54
- describe('lion-combobox', () => {
55
- describe('Rendering on API change', () => {
56
- it('renders when options changed', async () => {
57
- const entries = [
58
- {
59
- id: '1',
60
- label: 'Apple',
61
- },
62
- {
63
- id: '2',
64
- label: 'Artichoke',
65
- },
66
- {
67
- id: '3',
68
- label: 'Asparagus',
69
- },
70
- {
71
- id: '4',
72
- label: 'Banana',
73
- },
74
- {
75
- id: '5',
76
- label: 'Pineapple',
77
- },
78
- ];
79
- class Wrapper extends LitElement {
80
- static properties = {
81
- ...super.properties,
82
- entries: {
83
- type: Array,
84
- },
85
- };
86
- constructor() {
87
- super();
88
- this.entries = entries;
89
- }
90
- render() {
91
- return html`<lion-combobox name="foo">
92
- ${this.entries.map(
93
- (entry) => html`<lion-option .choiceValue="${entry.id}">${entry.label}</lion-option>`,
94
- )}
95
- </lion-combobox>`;
96
- }
97
- }
98
- const tagString = defineCE(Wrapper);
99
- const tag = unsafeStatic(tagString);
100
- const wrapperElement = /** @type {Wrapper} */ await fixture(html`<${tag}></${tag}>`);
101
- await waitUntil(
102
- () =>
103
- wrapperElement?.shadowRoot?.querySelector('lion-options')?.querySelectorAll('lion-option')
104
- .length === entries.length,
105
- );
106
- wrapperElement.entries = [
107
- {
108
- id: '4',
109
- label: 'Banana',
110
- },
111
- ];
112
- await waitUntil(
113
- () =>
114
- wrapperElement?.shadowRoot?.querySelector('lion-options')?.querySelectorAll('lion-option')
115
- .length === wrapperElement.entries.length,
116
- );
117
- const optionElements = wrapperElement.shadowRoot?.querySelectorAll('lion-option');
118
- expect(optionElements?.length).to.equal(wrapperElement.entries.length);
119
- const optionElement = optionElements?.item(0);
120
- expect(optionElement?.textContent).to.equal('Banana');
121
- });
122
- });
123
- describe('Options visibility', () => {
124
- it('hides options when text in input node is cleared after typing something by default', async () => {
125
- const el =
126
- /** @type {LionCombobox} */
127
- await fixture(html`
128
- <lion-combobox name="foo">
129
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
130
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
131
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
132
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
133
- </lion-combobox>
134
- `);
135
- const options = el.formElements;
136
- const visibleOptions = () => options.filter((o) => o.getAttribute('aria-hidden') !== 'true');
137
- async function performChecks() {
138
- await mimicUserTyping(el, 'c');
139
- await el.updateComplete;
140
- expect(visibleOptions().length).to.equal(4, `autocompleteMode is ${el.autocomplete}`);
141
- // Remove [hard], [c]
142
- await mimicUserTypingAdvanced(el, ['Backspace', 'Backspace']);
143
- await el.updateComplete;
144
- expect(visibleOptions().length).to.equal(0, `autocompleteMode is ${el.autocomplete}`);
145
- }
146
- el.autocomplete = 'none';
147
- await performChecks();
148
- el.autocomplete = 'list';
149
- await performChecks();
150
- el.autocomplete = 'inline';
151
- await performChecks();
152
- el.autocomplete = 'both';
153
- await performChecks();
154
- });
155
- it('hides all options on reset()', async () => {
156
- const el =
157
- /** @type {LionCombobox} */
158
- await fixture(html`
159
- <lion-combobox name="foo">
160
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
161
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
162
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
163
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
164
- </lion-combobox>
165
- `);
166
- const options = el.formElements;
167
- const visibleOptions = () => options.filter((o) => o.style.display !== 'none');
168
- await mimicUserTyping(el, 'cha');
169
- await el.updateComplete;
170
- expect(visibleOptions().length).to.equal(1);
171
- el.reset();
172
- await el.updateComplete;
173
- expect(visibleOptions().length).to.equal(0);
174
- });
175
- it('shows all options on reset() when showAllOnEmpty is set to true and overlay was open', async () => {
176
- const el =
177
- /** @type {LionCombobox} */
178
- await fixture(html`
179
- <lion-combobox name="foo" show-all-on-empty>
180
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
181
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
182
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
183
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
184
- </lion-combobox>
185
- `);
186
- const options = el.formElements;
187
- const visibleOptions = () => options.filter((o) => o.getAttribute('aria-hidden') !== 'true');
188
- await mimicUserTyping(el, 'cha');
189
- await el.updateComplete;
190
- expect(visibleOptions().length).to.equal(1);
191
- expect(el.opened).to.be.true;
192
- el.reset();
193
- await el.updateComplete;
194
- expect(visibleOptions().length).to.equal(4);
195
- });
196
- it('hides listbox on click/enter (when multiple-choice is false)', async () => {
197
- const el =
198
- /** @type {LionCombobox} */
199
- await fixture(html`
200
- <lion-combobox name="foo">
201
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
202
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
203
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
204
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
205
- </lion-combobox>
206
- `);
207
- const { _listboxNode } = getComboboxMembers(el);
208
- async function open() {
209
- await mimicUserTyping(el, 'ch');
210
- return el.updateComplete;
211
- }
212
- await open();
213
- expect(el.opened).to.be.true;
214
- const visibleOptions = el.formElements.filter((o) => o.style.display !== 'none');
215
- visibleOptions[0].click();
216
- expect(el.opened).to.be.false;
217
- await open();
218
- expect(el.opened).to.be.true;
219
- el.activeIndex = el.formElements.indexOf(visibleOptions[0]);
220
- mimicKeyPress(_listboxNode, 'Enter');
221
- await el.updateComplete;
222
- expect(el.opened).to.be.false;
223
- });
224
- describe('With ".showAllOnEmpty"', () => {
225
- it('keeps showing options when text in input node is cleared after typing something', async () => {
226
- const el =
227
- /** @type {LionCombobox} */
228
- await fixture(html`
229
- <lion-combobox name="foo" autocomplete="list" show-all-on-empty>
230
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
231
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
232
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
233
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
234
- </lion-combobox>
235
- `);
236
- const options = el.formElements;
237
- const visibleOptions = () =>
238
- options.filter((o) => o.getAttribute('aria-hidden') !== 'true');
239
- async function performChecks() {
240
- await mimicUserTyping(el, 'c');
241
- await el.updateComplete;
242
- expect(visibleOptions().length).to.equal(4);
243
- await mimicUserTyping(el, '');
244
- await el.updateComplete;
245
- expect(visibleOptions().length).to.equal(options.length);
246
- }
247
- el.autocomplete = 'none';
248
- await performChecks();
249
- el.autocomplete = 'list';
250
- await performChecks();
251
- el.autocomplete = 'inline';
252
- await performChecks();
253
- el.autocomplete = 'both';
254
- await performChecks();
255
- });
256
- it('shows overlay on click', async () => {
257
- const el =
258
- /** @type {LionCombobox} */
259
- await fixture(html`
260
- <lion-combobox name="foo" .showAllOnEmpty="${true}">
261
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
262
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
263
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
264
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
265
- </lion-combobox>
266
- `);
267
- const { _inputNode } = getComboboxMembers(el);
268
- expect(el.opened).to.be.false;
269
- _inputNode.dispatchEvent(
270
- new Event('click', {
271
- bubbles: true,
272
- composed: true,
273
- }),
274
- );
275
- await el.updateComplete;
276
- expect(el.opened).to.be.true;
277
- });
278
- it('hides overlay on [Escape] after being opened', async () => {
279
- const el =
280
- /** @type {LionCombobox} */
281
- await fixture(html`
282
- <lion-combobox name="foo" .showAllOnEmpty="${true}" opened>
283
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
284
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
285
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
286
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
287
- </lion-combobox>
288
- `);
289
- await el.updateComplete;
290
- const { _inputNode } = getComboboxMembers(el);
291
- _inputNode.dispatchEvent(
292
- new KeyboardEvent('keyup', {
293
- key: 'Escape',
294
- }),
295
- );
296
- await el.updateComplete;
297
- expect(el.opened).to.be.false;
298
- });
299
- it('hides listbox on click/enter (when multiple-choice is false)', async () => {
300
- const el =
301
- /** @type {LionCombobox} */
302
- await fixture(html`
303
- <lion-combobox name="foo" .showAllOnEmpty="${true}">
304
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
305
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
306
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
307
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
308
- </lion-combobox>
309
- `);
310
- const { _listboxNode, _inputNode } = getComboboxMembers(el);
311
- async function open() {
312
- await mimicUserTyping(el, 'ch');
313
- return el.updateComplete;
314
- }
315
-
316
- // FIXME: temp disable for Safari. Works locally, not in build
317
- const isSafari = (() => {
318
- const ua = navigator.userAgent.toLowerCase();
319
- return ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1;
320
- })();
321
- if (isSafari) {
322
- return;
323
- }
324
- await open();
325
- expect(el.opened).to.be.true;
326
- const visibleOptions = el.formElements.filter((o) => o.style.display !== 'none');
327
- visibleOptions[0].click();
328
- await el.updateComplete;
329
- expect(el.opened).to.be.false;
330
- _inputNode.value = '';
331
- _inputNode.blur();
332
- await open();
333
- await el.updateComplete;
334
- el.activeIndex = el.formElements.indexOf(visibleOptions[0]);
335
- mimicKeyPress(_listboxNode, 'Enter');
336
- expect(el.opened).to.be.false;
337
- });
338
- });
339
- });
340
- describe('Structure', () => {
341
- it('has a listbox node', async () => {
342
- const el =
343
- /** @type {LionCombobox} */
344
- await fixture(html`
345
- <lion-combobox name="foo">
346
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
347
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
348
- </lion-combobox>
349
- `);
350
- const { _listboxNode } = getComboboxMembers(el);
351
- expect(_listboxNode).to.exist;
352
- // TODO: worked before, find out what has changed in ScopedElements
353
- // expect(_listboxNode instanceof LionOptions).to.be.true;
354
- expect(el.querySelector('[role=listbox]')).to.equal(_listboxNode);
355
- });
356
- it('has a textbox element', async () => {
357
- const el =
358
- /** @type {LionCombobox} */
359
- await fixture(html`
360
- <lion-combobox name="foo">
361
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
362
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
363
- </lion-combobox>
364
- `);
365
- const { _comboboxNode } = getComboboxMembers(el);
366
- expect(_comboboxNode).to.exist;
367
- expect(el.querySelector('[role=combobox]')).to.equal(_comboboxNode);
368
- });
369
- it('has validator "MatchesOption" applied by default', async () => {
370
- const el =
371
- /** @type {LionCombobox} */
372
- await fixture(html`
373
- <lion-combobox name="foo">
374
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
375
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
376
- </lion-combobox>
377
- `);
378
- await mimicUserTyping(el, '30');
379
- await el.updateComplete;
380
- expect(el.hasFeedbackFor).to.include('error');
381
- expect(el.validationStates).to.have.property('error');
382
- expect(el.validationStates.error).to.have.property('MatchesOption');
383
- });
384
- it('keeps slottable provided in `slots` getter as direct host child', async () => {
385
- class MyEl extends SlotMixin(LionCombobox) {
386
- // @ts-ignore
387
- get slots() {
388
- return {
389
- ...super.slots,
390
- _lazyRenderedSlot: () => ({
391
- template: this.renderSlot
392
- ? html`<span id="lazyRenderedSlotId">(Optional)</span>`
393
- : html`${nothing}`,
394
- renderAsDirectHostChild: true,
395
- }),
396
- };
397
- }
398
- _labelTemplate() {
399
- return html`
400
- <div class="form-field__label">
401
- <slot name="label"></slot>
402
- <slot name="_lazyRenderedSlot"></slot>
403
- <slot></slot>
404
- </div>
405
- `;
406
- }
407
- constructor() {
408
- super();
409
- this.renderSlot = false;
410
- }
411
- }
412
- const tagName = defineCE(MyEl);
413
- const wrappingTag = unsafeStatic(tagName);
414
- const el =
415
- /** @type {MyEl} */
416
- await fixture(html`
417
- <${wrappingTag} label="my label">
418
- <lion-option .choiceValue="${'1'}">${'one'}</lion-option>
419
- </${wrappingTag}>
420
- `);
421
- await el.registrationComplete;
422
- el.renderSlot = true;
423
- await el.updateComplete;
424
- const lazyRenderedSlot = el.querySelector('#lazyRenderedSlotId');
425
- expect(lazyRenderedSlot?.parentElement === el).to.be.true;
426
- });
427
- });
428
- describe('Values', () => {
429
- it('syncs modelValue with textbox', async () => {
430
- const el =
431
- /** @type {LionCombobox} */
432
- await fixture(html`
433
- <lion-combobox name="foo" .modelValue="${'10'}">
434
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
435
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
436
- </lion-combobox>
437
- `);
438
- const { _inputNode } = getComboboxMembers(el);
439
- expect(_inputNode.value).to.equal('10');
440
- el.modelValue = '20';
441
- await el.updateComplete;
442
- expect(_inputNode.value).to.equal('20');
443
- });
444
- it('sets modelValue to Unparseable if no option is selected', async () => {
445
- const el =
446
- /** @type {LionCombobox} */
447
- await fixture(html`
448
- <lion-combobox name="foo" .modelValue="${'Artichoke'}">
449
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
450
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
451
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
452
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
453
- </lion-combobox>
454
- `);
455
- const { _inputNode } = getComboboxMembers(el);
456
- expect(el.modelValue).to.equal('Artichoke');
457
- expect(el.formElements[0].checked).to.be.true;
458
- el.setCheckedIndex(-1);
459
- el.__shouldAutocompleteNextUpdate = true;
460
- await el.updateComplete;
461
- expect(el.modelValue instanceof Unparseable).to.be.true;
462
- expect(el.modelValue.viewValue).to.equal('Artichoke');
463
- expect(el.formElements[0].checked).to.be.false;
464
- el.setCheckedIndex(-1);
465
- _inputNode.value = 'Foo';
466
- el.__shouldAutocompleteNextUpdate = true;
467
- await el.updateComplete;
468
- expect(el.modelValue instanceof Unparseable).to.be.true;
469
- expect(el.modelValue.viewValue).to.equal('Foo');
470
- expect(el.formElements[0].checked).to.be.false;
471
- el.setCheckedIndex(0);
472
- el.__shouldAutocompleteNextUpdate = true;
473
- await el.updateComplete;
474
- expect(el.modelValue instanceof Unparseable).to.be.false;
475
- expect(el.modelValue).to.equal('Artichoke');
476
- expect(el.formElements[0].checked).to.be.true;
477
- });
478
- it('sets modelValue to _inputNode.value if no option is selected when requireOptionMatch is false', async () => {
479
- const el =
480
- /** @type {LionCombobox} */
481
- await fixture(html`
482
- <lion-combobox name="foo" .modelValue="${'Artichoke'}" .requireOptionMatch="${false}">
483
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
484
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
485
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
486
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
487
- </lion-combobox>
488
- `);
489
- const { _inputNode } = getComboboxMembers(el);
490
- expect(el.modelValue).to.equal('Artichoke');
491
- expect(el.formElements[0].checked).to.be.true;
492
- el.setCheckedIndex(-1);
493
- el.__shouldAutocompleteNextUpdate = true;
494
- await el.updateComplete;
495
- expect(el.modelValue).to.equal('Artichoke');
496
- expect(el.formElements[0].checked).to.be.true;
497
- el.setCheckedIndex(-1);
498
- _inputNode.value = 'Foo';
499
- el.__shouldAutocompleteNextUpdate = true;
500
- await el.updateComplete;
501
- expect(el.modelValue).to.equal('Foo');
502
- expect(el.formElements[0].checked).to.be.false;
503
- });
504
- it('sets modelValue to empty array if no option is selected for multiple choice', async () => {
505
- const el =
506
- /** @type {LionCombobox} */
507
- await fixture(html`
508
- <lion-combobox name="foo" multiple-choice .modelValue="${['Artichoke']}">
509
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
510
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
511
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
512
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
513
- </lion-combobox>
514
- `);
515
- expect(el.modelValue).to.deep.equal(['Artichoke']);
516
- expect(el.formElements[0].checked).to.be.true;
517
- el.setCheckedIndex([]);
518
- await el.updateComplete;
519
- expect(el.modelValue).to.deep.equal([]);
520
- expect(el.formElements[0].checked).to.be.false;
521
- });
522
- it('clears modelValue and textbox value on clear()', async () => {
523
- const el =
524
- /** @type {LionCombobox} */
525
- await fixture(html`
526
- <lion-combobox name="foo" .modelValue="${'Artichoke'}">
527
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
528
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
529
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
530
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
531
- </lion-combobox>
532
- `);
533
- const { _inputNode } = getComboboxMembers(el);
534
- el.clear();
535
- expect(el.modelValue).to.equal('');
536
- expect(_inputNode.value).to.equal('');
537
- const el2 =
538
- /** @type {LionCombobox} */
539
- await fixture(html`
540
- <lion-combobox name="foo" multiple-choice .modelValue="${['Artichoke']}">
541
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
542
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
543
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
544
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
545
- </lion-combobox>
546
- `);
547
- el2.clear();
548
- expect(el2.modelValue).to.eql([]);
549
- // @ts-ignore [allow-protected] in test
550
- expect(el2._inputNode.value).to.equal('');
551
- });
552
- it('correctly emits event with an empty value when clear() is called', async () => {
553
- const el =
554
- /** @type {LionCombobox} */
555
- await fixture(
556
- html`<lion-combobox>
557
- <lion-option .choiceValue=${'red'}>Red</lion-option>
558
- <lion-option .choiceValue=${'green'}>Green</lion-option>
559
- <lion-option .choiceValue=${'blue'}>Blue</lion-option>
560
- </lion-combobox>`,
561
- );
562
- el.modelValue = 'red';
563
- await el.updateComplete;
564
- el.addEventListener('model-value-changed', ({ target }) => {
565
- expect(target).to.not.be.null;
566
- const { modelValue, value } = /** @type {LionCombobox} */ target;
567
- expect(value).to.equal('');
568
- expect(modelValue).to.equal('');
569
- });
570
- el.clear();
571
- await el.updateComplete;
572
- });
573
- it('updates option list after clear()', async () => {
574
- const el =
575
- /** @type {LionCombobox} */
576
- await fixture(html`
577
- <lion-combobox name="foo" multiple-choice>
578
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
579
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
580
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
581
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
582
- </lion-combobox>
583
- `);
584
- const options = el.formElements;
585
- await mimicUserTyping(el, 'a');
586
- await el.updateComplete;
587
- const visibleOptions = () => options.filter((o) => o.style.display !== 'none');
588
- expect(visibleOptions().length).to.equal(3, 'after input');
589
- el.clear();
590
- await el.updateComplete;
591
- expect(visibleOptions().length).to.equal(0, 'after clear');
592
- });
593
- it('keeps interaction states on clear()', async () => {
594
- const el =
595
- /** @type {LionCombobox} */
596
- await fixture(html`
597
- <lion-combobox name="foo">
598
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
599
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
600
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
601
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
602
- </lion-combobox>
603
- `);
604
- await mimicUserTyping(el, 'a');
605
- await el.updateComplete;
606
- expect(el.dirty).to.equal(true);
607
- el.clear();
608
- await el.updateComplete;
609
- expect(el.dirty).to.equal(true);
610
- });
611
- it('resets modelValue and textbox value on reset()', async () => {
612
- const el =
613
- /** @type {LionCombobox} */
614
- await fixture(html`
615
- <lion-combobox name="foo" .modelValue="${'Artichoke'}">
616
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
617
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
618
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
619
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
620
- </lion-combobox>
621
- `);
622
- const { _inputNode } = getComboboxMembers(el);
623
- el.modelValue = 'Chard';
624
- el.reset();
625
- expect(el.modelValue).to.equal('Artichoke');
626
- expect(_inputNode.value).to.equal('Artichoke');
627
- const el2 =
628
- /** @type {LionCombobox} */
629
- await fixture(html`
630
- <lion-combobox name="foo" multiple-choice .modelValue="${['Artichoke']}">
631
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
632
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
633
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
634
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
635
- </lion-combobox>
636
- `);
637
- el2.modelValue = ['Artichoke', 'Chard'];
638
- el2.reset();
639
- expect(el2.modelValue).to.deep.equal(['Artichoke']);
640
- // @ts-ignore [allow-protected] in test
641
- expect(el2._inputNode.value).to.equal('');
642
- });
643
- it('syncs textbox to modelValue', async () => {
644
- for (const autocompleteMode of ['none', 'list', 'inline', 'both']) {
645
- const el =
646
- /** @type {LionCombobox} */
647
- await fixture(html`
648
- <lion-combobox name="foo" show-all-on-empty .autocomplete="${autocompleteMode}">
649
- <lion-option .choiceValue="${'Aa'}" checked>Aa</lion-option>
650
- <lion-option .choiceValue="${'Bb'}">Bb</lion-option>
651
- </lion-combobox>
652
- `);
653
- const { _inputNode } = getComboboxMembers(el);
654
- expect(_inputNode.value).to.equal('Aa', `autocompleteMode is ${autocompleteMode}`);
655
- el.formElements[0].click();
656
- await el.updateComplete;
657
- expect(_inputNode.value).to.equal('Aa', `autocompleteMode is ${autocompleteMode}`);
658
- expect(el.checkedIndex).to.equal(0, `autocompleteMode is ${autocompleteMode}`);
659
- await mimicUserTyping(el, 'B');
660
- await el.updateComplete;
661
- if (autocompleteMode === 'none' || autocompleteMode === 'list') {
662
- expect(el.modelValue).to.deep.equal(
663
- {
664
- type: 'unparseable',
665
- viewValue: 'B',
666
- },
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
- }
675
- }
676
- });
677
- it('works with Required validator', async () => {
678
- const el =
679
- /** @type {LionCombobox} */
680
- await fixture(html`
681
- <lion-combobox name="foo" .validators=${[new Required()]}>
682
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
683
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
684
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
685
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
686
- </lion-combobox>
687
- `);
688
- el.submitted = true;
689
- await el.feedbackComplete;
690
- expect(el.hasFeedbackFor).to.include('error', 'hasFeedbackFor');
691
- await el.feedbackComplete;
692
- expect(el.showsFeedbackFor).to.include('error', 'showsFeedbackFor');
693
- });
694
- it('ignores empty string modelValue inputs', async () => {
695
- const el =
696
- /** @type {LionCombobox} */
697
- await fixture(html`
698
- <lion-combobox name="foo" multiple-choice autocomplete="none">
699
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
700
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
701
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
702
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
703
- </lion-combobox>
704
- `);
705
- el.requireOptionMatch = false;
706
- await el.updateComplete;
707
- const { _inputNode } = getComboboxMembers(el);
708
- mimicKeyPress(_inputNode, 'Enter');
709
- await el.updateComplete;
710
- expect(el.modelValue).to.eql([]);
711
- await mimicUserTyping(el, ' ');
712
- await el.updateComplete;
713
- mimicKeyPress(_inputNode, 'Enter');
714
- await el.updateComplete;
715
- expect(el.modelValue).to.eql([]);
716
- });
717
- it('allows a value outside of the option list when requireOptionMatch is false', async () => {
718
- const el =
719
- /** @type {LionCombobox} */
720
- await fixture(html`
721
- <lion-combobox name="foo" .validators=${[new Required()]}>
722
- <lion-option checked .choiceValue="${'Artichoke'}">Artichoke</lion-option>
723
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
724
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
725
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
726
- </lion-combobox>
727
- `);
728
- el.requireOptionMatch = false;
729
- const { _inputNode } = getComboboxMembers(el);
730
- expect(el.checkedIndex).to.equal(0);
731
- await mimicUserTyping(el, 'Foo');
732
- _inputNode.dispatchEvent(new Event('input'));
733
- await el.updateComplete;
734
- expect(el.checkedIndex).to.equal(-1);
735
- expect(el.modelValue).to.equal('Foo');
736
- expect(_inputNode.value).to.equal('Foo');
737
- });
738
- it('fires "model-value-changed" on every textbox change when requireOptionMatch is false and autocomplete is both', async () => {
739
- const el =
740
- /** @type {LionCombobox} */
741
- await fixture(html`
742
- <lion-combobox name="foo" autocomplete="both" .requireOptionMatch="${false}">
743
- <lion-option checked .choiceValue="${'Artichoke'}">Artichoke</lion-option>
744
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
745
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
746
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
747
- </lion-combobox>
748
- `);
749
- const fake = sinon.fake((ev) => ev.target.modelValue);
750
- el.addEventListener('model-value-changed', fake);
751
- await mimicUserTyping(el, 'Foo');
752
- await el.updateComplete;
753
- expect(fake).to.have.been.calledThrice;
754
- expect(fake.firstCall.returnValue).to.equal('F');
755
- expect(fake.secondCall.returnValue).to.equal('Fo');
756
- expect(fake.thirdCall.returnValue).to.equal('Foo');
757
- expect(el.modelValue).to.equal('Foo');
758
- });
759
- it('fires "model-value-changed" on every textbox change when requireOptionMatch is false and autocomplete is none', async () => {
760
- const el =
761
- /** @type {LionCombobox} */
762
- await fixture(html`
763
- <lion-combobox name="foo" autocomplete="none" .requireOptionMatch="${false}">
764
- <lion-option checked .choiceValue="${'Artichoke'}">Artichoke</lion-option>
765
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
766
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
767
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
768
- </lion-combobox>
769
- `);
770
- const fake = sinon.fake((ev) => ev.target.modelValue);
771
- el.addEventListener('model-value-changed', fake);
772
- await mimicUserTyping(el, 'Foo');
773
- await el.updateComplete;
774
- expect(fake.callCount).to.equal(4);
775
- expect(fake.firstCall.returnValue).to.equal('');
776
- expect(fake.secondCall.returnValue).to.equal('F');
777
- expect(fake.thirdCall.returnValue).to.equal('Fo');
778
- expect(fake.getCall(3).returnValue).to.equal('Foo');
779
- expect(el.modelValue).to.equal('Foo');
780
- });
781
- it('does not fire "model-value-changed" when requireOptionMatch is false but the textbox value did not change', async () => {
782
- const el =
783
- /** @type {LionCombobox} */
784
- await fixture(html`
785
- <lion-combobox name="foo" autocomplete="none" .requireOptionMatch="${false}">
786
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
787
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
788
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
789
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
790
- </lion-combobox>
791
- `);
792
- await mimicUserTyping(el, 'Foo');
793
- await el.updateComplete;
794
- const spy = sinon.spy();
795
- el.addEventListener('model-value-changed', spy);
796
-
797
- // Re-triggering autocompletion with the exact same textbox value should not dispatch again
798
- el.__shouldAutocompleteNextUpdate = true;
799
- await el.updateComplete;
800
- expect(spy).to.not.have.been.called;
801
- });
802
- it('fires "model-value-changed" via checked option repropagation (not the manual dispatch) when requireOptionMatch is true', async () => {
803
- const el =
804
- /** @type {LionCombobox} */
805
- await fixture(html`
806
- <lion-combobox name="foo" autocomplete="both">
807
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
808
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
809
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
810
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
811
- </lion-combobox>
812
- `);
813
- const spy = sinon.spy();
814
- el.addEventListener('model-value-changed', spy);
815
- await mimicUserTyping(el, 'cha');
816
- await el.updateComplete;
817
- expect(el.modelValue).to.equal('Chard');
818
- expect(spy).to.have.been.calledOnce;
819
- spy.resetHistory();
820
- // No option matches "xyz", so checkedIndex stays -1 and requireOptionMatch is true:
821
- // no event should be manually dispatched (normal Unparseable flow, no double event)
822
- await mimicUserTyping(el, 'xyz');
823
- await el.updateComplete;
824
- expect(el.modelValue instanceof Unparseable).to.be.true;
825
- expect(spy).to.have.been.calledOnce;
826
- });
827
- it('fires "model-value-changed" via checked option repropagation (not the manual dispatch) when requireOptionMatch is false and no option matches the input', async () => {
828
- const el =
829
- /** @type {LionCombobox} */
830
- await fixture(html`
831
- <lion-combobox name="foo" autocomplete="both">
832
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
833
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
834
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
835
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
836
- </lion-combobox>
837
- `);
838
- el.requireOptionMatch = false;
839
- await el.updateComplete;
840
- const spy = sinon.spy();
841
- el.addEventListener('model-value-changed', spy);
842
-
843
- // No option matches "Chards", so checkedIndex becomes -1 and requireOptionMatch is false:
844
- // no event should be manually dispatched (normal Unparseable flow, no double event)
845
- await mimicUserTyping(el, 'Chicorys');
846
- await el.updateComplete;
847
- expect(el.checkedIndex).to.equal(-1);
848
- expect(spy.args.length).to.equal(3);
849
- expect(spy.args[2][0].target.modelValue).to.equal('Chicorys');
850
- });
851
- it("doesn't select any similar options after using delete when requireOptionMatch is false", async () => {
852
- const el =
853
- /** @type {LionCombobox} */
854
- await fixture(html`
855
- <lion-combobox name="foo" .validators=${[new Required()]}>
856
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
857
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
858
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
859
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
860
- </lion-combobox>
861
- `);
862
- el.requireOptionMatch = false;
863
- const { _inputNode } = getComboboxMembers(el);
864
- await mimicUserTyping(el, 'Art');
865
- await el.updateComplete;
866
- await mimicUserTypingAdvanced(el, ['Delete']);
867
- await el.updateComplete;
868
- await el.updateComplete;
869
- mimicKeyPress(_inputNode, 'Enter');
870
- await el.updateComplete;
871
- expect(el.checkedIndex).to.equal(-1);
872
- expect(el.modelValue).to.equal('Art');
873
- expect(_inputNode.value).to.equal('Art');
874
- });
875
- it("when removing a letter it won't select the option", async () => {
876
- // We don't autocomplete when characters are removed
877
- const el =
878
- /** @type {LionCombobox} */
879
- await fixture(html`
880
- <lion-combobox name="foo">
881
- <lion-option checked .choiceValue="${'Artichoke'}">Artichoke</lion-option>
882
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
883
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
884
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
885
- </lion-combobox>
886
- `);
887
- const { _inputNode } = getComboboxMembers(el);
888
- expect(el.checkedIndex).to.equal(0);
889
-
890
- // Simulate backspace deleting the char at the end of the string
891
- mimicKeyPress(_inputNode, 'Backspace');
892
- _inputNode.dispatchEvent(new Event('input'));
893
- const arr = _inputNode.value.split('');
894
- arr.splice(_inputNode.value.length - 1, 1);
895
- _inputNode.value = arr.join('');
896
- await el.updateComplete;
897
- el.dispatchEvent(new Event('blur'));
898
- expect(el.checkedIndex).to.equal(-1);
899
- expect(el.modelValue instanceof Unparseable).to.be.true;
900
- expect(el.modelValue.viewValue).to.equal('Artichok');
901
- });
902
- it('allows the user to override the parser', async () => {
903
- const el =
904
- /** @type {LionCombobox} */
905
- await fixture(html`
906
- <lion-combobox
907
- name="foo"
908
- .parser="${/** @param {string} value */ (value) => value.replace(/[0-9]/g, '')}"
909
- >
910
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
911
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
912
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
913
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
914
- </lion-combobox>
915
- `);
916
- const { _inputNode } = getComboboxMembers(el);
917
- el.setCheckedIndex(-1);
918
- _inputNode.value = 'Foo123';
919
- el.__shouldAutocompleteNextUpdate = true;
920
- await el.updateComplete;
921
- expect(el.modelValue).to.equal('Foo');
922
- expect(el.formElements[0].checked).to.be.false;
923
- });
924
- it('allows custom selections when multi-choice when requireOptionMatch is false', async () => {
925
- const el =
926
- /** @type {LionCombobox} */
927
- await fixture(html`
928
- <lion-combobox
929
- name="foo"
930
- multiple-choice
931
- .validators=${[new Required()]}
932
- autocomplete="none"
933
- >
934
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
935
- <lion-option checked .choiceValue="${'Chard'}">Chard</lion-option>
936
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
937
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
938
- </lion-combobox>
939
- `);
940
- el.requireOptionMatch = false;
941
- await el.updateComplete;
942
- const { _inputNode } = getComboboxMembers(el);
943
- expect(el.modelValue).to.eql(['Chard']);
944
- expect(el.checkedIndex).to.eql([1]);
945
- await mimicUserTyping(el, 'Foo');
946
- await el.updateComplete;
947
- mimicKeyPress(_inputNode, 'Enter');
948
- await el.updateComplete;
949
- expect(el.modelValue).to.eql(['Chard', 'Foo']);
950
- expect(el.checkedIndex).to.eql([1]);
951
- await mimicUserTyping(el, 'Bar');
952
- await el.updateComplete;
953
- mimicKeyPress(_inputNode, 'Enter');
954
- await el.updateComplete;
955
- });
956
- it('allows many custom selections when multi-choice when requireOptionMatch is false', async () => {
957
- const el =
958
- /** @type {LionCombobox} */
959
- await fixture(html`
960
- <lion-combobox
961
- name="foo"
962
- multiple-choice
963
- .validators=${[new Required()]}
964
- autocomplete="none"
965
- >
966
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
967
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
968
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
969
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
970
- </lion-combobox>
971
- `);
972
- el.requireOptionMatch = false;
973
- await el.updateComplete;
974
- const { _inputNode } = getComboboxMembers(el);
975
- await mimicUserTyping(el, 'Foo');
976
- await el.updateComplete;
977
- mimicKeyPress(_inputNode, 'Enter');
978
- await el.updateComplete;
979
- expect(el.modelValue).to.eql(['Foo']);
980
- expect(el.checkedIndex).to.eql([]);
981
- await mimicUserTyping(el, 'Bar');
982
- await el.updateComplete;
983
- mimicKeyPress(_inputNode, 'Enter');
984
- await el.updateComplete;
985
- expect(el.modelValue).to.eql(['Foo', 'Bar']);
986
- expect(el.checkedIndex).to.eql([]);
987
- });
988
- it('allows new options when multi-choice when requireOptionMatch=false and autocomplete="both", without selecting similar values', async () => {
989
- const el =
990
- /** @type {LionCombobox} */
991
- await fixture(html`
992
- <lion-combobox
993
- name="foo"
994
- multiple-choice
995
- .requireOptionMatch=${false}
996
- autocomplete="both"
997
- >
998
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
999
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1000
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1001
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1002
- </lion-combobox>
1003
- `);
1004
- await el.updateComplete;
1005
- const { _inputNode } = getComboboxMembers(el);
1006
- await mimicUserTyping(el, 'Artist');
1007
- await el.updateComplete;
1008
- mimicKeyPress(_inputNode, 'Enter');
1009
- await el.updateComplete;
1010
- expect(el.modelValue).to.eql(['Artist']);
1011
- });
1012
- it('allows new options when multi-choice when requireOptionMatch=false and autocomplete="both", when deleting autocomplete values using Backspace', async () => {
1013
- const el =
1014
- /** @type {LionCombobox} */
1015
- await fixture(html`
1016
- <lion-combobox
1017
- name="foo"
1018
- multiple-choice
1019
- .requireOptionMatch=${false}
1020
- autocomplete="both"
1021
- >
1022
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1023
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1024
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1025
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1026
- </lion-combobox>
1027
- `);
1028
- await el.updateComplete;
1029
- const { _inputNode } = getComboboxMembers(el);
1030
- await mimicUserTyping(el, 'Art');
1031
- await el.updateComplete;
1032
- await mimicUserTypingAdvanced(el, ['Backspace']);
1033
- await el.updateComplete;
1034
- mimicKeyPress(_inputNode, 'Enter');
1035
- await el.updateComplete;
1036
- expect(el.modelValue).to.eql(['Art']);
1037
- });
1038
- it('allows new custom options when multi-choice when requireOptionMatch=false and autocomplete="both", when deleting autocompleted values using Delete', async () => {
1039
- const el =
1040
- /** @type {LionCombobox} */
1041
- await fixture(html`
1042
- <lion-combobox
1043
- name="foo"
1044
- multiple-choice
1045
- .requireOptionMatch=${false}
1046
- autocomplete="both"
1047
- >
1048
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1049
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1050
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1051
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1052
- </lion-combobox>
1053
- `);
1054
- await el.updateComplete;
1055
- const { _inputNode } = getComboboxMembers(el);
1056
- el.modelValue = [];
1057
- await mimicUserTyping(el, 'Art');
1058
- await el.updateComplete;
1059
- await mimicUserTypingAdvanced(el, ['Delete']);
1060
- await el.updateComplete;
1061
- mimicKeyPress(_inputNode, 'Enter');
1062
- await el.updateComplete;
1063
- expect(el.modelValue).to.eql(['Art']);
1064
- });
1065
- it('allows prefilling the combobox', async () => {
1066
- const autocompleteValues = ['inline', 'none', 'both', 'list'];
1067
- for (const autocompleteValue of autocompleteValues) {
1068
- const el =
1069
- /** @type {LionCombobox} */
1070
- await fixture(html`
1071
- <lion-combobox autocomplete="${autocompleteValue}" .modelValue="${'Chard'}" name="foo">
1072
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1073
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1074
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1075
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1076
- </lion-combobox>
1077
- `);
1078
- expect(el.modelValue).to.equal('Chard');
1079
- // @ts-expect-error
1080
- expect(el._inputNode.value).to.equal('Chard');
1081
- }
1082
- });
1083
- it('submits form on [Enter] when listbox is closed', async () => {
1084
- const submitSpy = sinon.spy((e) => e.preventDefault());
1085
- const el =
1086
- /** @type {HTMLFormElement} */
1087
- await fixture(html`
1088
- <form @submit=${submitSpy}>
1089
- <lion-combobox name="foo">
1090
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1091
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1092
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1093
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1094
- </lion-combobox>
1095
- <button type="submit">submit</button>
1096
- </form>
1097
- `);
1098
- const combobox = /** @type {LionCombobox} */ el.querySelector('[name="foo"]');
1099
- const { _inputNode } = getComboboxMembers(combobox);
1100
- await combobox.updateComplete;
1101
- _inputNode.focus();
1102
- await sendKeys({
1103
- press: 'Enter',
1104
- });
1105
- expect(submitSpy.callCount).to.equal(1);
1106
- });
1107
- it('does not submit form on [Enter] when listbox is opened', async () => {
1108
- const submitSpy = sinon.spy((e) => e.preventDefault());
1109
- const el =
1110
- /** @type {HTMLFormElement} */
1111
- await fixture(html`
1112
- <form @submit=${submitSpy}>
1113
- <lion-combobox name="foo">
1114
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1115
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1116
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1117
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1118
- </lion-combobox>
1119
- <button type="submit">submit</button>
1120
- </form>
1121
- `);
1122
- const combobox = /** @type {LionCombobox} */ el.querySelector('[name="foo"]');
1123
- const { _inputNode } = getComboboxMembers(combobox);
1124
- combobox.opened = true;
1125
- await combobox.updateComplete;
1126
- _inputNode.focus();
1127
- await sendKeys({
1128
- press: 'Enter',
1129
- });
1130
- expect(submitSpy.callCount).to.equal(0);
1131
- });
1132
- });
1133
- describe('Overlay visibility', () => {
1134
- it('does not show overlay on click', async () => {
1135
- const el =
1136
- /** @type {LionCombobox} */
1137
- await fixture(html`
1138
- <lion-combobox name="foo">
1139
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1140
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1141
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1142
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1143
- </lion-combobox>
1144
- `);
1145
- const { _comboboxNode } = getComboboxMembers(el);
1146
- expect(el.opened).to.equal(false);
1147
- _comboboxNode.dispatchEvent(
1148
- new Event('click', {
1149
- bubbles: true,
1150
- composed: true,
1151
- }),
1152
- );
1153
- await el.updateComplete;
1154
- expect(el.opened).to.equal(false);
1155
- });
1156
- it('does not flash the menu when _showOverlayCondition returns "false"', async () => {
1157
- class ComplexCombobox extends LionCombobox {
1158
- _showOverlayCondition() {
1159
- return false;
1160
- }
1161
- }
1162
- const tagName = defineCE(ComplexCombobox);
1163
- const tag = unsafeStatic(tagName);
1164
- const el =
1165
- /** @type {LionCombobox} */
1166
- await fixture(html`
1167
- <${tag} name="combo" label="Display only the label once selected">
1168
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1169
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1170
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1171
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1172
- </${tag}>
1173
- `);
1174
- const dialog = el.shadowRoot?.querySelector('dialog');
1175
- /**
1176
- * hasDropdownFlashed is `true` if the menu was shown for a short period of time and then got closed
1177
- */
1178
- let hasDropdownFlashed = false;
1179
- const observer = new MutationObserver((mutationList) => {
1180
- // eslint-disable-next-line no-unused-vars
1181
- for (const mutation of mutationList) {
1182
- if (dialog?.style.display === '') {
1183
- hasDropdownFlashed = true;
1184
- }
1185
- }
1186
- });
1187
- observer.observe(/** @type {Node} */ dialog, {
1188
- attributeFilter: ['style'],
1189
- });
1190
- const { _inputNode } = getComboboxMembers(el);
1191
- _inputNode.focus();
1192
- await sendKeys({
1193
- type: 'art',
1194
- });
1195
- expect(hasDropdownFlashed).to.be.false;
1196
- });
1197
- it('shows overlay on click when filled', async () => {
1198
- const el =
1199
- /** @type {LionCombobox} */
1200
- await fixture(html`
1201
- <lion-combobox name="foo">
1202
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1203
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1204
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1205
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1206
- </lion-combobox>
1207
- `);
1208
- const { _comboboxNode } = getComboboxMembers(el);
1209
- el.modelValue = 'Art';
1210
- expect(el.opened).to.equal(false);
1211
- _comboboxNode.dispatchEvent(
1212
- new Event('click', {
1213
- bubbles: true,
1214
- composed: true,
1215
- }),
1216
- );
1217
- await el.updateComplete;
1218
- expect(el.opened).to.equal(false);
1219
- });
1220
- it('shows overlay again after select and char keyup', async () => {
1221
- /**
1222
- * Scenario:
1223
- * [1] user focuses textbox: overlay hidden
1224
- * [2] user types char: overlay shows
1225
- * [3] user selects "Artichoke": overlay closes, textbox gets value "Artichoke" and textbox
1226
- * still has focus
1227
- * [4] user changes textbox value to "Artichoke": the overlay should show again
1228
- */
1229
- const el =
1230
- /** @type {LionCombobox} */
1231
- await fixture(html`
1232
- <lion-combobox name="foo">
1233
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1234
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1235
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1236
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1237
- </lion-combobox>
1238
- `);
1239
- const options = el.formElements;
1240
- const { _inputNode } = getComboboxMembers(el);
1241
- expect(el.opened).to.equal(false);
1242
-
1243
- // step [1]
1244
- _inputNode.dispatchEvent(
1245
- new Event('click', {
1246
- bubbles: true,
1247
- composed: true,
1248
- }),
1249
- );
1250
- await el.updateComplete;
1251
- expect(el.opened).to.equal(false);
1252
-
1253
- // step [2]
1254
- await mimicUserTyping(el, 'c');
1255
- await el.updateComplete;
1256
- expect(el.opened).to.equal(true);
1257
-
1258
- // step [3]
1259
- options[0].click();
1260
- await el.updateComplete;
1261
- expect(el.opened).to.equal(false);
1262
- expect(isActiveElement(_inputNode)).to.be.true;
1263
-
1264
- // step [4]
1265
- await el.updateComplete;
1266
- await mimicUserTyping(el, 'c');
1267
- await el.updateComplete;
1268
- expect(el.opened).to.equal(true);
1269
- });
1270
- it('syncs a manually checked selection on overlay close while the input remains focused', async () => {
1271
- const el =
1272
- /** @type {LionCombobox} */
1273
- await fixture(html`
1274
- <lion-combobox name="foo" autocomplete="list">
1275
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1276
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1277
- </lion-combobox>
1278
- `);
1279
- const { _inputNode } = getComboboxMembers(el);
1280
- _inputNode.focus();
1281
- el.opened = true;
1282
- await el.updateComplete;
1283
- expect(el.opened).to.equal(true);
1284
- expect(isActiveElement(_inputNode)).to.be.true;
1285
- el.setCheckedIndex(0);
1286
- await el.updateComplete;
1287
- expect(el.modelValue).to.equal('Artichoke');
1288
- expect(el.checkedIndex).to.equal(0);
1289
- expect(_inputNode.value).to.equal('');
1290
- el.opened = false;
1291
- await el.updateComplete;
1292
- expect(_inputNode.value).to.equal('Artichoke');
1293
- expect(isActiveElement(_inputNode)).to.be.true;
1294
- });
1295
- it('hides (and clears) overlay on [Escape]', async () => {
1296
- const el =
1297
- /** @type {LionCombobox} */
1298
- await fixture(html`
1299
- <lion-combobox name="foo">
1300
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1301
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1302
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1303
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1304
- </lion-combobox>
1305
- `);
1306
- const { _inputNode } = getComboboxMembers(el);
1307
- await mimicUserTyping(el, 'art');
1308
- await el.updateComplete;
1309
- expect(el.opened).to.equal(true);
1310
- expect(_inputNode.value).to.equal('Artichoke');
1311
- _inputNode.dispatchEvent(
1312
- new KeyboardEvent('keydown', {
1313
- key: 'Escape',
1314
- }),
1315
- );
1316
- expect(el.opened).to.equal(false);
1317
- expect(_inputNode.value).to.equal('');
1318
- });
1319
- it('hides overlay on [Enter]', async () => {
1320
- const el =
1321
- /** @type {LionCombobox} */
1322
- await fixture(html`
1323
- <lion-combobox name="foo">
1324
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1325
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1326
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1327
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1328
- </lion-combobox>
1329
- `);
1330
- const { _inputNode } = getComboboxMembers(el);
1331
- await mimicUserTyping(el, 'art');
1332
- await el.updateComplete;
1333
- expect(el.opened).to.equal(true);
1334
- expect(_inputNode.value).to.equal('Artichoke');
1335
-
1336
- // N.B. we do only trigger keydown here (and not mimicKeypress (both keyup and down)),
1337
- // because this closely mimics what happens in the browser
1338
- _inputNode.dispatchEvent(
1339
- new KeyboardEvent('keydown', {
1340
- key: 'Enter',
1341
- }),
1342
- );
1343
- expect(el.opened).to.equal(false);
1344
- expect(_inputNode.value).to.equal('Artichoke');
1345
- });
1346
- it('hides overlay on [Tab]', async () => {
1347
- const el =
1348
- /** @type {LionCombobox} */
1349
- await fixture(html`
1350
- <lion-combobox name="foo">
1351
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1352
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1353
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1354
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1355
- </lion-combobox>
1356
- `);
1357
- const { _inputNode } = getComboboxMembers(el);
1358
- await mimicUserTyping(el, 'art');
1359
- await el.updateComplete;
1360
- expect(el.opened).to.equal(true);
1361
- expect(_inputNode.value).to.equal('Artichoke');
1362
-
1363
- // N.B. we do only trigger keydown here (and not mimicKeypress (both keyup and down)),
1364
- // because this closely mimics what happens in the browser
1365
- _inputNode.dispatchEvent(
1366
- new KeyboardEvent('keydown', {
1367
- key: 'Tab',
1368
- }),
1369
- );
1370
- expect(el.opened).to.equal(false);
1371
- expect(_inputNode.value).to.equal('Artichoke');
1372
- });
1373
- it('keeps overlay open on [Shift]', async () => {
1374
- const el =
1375
- /** @type {LionCombobox} */
1376
- await fixture(html`
1377
- <lion-combobox name="foo">
1378
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1379
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1380
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1381
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1382
- </lion-combobox>
1383
- `);
1384
- const { _inputNode } = getComboboxMembers(el);
1385
- await mimicUserTyping(el, 'art');
1386
- await el.updateComplete;
1387
- expect(el.opened).to.equal(true);
1388
- expect(_inputNode.value).to.equal('Artichoke');
1389
-
1390
- // N.B. we do only trigger keydown here (and not mimicKeypress (both keyup and down)),
1391
- // because this closely mimics what happens in the browser
1392
- _inputNode.dispatchEvent(
1393
- new KeyboardEvent('keydown', {
1394
- key: 'Shift',
1395
- }),
1396
- );
1397
- expect(el.opened).to.equal(true);
1398
- expect(_inputNode.value).to.equal('Artichoke');
1399
- });
1400
- it('clears checkedIndex on empty text', async () => {
1401
- const el =
1402
- /** @type {LionCombobox} */
1403
- await fixture(html`
1404
- <lion-combobox name="foo">
1405
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1406
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1407
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1408
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1409
- </lion-combobox>
1410
- `);
1411
- const { _inputNode } = getComboboxMembers(el);
1412
- await mimicUserTyping(el, 'art');
1413
- await el.updateComplete;
1414
- expect(el.opened).to.equal(true);
1415
- expect(_inputNode.value).to.equal('Artichoke');
1416
- expect(el.checkedIndex).to.equal(0);
1417
-
1418
- // await mimicUserTyping(el, '');
1419
- await sendKeys({
1420
- press: 'Backspace',
1421
- });
1422
- await el.updateComplete;
1423
- el.opened = false;
1424
- await el.updateComplete;
1425
- expect(el.checkedIndex).to.equal(-1);
1426
- });
1427
- it('has the correct Overlay Config defined', async () => {
1428
- const el =
1429
- /** @type {LionCombobox} */
1430
- await fixture(html`
1431
- <lion-combobox name="foo" .showAllOnEmpty="${true}">
1432
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1433
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1434
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1435
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1436
- </lion-combobox>
1437
- `);
1438
- // @ts-expect-error [allow-protected] in test
1439
- const overlayCfg = el._defineOverlayConfig();
1440
- const equalsOrContainsInput = (/** @type {HTMLInputElement|HTMLDivElement} */ invokerNode) =>
1441
- // @ts-expect-error [allow-protected] in test
1442
- invokerNode === el._inputNode || invokerNode.contains(el._inputNode);
1443
- expect(overlayCfg.elementToFocusAfterHide).to.equal(undefined);
1444
- // @ts-expect-error
1445
- expect(equalsOrContainsInput(overlayCfg.invokerNode)).to.be.true;
1446
- expect(overlayCfg.visibilityTriggerFunction).to.equal(undefined);
1447
- });
1448
-
1449
- // NB: If this becomes a suite, move to separate file
1450
- describe('Subclassers', () => {
1451
- it('allows to control overlay visibility via "_showOverlayCondition": should not display overlay if currentValue length condition is not fulfilled', async () => {
1452
- class ShowOverlayConditionCombobox extends LionCombobox {
1453
- /** @param {{ currentValue: string, lastKey:string }} options */
1454
- _showOverlayCondition(options) {
1455
- return (
1456
- // @ts-ignore
1457
- this.__prevCboxValueNonSelected.length > 3 && super._showOverlayCondition(options)
1458
- );
1459
- }
1460
- }
1461
- const tagName = defineCE(ShowOverlayConditionCombobox);
1462
- const tag = unsafeStatic(tagName);
1463
- const el =
1464
- /** @type {LionCombobox} */
1465
- await fixture(html`
1466
- <${tag} name="foo">
1467
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1468
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1469
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1470
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1471
- </${tag}>
1472
- `);
1473
- await mimicUserTyping(el, 'aaa');
1474
- expect(el.opened).to.be.false;
1475
- });
1476
- it('allows to control overlay visibility via "_showOverlayCondition": should display overlay if currentValue length condition is fulfilled', async () => {
1477
- class ShowOverlayConditionCombobox extends LionCombobox {
1478
- /** @param {{ currentValue: string, lastKey:string }} options */
1479
- _showOverlayCondition(options) {
1480
- return (
1481
- // @ts-ignore
1482
- this.__prevCboxValueNonSelected.length > 3 && super._showOverlayCondition(options)
1483
- );
1484
- }
1485
- }
1486
- const tagName = defineCE(ShowOverlayConditionCombobox);
1487
- const tag = unsafeStatic(tagName);
1488
- const el =
1489
- /** @type {LionCombobox} */
1490
- await fixture(html`
1491
- <${tag} name="foo">
1492
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1493
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1494
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1495
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1496
- </${tag}>
1497
- `);
1498
- await mimicUserTyping(el, 'aaaa');
1499
- expect(el.opened).to.be.true;
1500
- });
1501
- it('allows to control overlay visibility via "_showOverlayCondition": should not display overlay if currentValue length condition is not fulfilled after once fulfilled', async () => {
1502
- class ShowOverlayConditionCombobox extends LionCombobox {
1503
- /** @param {{ currentValue: string, lastKey:string }} options */
1504
- _showOverlayCondition(options) {
1505
- return (
1506
- // @ts-ignore
1507
- this.__prevCboxValueNonSelected.length > 3 && super._showOverlayCondition(options)
1508
- );
1509
- }
1510
- }
1511
- const tagName = defineCE(ShowOverlayConditionCombobox);
1512
- const tag = unsafeStatic(tagName);
1513
- const el =
1514
- /** @type {LionCombobox} */
1515
- await fixture(html`
1516
- <${tag} name="foo">
1517
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1518
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1519
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1520
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1521
- </${tag}>
1522
- `);
1523
- await mimicUserTyping(el, 'aaaa');
1524
- expect(el.opened).to.be.true;
1525
- await mimicUserTyping(el, 'aaa');
1526
- await el.updateComplete;
1527
- expect(el.opened).to.be.false;
1528
- });
1529
- });
1530
- describe('Accessibility', () => {
1531
- it('sets "aria-posinset" and "aria-setsize" on visible entries', async () => {
1532
- const el =
1533
- /** @type {LionCombobox} */
1534
- await fixture(html`
1535
- <lion-combobox name="foo" multiple-choice>
1536
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1537
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1538
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1539
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1540
- </lion-combobox>
1541
- `);
1542
- const options = el.formElements;
1543
- expect(el.opened).to.equal(false);
1544
- await mimicUserTyping(el, 'art');
1545
- await el.updateComplete;
1546
- expect(el.opened).to.equal(true);
1547
- const visibleOptions = options.filter((o) => o.style.display !== 'none');
1548
- expect(visibleOptions.length).to.not.equal(0);
1549
- visibleOptions.forEach((o, i) => {
1550
- expect(o.getAttribute('aria-posinset')).to.equal(`${i + 1}`);
1551
- expect(o.getAttribute('aria-setsize')).to.equal(`${visibleOptions.length}`);
1552
- });
1553
- /**
1554
- * @param {{ style: { display: string; }; }} o
1555
- */
1556
- const hiddenOptions = options.filter((o) => o.style.display === 'none');
1557
- /**
1558
- * @param {{ hasAttribute: (arg0: string) => any; }} o
1559
- */
1560
- hiddenOptions.forEach((o) => {
1561
- expect(o.hasAttribute('aria-posinset')).to.equal(false);
1562
- expect(o.hasAttribute('aria-setsize')).to.equal(false);
1563
- });
1564
- });
1565
-
1566
- /**
1567
- * Note that we use aria-hidden instead of 'display:none' to allow for animations
1568
- * (like fade in/out)
1569
- */
1570
- it('sets aria-hidden="true" on hidden entries', async () => {
1571
- const el =
1572
- /** @type {LionCombobox} */
1573
- await fixture(html`
1574
- <lion-combobox name="foo">
1575
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1576
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1577
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1578
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1579
- </lion-combobox>
1580
- `);
1581
- const options = el.formElements;
1582
- expect(el.opened).to.equal(false);
1583
- await mimicUserTyping(el, 'art');
1584
- expect(el.opened).to.equal(true);
1585
- await el.updateComplete;
1586
- const visibleOptions = options.filter((o) => o.style.display !== 'none');
1587
- visibleOptions.forEach((o) => {
1588
- expect(o.hasAttribute('aria-hidden')).to.be.false;
1589
- });
1590
- const hiddenOptions = options.filter((o) => o.style.display === 'none');
1591
- hiddenOptions.forEach((o) => {
1592
- expect(o.getAttribute('aria-hidden')).to.equal('true');
1593
- });
1594
- });
1595
- it('dropdown has a label', async () => {
1596
- const el =
1597
- /** @type {LionCombobox} */
1598
- await fixture(html`
1599
- <lion-combobox name="foo" .validators=${[new Required()]}>
1600
- <lion-option checked .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1601
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1602
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1603
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1604
- </lion-combobox>
1605
- `);
1606
- const { _overlayCtrl } = getComboboxMembers(el);
1607
- expect(_overlayCtrl.contentWrapperNode.getAttribute('aria-label')).to.equal(
1608
- 'Combobox options popup',
1609
- );
1610
- });
1611
- });
1612
- });
1613
-
1614
- // Notice that the LionComboboxInvoker always needs to be used in conjunction with the
1615
- // LionCombobox, and therefore will be tested integrated,
1616
- describe('Invoker component integration', () => {
1617
- describe('Accessibility', () => {
1618
- it('sets role="combobox" on textbox wrapper/listbox sibling', async () => {
1619
- const el =
1620
- /** @type {LionCombobox} */
1621
- await fixture(html`
1622
- <lion-combobox name="foo">
1623
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1624
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
1625
- </lion-combobox>
1626
- `);
1627
- const { _comboboxNode } = getComboboxMembers(el);
1628
- expect(_comboboxNode.getAttribute('role')).to.equal('combobox');
1629
- });
1630
- it('sets aria-expanded to element with role="combobox" in wai-aria 1.0 and 1.1', async () => {
1631
- const el =
1632
- /** @type {LionCombobox} */
1633
- await fixture(html`
1634
- <lion-combobox name="foo" ._ariaVersion="${'1.0'}">
1635
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1636
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
1637
- </lion-combobox>
1638
- `);
1639
- const { _comboboxNode } = getComboboxMembers(el);
1640
- expect(_comboboxNode.getAttribute('aria-expanded')).to.equal('false');
1641
- el.opened = true;
1642
- await el.updateComplete;
1643
- expect(_comboboxNode.getAttribute('aria-expanded')).to.equal('true');
1644
- const el2 =
1645
- /** @type {LionCombobox} */
1646
- await fixture(html`
1647
- <lion-combobox name="foo" ._ariaVersion="${'1.1'}">
1648
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1649
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
1650
- </lion-combobox>
1651
- `);
1652
- const { _comboboxNode: comboboxNode2 } = getComboboxMembers(el2);
1653
- expect(comboboxNode2.getAttribute('aria-expanded')).to.equal('false');
1654
- el2.opened = true;
1655
- await el2.updateComplete;
1656
- expect(_comboboxNode.getAttribute('aria-expanded')).to.equal('true');
1657
- });
1658
- it('makes sure listbox node is not focusable', async () => {
1659
- const el =
1660
- /** @type {LionCombobox} */
1661
- await fixture(html`
1662
- <lion-combobox name="foo">
1663
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1664
- <lion-option .choiceValue="${'20'}">Item 2</lion-option>
1665
- </lion-combobox>
1666
- `);
1667
- const { _listboxNode } = getComboboxMembers(el);
1668
- expect(_listboxNode.hasAttribute('tabindex')).to.be.false;
1669
- });
1670
- });
1671
- });
1672
- describe('Selection display', () => {
1673
- class MySelectionDisplay extends LitElement {
1674
- /**
1675
- * @param {import('lit').PropertyValues } changedProperties
1676
- */
1677
- onComboboxElementUpdated(changedProperties) {
1678
- if (
1679
- changedProperties.has('modelValue') &&
1680
- // @ts-ignore
1681
- this.comboboxElement.multipleChoice
1682
- ) {
1683
- // do smth..
1684
- }
1685
- }
1686
- }
1687
- const selectionDisplayTag = unsafeStatic(defineCE(MySelectionDisplay));
1688
- it('stores internal reference _selectionDisplayNode in LionCombobox', async () => {
1689
- const el =
1690
- /** @type {LionCombobox} */
1691
- await fixture(html`
1692
- <lion-combobox name="foo">
1693
- <${selectionDisplayTag} slot="selection-display"></${selectionDisplayTag}>
1694
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1695
- </lion-combobox>
1696
- `);
1697
- const { _selectionDisplayNode } = getComboboxMembers(el);
1698
- expect(_selectionDisplayNode).to.equal(el.querySelector('[slot=selection-display]'));
1699
- });
1700
- it('sets a reference to combobox element in _selectionDisplayNode', async () => {
1701
- const el =
1702
- /** @type {LionCombobox} */
1703
- await fixture(html`
1704
- <lion-combobox name="foo">
1705
- <${selectionDisplayTag} slot="selection-display"></${selectionDisplayTag}>
1706
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1707
- </lion-combobox>
1708
- `);
1709
- // @ts-ignore allow protected members
1710
- expect(el._selectionDisplayNode.comboboxElement).to.equal(el);
1711
- });
1712
- it('calls "onComboboxElementUpdated(changedProperties)" on "updated" in _selectionDisplayNode', async () => {
1713
- const el =
1714
- /** @type {LionCombobox} */
1715
- await fixture(html`
1716
- <lion-combobox name="foo">
1717
- <${selectionDisplayTag} slot="selection-display"></${selectionDisplayTag}>
1718
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1719
- </lion-combobox>
1720
- `);
1721
- // @ts-ignore sinon type error
1722
- const spy = sinon.spy(el._selectionDisplayNode, 'onComboboxElementUpdated');
1723
- el.requestUpdate('modelValue', undefined);
1724
- await el.updateComplete;
1725
- expect(spy).to.have.been.calledOnce;
1726
- });
1727
-
1728
- // TODO: put those in distinct file if ./docs/lion-combobox-selection-display.js is accessible
1729
- // and exposable
1730
- describe.skip('Selected chips display', () => {
1731
- // it('displays chips next to textbox, ordered based on user selection', async () => {
1732
- // const el = /** @type {LionCombobox} */ (await fixture(html`
1733
- // <lion-combobox name="foo" multiple-choice>
1734
- // <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1735
- // <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1736
- // <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1737
- // <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1738
- // </lion-combobox>
1739
- // `));
1740
- // const options = el.formElements;
1741
- // options[2].checked = true; // Chicory
1742
- // options[0].checked = true; // Artichoke
1743
- // options[1].checked = true; // Chard
1744
- // const chips = Array.from(el._comboboxNode.querySelectorAll('.selection-chip'));
1745
- // expect(chips.map(elm => elm.textContent)).to.eql(['Chicory', 'Artichoke', 'Chard']);
1746
- // expect(el._comboboxNode.selectedElements).to.eql([options[2], options[0], options[1]]);
1747
- // });
1748
- // it('stages deletable chips on [Backspace]', async () => {
1749
- // const el = /** @type {LionCombobox} */ (await fixture(html`
1750
- // <lion-combobox name="foo" multiple-choice>
1751
- // <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1752
- // <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1753
- // <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1754
- // </lion-combobox>
1755
- // `));
1756
- // const options = el.formElements;
1757
- // options[0].checked = true; // Artichoke
1758
- // options[1].checked = true; // Chard
1759
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.false;
1760
- // el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }));
1761
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.true;
1762
- // el._inputNode.blur();
1763
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.false;
1764
- // });
1765
- // it('deletes staged chip on [Backspace]', async () => {
1766
- // const el = /** @type {LionCombobox} */ (await fixture(html`
1767
- // <lion-combobox name="foo" multiple-choice>
1768
- // <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1769
- // <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1770
- // <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1771
- // </lion-combobox>
1772
- // `));
1773
- // const options = el.formElements;
1774
- // options[0].checked = true; // Artichoke
1775
- // options[1].checked = true; // Chard
1776
- // expect(el._comboboxNode.selectedElements).to.eql([options[0], options[1]]);
1777
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.false;
1778
- // el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }));
1779
- // expect(el._comboboxNode.selectedElements).to.eql([options[0], options[1]]);
1780
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.true;
1781
- // el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }));
1782
- // expect(el._comboboxNode.selectedElements).to.eql([options[0]]);
1783
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.true;
1784
- // el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }));
1785
- // expect(el._comboboxNode.selectedElements).to.eql([]);
1786
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.true;
1787
- // el._inputNode.blur();
1788
- // expect(el._comboboxNode.removeChipOnNextBackspace).to.be.false;
1789
- // });
1790
- });
1791
- });
1792
- describe('Autocompletion', () => {
1793
- it('has autocomplete "both" by default', async () => {
1794
- const el =
1795
- /** @type {LionCombobox} */
1796
- await fixture(html`
1797
- <lion-combobox name="foo">
1798
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
1799
- </lion-combobox>
1800
- `);
1801
- expect(el.autocomplete).to.equal('both');
1802
- });
1803
- it('filters options when autocomplete is "both"', async () => {
1804
- const el =
1805
- /** @type {LionCombobox} */
1806
- await fixture(html`
1807
- <lion-combobox name="foo" autocomplete="both">
1808
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1809
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1810
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1811
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1812
- </lion-combobox>
1813
- `);
1814
- await mimicUserTyping(el, 'ch');
1815
- await el.updateComplete;
1816
- expect(getFilteredOptionValues(el)).to.eql(['Artichoke', 'Chard', 'Chicory']);
1817
- });
1818
- it('completes textbox when autocomplete is "both"', async () => {
1819
- const el =
1820
- /** @type {LionCombobox} */
1821
- await fixture(html`
1822
- <lion-combobox name="foo" autocomplete="both">
1823
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1824
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1825
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1826
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1827
- </lion-combobox>
1828
- `);
1829
- await mimicUserTyping(el, 'ch');
1830
- await el.updateComplete;
1831
- const { _inputNode } = getComboboxMembers(el);
1832
- expect(_inputNode.value).to.equal('Chard');
1833
- expect(_inputNode.selectionStart).to.equal(2);
1834
- expect(_inputNode.selectionEnd).to.equal(_inputNode.value.length);
1835
-
1836
- // We don't autocomplete when characters are removed
1837
- await mimicUserTyping(el, 'c'); // The user pressed backspace (number of chars decreased)
1838
- expect(_inputNode.value).to.equal('c');
1839
- expect(_inputNode.selectionStart).to.equal(_inputNode.value.length);
1840
- });
1841
- it('filters options when autocomplete is "list"', async () => {
1842
- const el =
1843
- /** @type {LionCombobox} */
1844
- await fixture(html`
1845
- <lion-combobox name="foo" autocomplete="list">
1846
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1847
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1848
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1849
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1850
- </lion-combobox>
1851
- `);
1852
- const { _inputNode } = getComboboxMembers(el);
1853
- await mimicUserTyping(el, 'ch');
1854
- await el.updateComplete;
1855
- expect(getFilteredOptionValues(el)).to.eql(['Artichoke', 'Chard', 'Chicory']);
1856
- expect(_inputNode.value).to.equal('ch');
1857
- });
1858
- it('does not filter options when autocomplete is "none"', async () => {
1859
- const el =
1860
- /** @type {LionCombobox} */
1861
- await fixture(html`
1862
- <lion-combobox name="foo" autocomplete="none">
1863
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1864
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1865
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1866
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1867
- </lion-combobox>
1868
- `);
1869
- await mimicUserTyping(el, 'ch');
1870
- await el.updateComplete;
1871
- expect(getFilteredOptionValues(el)).to.eql([
1872
- 'Artichoke',
1873
- 'Chard',
1874
- 'Chicory',
1875
- 'Victoria Plum',
1876
- ]);
1877
- });
1878
- it('does not autocomplete on [Enter] when textbox content does not match options', async () => {
1879
- const el =
1880
- /** @type {LionCombobox} */
1881
- await fixture(html`
1882
- <lion-combobox name="foo">
1883
- <lion-option .choiceValue="${'Mango'}">Mango</lion-option>
1884
- <lion-option .choiceValue="${'Lemon'}">Lemon</lion-option>
1885
- <lion-option .choiceValue="${'Apple'}">Apple</lion-option>
1886
- </lion-combobox>
1887
- `);
1888
- const { _inputNode } = getComboboxMembers(el);
1889
- mimicUserTypingAdvanced(el, ['m', 'a', 'k']);
1890
- await el.updateComplete;
1891
- mimicKeyPress(_inputNode, 'Enter');
1892
- await el.updateComplete;
1893
- await el.updateComplete;
1894
- expect(_inputNode.value).to.equal('Mak');
1895
- });
1896
- it('does not autocomplete on [Enter] when textbox content does not match options and content was cleared via [Backspace]', async () => {
1897
- const el =
1898
- /** @type {LionCombobox} */
1899
- await fixture(html`
1900
- <lion-combobox name="foo">
1901
- <lion-option .choiceValue="${'Mango'}">Mango</lion-option>
1902
- <lion-option .choiceValue="${'Lemon'}">Lemon</lion-option>
1903
- <lion-option .choiceValue="${'Apple'}">Apple</lion-option>
1904
- </lion-combobox>
1905
- `);
1906
- const { _inputNode } = getComboboxMembers(el);
1907
- mimicUserTypingAdvanced(el, ['m', 'o', 'Backspace', 'Backspace', 'm', 'o']);
1908
- await el.updateComplete;
1909
- await el.updateComplete;
1910
- await el.updateComplete;
1911
- await el.updateComplete;
1912
- mimicKeyPress(_inputNode, 'Enter');
1913
- await el.updateComplete;
1914
- await el.updateComplete;
1915
- expect(_inputNode.value).to.equal('Mo');
1916
- });
1917
- it('does not filter options when autocomplete is "inline"', async () => {
1918
- const el =
1919
- /** @type {LionCombobox} */
1920
- await fixture(html`
1921
- <lion-combobox name="foo" autocomplete="inline">
1922
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1923
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1924
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1925
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1926
- </lion-combobox>
1927
- `);
1928
- await mimicUserTyping(el, 'ch');
1929
- await el.updateComplete;
1930
- expect(getFilteredOptionValues(el)).to.eql([
1931
- 'Artichoke',
1932
- 'Chard',
1933
- 'Chicory',
1934
- 'Victoria Plum',
1935
- ]);
1936
- });
1937
- it('resets "checkedIndex" when going from matched to unmatched textbox value', async () => {
1938
- const el =
1939
- /** @type {LionCombobox} */
1940
- await fixture(html`
1941
- <lion-combobox name="foo" autocomplete="inline">
1942
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1943
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1944
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1945
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1946
- </lion-combobox>
1947
- `);
1948
- await mimicUserTyping(el, 'ch');
1949
- await el.updateComplete;
1950
- expect(el.checkedIndex).to.equal(1);
1951
- await mimicUserTyping(el, 'cho');
1952
- await el.updateComplete;
1953
- expect(el.checkedIndex).to.equal(-1);
1954
-
1955
- // Works for autocomplete 'both' as well.
1956
- const el2 =
1957
- /** @type {LionCombobox} */
1958
- await fixture(html`
1959
- <lion-combobox name="foo" autocomplete="both">
1960
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1961
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1962
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1963
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1964
- </lion-combobox>
1965
- `);
1966
- await mimicUserTyping(el2, 'ch');
1967
- await el2.updateComplete;
1968
- expect(el2.checkedIndex).to.equal(1);
1969
-
1970
- // Also works when 'diminishing amount of chars'
1971
- await mimicUserTyping(el2, 'x');
1972
- await el2.updateComplete;
1973
- expect(el2.checkedIndex).to.equal(-1);
1974
- });
1975
- it('resets "checkedIndex" when going from matched to another matched textbox value', async () => {
1976
- const el =
1977
- /** @type {LionCombobox} */
1978
- await fixture(html`
1979
- <lion-combobox name="foo" autocomplete="both">
1980
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1981
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
1982
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
1983
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
1984
- </lion-combobox>
1985
- `);
1986
-
1987
- /** Goes from 2nd option Chard to 3rd option Chicory */
1988
- await mimicUserTyping(el, 'ch');
1989
- await el.updateComplete;
1990
- expect(el.checkedIndex).to.equal(1);
1991
- await mimicUserTyping(el, 'chi');
1992
- await el.updateComplete;
1993
- expect(el.checkedIndex).to.equal(2);
1994
- const el2 =
1995
- /** @type {LionCombobox} */
1996
- await fixture(html`
1997
- <lion-combobox name="foo" autocomplete="both">
1998
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
1999
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2000
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2001
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2002
- </lion-combobox>
2003
- `);
2004
- await mimicUserTyping(el2, 'ch');
2005
- await el2.updateComplete;
2006
- expect(el2.checkedIndex).to.equal(1);
2007
-
2008
- // match-mode all ensures the user sees Artichoke option, but it's not
2009
- // auto-completed or auto-selected, because it doesn't start with "cho"
2010
- // See next test for more clarification
2011
- await mimicUserTyping(el2, 'cho');
2012
- await el2.updateComplete;
2013
- expect(el2.checkedIndex).to.equal(-1);
2014
- expect(getFilteredOptionValues(el2)).to.eql(['Artichoke']);
2015
- });
2016
- it('sets "checkedIndex" only if the match, matches from the beginning of the word', async () => {
2017
- const el =
2018
- /** @type {LionCombobox} */
2019
- await fixture(html`
2020
- <lion-combobox name="foo" autocomplete="both">
2021
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2022
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2023
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2024
- <lion-option .choiceValue="${'Daikon'}">Daikon</lion-option>
2025
- </lion-combobox>
2026
- `);
2027
-
2028
- // first match is Char'd', but better match is 'D'aikon
2029
- await mimicUserTyping(el, 'd');
2030
- await el.updateComplete;
2031
- expect(el.checkedIndex).to.equal(3);
2032
- expect(getFilteredOptionValues(el)).to.eql(['Chard', 'Daikon']);
2033
- });
2034
- it('completes chars inside textbox', async () => {
2035
- const el =
2036
- /** @type {LionCombobox} */
2037
- await fixture(html`
2038
- <lion-combobox name="foo" autocomplete="inline">
2039
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2040
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2041
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2042
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2043
- </lion-combobox>
2044
- `);
2045
- const { _inputNode } = getComboboxMembers(el);
2046
- await mimicUserTyping(el, 'ch');
2047
- await el.updateComplete;
2048
- expect(_inputNode.value).to.equal('Chard');
2049
- expect(_inputNode.selectionStart).to.equal('ch'.length);
2050
- expect(_inputNode.selectionEnd).to.equal('Chard'.length);
2051
- await mimicUserTypingAdvanced(el, ['i', 'c']);
2052
- await el.updateComplete;
2053
- expect(_inputNode.value).to.equal('Chicory');
2054
- expect(_inputNode.selectionStart).to.equal('chic'.length);
2055
- expect(_inputNode.selectionEnd).to.equal('Chicory'.length);
2056
-
2057
- // Diminishing chars, but autocompletion
2058
- await mimicUserTypingAdvanced(el, ['Backspace', 'Backspace', 'Backspace']); // Ch
2059
- await el.updateComplete;
2060
- expect(_inputNode.value).to.equal('Ch');
2061
- expect(_inputNode.selectionStart).to.equal('ch'.length);
2062
- expect(_inputNode.selectionEnd).to.equal('ch'.length);
2063
- });
2064
- it('synchronizes textbox on overlay close', async () => {
2065
- const el =
2066
- /** @type {LionCombobox} */
2067
- await fixture(html`
2068
- <lion-combobox name="foo" autocomplete="none">
2069
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2070
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2071
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2072
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2073
- </lion-combobox>
2074
- `);
2075
- const { _inputNode } = getComboboxMembers(el);
2076
- await el.updateComplete;
2077
- expect(_inputNode.value).to.equal('');
2078
-
2079
- /**
2080
- * @param {'none' | 'list' | 'inline' | 'both'} autocomplete
2081
- * @param {number|number[]} index
2082
- * @param {string} valueOnClose
2083
- */
2084
- async function performChecks(autocomplete, index, valueOnClose) {
2085
- await el.updateComplete;
2086
- el.opened = true;
2087
- el.setCheckedIndex(-1);
2088
- await el.updateComplete;
2089
- el.autocomplete = autocomplete;
2090
- el.setCheckedIndex(index);
2091
- await el.updateComplete;
2092
- el.opened = false;
2093
- await el.updateComplete;
2094
- expect(_inputNode.value).to.equal(valueOnClose, autocomplete);
2095
- }
2096
- await performChecks('none', 0, 'Artichoke');
2097
- await performChecks('list', 0, 'Artichoke');
2098
- await performChecks('inline', 0, 'Artichoke');
2099
- await performChecks('both', 0, 'Artichoke');
2100
- el.multipleChoice = true;
2101
- await performChecks('none', [0, 1], '');
2102
- await performChecks('list', [0, 1], '');
2103
- await performChecks('inline', [0, 1], '');
2104
- await performChecks('both', [0, 1], '');
2105
- });
2106
- it('is possible to adjust textbox synchronize condition on overlay close', async () => {
2107
- const el =
2108
- /** @type {LionCombobox} */
2109
- await fixture(html`
2110
- <lion-combobox name="foo" autocomplete="none" ._syncToTextboxCondition="${() => false}">
2111
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2112
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2113
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2114
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2115
- </lion-combobox>
2116
- `);
2117
- const { _inputNode } = getComboboxMembers(el);
2118
- expect(_inputNode.value).to.equal('');
2119
-
2120
- /**
2121
- * @param {'none' | 'list' | 'inline' | 'both'} autocomplete
2122
- * @param {number|number[]} index
2123
- * @param {string} valueOnClose
2124
- */
2125
- async function performChecks(autocomplete, index, valueOnClose) {
2126
- await el.updateComplete;
2127
- el.opened = true;
2128
- el.setCheckedIndex(-1);
2129
- await el.updateComplete;
2130
- el.autocomplete = autocomplete;
2131
- el.setCheckedIndex(index);
2132
- el.opened = false;
2133
- await el.updateComplete;
2134
- expect(_inputNode.value).to.equal(valueOnClose);
2135
- }
2136
- await performChecks('none', 0, '');
2137
- await performChecks('list', 0, '');
2138
- await performChecks('inline', 0, '');
2139
- await performChecks('both', 0, '');
2140
- el.multipleChoice = true;
2141
- await performChecks('none', [0, 1], '');
2142
- await performChecks('list', [0, 1], '');
2143
- await performChecks('inline', [0, 1], '');
2144
- await performChecks('both', [0, 1], '');
2145
- });
2146
- it('does inline autocompletion when adding chars', async () => {
2147
- const el =
2148
- /** @type {LionCombobox} */
2149
- await fixture(html`
2150
- <lion-combobox name="foo" autocomplete="inline">
2151
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2152
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2153
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2154
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2155
- </lion-combobox>
2156
- `);
2157
- await mimicUserTyping(el, 'ch'); // ch
2158
- await el.updateComplete; // Ch[ard]
2159
- expect(el.activeIndex).to.equal(1);
2160
- expect(el.checkedIndex).to.equal(1);
2161
- await mimicUserTypingAdvanced(el, ['i', 'c']); // Chic
2162
- await el.updateComplete; // Chic[ory]
2163
- expect(el.activeIndex).to.equal(2);
2164
- expect(el.checkedIndex).to.equal(2);
2165
- await mimicUserTypingAdvanced(el, ['Backspace', 'Backspace', 'Backspace', 'Backspace', 'h']); // Ch
2166
- await el.updateComplete; // Ch[ard]
2167
- expect(el.activeIndex).to.equal(1);
2168
- expect(el.checkedIndex).to.equal(1);
2169
- });
2170
- it('does inline autocompletion when changing the word', async () => {
2171
- const el =
2172
- /** @type {LionCombobox} */
2173
- await fixture(html`
2174
- <lion-combobox name="foo" autocomplete="inline">
2175
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2176
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2177
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2178
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2179
- </lion-combobox>
2180
- `);
2181
- await mimicUserTyping(el, 'ch');
2182
- await el.updateComplete;
2183
- expect(el.activeIndex).to.equal(1);
2184
- expect(el.checkedIndex).to.equal(1);
2185
- await mimicUserTypingAdvanced(el, ['i']);
2186
- await el.updateComplete;
2187
- expect(el.activeIndex).to.equal(2);
2188
- expect(el.checkedIndex).to.equal(2);
2189
-
2190
- // Diminishing chars, but autocompletion
2191
- await mimicUserTypingAdvanced(el, ['Backspace', 'Backspace', 'Backspace', 'Backspace', 'a']); // a
2192
- await el.updateComplete;
2193
- expect(el.activeIndex).to.equal(0);
2194
- expect(el.checkedIndex).to.equal(0);
2195
- });
2196
- it('filters options correctly when changing the middle of the word', async () => {
2197
- const el =
2198
- /** @type {LionCombobox} */
2199
- await fixture(html`
2200
- <lion-combobox name="foo" autocomplete="list" match-mode="all">
2201
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2202
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2203
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2204
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2205
- </lion-combobox>
2206
- `);
2207
- const { _inputNode } = getComboboxMembers(el);
2208
- await mimicUserTyping(el, 'char');
2209
- expect(_inputNode.value).to.equal('char');
2210
- await el.updateComplete; // Char
2211
-
2212
- expect(getFilteredOptionValues(el)).to.eql(['Chard']);
2213
- _inputNode.setSelectionRange(3, 3);
2214
- await mimicUserTypingAdvanced(el, ['Backspace', 'i', 'c', 'o']);
2215
- await el.updateComplete; // Chicor
2216
-
2217
- expect(_inputNode.value).to.equal('chicor');
2218
- expect(getFilteredOptionValues(el)).to.eql(['Chicory']);
2219
- });
2220
- it('computation of "user intends autofill" works correctly afer autofill', async () => {
2221
- const el =
2222
- /** @type {LionCombobox} */
2223
- await fixture(html`
2224
- <lion-combobox name="foo" autocomplete="inline">
2225
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2226
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2227
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2228
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2229
- </lion-combobox>
2230
- `);
2231
- const { _inputNode } = getComboboxMembers(el);
2232
- await mimicUserTyping(el, 'ch');
2233
- await el.updateComplete;
2234
- expect(_inputNode.value).to.equal('Chard');
2235
- expect(_inputNode.selectionStart).to.equal('Ch'.length);
2236
- expect(_inputNode.selectionEnd).to.equal('Chard'.length);
2237
-
2238
- // Autocompletion happened. When we go backwards ('Ch[ard]' => 'Ch'), we should not
2239
- // autocomplete to 'Chard' anymore.
2240
- await mimicUserTypingAdvanced(el, ['Backspace']);
2241
- await el.updateComplete;
2242
- expect(_inputNode.value).to.equal('Ch'); // so not 'Chard'
2243
- expect(_inputNode.selectionStart).to.equal('Ch'.length);
2244
- expect(_inputNode.selectionEnd).to.equal('Ch'.length);
2245
- });
2246
- describe('Server side completion support', () => {
2247
- const listboxData = ['lorem', 'ipsum', 'dolor', 'sit', 'amet'];
2248
- class MyEl extends LitElement {
2249
- constructor() {
2250
- super();
2251
- /** @type {string[]} */
2252
- this.options = [...listboxData];
2253
- }
2254
- clearOptions() {
2255
- /** @type {string[]} */
2256
- this.options = [];
2257
- this.requestUpdate();
2258
- }
2259
- addOption() {
2260
- this.options.push(`option ${this.options.length + 1}`);
2261
- this.requestUpdate();
2262
- }
2263
- fillAllOptions() {
2264
- this.options = [...listboxData];
2265
- this.requestUpdate();
2266
- }
2267
- get combobox() {
2268
- return /** @type {LionCombobox} */ this.shadowRoot?.querySelector('#combobox');
2269
- }
2270
- render() {
2271
- return html`
2272
- <lion-combobox id="combobox" label="Server side completion">
2273
- ${this.options.map(
2274
- (option) => html` <lion-option .choiceValue="${option}">${option}</lion-option> `,
2275
- )}
2276
- </lion-combobox>
2277
- `;
2278
- }
2279
- }
2280
- const tagName = defineCE(MyEl);
2281
- const wrappingTag = unsafeStatic(tagName);
2282
- it('calls "_handleAutocompletion" after externally changing options', async () => {
2283
- const el = /** @type {MyEl} */ await fixture(html`<${wrappingTag}></${wrappingTag}>`);
2284
- await el.combobox.registrationComplete;
2285
- // @ts-ignore [allow-protected] in test
2286
- const spy = sinon.spy(el.combobox, '_handleAutocompletion');
2287
- el.addOption();
2288
- await el.updateComplete;
2289
- await el.updateComplete;
2290
- expect(spy).to.have.been.calledOnce;
2291
- el.clearOptions();
2292
- await el.updateComplete;
2293
- await el.updateComplete;
2294
- expect(spy).to.have.been.calledTwice;
2295
- });
2296
- it('should handle dynamic options', async () => {
2297
- // Arrange
2298
- const el = /** @type {MyEl} */ await fixture(html`<${wrappingTag}></${wrappingTag}>`);
2299
- await el.combobox.registrationComplete;
2300
- const { _inputNode } = getComboboxMembers(el.combobox);
2301
-
2302
- // Act (start typing)
2303
- await mimicUserTypingAdvanced(el.combobox, ['l']);
2304
- // simulate fetching data from server
2305
- el.clearOptions();
2306
- await el.updateComplete;
2307
- await el.updateComplete;
2308
- el.fillAllOptions();
2309
- await el.updateComplete;
2310
- await el.updateComplete;
2311
-
2312
- // Assert
2313
- expect(_inputNode.value).to.equal('lorem');
2314
- expect(_inputNode.selectionStart).to.equal(1);
2315
- expect(_inputNode.selectionEnd).to.equal(_inputNode.value.length);
2316
- expect(getFilteredOptionValues(el.combobox)).to.eql(['lorem', 'dolor']);
2317
-
2318
- // Act (continue typing)
2319
- await mimicUserTypingAdvanced(el.combobox, ['o']);
2320
- // simulate fetching data from server
2321
- el.clearOptions();
2322
- await el.updateComplete;
2323
- await el.updateComplete;
2324
- el.fillAllOptions();
2325
- await el.updateComplete;
2326
- await el.updateComplete;
2327
-
2328
- // Assert
2329
- expect(_inputNode.value).to.equal('lorem');
2330
- expect(_inputNode.selectionStart).to.equal(2);
2331
- expect(_inputNode.selectionEnd).to.equal(_inputNode.value.length);
2332
- expect(getFilteredOptionValues(el.combobox)).to.eql(['lorem', 'dolor']);
2333
-
2334
- // We don't autocomplete when characters are removed
2335
- await mimicUserTypingAdvanced(el.combobox, ['Backspace', 'Backspace']);
2336
- expect(_inputNode.value).to.equal('l');
2337
- expect(_inputNode.selectionStart).to.equal(_inputNode.value.length);
2338
- });
2339
- });
2340
- describe('Subclassers', () => {
2341
- it('allows to configure autoselect', async () => {
2342
- class X extends LionCombobox {
2343
- _autoSelectCondition() {
2344
- return true;
2345
- }
2346
- }
2347
- const tagName = defineCE(X);
2348
- const tag = unsafeStatic(tagName);
2349
- const el =
2350
- /** @type {LionCombobox} */
2351
- await fixture(html`
2352
- <${tag} name="foo" opened>
2353
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2354
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2355
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2356
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2357
- </${tag}>
2358
- `);
2359
- await mimicUserTypingAdvanced(el, ['v', 'i']); // so we have options ['Victoria Plum']
2360
- await el.updateComplete;
2361
- expect(el.checkedIndex).to.equal(3);
2362
- });
2363
- });
2364
- it('highlights first occurrence of matching option', async () => {
2365
- const el =
2366
- /** @type {LionCombobox} */
2367
- await fixture(html`
2368
- <lion-combobox name="foo" match-mode="all">
2369
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2370
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2371
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2372
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2373
- </lion-combobox>
2374
- `);
2375
- const options = el.formElements;
2376
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'c');
2377
- await el.updateComplete;
2378
- expect(options[0]).lightDom.to.equal(`<span aria-label="Artichoke">Arti<b>c</b>hoke</span>`);
2379
- expect(options[1]).lightDom.to.equal(`<span aria-label="Chard"><b>C</b>hard</span>`);
2380
- expect(options[2]).lightDom.to.equal(`<span aria-label="Chicory"><b>C</b>hicory</span>`);
2381
- expect(options[3]).lightDom.to.equal(
2382
- `<span aria-label="Victoria Plum">Vi<b>c</b>toria Plum</span>`,
2383
- );
2384
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
2385
- await el.updateComplete;
2386
- expect(options[0]).lightDom.to.equal(`<span aria-label="Artichoke">Arti<b>ch</b>oke</span>`);
2387
- expect(options[1]).lightDom.to.equal(`<span aria-label="Chard"><b>Ch</b>ard</span>`);
2388
- expect(options[2]).lightDom.to.equal(`<span aria-label="Chicory"><b>Ch</b>icory</span>`);
2389
- expect(options[3]).lightDom.to.equal(`Victoria Plum`);
2390
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'D');
2391
- await el.updateComplete;
2392
- expect(options[0]).lightDom.to.equal(`Artichoke`);
2393
- expect(options[1]).lightDom.to.equal(`<span aria-label="Chard">Char<b>d</b></span>`);
2394
- expect(options[2]).lightDom.to.equal(`Chicory`);
2395
- expect(options[3]).lightDom.to.equal(`Victoria Plum`);
2396
- });
2397
- it('highlights matching complex options', async () => {
2398
- const el =
2399
- /** @type {LionCombobox} */
2400
- await fixture(html`
2401
- <lion-combobox name="foo" match-mode="all">
2402
- <lion-option .choiceValue="${'Artichoke'}">
2403
- <div>Artichoke</div>
2404
- <small>Cardoon</small>
2405
- </lion-option>
2406
- <lion-option .choiceValue="${'Chard'}">
2407
- <div>Chard</div>
2408
- <small>Beet</small>
2409
- </lion-option>
2410
- <lion-option .choiceValue="${'Chicory'}">
2411
- <div>Chicory</div>
2412
- <small>Chicory</small>
2413
- </lion-option>
2414
- <lion-option .choiceValue="${'Victoria Plum'}">
2415
- <div>Victoria Plum</div>
2416
- <small>Prunus domestica</small>
2417
- </lion-option>
2418
- </lion-combobox>
2419
- `);
2420
- const options = el.formElements;
2421
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
2422
- await el.updateComplete;
2423
- expect(options[0]).lightDom.to.equal(
2424
- `<span aria-label=" Artichoke Cardoon "><div>Arti<b>ch</b>oke</div><small>Cardoon</small>`,
2425
- );
2426
- expect(options[1]).lightDom.to.equal(
2427
- `<span aria-label=" Chard Beet "><div><b>Ch</b>ard</div><small>Beet</small>`,
2428
- );
2429
- expect(options[2]).lightDom.to.equal(
2430
- `<span aria-label=" Chicory Chicory "><div><b>Ch</b>icory</div><small><b>Ch</b>icory</small>`,
2431
- );
2432
- expect(options[3]).lightDom.to.equal(
2433
- `<div>Victoria Plum</div><small>Prunus domestica</small>`,
2434
- );
2435
- });
2436
- it('resets removes highlights on reset()', async () => {
2437
- const el =
2438
- /** @type {LionCombobox} */
2439
- await fixture(html`
2440
- <lion-combobox name="foo" match-mode="all">
2441
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2442
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2443
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2444
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2445
- </lion-combobox>
2446
- `);
2447
- const options = el.formElements;
2448
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'c');
2449
- await el.updateComplete;
2450
- expect(options[0]).lightDom.to.equal(`<span aria-label="Artichoke">Arti<b>c</b>hoke</span>`);
2451
- el.reset();
2452
- await el.updateComplete;
2453
- expect(options[0]).lightDom.to.equal(`Artichoke`);
2454
- });
2455
- it('synchronizes textbox when autocomplete is "inline" or "both"', async () => {
2456
- const el =
2457
- /** @type {LionCombobox} */
2458
- await fixture(html`
2459
- <lion-combobox name="foo" autocomplete="none">
2460
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2461
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2462
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2463
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2464
- </lion-combobox>
2465
- `);
2466
- const { _inputNode } = getComboboxMembers(el);
2467
- expect(_inputNode.value).to.equal('');
2468
- el.setCheckedIndex(-1);
2469
- el.autocomplete = 'list';
2470
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2471
- expect(_inputNode.value).to.equal('art');
2472
- el.setCheckedIndex(-1);
2473
- el.value = '';
2474
- el.autocomplete = 'none';
2475
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2476
- expect(_inputNode.value).to.equal('art');
2477
- el.setCheckedIndex(-1);
2478
- el.value = '';
2479
- el.autocomplete = 'inline';
2480
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2481
- expect(_inputNode.value).to.equal('Artichoke');
2482
- el.setCheckedIndex(-1);
2483
- el.value = '';
2484
- el.autocomplete = 'both';
2485
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2486
- expect(_inputNode.value).to.equal('Artichoke');
2487
- });
2488
- it('synchronizes last index to textbox when autocomplete is "inline" or "both" when multipleChoice', async () => {
2489
- const el =
2490
- /** @type {LionCombobox} */
2491
- await fixture(html`
2492
- <lion-combobox name="foo" autocomplete="none" multiple-choice>
2493
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2494
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2495
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2496
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2497
- </lion-combobox>
2498
- `);
2499
- const { _inputNode } = getComboboxMembers(el);
2500
- expect(_inputNode.value).to.eql('');
2501
- el.setCheckedIndex(-1);
2502
- el.setCheckedIndex(-1);
2503
- el.autocomplete = 'list';
2504
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2505
- expect(_inputNode.value).to.equal('art');
2506
- el.setCheckedIndex(-1);
2507
- el.value = '';
2508
- el.autocomplete = 'none';
2509
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2510
- expect(_inputNode.value).to.equal('art');
2511
- el.setCheckedIndex(-1);
2512
- el.value = '';
2513
- el.autocomplete = 'inline';
2514
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2515
- expect(_inputNode.value).to.equal('Artichoke');
2516
- el.setCheckedIndex(-1);
2517
- el.value = '';
2518
- el.autocomplete = 'both';
2519
- await mimicUserTypingAdvanced(el, ['a', 'r', 't']);
2520
- expect(_inputNode.value).to.equal('Artichoke');
2521
- });
2522
- describe('Subclassers', () => {
2523
- it('allows to override "_syncCheckedWithTextboxMultiple"', async () => {
2524
- class X extends LionCombobox {
2525
- // eslint-disable-next-line no-unused-vars
2526
- _syncToTextboxCondition() {
2527
- return true;
2528
- }
2529
-
2530
- /**
2531
- * @param {any} modelValue
2532
- * @param {any} oldModelValue
2533
- */
2534
- // eslint-disable-next-line no-unused-vars
2535
- _syncToTextboxMultiple(modelValue, oldModelValue) {
2536
- // In a real scenario (depending on how selection display works),
2537
- // you could override the default (last selected option) with '' for instance
2538
- this._setTextboxValue(`${modelValue}-${oldModelValue}-multi`);
2539
- }
2540
- }
2541
- const tagName = defineCE(X);
2542
- const tag = unsafeStatic(tagName);
2543
- const el =
2544
- /** @type {LionCombobox} */
2545
- await fixture(html`
2546
- <${tag} name="foo" autocomplete="none" multiple-choice>
2547
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2548
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2549
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2550
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2551
- </${tag}>
2552
- `);
2553
- const { _inputNode } = getComboboxMembers(el);
2554
- el.setCheckedIndex(-1);
2555
- el.autocomplete = 'none';
2556
- el.setCheckedIndex([0]);
2557
- expect(_inputNode.value).to.equal('Artichoke--multi');
2558
- el.setCheckedIndex(-1);
2559
- el.autocomplete = 'list';
2560
- el.setCheckedIndex([0]);
2561
- expect(_inputNode.value).to.equal('Artichoke--multi');
2562
- el.setCheckedIndex(-1);
2563
- el.autocomplete = 'inline';
2564
- el.setCheckedIndex([0]);
2565
- expect(_inputNode.value).to.equal('Artichoke--multi');
2566
- el.setCheckedIndex(-1);
2567
- el.autocomplete = 'both';
2568
- el.setCheckedIndex([0]);
2569
- expect(_inputNode.value).to.equal('Artichoke--multi');
2570
- });
2571
- describe('Override _getTextboxValueFromOption', () => {
2572
- it('allows to override "_getTextboxValueFromOption" and sync to textbox when multiple', async () => {
2573
- class X extends LionCombobox {
2574
- /**
2575
- * Return the value to be used for the input value
2576
- * @overridable
2577
- * @param {any} option
2578
- * @returns {string}
2579
- */
2580
- // eslint-disable-next-line class-methods-use-this
2581
- _getTextboxValueFromOption(option) {
2582
- return option.label;
2583
- }
2584
- }
2585
- const tagName = defineCE(X);
2586
- const tag = unsafeStatic(tagName);
2587
- const el =
2588
- /** @type {LionCombobox} */
2589
- await fixture(html`
2590
- <${tag} name="foo" autocomplete="both" multiple-choice>
2591
- <lion-option .label="${'Artichoke as label'}" .choiceValue="${{
2592
- value: 'Artichoke',
2593
- }}">Artichoke</lion-option>
2594
- <lion-option .label="${'Chard as label'}" .choiceValue="${{
2595
- value: 'Chard',
2596
- }}">Chard</lion-option>
2597
- <lion-option .label="${'Chicory as label'}" .choiceValue="${{
2598
- value: 'Chicory',
2599
- }}">Chicory</lion-option>
2600
- <lion-option .label="${'Victoria Plum as label'}" .choiceValue="${{
2601
- value: 'Victoria Plum',
2602
- }}">Victoria Plum</lion-option>
2603
- </${tag}>
2604
- `);
2605
- const { _inputNode } = getComboboxMembers(el);
2606
- el.setCheckedIndex(-1);
2607
-
2608
- // Act
2609
- el.setCheckedIndex([0]);
2610
-
2611
- // Assert
2612
- expect(_inputNode.value).to.equal('Artichoke as label');
2613
-
2614
- // Act
2615
- el.setCheckedIndex([3]);
2616
-
2617
- // Assert
2618
- expect(_inputNode.value).to.equal('Victoria Plum as label');
2619
- });
2620
- it('allows to override "_getTextboxValueFromOption" and sync modelValue with textbox', async () => {
2621
- class X extends LionCombobox {
2622
- /**
2623
- * Return the value to be used for the input value
2624
- * @overridable
2625
- * @param {any} option
2626
- * @returns {string}
2627
- */
2628
- // eslint-disable-next-line class-methods-use-this
2629
- _getTextboxValueFromOption(option) {
2630
- if (option && option.label) {
2631
- return option.label;
2632
- }
2633
- return this.modelValue;
2634
- }
2635
- }
2636
- const tagName = defineCE(X);
2637
- const tag = unsafeStatic(tagName);
2638
- const el =
2639
- /** @type {LionCombobox} */
2640
- await fixture(html`
2641
- <${tag} name="foo">
2642
- <lion-option .label="${'Artichoke as label'}" .choiceValue="${{
2643
- value: 'Artichoke',
2644
- }}">Artichoke</lion-option>
2645
- <lion-option .label="${'Chard as label'}" .choiceValue="${{
2646
- value: 'Chard',
2647
- }}" checked>Chard</lion-option>
2648
- <lion-option .label="${'Chicory as label'}" .choiceValue="${{
2649
- value: 'Chicory',
2650
- }}">Chicory</lion-option>
2651
- <lion-option .label="${'Victoria Plum as label'}" .choiceValue="${{
2652
- value: 'Victoria Plum',
2653
- }}">Victoria Plum</lion-option>
2654
- </${tag}>
2655
- `);
2656
- const { _inputNode } = getComboboxMembers(el);
2657
-
2658
- // Assume
2659
- expect(_inputNode.value).to.equal('Chard as label');
2660
-
2661
- // Act
2662
- el.modelValue = {
2663
- value: 'Chicory',
2664
- };
2665
- await el.updateComplete;
2666
-
2667
- // Assert
2668
- expect(_inputNode.value).to.equal('Chicory as label');
2669
- });
2670
- it('allows to override "_getTextboxValueFromOption" and clears modelValue and textbox value on clear()', async () => {
2671
- class X extends LionCombobox {
2672
- /**
2673
- * Return the value to be used for the input value
2674
- * @overridable
2675
- * @param {any} option
2676
- * @returns {string}
2677
- */
2678
- // eslint-disable-next-line class-methods-use-this
2679
- _getTextboxValueFromOption(option) {
2680
- if (option && option.label) {
2681
- return option.label;
2682
- }
2683
- return this.modelValue;
2684
- }
2685
- }
2686
- const tagName = defineCE(X);
2687
- const tag = unsafeStatic(tagName);
2688
- const el =
2689
- /** @type {LionCombobox} */
2690
- await fixture(html`
2691
- <${tag} name="foo" .modelValue="${{
2692
- value: 'Artichoke',
2693
- }}">
2694
- <lion-option .label="${'Artichoke as label'}" .choiceValue="${{
2695
- value: 'Artichoke',
2696
- }}">Artichoke</lion-option>
2697
- <lion-option .label="${'Chard as label'}" .choiceValue="${{
2698
- value: 'Chard',
2699
- }}">Chard</lion-option>
2700
- <lion-option .label="${'Chicory as label'}" .choiceValue="${{
2701
- value: 'Chicory',
2702
- }}">Chicory</lion-option>
2703
- <lion-option .label="${'Victoria Plum as label'}" .choiceValue="${{
2704
- value: 'Victoria Plum',
2705
- }}">Victoria Plum</lion-option>
2706
- </${tag}>
2707
- `);
2708
- const { _inputNode } = getComboboxMembers(el);
2709
-
2710
- // Assume
2711
- expect(_inputNode.value).to.equal('Artichoke as label');
2712
-
2713
- // Act
2714
- el.clear();
2715
-
2716
- // Assert
2717
- expect(el.modelValue).to.equal('');
2718
- expect(_inputNode.value).to.equal('');
2719
- });
2720
- it('allows to override "_getTextboxValueFromOption" and syncs textbox to modelValue', async () => {
2721
- class X extends LionCombobox {
2722
- /**
2723
- * Return the value to be used for the input value
2724
- * @overridable
2725
- * @param {any} option
2726
- * @returns {string}
2727
- */
2728
- // eslint-disable-next-line class-methods-use-this
2729
- _getTextboxValueFromOption(option) {
2730
- return option.label;
2731
- }
2732
- }
2733
- const tagName = defineCE(X);
2734
- const tag = unsafeStatic(tagName);
2735
- const el =
2736
- /** @type {LionCombobox} */
2737
- await fixture(html`
2738
- <${tag} name="foo">
2739
- <lion-option .label="${'Artichoke as label'}" .choiceValue="${{
2740
- value: 'Artichoke',
2741
- }}">Artichoke</lion-option>
2742
- <lion-option .label="${'Chard as label'}" .choiceValue="${{
2743
- value: 'Chard',
2744
- }}">Chard</lion-option>
2745
- <lion-option .label="${'Chicory as label'}" .choiceValue="${{
2746
- value: 'Chicory',
2747
- }}">Chicory</lion-option>
2748
- <lion-option .label="${'Victoria Plum as label'}" .choiceValue="${{
2749
- value: 'Victoria Plum',
2750
- }}">Victoria Plum</lion-option>
2751
- </${tag}>
2752
- `);
2753
- const { _inputNode } = getComboboxMembers(el);
2754
-
2755
- /** @param {string} autocompleteMode */
2756
- async function performChecks(autocompleteMode) {
2757
- await el.updateComplete;
2758
- await mimicUserTypingAdvanced(el, ['A', 'r', 't', 'i']);
2759
- await el.updateComplete;
2760
- expect(_inputNode.value).to.equal('Arti', `autocompleteMode is ${autocompleteMode}`);
2761
- await el.updateComplete;
2762
- expect(el.checkedIndex).to.equal(-1, `autocompleteMode is ${autocompleteMode}`);
2763
- _inputNode.value = '';
2764
- }
2765
- el.autocomplete = 'none';
2766
- await performChecks('none');
2767
- el.autocomplete = 'list';
2768
- await performChecks('list');
2769
- });
2770
- });
2771
- it('allows to override "_onFilterMatch" and "_onFilterUmatch"', async () => {
2772
- class X extends LionCombobox {
2773
- /**
2774
- * @overridable
2775
- * @param {LionOption & {__originalInnerHTML?:string}} option
2776
- * @param {string} matchingString
2777
- * @protected
2778
- */
2779
- _onFilterMatch(option, matchingString) {
2780
- Array.from(option.children).forEach((child) => {
2781
- if (child.hasAttribute('data-key')) {
2782
- this._highlightMatchedOption(child, matchingString);
2783
- }
2784
- });
2785
- // Alternatively, an extension can add an animation here
2786
- // option.style.display = '';
2787
- }
2788
-
2789
- /**
2790
- * @overridable
2791
- * @param {LionOption & {__originalInnerHTML?:string}} option
2792
- * @param {string} [curValue]
2793
- * @param {string} [prevValue]
2794
- * @protected
2795
- */
2796
- // eslint-disable-next-line no-unused-vars
2797
- _onFilterUnmatch(option, curValue, prevValue) {
2798
- Array.from(option.children).forEach((child) => {
2799
- if (child.hasAttribute('data-key')) {
2800
- this._unhighlightMatchedOption(child);
2801
- }
2802
- });
2803
- // Alternatively, an extension can add an animation here
2804
- // option.style.display = 'none';
2805
- }
2806
- }
2807
- const tagName = defineCE(X);
2808
- const tag = unsafeStatic(tagName);
2809
- const el =
2810
- /** @type {LionCombobox} */
2811
- await fixture(html`
2812
- <${tag} name="foo" autocomplete="both">
2813
- <lion-option .choiceValue="${'Artichoke'}">
2814
- <div data-key>Artichoke</div>
2815
- <small>Cardoon</small>
2816
- </lion-option>
2817
- <lion-option .choiceValue="${'Chard'}">
2818
- <div data-key>Chard</div>
2819
- <small>Beet</small>
2820
- </lion-option>
2821
- <lion-option .choiceValue="${'Chicory'}">
2822
- <div data-key>Chicory</div>
2823
- <small>Chicory</small>
2824
- </lion-option>
2825
- <lion-option .choiceValue="${'Victoria Plum'}">
2826
- <div data-key>Victoria Plum</div>
2827
- <small>Prunus domestica</small>
2828
- </lion-option>
2829
- </${tag}>
2830
- `);
2831
- const options = el.formElements;
2832
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
2833
- await el.updateComplete;
2834
- expect(options[0]).lightDom.to.equal(
2835
- `<div data-key><span aria-label="Artichoke">Arti<b>ch</b>oke</span></div><small>Cardoon</small>`,
2836
- );
2837
- expect(options[1]).lightDom.to.equal(
2838
- `<div data-key><span aria-label="Chard"><b>Ch</b>ard</div></span><small>Beet</small>`,
2839
- );
2840
- expect(options[2]).lightDom.to.equal(
2841
- `<div data-key><span aria-label="Chicory"><b>Ch</b>icory</span></div><small>Chicory</small>`,
2842
- );
2843
- expect(options[3]).lightDom.to.equal(
2844
- `<div data-key>Victoria Plum</div><small>Prunus domestica</small>`,
2845
- );
2846
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'D');
2847
- await el.updateComplete;
2848
- expect(options[0]).lightDom.to.equal(`<div data-key>Artichoke</div><small>Cardoon</small>`);
2849
- expect(options[1]).lightDom.to.equal(
2850
- `<div data-key><span aria-label="Chard">Char<b>d</b></div></span><small>Beet</small>`,
2851
- );
2852
- expect(options[2]).lightDom.to.equal(`<div data-key>Chicory</div><small>Chicory</small>`);
2853
- expect(options[3]).lightDom.to.equal(
2854
- `<div data-key>Victoria Plum</div><small>Prunus domestica</small>`,
2855
- );
2856
- });
2857
- });
2858
- describe('Active index behavior', () => {
2859
- it('sets the active index to the closest match on open by default', async () => {
2860
- const el =
2861
- /** @type {LionCombobox} */
2862
- await fixture(html`
2863
- <lion-combobox name="foo">
2864
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2865
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2866
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2867
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2868
- </lion-combobox>
2869
- `);
2870
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
2871
- await el.updateComplete;
2872
- expect(el.activeIndex).to.equal(1);
2873
- });
2874
- it('changes whether activeIndex is set to the closest match automatically depending on autocomplete', async () => {
2875
- /**
2876
- * Automatic selection (setting activeIndex to closest matching option) in lion is set for
2877
- * 'inline' & 'both' autocomplete, because it is unavoidable there
2878
- * For 'list' & 'none' autocomplete, it is turned off and manual selection is required.
2879
- * TODO: Make this configurable for list & none autocomplete?
2880
- */
2881
- const el =
2882
- /** @type {LionCombobox} */
2883
- await fixture(html`
2884
- <lion-combobox name="foo" autocomplete="none">
2885
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2886
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2887
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2888
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2889
- </lion-combobox>
2890
- `);
2891
- const { _inputNode } = getComboboxMembers(el);
2892
-
2893
- /**
2894
- * @param {LionCombobox} elm
2895
- * @param {'none'|'list'|'inline'|'both'} autocomplete
2896
- */
2897
- async function setup(elm, autocomplete) {
2898
- // eslint-disable-next-line no-param-reassign
2899
- elm.autocomplete = autocomplete;
2900
- // eslint-disable-next-line no-param-reassign
2901
- elm.activeIndex = -1;
2902
- // eslint-disable-next-line no-param-reassign
2903
- elm.setCheckedIndex(-1);
2904
- // eslint-disable-next-line no-param-reassign
2905
- elm.opened = true;
2906
- await elm.updateComplete;
2907
- }
2908
-
2909
- // https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
2910
- // Example 1. List Autocomplete with Manual Selection:
2911
- // does not set active at all until user selects
2912
- await setup(el, 'none');
2913
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
2914
- await el.updateComplete;
2915
- expect(el.activeIndex).to.equal(-1);
2916
- expect(el.opened).to.be.true;
2917
- mimicKeyPress(_inputNode, 'Enter');
2918
- expect(el.opened).to.be.false;
2919
- expect(el.activeIndex).to.equal(-1);
2920
-
2921
- // https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
2922
- // Example 2. List Autocomplete with Automatic Selection:
2923
- // does not set active at all until user selects
2924
- await setup(el, 'list');
2925
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
2926
- await el.updateComplete;
2927
- expect(el.opened).to.be.true;
2928
- expect(el.activeIndex).to.equal(-1);
2929
- mimicKeyPress(_inputNode, 'Enter');
2930
- expect(el.activeIndex).to.equal(-1);
2931
- expect(el.opened).to.be.false;
2932
-
2933
- // https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
2934
- // Example 3. List with Inline Autocomplete (mostly, but with aria-autocomplete="inline")
2935
- await setup(el, 'inline');
2936
- await mimicUserTyping(/** @type {LionCombobox} */ el, '');
2937
- await el.updateComplete;
2938
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
2939
- await el.updateComplete;
2940
- await el.updateComplete;
2941
- expect(el.opened).to.be.true;
2942
- expect(el.activeIndex).to.equal(1);
2943
- mimicKeyPress(_inputNode, 'Enter');
2944
- await el.updateComplete;
2945
- await el.updateComplete;
2946
- expect(el.activeIndex).to.equal(-1);
2947
- expect(el.opened).to.be.false;
2948
-
2949
- // https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
2950
- // Example 3. List with Inline Autocomplete
2951
- await setup(el, 'both');
2952
- await mimicUserTyping(/** @type {LionCombobox} */ el, '');
2953
- await el.updateComplete;
2954
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
2955
- await el.updateComplete;
2956
- mimicKeyPress(_inputNode, 'Enter');
2957
- expect(el.activeIndex).to.equal(1);
2958
- expect(el.opened).to.be.false;
2959
- });
2960
- it('sets the active index to the closest match on autocomplete', async () => {
2961
- const el =
2962
- /** @type {LionCombobox} */
2963
- await fixture(html`
2964
- <lion-combobox name="foo" autocomplete="both">
2965
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2966
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
2967
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
2968
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
2969
- </lion-combobox>
2970
- `);
2971
- const { _inputNode } = getComboboxMembers(el);
2972
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
2973
- await el.updateComplete;
2974
- expect(el.activeIndex).to.equal(1);
2975
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
2976
- await el.updateComplete;
2977
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'chi');
2978
- // Chard no longer matches, so should switch active to Chicory
2979
- await el.updateComplete;
2980
- expect(el.activeIndex).to.equal(2);
2981
-
2982
- // select artichoke
2983
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'artichoke');
2984
- await el.updateComplete;
2985
- mimicKeyPress(_inputNode, 'Enter');
2986
- await mimicUserTyping(/** @type {LionCombobox} */ el, '');
2987
- await el.updateComplete;
2988
- // change selection, active index should update to closest match
2989
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'vic');
2990
- await el.updateComplete;
2991
- expect(el.activeIndex).to.equal(3);
2992
- });
2993
- it('supports clearing by [Escape] key and resets active state on all options', async () => {
2994
- const el =
2995
- /** @type {LionCombobox} */
2996
- await fixture(html`
2997
- <lion-combobox name="foo">
2998
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
2999
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3000
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3001
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3002
- </lion-combobox>
3003
- `);
3004
- const { _inputNode } = getComboboxMembers(el);
3005
-
3006
- // Select something
3007
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'cha');
3008
- await el.updateComplete;
3009
- mimicKeyPress(_inputNode, 'Enter');
3010
- expect(el.activeIndex).to.equal(1);
3011
- mimicKeyPress(_inputNode, 'Escape');
3012
- await el.updateComplete;
3013
- expect(_inputNode.textContent).to.equal('');
3014
- el.formElements.forEach((option) => expect(option.active).to.be.false);
3015
-
3016
- // change selection, active index should update to closest match
3017
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'vic');
3018
- await el.updateComplete;
3019
- expect(el.activeIndex).to.equal(3);
3020
- });
3021
- });
3022
- describe('Accessibility', () => {
3023
- it('synchronizes autocomplete option to textbox', async () => {
3024
- let el;
3025
- [el] = await fruitFixture({
3026
- autocomplete: 'both',
3027
- });
3028
- expect(
3029
- getComboboxMembers(/** @type {LionCombobox} */ el)._inputNode.getAttribute(
3030
- 'aria-autocomplete',
3031
- ),
3032
- ).to.equal('both');
3033
- [el] = await fruitFixture({
3034
- autocomplete: 'list',
3035
- });
3036
- expect(
3037
- getComboboxMembers(/** @type {LionCombobox} */ el)._inputNode.getAttribute(
3038
- 'aria-autocomplete',
3039
- ),
3040
- ).to.equal('list');
3041
- [el] = await fruitFixture({
3042
- autocomplete: 'none',
3043
- });
3044
- expect(
3045
- getComboboxMembers(/** @type {LionCombobox} */ el)._inputNode.getAttribute(
3046
- 'aria-autocomplete',
3047
- ),
3048
- ).to.equal('none');
3049
- });
3050
- it('updates aria-activedescendant on textbox node', async () => {
3051
- const el =
3052
- /** @type {LionCombobox} */
3053
- await fixture(html`
3054
- <lion-combobox name="foo" autocomplete="none">
3055
- <lion-option .choiceValue="${'Artichoke'}" id="artichoke-option"
3056
- >Artichoke</lion-option
3057
- >
3058
- <lion-option .choiceValue="${'Chard'}" id="chard-option">Chard</lion-option>
3059
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3060
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3061
- </lion-combobox>
3062
- `);
3063
- const elProts = getComboboxMembers(el);
3064
- expect(elProts._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(
3065
- null,
3066
- );
3067
- expect(el.formElements[1].active).to.equal(false);
3068
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
3069
- await el.updateComplete;
3070
- expect(elProts._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(
3071
- null,
3072
- );
3073
- // el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
3074
- mimicKeyPress(elProts._inputNode, 'ArrowDown');
3075
- expect(elProts._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(
3076
- 'artichoke-option',
3077
- );
3078
- expect(el.formElements[1].active).to.equal(false);
3079
- const el2 =
3080
- /** @type {LionCombobox} */
3081
- await fixture(html`
3082
- <lion-combobox name="foo" autocomplete="both" match-mode="begin">
3083
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3084
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3085
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3086
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3087
- </lion-combobox>
3088
- `);
3089
- const el2Prots = getComboboxMembers(el2);
3090
- await mimicUserTyping(/** @type {LionCombobox} */ el2, 'ch');
3091
- await el2.updateComplete;
3092
- expect(el2Prots._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(
3093
- el2.formElements[1].id,
3094
- );
3095
- expect(el2.formElements[1].active).to.equal(true);
3096
- el2.autocomplete = 'list';
3097
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
3098
- await el2.updateComplete;
3099
- expect(el2Prots._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(
3100
- el2.formElements[1].id,
3101
- );
3102
- expect(el2.formElements[1].active).to.equal(true);
3103
- });
3104
- it('adds aria-label to highlighted options', async () => {
3105
- const [el, options] = await fruitFixture({
3106
- autocomplete: 'both',
3107
- matchMode: 'all',
3108
- });
3109
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'choke');
3110
- await el.updateComplete;
3111
- // @ts-ignore [dynamic-key-access-in-test]
3112
- const labelledElement = options[0].querySelector('span[aria-label="Artichoke"]');
3113
- expect(labelledElement).to.not.be.null;
3114
- expect(labelledElement.innerText).to.equal('Artichoke');
3115
- });
3116
- it('adds aria-label to highlighted complex options', async () => {
3117
- const el =
3118
- /** @type {LionCombobox} */
3119
- await fixture(html`
3120
- <lion-combobox name="foo" match-mode="all">
3121
- <lion-option .choiceValue="${'Artichoke'}">
3122
- <div>Artichoke</div>
3123
- <small>Cardoon</small>
3124
- </lion-option>
3125
- </lion-combobox>
3126
- `);
3127
- const options = el.formElements;
3128
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'choke');
3129
- await el.updateComplete;
3130
- const labelledElement = options[0].querySelector('span[aria-label=" Artichoke Cardoon "]');
3131
- expect(labelledElement).to.not.be.null;
3132
- expect(labelledElement.innerText).to.equal('Artichoke\nCardoon');
3133
- });
3134
- });
3135
- });
3136
- describe('Accessibility', () => {
3137
- describe('Aria versions', () => {
3138
- it('[role=combobox] wraps input node in v1.1', async () => {
3139
- const el =
3140
- /** @type {LionCombobox} */
3141
- await fixture(html`
3142
- <lion-combobox name="foo" ._ariaVersion="${'1.1'}">
3143
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
3144
- </lion-combobox>
3145
- `);
3146
- const { _comboboxNode, _inputNode } = getComboboxMembers(el);
3147
- expect(_comboboxNode.contains(_inputNode)).to.be.true;
3148
- });
3149
- it('has one input node with [role=combobox] in v1.0', async () => {
3150
- const el =
3151
- /** @type {LionCombobox} */
3152
- await fixture(html`
3153
- <lion-combobox name="foo" ._ariaVersion="${'1.0'}">
3154
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
3155
- </lion-combobox>
3156
- `);
3157
- const { _comboboxNode, _inputNode } = getComboboxMembers(el);
3158
- expect(_comboboxNode).to.equal(_inputNode);
3159
- });
3160
- it('autodetects aria version and sets it to 1.1 on Chromium browsers', async () => {
3161
- const browserDetectionIsChromiumOriginal = browserDetection.isChromium;
3162
- browserDetection.isChromium = true;
3163
- const el =
3164
- /** @type {LionCombobox} */
3165
- await fixture(html`
3166
- <lion-combobox name="foo">
3167
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
3168
- </lion-combobox>
3169
- `);
3170
- const elProts = getComboboxMembers(el);
3171
- expect(elProts._ariaVersion).to.equal('1.1');
3172
- browserDetection.isChromium = false;
3173
- const el2 =
3174
- /** @type {LionCombobox} */
3175
- await fixture(html`
3176
- <lion-combobox name="foo">
3177
- <lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
3178
- </lion-combobox>
3179
- `);
3180
- const el2Prots = getComboboxMembers(el2);
3181
- expect(el2Prots._ariaVersion).to.equal('1.0');
3182
-
3183
- // restore...
3184
- browserDetection.isChromium = browserDetectionIsChromiumOriginal;
3185
- });
3186
- });
3187
- });
3188
- describe('Multiple Choice', () => {
3189
- // TODO: possibly later share test with select-rich if it officially supports multipleChoice
3190
- it('does not close listbox on click', async () => {
3191
- const el =
3192
- /** @type {LionCombobox} */
3193
- await fixture(html`
3194
- <lion-combobox name="foo" multiple-choice>
3195
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3196
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3197
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3198
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3199
- </lion-combobox>
3200
- `);
3201
-
3202
- // activate opened listbox
3203
- await mimicUserTyping(el, 'ch');
3204
- await el.updateComplete;
3205
- expect(el.opened).to.equal(true);
3206
- const visibleOptions = el.formElements.filter((o) => o.style.display !== 'none');
3207
- visibleOptions[0].click();
3208
- expect(el.opened).to.equal(true);
3209
- });
3210
- it('does not close listbox on [Enter]', async () => {
3211
- const el =
3212
- /** @type {LionCombobox} */
3213
- await fixture(html`
3214
- <lion-combobox name="foo" multiple-choice>
3215
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3216
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3217
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3218
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3219
- </lion-combobox>
3220
- `);
3221
- const { _inputNode } = getComboboxMembers(el);
3222
- await mimicUserTyping(el, 'art');
3223
- await el.updateComplete;
3224
- expect(el.opened).to.equal(true);
3225
-
3226
- // N.B. we do only trigger keydown here (and not mimicKeypress (both keyup and down)),
3227
- // because this closely mimics what happens in the browser
3228
- _inputNode.dispatchEvent(
3229
- new KeyboardEvent('keydown', {
3230
- key: 'Enter',
3231
- }),
3232
- );
3233
- expect(el.opened).to.equal(true);
3234
- });
3235
- it('clears textbox after selection of a new item on [enter]', async () => {
3236
- const el =
3237
- /** @type {LionCombobox} */
3238
- await fixture(html`
3239
- <lion-combobox name="foo" multiple-choice>
3240
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3241
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3242
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3243
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3244
- </lion-combobox>
3245
- `);
3246
- const { _inputNode } = getComboboxMembers(el);
3247
- const options = el.formElements;
3248
- await mimicUserTyping(el, 'art');
3249
- await el.updateComplete;
3250
- expect(el.opened).to.equal(true);
3251
- const visibleOptions = () => options.filter((o) => o.style.display !== 'none');
3252
- expect(visibleOptions().length).to.equal(1);
3253
-
3254
- // N.B. we do only trigger keydown here (and not mimicKeypress (both keyup and down)),
3255
- // because this closely mimics what happens in the browser
3256
- _inputNode.dispatchEvent(
3257
- new KeyboardEvent('keydown', {
3258
- key: 'Enter',
3259
- }),
3260
- );
3261
- expect(el.opened).to.equal(true);
3262
- expect(visibleOptions().length).to.equal(0);
3263
- expect(_inputNode.value).to.equal('');
3264
- });
3265
- it('clears textbox after selection of a new item on click', async () => {
3266
- const el =
3267
- /** @type {LionCombobox} */
3268
- await fixture(html`
3269
- <lion-combobox name="foo" multiple-choice>
3270
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3271
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3272
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3273
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3274
- </lion-combobox>
3275
- `);
3276
- const { _inputNode } = getComboboxMembers(el);
3277
- const options = el.formElements;
3278
- await mimicUserTyping(el, 'art');
3279
- await el.updateComplete;
3280
- expect(el.opened).to.equal(true);
3281
- const visibleOptions = () => options.filter((o) => o.style.display !== 'none');
3282
- expect(visibleOptions().length).to.equal(1);
3283
- visibleOptions()[0].click();
3284
- expect(el.opened).to.equal(true);
3285
- expect(visibleOptions().length).to.equal(0);
3286
- expect(_inputNode.value).to.equal('');
3287
- });
3288
- it('submits form on [Enter] when listbox is closed', async () => {
3289
- const submitSpy = sinon.spy((e) => e.preventDefault());
3290
- const el =
3291
- /** @type {HTMLFormElement} */
3292
- await fixture(html`
3293
- <form @submit=${submitSpy}>
3294
- <lion-combobox name="foo" multiple-choice>
3295
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3296
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3297
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3298
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3299
- </lion-combobox>
3300
- <button type="submit">submit</button>
3301
- </form>
3302
- `);
3303
- const combobox = /** @type {LionCombobox} */ el.querySelector('[name="foo"]');
3304
- const { _inputNode } = getComboboxMembers(combobox);
3305
- await combobox.updateComplete;
3306
- _inputNode.focus();
3307
- await sendKeys({
3308
- press: 'Enter',
3309
- });
3310
- expect(submitSpy.callCount).to.equal(1);
3311
- });
3312
- it('does not submit form on [Enter] when listbox is opened', async () => {
3313
- const submitSpy = sinon.spy((e) => e.preventDefault());
3314
- const el =
3315
- /** @type {HTMLFormElement} */
3316
- await fixture(html`
3317
- <form @submit=${submitSpy}>
3318
- <lion-combobox name="foo" multiple-choice>
3319
- <lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
3320
- <lion-option .choiceValue="${'Chard'}">Chard</lion-option>
3321
- <lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
3322
- <lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
3323
- </lion-combobox>
3324
- <button type="submit">submit</button>
3325
- </form>
3326
- `);
3327
- const combobox = /** @type {LionCombobox} */ el.querySelector('[name="foo"]');
3328
- const { _inputNode } = getComboboxMembers(combobox);
3329
- combobox.opened = true;
3330
- await combobox.updateComplete;
3331
- _inputNode.focus();
3332
- await sendKeys({
3333
- press: 'Enter',
3334
- });
3335
- expect(submitSpy.callCount).to.equal(0);
3336
- });
3337
- });
3338
- describe('Match Mode', () => {
3339
- it('has a default value of "all"', async () => {
3340
- const [el] = await fruitFixture();
3341
- expect(el.matchMode).to.equal('all');
3342
- });
3343
- it('will suggest partial matches (in the middle of the word) when set to "all"', async () => {
3344
- const [el] = await fruitFixture();
3345
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'c');
3346
- await el.updateComplete;
3347
- expect(getFilteredOptionValues(/** @type {LionCombobox} */ el)).to.eql([
3348
- 'Artichoke',
3349
- 'Chard',
3350
- 'Chicory',
3351
- 'Victoria Plum',
3352
- ]);
3353
- });
3354
- it('will only suggest beginning matches when set to "begin"', async () => {
3355
- const [el] = await fruitFixture({
3356
- matchMode: 'begin',
3357
- });
3358
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
3359
- await el.updateComplete;
3360
- expect(getFilteredOptionValues(/** @type {LionCombobox} */ el)).to.eql(['Chard', 'Chicory']);
3361
- });
3362
- describe('Subclassers', () => {
3363
- it('allows for custom matching functions', async () => {
3364
- const [el] = await fruitFixture();
3365
- /**
3366
- * @param {{ value: any; }} option
3367
- * @param {any} curValue
3368
- */
3369
- function onlyExactMatches(option, curValue) {
3370
- return option.value === curValue;
3371
- }
3372
- el.matchCondition = onlyExactMatches;
3373
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'Chicory');
3374
- await el.updateComplete;
3375
- expect(getFilteredOptionValues(/** @type {LionCombobox} */ el)).to.eql(['Chicory']);
3376
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'Chicor');
3377
- await el.updateComplete;
3378
- expect(getFilteredOptionValues(/** @type {LionCombobox} */ el)).to.eql([]);
3379
- });
3380
- });
3381
- });
3382
- describe('Disabled state', () => {
3383
- it('does not open overlay or allow input when disabled', async () => {
3384
- const [el] = await fruitFixture({
3385
- disabled: true,
3386
- });
3387
- expect(el.disabled).to.equal(true);
3388
- const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ el);
3389
- expect(el.disabled).to.be.true;
3390
- expect(_inputNode.disabled).to.be.true;
3391
-
3392
- // Try to open overlay by clicking input
3393
- _inputNode.dispatchEvent(
3394
- new Event('click', {
3395
- bubbles: true,
3396
- composed: true,
3397
- }),
3398
- );
3399
- await el.updateComplete;
3400
- expect(el.opened).to.be.false;
3401
- });
3402
- it('does open overlay or allow input when disabled state is removed after it was previously disabled', async () => {
3403
- const [el] = await fruitFixture({
3404
- disabled: true,
3405
- });
3406
- expect(el.disabled).to.equal(true);
3407
- const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ el);
3408
- expect(el.disabled).to.be.true;
3409
- expect(_inputNode.disabled).to.be.true;
3410
-
3411
- // Try to open overlay by clicking input
3412
- _inputNode.dispatchEvent(
3413
- new Event('click', {
3414
- bubbles: true,
3415
- composed: true,
3416
- }),
3417
- );
3418
- await el.updateComplete;
3419
- expect(el.opened).to.be.false;
3420
- el.disabled = false;
3421
- await el.updateComplete;
3422
- expect(el.disabled).to.be.false;
3423
- expect(_inputNode.disabled).to.be.false;
3424
-
3425
- // Try to open overlay by clicking input
3426
- async function open() {
3427
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
3428
- return el.updateComplete;
3429
- }
3430
- await open();
3431
- expect(el.opened).to.be.true;
3432
- });
3433
- it('sets aria-disabled and disables focus when combobox is disabled', async () => {
3434
- const [el] = await fruitFixture({
3435
- disabled: true,
3436
- });
3437
- const { _comboboxNode, _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ el);
3438
-
3439
- // input should not be focusable
3440
- expect(_comboboxNode.tabIndex).to.equal(-1);
3441
- expect(_inputNode.tabIndex).to.equal(-1);
3442
-
3443
- // aria-disabled should be set
3444
- expect(el.getAttribute('aria-disabled')).to.equal('true');
3445
-
3446
- // aria-disabled should be set
3447
- expect(_inputNode.getAttribute('aria-disabled')).to.equal('true');
3448
- await expect(el).to.be.accessible();
3449
- });
3450
- it('ensure focus when combobox is readonly', async () => {
3451
- const [el] = await fruitFixture({
3452
- readonly: true,
3453
- });
3454
- const { _comboboxNode, _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ el);
3455
-
3456
- // input should be focusable
3457
- expect(_inputNode.tabIndex).to.equal(0);
3458
-
3459
- // aria-disabled should be set
3460
- expect(_comboboxNode.getAttribute('aria-readonly')).to.equal('true');
3461
-
3462
- // aria-disabled should be set
3463
- expect(_inputNode.getAttribute('aria-readonly')).to.equal('true');
3464
- await expect(el).to.be.accessible();
3465
- });
3466
- });
3467
- describe('Readonly state', () => {
3468
- it('does not open overlay or allow input when readonly', async () => {
3469
- const [el] = await fruitFixture({
3470
- readonly: true,
3471
- });
3472
- const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ el);
3473
- expect(el.readOnly).to.equal(true);
3474
- expect(el.readOnly).to.be.true;
3475
- expect(_inputNode.readOnly).to.be.true;
3476
-
3477
- // Try to open overlay by clicking input
3478
- _inputNode.dispatchEvent(
3479
- new Event('click', {
3480
- bubbles: true,
3481
- composed: true,
3482
- }),
3483
- );
3484
- await el.updateComplete;
3485
- expect(el.opened).to.be.false;
3486
- });
3487
- it('does open overlay or allow input when readonly state is removed after it was previously set', async () => {
3488
- const [el] = await fruitFixture({
3489
- readonly: true,
3490
- });
3491
- expect(el.readOnly).to.equal(true);
3492
- const { _inputNode } = getComboboxMembers(/** @type {LionCombobox} */ el);
3493
- expect(el.readOnly).to.be.true;
3494
- expect(_inputNode.readOnly).to.be.true;
3495
-
3496
- // Try to open overlay by clicking input
3497
- _inputNode.dispatchEvent(
3498
- new Event('click', {
3499
- bubbles: true,
3500
- composed: true,
3501
- }),
3502
- );
3503
- await el.updateComplete;
3504
- expect(el.opened).to.be.false;
3505
- el.readOnly = false;
3506
- await el.updateComplete;
3507
- expect(el.readOnly).to.be.false;
3508
- expect(_inputNode.readOnly).to.be.false;
3509
-
3510
- // Try to open overlay by clicking input
3511
- async function open() {
3512
- await mimicUserTyping(/** @type {LionCombobox} */ el, 'ch');
3513
- return el.updateComplete;
3514
- }
3515
- await open();
3516
- expect(el.opened).to.be.true;
3517
- });
3518
- });
3519
- describe('Special Characters in Search', () => {
3520
- it('correctly escapes all regex special characters in search queries', async () => {
3521
- const el =
3522
- /** @type {LionCombobox} */
3523
- await fixture(html`
3524
- <lion-combobox label="Search" name="foo">
3525
- <lion-option .choiceValue="${'test.com'}">test.com</lion-option>
3526
- <lion-option .choiceValue="${'func(arg)'}">func(arg)</lion-option>
3527
- <lion-option .choiceValue="${'array[0]'}">array[0]</lion-option>
3528
- <lion-option .choiceValue="${'C++'}">C++</lion-option>
3529
- <lion-option .choiceValue="${'a * b'}">a * b</lion-option>
3530
- <lion-option .choiceValue="${'What?'}">What?</lion-option>
3531
- <lion-option .choiceValue="${'$100'}">$100</lion-option>
3532
- <lion-option .choiceValue="${'x^2'}">x^2</lion-option>
3533
- <lion-option .choiceValue="${'a | b'}">a | b</lion-option>
3534
- <lion-option .choiceValue="${'path\\file'}">path\\file</lion-option>
3535
- <lion-option .choiceValue="${'{name}'}">{name}</lion-option>
3536
- <lion-option .choiceValue="${'user+tag@example.com'}">user+tag@example.com</lion-option>
3537
- <lion-option .choiceValue="${'(a+b)*c^2$'}">>(a+b)*c^2$</lion-option>
3538
- <lion-option .choiceValue="${'test|value?[x]'}">test|value?[x]</lion-option>
3539
- <lion-option .choiceValue="${'file.{txt|md}\\n'}">file.{txt|md}\\n</lion-option>
3540
- <lion-option .choiceValue="${'testXcom'}">testXcom</lion-option>
3541
- <lion-option .choiceValue="${'funcXargX'}">funcXargX</lion-option>
3542
- <lion-option .choiceValue="${'arrayX0X'}">arrayX0X</lion-option>
3543
- </lion-combobox>
3544
- `);
3545
-
3546
- // Test dot - should not act as wildcard
3547
- await mimicUserTyping(el, 'test.com');
3548
- await el.updateComplete;
3549
- expect(getFilteredOptionValues(el)).to.eql(['test.com']);
3550
-
3551
- // Test parentheses
3552
- await mimicUserTyping(el, '(arg)');
3553
- await el.updateComplete;
3554
- expect(getFilteredOptionValues(el)).to.eql(['func(arg)']);
3555
-
3556
- // Test square brackets
3557
- await mimicUserTyping(el, '[0]');
3558
- await el.updateComplete;
3559
- expect(getFilteredOptionValues(el)).to.eql(['array[0]']);
3560
-
3561
- // Test plus sign
3562
- await mimicUserTyping(el, '+');
3563
- await el.updateComplete;
3564
- expect(getFilteredOptionValues(el)).to.eql(['C++', 'user+tag@example.com', '(a+b)*c^2$']);
3565
-
3566
- // Test asterisk
3567
- await mimicUserTyping(el, '*');
3568
- await el.updateComplete;
3569
- expect(getFilteredOptionValues(el)).to.eql(['a * b', '(a+b)*c^2$']);
3570
-
3571
- // Test question mark
3572
- await mimicUserTyping(el, '?');
3573
- await el.updateComplete;
3574
- expect(getFilteredOptionValues(el)).to.eql(['What?', 'test|value?[x]']);
3575
-
3576
- // Test dollar sign
3577
- await mimicUserTyping(el, '$');
3578
- await el.updateComplete;
3579
- expect(getFilteredOptionValues(el)).to.eql(['$100', '(a+b)*c^2$']);
3580
-
3581
- // Test caret
3582
- await mimicUserTyping(el, '^2');
3583
- await el.updateComplete;
3584
- expect(getFilteredOptionValues(el)).to.eql(['x^2', '(a+b)*c^2$']);
3585
-
3586
- // Test pipe
3587
- await mimicUserTyping(el, '|');
3588
- await el.updateComplete;
3589
- expect(getFilteredOptionValues(el)).to.eql(['a | b', 'test|value?[x]', 'file.{txt|md}\\n']);
3590
-
3591
- // Test backslash
3592
- await mimicUserTyping(el, '\\');
3593
- await el.updateComplete;
3594
- expect(getFilteredOptionValues(el)).to.eql(['path\\file', 'file.{txt|md}\\n']);
3595
-
3596
- // Test curly braces
3597
- await mimicUserTyping(el, '{name}');
3598
- await el.updateComplete;
3599
- expect(getFilteredOptionValues(el)).to.eql(['{name}']);
3600
-
3601
- // Test complex combination with multiple special characters
3602
- await mimicUserTyping(el, '(a+b)*c');
3603
- await el.updateComplete;
3604
- expect(getFilteredOptionValues(el)).to.eql(['(a+b)*c^2$']);
3605
-
3606
- // Test email with plus and dot
3607
- await mimicUserTyping(el, 'user+tag@example.com');
3608
- await el.updateComplete;
3609
- expect(getFilteredOptionValues(el)).to.eql(['user+tag@example.com']);
3610
-
3611
- // Test combination with brackets, pipe and question mark
3612
- await mimicUserTyping(el, 'value?[x]');
3613
- await el.updateComplete;
3614
- expect(getFilteredOptionValues(el)).to.eql(['test|value?[x]']);
3615
-
3616
- // Test combination with dot, curly braces, pipe and backslash
3617
- await mimicUserTyping(el, '.{txt|md}');
3618
- await el.updateComplete;
3619
- expect(getFilteredOptionValues(el)).to.eql(['file.{txt|md}\\n']);
3620
- });
3621
- });
3622
- });
1
+ import { runComboboxSuite } from '../test-suites/lion-combobox.suite.js';
2
+ runComboboxSuite();