@brightspace-ui/core 1.215.0 → 1.219.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 (96) hide show
  1. package/components/button/button-mixin.js +1 -1
  2. package/components/calendar/calendar.js +5 -5
  3. package/components/card/card-footer-link.js +2 -2
  4. package/components/card/card.js +1 -1
  5. package/components/count-badge/count-badge-mixin.js +1 -1
  6. package/components/demo/code-view.js +4 -3
  7. package/components/demo/demo-snippet.js +5 -2
  8. package/components/dialog/dialog-confirm.js +1 -0
  9. package/components/dialog/dialog-mixin.js +7 -2
  10. package/components/dropdown/dropdown-button-subtle.js +1 -1
  11. package/components/dropdown/dropdown-button.js +1 -1
  12. package/components/dropdown/dropdown-content-mixin.js +6 -6
  13. package/components/dropdown/dropdown-context-menu.js +1 -1
  14. package/components/dropdown/dropdown-menu.js +1 -0
  15. package/components/dropdown/dropdown-more.js +1 -1
  16. package/components/dropdown/dropdown-opener-mixin.js +1 -0
  17. package/components/dropdown/dropdown-tabs.js +1 -0
  18. package/components/dropdown/dropdown.js +1 -0
  19. package/components/expand-collapse/expand-collapse-content.js +4 -4
  20. package/components/filter/README.md +1 -0
  21. package/components/filter/filter-dimension-set.js +7 -1
  22. package/components/filter/filter.js +63 -4
  23. package/components/focus-trap/focus-trap.js +6 -5
  24. package/components/form/demo/form-demo.js +1 -1
  25. package/components/form/demo/form-dialog-demo.js +4 -4
  26. package/components/form/demo/form-native-demo.js +1 -1
  27. package/components/form/form-element-mixin.js +12 -3
  28. package/components/form/form-errory-summary.js +2 -2
  29. package/components/form/form-native.js +1 -1
  30. package/components/form/form.js +1 -1
  31. package/components/hierarchical-view/hierarchical-view-mixin.js +7 -6
  32. package/components/html-block/html-block.js +1 -0
  33. package/components/inputs/demo/input-radio-solo-test.js +1 -1
  34. package/components/inputs/demo/input-radio-spacer-test.js +1 -1
  35. package/components/inputs/demo/input-select-test.js +1 -1
  36. package/components/inputs/input-checkbox.js +1 -1
  37. package/components/inputs/input-date-range.js +4 -3
  38. package/components/inputs/input-date-time-range-to.js +1 -1
  39. package/components/inputs/input-date-time-range.js +4 -3
  40. package/components/inputs/input-date-time.js +6 -5
  41. package/components/inputs/input-date.js +3 -0
  42. package/components/inputs/input-number.js +2 -1
  43. package/components/inputs/input-percent.js +2 -1
  44. package/components/inputs/input-search.js +3 -3
  45. package/components/inputs/input-text.js +8 -8
  46. package/components/inputs/input-textarea.js +5 -5
  47. package/components/inputs/input-time-range.js +4 -4
  48. package/components/inputs/input-time.js +1 -1
  49. package/components/link/link.js +1 -1
  50. package/components/list/demo/list-drag-and-drop.js +3 -1
  51. package/components/list/list-item-button-mixin.js +7 -1
  52. package/components/list/list-item-checkbox-mixin.js +1 -1
  53. package/components/list/list-item-drag-drop-mixin.js +10 -5
  54. package/components/list/list-item-drag-handle.js +43 -5
  55. package/components/list/list-item-generic-layout.js +5 -4
  56. package/components/list/list-item-link-mixin.js +3 -1
  57. package/components/list/list-item-mixin.js +55 -3
  58. package/components/list/list-item-role-mixin.js +2 -1
  59. package/components/list/list.js +2 -0
  60. package/components/menu/demo/custom-view.js +1 -1
  61. package/components/menu/menu-item-link.js +1 -1
  62. package/components/menu/menu-item-mixin.js +1 -1
  63. package/components/menu/menu.js +2 -2
  64. package/components/overflow-group/overflow-group.js +1 -1
  65. package/components/scroll-wrapper/demo/scroll-wrapper-test.js +1 -1
  66. package/components/selection/selection-action.js +1 -1
  67. package/components/selection/selection-input.js +1 -1
  68. package/components/selection/selection-select-all.js +1 -1
  69. package/components/switch/switch-mixin.js +1 -1
  70. package/components/table/table-col-sort-button.js +1 -1
  71. package/components/tabs/tabs.js +20 -15
  72. package/components/tooltip/tooltip.js +1 -1
  73. package/controllers/subscriber/README.md +3 -3
  74. package/controllers/subscriber/subscriberControllers.js +4 -4
  75. package/custom-elements.json +13 -0
  76. package/helpers/demo/announce-test.js +1 -0
  77. package/lang/ar.js +20 -5
  78. package/lang/cy.js +20 -5
  79. package/lang/da.js +20 -5
  80. package/lang/de.js +20 -5
  81. package/lang/en.js +16 -0
  82. package/lang/es-es.js +20 -5
  83. package/lang/es.js +20 -5
  84. package/lang/fr-fr.js +20 -5
  85. package/lang/fr.js +20 -5
  86. package/lang/ja.js +20 -5
  87. package/lang/ko.js +20 -5
  88. package/lang/nl.js +20 -5
  89. package/lang/pt.js +20 -5
  90. package/lang/sv.js +20 -5
  91. package/lang/tr.js +20 -5
  92. package/lang/zh-tw.js +20 -5
  93. package/lang/zh.js +20 -5
  94. package/mixins/arrow-keys-mixin.js +3 -1
  95. package/package.json +1 -1
  96. package/templates/primary-secondary/primary-secondary.js +2 -1
