@brightspace-ui/core 3.86.4 → 3.87.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.
@@ -48,6 +48,13 @@
48
48
  </template>
49
49
  </d2l-demo-snippet>
50
50
 
51
+ <h2>Disabled checkbox with tooltip</h2>
52
+ <d2l-demo-snippet>
53
+ <template>
54
+ <d2l-input-checkbox checked disabled disabled-tooltip="Explanation for why checkbox is disabled">Disabled checkbox with tooltip</d2l-input-checkbox>
55
+ </template>
56
+ </d2l-demo-snippet>
57
+
51
58
  <h2>Checkbox with label and secondary content</h2>
52
59
  <d2l-demo-snippet>
53
60
  <template>
@@ -1,12 +1,12 @@
1
1
  import '../colors/colors.js';
2
- import { css, html, LitElement } from 'lit';
2
+ import '../tooltip/tooltip.js';
3
+ import { css, html, LitElement, nothing } from 'lit';
3
4
  import { classMap } from 'lit/directives/class-map.js';
4
5
  import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
5
6
  import { getUniqueId } from '../../helpers/uniqueId.js';
6
7
  import { ifDefined } from 'lit/directives/if-defined.js';
7
8
  import { InputInlineHelpMixin } from './input-inline-help.js';
8
9
  import { offscreenStyles } from '../offscreen/offscreen.js';
9
- import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
10
10
  import { SkeletonMixin } from '../skeleton/skeleton-mixin.js';
11
11
 
