@momentum-design/components 0.92.2 → 0.92.4

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 (38) hide show
  1. package/dist/browser/index.js +304 -297
  2. package/dist/browser/index.js.map +4 -4
  3. package/dist/components/divider/divider.styles.js +1 -0
  4. package/dist/components/menubar/menubar.component.d.ts +2 -1
  5. package/dist/components/menubar/menubar.component.js +23 -2
  6. package/dist/components/menusection/menusection.component.d.ts +9 -0
  7. package/dist/components/menusection/menusection.component.js +15 -1
  8. package/dist/components/menusection/menusection.constants.d.ts +4 -1
  9. package/dist/components/menusection/menusection.constants.js +4 -1
  10. package/dist/components/popover/popover.component.d.ts +1 -1
  11. package/dist/components/popover/popover.component.js +9 -0
  12. package/dist/components/popover/popover.events.d.ts +12 -0
  13. package/dist/components/popover/popover.events.js +16 -0
  14. package/dist/components/popover/popover.utils.js +1 -1
  15. package/dist/components/select/select.component.d.ts +32 -37
  16. package/dist/components/select/select.component.js +132 -148
  17. package/dist/components/select/select.constants.d.ts +2 -1
  18. package/dist/components/select/select.constants.js +2 -1
  19. package/dist/components/select/select.styles.js +20 -17
  20. package/dist/components/select/select.types.d.ts +3 -1
  21. package/dist/components/selectlistbox/index.d.ts +7 -0
  22. package/dist/components/selectlistbox/index.js +4 -0
  23. package/dist/components/selectlistbox/selectlistbox.component.d.ts +18 -0
  24. package/dist/components/selectlistbox/selectlistbox.component.js +24 -0
  25. package/dist/components/selectlistbox/selectlistbox.constants.d.ts +2 -0
  26. package/dist/components/selectlistbox/selectlistbox.constants.js +3 -0
  27. package/dist/components/selectlistbox/selectlistbox.types.d.ts +3 -0
  28. package/dist/components/selectlistbox/selectlistbox.types.js +1 -0
  29. package/dist/custom-elements.json +541 -491
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.js +2 -1
  32. package/dist/react/index.d.ts +3 -2
  33. package/dist/react/index.js +3 -2
  34. package/dist/react/select/index.d.ts +2 -0
  35. package/dist/react/select/index.js +2 -0
  36. package/dist/react/selectlistbox/index.d.ts +15 -0
  37. package/dist/react/selectlistbox/index.js +24 -0
  38. package/package.json +1 -1
@@ -10,6 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  import { html, nothing } from 'lit';
11
11
  import { property, query, queryAssignedElements, state } from 'lit/decorators.js';
12
12
  import { ifDefined } from 'lit/directives/if-defined.js';
13
+ import { live } from 'lit/directives/live.js';
13
14
  import { KEYS } from '../../utils/keys';
14
15
  import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
15
16
  import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
@@ -20,7 +21,7 @@ import { TAG_NAME as OPTION_GROUP_TAG_NAME } from '../optgroup/optgroup.constant
20
21
  import { TAG_NAME as OPTION_TAG_NAME } from '../option/option.constants';
21
22
  import { POPOVER_PLACEMENT } from '../popover/popover.constants';
22
23
  import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
23
- import { ARROW_ICON, TRIGGER_ID } from './select.constants';
24
+ import { ARROW_ICON, LISTBOX_ID, TRIGGER_ID } from './select.constants';
24
25
  import styles from './select.styles';
25
26
  /**
26
27
  * The mdc-select component is a dropdown selection control that allows users to pick an option from a predefined list.
@@ -30,6 +31,8 @@ import styles from './select.styles';
30
31
  *
31
32
  * To set a default option, use the `selected` attribute on the `mdc-option` element.
32
33
  *
34
+ * **Note:** Make sure to add `mdc-selectlistbox` as a child of `mdc-select` and wrap options/optgroup in it to ensure proper accessibility functionality. Read more about it in SelectListBox documentation.
35
+ *
33
36
  * @dependency mdc-button
34
37
  * @dependency mdc-icon
35
38
  * @dependency mdc-popover
@@ -60,23 +63,43 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
60
63
  * @default auto
61
64
  */