@@ -1,9 +1,11 @@
1
1
  import '../button/button-icon.js';
2
2
  import '../icons/icon.js';
3
+ import '../tooltip/tooltip.js';
3
4
  import { css, html, LitElement } from 'lit-element/lit-element.js';
4
5
  import { buttonStyles } from '../button/button-styles.js';
5
6
  import { findComposedAncestor } from '../../helpers/dom.js';
6
7
  import { getFirstFocusableDescendant } from '../../helpers/focus.js';
8
+ import { getUniqueId } from '../../helpers/uniqueId.js';
7
9
  import { LocalizeCoreElement } from '../../lang/localize-core-element.js';
8
10
  import { RtlMixin } from '../../mixins/rtl-mixin.js';
9
11
 
@@ -36,6 +38,8 @@ export const dragActions = Object.freeze({
36
38
  up: 'up'
37
39
  });
38
40
 
41
+ let hasDisplayedKeyboardTooltip = false;
42
+
39
43
  /**
40
44
  * @fires d2l-list-item-drag-handle-action - Dispatched when an action performed on the drag handle
41
45
  */
@@ -58,6 +62,7 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
58
62
  * @type {string}
59
63
  */
60
64
  text: { type: String },
65
+ _displayKeyboardTooltip: { type: Boolean },
61
66
  _keyboardActive: { type: Boolean }
62
67
  };
63
68
  }
@@ -109,6 +114,15 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
109
114
  cursor: default;
110
115
  opacity: 0.5;
111
116
  }
117
+ d2l-tooltip > div {
118
+ font-weight: 700;
119
+ }
120
+ d2l-tooltip > ul {
121
+ padding-inline-start: 1rem;
122
+ }
123
+ .d2l-list-item-drag-handle-tooltip-key {
124
+ font-weight: 700;
125
+ }
112
126
  `];
113
127
  }
114
128
 
@@ -117,6 +131,8 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
117
131
 
118
132
  this.disabled = false;
119
133
 
134
+ this._buttonId = getUniqueId();
135
+ this._displayKeyboardTooltip = false;
120
136
  this._keyboardActive = false;
121
137
  this._movingElement = false;
122
138
  }
@@ -219,7 +235,14 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
219
235
  this._movingElement = false;
220
236
  }
221
237
 
222
- _onFocusOut(e) {
238
+ _onFocusInKeyboardButton() {
239
+ if (hasDisplayedKeyboardTooltip) return;
240
+ this._displayKeyboardTooltip = true;
241
+ hasDisplayedKeyboardTooltip = true;
242
+ }
243
+
244
+ _onFocusOutKeyboardButton(e) {
245
+ this._displayKeyboardTooltip = false;
223
246
  if (this._movingElement) {
224
247
  this._movingElement = false;
225
248
  e.stopPropagation();
@@ -266,17 +289,32 @@ class ListItemDragHandle extends LocalizeCoreElement(RtlMixin(LitElement)) {
266
289
  _renderKeyboardDragging() {
267
290
  return html`
268
291
  <button
292
+ aria-label="${this._defaultLabel}"
293
+ aria-live="assertive"
269
294
  class="d2l-list-item-drag-handle-keyboard-button"
270
- @focusout="${this._onFocusOut}"
271
- @keyup="${this._onActiveKeyboard}"
295
+ @focusin="${this._onFocusInKeyboardButton}"
296
+ @focusout="${this._onFocusOutKeyboardButton}"
297
+ id="${this._buttonId}"
272
298
  @keydown="${this._onPreventDefault}"
273
- aria-live="assertive"
274
- aria-label="${this._defaultLabel}">
299
+ @keyup="${this._onActiveKeyboard}">
275
300
  <d2l-icon icon="tier1:arrow-toggle-up" @click="${this._dispatchActionUp}" class="d2l-button-icon"></d2l-icon>
276
301
  <d2l-icon icon="tier1:arrow-toggle-down" @click="${this._dispatchActionDown}" class="d2l-button-icon"></d2l-icon>
277
302
  </button>