12
12
  export const cssSizes = {
@@ -53,7 +53,8 @@ export const checkboxStyles = css`
53
53
  border-width: 2px;
54
54
  outline-width: 0;
55
55
  }
56
- input[type="checkbox"].d2l-input-checkbox:disabled {
56
+ input[type="checkbox"].d2l-input-checkbox:disabled,
57
+ input[type="checkbox"].d2l-input-checkbox[aria-disabled="true"] {
57
58
  opacity: 0.5;
58
59
  }
59
60
  `;
@@ -64,7 +65,7 @@ export const checkboxStyles = css`
64
65
  * @slot inline-help - Help text that will appear below the input. Use this only when other helpful cues are not sufficient, such as a carefully-worded label.
65
66
  * @fires change - Dispatched when the checkbox's state changes
66
67
  */
67
- class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMixin(LitElement)))) {
68
+ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(LitElement))) {
68
69
 
69
70
  static get properties() {
70
71
  return {
@@ -88,6 +89,11 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
88
89
  * @type {boolean}
89
90
  */
90
91
  disabled: { type: Boolean },
92
+ /**
93
+ * Tooltip text when disabled
94
+ * @type {string}
95
+ */
96
+ disabledTooltip: { type: String, attribute: 'disabled-tooltip' },
91
97
  /**
92
98
  * Sets checkbox to an indeterminate state
93
99
  * @type {boolean}
@@ -107,7 +113,8 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
107
113
  * Value of the input
108
114
  * @type {string}
109
115
  */
110
- value: { type: String }
116
+ value: { type: String },
117
+ _isHovered: { state: true },
111
118
  };
112
119
  }
113
120
 
@@ -138,21 +145,12 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
138
145
  display: inline-block;
139
146
  font-size: 0.8rem;
140
147
  font-weight: 400;
141
- margin-left: ${cssSizes.checkboxMargin}rem;
148
+ margin-inline-start: ${cssSizes.checkboxMargin}rem;
142
149
  vertical-align: top;
143
150
  white-space: normal;
144
151
  }
145
- :host([dir="rtl"]) .d2l-input-checkbox-text {
146
- margin-left: 0;
147
- margin-right: ${cssSizes.checkboxMargin}rem;
148
- }
149
152
  :host([aria-label]) .d2l-input-checkbox-text {
150
- margin-left: 0;
151
- margin-right: 0;
152
- }
153
- :host([dir="rtl"][aria-label]) .d2l-input-checkbox-text {
154
- margin-left: 0;
155
- margin-right: 0;
153
+ margin-inline-start: 0;
156
154
  }
157
155
  :host([skeleton]) .d2l-input-checkbox-text.d2l-skeletize::before {
158
156
  bottom: 0.3rem;
@@ -182,8 +180,7 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
182
180
  this.name = '';
183
181
  this.notTabbable = false;
184
182
  this.value = 'on';
185
- this._descriptionId = getUniqueId();
186
- this._inlineHelpId = getUniqueId();
183
+ this._isHovered = false;
187
184
  }
188
185
 
189
186
  static get focusElementSelector() {
@@ -199,27 +196,33 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
199
196
  };
200
197
  const ariaChecked = this.indeterminate ? 'mixed' : undefined;
201
198
  const disabled = this.disabled || this.skeleton;
202
- const offscreenContainer = this.description ? html`<div class="d2l-offscreen" id="${this._descriptionId}">${this.description}</div>` : null;
203
- const ariaDescribedByIds = `${this.description ? this._descriptionId : ''} ${this._hasInlineHelp ? this._inlineHelpId : ''}`.trim();
199
+ const offscreenContainer = this.description ? html`<div class="d2l-offscreen" id="${this.#descriptionId}">${this.description}</div>` : null;
200
+ const ariaDescribedByIds = `${this.description ? this.#descriptionId : ''} ${this._hasInlineHelp ? this.#inlineHelpId : ''}`.trim();
201
+ const disabledTooltip = disabled && this.disabledTooltip ?
202
+ html`<d2l-tooltip align="start" class="vdiff-target" for="${this.#inputId}" ?force-show="${this._isHovered}" position="top">${this.disabledTooltip}</d2l-tooltip>` :
203
+ nothing;
204
204
  return html`
205
- <label>
205
+ <label @mouseleave="${this.#handleMouseLeave}" @mouseenter="${this.#handleMouseEnter}">
206
206
  <span class="d2l-input-checkbox-wrapper d2l-skeletize"><input
207
207
  aria-checked="${ifDefined(ariaChecked)}"
208
208
  aria-describedby="${ifDefined(ariaDescribedByIds.length > 0 ? ariaDescribedByIds : undefined)}"
209
+ aria-disabled="${ifDefined(disabled && this.disabledTooltip ? 'true' : undefined)}"
209
210
  aria-label="${ifDefined(this.ariaLabel)}"
210
- @change="${this._handleChange}"
211
+ @change="${this.#handleChange}"
211
212
  class="d2l-input-checkbox"
212
213
  @click="${this._handleClick}"
213
214
  .checked="${this.checked}"
214
- ?disabled="${disabled}"
215
+ ?disabled="${disabled && !this.disabledTooltip}"
216
+ id="${this.#inputId}"
215
217
  .indeterminate="${this.indeterminate}"
216
218
  name="${ifDefined(this.name)}"
217
219
  tabindex="${ifDefined(tabindex)}"
218
220
  type="checkbox"
219
221
  .value="${this.value}"></span><span class="${classMap(textClasses)}"><slot></slot></span>
220
222
  </label>
221
- ${this._renderInlineHelp(this._inlineHelpId)}
223
+ ${this._renderInlineHelp(this.#inlineHelpId)}
222
224
  ${offscreenContainer}
225
+ ${disabledTooltip}
223
226
  `;
224
227
  }
225
228
 
@@ -232,7 +235,11 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
232
235
  ));
233
236
  }
234
237
 
