@crowdstrike/glide-core 0.32.3 → 0.33.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 (56) hide show
  1. package/dist/accordion.js +3 -2
  2. package/dist/button-group.button.js +3 -2
  3. package/dist/button-group.js +3 -2
  4. package/dist/button.js +5 -10
  5. package/dist/button.styles.js +0 -12
  6. package/dist/checkbox-group.js +8 -7
  7. package/dist/checkbox.js +8 -7
  8. package/dist/checkbox.styles.js +1 -1
  9. package/dist/drawer.js +3 -2
  10. package/dist/dropdown.js +19 -18
  11. package/dist/dropdown.option.d.ts +2 -0
  12. package/dist/dropdown.option.js +26 -25
  13. package/dist/form-controls-layout.js +3 -2
  14. package/dist/icon-button.js +2 -2
  15. package/dist/inline-alert.js +3 -2
  16. package/dist/input.js +7 -7
  17. package/dist/label.js +3 -2
  18. package/dist/library/assert-slot.js +2 -2
  19. package/dist/library/assert-slot.test.js +92 -0
  20. package/dist/link.js +2 -2
  21. package/dist/menu.js +61 -30
  22. package/dist/menu.styles.js +1 -0
  23. package/dist/modal.icon-button.js +3 -2
  24. package/dist/modal.js +3 -2
  25. package/dist/option.d.ts +5 -0
  26. package/dist/option.js +28 -5
  27. package/dist/options.d.ts +2 -0
  28. package/dist/options.group.js +3 -2
  29. package/dist/options.js +7 -8
  30. package/dist/options.styles.js +0 -6
  31. package/dist/popover.js +3 -2
  32. package/dist/radio-group.js +4 -3
  33. package/dist/radio-group.radio.js +3 -2
  34. package/dist/select.d.ts +90 -0
  35. package/dist/select.js +532 -0
  36. package/dist/slider.js +3 -3
  37. package/dist/spinner.js +2 -2
  38. package/dist/split-button.js +3 -2
  39. package/dist/split-button.primary-button.js +2 -2
  40. package/dist/split-button.primary-link.js +2 -2
  41. package/dist/split-button.secondary-button.js +2 -2
  42. package/dist/styles/opacity-and-scale-animation.js +2 -1
  43. package/dist/styles/variables.css +7 -4
  44. package/dist/tab.group.js +3 -2
  45. package/dist/tab.js +3 -2
  46. package/dist/tab.panel.js +3 -2
  47. package/dist/tag.js +2 -2
  48. package/dist/textarea.js +8 -7
  49. package/dist/toast.js +2 -2
  50. package/dist/toast.toasts.js +3 -2
  51. package/dist/toggle.js +3 -2
  52. package/dist/tooltip.container.js +3 -2
  53. package/dist/tooltip.js +4 -7
  54. package/package.json +4 -4
  55. package/dist/library/shadow-root-mode.d.ts +0 -2
  56. package/dist/library/shadow-root-mode.js +0 -4
package/dist/label.js CHANGED
@@ -16,7 +16,6 @@ import styles from './label.styles.js';
16
16
  import { LocalizeController } from './library/localize.js';
17
17
  import assertSlot from './library/assert-slot.js';
18
18
  import onResize from './library/on-resize.js';
19
- import shadowRootMode from './library/shadow-root-mode.js';
20
19
  import final from './library/final.js';
21
20
  /**
22
21
  * @attr {boolean} [disabled=false]
@@ -50,10 +49,12 @@ let Label = class Label extends LitElement {
50
49
  this.#localize = new LocalizeController(this);
51
50
  this.#summarySlotElementRef = createRef();
52
51
  }
52
+ /* c8 ignore start */
53
53
  static { this.shadowRootOptions = {
54
54
  ...LitElement.shadowRootOptions,
55
- mode: shadowRootMode,
55
+ mode: window.navigator.webdriver ? 'open' : 'closed',
56
56
  }; }
57
+ /* c8 ignore end */
57
58
  static { this.styles = styles; }