62
65
  this.height = 'auto';
66
+ /**
67
+ * The placeholder text which will be shown on the text if provided.
68
+ */
69
+ this.placement = POPOVER_PLACEMENT.BOTTOM_START;
63
70
  /** @internal */
64
71
  this.baseIconName = ARROW_ICON.ARROW_DOWN;
65
72
  /** @internal */
66
73
  this.selectedValue = '';
67
74
  /** @internal */
68
75
  this.displayPopover = false;
69
- /** @internal */
70
- this.activeDescendant = '';
71
76
  }
72
77
  /**
73
- * A helper function which returns a flattened array of all valid options from the assigned slot.
78
+ * Modifies the listbox wrapper to ensure it has the correct attributes
79
+ * and IDs for accessibility.
80
+ *
81
+ * Once [ariaOwnsElements](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/ariaOwnsElements) is supported in browsers,
82
+ * this an be removed and mdc-option can be used directly in the select component with a listbox in a different
83
+ * shadow root and aria-owns attribute to connect them.
84
+ */
85
+ modifyListBoxWrapper() {
86
+ const slottedListBox = this.slottedListboxes[0];
87
+ if (!slottedListBox) {
88
+ return;
89
+ }
90
+ slottedListBox.setAttribute('id', LISTBOX_ID);
91
+ slottedListBox.setAttribute('aria-label', this.dataAriaLabel || '');
92
+ slottedListBox.setAttribute('aria-labelledby', TRIGGER_ID);
93
+ }
94
+ /**
95
+ * A helper function which returns a flattened array of all valid options from within the slotted listbox.
74
96
  * It takes care of the edge cases where the option is either a direct child or a
75
97
  * child of an option group.
76
98
  */
