@brightspace-ui/core 3.92.0 → 3.93.1

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.
@@ -31,7 +31,7 @@
31
31
  <d2l-filter-dimension-set-value key="art" text="Art" count="12"></d2l-filter-dimension-set-value>
32
32
  <d2l-filter-dimension-set-value key="astronomy" text="Astronomy" count="2" selected></d2l-filter-dimension-set-value>
33
33
  <d2l-filter-dimension-set-value key="biology" text="Biology" count="15"></d2l-filter-dimension-set-value>
34
- <d2l-filter-dimension-set-value key="chemistry" text="Chemistry" count="1"disabled></d2l-filter-dimension-set-value>
34
+ <d2l-filter-dimension-set-value key="chemistry" text="Chemistry" count="1" disabled selected></d2l-filter-dimension-set-value>
35
35
  <d2l-filter-dimension-set-value key="drama" text="Drama" count="23" disabled></d2l-filter-dimension-set-value>
36
36
  <d2l-filter-dimension-set-value key="english" text="English" count="100"></d2l-filter-dimension-set-value>
37
37
  <d2l-filter-dimension-set-value key="how-to" text="How To Write a How To Article With a Flashy Title" count="100"></d2l-filter-dimension-set-value>
@@ -52,17 +52,31 @@ class SelectAll extends FocusMixin(LocalizeCoreElement(SelectionObserverMixin(Li
52
52
  return html`
53
53
  <d2l-input-checkbox
54
54
  aria-label="${this.localize('components.selection.select-all')}"
55
- @change="${this._handleCheckboxChange}"
56
- ?checked="${this.selectionInfo.state === SelectionInfo.states.all || this.selectionInfo.state === SelectionInfo.states.allPages}"
55
+ @change="${this.#handleCheckboxChange}"
56
+ ?checked="${this.#getIsChecked()}"
57
57
  ?disabled="${this.disabled}"
58
58
  description="${ifDefined(this.selectionInfo.state !== SelectionInfo.states.none ? summary : undefined)}"
59
- ?indeterminate="${this.selectionInfo.state === SelectionInfo.states.some}">
59
+ ?indeterminate="${this.#getIsIndeterminate()}">
60
60
  </d2l-input-checkbox>
61
61
  `;
62
62
  }
63
63
 
64
- _handleCheckboxChange(e) {
65
- if (this._provider) this._provider.setSelectionForAll(e.target.checked, false);
64
+ #getIsChecked() {
65
+ return this.selectionInfo.state === SelectionInfo.states.all || this.selectionInfo.state === SelectionInfo.states.allPages;
66
+ }
67
+
68
+ #getIsIndeterminate() {
69
+ return this.selectionInfo.state === SelectionInfo.states.some;
70
+ }
71
+
72
+ async #handleCheckboxChange(e) {
73
+ const checkbox = e.target;
74
+ if (this._provider) this._provider.setSelectionForAll(checkbox.checked, false);
75
+
76
+ // keep inner checkbox checked and indeterminate in sync with this.state
77
+ await this.updateComplete;
78
+ checkbox.checked = this.#getIsChecked();
79
+ checkbox.indeterminate = this.#getIsIndeterminate();
66
80
  }
67
81
 
68
82
  }
@@ -11,6 +11,7 @@
11
11
  import '../../dropdown/dropdown-menu.js';
12
12
  import '../../menu/menu.js';
13
13
  import '../../menu/menu-item.js';
14
+ import '../tab.js';
14
15
  import '../tabs.js';
15
16
  import '../tab-panel.js';
16
17
  </script>
@@ -35,35 +36,17 @@
35
36
  </d2l-demo-snippet>
36
37
 
37
38
  <h2>Tabs (paired d2l-tab with d2l-panel)</h2>
39
+ <div>This format is still a WIP. Please do not use yet.</div>
38
40
 
39
41
  <d2l-demo-snippet>
40
- <div>This format is not yet functional. Please do not use yet.</div>
41
42
  <template>
42
43
  <d2l-tabs>
43
44
  <d2l-tab id="all" text="All" slot="tabs"></d2l-tab>
44
45
  <d2l-tab id="biology" text="Biology" slot="tabs"></d2l-tab>
45
46
  <d2l-tab id="chemistry" text="Chemistry" slot="tabs"></d2l-tab>
