@momentum-design/components 0.83.2 → 0.83.3

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.
@@ -1,7 +1,7 @@
1
1
  import { CSSResult, PropertyValues } from 'lit';
2
2
  import { Component } from '../../models';
3
3
  import type { DialogRole, DialogSize, DialogVariant } from './dialog.types';
4
- declare const Dialog_base: import("../../utils/mixins/index.types").Constructor<HTMLElement & import("../../utils/mixins/FocusTrapMixin").FocusTrapClassInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/CardAndDialogFooterMixin").CardAndDialogFooterMixinInterface> & typeof Component;
4
+ declare const Dialog_base: import("../../utils/mixins/index.types").Constructor<Component & import("../../utils/mixins/PreventScrollMixin").PreventScrollMixinInterface> & import("../../utils/mixins/index.types").Constructor<Component & import("../../utils/mixins/FocusTrapMixin").FocusTrapClassInterface> & import("../../utils/mixins/index.types").Constructor<import("../../utils/mixins/CardAndDialogFooterMixin").CardAndDialogFooterMixinInterface> & typeof Component;
5
5
  /**
6
6
  * Dialog component is a modal dialog that can be used to display information or prompt the user for input.
7
7
  * It can be used to create custom dialogs where content for the body and footer actions is provided by the consumer.
@@ -136,8 +136,13 @@ declare class Dialog extends Dialog_base {
136
136
  * For now FocusTrap is always true as the dialog is a modal component only.
137
137
  * This means it will always trap focus within the dialog when it is open.
138
138
  */
139
+ focusTrap: boolean;
140
+ /**
141
+ * For now preventScroll is always true as the dialog is a modal component only.
142
+ * This means scroll will be prevented when the dialog is open.
143
+ */
139
144
  /** @internal */
140
- protected focusTrap: boolean;
145
+ protected preventScroll: boolean;
141
146
  /** @internal */
142
147
  protected triggerElement: HTMLElement | null;
143
148
  /** @internal */
@@ -13,6 +13,7 @@ import { property } from 'lit/decorators.js';
13
13
  import styles from './dialog.styles';
14
14
  import { Component } from '../../models';
15
15
  import { FocusTrapMixin } from '../../utils/mixins/FocusTrapMixin';
16
+ import { PreventScrollMixin } from '../../utils/mixins/PreventScrollMixin';
16
17
  import { DEFAULTS } from './dialog.constants';
17
18
  import { TYPE, VALID_TEXT_TAGS } from '../text/text.constants';
18
19
  import { DialogEventManager } from './dialog.events';
@@ -66,7 +67,7 @@ import { CardAndDialogFooterMixin } from '../../utils/mixins/CardAndDialogFooter
66
67
  * @slot footer - This slot is for passing custom footer content. Only use this if really needed,
67
68
  * using the footer-link and footer-button slots is preferred
68
69
  */
69
- class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
70
+ class Dialog extends PreventScrollMixin(FocusTrapMixin(CardAndDialogFooterMixin(Component))) {
70
71
  constructor() {
71
72
  super(...arguments);
72
73
  /**
@@ -140,8 +141,13 @@ class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
140
141
  * For now FocusTrap is always true as the dialog is a modal component only.
141
142
  * This means it will always trap focus within the dialog when it is open.
142
143
  */
143
- /** @internal */
144
144
  this.focusTrap = true;
145
+ /**
146
+ * For now preventScroll is always true as the dialog is a modal component only.
147
+ * This means scroll will be prevented when the dialog is open.
148
+ */
149
+ /** @internal */
150
+ this.preventScroll = true;
145
151
  /** @internal */
146
152
  this.triggerElement = null;
147
153
  /** @internal */
@@ -158,6 +164,9 @@ class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
158
164
  return;
159
165
  }
160
166
  event.preventDefault();
167
+ // Prevent the event from propagating to the document level
168
+ // pressing escape on a dialog should only close the dialog, nothing else
169
+ event.stopPropagation();
161
170
  this.hideDialog();
162
171
  };