77
99
  getAllValidOptions() {
78
100
  var _a;
79
- return (((_a = this.optionsList) === null || _a === void 0 ? void 0 : _a.map(option => {
101
+ const optionsList = Array.from(((_a = this.slottedListboxes[0]) === null || _a === void 0 ? void 0 : _a.children) || []);
102
+ return ((optionsList === null || optionsList === void 0 ? void 0 : optionsList.map(option => {
80
103
  if (option.tagName.toLowerCase() === OPTION_TAG_NAME) {
81
104
  return option;
82
105
  }
@@ -86,15 +109,6 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
86
109
  return [];
87
110
  }).flat()) || []);
88
111
  }
89
- handlePopoverOpen() {
90
- this.displayPopover = true;
91
- this.baseIconName = ARROW_ICON.ARROW_UP;
92
- this.updateActivedescendant();
93
- }
94
- handlePopoverClose() {
95
- this.displayPopover = false;
96
- this.baseIconName = ARROW_ICON.ARROW_DOWN;
97
- }
98
112
  /**
99
113
  * Updates the tabindex and selected attribute of the options.
100
114
  * If selectedOption is provided, it will be set as the selected option.
@@ -164,11 +178,8 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
164
178
  else {
165
179
  this.inputElement.setCustomValidity('');
166
180
  }
167
- this.setValidity();
168
- }
169
- else {
170
- this.internals.setValidity({});
171
181
  }
182
+ this.setValidity();
172
183
  }
173
184
  /**
174
185
  * @internal
@@ -180,6 +191,7 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
180
191
  this.selectedIcon = null;
181
192
  this.internals.setFormValue(this.selectedValue);
182
193
  this.updateTabIndexForAllOptions();
194
+ this.requestUpdate();
183
195
  }
184
196
  /** @internal */
185
197
  formStateRestoreCallback(state) {
@@ -253,6 +265,18 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
253
265
  break;
254
266
  }
255
267
  }
268
+ handleClickCombobox(event) {
269
+ if (this.disabled || this.softDisabled || this.readonly) {
270
+ return;
271
+ }
272
+ if (this.displayPopover) {
273
+ this.closePopover();
274
+ }
275
+ else {
276
+ this.openPopover();
277
+ }
278
+ event.stopPropagation();
279
+ }
256
280
  /**
257
281
  * Handles the keydown event on the select element when the popover is closed.
258
282
  * The options are as follows:
@@ -262,15 +286,23 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
262
286
  * - END: Opens the popover and sets focus and tabindex on the last option.
263
287
  * @param event - The keyboard event.
264
288
  */
265
- handlePopoverOnClose(event) {
289
+ handleKeydownCombobox(event) {
290
+ if (this.disabled || this.softDisabled || this.readonly) {
291
+ return;
292
+ }
266
293
  switch (event.key) {
267
294
  case KEYS.ARROW_DOWN:
268
295
  case KEYS.ARROW_UP:
296
+ this.openPopover();
297
+ // Prevent the default browser behavior of scrolling down
298
+ event.preventDefault();
299
+ break;
269
300
  case KEYS.ENTER:
270
301
  case KEYS.SPACE:
271
302
  this.openPopover();
272
303
  // Prevent the default browser behavior of scrolling down
273
304
  event.preventDefault();
305
+ event.stopPropagation();
274
306
  break;
275
307
  case KEYS.HOME:
276
308
  this.openPopover();
@@ -332,24 +364,7 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
332
364
  }
333
365
  return -1;
334
366
  }
335
- updateActivedescendant(target) {
336
- var _a, _b, _c, _d, _e;
337
- const options = this.getAllValidOptions();
338
- if (target) {
339
- const currentIndex = options.findIndex(option => option === target);
340
- this.activeDescendant = (_b = (_a = options[currentIndex]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
341
- }
342
- else {
343
- // If no target is provided, find the option with tabindex="0" or the first option
344
- const focusedOption = options.find(option => option.getAttribute('tabindex') === '0');
345
- this.activeDescendant = (_e = (_c = focusedOption === null || focusedOption === void 0 ? void 0 : focusedOption.id) !== null && _c !== void 0 ? _c : (_d = options[0]) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : '';
346
- }
347
- }
348
- resetActivedescendant() {
349
- this.activeDescendant = '';
350
- }
351
367
  setFocusAndTabIndex(newIndex) {
352
- var _a;
353
368
  const options = this.getAllValidOptions();
354
369
  const targetOption = options[newIndex];
355
370
  if (targetOption) {
@@ -358,30 +373,24 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
358
373
  const newTabindex = newIndex === index ? '0' : '-1';
359
374
  node === null || node === void 0 ? void 0 : node.setAttribute('tabindex', newTabindex);
360
375
  });
361
- // Update activeDescendant after changing focus
362
- this.activeDescendant = (_a = targetOption.id) !== null && _a !== void 0 ? _a : '';
363
376
  }
364
377
  }
365
378
  openPopover() {
366
- var _a, _b;
367
379
  this.displayPopover = true;
368
- // Find the currently selected option or the first option
369
- const options = this.getAllValidOptions();
370
- const selectedOption = options.find(option => option.hasAttribute('selected'));
371
- const focusedOption = options.find(option => option.getAttribute('tabindex') === '0');
372
- // Set activeDescendant to the selected/focused option or first option
373
- this.activeDescendant = (_b = (_a = (selectedOption || focusedOption || options[0])) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
380
+ this.baseIconName = ARROW_ICON.ARROW_UP;
374
381
  }
375
382
  closePopover() {
376
383
  this.displayPopover = false;
377
- this.resetActivedescendant();
384
+ this.baseIconName = ARROW_ICON.ARROW_DOWN;
378
385
  }
379
386
  /**
380
387
  * Handles the first updated lifecycle event.
381
388
  * If an option is selected, use that as the value.
382
389
  * If not, use the placeholder if it exists, otherwise use the first option.
383
390
  */
384
- firstUpdated() {
391
+ async firstUpdated() {
392
+ await this.updateComplete;
393
+ this.modifyListBoxWrapper();
385
394
  const options = this.getAllValidOptions();
386
395
  const selectedOptionIndex = options.findIndex(option => option === null || option === void 0 ? void 0 : option.hasAttribute('selected'));
387
396
  if (selectedOptionIndex !== -1) {
@@ -399,122 +408,75 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
399
408
  this.manageRequired();
400
409
  }
401
410
  }
402
- /**
403
- * Generates the native select element.
404
- * The native select element is not rendered directly and is not visible on the UI.
405
- * It's rendered only on the DOM for accessibility purposes.
406
- * Instead, the overlay uses the native select element to generate the list of options.
407
- * @returns A TemplateResult representing the native select element.
408
- */
409
- getNativeSelect() {
410
- return html `
411
- <select
412
- part="native-select"
413
- id="${this.id}"
414
- tabindex="-1"
415
- aria-hidden="true"
416
- name="${this.name}"
417
- size="1"
418
- .value="${this.selectedValue}"
419
- ?autofocus="${this.autofocus}"
420
- ?disabled="${this.disabled}"
421
- ?required="${this.required}"
422
- @mousedown="${(event) => event.preventDefault()}"
423
- >
424
- ${this.getOptionsContentFromSlot()}
425
- </select>
426
- `;
427
- }
428
- /**
429
- * This method maps over all valid options and constructs their corresponding
430
- * HTML `<option>` elements. The attributes such as `value`, `label`, `disabled`,
431
- * and `selected` are extracted from the respective option elements.
432
- * If the attribute is not present, a default value or fallback is used.
433
- * The content of each `<option>` is set to the text content of the option element.
434
- * @returns An array of `TemplateResult` representing the option elements.
435
- */
436
- getOptionsContentFromSlot() {
437
- return this.getAllValidOptions().map(option => {
438
- var _a, _b;
439
- return html `
440
- <option
441
- part="native-select"
442
- value="${(_a = option.getAttribute('value')) !== null && _a !== void 0 ? _a : ''}"
443
- label="${(_b = option.getAttribute('label')) !== null && _b !== void 0 ? _b : ''}"
444
- ?disabled="${!!option.hasAttribute('disabled')}"
445
- ?selected="${!!option.hasAttribute('selected')}"
446
- >
447
- ${option.textContent}
448
- </option>
449
- `;
450
- });
451
- }
452
- /**
453
- * Generates the content for the popover associated with the select component.
454
- * If the component is disabled or readonly, returns `nothing`.
455
- * Otherwise, returns a `TemplateResult` that renders a popover with various configurations
456
- * such as visibility, interaction, and event handlers.
457
- * The popover acts as a dropdown list with options, allowing user interaction.
458
- */
459
- getPopoverContent() {
460
- if (this.disabled || this.readonly) {
461
- return nothing;
462
- }
463
- return html `
464
- <mdc-popover
465
- id="options-popover"
466
- triggerid="${TRIGGER_ID}"
467
- @keydown="${this.handlePopoverOnOpen}"
468
- interactive
469
- ?visible="${this.displayPopover}"
470
- hide-on-outside-click
471
- hide-on-escape
472
- focus-back-to-trigger
473
- focus-trap
474
- role="${ROLE.LISTBOX}"
475
- placement="${POPOVER_PLACEMENT.BOTTOM_START}"
476
- @shown="${this.handlePopoverOpen}"
477
- @hidden="${this.handlePopoverClose}"
478
- style="--mdc-popover-max-width: 100%; --mdc-popover-max-height: ${this.height};"
479
- >
480
- <slot @click="${this.handleOptionsClick}"></slot>
481
- </mdc-popover>
482
- `;
483
- }
484
411
  updated(changedProperties) {
485
412
  super.updated(changedProperties);
486
- if (changedProperties.has('disabled') || changedProperties.has('readonly')) {
487
- this.closePopover();
488
- this.handlePopoverClose();
489
- }
490
- if (changedProperties.has('displayPopover')) {
491
- if (this.displayPopover) {
492
- this.openPopover();
493
- }
494
- else {
413
+ if (changedProperties.has('disabled') ||
414
+ changedProperties.has('softDisabled') ||
415
+ changedProperties.has('readonly')) {
416
+ if (this.disabled || this.softDisabled || this.readonly) {
495
417
  this.closePopover();
496
418
  }
497
419
  }
420
+ if (changedProperties.has('dataAriaLabel')) {
421
+ this.modifyListBoxWrapper();
422
+ }
423
+ }
424
+ handleOnChange() {
425
+ this.selectedValue = this.inputElement.value;
426
+ this.internals.setFormValue(this.selectedValue);
498
427
  }
499
428
  render() {
500
429
  var _a, _b;
501
430
  return html `
502
431
  ${this.renderLabel()}
503
432
  <div part="container">
433
+ <select
434
+ part="native-select"
435
+ id="${this.id}"
436
+ tabindex="-1"
437
+ aria-hidden="true"
438
+ name="${this.name}"
439
+ size="1"
440
+ .value="${live(this.selectedValue)}"
441
+ ?autofocus="${this.autofocus}"
442
+ ?disabled="${this.disabled}"
443
+ ?required="${this.required}"
444
+ aria-disabled="${ifDefined(this.disabled || this.softDisabled)}"
445
+ @change="${this.handleOnChange}"
446
+ @mousedown="${(event) => event.preventDefault()}"
447
+ >
448
+ ${this.getAllValidOptions().map(option => {
449
+ var _a, _b;
450
+ return html `
451
+ <option
452
+ part="native-select"
453
+ value="${(_a = option.getAttribute('value')) !== null && _a !== void 0 ? _a : ''}"
454
+ label="${(_b = option.getAttribute('label')) !== null && _b !== void 0 ? _b : ''}"
455
+ ?disabled="${!!option.hasAttribute('disabled')}"
456
+ ?selected="${!!option.hasAttribute('selected')}"
457
+ >
458
+ ${option.textContent}
459
+ </option>
460
+ `;
461
+ })}
462
+ </select>
504
463
  <div
505
464
  id="${TRIGGER_ID}"
506
465
  part="base-container"
507
- @keydown="${this.handlePopoverOnClose}"
466
+ @click="${this.handleClickCombobox}"
467
+ @keydown="${this.handleKeydownCombobox}"
508
468
  tabindex="${this.disabled ? '-1' : '0'}"
509
469
  class="${this.disabled ? '' : 'mdc-focus-ring'}"
510
470
  role="${ROLE.COMBOBOX}"
511
- aria-activedescendant="${ifDefined(this.activeDescendant || undefined)}"
512
- aria-controls="${ifDefined(this.displayPopover ? 'options-popover' : undefined)}"
471
+ aria-controls="${LISTBOX_ID}"
513
472
  aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
514
473
  aria-labelledby="${this.label ? FORMFIELD_DEFAULTS.HEADING_ID : ''}"
515
474
  aria-expanded="${this.displayPopover ? 'true' : 'false'}"
475
+ aria-haspopup="${ROLE.LISTBOX}"
516
476
  aria-required="${this.required ? 'true' : 'false'}"
517
477
  aria-invalid="${this.helpTextType === VALIDATION.ERROR ? 'true' : 'false'}"
478
+ aria-disabled="${ifDefined(this.disabled || this.softDisabled)}"
479
+ aria-readonly="${ifDefined(this.readonly)}"
518
480
  >
519
481
  ${this.selectedIcon
520
482
  ? html `<mdc-icon length-unit="rem" size="1" name="${this.selectedIcon}" part="selected-icon"></mdc-icon>`
@@ -530,7 +492,25 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
530
492
  <mdc-icon size="1" length-unit="rem" name="${this.baseIconName}"></mdc-icon>
531
493
  </div>
532
494
  </div>
533
- ${this.getNativeSelect()} ${this.getPopoverContent()}
495
+ <mdc-popover
496
+ trigger="manual"
497
+ triggerid="${TRIGGER_ID}"
498
+ @keydown="${this.handlePopoverOnOpen}"
499
+ interactive
500
+ ?visible="${this.displayPopover}"
501
+ role=""
502
+ hide-on-outside-click
503
+ hide-on-escape
504
+ focus-back-to-trigger
505
+ focus-trap
506
+ size
507
+ placement="${this.placement}"
508
+ @closebyescape="${this.closePopover}"
509
+ @closebyoutsideclick="${this.closePopover}"
510
+ style="--mdc-popover-max-width: 100%; --mdc-popover-max-height: ${this.height};"
511
+ >
512
+ <slot @click="${this.handleOptionsClick}"></slot>
513
+ </mdc-popover>
534
514
  </div>
535
515
  ${this.renderHelperText()}
536
516
  `;
@@ -550,9 +530,17 @@ __decorate([
550
530
  __metadata("design:type", Object)
551
531
  ], Select.prototype, "height", void 0);
552
532
  __decorate([
553
- queryAssignedElements(),
533
+ property({ type: String, reflect: true }),
534
+ __metadata("design:type", String)
535
+ ], Select.prototype, "placement", void 0);
536
+ __decorate([
537
+ property({ type: Boolean, attribute: 'soft-disabled' }),
538
+ __metadata("design:type", Boolean)
539
+ ], Select.prototype, "softDisabled", void 0);
540
+ __decorate([
541
+ queryAssignedElements({ selector: 'mdc-selectlistbox' }),
554
542
  __metadata("design:type", Array)
555
- ], Select.prototype, "optionsList", void 0);
543
+ ], Select.prototype, "slottedListboxes", void 0);
556
544
  __decorate([
557
545
  state(),
558
546
  __metadata("design:type", String)
@@ -573,10 +561,6 @@ __decorate([
573
561
  state(),
574
562
  __metadata("design:type", Object)
575
563
  ], Select.prototype, "displayPopover", void 0);
576
- __decorate([
577
- state(),
578
- __metadata("design:type", Object)
579
- ], Select.prototype, "activeDescendant", void 0);
580
564
  __decorate([
581
565
  query('select'),
582
566
  __metadata("design:type", HTMLInputElement)
@@ -4,4 +4,5 @@ declare const ARROW_ICON: {
4
4
  readonly ARROW_DOWN: "arrow-down-bold";
5
5
  };
6
6
  declare const TRIGGER_ID = "select-base-triggerid";
7
- export { ARROW_ICON, TAG_NAME, TRIGGER_ID };
7
+ declare const LISTBOX_ID = "select-listbox";
8
+ export { ARROW_ICON, TAG_NAME, TRIGGER_ID, LISTBOX_ID };
@@ -5,4 +5,5 @@ const ARROW_ICON = {
5
5
  ARROW_DOWN: 'arrow-down-bold',
6
6
  };
7
7
  const TRIGGER_ID = 'select-base-triggerid';
8
- export { ARROW_ICON, TAG_NAME, TRIGGER_ID };
8
+ const LISTBOX_ID = 'select-listbox';
9
+ export { ARROW_ICON, TAG_NAME, TRIGGER_ID, LISTBOX_ID };
@@ -70,14 +70,25 @@ const styles = css `
70
70
  display: flex;
71
71
  padding: 2px;
72
72
  }
73
- :host([readonly])::part(icon-container) {
74
- color: var(--mdc-select-disabled-text-color);
75
- }
76
73
  :host::part(popover-content) {
77
74
  min-width: auto;
78
- overflow: scroll;
75
+ overflow-y: auto;
76
+ }
77
+
78
+ /* Help text border colors */
79
+ :host([help-text-type='success'])::part(base-container) {
80
+ border-color: var(--mdc-select-success-border-color);
81
+ }
82
+ :host([help-text-type='error'])::part(base-container) {
83
+ border-color: var(--mdc-select-error-border-color);
84
+ }
85
+ :host([help-text-type='warning'])::part(base-container) {
86
+ border-color: var(--mdc-select-warning-border-color);
79
87
  }
88
+
89
+ /* Disabled, readonly, soft-disabled */
80
90
  :host([disabled])::part(base-container),
91
+ :host([soft-disabled])::part(base-container),
81
92
  :host([readonly])::part(base-container),
82
93
  :host([help-text-type='success'][disabled])::part(base-container),
83
94
  :host([help-text-type='error'][disabled])::part(base-container),
@@ -88,24 +99,16 @@ const styles = css `
88
99
  border-color: var(--mdc-select-disabled-border-color);
89
100
  background: var(--mdc-select-disabled-background-color);
90
101
  }
91
- :host([disabled]:hover)::part(base-container),
92
- :host([readonly]:hover)::part(base-container) {
93
- background-color: unset;
94
- }
95
102
  :host([readonly])::part(base-text) {
96
103
  color: var(--mdc-select-selected-text-color);
97
104
  }
98
- :host([disabled])::part(base-text) {
105
+ :host([disabled])::part(base-text),
106
+ :host([soft-disabled])::part(base-text) {
99
107
  color: var(--mdc-select-disabled-text-color);
100
108
  }
101
- :host([help-text-type='success'])::part(base-container) {
102
- border-color: var(--mdc-select-success-border-color);
103
- }
104
- :host([help-text-type='error'])::part(base-container) {
105
- border-color: var(--mdc-select-error-border-color);
106
- }
107
- :host([help-text-type='warning'])::part(base-container) {
108
- border-color: var(--mdc-select-warning-border-color);
109
+ :host([soft-disabled])::part(icon-container),
110
+ :host([readonly])::part(icon-container) {
111
+ color: var(--mdc-select-disabled-text-color);
109
112
  }
110
113
  `;
111
114
  export default [styles, ...hostFocusRingStyles(true)];
@@ -1,4 +1,5 @@
1
1
  import type { ValueOf } from '../../utils/types';
2
+ import type { PopoverPlacement } from '../popover/popover.types';
2
3
  import { ARROW_ICON } from './select.constants';
3
4
  interface Events {
4
5
  onClickEvent: MouseEvent;
@@ -7,5 +8,6 @@ interface Events {
7
8
  onKeyDownEvent: KeyboardEvent;
8
9
  onFocusEvent: FocusEvent;
9
10
  }
11
+ type Placement = Extract<PopoverPlacement, 'bottom-start' | 'top-start'>;
10
12
  type ArrowIcon = ValueOf<typeof ARROW_ICON>;
11
- export type { Events, ArrowIcon };
13
+ export type { Events, ArrowIcon, Placement };
@@ -0,0 +1,7 @@
1
+ import Selectlistbox from './selectlistbox.component';
2
+ declare global {
3
+ interface HTMLElementTagNameMap {
4
+ ['mdc-selectlistbox']: Selectlistbox;
5
+ }
6
+ }
7
+ export default Selectlistbox;
@@ -0,0 +1,4 @@
1
+ import Selectlistbox from './selectlistbox.component';
2
+ import { TAG_NAME } from './selectlistbox.constants';
3
+ Selectlistbox.register(TAG_NAME);
4
+ export default Selectlistbox;
@@ -0,0 +1,18 @@
1
+ import { Component } from '../../models';
2
+ /**
3
+ * Selectlistbox component as Light DOM component to act as a simple wrapper
4
+ * for mdc-option components to ensure accessibility and proper role assignment.
5
+ *
6
+ * Once [ariaOwnsElements](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/ariaOwnsElements) is supported in browsers, this component can be removed
7
+ * and mdc-option can be used directly in the select component with a listbox in a different
8
+ * shadow root and aria-owns attribute to connect them.
9
+ *
10
+ * @tagname mdc-selectlistbox
11
+ *
12
+ * @slot default - This is a default/unnamed slot, which can be used to insert mdc-option components.
13
+ */
14
+ declare class Selectlistbox extends Component {
15
+ role: "listbox";
16
+ protected createRenderRoot(): HTMLElement | DocumentFragment;
17
+ }
18
+ export default Selectlistbox;
@@ -0,0 +1,24 @@
1
+ import { Component } from '../../models';
2
+ import { ROLE } from '../../utils/roles';
3
+ /**
4
+ * Selectlistbox component as Light DOM component to act as a simple wrapper
5
+ * for mdc-option components to ensure accessibility and proper role assignment.
6
+ *
7
+ * Once [ariaOwnsElements](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/ariaOwnsElements) is supported in browsers, this component can be removed
8
+ * and mdc-option can be used directly in the select component with a listbox in a different
9
+ * shadow root and aria-owns attribute to connect them.
10
+ *
11
+ * @tagname mdc-selectlistbox
12
+ *
13
+ * @slot default - This is a default/unnamed slot, which can be used to insert mdc-option components.
14
+ */
15
+ class Selectlistbox extends Component {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.role = ROLE.LISTBOX;
19
+ }
20
+ createRenderRoot() {
21
+ return this;
22
+ }
23
+ }
24
+ export default Selectlistbox;
@@ -0,0 +1,2 @@
1
+ declare const TAG_NAME: "mdc-selectlistbox";
2
+ export { TAG_NAME };
@@ -0,0 +1,3 @@
1
+ import utils from '../../utils/tag-name';
2
+ const TAG_NAME = utils.constructTagName('selectlistbox');
3
+ export { TAG_NAME };
@@ -0,0 +1,3 @@
1
+ interface Events {
2
+ }
3
+ export type { Events };