46
- <d2l-tab id="earth" text="Earth &amp; Planetary Sciences" slot="tabs"></d2l-tab>
47
- <d2l-tab id="physics" text="Physics" slot="tabs"></d2l-tab>
48
- <d2l-tab id="math" text="Math" slot="tabs"></d2l-tab>
49
47
  <d2l-tab-panel labelled-by="all" slot="panels">Tab content for All</d2l-tab-panel>
50
48
  <d2l-tab-panel labelled-by="biology" slot="panels">Tab content for Biology</d2l-tab-panel>
51
49
  <d2l-tab-panel labelled-by="chemistry" slot="panels">Tab content for Chemistry</d2l-tab-panel>
52
- <d2l-tab-panel labelled-by="earth" slot="panels">Tab content for Earth &amp; Planetary Sciences</d2l-tab-panel>
53
- <d2l-tab-panel labelled-by="physics" slot="panels">Tab content for physics</d2l-tab-panel>
54
- <d2l-tab-panel labelled-by="math" slot="panels">Tab content for math</d2l-tab-panel>
55
- <d2l-dropdown-button-subtle slot="ext" text="Explore Topics">
56
- <d2l-dropdown-menu>
57
- <d2l-menu label="Astronomy">
58
- <d2l-menu-item text="Introduction"></d2l-menu-item>
59
- <d2l-menu-item text="Searching for the Heavens "></d2l-menu-item>
60
- <d2l-menu-item text="The Solar System"></d2l-menu-item>
61
- <d2l-menu-item text="Stars &amp; Galaxies"></d2l-menu-item>
62
- <d2l-menu-item text="The Night Sky"></d2l-menu-item>
63
- <d2l-menu-item text="The Universe"></d2l-menu-item>
64
- </d2l-menu>
65
- </d2l-dropdown-menu>
66
- </d2l-dropdown-button-subtle>
67
50
  </d2l-tabs>
68
51
  </template>
69
52
  </d2l-demo-snippet>
@@ -12,6 +12,8 @@ export const TabMixin = superclass => class extends SkeletonMixin(superclass) {
12
12
  static get properties() {
13
13
  return {
14
14
  selected: { type: Boolean, reflect: true },
15
+ // eslint-disable-next-line lit/no-native-attributes
16
+ role: { type: String, reflect: true }
15
17
  };
16
18
  }
17
19
 
@@ -62,10 +64,8 @@ export const TabMixin = superclass => class extends SkeletonMixin(superclass) {
62
64
 
63
65
  constructor() {
64
66
  super();
65
- this.ariaSelected = false;
66
67
  this.role = 'tab';
67
68
  this.selected = false;
68
- this.tabIndex = -1;
69
69
  }
70
70
 
71
71
  connectedCallback() {
@@ -22,7 +22,6 @@ const scrollButtonWidth = 56;
22
22
  * A component for tabbed content. It supports the "d2l-tab-panel" component for the content, renders tabs responsively, and provides virtual scrolling for large tab lists.
23
23
  * @slot - Contains the tab panels (e.g., "d2l-tab-panel" components)
24
24
  * @slot ext - Additional content (e.g., a button) positioned at right
25
- * @fires d2l-tabs-initialized - Dispatched when the component is initialized
26
25
  */
27
26
  class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))) {
28
27
 
@@ -35,6 +34,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
35
34
  maxToShow: { type: Number, attribute: 'max-to-show' },
36
35
  _allowScrollNext: { type: Boolean },
37
36
  _allowScrollPrevious: { type: Boolean },
37
+ _defaultSlotBehavior: { state: true },
38
38
  _maxWidth: { type: Number },
39
39
  _scrollCollapsed: { type: Boolean },
40
40
  _state: { type: String },
@@ -199,12 +199,13 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
199
199
  this.maxToShow = -1;
200
200
  this._allowScrollNext = false;
201
201
  this._allowScrollPrevious = false;
202
+ this._defaultSlotBehavior = true; // remove after d2l-tab/d2l-tab-panel backport
202
203
  this._loadingCompleteResolve = undefined;
203
204
  this._loadingCompletePromise = new Promise(resolve => this._loadingCompleteResolve = resolve);
204
205
  this._maxWidth = null;
205
206
  this._scrollCollapsed = false;
206
207
  this._state = 'shown';
207
- this._tabInfos = [];
208
+ this._tabInfos = []; // remove after d2l-tab/d2l-tab-panel backport
208
209
  this._translationValue = 0;
209
210
  }
210
211
 
@@ -315,6 +316,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
315
316
  text="${tabInfo.text}">
316
317
  </d2l-tab-internal>
