@pine-ds/core 3.29.0 → 3.30.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 (123) hide show
  1. package/components/form.js +1 -1
  2. package/components/pds-avatar.js +1 -1
  3. package/components/pds-checkbox2.js +1 -1
  4. package/components/pds-combobox.js +2 -2
  5. package/components/pds-input.js +1 -1
  6. package/components/pds-modal.js +1 -1
  7. package/components/pds-multiselect.js +1 -1
  8. package/components/pds-radio.js +1 -1
  9. package/components/pds-select.js +1 -1
  10. package/components/pds-switch.js +1 -1
  11. package/components/pds-textarea.js +1 -1
  12. package/dist/cjs/{form-CpM5Z8q7.js → form-MhwfKTuo.js} +11 -7
  13. package/dist/cjs/loader.cjs.js +1 -1
  14. package/dist/cjs/pds-avatar.cjs.entry.js +2 -2
  15. package/dist/cjs/pds-checkbox.cjs.entry.js +4 -4
  16. package/dist/cjs/pds-combobox.cjs.entry.js +20 -4
  17. package/dist/cjs/pds-input.cjs.entry.js +2 -2
  18. package/dist/cjs/pds-modal.cjs.entry.js +35 -5
  19. package/dist/cjs/pds-multiselect.cjs.entry.js +5 -5
  20. package/dist/cjs/pds-radio-group.cjs.entry.js +1 -1
  21. package/dist/cjs/pds-radio.cjs.entry.js +5 -5
  22. package/dist/cjs/pds-select.cjs.entry.js +7 -2
  23. package/dist/cjs/pds-switch.cjs.entry.js +4 -4
  24. package/dist/cjs/pds-textarea.cjs.entry.js +4 -4
  25. package/dist/cjs/pine-core.cjs.js +1 -1
  26. package/dist/collection/components/pds-avatar/pds-avatar.js +3 -2
  27. package/dist/collection/components/pds-checkbox/pds-checkbox.js +3 -3
  28. package/dist/collection/components/pds-combobox/pds-combobox.css +32 -0
  29. package/dist/collection/components/pds-combobox/pds-combobox.js +76 -3
  30. package/dist/collection/components/pds-combobox/stories/pds-combobox.stories.js +25 -0
  31. package/dist/collection/components/pds-input/pds-input.js +1 -1
  32. package/dist/collection/components/pds-modal/pds-modal.js +58 -5
  33. package/dist/collection/components/pds-modal/stories/pds-modal.stories.js +77 -0
  34. package/dist/collection/components/pds-multiselect/pds-multiselect.css +2 -2
  35. package/dist/collection/components/pds-multiselect/pds-multiselect.js +3 -3
  36. package/dist/collection/components/pds-radio/pds-radio.css +6 -0
  37. package/dist/collection/components/pds-radio/pds-radio.js +3 -3
  38. package/dist/collection/components/pds-select/pds-select.js +7 -2
  39. package/dist/collection/components/pds-switch/pds-switch.js +3 -3
  40. package/dist/collection/components/pds-textarea/pds-textarea.js +3 -3
  41. package/dist/collection/utils/form.js +11 -7
  42. package/dist/docs.json +107 -1
  43. package/dist/esm/{form-C3xDZBme.js → form-BuRbS7Hp.js} +11 -7
  44. package/dist/esm/loader.js +1 -1
  45. package/dist/esm/pds-avatar.entry.js +2 -2
  46. package/dist/esm/pds-checkbox.entry.js +4 -4
  47. package/dist/esm/pds-combobox.entry.js +20 -4
  48. package/dist/esm/pds-input.entry.js +2 -2
  49. package/dist/esm/pds-modal.entry.js +35 -5
  50. package/dist/esm/pds-multiselect.entry.js +5 -5
  51. package/dist/esm/pds-radio-group.entry.js +1 -1
  52. package/dist/esm/pds-radio.entry.js +5 -5
  53. package/dist/esm/pds-select.entry.js +7 -2
  54. package/dist/esm/pds-switch.entry.js +4 -4
  55. package/dist/esm/pds-textarea.entry.js +4 -4
  56. package/dist/esm/pine-core.js +1 -1
  57. package/dist/esm-es5/form-BuRbS7Hp.js +1 -0
  58. package/dist/esm-es5/loader.js +1 -1
  59. package/dist/esm-es5/pds-avatar.entry.js +1 -1
  60. package/dist/esm-es5/pds-checkbox.entry.js +1 -1
  61. package/dist/esm-es5/pds-combobox.entry.js +2 -2
  62. package/dist/esm-es5/pds-input.entry.js +1 -1
  63. package/dist/esm-es5/pds-modal.entry.js +1 -1
  64. package/dist/esm-es5/pds-multiselect.entry.js +1 -1
  65. package/dist/esm-es5/pds-radio-group.entry.js +1 -1
  66. package/dist/esm-es5/pds-radio.entry.js +1 -1
  67. package/dist/esm-es5/pds-select.entry.js +1 -1
  68. package/dist/esm-es5/pds-switch.entry.js +1 -1
  69. package/dist/esm-es5/pds-textarea.entry.js +1 -1
  70. package/dist/esm-es5/pine-core.js +1 -1
  71. package/dist/pine-core/p-1482053d.entry.js +1 -0
  72. package/dist/pine-core/p-1e87e72a.entry.js +1 -0
  73. package/dist/pine-core/{p-978b848b.system.entry.js → p-31679dbc.system.entry.js} +1 -1
  74. package/dist/pine-core/p-3c09f2d4.system.entry.js +1 -0
  75. package/dist/pine-core/{p-5f4d2c5c.system.entry.js → p-3cece186.system.entry.js} +1 -1
  76. package/dist/pine-core/p-43268325.system.entry.js +1 -0
  77. package/dist/pine-core/{p-092a72d2.system.entry.js → p-559042b7.system.entry.js} +1 -1
  78. package/dist/pine-core/p-5aa59b20.entry.js +2 -0
  79. package/dist/pine-core/{p-82af7d58.system.entry.js → p-69372e72.system.entry.js} +1 -1
  80. package/dist/pine-core/p-69e1a82b.system.entry.js +3 -0
  81. package/dist/pine-core/{p-02977c6f.entry.js → p-7d15be6a.entry.js} +1 -1
  82. package/dist/pine-core/p-809e0506.entry.js +1 -0
  83. package/dist/pine-core/{p-ba54470a.system.entry.js → p-86d12f3c.system.entry.js} +1 -1
  84. package/dist/pine-core/p-B1rytYxP.system.js +1 -1
  85. package/dist/pine-core/p-BuRbS7Hp.js +1 -0
  86. package/dist/pine-core/p-BxIzb-Wc.system.js +1 -0
  87. package/dist/pine-core/p-a07b331a.entry.js +1 -0
  88. package/dist/pine-core/p-a0edb4ad.entry.js +1 -0
  89. package/dist/pine-core/p-aa0753e1.entry.js +1 -0
  90. package/dist/pine-core/p-c625d5ea.system.entry.js +1 -0
  91. package/dist/pine-core/{p-dc48f603.entry.js → p-caf73216.entry.js} +1 -1
  92. package/dist/pine-core/{p-e81b4d04.system.entry.js → p-d00f54f0.system.entry.js} +1 -1
  93. package/dist/pine-core/p-dfa70d20.entry.js +1 -0
  94. package/dist/pine-core/p-fb17773a.system.entry.js +1 -0
  95. package/dist/pine-core/p-fef6694d.entry.js +1 -0
  96. package/dist/pine-core/pine-core.esm.js +1 -1
  97. package/dist/types/components/pds-avatar/pds-avatar.d.ts +1 -0
  98. package/dist/types/components/pds-combobox/pds-combobox.d.ts +14 -0
  99. package/dist/types/components/pds-modal/pds-modal.d.ts +10 -0
  100. package/dist/types/components/pds-select/pds-select.d.ts +1 -0
  101. package/dist/types/components.d.ts +38 -0
  102. package/dist/types/utils/form.d.ts +4 -1
  103. package/dist/vscode.html-data.json +10909 -0
  104. package/hydrate/index.js +96 -36
  105. package/hydrate/index.mjs +96 -36
  106. package/package.json +2 -2
  107. package/dist/esm-es5/form-C3xDZBme.js +0 -1
  108. package/dist/pine-core/p-054f7011.entry.js +0 -1
  109. package/dist/pine-core/p-0d6daa35.entry.js +0 -1
  110. package/dist/pine-core/p-2a74b396.entry.js +0 -1
  111. package/dist/pine-core/p-69e47ffc.system.entry.js +0 -1
  112. package/dist/pine-core/p-6fb68ad1.entry.js +0 -1
  113. package/dist/pine-core/p-72a1b563.system.entry.js +0 -1
  114. package/dist/pine-core/p-7d15b65e.entry.js +0 -1
  115. package/dist/pine-core/p-902b5790.entry.js +0 -2
  116. package/dist/pine-core/p-9ba86e34.entry.js +0 -1
  117. package/dist/pine-core/p-9f041a63.system.entry.js +0 -3
  118. package/dist/pine-core/p-B1u-QvFn.system.js +0 -1
  119. package/dist/pine-core/p-C3xDZBme.js +0 -1
  120. package/dist/pine-core/p-ce715993.entry.js +0 -1
  121. package/dist/pine-core/p-e0fb06ea.system.entry.js +0 -1
  122. package/dist/pine-core/p-e4b69d10.entry.js +0 -1
  123. package/dist/pine-core/p-fb10caff.system.entry.js +0 -1