163
172
  /**
@@ -177,17 +186,20 @@ class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
177
186
  }
178
187
  connectedCallback() {
179
188
  super.connectedCallback();
180
- document.addEventListener('keydown', this.onEscapeKeydown.bind(this));
189
+ // event listener can be added to the element directly, since dialog is a modal component
190
+ // and it will not be allowed to be focused outside of it
191
+ this.addEventListener('keydown', this.onEscapeKeydown.bind(this));
181
192
  }
182
193
  disconnectedCallback() {
183
194
  var _a, _b, _c;
184
195
  super.disconnectedCallback();
185
- document.removeEventListener('keydown', this.onEscapeKeydown);
196
+ this.removeEventListener('keydown', this.onEscapeKeydown);
186
197
  (_a = this.backdropElement) === null || _a === void 0 ? void 0 : _a.remove();
187
198
  this.backdropElement = null;
188
- (_b = this.deactivateFocusTrap) === null || _b === void 0 ? void 0 : _b.call(this);
189
199
  // Set aria-expanded attribute on the trigger element to false if it exists
190
- (_c = this.triggerElement) === null || _c === void 0 ? void 0 : _c.setAttribute('aria-expanded', 'false');
200
+ (_b = this.triggerElement) === null || _b === void 0 ? void 0 : _b.setAttribute('aria-expanded', 'false');
201
+ this.deactivatePreventScroll();
202
+ (_c = this.deactivateFocusTrap) === null || _c === void 0 ? void 0 : _c.call(this);
191
203
  this.focusBackToTrigger();
192
204
  DialogEventManager.onDestroyedDialog(this);
193
205
  }
@@ -222,6 +234,12 @@ class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
222
234
  || changedProperties.has('descriptionText')) {
223
235
  this.setupAriaLabelledDescribedBy();
224
236
  }
237
+ if (changedProperties.has('focusTrap')) {
238
+ // if focusTrap turned false and the popover is visible, deactivate the focus trap
239
+ if (!this.focusTrap && this.visible) {
240
+ this.deactivateFocusTrap();
241
+ }
242
+ }
225
243
  }
226
244
  /**
227
245
  * Sets up the aria-haspopup attribute for the dialog.
@@ -304,8 +322,8 @@ class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
304
322
  if (newValue && !oldValue) {
305
323
  // Store the currently focused element before opening the dialog
306
324
  this.lastActiveElement = document.activeElement;
307
- this.enabledPreventScroll = true;
308
325
  this.createBackdrop();
326
+ this.activatePreventScroll();
309
327
  await this.updateComplete;
310
328
  (_a = this.activateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
311
329
  (_b = this.setInitialFocus) === null || _b === void 0 ? void 0 : _b.call(this);
@@ -316,9 +334,10 @@ class Dialog extends FocusTrapMixin(CardAndDialogFooterMixin(Component)) {
316
334
  else if (!newValue && oldValue) {
317
335
  (_d = this.backdropElement) === null || _d === void 0 ? void 0 : _d.remove();
318
336
  this.backdropElement = null;
319
- (_e = this.deactivateFocusTrap) === null || _e === void 0 ? void 0 : _e.call(this);
320
337
  // Set aria-expanded attribute on the trigger element to false if it exists
321
- (_f = this.triggerElement) === null || _f === void 0 ? void 0 : _f.setAttribute('aria-expanded', 'false');
338
+ (_e = this.triggerElement) === null || _e === void 0 ? void 0 : _e.setAttribute('aria-expanded', 'false');
339
+ this.deactivatePreventScroll();
340
+ (_f = this.deactivateFocusTrap) === null || _f === void 0 ? void 0 : _f.call(this);
322
341
  this.focusBackToTrigger();
323
342
  DialogEventManager.onHideDialog(this);
324
343
  }
@@ -1,7 +1,7 @@
1
1
  import { CSSResult, PropertyValues } from 'lit';
2
2
  import { Component } from '../../models';
3
3
  import { PopoverColor, PopoverPlacement, PopoverTrigger } from './popover.types';
4
- declare const Popover_base: import("../../utils/mixins/index.types").Constructor<HTMLElement & import("../../utils/mixins/FocusTrapMixin").FocusTrapClassInterface> & typeof Component;
4
+ declare const Popover_base: import("../../utils/mixins/index.types").Constructor<Component & import("../../utils/mixins/PreventScrollMixin").PreventScrollMixinInterface> & import("../../utils/mixins/index.types").Constructor<Component & import("../../utils/mixins/FocusTrapMixin").FocusTrapClassInterface> & typeof Component;
5
5
  /**
6
6
  * Popover component is a lightweight floating UI element that displays additional content when triggered.
7
7
  * It can be used for tooltips, dropdowns, or contextual menus.
@@ -94,7 +94,7 @@ declare class Popover extends Popover_base {
94
94
  */
