@momentum-design/components 0.81.2 → 0.81.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 (32) hide show
  1. package/dist/browser/index.js +230 -225
  2. package/dist/browser/index.js.map +4 -4
  3. package/dist/components/avatarbutton/avatarbutton.component.js +2 -1
  4. package/dist/components/badge/badge.component.js +2 -1
  5. package/dist/components/optgroup/optgroup.component.js +3 -2
  6. package/dist/components/option/option.component.d.ts +0 -4
  7. package/dist/components/option/option.component.js +3 -9
  8. package/dist/components/popover/popover.component.js +4 -4
  9. package/dist/components/popover/popover.utils.js +7 -7
  10. package/dist/components/progressbar/progressbar.component.js +2 -1
  11. package/dist/components/progressspinner/progressspinner.component.js +2 -1
  12. package/dist/components/radio/radio.component.js +2 -1
  13. package/dist/components/select/select.component.d.ts +0 -10
  14. package/dist/components/select/select.component.js +67 -53
  15. package/dist/components/select/select.constants.d.ts +2 -1
  16. package/dist/components/select/select.constants.js +2 -1
  17. package/dist/components/select/select.styles.js +2 -1
  18. package/dist/components/spinner/spinner.component.js +2 -1
  19. package/dist/components/tab/tab.component.js +2 -1
  20. package/dist/components/tablist/tablist.component.js +2 -1
  21. package/dist/components/toggle/toggle.component.js +2 -1
  22. package/dist/custom-elements.json +1229 -1274
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +2 -2
  25. package/dist/react/index.d.ts +4 -4
  26. package/dist/react/index.js +4 -4
  27. package/dist/utils/keys.d.ts +1 -0
  28. package/dist/utils/keys.js +1 -0
  29. package/dist/utils/mixins/FormInternalsMixin.js +1 -1
  30. package/dist/utils/roles.d.ts +9 -0
  31. package/dist/utils/roles.js +9 -0
  32. package/package.json +1 -1
@@ -12,6 +12,7 @@ import { property } from 'lit/decorators.js';
12
12
  import { ifDefined } from 'lit/directives/if-defined.js';
13
13
  import { AvatarComponentMixin } from '../../utils/mixins/AvatarComponentMixin';
14
14
  import { IconNameMixin } from '../../utils/mixins/IconNameMixin';
15
+ import { ROLE } from '../../utils/roles';
15
16
  import { AVATAR_SIZE, DEFAULTS } from '../avatar/avatar.constants';
16
17
  import { DEFAULTS as BUTTON_DEFAULTS } from '../button/button.constants';
17
18
  import Buttonsimple from '../buttonsimple/buttonsimple.component';
@@ -48,7 +49,7 @@ class AvatarButton extends AvatarComponentMixin(IconNameMixin(Buttonsimple)) {
48
49
  this.active = undefined;
49
50
  this.disabled = undefined;
50
51
  this.softDisabled = undefined;
51
- this.role = 'button';
52
+ this.role = ROLE.BUTTON;
52
53
  this.type = BUTTON_DEFAULTS.TYPE;
53
54
  }