235
- _handleChange(e) {
238
+ #descriptionId = getUniqueId();
239
+ #inlineHelpId = getUniqueId();
240
+ #inputId = getUniqueId();
241
+
242
+ #handleChange(e) {
236
243
  this.checked = e.target.checked;
237
244
  this.indeterminate = false;
238
245
  this.dispatchEvent(new CustomEvent(
@@ -241,16 +248,13 @@ class InputCheckbox extends InputInlineHelpMixin(FocusMixin(SkeletonMixin(RtlMix
241
248
  ));
242
249
  }
243
250
 
244
- /**
245
- * This is needed only for Legacy-Edge AND going from indeterminate to checked/unchecked.
246
- * When the indeterminate state is set, and the checkbox is clicked, the _handleChange
247
- * function is NOT triggered, therefore we have to detect the click and handle it ourselves.
248
- */
249
- _handleClick() {
250
- const browserType = window.navigator.userAgent;
251
- if (this.indeterminate && (browserType.indexOf('Edge') > -1)) {
252
- this.simulateClick();
253
- }
251
+ #handleMouseEnter() {
252
+ this._isHovered = true;
253
+ }
254
+
255
+ #handleMouseLeave() {
256
+ this._isHovered = false;
254
257
  }
258
+
255
259
  }
256
260
  customElements.define('d2l-input-checkbox', InputCheckbox);
@@ -0,0 +1,301 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="UTF-8">
6
+ <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
7
+ <script type="module">
8
+ import '../../demo/demo-page.js';
9
+ import '../list-item-content.js';
10
+ import '../list-item.js';
11
+ import '../list-controls.js';
12
+ import '../list.js';
13
+ import '../../selection/selection-action.js';
14
+ </script>
15
+ <style>
16
+ d2l-list::before {
17
+ box-sizing: border-box;
18
+ color: var(--d2l-color-tungsten);
19
+ content: attr(data-selection-info);
20
+ font-size: 0.7rem;
21
+ inset-block-start: 0;
22
+ inset-inline-end: 0;
23
+ margin: 0 0.4rem;
24
+ padding: 0;
25
+ position: absolute;
26
+ }
27
+ </style>
28
+ </head>
29
+ <body unresolved>
30
+
31
+ <d2l-demo-page page-title="d2l-list (selection)">
32
+
33
+ <h2>Nested - 1 - all selectable</h2>
34
+
35
+ <d2l-demo-snippet>
36
+ <template>
37
+ <d2l-list grid>
38
+ <d2l-list-controls slot="controls">
39
+ <d2l-selection-action icon="tier1:bookmark-hollow" text="Bookmark" requires-selection></d2l-selection-action>
40
+ <d2l-selection-action icon="tier1:gear" text="Settings"></d2l-selection-action>
41
+ </d2l-list-controls>
42
+ <d2l-list-item selectable key="Item-1" label="Label for Item-1">
43
+ <d2l-list-item-content>
44
+ <div>Item-1 - Earth Sciences</div>
45
+ <div slot="supporting-info">Supporting Info</div>
46
+ </d2l-list-item-content>
47
+ <d2l-list slot="nested" grid separators="all">
48
+ <d2l-list-item selectable key="Item-1-1" label="Label for Item-1-1">
49
+ <d2l-list-item-content>
50
+ <div>Item-1-1 - Introductory Earth Sciences</div>
51
+ <div slot="supporting-info">Supporting Info</div>
52
+ </d2l-list-item-content>
53
+ </d2l-list-item>
54
+ <d2l-list-item selectable key="Item-1-2" label="Label for Item-1-2">
55
+ <d2l-list-item-content>
56
+ <div>Item-1-2 - Flow and Transport Through Fractured Rocks</div>
57
+ <div slot="supporting-info">Supporting Info</div>
58
+ </d2l-list-item-content>
59
+ </d2l-list-item>
60
+ <d2l-list-item selectable key="Item-1-3" label="Label for Item-1-3">
61
+ <d2l-list-item-content>
62
+ <div>Item-1-3 - Applied Wetland Science</div>
63
+ <div slot="supporting-info">Supporting Info</div>
64
+ </d2l-list-item-content>
65
+ </d2l-list-item>
66
+ </d2l-list>
67
+ </d2l-list-item>
68
+ <d2l-list-item selectable key="Item-2" label="Label for Item-2">
69
+ <d2l-list-item-content>
70
+ <div>Item-2 - Biology</div>
71
+ <div slot="supporting-info">Supporting Info</div>
72
+ </d2l-list-item-content>
73
+ </d2l-list-item>
74
+ <d2l-list-item selectable key="Item-3" label="Label for Item-3">
75
+ <d2l-list-item-content>
76
+ <div>Item-3 - Computer Science</div>
77
+ <div slot="supporting-info">Supporting Info</div>
78
+ </d2l-list-item-content>
79
+ </d2l-list-item>
80
+ </d2l-list>
81
+ </template>
82
+ </d2l-demo-snippet>
83
+
84
+ <h2>Nested - 2 - L1 not selectable, L2 selectable</h2>
85
+
86
+ <d2l-demo-snippet>
87
+ <template>
88
+ <d2l-list grid>
89
+ <d2l-list-controls slot="controls">
90
+ <d2l-selection-action icon="tier1:bookmark-hollow" text="Bookmark" requires-selection></d2l-selection-action>
91
+ <d2l-selection-action icon="tier1:gear" text="Settings"></d2l-selection-action>
92
+ </d2l-list-controls>
93
+ <d2l-list-item key="Item-1" label="Label for Item-1">
94
+ <d2l-list-item-content>
95
+ <div>Item-1 - Earth Sciences</div>
96
+ <div slot="supporting-info">Supporting Info</div>
97
+ </d2l-list-item-content>
98
+ <d2l-list slot="nested" grid separators="all">
99
+ <d2l-list-item selectable key="Item-1-1" label="Label for Item-1-1">
100
+ <d2l-list-item-content>
101
+ <div>Item-1-1 - Introductory Earth Sciences</div>
102
+ <div slot="supporting-info">Supporting Info</div>
103
+ </d2l-list-item-content>
104
+ </d2l-list-item>
105
+ <d2l-list-item selectable key="Item-1-2" label="Label for Item-1-2">
106
+ <d2l-list-item-content>
107
+ <div>Item-1-2 - Flow and Transport Through Fractured Rocks</div>
108
+ <div slot="supporting-info">Supporting Info</div>
109
+ </d2l-list-item-content>
110
+ </d2l-list-item>
111
+ <d2l-list-item selectable key="Item-1-3" label="Label for Item-1-3">
112
+ <d2l-list-item-content>
113
+ <div>Item-1-3 - Applied Wetland Science</div>
114
+ <div slot="supporting-info">Supporting Info</div>
115
+ </d2l-list-item-content>
116
+ </d2l-list-item>
117
+ </d2l-list>
118
+ </d2l-list-item>
119
+ <d2l-list-item key="Item-2" label="Label for Item-2">
120
+ <d2l-list-item-content>
121
+ <div>Item-2 - Biology</div>
122
+ <div slot="supporting-info">Supporting Info</div>
123
+ </d2l-list-item-content>
124
+ </d2l-list-item>
125
+ <d2l-list-item key="Item-3" label="Label for Item-3">
126
+ <d2l-list-item-content>
127
+ <div>Item-3 - Computer Science</div>
128
+ <div slot="supporting-info">Supporting Info</div>
129
+ </d2l-list-item-content>
130
+ </d2l-list-item>
131
+ </d2l-list>
132
+ </template>
133
+ </d2l-demo-snippet>
134
+
135
+ <h2>Nested - 3 - L1 selectable, L2 not selectable</h2>
136
+
137
+ <d2l-demo-snippet>
138
+ <template>
139
+ <d2l-list grid>
140
+ <d2l-list-controls slot="controls">
141
+ <d2l-selection-action icon="tier1:bookmark-hollow" text="Bookmark" requires-selection></d2l-selection-action>
142
+ <d2l-selection-action icon="tier1:gear" text="Settings"></d2l-selection-action>
143
+ </d2l-list-controls>
144
+ <d2l-list-item selectable key="Item-1" label="Label for Item-1">
145
+ <d2l-list-item-content>
146
+ <div>Item-1 - Earth Sciences</div>
147
+ <div slot="supporting-info">Supporting Info</div>
148
+ </d2l-list-item-content>
149
+ <d2l-list slot="nested" grid separators="all">
150
+ <d2l-list-item key="Item-1-1" label="Label for Item-1-1">
151
+ <d2l-list-item-content>
152
+ <div>Item-1-1 - Introductory Earth Sciences</div>
153
+ <div slot="supporting-info">Supporting Info</div>
154
+ </d2l-list-item-content>
155
+ </d2l-list-item>
156
+ <d2l-list-item key="Item-1-2" label="Label for Item-1-2">
157
+ <d2l-list-item-content>
158
+ <div>Item-1-2 - Flow and Transport Through Fractured Rocks</div>
159
+ <div slot="supporting-info">Supporting Info</div>
160
+ </d2l-list-item-content>
161
+ </d2l-list-item>
162
+ <d2l-list-item key="Item-1-3" label="Label for Item-1-3">
163
+ <d2l-list-item-content>
164
+ <div>Item-1-3 - Applied Wetland Science</div>
165
+ <div slot="supporting-info">Supporting Info</div>
166
+ </d2l-list-item-content>
167
+ </d2l-list-item>
168
+ </d2l-list>
169
+ </d2l-list-item>
170
+ <d2l-list-item selectable key="Item-2" label="Label for Item-2">
171
+ <d2l-list-item-content>
172
+ <div>Item-2 - Biology</div>
173
+ <div slot="supporting-info">Supporting Info</div>
174
+ </d2l-list-item-content>
175
+ </d2l-list-item>
176
+ <d2l-list-item selectable key="Item-3" label="Label for Item-3">
177
+ <d2l-list-item-content>
178
+ <div>Item-3 - Computer Science</div>
179
+ <div slot="supporting-info">Supporting Info</div>
180
+ </d2l-list-item-content>
181
+ </d2l-list-item>
182
+ </d2l-list>
183
+ </template>
184
+ </d2l-demo-snippet>
185
+
186
+ <h2>Nested - 4 - L1 selectable, L2 not selectable, L3 selectable</h2>
187
+
188
+ <d2l-demo-snippet>
189
+ <template>
190
+ <d2l-list grid>
191
+ <d2l-list-controls slot="controls">
192
+ <d2l-selection-action icon="tier1:bookmark-hollow" text="Bookmark" requires-selection></d2l-selection-action>
193
+ <d2l-selection-action icon="tier1:gear" text="Settings"></d2l-selection-action>
194
+ </d2l-list-controls>
195
+ <d2l-list-item selectable key="Item-1" label="Label for Item-1">
196
+ <d2l-list-item-content>
197
+ <div>Item-1 - Earth Sciences</div>
198
+ <div slot="supporting-info">Supporting Info</div>
199
+ </d2l-list-item-content>
200
+ <d2l-list slot="nested" grid separators="all">
201
+ <d2l-list-item key="Item-1-1" label="Label for Item-1-1">
202
+ <d2l-list-item-content>
203
+ <div>Item-1-1 - Introductory Earth Sciences</div>
204
+ <div slot="supporting-info">Supporting Info</div>
205
+ </d2l-list-item-content>
206
+ </d2l-list-item>
207
+ <d2l-list-item key="Item-1-2" label="Label for Item-1-2">
208
+ <d2l-list-item-content>
209
+ <div>Item-1-2 - Flow and Transport Through Fractured Rocks</div>
210
+ <div slot="supporting-info">Supporting Info</div>
211
+ </d2l-list-item-content>
212
+ <d2l-list slot="nested" grid separators="all">
213
+ <d2l-list-item selectable key="Item-1-2-1" label="Label for Item-1-2-1">
214
+ <d2l-list-item-content>
215
+ <div>Item-1-2-1 - Hydraulic Properties of Fractures</div>
216
+ <div slot="supporting-info">Supporting Info</div>
217
+ </d2l-list-item-content>
218
+ </d2l-list-item>
219
+ <d2l-list-item selectable key="Item-1-2-2" label="Label for Item-1-2-2">
220
+ <d2l-list-item-content>
221
+ <div>Item-1-2-2 - Contaminant Transport</div>
222
+ <div slot="supporting-info">Supporting Info</div>
223
+ </d2l-list-item-content>
224
+ </d2l-list-item>
225
+ </d2l-list>
226
+ </d2l-list-item>
227
+ <d2l-list-item key="Item-1-3" label="Label for Item-1-3">
228
+ <d2l-list-item-content>
229
+ <div>Item-1-3 - Applied Wetland Science</div>
230
+ <div slot="supporting-info">Supporting Info</div>
231
+ </d2l-list-item-content>
232
+ </d2l-list-item>
233
+ </d2l-list>
234
+ </d2l-list-item>
235
+ <d2l-list-item selectable key="Item-2" label="Label for Item-2">
236
+ <d2l-list-item-content>
237
+ <div>Item-2 - Biology</div>
238
+ <div slot="supporting-info">Supporting Info</div>
239
+ </d2l-list-item-content>
240
+ </d2l-list-item>
241
+ <d2l-list-item selectable key="Item-3" label="Label for Item-3">
242
+ <d2l-list-item-content>
243
+ <div>Item-3 - Computer Science</div>
244
+ <div slot="supporting-info">Supporting Info</div>
245
+ </d2l-list-item-content>
246
+ </d2l-list-item>
247
+ </d2l-list>
248
+ </template>
249
+ </d2l-demo-snippet>
250
+
251
+ <h2>Nested - 5 - L1 mixed</h2>
252
+
253
+ <d2l-demo-snippet>
254
+ <template>
255
+ <d2l-list grid>
256
+ <d2l-list-controls slot="controls">
257
+ <d2l-selection-action icon="tier1:bookmark-hollow" text="Bookmark" requires-selection></d2l-selection-action>
258
+ <d2l-selection-action icon="tier1:gear" text="Settings"></d2l-selection-action>
259
+ </d2l-list-controls>
260
+ <d2l-list-item selectable key="Item-1" label="Label for Item-1">
261
+ <d2l-list-item-content>
262
+ <div>Item-1 - Earth Sciences</div>
263
+ <div slot="supporting-info">Supporting Info</div>
264
+ </d2l-list-item-content>
265
+ </d2l-list-item>
266
+ <d2l-list-item key="Item-2" label="Label for Item-2">
267
+ <d2l-list-item-content>
268
+ <div>Item-2 - Biology</div>
269
+ <div slot="supporting-info">Supporting Info</div>
270
+ </d2l-list-item-content>
271
+ </d2l-list-item>
272
+ <d2l-list-item selectable key="Item-3" label="Label for Item-3">
273
+ <d2l-list-item-content>
274
+ <div>Item-3 - Computer Science</div>
275
+ <div slot="supporting-info">Supporting Info</div>
276
+ </d2l-list-item-content>
277
+ </d2l-list-item>
278
+ </d2l-list>
279
+ </template>
280
+ </d2l-demo-snippet>
281
+
282
+ </d2l-demo-page>
283
+
284
+ <script>
285
+ function updateSelectionInfoText(list) {
286
+ const info = list.getSelectionInfo(true);
287
+ list.setAttribute('data-selection-info', `Selection Info: ${info.state}, ${info.keys.length}`);
288
+ }
289
+
290
+ setTimeout(() => {
291
+ document.querySelectorAll('d2l-list').forEach(list => {
292
+ list.addEventListener('d2l-list-selection-changes', (e) => {
293
+ updateSelectionInfoText(e.target);
294
+ console.log('d2l-list-selection-changes', e.detail);
295
+ });
296
+ updateSelectionInfoText(list);
297
+ });
298
+ }, 1000);
299
+ </script>
300
+ </body>
301
+ </html>
@@ -70,7 +70,9 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(s
70
70
  const oldVal = this._selectionInfo;
71
71
  if (oldVal !== val) {
72
72
  this._selectionInfo = val;
73
- this.setSelected(this._selectionInfo.state === SelectionInfo.states.all);
73
+ if (this._selectionInfo.state !== SelectionInfo.states.notSet) {
74
+ this.setSelected(this._selectionInfo.state === SelectionInfo.states.all);
75
+ }
74
76
  this.requestUpdate('selectionInfo', oldVal);
75
77
  }
76
78
  }
@@ -86,7 +88,9 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(s
86
88
  updated(changedProperties) {
87
89
  super.updated(changedProperties);
88
90
  if (!this._selectionProvider || !changedProperties.has('selectionInfo')) return;
89
- this.selected = (this.selectionInfo.state === SelectionInfo.states.all);
91
+ if (this.selectionInfo.state !== SelectionInfo.states.notSet) {
92
+ this.selected = (this.selectionInfo.state === SelectionInfo.states.all);
93
+ }
90
94
  }
91
95
 
92
96
  willUpdate(changedProperties) {
@@ -174,8 +178,6 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(s
174
178
  }
175
179
 
176
180
  _updateNestedSelectionProvider() {
177
- if (!this.selectable) return;
178
-
179
181
  const nestedList = this._getNestedList();
180
182
  if (this._selectionProvider === nestedList) return;
181
183
 
@@ -651,9 +651,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
651
651
  }
652
652
 
653
653
  _onNestedSlotChange() {
654
- if (this.selectable) {
655
- this._onNestedSlotChangeCheckboxMixin();
656
- }
654
+ this._onNestedSlotChangeCheckboxMixin();
657
655
  const nestedList = this._getNestedList();
658
656
  if (this._hasNestedList !== !!nestedList) {
659
657
  this._hasNestedList = !!nestedList;
@@ -274,15 +274,24 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
274
274
  const selectionInfo = super.getSelectionInfo();
275
275
  if (!includeNested) return selectionInfo;
276
276
 
277
+ let state = selectionInfo.state;
277
278
  let keys = selectionInfo.keys;
278
279
 
279
280
  this.getItems().forEach(item => {
280
281
  if (item._selectionProvider) {
282
+ const itemSelectionInfo = item._selectionProvider.getSelectionInfo(true);
283
+ if (state === SelectionInfo.states.notSet) {
284
+ state = itemSelectionInfo.state;
285
+ } else if (state === SelectionInfo.states.none && itemSelectionInfo.state !== SelectionInfo.states.notSet && itemSelectionInfo.state !== SelectionInfo.states.none) {
286
+ state = SelectionInfo.states.some;
287
+ } else if (state === SelectionInfo.states.all && (itemSelectionInfo.state === SelectionInfo.states.some || itemSelectionInfo.state === SelectionInfo.states.none)) {
288
+ state = SelectionInfo.states.some;
289
+ }
281
290
  keys = [...keys, ...item._selectionProvider.getSelectionInfo(true).keys];
282
291
  }
283
292
  });
284
293
 
285
- return new SelectionInfo(keys, selectionInfo.state);
294
+ return new SelectionInfo(keys, state);
286
295
  }
287
296
 
288
297
  resizedCallback(width, breakpointsChanged) {
@@ -300,6 +309,16 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
300
309
  });
301
310
  }
302
311
 
312
+ setSelectionForAll(selected, selectAllPages) {
313
+ super.setSelectionForAll(selected, selectAllPages);
314
+ // list-specific logic to push selection state deeper into tree - required to support intermediate nested lists with no direct selectables but with their own nested lists containing selectables
315
+ this.getItems().forEach(item => {
316
+ if (!item.selectable && item._selectionProvider) {
317
+ item._selectionProvider.setSelectionForAll(selected, selectAllPages);
318
+ }
319
+ });
320
+ }
321
+
303
322
  _getItemByIndex(index) {
304
323
  const items = this.getItems() || [];
305
324
  return items[index];
@@ -16,6 +16,7 @@ export class SelectionInfo {
16
16
  if (!allEnabledSelected) allEnabledSelected = false;
17
17
  if (!keys) keys = [];
18
18
  if (!state) state = SelectionInfo.states.none;
19
+
19
20
  this.#allEnabledSelected = allEnabledSelected;
20
21
  this.#keys = keys;
21
22
  this.#state = state;
@@ -38,7 +39,8 @@ export class SelectionInfo {
38
39
  none: 'none',
39
40
  some: 'some',
40
41
  all: 'all',
41
- allPages: 'all-pages'
42
+ allPages: 'all-pages',
43
+ notSet: 'not-set'
42
44
  };
43
45
  }
44
46
 
@@ -98,12 +100,13 @@ export const SelectionMixin = superclass => class extends RtlMixin(CollectionMix
98
100
 
99
101
  getSelectionInfo() {
100
102
  let allEnabledSelected = true;
101
- let state = SelectionInfo.states.none;
103
+ let state = (this._selectionSelectables.size > 0 ? SelectionInfo.states.none : SelectionInfo.states.notSet);
102
104
  const keys = [];
103
105
 
104
106
  if (this._selectAllPages) {
105
107
  state = SelectionInfo.states.allPages;
106
108
  } else {
109
+
107
110
  this._selectionSelectables.forEach(selectable => {
108
111
  if (selectable.selected) keys.push(selectable.key);
109
112
  if (!selectable.disabled && !selectable.selected) allEnabledSelected = false;
@@ -5024,6 +5024,11 @@
5024
5024
  "description": "ACCESSIBILITY: Additional information communicated to screenreader users when focusing on the input",
5025
5025
  "type": "string"
5026
5026
  },
5027
+ {
5028
+ "name": "disabled-tooltip",
5029
+ "description": "Tooltip text when disabled",
5030
+ "type": "string"
5031
+ },
5027
5032
  {
5028
5033
  "name": "checked",
5029
5034
  "description": "Checked state",
@@ -5079,6 +5084,12 @@
5079
5084
  "description": "ACCESSIBILITY: Additional information communicated to screenreader users when focusing on the input",
5080
5085
  "type": "string"
5081
5086
  },
5087
+ {
5088
+ "name": "disabledTooltip",
5089
+ "attribute": "disabled-tooltip",
5090
+ "description": "Tooltip text when disabled",
5091
+ "type": "string"
5092
+ },
5082
5093
  {
5083
5094
  "name": "checked",
5084
5095
  "attribute": "checked",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.86.4",
3
+ "version": "3.87.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",