303
+ ${this._displayKeyboardTooltip ? html`<d2l-tooltip align="start" for="${this._buttonId}" for-type="descriptor">${this._renderTooltipContent()}</d2l-tooltip>` : ''}
278
304
  `;
279
305
  }
306
+
307
+ _renderTooltipContent() {
308
+ return html`
309
+ <div>${this.localize('components.list-item-drag-handle-tooltip.title')}</div>
310
+ <ul>
311
+ <li><span class="d2l-list-item-drag-handle-tooltip-key">${this.localize('components.list-item-drag-handle-tooltip.enter-key')}</span> - ${this.localize('components.list-item-drag-handle-tooltip.enter-desc')}</li>
312
+ <li><span class="d2l-list-item-drag-handle-tooltip-key">${this.localize('components.list-item-drag-handle-tooltip.up-down-key')}</span> - ${this.localize('components.list-item-drag-handle-tooltip.up-down-desc')}</li>
313
+ <li><span class="d2l-list-item-drag-handle-tooltip-key">${this.localize('components.list-item-drag-handle-tooltip.left-right-key')}</span> - ${this.localize('components.list-item-drag-handle-tooltip.left-right-desc')}</li>
314
+ </ul>
315
+ `;
316
+ }
317
+
280
318
  }
281
319
 
282
320
  customElements.define('d2l-list-item-drag-handle', ListItemDragHandle);
@@ -171,7 +171,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
171
171
  }
172
172
 
173
173
  _focusCellItem(num, itemNum) {
174
- const cell = this.shadowRoot.querySelector(`[data-cell-num="${num}"]`);
174
+ const cell = this.shadowRoot && this.shadowRoot.querySelector(`[data-cell-num="${num}"]`);
175
175
  if (!cell) return;
176
176
 
177
177
  const firstFocusable = getFirstFocusableDescendant(cell);
@@ -197,7 +197,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
197
197
  let focusable = null;
198
198
  let num = 1;
199
199
  do {
200
- cell = this.shadowRoot.querySelector(`[data-cell-num="${num++}"]`);
200
+ cell = this.shadowRoot && this.shadowRoot.querySelector(`[data-cell-num="${num++}"]`);
201
201
  if (cell) {
202
202
  focusable = getLastFocusableDescendant(cell) || focusable;
203
203
  }
@@ -220,7 +220,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
220
220
  let focusable = null;
221
221
 
222
222
  do {
223
- cell = this.shadowRoot.querySelector(`[data-cell-num="${num}"]`);
223
+ cell = this.shadowRoot && this.shadowRoot.querySelector(`[data-cell-num="${num}"]`);
224
224
  if (cell) {
225
225
  focusable = forward ? getFirstFocusableDescendant(cell) : getLastFocusableDescendant(cell);
226
226
  }
@@ -381,7 +381,8 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
381
381
  }
382
382
 
383
383
  _getThisCell() {
384
- return this.shadowRoot.querySelector(`.d2l-cell[data-cell-num="${this._cellNum}"]`);
384
+ return this.shadowRoot &&
385
+ this.shadowRoot.querySelector(`.d2l-cell[data-cell-num="${this._cellNum}"]`);
385
386
  }
386
387
 
387
388
  _onKeydown(event) {
@@ -1,5 +1,6 @@
1
1
  import '../colors/colors.js';
2
2
  import { css, html } from 'lit-element/lit-element.js';
3
+ import { getUniqueId } from '../../helpers/uniqueId.js';
3
4
  import { ListItemMixin } from './list-item-mixin.js';
4
5
 
5
6
  export const ListItemLinkMixin = superclass => class extends ListItemMixin(superclass) {
@@ -42,6 +43,7 @@ export const ListItemLinkMixin = superclass => class extends ListItemMixin(super
42
43
  constructor() {
43
44
  super();
44
45
  this.actionHref = null;
46
+ this._primaryActionId = getUniqueId();
45
47
  }
46
48
 
47
49
  _handleLinkClick() {
@@ -51,7 +53,7 @@ export const ListItemLinkMixin = superclass => class extends ListItemMixin(super
51
53
 
52
54
  _renderPrimaryAction(labelledBy) {
53
55
  if (!this.actionHref) return;
54
- return html`<a aria-labelledby="${labelledBy}" href="${this.actionHref}" @click="${this._handleLinkClick}"></a>`;
56
+ return html`<a id="${this._primaryActionId}" aria-labelledby="${labelledBy}" href="${this.actionHref}" @click="${this._handleLinkClick}"></a>`;
55
57
  }
56
58
 
57
59
  };
@@ -1,6 +1,7 @@
1
1
  import '../colors/colors.js';
2
2
  import './list-item-generic-layout.js';
3
3
  import './list-item-placement-marker.js';
4
+ import '../tooltip/tooltip.js';
4
5
  import { css, html } from 'lit-element/lit-element.js';
5
6
  import { findComposedAncestor, getComposedParent } from '../../helpers/dom.js';
6
7
  import { classMap } from 'lit-html/directives/class-map.js';
@@ -10,10 +11,28 @@ import { ifDefined } from 'lit-html/directives/if-defined.js';
10
11
  import { ListItemCheckboxMixin } from './list-item-checkbox-mixin.js';
11
12
  import { ListItemDragDropMixin } from './list-item-drag-drop-mixin.js';
12
13
  import { ListItemRoleMixin } from './list-item-role-mixin.js';
14
+ import { LocalizeCoreElement } from '../../lang/localize-core-element.js';
13
15
  import { nothing } from 'lit-html';
14
16
  import ResizeObserver from 'resize-observer-polyfill';
15
17
  import { RtlMixin } from '../../mixins/rtl-mixin.js';
16
18
 
19
+ let tabPressed = false;
20
+ let tabListenerAdded = false;
21
+ function addTabListener() {
22
+ if (tabListenerAdded) return;
23
+ tabListenerAdded = true;
24
+ document.addEventListener('keydown', e => {
25
+ if (e.keyCode !== 9) return;
26
+ tabPressed = true;
27
+ });
28
+ document.addEventListener('keyup', e => {
29
+ if (e.keyCode !== 9) return;
30
+ tabPressed = false;
31
+ });
32
+ }
33
+
34
+ let hasDisplayedKeyboardTooltip = false;
35
+
17
36
  const ro = new ResizeObserver(entries => {
18
37
  entries.forEach(entry => {
19
38
  if (!entry || !entry.target || !entry.target.resizedCallback) {
@@ -25,7 +44,7 @@ const ro = new ResizeObserver(entries => {
25
44
 
26
45
  const defaultBreakpoints = [842, 636, 580, 0];
27
46
 
28
- export const ListItemMixin = superclass => class extends ListItemDragDropMixin(ListItemCheckboxMixin(ListItemRoleMixin(RtlMixin(superclass)))) {
47
+ export const ListItemMixin = superclass => class extends LocalizeCoreElement(ListItemDragDropMixin(ListItemCheckboxMixin(ListItemRoleMixin(RtlMixin(superclass))))) {
29
48
 
30
49
  static get properties() {
31
50
  return {
@@ -40,6 +59,7 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
40
59
  */
41
60
  slim: { type: Boolean },
42
61
  _breakpoint: { type: Number },
62
+ _displayKeyboardTooltip: { type: Boolean },
43
63
  _dropdownOpen: { type: Boolean, attribute: '_dropdown-open', reflect: true },
44
64
  _hoveringPrimaryAction: { type: Boolean },
45
65
  _focusing: { type: Boolean },
@@ -237,6 +257,15 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
237
257
  :host([draggable][selected]:not([disabled])) d2l-list-item-generic-layout.d2l-focusing + .d2l-list-item-active-border {
238
258
  transform: rotate(1deg);
239
259
  }
260
+ d2l-tooltip > div {
261
+ font-weight: 700;
262
+ }
263
+ d2l-tooltip > ul {
264
+ padding-inline-start: 1rem;
265
+ }
266
+ .d2l-list-item-tooltip-key {
267
+ font-weight: 700;
268
+ }
240
269
  `];
