@brightspace-ui/core 3.225.1 → 3.227.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.
@@ -360,7 +360,7 @@ Use the Copy button to enable users to copy a text value to the clipboard. The `
360
360
  |--|--|--|
361
361
  | `disabled` | Boolean | Disables the button |
362
362
  | `description` | String | A description to be added to the button for accessibility when text on button does not provide enough context |
363
- | `silm` | Boolean | Makes the button slimmer |
363
+ | `slim` | Boolean | Makes the button slimmer |
364
364
  | `text` | String | Label text for the button |
365
365
  <!-- docs: end hidden content -->
366
366
 
@@ -1,6 +1,9 @@
1
- import { findComposedAncestor, getComposedParent, isComposedAncestor } from '../../helpers/dom.js';
1
+ import { addResizeNoopEventListener, findComposedAncestor, getComposedParent, isComposedAncestor, removeResizeNoopEventListener } from '../../helpers/dom.js';
2
2
  import { getNextFocusable, getPreviousFocusable } from '../../helpers/focus.js';
3
3
  import { css } from 'lit';
4
+ import { getFlag } from '../../helpers/flags.js';
5
+
6
+ const ignoreNoopResizeEventsFlag = getFlag('GAUD-9520-ignore-no-op-resize-events', true);
4
7
 
5
8
  const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
6
9
  const __nativeFocus = document.createElement('div').focus;
@@ -141,7 +144,11 @@ export const HierarchicalViewMixin = superclass => class extends superclass {
141
144
  this.addEventListener('focus', this.__focusCapture, true);
142
145
  this.addEventListener('focusout', this.__focusOutCapture, true);
143
146
  this.__onWindowResize = this.__onWindowResize.bind(this);
144
- window.addEventListener('resize', this.__onWindowResize);
147
+ if (ignoreNoopResizeEventsFlag) {
148
+ addResizeNoopEventListener(this.__onWindowResize);
149
+ } else {
150
+ window.addEventListener('resize', this.__onWindowResize);
151
+ }
145
152
  }
146
153
  });
147
154
  }
@@ -151,7 +158,12 @@ export const HierarchicalViewMixin = superclass => class extends superclass {
151
158
 
152
159
  this.removeEventListener('focus', this.__focusCapture);
153
160
  this.removeEventListener('focusout', this.__focusOutCapture);
154
- window.removeEventListener('resize', this.__onWindowResize);
161
+ if (ignoreNoopResizeEventsFlag) {
162
+ removeResizeNoopEventListener(this.__onWindowResize);
163
+ } else {
164
+ window.removeEventListener('resize', this.__onWindowResize);
165
+ }
166
+
155
167
  if (this.__intersectionObserver) {
156
168
  this.__intersectionObserver.disconnect();
157
169
  this.__isAutoSized = false;
@@ -58,7 +58,19 @@
58
58
  <d2l-demo-snippet>
59
59
  <template>
60
60
  <d2l-input-checkbox label="Label for checkbox">
61
- <div slot="supporting" style="color: #999999;">
61
+ <div slot="supporting">
62
+ Additional content can go here and will<br>
63
+ also line up nicely with the checkbox.
64
+ </div>
65
+ </d2l-input-checkbox>
66
+ </template>
67
+ </d2l-demo-snippet>
68
+
69
+ <h2>Checkbox with label and supporting content with progressive disclosure</h2>
70
+ <d2l-demo-snippet>
71
+ <template>
72
+ <d2l-input-checkbox label="Toggle content with checkbox" supporting-hidden-when-unchecked>
73
+ <div slot="supporting">
62
74
  Additional content can go here and will<br>
63
75
  also line up nicely with the checkbox.
64
76
  </div>
@@ -88,6 +88,7 @@ When provided with a `name`, the checkbox will participate in forms if it is `ch
88
88
  | `label-hidden` | Boolean | Hides the label visually (moves it to the input's `aria-label` attribute) |
89
89
  | `name` | String | Name of the form control. Submitted with the form as part of a name/value pair. |
90
90
  | `not-tabbable` | Boolean | Sets `tabindex="-1"` on the checkbox. Note that an alternative method of focusing is necessary to implement if using this property. |
91
+ | `supporting-hidden-when-unchecked` | Boolean | Hides the supporting slot when unchecked. |
91
92
  | `value` | String | Value of the input |
92
93
 
93
94
  ### Events
@@ -1,4 +1,5 @@
1
1
  import '../colors/colors.js';
2
+ import '../expand-collapse/expand-collapse-content.js';
2
3
  import '../tooltip/tooltip.js';
3
4
  import { css, html, LitElement, nothing } from 'lit';
4
5
  import { classMap } from 'lit/directives/class-map.js';
@@ -176,6 +177,11 @@ class InputCheckbox extends FormElementMixin(InputInlineHelpMixin(FocusMixin(Ske
176
177
  * @type {boolean}
177
178
  */
178
179
  notTabbable: { type: Boolean, attribute: 'not-tabbable' },
180
+ /**
181
+ * Hides the supporting slot when unchecked
182
+ * @type {boolean}
183
+ */
184
+ supportingHiddenWhenUnchecked: { type: Boolean, attribute: 'supporting-hidden-when-unchecked', reflect: true },
179
185
  /**
180
186
  * Value of the input
181
187
  * @type {string}
@@ -235,12 +241,8 @@ class InputCheckbox extends FormElementMixin(InputInlineHelpMixin(FocusMixin(Ske
235
241
  vertical-align: top;
236
242
  }
237
243
  .d2l-input-checkbox-supporting {
238
- display: none;
239
244
  margin-block-start: 0.6rem;
240
245
  }
241
- .d2l-input-checkbox-supporting-visible {
242
- display: block;
243
- }
244
246
  `
245
247
  ];
246
248
  }
@@ -254,6 +256,7 @@ class InputCheckbox extends FormElementMixin(InputInlineHelpMixin(FocusMixin(Ske
254
256
  this.labelHidden = false;
255
257
  this.name = '';
256
258
  this.notTabbable = false;
259
+ this.supportingHiddenWhenUnchecked = false;
257
260
  this.value = 'on';
258
261
  this._hasSupporting = false;
259
262
  this._isHovered = false;
@@ -265,10 +268,7 @@ class InputCheckbox extends FormElementMixin(InputInlineHelpMixin(FocusMixin(Ske
265
268
 
266
269
  render() {
267
270
  const tabindex = this.notTabbable ? -1 : undefined;
268
- const supportingClasses = {
269
- 'd2l-input-checkbox-supporting': true,
270
- 'd2l-input-checkbox-supporting-visible': this._hasSupporting
271
- };
271
+ const supportingContentVisible = this._hasSupporting && (this.checked || !this.supportingHiddenWhenUnchecked);
272
272
  const textClasses = {
273
273
  'd2l-input-checkbox-text': true,
274
274
  'd2l-skeletize': true,
@@ -305,7 +305,9 @@ class InputCheckbox extends FormElementMixin(InputInlineHelpMixin(FocusMixin(Ske
305
305
  ${this._renderInlineHelp(this.#inlineHelpId)}
306
306
  ${offscreenContainer}
307
307
  ${disabledTooltip}
308
- <div class="${classMap(supportingClasses)}" @change="${this.#handleSupportingChange}"><slot name="supporting" @slotchange="${this.#handleSupportingSlotChange}"></slot></div>
308
+ <d2l-expand-collapse-content ?expanded="${supportingContentVisible}">
309
+ <div class="d2l-input-checkbox-supporting" @change="${this.#handleSupportingChange}"><slot name="supporting" @slotchange="${this.#handleSupportingSlotChange}"></slot></div>
310
+ </d2l-expand-collapse-content>
309
311
  `;
310
312
  }
311
313
 
@@ -1,3 +1,4 @@
1
+ import '../expand-collapse/expand-collapse-content.js';
1
2
  import '../tooltip/tooltip.js';
2
3
  import { css, html, LitElement, nothing } from 'lit';
3
4
  import { classMap } from 'lit/directives/class-map.js';
@@ -80,12 +81,8 @@ class InputRadio extends InputInlineHelpMixin(SkeletonMixin(FocusMixin(PropertyR
80
81
  margin-inline-start: 1.7rem;
81
82
  }
82
83
  .d2l-input-radio-supporting {
83
- display: none;
84
84
  margin-block-start: 0.6rem;
85
85
  }
86
- .d2l-input-radio-supporting-visible {
87
- display: block;
88
- }
89
86
  `];
90
87
  }
91
88
 
@@ -129,6 +126,7 @@ class InputRadio extends InputInlineHelpMixin(SkeletonMixin(FocusMixin(PropertyR
129
126
 
130
127
  render() {
131
128
  const allowFocus = !this.focusDisabled && this._focusable;
129
+ const supportingContentVisible = this._hasSupporting && (!this.supportingHiddenWhenUnchecked || this._checked);
132
130
  const labelStyles = {
133
131
  alignItems: this._horizontal ? 'flex-start' : undefined
134
132
  };
@@ -143,10 +141,6 @@ class InputRadio extends InputInlineHelpMixin(SkeletonMixin(FocusMixin(PropertyR
143
141
  'd2l-hovering': this._isHovered && !this.focusDisabled,
144
142
  'd2l-skeletize': true
145
143
  };
146
- const supportingClasses = {
147
- 'd2l-input-radio-supporting': true,
148
- 'd2l-input-radio-supporting-visible': this._hasSupporting && (!this.supportingHiddenWhenUnchecked || this._checked),
149
- };
150
144
  const description = this.description ? html`<div id="${this.#descriptionId}" hidden>${this.description}</div>` : nothing;
151
145
  const ariaDescribedByIds = `${this.description ? this.#descriptionId : ''} ${this._hasInlineHelp ? this.#inlineHelpId : ''}`.trim();
152
146
  const disabledTooltip = this.disabled && this.disabledTooltip ?
@@ -169,7 +163,9 @@ class InputRadio extends InputInlineHelpMixin(SkeletonMixin(FocusMixin(PropertyR
169
163
  ${this._renderInlineHelp(this.#inlineHelpId)}
170
164
  ${description}
171
165
  ${disabledTooltip}
172
- <div class="${classMap(supportingClasses)}" @change="${this.#handleSupportingChange}"><slot name="supporting" @slotchange="${this.#handleSupportingSlotChange}"></slot></div>
166
+ <d2l-expand-collapse-content ?expanded="${supportingContentVisible}">
167
+ <div class="d2l-input-radio-supporting" @change="${this.#handleSupportingChange}"><slot name="supporting" @slotchange="${this.#handleSupportingSlotChange}"></slot></div>
168
+ </d2l-expand-collapse-content>
173
169
  `;
174
170
  }
175
171
 
@@ -1,15 +1,18 @@
1
1
  import '../backdrop/backdrop.js';
2
2
  import '../colors/colors.js';
3
3
  import '../focus-trap/focus-trap.js';
4
+ import { addResizeNoopEventListener, getComposedParent, isComposedAncestor, removeResizeNoopEventListener } from '../../helpers/dom.js';
4
5
  import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
5
6
  import { css, html, nothing } from 'lit';
6
7
  import { getComposedActiveElement, getFirstFocusableDescendant, getPreviousFocusableAncestor } from '../../helpers/focus.js';
7
- import { getComposedParent, isComposedAncestor } from '../../helpers/dom.js';
8
8
  import { _offscreenStyleDeclarations } from '../offscreen/offscreen.js';
9
9
  import { classMap } from 'lit/directives/class-map.js';
10
+ import { getFlag } from '../../helpers/flags.js';
10
11
  import { styleMap } from 'lit/directives/style-map.js';
11
12
  import { tryGetIfrauBackdropService } from '../../helpers/ifrauBackdropService.js';
12
13
 
14
+ const ignoreNoopResizeEventsFlag = getFlag('GAUD-9520-ignore-no-op-resize-events', true);
15
+
13
16
  export const positionLocations = Object.freeze({
14
17
  blockEnd: 'block-end',
15
18
  blockStart: 'block-start',
@@ -612,7 +615,11 @@ export const PopoverMixin = superclass => class extends superclass {
612
615
  this.#removeRepositionHandlers();
613
616
  this.#ancestorMutations = new Map();
614
617
 
615
- window.addEventListener('resize', this.#handleResizeBound);
618
+ if (ignoreNoopResizeEventsFlag) {
619
+ addResizeNoopEventListener(this.#handleResizeBound);
620
+ } else {
621
+ window.addEventListener('resize', this.#handleResizeBound);
622
+ }
616
623
 
617
624
  this._ancestorMutationObserver ??= new MutationObserver(this.#handleAncestorMutationBound);
618
625
  const mutationConfig = { attributes: true, childList: true, subtree: true };
@@ -1186,7 +1193,11 @@ export const PopoverMixin = superclass => class extends superclass {
1186
1193
  });
1187
1194
  this._scrollablesObserved = null;
1188
1195
  this._ancestorMutationObserver?.disconnect();
1189
- window.removeEventListener('resize', this.#handleResizeBound);
1196
+ if (ignoreNoopResizeEventsFlag) {
1197
+ removeResizeNoopEventListener(this.#handleResizeBound);
1198
+ } else {
1199
+ window.removeEventListener('resize', this.#handleResizeBound);
1200
+ }
1190
1201
  }
1191
1202
 
1192
1203
  #reposition() {
@@ -5407,6 +5407,12 @@
5407
5407
  "type": "boolean",
5408
5408
  "default": "false"
5409
5409
  },
5410
+ {
5411
+ "name": "supporting-hidden-when-unchecked",
5412
+ "description": "Hides the supporting slot when unchecked",
5413
+ "type": "boolean",
5414
+ "default": "false"
5415
+ },
5410
5416
  {
5411
5417
  "name": "value",
5412
5418
  "description": "Value of the input",
@@ -5480,6 +5486,13 @@
5480
5486
  "type": "boolean",
5481
5487
  "default": "false"
5482
5488
  },
5489
+ {
5490
+ "name": "supportingHiddenWhenUnchecked",
5491
+ "attribute": "supporting-hidden-when-unchecked",
5492
+ "description": "Hides the supporting slot when unchecked",
5493
+ "type": "boolean",
5494
+ "default": "false"
5495
+ },
5483
5496
  {
5484
5497
  "name": "value",
5485
5498
  "attribute": "value",
package/helpers/dom.js CHANGED
@@ -263,3 +263,41 @@ export function querySelectorComposed(node, selector) {
263
263
 
264
264
  return null;
265
265
  }
266
+
267
+ const resizeNoopEventListener = new Set();
268
+ const resizeNoopRect = {};
269
+
270
+ if (globalThis.addEventListener) {
271
+ globalThis.addEventListener('resize', e => {
272
+ if (resizeNoopEventListener.size === 0) return;
273
+
274
+ const frameElement = e.target.frameElement;
275
+ if (frameElement?.classList.contains('d2l-iframe-fit-user-content')) {
276
+ // ignore if the iframe is spamming no-op resize events
277
+ if (resizeNoopRect.height === frameElement.scrollHeight && resizeNoopRect.width === frameElement.scrollWidth) {
278
+ return;
279
+ }
280
+ resizeNoopRect.height = frameElement.scrollHeight;
281
+ resizeNoopRect.width = frameElement.scrollWidth;
282
+ }
283
+
284
+ resizeNoopEventListener.forEach(listener => {
285
+ listener(e);
286
+ });
287
+ });
288
+ }
289
+
290
+ export function addResizeNoopEventListener(listener) {
291
+ resizeNoopEventListener.add(listener);
292
+ resizeNoopRect.height = null;
293
+ resizeNoopRect.width = null;
294
+ }
295
+
296
+ export function removeResizeNoopEventListener(listener) {
297
+ resizeNoopEventListener.delete(listener);
298
+ }
299
+
300
+ // testing only
301
+ export function clearResizeNoopEventListeners() {
302
+ resizeNoopEventListener.clear();
303
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.225.1",
3
+ "version": "3.227.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",