58
59
  render() {
59
60
  // `aria-hidden` is used on the tooltip so the contents of the label aren't
@@ -48,11 +48,11 @@ class AssertSlot extends Directive {
48
48
  part.element?.addEventListener('slotchange', () => {
49
49
  if (isOptional &&
50
50
  part.element instanceof HTMLSlotElement &&
51
- part.element.assignedNodes().length === 0) {
51
+ part.element.assignedNodes({ flatten: true }).length === 0) {
52
52
  return;
53
53
  }
54
54
  if (part.element instanceof HTMLSlotElement &&
55
- part.element.assignedNodes().length === 0) {
55
+ part.element.assignedNodes({ flatten: true }).length === 0) {
56
56
  if (slotted && slotted.length > 0) {
57
57
  const message = part.element.name
58
58
  ? `Expected the "${part.element.name}" slot of ${host.constructor.name} to have a slotted element that extends ${slotted
@@ -51,6 +51,34 @@ let WhenNotUsedInsideATag = class WhenNotUsedInsideATag extends LitElement {
51
51
  WhenNotUsedInsideATag = __decorate([
52
52
  customElement('glide-core-when-not-used-inside-tag')
53
53
  ], WhenNotUsedInsideATag);
54
+ let Reslotted = class Reslotted extends LitElement {
55
+ constructor() {
56
+ super(...arguments);
57
+ this.optional = false;
58
+ this.slotted = [];
59
+ }
60
+ render() {
61
+ return html `<glide-core-with-slot
62
+ name=${ifDefined(this.name)}
63
+ .slotted=${this.slotted}
64
+ ?optional=${this.optional}
65
+ >
66
+ <slot name=${ifDefined(this.name)}></slot>
67
+ </glide-core-with-slot>`;
68
+ }
69
+ };
70
+ __decorate([
71
+ property()
72
+ ], Reslotted.prototype, "name", void 0);
73
+ __decorate([
74
+ property({ type: Boolean })
75
+ ], Reslotted.prototype, "optional", void 0);
76
+ __decorate([
77
+ property({ type: Array })
78
+ ], Reslotted.prototype, "slotted", void 0);
79
+ Reslotted = __decorate([
80
+ customElement('glide-core-reslotted')
81
+ ], Reslotted);
54
82
  it('throws when a required default slot is empty', async () => {
55
83
  const stub = sinon.stub(console, 'error');
56
84
  const spy = sinon.spy();
@@ -283,3 +311,67 @@ it('throws when not used inside an opening tag', async () => {
283
311
  }
284
312
  expect(spy.args.at(0)?.at(0).message).to.equal("Directive must be inside the element's opening tag.");
285
313
  });
314
+ it('throws when a reslotted and required default slot is empty', async () => {
315
+ const stub = sinon.stub(console, 'error');
316
+ const spy = sinon.spy();
317
+ const onerror = window.onerror;
318
+ // eslint-disable-next-line unicorn/prefer-add-event-listener
319
+ window.onerror = spy;
320
+ await fixture(html `<glide-core-reslotted
321
+ .slotted=${[HTMLDivElement]}
322
+ ></glide-core-reslotted>`);
323
+ expect(spy.callCount).to.equal(1);
324
+ expect(spy.args.at(0)?.at(0)).to.equal('Uncaught TypeError: Expected WithSlot to have a slotted element that extends HTMLDivElement.');
325
+ // eslint-disable-next-line unicorn/prefer-add-event-listener
326
+ window.onerror = onerror;
327
+ stub.restore();
328
+ });
329
+ it('throws when a reslotted default slot has the wrong element', async () => {
330
+ const stub = sinon.stub(console, 'error');
331
+ const spy = sinon.spy();
332
+ const onerror = window.onerror;
333
+ // eslint-disable-next-line unicorn/prefer-add-event-listener
334
+ window.onerror = spy;
335
+ await fixture(html `<glide-core-reslotted .slotted=${[HTMLDivElement]}>
336
+ <a href="/">Link</a>
337
+ </glide-core-reslotted>`);
338
+ expect(spy.callCount).to.equal(1);
339
+ expect(spy.args.at(0)?.at(0)).to.equal('Uncaught TypeError: Expected WithSlot to have a slotted element that extends HTMLDivElement. Extends HTMLAnchorElement instead.');
340
+ // eslint-disable-next-line unicorn/prefer-add-event-listener
341
+ window.onerror = onerror;
342
+ stub.restore();
343
+ });
344
+ it('does not throw when a reslotted default slot has the correct element', async () => {
345
+ const spy = sinon.spy();
346
+ window.addEventListener('error', spy);
347
+ await fixture(html `<glide-core-reslotted .slotted=${[HTMLDivElement]}>
348
+ <div>Content</div>
349
+ </glide-core-reslotted>`);
350
+ expect(spy.callCount).to.equal(0);
351
+ });
352
+ it('throws when a reslotted and required named slot is empty', async () => {
353
+ const stub = sinon.stub(console, 'error');
354
+ const spy = sinon.spy();
355
+ window.addEventListener('unhandledrejection', spy, { once: true });
356
+ await fixture(html `<glide-core-reslotted
357
+ name="test"
358
+ .slotted=${[HTMLDivElement]}
359
+ ></glide-core-reslotted>`);
360
+ await waitUntil(() => spy.callCount);
361
+ expect(spy.callCount).to.equal(1);
362
+ expect(spy.args.at(0)?.at(0) instanceof PromiseRejectionEvent).to.be.true;
363
+ expect(spy.args.at(0)?.at(0).reason.message).to.equal('Expected the "test" slot of WithSlot to have a slotted element that extends HTMLDivElement.');
364
+ stub.restore();
365
+ });
366
+ it('does not throw when a reslotted optional named slot is empty', async () => {
367
+ const spy = sinon.spy();
368
+ window.addEventListener('unhandledrejection', spy, { once: true });
369
+ await fixture(html `<glide-core-reslotted name="test" optional></glide-core-reslotted>`);
370
+ expect(spy.callCount).to.equal(0);
371
+ });
372
+ it('does not throw when a reslotted optional slot is empty', async () => {
373
+ const spy = sinon.spy();
374
+ window.addEventListener('unhandledrejection', spy, { once: true });
375
+ await fixture(html `<glide-core-reslotted optional></glide-core-reslotted>`);
376
+ expect(spy.callCount).to.equal(0);
377
+ });
package/dist/link.js CHANGED
@@ -11,7 +11,6 @@ import { ifDefined } from 'lit/directives/if-defined.js';
11
11
  import { createRef, ref } from 'lit/directives/ref.js';
12
12
  import packageJson from '../package.json' with { type: 'json' };
13
13
  import styles from './link.styles.js';
14
- import shadowRootMode from './library/shadow-root-mode.js';
15
14
  import final from './library/final.js';
16
15
  import required from './library/required.js';
17
16
  /**
@@ -32,10 +31,11 @@ let Link = class Link extends LitElement {
32
31
  this.version = packageJson.version;
33
32
  this.#componentElementRef = createRef();
34
33
  }
34
+ /* c8 ignore start */
35
35
  static { this.shadowRootOptions = {
36
36
  ...LitElement.shadowRootOptions,
37
37
  delegatesFocus: true,
38
- mode: shadowRootMode,
38
+ mode: window.navigator.webdriver ? 'open' : 'closed',
39
39
  }; }
40
40
  static { this.styles = styles; }
41
41
  click() {
package/dist/menu.js CHANGED
@@ -18,7 +18,6 @@ import Option from './option.js';
18
18
  import Input from './input.js';
19
19
  import assertSlot from './library/assert-slot.js';
20
20
  import styles from './menu.styles.js';
21
- import shadowRootMode from './library/shadow-root-mode.js';
22
21
  import final from './library/final.js';
23
22
  import uniqueId from './library/unique-id.js';
24
23
  /**
@@ -107,10 +106,12 @@ let Menu = class Menu extends LitElement {
107
106
  };
108
107
  }
109
108
  static { Menu_1 = this; }
109
+ /* c8 ignore start */
110
110
  static { this.shadowRootOptions = {
111
111
  ...LitElement.shadowRootOptions,
112
- mode: shadowRootMode,
112
+ mode: window.navigator.webdriver ? 'open' : 'closed',
113
113
  }; }
114
+ /* c8 ignore end */
114
115
  static { this.styles = styles; }
115
116
  /**
116
117
  * @default false
@@ -184,9 +185,9 @@ let Menu = class Menu extends LitElement {
184
185
  firstUpdated() {
185
186
  if (this.#optionsElement && this.#targetElement) {
186
187
  this.#optionsElement.privateLoading = this.loading;
187
- this.#targetElement.ariaDescription = this.loading
188
- ? this.#localize.term('loading')
189
- : null;
188
+ if (this.loading) {
189
+ this.#targetElement.ariaDescription = this.#localize.term('loading');
190
+ }
190
191
  }
191
192
  if (this.open && !this.isTargetDisabled) {
192
193
  const openedSubMenus = this.#subMenus.filter(({ open }) => open);
@@ -298,8 +299,8 @@ let Menu = class Menu extends LitElement {
298
299
  @keydown=${this.#onTargetAndDefaultSlotKeyDown}
299
300
  @mousedown=${this.#onDefaultSlotMouseDown}
300
301
  @mouseover=${this.#onDefaultSlotMouseOver}
301
- @private-disabled-change=${this.#onDefaultSlotDisabledChange}
302
- @private-slot-change=${this.#onDefaultSlotSlotChange}
302
+ @disabled=${this.#onDefaultSlotDisabled}
303
+ @slotchange=${this.#onDefaultSlotSlotChange}
303
304
  @toggle=${this.#onDefaultSlotToggle}
304
305
  ${assertSlot([Element])}
305
306
  ${ref(this.#defaultSlotElementRef)}
@@ -457,7 +458,9 @@ let Menu = class Menu extends LitElement {
457
458
  this.#targetElement?.focus(options);
458
459
  }
459
460
  }
460
- #hide() {
461
+ async #hide() {
462
+ // There's a comment in `#show()` explaining why.
463
+ await this.#activeOption?.updateComplete;
461
464
  this.#cleanUpFloatingUi?.();
462
465
  this.#defaultSlotElementRef.value?.hidePopover();
463
466
  if (this.#optionsElement) {
@@ -545,7 +548,7 @@ let Menu = class Menu extends LitElement {
545
548
  }
546
549
  });
547
550
  }
548
- #onDefaultSlotDisabledChange(event) {
551
+ #onDefaultSlotDisabled(event) {
549
552
  if (this.#activeOption === event.target &&
550
553
  event.target instanceof Option &&
551
554
  event.target.disabled &&
@@ -1051,6 +1054,13 @@ let Menu = class Menu extends LitElement {
1051
1054
  this.#hide();
1052
1055
  }
1053
1056
  });
1057
+ /* c8 ignore start */
1058
+ if (this.#targetElement?.ariaDescription !== null &&
1059
+ this.#targetElement?.ariaDescription !== this.#localize.term('loading')) {
1060
+ // eslint-disable-next-line no-console
1061
+ console.warn("Menu will overwrite the `aria-description` on your target when Menu's `loading` attribute it set.");
1062
+ }
1063
+ /* c8 ignore end */
1054
1064
  if (this.#targetElement && this.#optionsElement) {
1055
1065
  observer.observe(this.#targetElement, {
1056
1066
  attributeFilter: ['aria-disabled', 'disabled'],
@@ -1153,7 +1163,44 @@ let Menu = class Menu extends LitElement {
1153
1163
  #onTargetSlotInput() {
1154
1164
  this.open = true;
1155
1165
  }
1156
- #show() {
1166
+ async #show() {
1167
+ if (this.#previouslyActiveOption &&
1168
+ !this.#previouslyActiveOption.disabled &&
1169
+ this.#optionsElement) {
1170
+ this.#previouslyActiveOption.privateActive = true;
1171
+ }
1172
+ else if (this.#firstEnabledOption && this.#optionsElement) {
1173
+ this.#firstEnabledOption.privateActive = true;
1174
+ this.#previouslyActiveOption = this.#firstEnabledOption;
1175
+ }
1176
+ if (this.#optionsElement && this.#activeOption?.id) {
1177
+ this.#optionsElement.ariaActivedescendant = this.#activeOption.id;
1178
+ this.#activeOption.privateTooltipOpen = this.privateOpenedViaKeyboard;
1179
+ }
1180
+ else if (this.#optionsElement) {
1181
+ this.#optionsElement.ariaActivedescendant = '';
1182
+ }
1183
+ // If we don't wait for the Option to update, it'll briefly appear as inactive when
1184
+ // Menu opens because `showPopover()` is synchronous and component updates are not.
1185
+ //
1186
+ // A side effect of waiting, however, is that calling `#show()` then immediately
1187
+ // calling `#hide()` won't work as expected: Menu will never close because the
1188
+ // `showPopover()` call below will be slightly delayed. So `hidePopover()`, via
1189
+ // `#hide()`, will be called just before `showPopover()`.
1190
+ //
1191
+ // That's why we also wait for the Option to update in `#hide()`. `#hide()` being
1192
+ // called immediately after `#show()` may seem unlikely. But Menu itself does it
1193
+ // when multiple sub-Menus are initially open.
1194
+ //
1195
+ // When, for example, Menu and two of its sub-Menus are initially open, each
1196
+ // sub-Menu's `open` setter will get called, and each `open` setter will call
1197
+ // `#show()`.
1198
+ //
1199
+ // But two open sub-Menus doesn't make sense. So Menu, shortly after the sub-Menus'
1200
+ // `open` setters are initially called, will set the `open` property of the other
1201
+ // sub-Menu to `false` in `firstUpdated()`. Thus a `#hide()` call immediately after
1202
+ // `#show()`.
1203
+ await this.#activeOption?.updateComplete;
1157
1204
  this.#cleanUpFloatingUi?.();
1158
1205
  // Ideally, we wouldn't show the popover until after Floating UI has calculated its
1159
1206
  // position. But calling `showPopover()` changes the nature of how `top` and `left`
@@ -1206,27 +1253,11 @@ let Menu = class Menu extends LitElement {
1206
1253
  else if (!this.#isSubMenu && this.#targetElement) {
1207
1254
  this.#targetElement.ariaExpanded = 'true';
1208
1255
  }
1209
- if (this.#optionsElement && this.#activeOption?.id) {
1210
- this.#optionsElement.ariaActivedescendant = this.#activeOption.id;
1211
- }
1212
- if (this.#previouslyActiveOption &&
1213
- !this.#previouslyActiveOption.disabled &&
1214
- this.#optionsElement) {
1215
- this.#previouslyActiveOption.privateActive = true;
1216
- this.#previouslyActiveOption.privateTooltipOpen =
1217
- this.privateOpenedViaKeyboard;
1218
- this.#optionsElement.ariaActivedescendant =
1219
- this.#previouslyActiveOption.id;
1220
- }
1221
- else if (this.#firstEnabledOption && this.#optionsElement) {
1222
- this.#firstEnabledOption.privateActive = true;
1223
- this.#firstEnabledOption.privateTooltipOpen =
1224
- this.privateOpenedViaKeyboard;
1225
- this.#previouslyActiveOption = this.#firstEnabledOption;
1226
- this.#optionsElement.ariaActivedescendant = this.#firstEnabledOption.id;
1256
+ if (this.#isSubMenu && this.#parentOption) {
1257
+ this.#parentOption.ariaExpanded = 'true';
1227
1258
  }
1228
- else if (this.#optionsElement) {
1229
- this.#optionsElement.ariaActivedescendant = '';
1259
+ else if (!this.#isSubMenu && this.#targetElement) {
1260
+ this.#targetElement.ariaExpanded = 'true';
1230
1261
  }
1231
1262
  if (this.#targetElement && this.#defaultSlotElementRef.value) {
1232
1263
  this.#cleanUpFloatingUi = autoUpdate(this.#targetElement, this.#defaultSlotElementRef.value, () => {
@@ -35,6 +35,7 @@ export default [
35
35
  padding-block-end: 0;
36
36
  padding-block-start: var(--glide-core-spacing-base-xxxs);
37
37
  padding-inline: var(--glide-core-spacing-base-xxxs);
38
+ position: absolute;
38
39
 
39
40
  /*
40
41
  This little hack replaces "padding-block-end", which the last option overlaps
@@ -11,7 +11,6 @@ import { ifDefined } from 'lit/directives/if-defined.js';
11
11
  import packageJson from '../package.json' with { type: 'json' };
12
12
  import styles from './modal.icon-button.styles.js';
13
13
  import assertSlot from './library/assert-slot.js';
14
- import shadowRootMode from './library/shadow-root-mode.js';
15
14
  import final from './library/final.js';
16
15
  import required from './library/required.js';
17
16
  /**
@@ -27,10 +26,12 @@ let ModalIconButton = class ModalIconButton extends LitElement {
27
26
  super(...arguments);
28
27
  this.version = packageJson.version;
29
28
  }
29
+ /* c8 ignore start */
30
30
  static { this.shadowRootOptions = {
31
31
  ...LitElement.shadowRootOptions,
32
- mode: shadowRootMode,
32
+ mode: window.navigator.webdriver ? 'open' : 'closed',
33
33
  }; }
34
+ /* c8 ignore end */
34
35
  static { this.styles = styles; }
35
36
  render() {
36
37
  return html `
package/dist/modal.js CHANGED
@@ -18,7 +18,6 @@ import Tooltip from './tooltip.js';
18
18
  import styles from './modal.styles.js';
19
19
  import xIcon from './icons/x.js';
20
20
  import assertSlot from './library/assert-slot.js';
21
- import shadowRootMode from './library/shadow-root-mode.js';
22
21
  import severityInformationalIcon from './icons/severity-informational.js';
23
22
  import severityMediumIcon from './icons/severity-medium.js';
24
23
  import severityCriticalIcon from './icons/severity-critical.js';
@@ -107,10 +106,12 @@ let Modal = class Modal extends LitElement {
107
106
  });
108
107
  };
109
108
  }
109
+ /* c8 ignore start */
110
110
  static { this.shadowRootOptions = {
111
111
  ...LitElement.shadowRootOptions,
112
- mode: shadowRootMode,
112
+ mode: window.navigator.webdriver ? 'open' : 'closed',
113
113
  }; }
114
+ /* c8 ignore end */
114
115
  static { this.styles = styles; }
115
116
  /**
116
117
  * @default false
package/dist/option.d.ts CHANGED
@@ -28,6 +28,11 @@ declare global {
28
28
  * @slot {Element | Text} [content] - This is the unhappy path. It's the escape hatch where you can render arbitrary content and lay it out however you need to. If you go this route, `slot="icon"` and `slot="submenu"` will become unavailable. And the `label` and `description` attributes won't be rendered. The `label` attribute is still required. We'll show it in a tooltip when your content overflows. If you need a second line of text in the tooltip, provide you can provide it via the `description` attribute.
29
29
  * @slot {Element} [icon]
30
30
  * @slot {Menu} [submenu]
31
+ *
32
+ * @fires {Event} deselected
33
+ * @fires {Event} disabled
34
+ * @fires {Event} enabled
35
+ * @fires {Event} selected
31
36
  */
32
37
  export default class Option extends LitElement {
33
38
  #private;
package/dist/option.js CHANGED
@@ -15,7 +15,6 @@ import packageJson from '../package.json' with { type: 'json' };
15
15
  import styles from './option.styles.js';
16
16
  import assertSlot from './library/assert-slot.js';
17
17
  import checkedIcon from './icons/checked.js';
18
- import shadowRootMode from './library/shadow-root-mode.js';
19
18
  import final from './library/final.js';
20
19
  import uniqueId from './library/unique-id.js';
21
20
  import Menu from './menu.js';
@@ -45,6 +44,11 @@ import required from './library/required.js';
45
44
  * @slot {Element | Text} [content] - This is the unhappy path. It's the escape hatch where you can render arbitrary content and lay it out however you need to. If you go this route, `slot="icon"` and `slot="submenu"` will become unavailable. And the `label` and `description` attributes won't be rendered. The `label` attribute is still required. We'll show it in a tooltip when your content overflows. If you need a second line of text in the tooltip, provide you can provide it via the `description` attribute.
46
45
  * @slot {Element} [icon]
47
46
  * @slot {Menu} [submenu]
47
+ *
48
+ * @fires {Event} deselected
49
+ * @fires {Event} disabled
50
+ * @fires {Event} enabled
51
+ * @fires {Event} selected
48
52
  */
49
53
  let Option = class Option extends LitElement {
50
54
  constructor() {
@@ -61,8 +65,8 @@ let Option = class Option extends LitElement {
61
65
  this.hasIconSlot = false;
62
66
  this.hasSubMenuSlot = false;
63
67
  this.isContentSlotOverflow = false;
64
- // Set in `#onSubmenuToggle()`. Used to toggle the sub-Menu's target as open
65
- // or closed visually.
68
+ // Set in `#onSubmenuToggle()`. Used to toggle the sub-Menu's target as open or
69
+ // closed visually.
66
70
  this.isSubmenuOpen = false;
67
71
  this.#containerElementRef = createRef();
68
72
  this.#contentSlotElementRef = createRef();
@@ -73,10 +77,12 @@ let Option = class Option extends LitElement {
73
77
  this.#labelElementRef = createRef();
74
78
  this.#tooltipElementRef = createRef();
75
79
  }
80
+ /* c8 ignore start */
76
81
  static { this.shadowRootOptions = {
77
82
  ...LitElement.shadowRootOptions,
78
- mode: shadowRootMode,
83
+ mode: window.navigator.webdriver ? 'open' : 'closed',
79
84
  }; }
85
+ /* c8 ignore end */
80
86
  static { this.styles = styles; }
81
87
  // Consumers may chose not to take the happy path and instead use the "content"
82
88
  // slot. In that case, we don't render `label`. But `label` still needs to be
@@ -116,9 +122,15 @@ let Option = class Option extends LitElement {
116
122
  return this.#isDisabled;
117
123
  }
118
124
  set disabled(isDisabled) {
125
+ const hasChanged = isDisabled !== this.#isDisabled;
119
126
  this.#isDisabled = isDisabled;
120
127
  this.ariaDisabled = isDisabled ? 'true' : 'false';
121
- this.dispatchEvent(new Event('private-disabled-change', { bubbles: true }));
128
+ if (hasChanged && isDisabled) {
129
+ this.dispatchEvent(new Event('disabled', { bubbles: true }));
130
+ }
131
+ else if (hasChanged) {
132
+ this.dispatchEvent(new Event('enabled', { bubbles: true }));
133
+ }
122
134
  }
123
135
  get privateActive() {
124
136
  return this.#isActive;
@@ -142,8 +154,19 @@ let Option = class Option extends LitElement {
142
154
  return this.#isSelected;
143
155
  }
144
156
  set selected(isSelected) {
157
+ const hasChanged = isSelected !== this.#isSelected;
145
158
  this.ariaSelected = isSelected.toString();
146
159
  this.#isSelected = isSelected;
160
+ if (hasChanged && isSelected) {
161
+ this.dispatchEvent(new Event('selected', {
162
+ bubbles: true,
163
+ }));
164
+ }
165
+ else if (hasChanged) {
166
+ this.dispatchEvent(new Event('deselected', {
167
+ bubbles: true,
168
+ }));
169
+ }
147
170
  }
148
171
  click() {
149
172
  // You'd think this condition wouldn't be needed because `#onTooltipClick()` has a
package/dist/options.d.ts CHANGED
@@ -20,6 +20,8 @@ declare global {
20
20
  * @attr {string} [version]
21
21
  *
22
22
  * @slot {Option | Text}
23
+ *
24
+ * @fires {Event} slotchange
23
25
  */
24
26
  export default class Options extends LitElement {
25
27
  #private;
@@ -9,7 +9,6 @@ import { customElement, property } from 'lit/decorators.js';
9
9
  import { classMap } from 'lit/directives/class-map.js';
10
10
  import packageJson from '../package.json' with { type: 'json' };
11
11
  import styles from './options.group.styles.js';
12
- import shadowRootMode from './library/shadow-root-mode.js';
13
12
  import final from './library/final.js';
14
13
  import assertSlot from './library/assert-slot.js';
15
14
  import Option from './option.js';
@@ -31,10 +30,12 @@ let OptionsGroup = class OptionsGroup extends LitElement {
31
30
  this.role = 'group';
32
31
  this.version = packageJson.version;
33
32
  }
33
+ /* c8 ignore start */
34
34
  static { this.shadowRootOptions = {
35
35
  ...LitElement.shadowRootOptions,
36
- mode: shadowRootMode,
36
+ mode: window.navigator.webdriver ? 'open' : 'closed',
37
37
  }; }
38
+ /* c8 ignore end */
38
39
  static { this.styles = styles; }
39
40
  /**
40
41
  * @default undefined
package/dist/options.js CHANGED
@@ -5,14 +5,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { html, LitElement } from 'lit';
8
- import { classMap } from 'lit/directives/class-map.js';
9
8
  import { customElement, property } from 'lit/decorators.js';
10
9
  import { map } from 'lit/directives/map.js';
11
10
  import { when } from 'lit/directives/when.js';
12
11
  import { range } from 'lit/directives/range.js';
13
12
  import packageJson from '../package.json' with { type: 'json' };
14
13
  import styles from './options.styles.js';
15
- import shadowRootMode from './library/shadow-root-mode.js';
16
14
  import final from './library/final.js';
17
15
  import uniqueId from './library/unique-id.js';
18
16
  import assertSlot from './library/assert-slot.js';
@@ -47,6 +45,8 @@ import OptionsGroup from './options.group.js';
47
45
  * @attr {string} [version]
48
46
  *
49
47
  * @slot {Option | Text}
48
+ *
49
+ * @fires {Event} slotchange
50
50
  */
51
51
  let Options = class Options extends LitElement {
52
52
  constructor() {
@@ -66,10 +66,12 @@ let Options = class Options extends LitElement {
66
66
  this.tabIndex = -1;
67
67
  this.version = packageJson.version;
68
68
  }
69
+ /* c8 ignore start */
69
70
  static { this.shadowRootOptions = {
70
71
  ...LitElement.shadowRootOptions,
71
- mode: shadowRootMode,
72
+ mode: window.navigator.webdriver ? 'open' : 'closed',
72
73
  }; }
74
+ /* c8 ignore end */
73
75
  static { this.styles = styles; }
74
76
  render() {
75
77
  // Without `role="none"` VoiceOver doesn't announce how many options are available.
@@ -78,10 +80,7 @@ let Options = class Options extends LitElement {
78
80
  // `role="option"`).
79
81
  return html `<div class="component" role="none">
80
82
  <slot
81
- class=${classMap({
82
- 'default-slot': true,
83
- loading: this.privateLoading,
84
- })}
83
+ ?hidden=${this.privateLoading}
85
84
  @slotchange=${this.#onDefaultSlotChange}
86
85
  ${assertSlot([OptionsGroup, Option, Text], true)}
87
86
  >
@@ -96,7 +95,7 @@ let Options = class Options extends LitElement {
96
95
  </div>`;
97
96
  }
98
97
  #onDefaultSlotChange() {
99
- this.dispatchEvent(new Event('private-slot-change', { bubbles: true }));
98
+ this.dispatchEvent(new Event('slotchange', { bubbles: true }));
100
99
  }
101
100
  };
102
101
  __decorate([
@@ -11,11 +11,5 @@ export default [
11
11
  */
12
12
  display: block;
13
13
  }
14
-
15
- .default-slot {
16
- &.loading {
17
- display: none;
18
- }
19
- }
20
14
  `,
21
15
  ];
package/dist/popover.js CHANGED
@@ -13,7 +13,6 @@ import { customElement, property, state } from 'lit/decorators.js';
13
13
  import packageJson from '../package.json' with { type: 'json' };
14
14
  import styles from './popover.styles.js';
15
15
  import assertSlot from './library/assert-slot.js';
16
- import shadowRootMode from './library/shadow-root-mode.js';
17
16
  import final from './library/final.js';
18
17
  /**
19
18
  * @attr {boolean} [disabled=false]
@@ -76,10 +75,12 @@ let Popover = class Popover extends LitElement {
76
75
  this.open = false;
77
76
  };
78
77
  }
78
+ /* c8 ignore start */
79
79
  static { this.shadowRootOptions = {
80
80
  ...LitElement.shadowRootOptions,
81
- mode: shadowRootMode,
81
+ mode: window.navigator.webdriver ? 'open' : 'closed',
82
82
  }; }
83
+ /* c8 ignore end */
83
84
  static { this.styles = styles; }
84
85
  /**
85
86
  * @default false
@@ -17,7 +17,6 @@ import packageJson from '../package.json' with { type: 'json' };
17
17
  import RadioGroupRadio from './radio-group.radio.js';
18
18
  import styles from './radio-group.styles.js';
19
19
  import assertSlot from './library/assert-slot.js';
20
- import shadowRootMode from './library/shadow-root-mode.js';
21
20
  import final from './library/final.js';
22
21
  import required from './library/required.js';
23
22
  /**
@@ -64,10 +63,12 @@ import required from './library/required.js';
64
63
  */
65
64
  let RadioGroup = class RadioGroup extends LitElement {
66
65
  static { this.formAssociated = true; }
66
+ /* c8 ignore start */
67
67
  static { this.shadowRootOptions = {
68
68
  ...LitElement.shadowRootOptions,
69
- mode: shadowRootMode,
69
+ mode: window.navigator.webdriver ? 'open' : 'closed',
70
70
  }; }
71
+ /* c8 ignore end */
71
72
  static { this.styles = styles; }
72
73
  /**
73
74
  * @default false
@@ -334,7 +335,7 @@ let RadioGroup = class RadioGroup extends LitElement {
334
335
  reportValidity() {
335
336
  this.isReportValidityOrSubmit = true;
336
337
  const isValid = this.#internals.reportValidity();
337
- // Ensures that getters referencing `this.validity.valid` are updated.
338
+ // Ensures getters referencing `this.validity.valid` re-run.
338
339
  this.requestUpdate();
339
340
  return isValid;
340
341
  }
@@ -9,7 +9,6 @@ import { classMap } from 'lit/directives/class-map.js';
9
9
  import { customElement, property, state } from 'lit/decorators.js';
10
10
  import packageJson from '../package.json' with { type: 'json' };
11
11
  import styles from './radio-group.radio.styles.js';
12
- import shadowRootMode from './library/shadow-root-mode.js';
13
12
  import final from './library/final.js';
14
13
  import required from './library/required.js';
15
14
  /**
@@ -34,10 +33,12 @@ let RadioGroupRadio = class RadioGroupRadio extends LitElement {
34
33
  this.#privateRequired = false;
35
34
  this.#value = '';
36
35
  }
36
+ /* c8 ignore start */
37
37
  static { this.shadowRootOptions = {
38
38
  ...LitElement.shadowRootOptions,
39
- mode: shadowRootMode,
39
+ mode: window.navigator.webdriver ? 'open' : 'closed',
40
40
  }; }
41
+ /* c8 ignore end */
41
42
  static { this.styles = styles; }
42
43
  /**
43
44
  * @default undefined