241
270
 
242
271
  super.styles && styles.unshift(super.styles);
@@ -249,6 +278,7 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
249
278
  this.slim = false;
250
279
  this._breakpoint = 0;
251
280
  this._contentId = getUniqueId();
281
+ this._displayKeyboardTooltip = false;
252
282
  }
253
283
 
254
284
  get breakpoints() {
@@ -265,6 +295,9 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
265
295
  connectedCallback() {
266
296
  super.connectedCallback();
267
297
  ro.observe(this);
298
+ if (this.role === 'rowgroup') {
299
+ addTabListener();
300
+ }
268
301
  }
269
302
 
270
303
  disconnectedCallback() {
@@ -303,6 +336,7 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
303
336
  }
304
337
 
305
338
  _getNestedList() {
339
+ if (!this.shadowRoot) return;
306
340
  const nestedSlot = this.shadowRoot.querySelector('slot[name="nested"]');
307
341
  let nestedNodes = nestedSlot.assignedNodes();
308
342
  if (nestedNodes.length === 0) {
@@ -350,6 +384,9 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
350
384
 
351
385
  _onFocusIn() {
352
386
  this._focusing = true;
387
+ if (this.role !== 'rowgroup' || !tabPressed || hasDisplayedKeyboardTooltip) return;
388
+ this._displayKeyboardTooltip = true;
389
+ hasDisplayedKeyboardTooltip = true;
353
390
  }
354
391
 
355
392
  _onFocusInPrimaryAction() {
@@ -358,6 +395,7 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
358
395
 
359
396
  _onFocusOut() {
360
397
  this._focusing = false;
398
+ this._displayKeyboardTooltip = false;
361
399
  }
362
400
 
363
401
  _onFocusOutPrimaryAction() {
@@ -397,6 +435,7 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
397
435
  };
398
436
 
399
437
  const primaryAction = this._renderPrimaryAction ? this._renderPrimaryAction(this._contentId) : null;
438
+ const tooltipForId = (primaryAction ? this._primaryActionId : (this.selectable ? this._checkboxId : null));
400
439
 
401
440
  return html`
402
441
  ${this._renderTopPlacementMarker(html`<d2l-list-item-placement-marker></d2l-list-item-placement-marker>`)}
@@ -412,11 +451,11 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
412
451
  ${this._renderDragHandle(this._renderOutsideControl)}
413
452
  ${this._renderDragTarget(this._renderOutsideControlAction)}
414
453
  ${this.selectable ? html`
415
- <div slot="control">${ this._renderCheckbox() }</div>
454
+ <div slot="control">${this._renderCheckbox()}</div>
416
455
  <div slot="control-action"
417
456
  @mouseenter="${this._onMouseEnter}"
418
457
  @mouseleave="${this._onMouseLeave}">
419
- ${ this._renderCheckboxAction('') }
458
+ ${this._renderCheckboxAction('')}
420
459
  </div>` : nothing }
421
460
  ${primaryAction ? html`
422
461
  <div slot="content-action"
@@ -445,6 +484,7 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
445
484
  <div class="d2l-list-item-active-border"></div>
446
485
  </div>
447
486
  ${this._renderBottomPlacementMarker(html`<d2l-list-item-placement-marker></d2l-list-item-placement-marker>`)}
487
+ ${this._displayKeyboardTooltip && tooltipForId ? html`<d2l-tooltip align="start" announced for="${tooltipForId}" for-type="descriptor">${this._renderTooltipContent()}</d2l-tooltip>` : ''}
448
488
  `;
449
489
 
450
490
  }
@@ -457,4 +497,16 @@ export const ListItemMixin = superclass => class extends ListItemDragDropMixin(L
457
497
  return html`<div slot="outside-control-action" @mouseenter="${this._onMouseEnter}" @mouseleave="${this._onMouseLeave}">${dragTarget}</div>`;
458
498
  }
459
499
 
500
+ _renderTooltipContent() {
501
+ return html`
502
+ <div>${this.localize('components.list-item-tooltip.title')}</div>
503
+ <ul>
504
+ <li><span class="d2l-list-item-tooltip-key">${this.localize('components.list-item-tooltip.enter-key')}</span> - ${this.localize('components.list-item-tooltip.enter-desc')}</li>
505
+ <li><span class="d2l-list-item-tooltip-key">${this.localize('components.list-item-tooltip.up-down-key')}</span> - ${this.localize('components.list-item-tooltip.up-down-desc')}</li>
506
+ <li><span class="d2l-list-item-tooltip-key">${this.localize('components.list-item-tooltip.left-right-key')}</span> - ${this.localize('components.list-item-tooltip.left-right-desc')}</li>
507
+ <li><span class="d2l-list-item-tooltip-key">${this.localize('components.list-item-tooltip.page-up-down-key')}</span> - ${this.localize('components.list-item-tooltip.page-up-down-desc')}</li>
508
+ </ul>
509
+ `;
510
+ }
511
+
460
512
  };
@@ -19,8 +19,9 @@ export const ListItemRoleMixin = superclass => class extends superclass {
19
19
 
20
20
  const separators = parent.getAttribute('separators');
21
21
 
22
- this.role = parent.grid ? 'rowgroup' : 'listitem';
22
+ this.role = parent.hasAttribute('grid') ? 'rowgroup' : 'listitem';
23
23
  this._separators = separators || undefined;
24
24
  this._extendSeparators = parent.hasAttribute('extend-separators');
25
25
  }
26
+
26
27
  };
@@ -101,6 +101,7 @@ class List extends SelectionMixin(LitElement) {
101
101
  }
102
102
 
103
103
  getItems() {
104
+ if (!this.shadowRoot) return;
104
105
  const slot = this.shadowRoot.querySelector('slot:not([name])');
105
106
  if (!slot) return [];
106
107
  return slot.assignedNodes({ flatten: true }).filter((node) => {
@@ -157,6 +158,7 @@ class List extends SelectionMixin(LitElement) {
157
158
  _handleKeyDown(e) {
158
159
  if (!this.grid || this.slot === 'nested' || e.keyCode !== keyCodes.TAB) return;
159
160
  e.preventDefault();
161
+ if (!this.shadowRoot) return;
160
162
  const focusable = (e.shiftKey ? getPreviousFocusable(this.shadowRoot.querySelector('slot:not([name])'))
161
163
  : getNextFocusable(this, false, true, true));
162
164
  if (focusable) focusable.focus();
@@ -48,7 +48,7 @@ class CustomView extends HierarchicalViewMixin(LitElement) {
48
48
  }
49
49
 
50
50
  focus() {
51
- this.shadowRoot.querySelector('.d2l-custom-view-back-container > a').focus();
51
+ if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-custom-view-back-container > a').focus();
52
52
  }
53
53
 
54
54
  _handleHide(e) {
@@ -84,7 +84,7 @@ class MenuItemLink extends MenuItemMixin(LitElement) {
84
84
  }
85
85
 
86
86
  _onClick() {
87
- this.shadowRoot.querySelector('a').dispatchEvent(new CustomEvent('click'));
87
+ if (this.shadowRoot) this.shadowRoot.querySelector('a').dispatchEvent(new CustomEvent('click'));
88
88
  }
89
89
 
90
90
  _onKeyDown(e) {
@@ -109,7 +109,7 @@ export const MenuItemMixin = superclass => class extends FocusVisiblePolyfillMix
109
109
  }
110
110
 
111
111
  __initializeItem() {
112
- const slot = this.shadowRoot.querySelector('slot:not([name])');
112
+ const slot = this.shadowRoot && this.shadowRoot.querySelector('slot:not([name])');
113
113
  if (!slot) {
114
114
  return;
115
115
  }
@@ -221,11 +221,11 @@ class Menu extends ThemeMixin(HierarchicalViewMixin(FocusVisiblePolyfillMixin(Li
221
221
  }
222
222
 
223
223
  _getMenuItemReturn() {
224
- return this.shadowRoot.querySelector('d2l-menu-item-return');
224
+ return this.shadowRoot && this.shadowRoot.querySelector('d2l-menu-item-return');
225
225
  }
226
226
 
227
227
  _getMenuItems() {
228
- const slot = this.shadowRoot.querySelector('slot');
228
+ const slot = this.shadowRoot && this.shadowRoot.querySelector('slot');
229
229
  if (!slot) return;
230
230
  const items = slot.assignedNodes({ flatten: true }).filter((node) => node.nodeType === Node.ELEMENT_NODE);
231
231
 
@@ -318,7 +318,7 @@ class OverflowGroup extends RtlMixin(LocalizeCoreElement(LitElement)) {
318
318
  }
319
319
 
320
320
  _chomp() {
321
- if (!this._itemLayouts) return;
321
+ if (!this.shadowRoot || !this._itemLayouts) return;
322
322
 
323
323
  this._overflowMenu = this.shadowRoot.querySelector('.d2l-overflow-dropdown');
324
324
  this._overflowMenuMini = this.shadowRoot.querySelector('.d2l-overflow-dropdown-mini');
@@ -53,7 +53,7 @@ class TestScrollWrapper extends RtlMixin(LitElement) {
53
53
  }
54
54
 
55
55
  focus() {
56
- forceFocusVisible(this.shadowRoot.querySelector('d2l-scroll-wrapper')._container);
56
+ if (this.shadowRoot) forceFocusVisible(this.shadowRoot.querySelector('d2l-scroll-wrapper')._container);
57
57
  }
58
58
 
59
59
  }
@@ -79,7 +79,7 @@ class Action extends LocalizeCoreElement(SelectionObserverMixin(ButtonMixin(RtlM
79
79
  }
80
80
 
81
81
  focus() {
82
- const elem = this.shadowRoot.querySelector('d2l-button-subtle');
82
+ const elem = this.shadowRoot && this.shadowRoot.querySelector('d2l-button-subtle');
83
83
  if (elem) elem.focus();
84
84
  }
85
85
 
@@ -142,7 +142,7 @@ class Input extends SkeletonMixin(LabelledMixin(LitElement)) {
142
142
  }
143
143
 
144
144
  focus() {
145
- const elem = this.shadowRoot.firstElementChild;
145
+ const elem = this.shadowRoot && this.shadowRoot.firstElementChild;
146
146
  if (elem) elem.focus();
147
147
  }
148
148
 
@@ -57,7 +57,7 @@ class SelectAll extends LocalizeCoreElement(SelectionObserverMixin(LitElement))
57
57
  }
58
58
 
59
59
  focus() {
60
- const elem = this.shadowRoot.querySelector('d2l-input-checkbox');
60
+ const elem = this.shadowRoot && this.shadowRoot.querySelector('d2l-input-checkbox');
61
61
  if (elem) elem.focus();
62
62
  }
63
63
 
@@ -213,7 +213,7 @@ export const SwitchMixin = superclass => class extends RtlMixin(FocusVisiblePoly
213
213
  }
214
214
 
215
215
  focus() {
216
- const elem = this.shadowRoot.querySelector('.d2l-switch-container');
216
+ const elem = this.shadowRoot && this.shadowRoot.querySelector('.d2l-switch-container');
217
217
  if (elem) elem.focus();
218
218
  }
219
219
 
@@ -71,7 +71,7 @@ export class TableColSortButton extends LitElement {
71
71
  }
72
72
 
73
73
  focus() {
74
- const button = this.shadowRoot.querySelector('button');
74
+ const button = this.shadowRoot && this.shadowRoot.querySelector('button');
75
75
  if (button) button.focus();
76
76
  }
77
77
 
@@ -374,34 +374,37 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
374
374
  }
375
375
 
376
376
  getTabListRect() {
377
+ if (!this.shadowRoot) return undefined;
377
378
  return this.shadowRoot.querySelector('.d2l-tabs-container-list').getBoundingClientRect();
378
379
  }
379
380
 
380
381
  _animateTabAddition(tabInfo) {
381
- const tab = this.shadowRoot.querySelector(`d2l-tab-internal[controls-panel="${cssEscape(tabInfo.id)}"]`);
382
+ const tab = this.shadowRoot
383
+ && this.shadowRoot.querySelector(`d2l-tab-internal[controls-panel="${cssEscape(tabInfo.id)}"]`);
382
384
  return new Promise((resolve) => {
383
385
  const handleTransitionEnd = (e) => {
384
386
  if (e.propertyName !== 'max-width') return;
385
- tab.removeEventListener('transitionend', handleTransitionEnd);
387
+ if (tab) tab.removeEventListener('transitionend', handleTransitionEnd);
386
388
  resolve();
387
389
  };
388
- tab.addEventListener('transitionend', handleTransitionEnd);
390
+ if (tab) tab.addEventListener('transitionend', handleTransitionEnd);
389
391
  tabInfo.state = '';
390
392
  this.requestUpdate();
391
393
  });
392
394
  }
393
395
 
394
396
  _animateTabRemoval(tabInfo) {
395
- const tab = this.shadowRoot.querySelector(`d2l-tab-internal[controls-panel="${cssEscape(tabInfo.id)}"]`);
397
+ const tab = this.shadowRoot &&
398
+ this.shadowRoot.querySelector(`d2l-tab-internal[controls-panel="${cssEscape(tabInfo.id)}"]`);
396
399
  return new Promise((resolve) => {
397
400
  const handleTransitionEnd = (e) => {
398
401
  if (e.propertyName !== 'max-width') return;
399
- tab.removeEventListener('transitionend', handleTransitionEnd);
402
+ if (tab) tab.removeEventListener('transitionend', handleTransitionEnd);
400
403
  this._tabInfos.splice(this._tabInfos.findIndex(info => info.id === tabInfo.id), 1);
401
404
  this.requestUpdate();
402
405
  resolve();
403
406
  };
404
- tab.addEventListener('transitionend', handleTransitionEnd);
407
+ if (tab) tab.addEventListener('transitionend', handleTransitionEnd);
405
408
  });
406
409
  }
407
410
 
@@ -488,7 +491,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
488
491
  }
489
492
 
490
493
  async _focusSelected() {
491
- const selectedTab = this.shadowRoot.querySelector('d2l-tab-internal[aria-selected="true"]');
494
+ const selectedTab = this.shadowRoot && this.shadowRoot.querySelector('d2l-tab-internal[aria-selected="true"]');
492
495
  if (!selectedTab) return;
493
496
 
494
497
  const selectedTabInfo = this._getTabInfo(selectedTab.controlsPanel);
@@ -517,6 +520,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
517
520
  }
518
521
 
519
522
  _getPanel(id) {
523
+ if (!this.shadowRoot) return;
520
524
  // use simple selector for slot (Edge)
521
525
  const slot = this.shadowRoot.querySelector('.d2l-panels-container').querySelector('slot');
522
526
  const panels = this._getPanels(slot);
@@ -702,7 +706,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
702
706
  await this._scrollToPosition(newTranslationValue);
703
707
  await this._updateScrollVisibility(newMeasures);
704
708
 
705
- if (!isOverflowingNext) {
709
+ if (!isOverflowingNext && this.shadowRoot) {
706
710
  this.shadowRoot.querySelector('.d2l-tabs-scroll-previous-container button').focus();
707
711
  }
708
712
 
@@ -735,7 +739,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
735
739
  await this._scrollToPosition(newTranslationValue);
736
740
  await this._updateScrollVisibility(newMeasures);
737
741
 
738
- if (!isOverflowingPrevious) {
742
+ if (!isOverflowingPrevious && this.shadowRoot) {
739
743
  this.shadowRoot.querySelector('.d2l-tabs-scroll-next-container button').focus();
740
744
  }
741
745
 
@@ -777,7 +781,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
777
781
  }
778
782
 
779
783
  this._translationValue = translationValue;
780
- if (reduceMotion) return this.updateComplete;
784
+ if (!this.shadowRoot || reduceMotion) return this.updateComplete;
781
785
 
782
786
  return new Promise((resolve) => {
783
787
  const tabList = this.shadowRoot.querySelector('.d2l-tabs-container-list');
@@ -805,13 +809,13 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
805
809
  expandedPromise = this.updateComplete;
806
810
  } else {
807
811
  expandedPromise = new Promise((resolve) => {
808
- const tabsContainer = this.shadowRoot.querySelector('.d2l-tabs-container');
812
+ const tabsContainer = this.shadowRoot && this.shadowRoot.querySelector('.d2l-tabs-container');
809
813
  const handleTransitionEnd = (e) => {
810
814
  if (e.propertyName !== 'max-width') return;
811
- tabsContainer.removeEventListener('transitionend', handleTransitionEnd);
815
+ if (tabsContainer) tabsContainer.removeEventListener('transitionend', handleTransitionEnd);
812
816
  resolve();
813
817
  };
814
- tabsContainer.addEventListener('transitionend', handleTransitionEnd);
818
+ if (tabsContainer) tabsContainer.addEventListener('transitionend', handleTransitionEnd);
815
819
  this._scrollCollapsed = false;
816
820
  this._maxWidth = measures.totalTabsWidth + 50;
817
821
  });
@@ -828,7 +832,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
828
832
  if (!this._allowScrollPrevious) {
829
833
  this._focusSelected();
830
834
  } else {
831
- this.shadowRoot.querySelector('.d2l-tabs-scroll-previous-container button').focus();
835
+ if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-tabs-scroll-previous-container button').focus();
832
836
  }
833
837
  }
834
838
 
@@ -838,6 +842,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
838
842
 
839
843
  _updateMeasures() {
840
844
  let totalTabsWidth = 0;
845
+ if (!this.shadowRoot) return;
841
846
  const tabs = [...this.shadowRoot.querySelectorAll('d2l-tab-internal')];
842
847
 
843
848
  const tabRects = tabs.map((tab) => {
@@ -894,7 +899,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(RtlMixin(FocusVisiblePolyf
894
899
  // don't animate the tabs list visibility if it's the inital render
895
900
  if (reduceMotion || !this._initialized) {
896
901
  this._state = 'hidden';
897
- } else {
902
+ } else if (this.shadowRoot) {
898
903
  const layout = this.shadowRoot.querySelector('.d2l-tabs-layout');
899
904
  const handleTransitionEnd = (e) => {
900
905
  if (e.propertyName !== 'max-height') return;
@@ -715,7 +715,7 @@ class Tooltip extends RtlMixin(LitElement) {
715
715
  }
716
716
 
717
717
  _getContent() {
718
- return this.shadowRoot.querySelector('.d2l-tooltip-content');
718
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-tooltip-content');
719
719
  }
720
720
 
721
721
  _isAboveOrBelow() {
@@ -1,6 +1,6 @@
1
1
  # Subscriber Controllers
2
2
 
3
- The `SubscriberRegistryController` and the corresponding `*SubscriberController`s can be used to create a subscription system within your app. Components can setup a subscriber registry instance to keep track of all components subscribed to them with the `SubscriberRegistryController`. Whenever it makes sense to do so, they can iterate over their subscribers to perform some action, update them with new data, etc. Components can subscribe themselves to different registries using the `IdSubscriberController` or the `EventSubscriberController`. This system supports a many-to-many relationship - registry components can contain multiple registry instances with multiple subscribers in each, and subscriber components can subscribe to multiple different registries.
3
+ The `SubscriberRegistryController` and the corresponding `*SubscriberController`s can be used to create a subscription system within your app. Components can set up a subscriber registry instance to keep track of all components subscribed to them with the `SubscriberRegistryController`. Whenever it makes sense to do so, they can iterate over their subscribers to perform some action, update them with new data, etc. Components can subscribe themselves to different registries using the `IdSubscriberController` or the `EventSubscriberController`. This system supports a many-to-many relationship - registry components can contain multiple registry instances with multiple subscribers in each, and subscriber components can subscribe to multiple different registries.
4
4
 
5
5
  ## Usage
6
6
 
@@ -27,7 +27,7 @@ class CableSubscription extends LitElement {
27
27
  { onSubscribe: this._unlockKidsChannels.bind(this) }, {});
28
28
  }
29
29
 
30
- getController(controllerId) {
30
+ getSubscriberController(controllerId) {
31
31
  if (controllerId === 'sports') {
32
32
  return this._sportsSubscribers;
33
33
  } else if (controllerId === 'movies') {
@@ -51,7 +51,7 @@ class CableSubscription extends LitElement {
51
51
 
52
52
  When creating the controller, you can pass in callbacks to run whenever a subscriber is added, removed, or `updateSubscribers` is called (which handles request debouncing for you).
53
53
 
54
- The `*subscriberController`s will use a `getController` method that needs to be exposed on the registry component. If you only have one `SubscriberRegistryController` you can simple return that. If you have multiple, you will return the proper controller depending on the id the subscriber component passed to you.
54
+ The `*subscriberController`s will use a `getSubscriberController` method that needs to be exposed on the registry component. If you only have one `SubscriberRegistryController` you can simple return that. If you have multiple, you will return the proper controller depending on the id the subscriber component passed to you.
55
55
 
56
56
  Once this has been set up, components can subscribe to particular registries two different ways:
57
57
  1. Using a matching event name with `EventSubscriberController`. The component will need to be a child of the registry component for this to work.