@brightspace-ui/core 1.214.0 → 1.218.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 (76) 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-checkbox-mixin.js +1 -1
  52. package/components/list/list-item-drag-drop-mixin.js +10 -5
  53. package/components/list/list-item-generic-layout.js +5 -4
  54. package/components/list/list-item-mixin.js +1 -0
  55. package/components/list/list.js +2 -0
  56. package/components/menu/demo/custom-view.js +1 -1
  57. package/components/menu/menu-item-link.js +1 -1
  58. package/components/menu/menu-item-mixin.js +1 -1
  59. package/components/menu/menu.js +2 -2
  60. package/components/overflow-group/overflow-group.js +1 -1
  61. package/components/scroll-wrapper/demo/scroll-wrapper-test.js +1 -1
  62. package/components/selection/selection-action.js +1 -1
  63. package/components/selection/selection-input.js +1 -1
  64. package/components/selection/selection-select-all.js +1 -1
  65. package/components/switch/switch-mixin.js +1 -1
  66. package/components/table/table-col-sort-button.js +1 -1
  67. package/components/tabs/tabs.js +20 -15
  68. package/components/tooltip/tooltip.js +1 -1
  69. package/controllers/subscriber/README.md +3 -3
  70. package/controllers/subscriber/subscriberControllers.js +4 -4
  71. package/custom-elements.json +13 -0
  72. package/directives/animate/animate.js +35 -18
  73. package/helpers/demo/announce-test.js +1 -0
  74. package/mixins/arrow-keys-mixin.js +3 -1
  75. package/package.json +1 -1
  76. package/templates/primary-secondary/primary-secondary.js +2 -1
@@ -108,7 +108,7 @@ export const ButtonMixin = superclass => class extends FocusVisiblePolyfillMixin
108
108
  }
109
109
 
110
110
  focus() {
111
- const button = this.shadowRoot.querySelector('button');
111
+ const button = this.shadowRoot && this.shadowRoot.querySelector('button');
112
112
  if (button) button.focus();
113
113
  }
114
114
 
@@ -557,7 +557,7 @@ class Calendar extends LocalizeCoreElement(RtlMixin(LitElement)) {
557
557
  await this.updateComplete;
558
558
  this._focusDateAddFocus();
559
559
  } else {
560
- const button = this.shadowRoot.querySelector('d2l-button-icon');
560
+ const button = this.shadowRoot && this.shadowRoot.querySelector('d2l-button-icon');
561
561
  if (button) button.focus();
562
562
  }
563
563
  }