54
55
  update(changedProperties) {
@@ -13,6 +13,7 @@ import { property } from 'lit/decorators.js';
13
13
  import { ifDefined } from 'lit/directives/if-defined.js';
14
14
  import { Component } from '../../models';
15
15
  import { IconNameMixin } from '../../utils/mixins/IconNameMixin';
16
+ import { ROLE } from '../../utils/roles';
16
17
  import { TYPE as FONT_TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
17
18
  import { DEFAULTS, ICON_NAMES_LIST, ICON_STATE, ICON_VARIANT, TYPE as BADGE_TYPE } from './badge.constants';
18
19
  import styles from './badge.styles';
@@ -147,7 +148,7 @@ class Badge extends IconNameMixin(Component) {
147
148
  */
148
149
  setRoleByAriaLabel() {
149
150
  if (this.ariaLabel) {
150
- this.role = 'img';
151
+ this.role = ROLE.IMG;
151
152
  }
152
153
  else {
153
154
  this.role = null;
@@ -12,6 +12,7 @@ import { property, queryAssignedElements } from 'lit/decorators.js';
12
12
  import { Component } from '../../models';
13
13
  import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
14
14
  import { DisabledMixin } from '../../utils/mixins/DisabledMixin';
15
+ import { ROLE } from '../../utils/roles';
15
16
  import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
16
17
  import { HEADER_ID } from './optgroup.constants';
17
18
  import styles from './optgroup.styles';
@@ -55,12 +56,12 @@ class OptGroup extends DataAriaLabelMixin(DisabledMixin(Component)) {
55
56
  ` : nothing;
56
57
  return html `
57
58
  <div
58
- role="group"
59
+ role="${ROLE.GROUP}"
59
60
  aria-labelledby="${this.label ? HEADER_ID : ''}"
60
61
  aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
61
62
  >
62
63
  ${headerText}
63
- <slot role="presentation"></slot>
64
+ <slot role="${ROLE.PRESENTATION}"></slot>
64
65
  </div>
65
66
  `;
66
67
  }
@@ -41,10 +41,6 @@ declare class Option extends Option_base {
41
41
  * It is called internally when the slot is changed.
42
42
  */
43
43
  private handleDefaultSlotChange;
44
- /**
45
- * Updates the attribute of the option to reflect the current state.
46
- */
47
- private updateAttribute;
48
44
  update(changedProperties: PropertyValues): void;
49
45
  render(): import("lit-html").TemplateResult<1>;
50
46
  static styles: Array<CSSResult>;
@@ -51,8 +51,8 @@ class Option extends FormInternalsMixin(ListItem) {
51
51
  super.connectedCallback();
52
52
  this.role = 'option';
53
53
  this.variant = LISTITEM_VARIANTS.INSET_RECTANGLE;
54
- this.updateAttribute('aria-selected', `${this.selected}`);
55
- this.updateAttribute('aria-disabled', `${!!this.disabled}`);
54
+ this.setAttribute('aria-selected', `${this.selected}`);
55
+ this.setAttribute('aria-disabled', `${!!this.disabled}`);
56
56
  // Option will not contain below fields
57
57
  this.name = undefined;
58
58
  this.secondaryLabel = undefined;
@@ -71,16 +71,10 @@ class Option extends FormInternalsMixin(ListItem) {
71
71
  this.label = (_c = (_b = slot.assignedNodes()[0]) === null || _b === void 0 ? void 0 : _b.textContent) === null || _c === void 0 ? void 0 : _c.trim();
72
72
  }
73
73
  }
74
- /**
75
- * Updates the attribute of the option to reflect the current state.
76
- */
77
- updateAttribute(attributeName, value) {
78
- this.setAttribute(attributeName, value);
79
- }
80
74
  update(changedProperties) {
81
75
  super.update(changedProperties);
82
76
  if (changedProperties.has('selected')) {
83
- this.updateAttribute('aria-selected', `${this.selected}`);
77
+ this.setAttribute('aria-selected', `${this.selected}`);
84
78
  }
85
79
  }
86
80
  render() {
@@ -370,7 +370,7 @@ class Popover extends FocusTrapMixin(Component) {
370
370
  this.triggerElement.addEventListener('mouseleave', this.startCloseDelay);
371
371
  this.addEventListener('mouseenter', this.cancelCloseDelay);
372
372
  this.addEventListener('mouseleave', this.startCloseDelay);
373
- hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.addEventListener('mouseenter', this.cancelCloseDelay);
373
+ hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.addEventListener('mouseenter', this.showPopover);
374
374
  }
375
375
  if (this.trigger.includes('focusin')) {
376
376
  this.triggerElement.addEventListener('focusin', this.showPopover);
@@ -394,7 +394,7 @@ class Popover extends FocusTrapMixin(Component) {
394
394
  this.removeEventListener('mouseleave', this.startCloseDelay);
395
395
  this.triggerElement.removeEventListener('focusin', this.showPopover);
396
396
  this.triggerElement.removeEventListener('focusout', this.hidePopover);
397
- hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.removeEventListener('mouseenter', this.cancelCloseDelay);
397
+ hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.removeEventListener('mouseenter', this.showPopover);
398
398
  this.removeEventListener('focus-trap-exit', this.hidePopover);
399
399
  }
400
400
  async updated(changedProperties) {
@@ -553,7 +553,7 @@ class Popover extends FocusTrapMixin(Component) {
553
553
  if (this.arrowElement) {
554
554
  const arrowLen = this.arrowElement.offsetHeight;
555
555
  const arrowOffset = Math.sqrt(2 * arrowLen ** 2) / 2;
556
- popoverOffset = arrowOffset + this.offset;
556
+ popoverOffset += arrowOffset;
557
557
  middleware.push(arrow({ element: this.arrowElement, padding: 12 }));
558
558
  }
559
559
  }
@@ -569,7 +569,7 @@ class Popover extends FocusTrapMixin(Component) {
569
569
  if (middlewareData.arrow && this.arrowElement) {
570
570
  this.utils.updateArrowStyle(middlewareData.arrow, placement);
571
571
  }
572
- if (this.trigger.includes('mouseenter')) {
572
+ if (this.trigger.includes('mouseenter') && this.interactive) {
573
573
  this.utils.setupHoverBridge(placement);
574
574
  }
575
575
  });
@@ -1,3 +1,4 @@
1
+ import { ROLE } from '../../utils/roles';
1
2
  export class PopoverUtils {
2
3
  constructor(popover) {
3
4
  /** @internal */
@@ -89,11 +90,13 @@ export class PopoverUtils {
89
90
  */
90
91
  setupAccessibility() {
91
92
  var _a, _b, _c;
92
- if (this.popover.role === 'dialog' || this.popover.role === 'alertdialog') {
93
- this.popover.toggleAttribute('aria-modal', this.popover.interactive);
93
+ if (this.popover.role === ROLE.DIALOG || this.popover.role === ROLE.ALERTDIALOG) {
94
+ this.popover.setAttribute('aria-modal', 'true');
95
+ }
96
+ else {
97
+ this.popover.removeAttribute('aria-modal');
94
98
  }
95
99
  if (this.popover.interactive) {
96
- this.popover.setAttribute('aria-modal', 'true');
97
100
  if (!this.popover.ariaLabel) {
98
101
  this.popover.ariaLabel = ((_a = this.popover.triggerElement) === null || _a === void 0 ? void 0 : _a.ariaLabel)
99
102
  || ((_b = this.popover.triggerElement) === null || _b === void 0 ? void 0 : _b.textContent)
@@ -103,9 +106,6 @@ export class PopoverUtils {
103
106
  this.popover.ariaLabelledby = ((_c = this.popover.triggerElement) === null || _c === void 0 ? void 0 : _c.id) || '';
104
107
  }
105
108
  }
106
- else {
107
- this.popover.removeAttribute('aria-modal');
108
- }
109
109
  }
110
110
  /**
111
111
  * Updates the aria-haspopup attribute on the trigger element.
@@ -185,7 +185,6 @@ export class PopoverUtils {
185
185
  if (!this.popover.backdropElement) {
186
186
  const backdrop = document.createElement('div');
187
187
  backdrop.classList.add('popover-backdrop');
188
- (_a = this.popover.parentElement) === null || _a === void 0 ? void 0 : _a.appendChild(backdrop);
189
188
  const styleElement = document.createElement('style');
190
189
  styleElement.textContent = `
191
190
  .popover-backdrop {
@@ -199,6 +198,7 @@ export class PopoverUtils {
199
198
  }
200
199
  `;
201
200
  backdrop.appendChild(styleElement);
201
+ (_a = this.popover.parentElement) === null || _a === void 0 ? void 0 : _a.appendChild(backdrop);
202
202
  this.popover.backdropElement = backdrop;
203
203
  }
204
204
  }
@@ -15,6 +15,7 @@ import FormfieldWrapper from '../formfieldwrapper';
15
15
  import { DEFAULTS, VARIANT } from './progressbar.constants';
16
16
  import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
17
17
  import { VALIDATION } from '../formfieldwrapper/formfieldwrapper.constants';
18
+ import { ROLE } from '../../utils/roles';
18
19
  /**
19
20
  * mdc-progressbar component visually represents a progress indicator, typically used to show
20
21
  * the completion state of an ongoing process (e.g., loading, file upload, etc.).
@@ -79,7 +80,7 @@ class Progressbar extends DataAriaLabelMixin(FormfieldWrapper) {
79
80
  return html `
80
81
  <div
81
82
  part="progress-container ${isGap ? 'gap' : ''}"
82
- role="progressbar"
83
+ role="${ROLE.PROGRESSBAR}"
83
84
  aria-valuenow="${this.clampedValue}"
84
85
  aria-valuemin="0"
85
86
  aria-valuemax="100"
@@ -4,6 +4,7 @@ import { Component } from '../../models';
4
4
  import Progressbar from '../progressbar/progressbar.component';
5
5
  import { DEFAULTS, ICON_NAME } from './progressspinner.constants';
6
6
  import { getProgressArc, getProgressOffset, getRemainingArc, getRemainingOffset } from './progressspiner.utils';
7
+ import { ROLE } from '../../utils/roles';
7
8
  /**
8
9
  * `mdc-progressspinner` is a customizable, circular progress indicator component.
9
10
  * It visually represents the current completion state of a process, such as loading,
@@ -51,7 +52,7 @@ class Progressspinner extends Progressbar {
51
52
  return html `
52
53
  <div
53
54
  part="spinner-container ${this.variant}"
54
- role="progressbar"
55
+ role="${ROLE.PROGRESSBAR}"
55
56
  aria-valuenow="${this.clampedValue}"
56
57
  aria-valuemin="0"
57
58
  aria-valuemax="100"
@@ -16,6 +16,7 @@ import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
16
16
  import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
17
17
  import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
18
18
  import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
19
+ import { ROLE } from '../../utils/roles';
19
20
  /**
20
21
  * Radio allow users to select single options from a list or turn an item/feature on or off.
21
22
  * These are often used in forms, settings, and selection in lists.
@@ -285,7 +286,7 @@ class Radio extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
285
286
  <input
286
287
  id="${this.id}"
287
288
  type="radio"
288
- role="radio"
289
+ role="${ROLE.RADIO}"
289
290
  ?autofocus="${this.autofocus}"
290
291
  name="${ifDefined(this.name)}"
291
292
  value="${ifDefined(this.value)}"
@@ -55,8 +55,6 @@ declare class Select extends Select_base implements AssociatedFormControl {
55
55
  displayPopover: boolean;
56
56
  /** @internal */
57
57
  activeDescendant: string;
58
- /** @internal */
59
- popoverWidth: string;
60
58
  /**
61
59
  * @internal
62
60
  * The native select element
@@ -108,18 +106,10 @@ declare class Select extends Select_base implements AssociatedFormControl {
108
106
  /** @internal */
109
107
  formStateRestoreCallback(state: string): void;
110
108
  private dispatchChange;
111
- /**
112
- * Handles the keydown event on the select element.
113
- * If the popover is open, then it calls `handlePopoverOnOpen` with the event.
114
- * If the popover is closed, then it calls `handlePopoverOnClose` with the event.
115
- * @param event - The keyboard event.
116
- */
117
- private handleKeydown;
118
109
  /**
119
110
  * Handles the keydown event on the select element when the popover is open.
120
111
  * The options are as follows:
121
112
  * - SPACE or ENTER: Selects the currently active option and closes the popover.
122
- * - ESCAPE: Closes the popover.
123
113
  * - HOME: Sets focus and tabindex on the first option.
124
114
  * - END: Sets focus and tabindex on the last option.
125
115
  * - ARROW_DOWN, ARROW_UP, PAGE_DOWN, PAGE_UP: Handles navigation between options.
@@ -9,16 +9,18 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { html, nothing } from 'lit';
11
11
  import { property, query, queryAssignedElements, state } from 'lit/decorators.js';
12
+ import { ifDefined } from 'lit/directives/if-defined.js';
12
13
  import { KEYS } from '../../utils/keys';
13
14
  import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
14
15
  import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
16
+ import { ROLE } from '../../utils/roles';
15
17
  import FormfieldWrapper from '../formfieldwrapper/formfieldwrapper.component';
16
- import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
18
+ import { DEFAULTS as FORMFIELD_DEFAULTS, VALIDATION } from '../formfieldwrapper/formfieldwrapper.constants';
17
19
  import { TAG_NAME as OPTION_GROUP_TAG_NAME } from '../optgroup/optgroup.constants';
18
20
  import { TAG_NAME as OPTION_TAG_NAME } from '../option/option.constants';
19
21
  import { POPOVER_PLACEMENT } from '../popover/popover.constants';
20
22
  import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
21
- import { ARROW_ICON } from './select.constants';
23
+ import { ARROW_ICON, TRIGGER_ID } from './select.constants';
22
24
  import styles from './select.styles';
23
25
  /**
24
26
  * The mdc-select component is a dropdown selection control that allows users to pick an option from a predefined list.
@@ -63,8 +65,6 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
63
65
  this.displayPopover = false;
64
66
  /** @internal */
65
67
  this.activeDescendant = '';
66
- /** @internal */
67
- this.popoverWidth = '100%';
68
68
  }
69
69
  connectedCallback() {
70
70
  super.connectedCallback();
@@ -118,11 +118,10 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
118
118
  option === null || option === void 0 ? void 0 : option.removeAttribute('selected');
119
119
  }
120
120
  });
121
- if (isTabIndexSet) {
122
- return;
121
+ if (!isTabIndexSet) {
122
+ // if no option is selected, set the first option as focused
123
+ (_a = this.getAllValidOptions()[0]) === null || _a === void 0 ? void 0 : _a.setAttribute('tabindex', '0');
123
124
  }
124
- // if no option is selected, set the first option as focused
125
- (_a = this.getAllValidOptions()[0]) === null || _a === void 0 ? void 0 : _a.setAttribute('tabindex', '0');
126
125
  }
127
126
  /**
128
127
  * A private method which is called when an option is clicked.
@@ -197,25 +196,10 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
197
196
  bubbles: true,
198
197
  }));
199
198
  }
200
- /**
201
- * Handles the keydown event on the select element.
202
- * If the popover is open, then it calls `handlePopoverOnOpen` with the event.
203
- * If the popover is closed, then it calls `handlePopoverOnClose` with the event.
204
- * @param event - The keyboard event.
205
- */
206
- handleKeydown(event) {
207
- if (this.displayPopover) {
208
- this.handlePopoverOnOpen(event);
209
- }
210
- else {
211
- this.handlePopoverOnClose(event);
212
- }
213
- }
214
199
  /**
215
200
  * Handles the keydown event on the select element when the popover is open.
216
201
  * The options are as follows:
217
202
  * - SPACE or ENTER: Selects the currently active option and closes the popover.
218
- * - ESCAPE: Closes the popover.
219
203
  * - HOME: Sets focus and tabindex on the first option.
220
204
  * - END: Sets focus and tabindex on the last option.
221
205
  * - ARROW_DOWN, ARROW_UP, PAGE_DOWN, PAGE_UP: Handles navigation between options.
@@ -224,6 +208,12 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
224
208
  handlePopoverOnOpen(event) {
225
209
  var _a;
226
210
  switch (event.key) {
211
+ case KEYS.TAB: {
212
+ const focusedOptionIndex = this.getAllValidOptions().findIndex((option) => option === event.target);
213
+ this.setFocusAndTabIndex(focusedOptionIndex);
214
+ event.preventDefault();
215
+ break;
216
+ }
227
217
  case KEYS.SPACE:
228
218
  this.updateTabIndexForAllOptions(event.target);
229
219
  this.closePopover();
@@ -236,10 +226,6 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
236
226
  // if the popover is closed, then we submit the form.
237
227
  (_a = this.form) === null || _a === void 0 ? void 0 : _a.requestSubmit();
238
228
  break;
239
- case KEYS.ESCAPE:
240
- this.closePopover();
241
- event.stopPropagation();
242
- break;
243
229
  case KEYS.HOME:
244
230
  this.setFocusAndTabIndex(0);
245
231
  event.preventDefault();
@@ -253,7 +239,7 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
253
239
  case KEYS.PAGE_DOWN:
254
240
  case KEYS.PAGE_UP:
255
241
  this.handleOptionsNavigation(event);
256
- this.updateActivedescendant(event.target);
242
+ event.preventDefault();
257
243
  break;
258
244
  default:
259
245
  break;
@@ -339,24 +325,44 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
339
325
  return -1;
340
326
  }
341
327
  updateActivedescendant(target) {
342
- var _a, _b;
343
- const currentIndex = this.getAllValidOptions().findIndex((option) => option === target);
344
- this.activeDescendant = ((_a = this.getAllValidOptions()[currentIndex]) === null || _a === void 0 ? void 0 : _a.id) || ((_b = this.getAllValidOptions()[0]) === null || _b === void 0 ? void 0 : _b.id);
328
+ var _a, _b, _c, _d, _e;
329
+ const options = this.getAllValidOptions();
330
+ if (target) {
331
+ const currentIndex = options.findIndex((option) => option === target);
332
+ this.activeDescendant = (_b = (_a = options[currentIndex]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
333
+ }
334
+ else {
335
+ // If no target is provided, find the option with tabindex="0" or the first option
336
+ const focusedOption = options.find((option) => option.getAttribute('tabindex') === '0');
337
+ 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 : '';
338
+ }
345
339
  }
346
340
  resetActivedescendant() {
347
341
  this.activeDescendant = '';
348
342
  }
349
343
  setFocusAndTabIndex(newIndex) {
350
344
  var _a;
351
- (_a = this.getAllValidOptions()[newIndex]) === null || _a === void 0 ? void 0 : _a.focus();
352
- this.getAllValidOptions().forEach((node, index) => {
353
- const newTabindex = newIndex === index ? '0' : '-1';
354
- node === null || node === void 0 ? void 0 : node.setAttribute('tabindex', newTabindex);
355
- });
345
+ const options = this.getAllValidOptions();
346
+ const targetOption = options[newIndex];
347
+ if (targetOption) {
348
+ targetOption.focus();
349
+ options.forEach((node, index) => {
350
+ const newTabindex = newIndex === index ? '0' : '-1';
351
+ node === null || node === void 0 ? void 0 : node.setAttribute('tabindex', newTabindex);
352
+ });
353
+ // Update activeDescendant after changing focus
354
+ this.activeDescendant = (_a = targetOption.id) !== null && _a !== void 0 ? _a : '';
355
+ }
356
356
  }
357
357
  openPopover() {
358
+ var _a, _b;
358
359
  this.displayPopover = true;
359
- this.resetActivedescendant();
360
+ // Find the currently selected option or the first option
361
+ const options = this.getAllValidOptions();
362
+ const selectedOption = options.find((option) => option.hasAttribute('selected'));
363
+ const focusedOption = options.find((option) => option.getAttribute('tabindex') === '0');
364
+ // Set activeDescendant to the selected/focused option or first option
365
+ this.activeDescendant = (_b = (_a = (selectedOption || focusedOption || options[0])) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
360
366
  }
361
367
  closePopover() {
362
368
  this.displayPopover = false;
@@ -398,6 +404,7 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
398
404
  part="native-select"
399
405
  id="${this.id}"
400
406
  tabindex="-1"
407
+ aria-hidden="true"
401
408
  name="${this.name}"
402
409
  size="1"
403
410
  .value="${this.selectedValue}"
@@ -449,21 +456,23 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
449
456
  return html `
450
457
  <mdc-popover
451
458
  id="options-popover"
452
- triggerid="select-base-triggerid"
453
- @keydown="${this.handleKeydown}"
459
+ triggerid="${TRIGGER_ID}"
460
+ @keydown="${this.handlePopoverOnOpen}"
454
461
  interactive
455
462
  ?visible="${this.displayPopover}"
456
463
  hide-on-outside-click
464
+ hide-on-escape
457
465
  focus-back-to-trigger
458
466
  focus-trap
459
- role="listbox"
467
+ role="${ROLE.LISTBOX}"
460
468
  placement="${POPOVER_PLACEMENT.BOTTOM_START}"
461
469
  @shown="${this.handlePopoverOpen}"
462
470
  @hidden="${this.handlePopoverClose}"
463
- style="--mdc-popover-max-width: ${this.popoverWidth}; --mdc-popover-max-height: ${this.height};"
471
+ style="--mdc-popover-max-width: 100%; --mdc-popover-max-height: ${this.height};"
464
472
  >
465
- <slot @click="${this.handleOptionsClick}"></slot>
466
- </mdc-popover>
473
+ <slot
474
+ @click="${this.handleOptionsClick}"></slot>
475
+ </mdc-popover>
467
476
  `;
468
477
  }
469
478
  updated(changedProperties) {
@@ -472,6 +481,14 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
472
481
  this.closePopover();
473
482
  this.handlePopoverClose();
474
483
  }
484
+ if (changedProperties.has('displayPopover')) {
485
+ if (this.displayPopover) {
486
+ this.openPopover();
487
+ }
488
+ else {
489
+ this.closePopover();
490
+ }
491
+ }
475
492
  }
476
493
  render() {
477
494
  var _a, _b;
@@ -479,18 +496,19 @@ class Select extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
479
496
  ${this.renderLabel()}
480
497
  <div part="container">
481
498
  <div
482
- id="select-base-triggerid"
499
+ id="${TRIGGER_ID}"
483
500
  part="base-container"
484
- @keydown="${this.handleKeydown}"
501
+ @keydown="${this.handlePopoverOnClose}"
485
502
  tabindex="${this.disabled ? '-1' : '0'}"
486
503
  class="${this.disabled ? '' : 'mdc-focus-ring'}"
487
- role="combobox"
488
- aria-activedescendant="${this.activeDescendant}"
489
- aria-haspopup="listbox"
504
+ role="${ROLE.COMBOBOX}"
505
+ aria-activedescendant="${ifDefined(this.activeDescendant || undefined)}"
506
+ aria-controls="${(ifDefined(this.displayPopover ? 'options-popover' : undefined))}"
490
507
  aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
491
508
  aria-labelledby="${this.label ? FORMFIELD_DEFAULTS.HEADING_ID : ''}"
492
509
  aria-expanded="${this.displayPopover ? 'true' : 'false'}"
493
- aria-controls="options-popover"
510
+ aria-required="${this.required ? 'true' : 'false'}"
511
+ aria-invalid="${this.helpTextType === VALIDATION.ERROR ? 'true' : 'false'}"
494
512
  >
495
513
  ${this.selectedIcon
496
514
  ? html `<mdc-icon length-unit="rem" size="1" name="${this.selectedIcon}" part="selected-icon"></mdc-icon>`
@@ -554,10 +572,6 @@ __decorate([
554
572
  state(),
555
573
  __metadata("design:type", Object)
556
574
  ], Select.prototype, "activeDescendant", void 0);
557
- __decorate([
558
- state(),
559
- __metadata("design:type", Object)
560
- ], Select.prototype, "popoverWidth", void 0);
561
575
  __decorate([
562
576
  query('select'),
563
577
  __metadata("design:type", HTMLInputElement)
@@ -3,4 +3,5 @@ declare const ARROW_ICON: {
3
3
  readonly ARROW_UP: "arrow-up-bold";
4
4
  readonly ARROW_DOWN: "arrow-down-bold";
5
5
  };
6
- export { ARROW_ICON, TAG_NAME };
6
+ declare const TRIGGER_ID = "select-base-triggerid";
7
+ export { ARROW_ICON, TAG_NAME, TRIGGER_ID };
@@ -4,4 +4,5 @@ const ARROW_ICON = {
4
4
  ARROW_UP: 'arrow-up-bold',
5
5
  ARROW_DOWN: 'arrow-down-bold',
6
6
  };
7
- export { ARROW_ICON, TAG_NAME };
7
+ const TRIGGER_ID = 'select-base-triggerid';
8
+ export { ARROW_ICON, TAG_NAME, TRIGGER_ID };
@@ -28,7 +28,7 @@ const styles = css `
28
28
  padding: 0;
29
29
  position: absolute;
30
30
  width: 100%;
31
- height: 1px;
31
+ height: 0;
32
32
  z-index: -1;
33
33
  }
34
34
  :host::part(container) {
@@ -75,6 +75,7 @@ const styles = css `
75
75
  }
76
76
  :host::part(popover-content) {
77
77
  min-width: auto;
78
+ overflow: scroll;
78
79
  }
79
80
  :host([disabled])::part(base-container),
80
81
  :host([readonly])::part(base-container),
@@ -12,6 +12,7 @@ import { property } from 'lit/decorators.js';
12
12
  import styles from './spinner.styles';
13
13
  import { Component } from '../../models';
14
14
  import { DEFAULTS } from './spinner.constants';
15
+ import { ROLE } from '../../utils/roles';
15
16
  /**
16
17
  * `mdc-spinner` is loading spinner which is an indeterminate progress indicator, meaning
17
18
  * it's best for cases where the progress or duration of a process is variable or unknown.
@@ -85,7 +86,7 @@ class Spinner extends Component {
85
86
  updated(changedProperties) {
86
87
  super.updated(changedProperties);
87
88
  if (changedProperties.has('ariaLabel')) {
88
- this.role = this.ariaLabel ? 'img' : null;
89
+ this.role = this.ariaLabel ? ROLE.IMG : null;
89
90
  this.ariaHidden = this.ariaLabel ? 'false' : 'true';
90
91
  }
91
92
  }
@@ -15,6 +15,7 @@ import { getIconNameWithoutStyle } from '../button/button.utils';
15
15
  import Buttonsimple from '../buttonsimple/buttonsimple.component';
16
16
  import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
17
17
  import { IconNameMixin } from '../../utils/mixins/IconNameMixin';
18
+ import { ROLE } from '../../utils/roles';
18
19
  /**
19
20
  * `mdc-tab` is Tab component to be used within the Tabgroup.
20
21
  *
@@ -152,7 +153,7 @@ class Tab extends IconNameMixin(Buttonsimple) {
152
153
  }
153
154
  connectedCallback() {
154
155
  super.connectedCallback();
155
- this.role = 'tab';
156
+ this.role = ROLE.TAB;
156
157
  this.softDisabled = undefined;
157
158
  this.size = undefined;
158
159
  this.type = undefined;
@@ -16,6 +16,7 @@ import { ARROW_BUTTON_DIRECTION, KEYCODES } from './tablist.constants';
16
16
  import Tab from '../tab/tab.component';
17
17
  import Button from '../button/button.component';
18
18
  import { getFirstTab, getLastTab, getNextTab, getPreviousTab, findTab, getActiveTab } from './tablist.utils';
19
+ import { ROLE } from '../../utils/roles';
19
20
  /**
20
21
  * Tab list organizes tabs into a container.
21
22
  *
@@ -443,7 +444,7 @@ class TabList extends Component {
443
444
  return html ` ${arrowButton('backward')}
444
445
  <div
445
446
  class="container"
446
- role="tablist"
447
+ role="${ROLE.TABLIST}"
447
448
  tabindex="-1"
448
449
  aria-label="${ifDefined(this.dataAriaLabel)}">
449
450
  <slot></slot>
@@ -12,6 +12,7 @@ import { property } from 'lit/decorators.js';
12
12
  import { ifDefined } from 'lit/directives/if-defined.js';
13
13
  import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
14
14
  import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
15
+ import { ROLE } from '../../utils/roles';
15
16
  import FormfieldWrapper from '../formfieldwrapper';
16
17
  import { DEFAULTS as FORMFIELD_DEFAULTS } from '../formfieldwrapper/formfieldwrapper.constants';
17
18
  import { DEFAULTS, TOGGLE_SIZE } from './toggle.constants';
@@ -189,7 +190,7 @@ class Toggle extends FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)) {
189
190
  id="${this.id}"
190
191
  type="checkbox"
191
192
  part="toggle-input"
192
- role="switch"
193
+ role="${ROLE.CHECKBOX}"
193
194
  ?autofocus="${this.autofocus}"
194
195
  ?required="${this.required}"
195
196
  name="${ifDefined(this.name)}"