@brightspace-ui/core 3.239.1 → 3.240.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.
@@ -2,6 +2,8 @@ import { _generateResetStyles, heading3Styles } from '../typography/styles.js';
2
2
  import { css, html, LitElement, nothing } from 'lit';
3
3
  import { DialogMixin } from './dialog-mixin.js';
4
4
  import { dialogStyles } from './dialog-styles.js';
5
+ import { getFlag } from '../../helpers/flags.js';
6
+ import { getFocusRingStyles } from '../../helpers/focus.js';
5
7
  import { getUniqueId } from '../../helpers/uniqueId.js';
6
8
  import { ifDefined } from 'lit/directives/if-defined.js';
7
9
  import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
@@ -35,11 +37,25 @@ class DialogConfirm extends LocalizeCoreElement(DialogMixin(LitElement)) {
35
37
  max-width: 420px;
36
38
  }
37
39
 
38
- .d2l-dialog-content > div {
40
+ .d2l-dialog-header > h2 {
41
+ margin: 0;
42
+ width: fit-content;
43
+ }
44
+
45
+ ${getFocusRingStyles(pseudoClass => `.d2l-dialog-content:${pseudoClass} > div`, { extraStyles: css`
46
+
47
+ --d2l-focus-ring-offset: -1px; border-radius: 6px;`
48
+ })}
49
+ .d2l-dialog-content:focus,
50
+ .d2l-dialog-content:focus-visible {
51
+ outline: none;
52
+ }
53
+
54
+ .d2l-dialog-content {
39
55
  padding-top: 30px;
40
56
  }
41
57
 
42
- .d2l-dialog-header + .d2l-dialog-content > div {
58
+ .d2l-dialog-header + .d2l-dialog-content {
43
59
  padding-top: 0;
44
60
  }
45
61
 
@@ -64,7 +80,7 @@ class DialogConfirm extends LocalizeCoreElement(DialogMixin(LitElement)) {
64
80
  top: 0;
65
81
  }
66
82
 
67
- .d2l-dialog-content > div {
83
+ .d2l-dialog-content {
68
84
  padding-top: 20px;
69
85
  }
70
86
 
@@ -76,21 +92,37 @@ class DialogConfirm extends LocalizeCoreElement(DialogMixin(LitElement)) {
76
92
  constructor() {
77
93
  super();
78
94
  this.critical = false;
95
+ this.preferNative = getFlag('GAUD-9644-prefer-native-confirm-dialogs', false);
79
96
  this._criticalLabelId = getUniqueId();
80
97
  this._textId = getUniqueId();
81
98
  this._titleId = getUniqueId();
82
99
  }
83
100
 
84
101
  render() {
85
- const contentTabIndex = !this.focusableContentElemPresent ? '0' : undefined;
102
+ let contentTabIndex = undefined;
103
+ let titleTabIndex = undefined;
104
+ let contentAutofocus = false;
105
+ let titleAutofocus = false;
106
+ if (this._useNative) {
107
+ if (this.titleText) {
108
+ titleTabIndex = '-1';
109
+ titleAutofocus = true;
110
+ } else {
111
+ contentTabIndex = '-1';
112
+ contentAutofocus = true;
113
+ }
114
+ } else {
115
+ contentTabIndex = !this.focusableContentElemPresent ? '0' : undefined;
116
+ }
117
+
86
118
  const inner = html`
87
119
  ${this.critical ? html`<div id="${this._criticalLabelId}" hidden>${this.localize('components.dialog.critical')}</div>` : nothing}
88
120
  <div class="d2l-dialog-inner">
89
121
  ${this.titleText ? html`
90
122
  <div class="d2l-dialog-header">
91
- <div><h2 id="${this._titleId}" class="d2l-heading-3">${this.titleText}</h2></div>
123
+ <h2 id="${this._titleId}" class="d2l-heading-3" tabindex="${ifDefined(titleTabIndex)}" ?autofocus="${titleAutofocus}">${this.titleText}</h2>
92
124
  </div>` : null}
93
- <div id="${this._textId}" class="d2l-dialog-content" tabindex="${ifDefined(contentTabIndex)}">
125
+ <div id="${this._textId}" class="d2l-dialog-content" tabindex="${ifDefined(contentTabIndex)}" ?autofocus="${contentAutofocus}">
94
126
  <div>${this.text ? this.text.split('\n').map(line => html`<p>${line}</p>`) : null}</div>
95
127
  </div>
96
128
  <div class="d2l-dialog-footer">
@@ -113,7 +145,7 @@ class DialogConfirm extends LocalizeCoreElement(DialogMixin(LitElement)) {
113
145
  }
114
146
 
115
147
  _focusInitial() {
116
- if (!this.shadowRoot) return;
148
+ if (!this.shadowRoot || this._useNative) return;
117
149
  const footer = this.shadowRoot.querySelector('.d2l-dialog-footer-slot');
118
150
  const nodes = footer.assignedNodes();
119
151
  for (let i = 0; i < nodes.length; i++) {
@@ -193,6 +193,7 @@ class DialogFullscreen extends PropertyRequiredMixin(LocalizeCoreElement(AsyncCo
193
193
  super();
194
194
  this.async = false;
195
195
  this.noPadding = false;
196
+ this.preferNative = false;
196
197
  this._autoSize = false;
197
198
  this._hasFooterContent = false;
198
199
  this._icon = 'tier1:close-large-thick';
@@ -19,10 +19,7 @@ window.D2L.DialogMixin = window.D2L.DialogMixin || {};
19
19
  // https://bugs.webkit.org/show_bug.cgi?id=233320
20
20
  // starting in Chrome 102, all elements inside <dialog>s that are inside shadow roots have null offsetParent
21
21
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1331803
22
- window.D2L.DialogMixin.hasNative = false;
23
- if (window.D2L.DialogMixin.preferNative === undefined) {
24
- window.D2L.DialogMixin.preferNative = true;
25
- }
22
+ window.D2L.DialogMixin.hasNative = (window.HTMLDialogElement !== undefined);
26
23
 
27
24
  const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
28
25
  const abortAction = 'abort';
@@ -44,6 +41,11 @@ export const DialogMixin = superclass => class extends superclass {
44
41
  * ADVANCED: Opt out of dialog content scrolling
45
42
  */
46
43
  noContentScroll: { type: Boolean, attribute: 'no-content-scroll', reflect: true },
44
+ /**
45
+ * @ignore
46
+ * Do NOT use this
47
+ */
48
+ preferNative: { type: Boolean, attribute: 'prefer-native' },
47
49
  /**
48
50
  * The optional title for the dialog
49
51
  */
@@ -72,6 +74,7 @@ export const DialogMixin = superclass => class extends superclass {
72
74
  this.focusableContentElemPresent = false;
73
75
  this.noContentScroll = false;
74
76
  this.opened = false;
77
+ this.preferNative = false;
75
78
  this._autoSize = true;
76
79
  this._dialogId = getUniqueId();
77
80
  this._fullscreenWithin = 0;
@@ -91,7 +94,6 @@ export const DialogMixin = superclass => class extends superclass {
91
94
  this._top = 0;
92
95
  this._updateOverflow = this._updateOverflow.bind(this);
93
96
  this._updateSize = this._updateSize.bind(this);
94
- this._useNative = (window.D2L.DialogMixin.hasNative && window.D2L.DialogMixin.preferNative);
95
97
  this._width = 0;
96
98
  }
97
99
 
@@ -127,6 +129,13 @@ export const DialogMixin = superclass => class extends superclass {
127
129
  }
128
130
  }
129
131
 
132
+ willUpdate(changedProperties) {
133
+ super.willUpdate(changedProperties);
134
+ if (this.#useNativeInitialized) return;
135
+ this._useNative = (window.D2L.DialogMixin.hasNative && this.preferNative);
136
+ this.#useNativeInitialized = true;
137
+ }
138
+
130
139
  open() {
131
140
  if (this.opened) return;
132
141
  this.opened = true;
@@ -155,6 +164,8 @@ export const DialogMixin = superclass => class extends superclass {
155
164
  await Promise.all(composedChildren.map(child => waitForElem(child, predicate)));
156
165
  }
157
166
 
167
+ #useNativeInitialized = false;
168
+
158
169
  _addHandlers() {
159
170
  window.addEventListener('resize', this._updateSize);
160
171
  this.addEventListener('touchstart', this._handleTouchStart);
@@ -387,7 +398,7 @@ export const DialogMixin = superclass => class extends superclass {
387
398
  return abortEvent.defaultPrevented;
388
399
  }
389
400
 
390
- _open() {
401
+ async _open() {
391
402
  if (!this.opened) return;
392
403
 
393
404
  this._opener = getComposedActiveElement();
@@ -411,6 +422,8 @@ export const DialogMixin = superclass => class extends superclass {
411
422
  });
412
423
 
413
424
  if (this._useNative) {
425
+ this._state = 'showing';
426
+ await this.updateComplete;
414
427
  dialog.showModal();
415
428
  }
416
429
 
@@ -432,7 +445,7 @@ export const DialogMixin = superclass => class extends superclass {
432
445
  }, 0);
433
446
 
434
447
  await this._updateSize();
435
- this._state = 'showing';
448
+ if (!this._useNative) this._state = 'showing';
436
449
  await this.updateComplete;
437
450
 
438
451
  // edge case: no children were focused, try again after one redraw
@@ -103,6 +103,7 @@ class Dialog extends PropertyRequiredMixin(LocalizeCoreElement(AsyncContainerMix
103
103
  this.critical = false;
104
104
  this.describeContent = false;
105
105
  this.fullHeight = false;
106
+ this.preferNative = false;
106
107
  this.width = 600;
107
108
  this._criticalLabelId = getUniqueId();
108
109
  this._handleResize = this._handleResize.bind(this);
@@ -2548,6 +2548,10 @@
2548
2548
  "description": "Whether or not the dialog is open",
2549
2549
  "type": "boolean",
2550
2550
  "default": "false"
2551
+ },
2552
+ {
2553
+ "name": "preferNative",
2554
+ "default": "false"
2551
2555
  }
2552
2556
  ],
2553
2557
  "events": [
@@ -2667,6 +2671,11 @@
2667
2671
  "description": "Whether or not the dialog is open",
2668
2672
  "type": "boolean",
2669
2673
  "default": "false"
2674
+ },
2675
+ {
2676
+ "name": "preferNative",
2677
+ "type": "boolean",
2678
+ "default": "false"
2670
2679
  }
2671
2680
  ],
2672
2681
  "events": [
@@ -2816,6 +2825,11 @@
2816
2825
  "description": "Whether or not the dialog is open",
2817
2826
  "type": "boolean",
2818
2827
  "default": "false"
2828
+ },
2829
+ {
2830
+ "name": "preferNative",
2831
+ "type": "boolean",
2832
+ "default": "false"
2819
2833
  }
2820
2834
  ],
2821
2835
  "events": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.239.1",
3
+ "version": "3.240.0",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/BrightspaceUI/core.git",