@@ -582,7 +582,7 @@ class Calendar extends LocalizeCoreElement(RtlMixin(LitElement)) {
582
582
 
583
583
  async _getDateElement(date) {
584
584
  await this.updateComplete;
585
- return this.shadowRoot.querySelector(`td[data-date="${date.getDate()}"][data-month="${date.getMonth()}"][data-year="${date.getFullYear()}"]`);
585
+ return this.shadowRoot && this.shadowRoot.querySelector(`td[data-date="${date.getDate()}"][data-month="${date.getMonth()}"][data-year="${date.getFullYear()}"]`);
586
586
  }
587
587
 
588
588
  _getInitialFocusDate() {
@@ -715,7 +715,7 @@ class Calendar extends LocalizeCoreElement(RtlMixin(LitElement)) {
715
715
  if (!canUpdateFocusDate) this._focusDate = undefined;
716
716
  if (this._focusDate) this._focusDateAddFocus();
717
717
  else {
718
- const buttons = this.shadowRoot.querySelectorAll('d2l-button-icon');
718
+ const buttons = this.shadowRoot && this.shadowRoot.querySelectorAll('d2l-button-icon');
719
719
  if (buttons && buttons.length > 0) buttons[0].focus();
720
720
  }
721
721
  preventDefault = true;
@@ -742,7 +742,7 @@ class Calendar extends LocalizeCoreElement(RtlMixin(LitElement)) {
742
742
  if (!canUpdateFocusDate) this._focusDate = undefined;
743
743
  if (this._focusDate) this._focusDateAddFocus();
744
744
  else {
745
- const buttons = this.shadowRoot.querySelectorAll('d2l-button-icon');
745
+ const buttons = this.shadowRoot && this.shadowRoot.querySelectorAll('d2l-button-icon');
746
746
  if (buttons && buttons.length > 1) buttons[1].focus();
747
747
  }
748
748
  preventDefault = true;
@@ -832,7 +832,7 @@ class Calendar extends LocalizeCoreElement(RtlMixin(LitElement)) {
832
832
  }
833
833
 
834
834
  await this.updateComplete; // for case of keyboard navigation where second month contains no enabled dates
835
- if (this.shadowRoot.querySelector('.d2l-calendar-date:enabled')) {
835
+ if (this.shadowRoot && this.shadowRoot.querySelector('.d2l-calendar-date:enabled')) {
836
836
  const validDates = this.shadowRoot.querySelectorAll('.d2l-calendar-date:enabled');
837
837
  const focusDate = validDates[latestPossibleFocusDate ? (validDates.length - 1) : 0].parentNode;
838
838
  const year = focusDate.getAttribute('data-year');
@@ -149,11 +149,11 @@ class CardFooterLink extends RtlMixin(LitElement) {
149
149
  }
150
150
 
151
151
  _onBlur() {
152
- this.shadowRoot.querySelector('d2l-count-badge-icon').forceFocusRing = false;
152
+ if (this.shadowRoot) this.shadowRoot.querySelector('d2l-count-badge-icon').forceFocusRing = false;
153
153
  }
154
154
 
155
155
  _onFocus() {
156
- this.shadowRoot.querySelector('d2l-count-badge-icon').forceFocusRing = true;
156
+ if (this.shadowRoot) this.shadowRoot.querySelector('d2l-count-badge-icon').forceFocusRing = true;
157
157
  }
158
158
 
159
159
  }
@@ -307,7 +307,7 @@ class Card extends RtlMixin(LitElement) {
307
307
  }
308
308
 
309
309
  focus() {
310
- const elem = this.shadowRoot.querySelector('a');
310
+ const elem = this.shadowRoot && this.shadowRoot.querySelector('a');
311
311
  if (!elem) return;
312
312
  elem.focus();
313
313
  }
@@ -7,7 +7,7 @@ import { LocalizeCoreElement } from '../../lang/localize-core-element.js';
7
7
  import { offscreenStyles } from '../offscreen/offscreen.js';
8
8
  import { RtlMixin } from '../../mixins/rtl-mixin.js';
9
9
  import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
10
- import { styleMap } from 'lit-html/directives/style-map';
10
+ import { styleMap } from 'lit-html/directives/style-map.js';
11
11
 
12
12
  export const CountBadgeMixin = superclass => class extends LocalizeCoreElement(SkeletonMixin(RtlMixin(superclass))) {
13
13
 
@@ -36,7 +36,7 @@ class CodeView extends LitElement {
36
36
  const path = `/node_modules/prismjs/components/prism-${language}.min.js`;
37
37
  this._dependenciesPromise = import(path);
38
38
  }
39
- this._updateCode(this.shadowRoot.querySelector('slot'));
39
+ if (this.shadowRoot) this._updateCode(this.shadowRoot.querySelector('slot'));
40
40
  super.attributeChangedCallback(name, oldval, newval);
41
41
  }
42
42
 
@@ -52,11 +52,12 @@ class CodeView extends LitElement {
52
52
  }
53
53
 
54
54
  forceUpdate() {
55
- this._updateCode(this.shadowRoot.querySelector('slot'));
55
+ if (this.shadowRoot) this._updateCode(this.shadowRoot.querySelector('slot'));
56
56
  }
57
57
 
58
58
  get _codeTemplate() {
59
- return html`<pre class="language-${this.language}"><code class="language-${this.language}">${unsafeHTML(this._code)}</code></pre>`;
59
+ const code = this._code !== undefined ? unsafeHTML(this._code) : '';
60
+ return html`<pre class="language-${this.language}"><code class="language-${this.language}">${code}</code></pre>`;
60
61
  }
61
62
 
62
63
  _formatCode(text) {
@@ -105,18 +105,19 @@ class DemoSnippet extends LitElement {
105
105
 
106
106
  changedProperties.forEach((_, prop) => {
107
107
  if (prop === '_code') {
108
- this.shadowRoot.querySelector('d2l-code-view').forceUpdate();
108
+ if (this.shadowRoot) this.shadowRoot.querySelector('d2l-code-view').forceUpdate();
109
109
  this._updateHasSkeleton();
110
110
  }
111
111
  });
112
112
  }
113
113
 
114
114
  forceCodeUpdate() {
115
- this._updateCode(this.shadowRoot.querySelector('slot:not([name="_demo"])'));
115
+ if (this.shadowRoot) this._updateCode(this.shadowRoot.querySelector('slot:not([name="_demo"])'));
116
116
  }
117
117
 
118
118
  _applyAttr(name, value, applyToShadowRoot) {
119
119
  const query = this._isTemplate ? 'slot[name="_demo"]' : 'slot:not([name="_demo"])';
120
+ if (!this.shadowRoot) return;
120
121
  const nodes = this.shadowRoot.querySelector(query).assignedNodes();
121
122
  if (nodes.length === 0) return;
122
123
  const doApply = (nodes, isRoot) => {
@@ -191,6 +192,7 @@ class DemoSnippet extends LitElement {
191
192
  }
192
193
 
193
194
  _removeImportedDemo() {
195
+ if (!this.shadowRoot) return;
194
196
  const nodes = this.shadowRoot.querySelector('slot[name="_demo"]').assignedNodes();
195
197
  for (let i = nodes.length - 1; i === 0; i--) {
196
198
  nodes[i].parentNode.removeChild(nodes[i]);
@@ -231,6 +233,7 @@ class DemoSnippet extends LitElement {
231
233
  _updateHasSkeleton() {
232
234
 
233
235
  const query = this._isTemplate ? 'slot[name="_demo"]' : 'slot:not([name="_demo"])';
236
+ if (!this.shadowRoot) return;
234
237
  const nodes = this.shadowRoot.querySelector(query).assignedNodes();
235
238
 
236
239
  const doApply = (nodes) => {
@@ -81,6 +81,7 @@ class DialogConfirm extends DialogMixin(LitElement) {
81
81
  }
82
82
 
83
83
  _focusInitial() {
84
+ if (!this.shadowRoot) return;
84
85
  const footer = this.shadowRoot.querySelector('.d2l-dialog-footer-slot');
85
86
  const nodes = footer.assignedNodes();
86
87
  for (let i = 0; i < nodes.length; i++) {
@@ -129,7 +129,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
129
129
 
130
130
  _addHandlers() {
131
131
  window.addEventListener('resize', this._updateSize);
132
- this.shadowRoot.querySelector('.d2l-dialog-content').addEventListener('scroll', this._updateOverflow);
132
+ if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dialog-content').addEventListener('scroll', this._updateOverflow);
133
133
  }
134
134
 
135
135
  _close(action) {
@@ -139,6 +139,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
139
139
  clearDismissible(this._dismissibleId);
140
140
  this._dismissibleId = null;
141
141
 
142
+ if (!this.shadowRoot) return;
142
143
  const dialog = this.shadowRoot.querySelector('.d2l-dialog-outer');
143
144
 
144
145
  const doClose = () => {
@@ -161,6 +162,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
161
162
  }
162
163
 
163
164
  _focusFirst() {
165
+ if (!this.shadowRoot) return;
164
166
  const content = this.shadowRoot.querySelector('.d2l-dialog-content');
165
167
  if (content) {
166
168
  const firstFocusable = getNextFocusable(content);
@@ -187,6 +189,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
187
189
  }
188
190
 
189
191
  _getHeight() {
192
+ if (!this.shadowRoot) return;
190
193
 
191
194
  const availableHeight = this._ifrauContextInfo
192
195
  ? this._ifrauContextInfo.availableHeight - this._margin.top - this._margin.bottom
@@ -303,6 +306,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
303
306
  this._action = undefined;
304
307
  this._addHandlers();
305
308
 
309
+ if (!this.shadowRoot) return;
306
310
  const dialog = this.shadowRoot.querySelector('.d2l-dialog-outer');
307
311
 
308
312
  const animPromise = new Promise((resolve) => {
@@ -357,7 +361,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
357
361
 
358
362
  _removeHandlers() {
359
363
  window.removeEventListener('resize', this._updateSize);
360
- this.shadowRoot.querySelector('.d2l-dialog-content').removeEventListener('scroll', this._updateOverflow);
364
+ if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dialog-content').removeEventListener('scroll', this._updateOverflow);
361
365
  }
362
366
 
363
367
  _render(inner, info, iframeTopOverride) {
@@ -423,6 +427,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
423
427
  }
424
428
 
425
429
  _updateOverflow() {
430
+ if (!this.shadowRoot) return;
426
431
  const content = this.shadowRoot.querySelector('.d2l-dialog-content');
427
432
  this._overflowTop = (content.scrollTop > 0);
428
433
  this._overflowBottom = (content.scrollHeight > content.scrollTop + content.clientHeight);
@@ -41,7 +41,7 @@ class DropdownButtonSubtle extends DropdownOpenerMixin(LitElement) {
41
41
  * @return {HTMLElement}
42
42
  */
43
43
  getOpenerElement() {
44
- return this.shadowRoot.querySelector('d2l-button-subtle');
44
+ return this.shadowRoot && this.shadowRoot.querySelector('d2l-button-subtle');
45
45
  }
46
46
 
47
47
  }
@@ -72,7 +72,7 @@ class DropdownButton extends DropdownOpenerMixin(RtlMixin(LitElement)) {
72
72
  * @return {HTMLElement}
73
73
  */
74
74
  getOpenerElement() {
75
- return this.shadowRoot.querySelector('d2l-button');
75
+ return this.shadowRoot && this.shadowRoot.querySelector('d2l-button');
76
76
  }
77
77
 
78
78
  }
@@ -338,7 +338,7 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
338
338
  };
339
339
 
340
340
  if (!reduceMotion && this._useMobileStyling && this.mobileTray && isVisible(this)) {
341
- this.shadowRoot.querySelector('.d2l-dropdown-content-width')
341
+ if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dropdown-content-width')
342
342
  .addEventListener('animationend', hide, { once: true });
343
343
  this._closing = true;
344
344
  this._showBackdrop = false;
@@ -399,15 +399,15 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
399
399
  }
400
400
 
401
401
  __getContentBottom() {
402
- return this.shadowRoot.querySelector('.d2l-dropdown-content-bottom');
402
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-bottom');
403
403
  }
404
404
 
405
405
  __getContentContainer() {
406
- return this.shadowRoot.querySelector('.d2l-dropdown-content-container');
406
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-container');
407
407
  }
408
408
 
409
409
  __getContentTop() {
410
- return this.shadowRoot.querySelector('.d2l-dropdown-content-top');
410
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-top');
411
411
  }
412
412
 
413
413
  __getOpener() {
@@ -420,11 +420,11 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
420
420
  }
421
421
 
422
422
  __getPositionContainer() {
423
- return this.shadowRoot.querySelector('.d2l-dropdown-content-position');
423
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-position');
424
424
  }
425
425
 
426
426
  __getWidthContainer() {
427
- return this.shadowRoot.querySelector('.d2l-dropdown-content-width');
427
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-width');
428
428
  }
429
429
 
430
430
  __handleFooterSlotChange(e) {
@@ -55,7 +55,7 @@ class DropdownContextMenu extends DropdownOpenerMixin(VisibleOnAncestorMixin(Lit
55
55
  * @return {HTMLElement}
56
56
  */
57
57
  getOpenerElement() {
58
- return this.shadowRoot.querySelector('d2l-button-icon');
58
+ return this.shadowRoot && this.shadowRoot.querySelector('d2l-button-icon');
59
59
  }
60
60
 
61
61
  }
@@ -51,6 +51,7 @@ class DropdownMenu extends ThemeMixin(DropdownContentMixin(LitElement)) {
51
51
  }
52
52
 
53
53
  __getMenuElement() {
54
+ if (!this.shadowRoot) return undefined;
54
55
  return this.shadowRoot.querySelector('.d2l-dropdown-content-slot')
55
56
  .assignedNodes().filter(node => node.hasAttribute
56
57
  && (node.getAttribute('role') === 'menu' || node.getAttribute('role') === 'listbox'))[0];
@@ -55,7 +55,7 @@ class DropdownMore extends DropdownOpenerMixin(VisibleOnAncestorMixin(LitElement
55
55
  * @return {HTMLElement}
56
56
  */
57
57
  getOpenerElement() {
58
- return this.shadowRoot.querySelector('d2l-button-icon');
58
+ return this.shadowRoot && this.shadowRoot.querySelector('d2l-button-icon');
59
59
  }
60
60
 
61
61
  }
@@ -170,6 +170,7 @@ export const DropdownOpenerMixin = superclass => class extends superclass {
170
170
  }
171
171
 
172
172
  __getContentElement() {
173
+ if (!this.shadowRoot) return undefined;
173
174
  return this.shadowRoot.querySelector('slot:not([name])').assignedNodes()
174
175
  .filter(node => node.hasAttribute && node.hasAttribute('dropdown-content'))[0];
175
176
  }
@@ -37,6 +37,7 @@ class DropdownTabs extends DropdownContentMixin(LitElement) {
37
37
  }
38
38
 
39
39
  _getTabsElement() {
40
+ if (!this.shadowRoot) return undefined;
40
41
  return this.shadowRoot.querySelector('.d2l-dropdown-content-container > slot')
41
42
  .assignedNodes()
42
43
  .filter(node => node.hasAttribute && node.tagName === 'D2L-TABS')[0];
@@ -21,6 +21,7 @@ class Dropdown extends DropdownOpenerMixin(LitElement) {
21
21
  * @return {HTMLElement}
22
22
  */
23
23
  getOpenerElement() {
24
+ if (!this.shadowRoot) return undefined;
24
25
  return this.shadowRoot.querySelector('slot')
25
26
  .assignedNodes()
26
27
  .filter(node => node.classList && node.classList.contains('d2l-dropdown-opener'))[0];
@@ -117,8 +117,8 @@ class ExpandCollapseContent extends LitElement {
117
117
  await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
118
118
  if (this._state === states.PREEXPANDING) {
119
119
  this._state = states.EXPANDING;
120
- const content = this.shadowRoot.querySelector('.d2l-expand-collapse-content-inner');
121
- this._height = `${content.scrollHeight}px`;
120
+ const content = this.shadowRoot && this.shadowRoot.querySelector('.d2l-expand-collapse-content-inner');
121
+ if (content) this._height = `${content.scrollHeight}px`;
122
122
  }
123
123
  }
124
124
  } else {
@@ -134,8 +134,8 @@ class ExpandCollapseContent extends LitElement {
134
134
  this._eventPromiseResolve();
135
135
  } else {
136
136
  this._state = states.PRECOLLAPSING;
137
- const content = this.shadowRoot.querySelector('.d2l-expand-collapse-content-inner');
138
- this._height = `${content.scrollHeight}px`;
137
+ const content = this.shadowRoot && this.shadowRoot.querySelector('.d2l-expand-collapse-content-inner');
138
+ if (content) this._height = `${content.scrollHeight}px`;
139
139
  await this.updateComplete;
140
140
  await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
141
141
  if (this._state === states.PRECOLLAPSING) {
@@ -189,6 +189,7 @@ Set dimension on mobile:
189
189
  | `select-all` | Boolean | Whether to show a select all checkbox and selection summary for this dimension |
190
190
  | `selection-single` | Boolean | Whether only one value can be selected at a time for this dimension |
191
191
  | `text` | String, required | Text for the dimension in the menu |
192
+ | `value-only-active-filter-text` | Boolean | Whether to hide the dimension in the text sent to active filter subscribers |
192
193
  <!-- docs: end hidden content -->
193
194
 
194
195
  ## Filter Dimension: Set Value [d2l-filter-dimension-set-value]
@@ -38,7 +38,12 @@ class FilterDimensionSet extends LitElement {
38
38
  * REQUIRED: The text that is displayed for the dimension title
39
39
  * @type {string}
40
40
  */
41
- text: { type: String }
41
+ text: { type: String },
42
+ /**
43
+ * Whether to hide the dimension in the text sent to active filter subscribers
44
+ * @type {boolean}
45
+ */
46
+ valueOnlyActiveFilterText: { type: Boolean, attribute: 'value-only-active-filter-text' }
42
47
  };
43
48
  }
44
49
 
@@ -49,6 +54,7 @@ class FilterDimensionSet extends LitElement {
49
54
  this.selectAll = false;
50
55
  this.selectionSingle = false;
51
56
  this.text = '';
57
+ this.valueOnlyActiveFilterText = false;
52
58
  this._slot = null;
53
59
  }
54
60
 
@@ -23,6 +23,7 @@ import { ifDefined } from 'lit-html/directives/if-defined.js';
23
23
  import { LocalizeCoreElement } from '../../lang/localize-core-element.js';
24
24
  import { offscreenStyles } from '../offscreen/offscreen.js';
25
25
  import { RtlMixin } from '../../mixins/rtl-mixin.js';
26
+ import { SubscriberRegistryController } from '../../controllers/subscriber/subscriberControllers.js';
26
27
 
27
28
  const ARROWLEFT_KEY_CODE = 37;
28
29
  const ESCAPE_KEY_CODE = 27;
@@ -143,6 +144,22 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
143
144
  this._dimensions = [];
144
145
  this._openedDimensions = [];
145
146
  this._totalAppliedCount = 0;
147
+
148
+ this._activeFilters = null;
149
+ this._activeFiltersSubscribers = new SubscriberRegistryController(this,
150
+ { onSubscribe: this._updateActiveFiltersSubscriber.bind(this), updateSubscribers: this._updateActiveFiltersSubscribers.bind(this) },
151
+ {}
152
+ );
153
+ }
154
+
155
+ connectedCallback() {
156
+ super.connectedCallback();
157
+ this._activeFiltersSubscribers.hostConnected();
158
+ }
159
+
160
+ disconnectedCallback() {
161
+ super.disconnectedCallback();
162
+ this._activeFiltersSubscribers.hostDisconnected();
146
163
  }
147
164
 
148
165
  firstUpdated(changedProperties) {
@@ -213,10 +230,14 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
213
230
  }
214
231
 
215
232
  focus() {
216
- const opener = this.shadowRoot.querySelector('d2l-dropdown-button-subtle');
233
+ const opener = this.shadowRoot && this.shadowRoot.querySelector('d2l-dropdown-button-subtle');
217
234
  if (opener) opener.focus();
218
235
  }
219
236
 
237
+ getSubscriberController() {
238
+ return this._activeFiltersSubscribers;
239
+ }
240
+
220
241
  requestFilterClearAll() {
221
242
  this._handleClearAll();
222
243
  }
@@ -423,6 +444,8 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
423
444
  }));
424
445
  this._changeEventsToDispatch = new Map();
425
446
  this._changeEventTimeout = null;
447
+
448
+ this._activeFiltersSubscribers.updateSubscribers();
426
449
  }
427
450
 
428
451
  _dispatchDimensionFirstOpenEvent(key) {
@@ -511,9 +534,11 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
511
534
  dimension.appliedCount--;
512
535
  this._totalAppliedCount--;
513
536
  }
537
+ this._activeFiltersSubscribers.updateSubscribers();
514
538
  } else if (prop === 'values') {
515
539
  if (dimension.searchValue) shouldSearch = true;
516
540
  shouldRecount = true;
541
+ this._activeFiltersSubscribers.updateSubscribers();
517
542
  }
518
543
  });
519
544
 
@@ -523,7 +548,7 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
523
548
  }
524
549
 
525
550
  _handleDimensionHide() {
526
- this.shadowRoot.querySelector(`d2l-hierarchical-view[data-key="${this._activeDimensionKey}"]`).hide();
551
+ if (this.shadowRoot) this.shadowRoot.querySelector(`d2l-hierarchical-view[data-key="${this._activeDimensionKey}"]`).hide();
527
552
  }
528
553
 
529
554
  _handleDimensionHideKeyPress(e) {
@@ -538,8 +563,9 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
538
563
  }
539
564
 
540
565
  _handleDimensionShowComplete() {
541
- const returnButton = this.shadowRoot.querySelector('d2l-button-icon[icon="tier1:chevron-left"]');
542
- returnButton.focus();
566
+ const returnButton = this.shadowRoot
567
+ && this.shadowRoot.querySelector('d2l-button-icon[icon="tier1:chevron-left"]');
568
+ if (returnButton) returnButton.focus();
543
569
  }
544
570
 
545
571
  _handleDimensionShowStart(e) {
@@ -608,6 +634,7 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
608
634
  info.searchType = dimension.searchType;
609
635
  info.selectionSingle = dimension.selectionSingle;
610
636
  if (dimension.selectAll && !dimension.selectionSingle) info.selectAllIdPrefix = SET_DIMENSION_ID_PREFIX;
637
+ info.valueOnlyActiveFilterText = dimension.valueOnlyActiveFilterText;
611
638
  const values = dimension._getValues();
612
639
  info.values = values;
613
640
  break;
@@ -618,6 +645,7 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
618
645
  });
619
646
 
620
647
  this._setFilterCounts();
648
+ this._activeFiltersSubscribers.updateSubscribers();
621
649
  }
622
650
 
623
651
  _isDimensionEmpty(dimension) {
@@ -716,6 +744,37 @@ class Filter extends LocalizeCoreElement(RtlMixin(LitElement)) {
716
744
  e.stopPropagation();
717
745
  }
718
746
 
747
+ _updateActiveFilters() {
748
+ const activeFilters = [];
749
+
750
+ this._dimensions.forEach(dimension => {
751
+ switch (dimension.type) {
752
+ case 'd2l-filter-dimension-set': {
753
+ dimension.values.forEach(value => {
754
+ if (value.selected) {
755
+ const keyObject = { dimension: dimension.key, value: value.key };
756
+ const text = dimension.valueOnlyActiveFilterText ? value.text : `${dimension.text}: ${value.text}`;
757
+ activeFilters.push({ keyObject: keyObject, text: text });
758
+ }
759
+ });
760
+ break;
761
+ }
762
+ }
763
+ });
764
+
765
+ this._activeFilters = activeFilters;
766
+ }
767
+
768
+ _updateActiveFiltersSubscriber(subscriber) {
769
+ if (!this._activeFilters) this._updateActiveFilters();
770
+ subscriber.updateActiveFilters(this.id, this._activeFilters);
771
+ }
772
+
773
+ _updateActiveFiltersSubscribers(subscribers) {
774
+ this._updateActiveFilters();
775
+ subscribers.forEach(subscriber => subscriber.updateActiveFilters(this.id, this._activeFilters));
776
+ }
777
+
719
778
  }
720
779
 
721
780
  customElements.define('d2l-filter', Filter);
@@ -56,18 +56,19 @@ class FocusTrap extends LitElement {
56
56
  }
57
57
 
58
58
  focus() {
59
- const focusable = this.shadowRoot.querySelector('.d2l-focus-trap-start');
59
+ const focusable = this.shadowRoot && this.shadowRoot.querySelector('.d2l-focus-trap-start');
60
60
  if (focusable) focusable.focus();
61
61
  }
62
62
 
63
63
  _focusFirst() {
64
- const focusable = getNextFocusable(this.shadowRoot.querySelector('.d2l-focus-trap-start'));
64
+ const focusable = this.shadowRoot &&
65
+ getNextFocusable(this.shadowRoot.querySelector('.d2l-focus-trap-start'));
65
66
  if (focusable) forceFocusVisible(focusable);
66
67
  this.dispatchEvent(new CustomEvent('d2l-focus-trap-enter', { bubbles: true, composed: true }));
67
68
  }
68
69
 
69
70
  _getContainer() {
70
- return this.shadowRoot.querySelector('.d2l-focus-trap-start').parentNode;
71
+ return this.shadowRoot && this.shadowRoot.querySelector('.d2l-focus-trap-start').parentNode;
71
72
  }
72
73
 
73
74
  _handleBodyFocus(e) {
@@ -80,7 +81,7 @@ class FocusTrap extends LitElement {
80
81
 
81
82
  _handleEndFocusIn(e) {
82
83
  const container = this._getContainer();
83
- if (isComposedAncestor(container, e.relatedTarget)) {
84
+ if (this.shadowRoot && isComposedAncestor(container, e.relatedTarget)) {
84
85
  // user is exiting trap via forward tabbing...
85
86
  const firstFocusable = getNextFocusable(this.shadowRoot.querySelector('.d2l-focus-trap-start'));
86
87
  if (firstFocusable) {
@@ -96,7 +97,7 @@ class FocusTrap extends LitElement {
96
97
 
97
98
  _handleStartFocusIn(e) {
98
99
  const container = this._getContainer();
99
- if (isComposedAncestor(container, e.relatedTarget)) {
100
+ if (this.shadowRoot && isComposedAncestor(container, e.relatedTarget)) {
100
101
  // user is exiting trap via back tabbing...
101
102
  const lastFocusable = getPreviousFocusable(this.shadowRoot.querySelector('.d2l-focus-trap-end'));
102
103
  if (lastFocusable) {
@@ -86,7 +86,7 @@ class FormNestedDemo extends LitElement {
86
86
  }
87
87
 
88
88
  _submit() {
89
- this.shadowRoot.querySelector('#root').submit();
89
+ if (this.shadowRoot) this.shadowRoot.querySelector('#root').submit();
90
90
  }
91
91
 
92
92
  _validatePassword(e) {
@@ -66,11 +66,11 @@ class FormDialogDemo extends LitElement {
66
66
  e.stopPropagation();
67
67
  // eslint-disable-next-line no-console
68
68
  console.log(e.detail.formData);
69
- this.shadowRoot.querySelector('#dialog').opened = false;
69
+ if (this.shadowRoot) this.shadowRoot.querySelector('#dialog').opened = false;
70
70
  }
71
71
 
72
72
  _onDialogSubmitClicked() {
73
- this.shadowRoot.querySelector('#dialog-secondary-form').submit();
73
+ if (this.shadowRoot) this.shadowRoot.querySelector('#dialog-secondary-form').submit();
74
74
  }
75
75
 
76
76
  _onSubmit(e) {
@@ -79,11 +79,11 @@ class FormDialogDemo extends LitElement {
79
79
  }
80
80
 
81
81
  _onSubmitClicked() {
82
- this.shadowRoot.querySelector('#dialog-main-form').submit();
82
+ if (this.shadowRoot) this.shadowRoot.querySelector('#dialog-main-form').submit();
83
83
  }
84
84
 
85
85
  _openDialog() {
86
- this.shadowRoot.querySelector('#dialog').opened = true;
86
+ if (this.shadowRoot) this.shadowRoot.querySelector('#dialog').opened = true;
87
87
  }
88
88
 
89
89
  _validatePassword(e) {
@@ -69,7 +69,7 @@ class FormNativeDemo extends LitElement {
69
69
  }
70
70
 
71
71
  _onClick(e) {
72
- this.shadowRoot.querySelector('d2l-form-native').requestSubmit(e.target);
72
+ if (this.shadowRoot) this.shadowRoot.querySelector('d2l-form-native').requestSubmit(e.target);
73
73
  }
74
74
 
75
75
  _validatePassword(e) {
@@ -136,9 +136,6 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
136
136
  /** @ignore */
137
137
  this.childErrors = new Map();
138
138
  this._errors = [];
139
-
140
- this.shadowRoot.addEventListener('d2l-validation-custom-connected', this._validationCustomConnected);
141
- this.shadowRoot.addEventListener('d2l-form-element-errors-change', this._onFormElementErrorsChange);
142
139
  }
143
140
 
144
141
  /** @ignore */
@@ -160,6 +157,18 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
160
157
  return this._validity;
161
158
  }
162
159
 
160
+ connectedCallback() {
161
+ super.connectedCallback();
162
+ this.shadowRoot.addEventListener('d2l-validation-custom-connected', this._validationCustomConnected);
163
+ this.shadowRoot.addEventListener('d2l-form-element-errors-change', this._onFormElementErrorsChange);
164
+ }
165
+
166
+ disconnectedCallback() {
167
+ super.disconnectedCallback();
168
+ this.shadowRoot.removeEventListener('d2l-validation-custom-connected', this._validationCustomConnected);
169
+ this.shadowRoot.removeEventListener('d2l-form-element-errors-change', this._onFormElementErrorsChange);
170
+ }
171
+
163
172
  updated(changedProperties) {
164
173
  if (changedProperties.has('_errors') || changedProperties.has('childErrors')) {
165
174
  let errors = this._errors;