95
95
  focusTrap: boolean;
96
96
  /**
97
- * Prevent outside scrolling when popover show.
97
+ * Prevent outside scrolling when popover is shown.
98
98
  * @default false
99
99
  */
100
100
  preventScroll: boolean;
@@ -109,7 +109,10 @@ declare class Popover extends Popover_base {
109
109
  */
110
110
  closeButton: boolean;
111
111
  /**
112
- * Determines whether the popover is interactive
112
+ * Determines whether the popover is interactive.
113
+ * Make sure to set focusTrap to true to keep the focus inside the popover in case necessary.
114
+ * Setting interactive to true will not automatically set focusTrap!
115
+ *
113
116
  * @default false
114
117
  */
115
118
  interactive: boolean;
@@ -282,10 +285,6 @@ declare class Popover extends Popover_base {
282
285
  * Toggles the popover visibility.
283
286
  */
284
287
  togglePopoverVisible: () => void;
285
- /**
286
- * Sets the focusable elements inside the popover.
287
- */
288
- private handleCreatePopoverFirstUpdate;
289
288
  /**
290
289
  * Positions the popover based on the trigger element.
291
290
  * It also handles the flip, size and arrow placement.
@@ -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 { FocusTrapMixin } from '../../utils/mixins/FocusTrapMixin';
16
+ import { PreventScrollMixin } from '../../utils/mixins/PreventScrollMixin';
16
17
  import { COLOR, DEFAULTS, POPOVER_PLACEMENT, TRIGGER } from './popover.constants';
17
18
  import { PopoverEventManager } from './popover.events';
18
19
  import { popoverStack } from './popover.stack';
@@ -48,7 +49,7 @@ import { PopoverUtils } from './popover.utils';
48
49
  * @slot - Default slot for the popover content
49
50
  *
50
51
  */
51
- class Popover extends FocusTrapMixin(Component) {
52
+ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
52
53
  constructor() {
53
54
  super();
54
55
  /**
@@ -112,7 +113,7 @@ class Popover extends FocusTrapMixin(Component) {
112
113
  */
113
114
  this.focusTrap = DEFAULTS.FOCUS_TRAP;
114
115
  /**
115
- * Prevent outside scrolling when popover show.
116
+ * Prevent outside scrolling when popover is shown.
116
117
  * @default false
117
118
  */
118
119
  this.preventScroll = DEFAULTS.PREVENT_SCROLL;
@@ -127,7 +128,10 @@ class Popover extends FocusTrapMixin(Component) {
127
128
  */
128
129
  this.closeButton = DEFAULTS.CLOSE_BUTTON;
129
130
  /**
130
- * Determines whether the popover is interactive
131
+ * Determines whether the popover is interactive.
132
+ * Make sure to set focusTrap to true to keep the focus inside the popover in case necessary.
133
+ * Setting interactive to true will not automatically set focusTrap!
134
+ *
131
135
  * @default false
132
136
  */
133
137
  this.interactive = DEFAULTS.INTERACTIVE;
@@ -336,6 +340,7 @@ class Popover extends FocusTrapMixin(Component) {
336
340
  this.utils = new PopoverUtils(this);
337
341
  }
338
342
  async firstUpdated(changedProperties) {
343
+ var _a, _b;
339
344
  super.firstUpdated(changedProperties);
340
345
  this.utils.setupAppendTo();
341
346
  [this.openDelay, this.closeDelay] = this.utils.setupDelay();
@@ -345,7 +350,14 @@ class Popover extends FocusTrapMixin(Component) {
345
350
  PopoverEventManager.onCreatedPopover(this);
346
351
  if (this.visible) {
347
352
  this.positionPopover();
348
- await this.handleCreatePopoverFirstUpdate();
353
+ this.activatePreventScroll();
354
+ // If the popover is visible on first update and focustrap is enabled, we need to activate the focus trap
355
+ if (this.interactive && this.focusTrap) {
356
+ // Wait for the first update to complete before setting focusable elements
357
+ await this.updateComplete;
358
+ (_a = this.activateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
359
+ (_b = this.setInitialFocus) === null || _b === void 0 ? void 0 : _b.call(this);
360
+ }
349
361
  }
350
362
  }
351
363
  async disconnectedCallback() {
@@ -353,6 +365,7 @@ class Popover extends FocusTrapMixin(Component) {
353
365
  super.disconnectedCallback();
354
366
  this.removeEventListeners();
355
367
  (_a = this.deactivateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
368
+ this.deactivatePreventScroll();
356
369
  PopoverEventManager.onDestroyedPopover(this);
357
370
  popoverStack.remove(this);
358
371
  }
@@ -454,6 +467,21 @@ class Popover extends FocusTrapMixin(Component) {
454
467
  if (changedProperties.has('interactive') || changedProperties.has('disableAriaHasPopup')) {
455
468
  this.utils.updateAriaHasPopupAttribute();
456
469
  }
470
+ if (changedProperties.has('focusTrap')) {
471
+ // if focusTrap turned false and the popover is visible, deactivate the focus trap
472
+ if (!this.focusTrap && this.visible) {
473
+ this.deactivateFocusTrap();
474
+ }
475
+ }
476
+ if (changedProperties.has('preventScroll')) {
477
+ // if preventScroll turned false and the popover is visible, deactivate the prevent scroll
478
+ if (!this.preventScroll && this.visible) {
479
+ this.deactivatePreventScroll();
480
+ }
481
+ else if (this.preventScroll && this.visible) {
482
+ this.activatePreventScroll();
483
+ }
484
+ }
457
485
  }
458
486
  /**
459
487
  * Handles the popover visibility change and position the popover.
@@ -463,7 +491,7 @@ class Popover extends FocusTrapMixin(Component) {
463
491
  * @param newValue - The new value of the visible property.
464
492
  */
465
493
  async isOpenUpdated(oldValue, newValue) {
466
- var _a, _b, _c, _d, _e;
494
+ var _a, _b, _c, _d, _e, _f, _g;
467
495
  if (oldValue === newValue || !this.triggerElement) {
468
496
  return;
469
497
  }
@@ -471,13 +499,11 @@ class Popover extends FocusTrapMixin(Component) {
471
499
  if (popoverStack.peek() !== this) {
472
500
  popoverStack.push(this);
473
501
  }
474
- this.enabledPreventScroll = this.preventScroll;
475
502
  if (this.backdrop) {
476
503
  this.utils.createBackdrop();
477
504
  this.triggerElement.style.zIndex = `${this.zIndex}`;
478
505
  }
479
506
  this.positionPopover();
480
- await this.handleCreatePopoverFirstUpdate();
481
507
  if (this.hideOnBlur) {
482
508
  this.addEventListener('focusout', this.onPopoverFocusOut);
483
509
  if (this.trigger === 'click') {
@@ -491,6 +517,13 @@ class Popover extends FocusTrapMixin(Component) {
491
517
  this.addEventListener('keydown', this.onEscapeKeydown);
492
518
  (_a = this.triggerElement) === null || _a === void 0 ? void 0 : _a.addEventListener('keydown', this.onEscapeKeydown);
493
519
  }
520
+ this.activatePreventScroll();
521
+ if (this.interactive && this.focusTrap) {
522
+ // Wait for the update to complete before setting focusable elements
523
+ await this.updateComplete;
524
+ (_b = this.activateFocusTrap) === null || _b === void 0 ? void 0 : _b.call(this);
525
+ (_c = this.setInitialFocus) === null || _c === void 0 ? void 0 : _c.call(this);
526
+ }
494
527
  PopoverEventManager.onShowPopover(this);
495
528
  }
496
529
  else {
@@ -498,7 +531,7 @@ class Popover extends FocusTrapMixin(Component) {
498
531
  popoverStack.pop();
499
532
  }
500
533
  if (this.backdropElement) {
501
- (_b = this.backdropElement) === null || _b === void 0 ? void 0 : _b.remove();
534
+ (_d = this.backdropElement) === null || _d === void 0 ? void 0 : _d.remove();
502
535
  this.backdropElement = null;
503
536
  }
504
537
  if (this.hideOnBlur) {
@@ -512,9 +545,8 @@ class Popover extends FocusTrapMixin(Component) {
512
545
  }
513
546
  if (this.hideOnEscape) {
514
547
  this.removeEventListener('keydown', this.onEscapeKeydown);
515
- (_c = this.triggerElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('keydown', this.onEscapeKeydown);
548
+ (_e = this.triggerElement) === null || _e === void 0 ? void 0 : _e.removeEventListener('keydown', this.onEscapeKeydown);
516
549
  }
517
- (_d = this.deactivateFocusTrap) === null || _d === void 0 ? void 0 : _d.call(this);
518
550
  if (!this.disableAriaExpanded) {
519
551
  this.triggerElement.removeAttribute('aria-expanded');
520
552
  }
@@ -522,24 +554,14 @@ class Popover extends FocusTrapMixin(Component) {
522
554
  if (!this.interactive) {
523
555
  this.triggerElement.removeAttribute('aria-haspopup');
524
556
  }
557
+ this.deactivatePreventScroll();
558
+ (_f = this.deactivateFocusTrap) === null || _f === void 0 ? void 0 : _f.call(this);
525
559
  if (this.focusBackToTrigger) {
526
- (_e = this.triggerElement) === null || _e === void 0 ? void 0 : _e.focus();
560
+ (_g = this.triggerElement) === null || _g === void 0 ? void 0 : _g.focus();
527
561
  }
528
562
  PopoverEventManager.onHidePopover(this);
529
563
  }
530
564
  }
531
- /**
532
- * Sets the focusable elements inside the popover.
533
- */
534
- async handleCreatePopoverFirstUpdate() {
535
- var _a, _b;
536
- if (this.visible && this.interactive) {
537
- // Wait for the first update to complete before setting focusable elements
538
- await this.updateComplete;
539
- (_a = this.activateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
540
- (_b = this.setInitialFocus) === null || _b === void 0 ? void 0 : _b.call(this);
541
- }
542
- }
543
565
  /**
544
566
  * Positions the popover based on the trigger element.
545
567
  * It also handles the flip, size and arrow placement.
@@ -57,7 +57,7 @@ class Tooltip extends Popover {
57
57
  this.placement = this.placement || DEFAULTS.PLACEMENT;
58
58
  this.role = ROLE.TOOLTIP;
59
59
  this.trigger = 'mouseenter focusin';
60
- this.enabledPreventScroll = false;
60
+ this.preventScroll = false;
61
61
  this.flip = true;
62
62
  this.preventScroll = false;
63
63
  this.closeButton = false;