317
318
  `)}
319
+ <slot name="tabs" @slotchange="${this._handleTabsSlotChange}"></slot>
318
320
  </div>
319
321
  `)}
320
322
  <div class="d2l-tabs-scroll-next-container">
@@ -330,7 +332,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
330
332
  <div class="${classMap(panelContainerClasses)}"
331
333
  @d2l-tab-panel-selected="${this._handlePanelSelected}"
332
334
  @d2l-tab-panel-text-changed="${this._handlePanelTextChange}">
333
- <slot @slotchange="${this._handlePanelsSlotChange}"></slot>
335
+ <slot @slotchange="${this._handleDefaultSlotChange}"></slot>
336
+ <slot name="panels"></slot>
334
337
  </div>
335
338
  `;
336
339
  }
@@ -340,7 +343,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
340
343
  }
341
344
 
342
345
  async getLoadingComplete() {
343
- return this._loadingCompletePromise;
346
+ return this._defaultSlotBehavior ? this._loadingCompletePromise : true;
344
347
  }
345
348
 
346
349
  getTabListRect() {
@@ -495,6 +498,16 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
495
498
  }
496
499
 
497
500
  _getPanel(id) {
501
+ if (this._defaultSlotBehavior) return this._getPanelDefaultSlotBehavior(id);
502
+
503
+ if (!this.shadowRoot) return;
504
+ const slot = this.shadowRoot.querySelector('slot[name="panels"]');
505
+ const panels = this._getPanels(slot);
506
+ return panels.find(panel => panel.labelledBy === id);
507
+ }
508
+
509
+ // remove after d2l-tab/d2l-tab-panel backport
510
+ _getPanelDefaultSlotBehavior(id) {
498
511
  if (!this.shadowRoot) return;
499
512
  // use simple selector for slot (Edge)
500
513
  const slot = this.shadowRoot.querySelector('.d2l-panels-container').querySelector('slot');
@@ -508,30 +521,16 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
508
521
 
509
522
  _getPanels(slot) {
510
523
  if (!slot) return;
511
- return slot.assignedNodes({ flatten: true })
512
- .filter((node) => node.nodeType === Node.ELEMENT_NODE && node.role === 'tabpanel');
524
+ return slot.assignedElements({ flatten: true }).filter((node) => node.role === 'tabpanel');
513
525
  }
514
526
 
527
+ // remove after d2l-tab/d2l-tab-panel backport
515
528
  _getTabInfo(id) {
516
529
  return this._tabInfos.find((t) => t.id === id);
517
530
  }
518
531
 
519
- _handleFocusOut(e) {
520
- if (e.relatedTarget && e.relatedTarget.role === 'tab') return;
521
- this._resetFocusables();
522
- }
523
-
524
- _handlePanelSelected(e) {
525
- const tabInfo = this._getTabInfo(e.target.id);
526
- // event could be from nested tabs
527
- if (!tabInfo) return;
528
-
529
- this._setFocusable(tabInfo);
530
- tabInfo.selected = true;
531
- this.requestUpdate();
532
- }
533
-
534
- async _handlePanelsSlotChange(e) {
532
+ async _handleDefaultSlotChange(e) {
533
+ if (!this._defaultSlotBehavior) return;
535
534
 
536
535
  const panels = this._getPanels(e.target);
537
536
 
@@ -611,10 +610,24 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
611
610
  });
612
611
  }
613
612
 
614
- this.dispatchEvent(new CustomEvent(
615
- 'd2l-tabs-initialized', { bubbles: true, composed: true }
616
- ));
613
+ }
617
614
 
615
+ _handleFocusOut(e) {
616
+ if (e.relatedTarget && e.relatedTarget.role === 'tab') return;
617
+ this._resetFocusables();
618
+ }
619
+
620
+ // remove after d2l-tab/d2l-tab-panel backport
621
+ _handlePanelSelected(e) {
622
+ if (!this._defaultSlotBehavior) return;
623
+
624
+ const tabInfo = this._getTabInfo(e.target.id);
625
+ // event could be from nested tabs
626
+ if (!tabInfo) return;
627
+
628
+ this._setFocusable(tabInfo);
629
+ tabInfo.selected = true;
630
+ this.requestUpdate();
618
631
  }
619
632
 
620
633
  async _handlePanelTextChange(e) {
@@ -713,6 +726,35 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
713
726
  }
714
727
 
715
728
  async _handleTabSelected(e) {
729
+ if (this._defaultSlotBehavior) {
730
+ this._handleTabSelectedDefaultSlotBehavior(e);
731
+ return;
732
+ }
733
+
734
+ const selectedTab = e.target;
735
+ const selectedPanel = this._getPanel(e.target.id);
736
+ selectedTab.tabIndex = 0;
737
+
738
+ await this.updateComplete;
739
+
740
+ selectedPanel.selected = true;
741
+ this._tabs.forEach((tab) => {
742
+ if (tab.id !== selectedTab.id) {
743
+ if (tab.selected) {
744
+ tab.selected = false;
745
+ const panel = this._getPanel(tab.id);
746
+ // panel may not exist if it's being removed
747
+ if (panel) panel.selected = false;
748
+ }
749
+ if (tab.tabIndex === 0) tab.tabIndex = -1;
750
+ }
751
+ });
752
+
753
+ this.requestUpdate();
754
+ }
755
+
756
+ // remove after d2l-tab/d2l-tab-panel backport
757
+ async _handleTabSelectedDefaultSlotBehavior(e) {
716
758
  e.stopPropagation();
717
759
 
718
760
  const selectedTab = e.target;
@@ -739,6 +781,38 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
739
781
  this.requestUpdate();
740
782
  }
741
783
 
784
+ async _handleTabsSlotChange(e) {
785
+ this._defaultSlotBehavior = false;
786
+
787
+ this._tabs = e.target.assignedElements({ flatten: true }).filter((node) => node.role === 'tab');
788
+
789
+ // handle case where there are less than two tabs initially
790
+ this._updateTabListVisibility(this._tabs);
791
+
792
+ if (!this._initialized && this._tabs.length === 0) return;
793
+
794
+ let selectedTab;
795
+ if (this._tabs.length > 0) {
796
+ selectedTab = this._tabs.find((tab) => tab.state !== 'removing');
797
+ if (selectedTab) {
798
+ selectedTab.selected = true;
799
+ selectedTab.tabIndex = 0;
800
+ }
801
+ }
802
+
803
+ await this.updateComplete;
804
+
805
+ if (!this._initialized && this._tabs.length > 0) {
806
+ this._initialized = true;
807
+ }
808
+
809
+ if (selectedTab) {
810
+ // set corresponding panel to selected
811
+ const selectedPanel = this._getPanel(selectedTab.id);
812
+ if (selectedPanel) selectedPanel.selected = true;
813
+ }
814
+ }
815
+
742
816
  _isPositionInLeftScrollArea(position) {
743
817
  return position > 0 && position < scrollButtonWidth;
744
818
  }
@@ -12838,6 +12838,11 @@
12838
12838
  "description": "ACCESSIBILITY: REQUIRED: The text used for the tab, as well as labelling the panel.",
12839
12839
  "type": "string"
12840
12840
  },
12841
+ {
12842
+ "name": "role",
12843
+ "type": "string",
12844
+ "default": "\"tab\""
12845
+ },
12841
12846
  {
12842
12847
  "name": "selected",
12843
12848
  "type": "boolean",
@@ -12856,13 +12861,9 @@
12856
12861
  "description": "ACCESSIBILITY: REQUIRED: The text used for the tab, as well as labelling the panel.",
12857
12862
  "type": "string"
12858
12863
  },
12859
- {
12860
- "name": "ariaSelected",
12861
- "type": "boolean",
12862
- "default": "false"
12863
- },
12864
12864
  {
12865
12865
  "name": "role",
12866
+ "attribute": "role",
12866
12867
  "type": "string",
12867
12868
  "default": "\"tab\""
12868
12869
  },
@@ -12872,11 +12873,6 @@
12872
12873
  "type": "boolean",
12873
12874
  "default": "false"
12874
12875
  },
12875
- {
12876
- "name": "tabIndex",
12877
- "type": "number",
12878
- "default": "-1"
12879
- },
12880
12876
  {
12881
12877
  "name": "skeleton",
12882
12878
  "attribute": "skeleton",
@@ -12935,12 +12931,6 @@
12935
12931
  "type": "boolean"
12936
12932
  }
12937
12933
  ],
12938
- "events": [
12939
- {
12940
- "name": "d2l-tabs-initialized",
12941
- "description": "Dispatched when the component is initialized"
12942
- }
12943
- ],
12944
12934
  "slots": [
12945
12935
  {
12946
12936
  "name": "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.92.0",
3
+ "version": "3.93.1",
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",