@inglorious/ui 7.2.0 → 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/ui",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "description": "A design system for Inglorious Web with render-function components and customizable themes",
5
5
  "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -39,6 +39,13 @@
39
39
  background: var(--iw-input-bg-hover);
40
40
  }
41
41
 
42
+ /* The control is focusable so it can be opened from the keyboard. */
43
+ &:focus-visible {
44
+ outline: none;
45
+ border-color: var(--iw-input-border-focus);
46
+ box-shadow: var(--iw-input-shadow-focus);
47
+ }
48
+
42
49
  &.iw-combobox-control-open {
43
50
  border-color: var(--iw-input-border-focus);
44
51
  box-shadow: var(--iw-input-shadow-focus);
@@ -116,11 +116,18 @@ export const Combobox = {
116
116
  /**
117
117
  * Renders the clickable control element that shows the current selection and toggles the dropdown.
118
118
  * Handles placeholder, clear button, and the open/disabled state.
119
+ *
120
+ * The control is the only way into a closed combobox, so it has to be
121
+ * focusable: it was a plain div with a click handler, which meant tabbing
122
+ * skipped the field entirely and none of the keyboard handling was reachable.
123
+ * It keeps focus while the dropdown is open unless a search input takes it.
119
124
  * @param {ComboboxProps} props
120
125
  * @returns {TemplateResult}
121
126
  */
122
127
  renderControl(props) {
128
+ const controlId = props.id ? `${props.id}-control` : undefined
123
129
  return html`<div
130
+ id=${controlId ?? ""}
124
131
  class="iw-combobox-control ${classMap({
125
132
  "iw-combobox-control-open": props.isOpen,
126
133
  "iw-combobox-control-disabled": props.isDisabled,
@@ -129,6 +136,12 @@ export const Combobox = {
129
136
  Array.isArray(props.selectedValue) &&
130
137
  props.selectedValue.length,
131
138
  })}"
139
+ role="combobox"
140
+ aria-haspopup="listbox"
141
+ aria-expanded=${props.isOpen ? "true" : "false"}
142
+ aria-controls=${controlId ? `${controlId}-listbox` : ""}
143
+ tabindex=${props.isDisabled ? -1 : 0}
144
+ @keydown=${(event) => handleControlKeyDown(props, event)}
132
145
  @click=${() => !props.isDisabled && props.onToggle?.()}
133
146
  >
134
147
  ${when(
@@ -247,7 +260,9 @@ export const Combobox = {
247
260
  const filteredOptions = filterOptions(props.options, props.searchTerm)
248
261
 
249
262
  return html`<div
263
+ id=${props.id ? `${props.id}-control-listbox` : ""}
250
264
  class="iw-combobox-dropdown"
265
+ role="listbox"
251
266
  ${ref((el) => trackOutsideClick(props, el))}
252
267
  >
253
268
  ${when(props.isSearchable, () => this.renderSearchInput?.(props))}
@@ -316,6 +331,9 @@ export const Combobox = {
316
331
  const isFocused = index === props.focusedIndex
317
332
 
318
333
  return html`<div
334
+ id=${props.id ? `${props.id}-option-${index}` : ""}
335
+ role="option"
336
+ aria-selected=${isSelected ? "true" : "false"}
319
337
  class="iw-combobox-dropdown-options-option ${classMap({
320
338
  "iw-combobox-dropdown-options-option-selected": isSelected,
321
339
  "iw-combobox-dropdown-options-option-focused": isFocused,
@@ -364,6 +382,31 @@ export const Combobox = {
364
382
  },
365
383
  }
366
384
 
385
+ /**
386
+ * Opens a closed combobox from the keyboard, and navigates an open one.
387
+ *
388
+ * The control keeps DOM focus whenever the combobox is not searchable — there
389
+ * is no search input to hand focus to — so while open it drives the same
390
+ * navigation handleKeyDown() gives the search input, rather than toggling.
391
+ * @param {ComboboxProps} props
392
+ * @param {KeyboardEvent} event
393
+ */
394
+ function handleControlKeyDown(props, event) {
395
+ if (props.isDisabled) return
396
+
397
+ if (props.isOpen) {
398
+ handleKeyDown(props, event)
399
+ return
400
+ }
401
+
402
+ if (event.key !== "Enter" && event.key !== " " && event.key !== "ArrowDown") {
403
+ return
404
+ }
405
+ // Space would otherwise scroll the page out from under the dropdown.
406
+ event.preventDefault()
407
+ props.onToggle?.()
408
+ }
409
+
367
410
  /**
368
411
  * Handle key navigation.
369
412
  * @param {ComboboxProps} props