@@ -22,6 +22,16 @@ export class PdsModal {
22
22
  * @default true
23
23
  */
24
24
  this.scrollable = true;
25
+ /**
26
+ * Whether the modal opens outside the browser top layer as a non-modal dialog.
27
+ * When `true` it opens with `dialog.show()` instead of `dialog.showModal()`, so
28
+ * overlays rendered elsewhere in the DOM (file pickers, editor menus) can display
29
+ * above it via `z-index`. The page is not made inert and focus is not trapped in
30
+ * this mode. Read when the modal opens; changing it while the modal is open is
31
+ * not supported.
32
+ * @default false
33
+ */
34
+ this.disableTopLayer = false;
25
35
  /**
26
36
  * Stores the list of focusable elements in the modal
27
37
  */
@@ -43,6 +53,13 @@ export class PdsModal {
43
53
  return;
44
54
  // Handle Escape key to close the modal
45
55
  if (e.key === 'Escape') {
56
+ // In non-top-layer mode, focus can move into an overlay stacked above the
57
+ // modal (the reason disableTopLayer exists). If that overlay owns focus,
58
+ // leave Escape to it rather than dismissing this modal out from under it.
59
+ const active = document.activeElement;
60
+ if (this.disableTopLayer && active && active !== document.body && !this.el.contains(active)) {
61
+ return;
62
+ }
46
63
  // Always prevent native dialog close behavior
47
64
  e.preventDefault();
48
65
  // Only close if backdropDismiss is enabled and this is the innermost modal
@@ -53,6 +70,11 @@ export class PdsModal {
53
70
  }
54
71
  // Handle Tab key for focus trapping
55
72
  if (e.key === 'Tab') {
73
+ // In non-top-layer mode the modal is deliberately not focus-isolated: focus
74
+ // must be able to leave it into overlays stacked above (the whole point of
75
+ // disableTopLayer), so do not trap Tab here.
76
+ if (this.disableTopLayer)
77
+ return;
56
78
  // If there are no focusable elements, do nothing
57
79
  if (this.focusableElements.length === 0)
58
80
  return;
@@ -170,8 +192,16 @@ export class PdsModal {
170
192
  try {
171
193
  // Store the currently focused element to restore focus when modal closes
172
194
  this.previousActiveElement = document.activeElement;
173
- // Use native dialog showModal method which makes the rest of the page inert
174
- this.modalRef.showModal();
195
+ // showModal() promotes the dialog to the browser top layer (and makes the
196
+ // rest of the page inert), which prevents any overlay outside the dialog
197
+ // from ever painting above it. show() opens a non-modal dialog that stays
198
+ // in the normal stacking context so those overlays can stack above it.
199
+ if (this.disableTopLayer) {
200
+ this.modalRef.show();
201
+ }
202
+ else {
203
+ this.modalRef.showModal();
204
+ }
175
205
  this.open = true;
176
206
  // Update focusable elements and set initial focus
177
207
  // Using a longer timeout to ensure all components are fully rendered
@@ -233,14 +263,14 @@ export class PdsModal {
233
263
  return thisZIndex === maxZIndex;
234
264
  }
235
265
  render() {
236
- return (h("dialog", { key: 'cf2710a889d8f0e80a3551088ab38a88dc44ad1e', class: {
266
+ return (h("dialog", { key: 'd345ea0a585213fd7879a2fe27071705fbdba79b', class: {
237
267
  'pds-modal__backdrop': true,
238
268
  'open': this.open
239
- }, "aria-modal": "true", "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick }, h("div", { key: 'a06d017153e67ad38bc1ced56ba22aeade977aaf', class: {
269
+ }, "aria-modal": this.disableTopLayer ? 'false' : 'true', "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick }, h("div", { key: '34c9a222d58ffd9736a36ab6848679186167e878', class: {
240
270
  'pds-modal': true,
241
271
  [`pds-modal--${this.size}`]: true,
242
272
  'pds-modal--scrollable': this.scrollable
243
- }, part: "modal" }, h("slot", { key: 'd995c586809a4940c091912244a86cb4605a961c' }))));
273
+ }, part: "modal" }, h("slot", { key: '4eef9c3001b6a7c972fa8e49fa3f0b140c7fe7ce' }))));
244
274
  }
245
275
  static get is() { return "pds-modal"; }
246
276
  static get originalStyleUrls() {
@@ -365,6 +395,29 @@ export class PdsModal {
365
395
  "reflect": false,
366
396
  "attribute": "scrollable",
367
397
  "defaultValue": "true"
398
+ },
399
+ "disableTopLayer": {
400
+ "type": "boolean",
401
+ "mutable": false,
402
+ "complexType": {
403
+ "original": "boolean",
404
+ "resolved": "boolean",
405
+ "references": {}
406
+ },
407
+ "required": false,
408
+ "optional": false,
409
+ "docs": {
410
+ "tags": [{
411
+ "name": "default",
412
+ "text": "false"
413
+ }],
414
+ "text": "Whether the modal opens outside the browser top layer as a non-modal dialog.\nWhen `true` it opens with `dialog.show()` instead of `dialog.showModal()`, so\noverlays rendered elsewhere in the DOM (file pickers, editor menus) can display\nabove it via `z-index`. The page is not made inert and focus is not trapped in\nthis mode. Read when the modal opens; changing it while the modal is open is\nnot supported."
415
+ },
416
+ "getter": false,
417
+ "setter": false,
418
+ "reflect": false,
419
+ "attribute": "disable-top-layer",
420
+ "defaultValue": "false"
368
421
  }
369
422
  };
370
423
  }
@@ -8,6 +8,7 @@ export default {
8
8
  args: {
9
9
  backdropDismiss: true,
10
10
  componentId: 'modal-demo',
11
+ disableTopLayer: false,
11
12
  open: false,
12
13
  scrollable: true,
13
14
  size: 'md',
@@ -26,6 +27,7 @@ const BaseTemplate = (args) => html`
26
27
  component-id="${args.componentId}"
27
28
  size="${args.size}"
28
29
  ?backdrop-dismiss=${args.backdropDismiss}
30
+ ?disable-top-layer=${args.disableTopLayer}
29
31
  scrollable="${args.scrollable}"
30
32
  ?open=${args.open}
31
33
  key="${args.scrollable ? 'scrollable' : 'non-scrollable'}"
@@ -652,3 +654,78 @@ export const CustomBorders = CustomBordersTemplate.bind({});
652
654
  CustomBorders.args = {
653
655
  componentId: 'custom-borders-modal',
654
656
  };
657
+
658
+ // Demonstrates `disable-top-layer`: the modal opens as a non-modal dialog
659
+ // (dialog.show()) so an overlay appended to document.body can display above it.
660
+ // With the default top-layer behavior the same overlay would render behind the
661
+ // modal and be unreachable.
662
+ const DisableTopLayerTemplate = (args) => html`
663
+ <div style="padding: 1rem;">
664
+ <pds-button onClick="document.querySelector('#${args.componentId}').open = true">
665
+ Open Modal (disable-top-layer)
666
+ </pds-button>
667
+
668
+ <pds-modal
669
+ id="${args.componentId}"
670
+ component-id="${args.componentId}"
671
+ size="${args.size}"
672
+ ?backdrop-dismiss=${args.backdropDismiss}
673
+ ?disable-top-layer=${args.disableTopLayer}
674
+ ?open=${args.open}
675
+ >
676
+ <pds-modal-header>
677
+ <pds-box direction="column" fit padding="md">
678
+ <pds-box align-items="center" fit justify-content="space-between">
679
+ <pds-text tag="h2" size="h3">Modal Title</pds-text>
680
+ <pds-button
681
+ class="pds-modal__close"
682
+ variant="unstyled"
683
+ icon-only="true"
684
+ onclick="document.querySelector('#${args.componentId}').open = false"
685
+ aria-label="Close modal"
686
+ part="close-button"
687
+ >
688
+ <pds-icon slot="start" name="remove" aria-hidden="true"></pds-icon>
689
+ </pds-button>
690
+ </pds-box>
691
+ </pds-box>
692
+ </pds-modal-header>
693
+
694
+ <pds-modal-content>
695
+ <pds-box fit direction="column" padding-inline-start="md" padding-inline-end="md" gap="sm">
696
+ <p>
697
+ This modal uses <code>disable-top-layer</code>, so it renders in the normal stacking
698
+ context instead of the browser top layer.
699
+ </p>
700
+ <p>
701
+ An overlay appended to <code>document.body</code> with a higher <code>z-index</code> can
702
+ therefore display above it &mdash; useful for file pickers, rich-text editor menus, and
703
+ other body-mounted overlays that would otherwise be trapped behind a top-layer dialog.
704
+ </p>
705
+ <pds-button
706
+ variant="secondary"
707
+ onclick="(function(){var o=document.createElement('button');o.type='button';o.textContent='Overlay above the modal — focusable, keyboard-dismissable. Click or press Enter.';o.setAttribute('style','position:fixed;inset:auto 2rem 2rem auto;max-width:20rem;padding:1rem;border:0;text-align:start;border-radius:8px;background:#111;color:#fff;z-index:2147483647;cursor:pointer;box-shadow:0 8px 24px rgba(0,0,0,.4)');var modal=document.querySelector('#${args.componentId}');var remove=function(){o.remove()};o.onclick=remove;o.addEventListener('keydown',function(e){if(e.key==='Escape')remove()});if(modal)modal.addEventListener('pdsModalClose',remove,{once:true});document.body.appendChild(o);o.focus();})()"
708
+ >
709
+ Show overlay above modal
710
+ </pds-button>
711
+ </pds-box>
712
+ </pds-modal-content>
713
+
714
+ <pds-modal-footer>
715
+ <pds-box justify-content="end" fit padding="md" gap="sm">
716
+ <pds-button variant="secondary" onclick="document.querySelector('#${args.componentId}').open = false">
717
+ Close
718
+ </pds-button>
719
+ </pds-box>
720
+ </pds-modal-footer>
721
+ </pds-modal>
722
+ </div>
723
+ `;
724
+
725
+ export const DisableTopLayer = DisableTopLayerTemplate.bind({});
726
+ DisableTopLayer.args = {
727
+ componentId: 'disable-top-layer-modal',
728
+ disableTopLayer: true,
729
+ size: 'md',
730
+ open: false,
731
+ };
@@ -291,7 +291,7 @@
291
291
 
292
292
  .pds-multiselect__helper {
293
293
  color: var(--pine-color-text-message);
294
- font: var(--pine-typography-body-sm);
294
+ font: var(--pine-typography-body-sm-medium);
295
295
  margin-block-start: var(--pine-dimension-2xs);
296
296
  margin-inline-start: var(--pine-dimension-none);
297
297
  }
@@ -300,7 +300,7 @@
300
300
  align-items: flex-start;
301
301
  color: var(--pine-color-text-message-danger);
302
302
  display: flex;
303
- font: var(--pine-typography-body-sm);
303
+ font: var(--pine-typography-body-sm-medium);
304
304
  gap: var(--pine-dimension-2xs);
305
305
  margin-block-start: var(--pine-dimension-2xs);
306
306
  margin-inline-start: var(--pine-dimension-none);
@@ -1031,16 +1031,16 @@ export class PdsMultiselect {
1031
1031
  'pds-multiselect__trigger--invalid': this.invalid || !!this.errorMessage,
1032
1032
  'pds-multiselect__trigger--has-value': hasSelections,
1033
1033
  'pds-multiselect__trigger--pill-inline': true,
1034
- }, onClick: this.handleTriggerClick }, this.renderInlinePills(), h("button", { ref: el => (this.triggerEl = el), type: "button", id: this.componentId, part: "pill-toggle", class: "pds-multiselect__pill-toggle", disabled: this.disabled, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-labelledby": this.label ? `${this.componentId}-label` : undefined, "aria-required": this.required ? 'true' : undefined, "aria-describedby": assignDescription(this.componentId, this.invalid || !!this.errorMessage, this.errorMessage || this.helperMessage), "aria-invalid": this.invalid || !!this.errorMessage ? 'true' : undefined, onKeyDown: this.handleTriggerKeyDown }, h("pds-icon", { class: "pds-multiselect__icon", icon: enlarge })))) : (h("button", { ref: el => (this.triggerEl = el), type: "button", part: "trigger", class: {
1034
+ }, onClick: this.handleTriggerClick }, this.renderInlinePills(), h("button", { ref: el => (this.triggerEl = el), type: "button", id: this.componentId, part: "pill-toggle", class: "pds-multiselect__pill-toggle", disabled: this.disabled, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-labelledby": this.label ? `${this.componentId}-label` : undefined, "aria-required": this.required ? 'true' : undefined, "aria-describedby": assignDescription(this.componentId, this.invalid || !!this.errorMessage, this.helperMessage, this.errorMessage), "aria-invalid": this.invalid || !!this.errorMessage ? 'true' : undefined, onKeyDown: this.handleTriggerKeyDown }, h("pds-icon", { class: "pds-multiselect__icon", icon: enlarge })))) : (h("button", { ref: el => (this.triggerEl = el), type: "button", part: "trigger", class: {
1035
1035
  'pds-multiselect__trigger': true,
1036
1036
  'pds-multiselect__trigger--open': this.isOpen,
1037
1037
  'pds-multiselect__trigger--disabled': this.disabled,
1038
1038
  'pds-multiselect__trigger--invalid': this.invalid || !!this.errorMessage,
1039
1039
  'pds-multiselect__trigger--has-value': hasSelections,
1040
- }, id: this.componentId, disabled: this.disabled, "aria-required": this.required ? 'true' : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-describedby": assignDescription(this.componentId, this.invalid || !!this.errorMessage, this.errorMessage || this.helperMessage), "aria-invalid": this.invalid || !!this.errorMessage ? 'true' : undefined, onClick: this.handleTriggerClick, onKeyDown: this.handleTriggerKeyDown }, h("span", { class: {
1040
+ }, id: this.componentId, disabled: this.disabled, "aria-required": this.required ? 'true' : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-describedby": assignDescription(this.componentId, this.invalid || !!this.errorMessage, this.helperMessage, this.errorMessage), "aria-invalid": this.invalid || !!this.errorMessage ? 'true' : undefined, onClick: this.handleTriggerClick, onKeyDown: this.handleTriggerKeyDown }, h("span", { class: {
1041
1041
  'pds-multiselect__trigger-text': true,
1042
1042
  'pds-multiselect__trigger-text--placeholder': !hasSelections || (this.selectedDisplay === 'pill' && this.pillPosition === 'below'),
1043
- } }, this.getTriggerText()), h("pds-icon", { class: "pds-multiselect__icon", icon: enlarge }))), this.renderDropdown()), this.selectedDisplay === 'pill' && this.pillPosition === 'below' && this.renderBelowPills(), h("span", { key: '973443f9eb29dcf7fb1bde3f9101ac02967a8fd9', class: "visually-hidden", "aria-live": "polite", "aria-atomic": "true" }, this.removalAnnouncement), this.helperMessage && !(this.errorMessage && this.errorMessage.length > 0) && (h("p", { key: '568aab2b278abb7f4426d84ee926b4883375fb11', class: "pds-multiselect__helper", id: messageId(this.componentId, 'helper') }, this.helperMessage)), this.errorMessage && (h("p", { key: 'daddf192efd47df8c15267b9896913b926e02544', class: "pds-multiselect__error", id: messageId(this.componentId, 'error') }, h("pds-icon", { key: 'd0c72ab75a8e02e61c09e980ae817790804a776a', icon: danger, size: "small" }), this.errorMessage)), h("div", { key: '0453db2fca10b3573b1dd244875f90c856c8887b', style: { display: 'none' } }, h("slot", { key: 'a612741afc9545e5e50c47d27c3c27c1d7cade3c' })))));
1043
+ } }, this.getTriggerText()), h("pds-icon", { class: "pds-multiselect__icon", icon: enlarge }))), this.renderDropdown()), this.selectedDisplay === 'pill' && this.pillPosition === 'below' && this.renderBelowPills(), h("span", { key: '199804f5352818d32542864e3a61f7a9364cc2e3', class: "visually-hidden", "aria-live": "polite", "aria-atomic": "true" }, this.removalAnnouncement), this.helperMessage && !(this.errorMessage && this.errorMessage.length > 0) && (h("p", { key: 'e6855ff0aaf18f59dced2df5d636cadb4d47427b', class: "pds-multiselect__helper", id: messageId(this.componentId, 'helper') }, this.helperMessage)), this.errorMessage && (h("p", { key: '29eae23d8db31b9b9c292d5cd1dc5f641613ed32', class: "pds-multiselect__error", id: messageId(this.componentId, 'error') }, h("pds-icon", { key: 'aad6fd945998957cd28ab6eb97c4d80f0a732ea5', icon: danger, size: "small" }), this.errorMessage)), h("div", { key: '7d0d56a1e097cfc0c7396c1957d16014cb071b1e', style: { display: 'none' } }, h("slot", { key: '6e7fc9dbdab3977248439aa5ea14f13e0f45bbc6' })))));
1044
1044
  }
1045
1045
  static get is() { return "pds-multiselect"; }
1046
1046
  static get encapsulation() { return "shadow"; }
@@ -59,6 +59,9 @@
59
59
  color: var(--pine-color-text-label-disabled);
60
60
  cursor: not-allowed;
61
61
  }
62
+ :host(.has-border):has(input:disabled) label::after {
63
+ cursor: not-allowed;
64
+ }
62
65
  :host(.has-border).is-invalid .pds-radio__message--error {
63
66
  color: var(--pine-color-text-message-danger);
64
67
  }
@@ -255,6 +258,9 @@ label:has(input:disabled) {
255
258
  color: var(--pine-color-text-label-disabled);
256
259
  cursor: not-allowed;
257
260
  }
261
+ :host(.has-image).is-disabled label::after {
262
+ cursor: not-allowed;
263
+ }
258
264
  :host(.has-image).is-disabled .pds-radio__message {
259
265
  color: var(--pine-color-text-label-disabled);
260
266
  }
@@ -80,11 +80,11 @@ export class PdsRadio {
80
80
  }
81
81
  render() {
82
82
  const renderLabelAndMessages = () => [
83
- h("label", { htmlFor: this.componentId, key: `${this.componentId}-label` }, h("input", { key: '75aca490be2db050f66ddd9cb45ce122bea4db53', "aria-describedby": assignDescription(this.componentId, this.invalid, this.helperMessage), "aria-invalid": this.invalid ? "true" : undefined, type: "radio", id: this.componentId, name: this.name, value: this.value, checked: this.checked, required: this.required, disabled: this.disabled, onChange: this.handleRadioChange, class: this.hasImage() ? 'visually-hidden' : '' }), h("span", { key: '1bbc7398d3be35e301526b0f842274a3f1db4457', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)),
83
+ h("label", { htmlFor: this.componentId, key: `${this.componentId}-label` }, h("input", { key: 'ca6cb85ace418259f13b6be404e125d4c1c3a778', "aria-describedby": assignDescription(this.componentId, this.invalid, this.helperMessage, this.errorMessage), "aria-invalid": this.invalid ? "true" : undefined, type: "radio", id: this.componentId, name: this.name, value: this.value, checked: this.checked, required: this.required, disabled: this.disabled, onChange: this.handleRadioChange, class: this.hasImage() ? 'visually-hidden' : '' }), h("span", { key: '968c3dcc6b3e18a177db0570a9104b4755827a3e', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)),
84
84
  this.helperMessage && (h("div", { key: `${this.componentId}-helper`, class: 'pds-radio__message', id: messageId(this.componentId, 'helper') }, this.helperMessage)),
85
- this.errorMessage && (h("div", { key: `${this.componentId}-error`, class: `pds-radio__message pds-radio__message--error`, id: messageId(this.componentId, 'error'), "aria-live": "assertive" }, h("pds-icon", { key: 'e1a1228fa52e68623098f09493d93aa03d2e65c4', icon: danger, size: "small" }), this.errorMessage))
85
+ this.errorMessage && (h("div", { key: `${this.componentId}-error`, class: `pds-radio__message pds-radio__message--error`, id: messageId(this.componentId, 'error'), "aria-live": "assertive" }, h("pds-icon", { key: '2dec1d43ade25a8ff70301fb661174eff5efadeb', icon: danger, size: "small" }), this.errorMessage))
86
86
  ];
87
- return (h(Host, { key: '60fabeef5778fd30e46cfd23d05684e1743bc57f', class: this.classNames() }, this.hasImage() && (h("div", { key: '9ba3e60182a68cc8e996623cf46e17001ff451f6', class: "pds-radio__image-container", part: "image-container" }, h("slot", { key: 'bf8927129eb68ee7ed07a1f8f8727f013254e133', name: "image", onSlotchange: () => (this._hasImage = this.hasImageSlot()) }))), this.hasImage() ? (h("div", { class: "pds-radio__content-wrapper" }, renderLabelAndMessages())) : (renderLabelAndMessages())));
87
+ return (h(Host, { key: '75563601db01d7db2a4c4e74364c0bb2d57ac9ae', class: this.classNames() }, this.hasImage() && (h("div", { key: '120ca4dd9d3828cda49c0f7539f429e2aaa89329', class: "pds-radio__image-container", part: "image-container" }, h("slot", { key: 'a03496e1acc3799740aa20342408de97991cad68', name: "image", onSlotchange: () => (this._hasImage = this.hasImageSlot()) }))), this.hasImage() ? (h("div", { class: "pds-radio__content-wrapper" }, renderLabelAndMessages())) : (renderLabelAndMessages())));
88
88
  }
89
89
  static get is() { return "pds-radio"; }
90
90
  static get encapsulation() { return "scoped"; }
@@ -1,5 +1,5 @@
1
1
  import { Host, h } from "@stencil/core";
2
- import { isSpecTest, messageId, exposeTypeProperty } from "../../utils/form";
2
+ import { assignDescription, isSpecTest, messageId, exposeTypeProperty } from "../../utils/form";
3
3
  import { danger, enlarge } from "@pine-ds/icons/icons";
4
4
  /**
5
5
  * @slot action - Content to be displayed in the label area, typically for help icons or links
@@ -140,6 +140,11 @@ export class PdsSelect {
140
140
  return null;
141
141
  return (h("div", { class: "pds-select__message" }, this.getHelperMessage(), this.getErrorMessage()));
142
142
  }
143
+ // The field also renders as invalid whenever an error message is present, via
144
+ // `select:has(~ .pds-select__message .pds-select__error-message)` in the styles.
145
+ isInvalid() {
146
+ return Boolean(this.invalid) || Boolean(this.errorMessage);
147
+ }
143
148
  classNames() {
144
149
  const classNames = [];
145
150
  if (this.invalid) {
@@ -223,7 +228,7 @@ export class PdsSelect {
223
228
  }
224
229
  render() {
225
230
  const hasAction = this.el.querySelector('[slot="action"]') !== null;
226
- return (h(Host, { key: '71f6bfba3361eaac3459c1b30b23183503b57ae5', "aria-disabled": this.disabled ? 'true' : null, class: this.classNames(), "has-action": hasAction && !this.hideLabel ? 'true' : null }, h("div", { key: 'cbd0059fc405179a59c29f81510d2948e358531c', class: "pds-select" }, !this.hideLabel && (h("div", { key: '87d42d145ec0a46a474bb599e46226efc72d1acb', class: "pds-select__label-wrapper" }, h("label", { key: '23e4b10a8b99023d1779de0eb6c56ff31189dce1', htmlFor: this.componentId }, h("span", { key: 'b655a0850ce9ce173fbd814461df8529c9c150d9', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)), hasAction && this.renderAction())), h("select", { key: 'a9f2cd048cdea6f0a44305a2760c418617d7c0eb', "aria-label": this.hideLabel ? this.label : undefined, autocomplete: this.autocomplete || undefined, class: "pds-select__field", disabled: this.disabled, id: this.componentId, multiple: this.multiple, name: this.name, onBlur: this.onBlur, onChange: this.onSelectUpdate, onFocus: this.onFocus, onKeyDown: this.onKeyDown, part: "select", required: this.required, ref: (el) => (this.selectEl = el) }), h("div", { key: 'cca5b1b634d1d77bc4164cb196fdd1c4ffcb888e', "aria-hidden": "true", class: "hidden", ref: (el) => (this.slotContainer = el) }, h("slot", { key: '018780883212786a89c5e6f776dc8c69853bf732', onSlotchange: this.handleSlotChange })), this.renderMessages(), !this.multiple && h("pds-icon", { key: '10e82ad220b4e9d8d9c0710a14335d5603bd6164', "aria-hidden": "true", class: "pds-select__select-icon", icon: enlarge }))));
231
+ return (h(Host, { key: 'c74f5e86a09bd3fe6e8048ca103ec9aa66a98fc6', "aria-disabled": this.disabled ? 'true' : null, class: this.classNames(), "has-action": hasAction && !this.hideLabel ? 'true' : null }, h("div", { key: '76de23d6b0d9e32fab48e09a8f6ee3211e196115', class: "pds-select" }, !this.hideLabel && (h("div", { key: '1db87074447cbc819e6b06dd89135d018656a471', class: "pds-select__label-wrapper" }, h("label", { key: 'bc28c3b3361a19ee14a1b79edda736340009d87d', htmlFor: this.componentId }, h("span", { key: 'f0828e0e83c7c0d7cf50e08df4b861dad4df9eb9', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)), hasAction && this.renderAction())), h("select", { key: 'd7d254887c976a95ad1f1da2ea540d6d3f986630', "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-label": this.hideLabel ? this.label : undefined, autocomplete: this.autocomplete || undefined, class: "pds-select__field", disabled: this.disabled, id: this.componentId, multiple: this.multiple, name: this.name, onBlur: this.onBlur, onChange: this.onSelectUpdate, onFocus: this.onFocus, onKeyDown: this.onKeyDown, part: "select", required: this.required, ref: (el) => (this.selectEl = el) }), h("div", { key: '793a84d41bd5e18d64757238704a053b1135a83c', "aria-hidden": "true", class: "hidden", ref: (el) => (this.slotContainer = el) }, h("slot", { key: '27ed9ff31bbf83fcbd2ed8dd2b23b97422b18d25', onSlotchange: this.handleSlotChange })), this.renderMessages(), !this.multiple && h("pds-icon", { key: '43d415eab0fc8a3c02e19848a5622ea43f04256e', "aria-hidden": "true", class: "pds-select__select-icon", icon: enlarge }))));
227
232
  }
228
233
  static get is() { return "pds-select"; }
229
234
  static get encapsulation() { return "shadow"; }
@@ -82,9 +82,9 @@ export class PdsSwitch {
82
82
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
83
83
  }
84
84
  render() {
85
- return (h(Host, { key: '96ee8c40c04e95b461ba2942b740503897a56b27', class: this.switchClassNames(), "aria-disabled": this.disabled ? 'true' : null }, h("label", { key: '441d6854f2c0b02ecf3d874198b3e0636d68c83b', htmlFor: this.componentId }, h("input", Object.assign({ key: 'a26cd5fb407abc20da3e9dcfe0e52333690b783a', "aria-describedby": assignDescription(this.componentId, this.invalid, this.errorMessage || this.helperMessage), "aria-invalid": this.invalid ? "true" : undefined, checked: this.checked, class: "pds-switch__input", disabled: this.disabled, id: this.componentId, name: this.name ? this.name : this.componentId, onChange: this.onSwitchUpdate, required: this.required, type: "checkbox", value: this.value }, this.inheritedAttributes)), h("span", { key: '5762c48f481c5c5d7b62e8dab40579a4e7ff290b', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)), this.helperMessage &&
86
- h("div", { key: '64c14b8833b45b4e9efb2f76faa47f0d24127c91', class: `pds-switch__message`, id: messageId(this.componentId, 'helper') }, this.helperMessage), this.errorMessage &&
87
- h("div", { key: 'cecd2e8da713e74b8e6b528b50733015f5d5069b', class: `pds-switch__message pds-switch__message--error`, id: messageId(this.componentId, 'error'), "aria-live": "assertive" }, h("pds-icon", { key: '3a1c8f87b3909cdad1bc680931f5521ecb77acd6', icon: danger, size: "small" }), this.errorMessage)));
85
+ return (h(Host, { key: '96ee8c40c04e95b461ba2942b740503897a56b27', class: this.switchClassNames(), "aria-disabled": this.disabled ? 'true' : null }, h("label", { key: '441d6854f2c0b02ecf3d874198b3e0636d68c83b', htmlFor: this.componentId }, h("input", Object.assign({ key: 'ed0c344ab46d83faf902571fcd282a605f5cfbec', "aria-describedby": assignDescription(this.componentId, this.invalid, this.helperMessage, this.errorMessage), "aria-invalid": this.invalid ? "true" : undefined, checked: this.checked, class: "pds-switch__input", disabled: this.disabled, id: this.componentId, name: this.name ? this.name : this.componentId, onChange: this.onSwitchUpdate, required: this.required, type: "checkbox", value: this.value }, this.inheritedAttributes)), h("span", { key: 'a2e89156f56d6c921be9b866823bb4b36710b40f', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)), this.helperMessage &&
86
+ h("div", { key: '1465778f0a69d4c87108a80603d123bf0500911d', class: `pds-switch__message`, id: messageId(this.componentId, 'helper') }, this.helperMessage), this.errorMessage &&
87
+ h("div", { key: '2a652c3019f45a060283f98ea97ac83e17d7de33', class: `pds-switch__message pds-switch__message--error`, id: messageId(this.componentId, 'error'), "aria-live": "assertive" }, h("pds-icon", { key: '438895d9ad8799b540ab239ac90bedcb0231d2f0', icon: danger, size: "small" }), this.errorMessage)));
88
88
  }
89
89
  static get is() { return "pds-switch"; }
90
90
  static get encapsulation() { return "shadow"; }
@@ -302,9 +302,9 @@ export class PdsTextarea {
302
302
  render() {
303
303
  const value = this.getValue();
304
304
  return (h(Host, { key: 'd13869d989752c76c80a46ebb0291df2c0546cc2', "aria-disabled": this.disabled ? 'true' : null, "aria-readonly": this.readonly ? 'true' : null, "has-action": this.hasAction && !this.hideLabel ? 'true' : null }, h("div", { key: '19454f642896485dc22bc5dac8b6ed3b4ae5627a', class: "pds-textarea" }, this.label &&
305
- h("div", { key: 'def9e117ba3667a542e8cec32c2eabfa701c3305', class: "pds-textarea__label-wrapper" }, h("label", { key: '893a1db325ece460c627e72b765289098823e49e', htmlFor: this.componentId }, h("span", { key: 'd047f17441aa81800057d7403af15f6e38c53f6c', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)), !this.hideLabel && this.renderAction()), h("div", { key: '7d0f2fab56fee9b9d9e0facabc7e607cb6bee23b', class: "pds-textarea__field-wrapper" }, h("textarea", Object.assign({ key: 'a3ecb78c5b8f42f8350abe623bacb55be6c4a0c0', ref: (el) => this.nativeTextarea = el, "aria-describedby": assignDescription(this.componentId, this.invalid, this.helperMessage), "aria-invalid": this.invalid ? "true" : undefined, autocomplete: this.autocomplete, class: this.textareaClassNames(), disabled: this.disabled, id: this.componentId, maxlength: this.maxLength, name: this.name, placeholder: this.placeholder, readOnly: this.readonly, required: this.required, rows: this.rows, onBlur: this.onBlur, onChange: this.onTextareaChange, onFocus: this.onFocus, onInput: this.onInput, onKeyDown: this.onKeyDown }, this.inheritedAttributes), value), this.renderCharacterCounter()), this.helperMessage &&
306
- h("p", { key: 'b5229a6ef0b2c4ee5dcb820ce2b9bbf6334adef8', class: "pds-textarea__helper-message", id: messageId(this.componentId, 'helper') }, this.helperMessage), this.invalid &&
307
- h("p", { key: '8e6dfa97496d553b8e1e2a50c1221fad3ddb539a', "aria-live": "assertive", class: "pds-textarea__error-message", id: messageId(this.componentId, 'error') }, h("pds-icon", { key: 'f3ed2677239e6d135480cf55d1d908936d746322', icon: danger, size: "small" }), this.errorMessage))));
305
+ h("div", { key: 'def9e117ba3667a542e8cec32c2eabfa701c3305', class: "pds-textarea__label-wrapper" }, h("label", { key: '893a1db325ece460c627e72b765289098823e49e', htmlFor: this.componentId }, h("span", { key: 'd047f17441aa81800057d7403af15f6e38c53f6c', class: this.hideLabel ? 'visually-hidden' : '' }, this.label)), !this.hideLabel && this.renderAction()), h("div", { key: '7d0f2fab56fee9b9d9e0facabc7e607cb6bee23b', class: "pds-textarea__field-wrapper" }, h("textarea", Object.assign({ key: '261e6031c61832acde2d595248ccdc8bd5c84768', ref: (el) => this.nativeTextarea = el, "aria-describedby": assignDescription(this.componentId, this.invalid, this.helperMessage, this.errorMessage), "aria-invalid": this.invalid ? "true" : undefined, autocomplete: this.autocomplete, class: this.textareaClassNames(), disabled: this.disabled, id: this.componentId, maxlength: this.maxLength, name: this.name, placeholder: this.placeholder, readOnly: this.readonly, required: this.required, rows: this.rows, onBlur: this.onBlur, onChange: this.onTextareaChange, onFocus: this.onFocus, onInput: this.onInput, onKeyDown: this.onKeyDown }, this.inheritedAttributes), value), this.renderCharacterCounter()), this.helperMessage &&
306
+ h("p", { key: 'de40f9d13bda3d9a224349b7f4ca38194e7adbb5', class: "pds-textarea__helper-message", id: messageId(this.componentId, 'helper') }, this.helperMessage), this.invalid &&
307
+ h("p", { key: '1306917ce521c3ad0d303d255f9e7aefd0e65de7', "aria-live": "assertive", class: "pds-textarea__error-message", id: messageId(this.componentId, 'error') }, h("pds-icon", { key: '1c9e22b9b4bf84f671797bbfb9080bdbdb7619a7', icon: danger, size: "small" }), this.errorMessage))));
308
308
  }
309
309
  static get is() { return "pds-textarea"; }
310
310
  static get encapsulation() { return "shadow"; }
@@ -6,14 +6,18 @@ export const messageId = (id, messageType) => {
6
6
  };
7
7
  /**
8
8
  * Assign aria-description id to relate messages with form element
9
+ *
10
+ * Returns the error message id when invalid, otherwise the helper message id.
11
+ * Pass only messages the component renders — a described id must exist in the DOM.
9
12
  */
10
- export const assignDescription = (id, invalid, helperMessage) => {
11
- if (!helperMessage)
12
- return;
13
- let relatedId = messageId(id, 'helper');
14
- if (invalid)
15
- relatedId = messageId(id, 'error');
16
- return relatedId;
13
+ export const assignDescription = (id, invalid, helperMessage, errorMessage) => {
14
+ const hasErrorMessage = Boolean(errorMessage);
15
+ const hasHelperMessage = Boolean(helperMessage);
16
+ if (invalid && hasErrorMessage)
17
+ return messageId(id, 'error');
18
+ if (hasHelperMessage)
19
+ return messageId(id, 'helper');
20
+ return undefined;
17
21
  };
18
22
  /**
19
23
  * Updates the `invalid` prop of a form element based on it's values validity
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-08-13T20:44:44",
2
+ "timestamp": "2026-08-27T21:56:56",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.43.5",
@@ -705,6 +705,10 @@
705
705
  {
706
706
  "name": "part",
707
707
  "text": "image - The main image element that represents the avatar component."
708
+ },
709
+ {
710
+ "name": "part",
711
+ "text": "initials - The text element that renders the avatar's initials, for restyling their color."
708
712
  }
709
713
  ],
710
714
  "usage": {},
@@ -1083,6 +1087,10 @@
1083
1087
  "name": "image",
1084
1088
  "docs": "The main image element that represents the avatar component."
1085
1089
  },
1090
+ {
1091
+ "name": "initials",
1092
+ "docs": "The text element that renders the avatar's initials, for restyling their color."
1093
+ },
1086
1094
  {
1087
1095
  "name": "status",
1088
1096
  "docs": ""
@@ -13703,6 +13711,29 @@
13703
13711
  "getter": false,
13704
13712
  "setter": false
13705
13713
  },
13714
+ {
13715
+ "name": "errorMessage",
13716
+ "type": "string",
13717
+ "complexType": {
13718
+ "original": "string",
13719
+ "resolved": "string",
13720
+ "references": {}
13721
+ },
13722
+ "mutable": false,
13723
+ "attr": "error-message",
13724
+ "reflectToAttr": false,
13725
+ "docs": "Error message to display.",
13726
+ "docsTags": [],
13727
+ "values": [
13728
+ {
13729
+ "type": "string"
13730
+ }
13731
+ ],
13732
+ "optional": true,
13733
+ "required": false,
13734
+ "getter": false,
13735
+ "setter": false
13736
+ },
13706
13737
  {
13707
13738
  "name": "fetchTimeout",
13708
13739
  "type": "number",
@@ -13761,6 +13792,29 @@
13761
13792
  "getter": false,
13762
13793
  "setter": false
13763
13794
  },
13795
+ {
13796
+ "name": "helperMessage",
13797
+ "type": "string",
13798
+ "complexType": {
13799
+ "original": "string",
13800
+ "resolved": "string",
13801
+ "references": {}
13802
+ },
13803
+ "mutable": false,
13804
+ "attr": "helper-message",
13805
+ "reflectToAttr": false,
13806
+ "docs": "Helper message to display below the combobox.",
13807
+ "docsTags": [],
13808
+ "values": [
13809
+ {
13810
+ "type": "string"
13811
+ }
13812
+ ],
13813
+ "optional": true,
13814
+ "required": false,
13815
+ "getter": false,
13816
+ "setter": false
13817
+ },
13764
13818
  {
13765
13819
  "name": "hideLabel",
13766
13820
  "type": "boolean",
@@ -13790,6 +13844,29 @@
13790
13844
  "getter": false,
13791
13845
  "setter": false
13792
13846
  },
13847
+ {
13848
+ "name": "invalid",
13849
+ "type": "boolean",
13850
+ "complexType": {
13851
+ "original": "boolean",
13852
+ "resolved": "boolean",
13853
+ "references": {}
13854
+ },
13855
+ "mutable": false,
13856
+ "attr": "invalid",
13857
+ "reflectToAttr": false,
13858
+ "docs": "If true, the combobox is in an invalid state.",
13859
+ "docsTags": [],
13860
+ "values": [
13861
+ {
13862
+ "type": "boolean"
13863
+ }
13864
+ ],
13865
+ "optional": true,
13866
+ "required": false,
13867
+ "getter": false,
13868
+ "setter": false
13869
+ },
13793
13870
  {
13794
13871
  "name": "label",
13795
13872
  "type": "string",
@@ -17383,6 +17460,35 @@
17383
17460
  "getter": false,
17384
17461
  "setter": false
17385
17462
  },
17463
+ {
17464
+ "name": "disableTopLayer",
17465
+ "type": "boolean",
17466
+ "complexType": {
17467
+ "original": "boolean",
17468
+ "resolved": "boolean",
17469
+ "references": {}
17470
+ },
17471
+ "mutable": false,
17472
+ "attr": "disable-top-layer",
17473
+ "reflectToAttr": false,
17474
+ "docs": "Whether the modal opens outside the browser top layer as a non-modal dialog.\nWhen `true` it opens with `dialog.show()` instead of `dialog.showModal()`, so\noverlays rendered elsewhere in the DOM (file pickers, editor menus) can display\nabove it via `z-index`. The page is not made inert and focus is not trapped in\nthis mode. Read when the modal opens; changing it while the modal is open is\nnot supported.",
17475
+ "docsTags": [
17476
+ {
17477
+ "name": "default",
17478
+ "text": "false"
17479
+ }
17480
+ ],
17481
+ "default": "false",
17482
+ "values": [
17483
+ {
17484
+ "type": "boolean"
17485
+ }
17486
+ ],
17487
+ "optional": false,
17488
+ "required": false,
17489
+ "getter": false,
17490
+ "setter": false
17491
+ },
17386
17492
  {
17387
17493
  "name": "open",
17388
17494
  "type": "boolean",
@@ -6,14 +6,18 @@ const messageId = (id, messageType) => {
6
6
  };
7
7
  /**
8
8
  * Assign aria-description id to relate messages with form element
9
+ *
10
+ * Returns the error message id when invalid, otherwise the helper message id.
11
+ * Pass only messages the component renders — a described id must exist in the DOM.
9
12
  */
10
- const assignDescription = (id, invalid, helperMessage) => {
11
- if (!helperMessage)
12
- return;
13
- let relatedId = messageId(id, 'helper');
14
- if (invalid)
15
- relatedId = messageId(id, 'error');
16
- return relatedId;
13
+ const assignDescription = (id, invalid, helperMessage, errorMessage) => {
14
+ const hasErrorMessage = Boolean(errorMessage);
15
+ const hasHelperMessage = Boolean(helperMessage);
16
+ if (invalid && hasErrorMessage)
17
+ return messageId(id, 'error');
18
+ if (hasHelperMessage)
19
+ return messageId(id, 'helper');
20
+ return undefined;
17
21
  };
18
22
  /**
19
23
  * Updates the `invalid` prop of a form element based on it's values validity