@brightspace-ui/core 1.209.1 → 1.212.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.
package/README.md CHANGED
@@ -55,6 +55,8 @@ npm install @brightspace-ui/core
55
55
  * [Tooltip](components/tooltip/): tooltip components
56
56
  * [Typography](components/typography/): typography styles and components
57
57
  * [Validation](components/validation/): plugin custom validation logic to native and custom form elements
58
+ * Controllers
59
+ * [Subscriber](controllers/subscriber/): for managing a registry of subscribers in a many-to-many relationship
58
60
  * Directives
59
61
  * [Animate](directives/animate/): animate showing, hiding and removal of elements
60
62
  * Helpers
@@ -119,29 +121,32 @@ Note: The axe tests require `prefers-reduced-motion` emulation to be turned on i
119
121
 
120
122
  This repo uses the [@brightspace-ui/visual-diff utility](https://github.com/BrightspaceUI/visual-diff/) to compare current snapshots against a set of golden snapshots stored in source control.
121
123
 
122
- The golden snapshots in source control must be updated by Github Actions. If your PR's code changes result in visual differences, a PR with the new goldens will be automatically opened for you against your branch.
124
+ The golden snapshots in source control must be updated by the [visual-diff GitHub Action](https://github.com/BrightspaceUI/actions/tree/main/visual-diff). If a pull request results in visual differences, a draft pull request with the new goldens will automatically be opened against its branch.
123
125
 
124
- If you'd like to run the tests locally to help troubleshoot or develop new tests, you can use these commands:
126
+ To run the tests locally to help troubleshoot or develop new tests, first install these dependencies:
125
127
 
126
128
  ```shell
127
- # Install dependencies locally
128
- npm install esm mocha puppeteer @brightspace-ui/visual-diff --no-save
129
+ npm install @brightspace-ui/visual-diff@X mocha@Y puppeteer@Z --no-save
130
+ ```
129
131
 
130
- # run visual-diff tests
131
- npx mocha './**/*.visual-diff.js' -t 10000 --require esm
132
+ Replace `X`, `Y` and `Z` with [the current versions](https://github.com/BrightspaceUI/actions/tree/main/visual-diff#current-dependency-versions) the action is using.
132
133
 
133
- # subset of visual-diff tests:
134
- npx mocha './**/*.visual-diff.js' -t 10000 --require esm -g some-pattern
134
+ Then run the tests:
135
135
 
136
+ ```shell
137
+ # run visual-diff tests
138
+ npx mocha './**/*.visual-diff.js' -t 10000
139
+ # subset of visual-diff tests:
140
+ npx mocha './**/*.visual-diff.js' -t 10000 -g some-pattern
136
141
  # update visual-diff goldens
137
- npx mocha './**/*.visual-diff.js' -t 10000 --require esm --golden
142
+ npx mocha './**/*.visual-diff.js' -t 10000 --golden
138
143
  ```
139
144
 
140
145
  ## Versioning & Releasing
141
146
 
142
147
  > TL;DR: Commits prefixed with `fix:` and `feat:` will trigger patch and minor releases when merged to `main`. Read on for more details...
143
148
 
144
- The [semantic-release GitHub Action](https://github.com/BrightspaceUI/actions/tree/master/semantic-release) is called from the `release.yml` GitHub Action workflow to handle version changes and releasing.
149
+ The [semantic-release GitHub Action](https://github.com/BrightspaceUI/actions/tree/main/semantic-release) is called from the `release.yml` GitHub Action workflow to handle version changes and releasing.
145
150
 
146
151
  ### Version Changes
147
152
 
@@ -394,7 +394,7 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
394
394
  if (this.opened) {
395
395
  this.close();
396
396
  } else {
397
- this.open(applyFocus);
397
+ this.open(!this.noAutoFocus && applyFocus);
398
398
  }
399
399
  }
400
400
 
@@ -518,11 +518,10 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
518
518
 
519
519
  await this.__position();
520
520
  this._showBackdrop = this._useMobileStyling && this.mobileTray;
521
-
522
521
  if (!this.noAutoFocus && this.__applyFocus) {
523
522
  const focusable = getFirstFocusableDescendant(this);
524
523
  if (focusable) {
525
- // bumping this to the next frame is required to prevent Legacy-Edge from crazily invoking click on the focused element
524
+ // Removing the rAF call can allow infinite focus looping to happen in content using a focus trap
526
525
  requestAnimationFrame(() => focusable.focus());
527
526
  } else {
528
527
  content.setAttribute('tabindex', '-1');
@@ -1014,7 +1013,7 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
1014
1013
  const content = this.__getContentContainer();
1015
1014
  const focusable = getFirstFocusableDescendant(content);
1016
1015
  if (focusable) {
1017
- // bumping this to the next frame is required to prevent Legacy-Edge from crazily invoking click on the focused element
1016
+ // Removing the rAF call can allow infinite focus looping to happen in content using a focus trap
1018
1017
  requestAnimationFrame(() => focusable.focus());
1019
1018
  } else {
1020
1019
  content.setAttribute('tabindex', '-1');
@@ -112,9 +112,7 @@ class DropdownMenu extends ThemeMixin(DropdownContentMixin(LitElement)) {
112
112
 
113
113
  menu.resize();
114
114
 
115
- if (this.__applyFocus) {
116
- menu.focus();
117
- }
115
+ menu.focus();
118
116
  }
119
117
 
120
118
  _onSelect(e) {
@@ -262,7 +262,7 @@ export const DropdownOpenerMixin = superclass => class extends superclass {
262
262
  this._isHovering = false;
263
263
  this.openDropdown(false);
264
264
  }
265
- } else this.toggleOpen(false);
265
+ } else this.toggleOpen(true);
266
266
  }
267
267
 
268
268
  /* used by open-on-hover option */
@@ -5,7 +5,8 @@ import { html, LitElement } from 'lit-element/lit-element.js';
5
5
  import { ifDefined } from 'lit-html/directives/if-defined.js';
6
6
  import { repeat } from 'lit-html/directives/repeat.js';
7
7
 
8
- class ListDemoDragAndDropUsage extends LitElement {
8
+ class ListDemoDragAndDropPosition extends LitElement {
9
+
9
10
  static get properties() {
10
11
  return {
11
12
  list: { type: Array },
@@ -92,4 +93,4 @@ class ListDemoDragAndDropUsage extends LitElement {
92
93
  }
93
94
  }
94
95
 
95
- customElements.define('d2l-list-demo-drag-and-drop-usage', ListDemoDragAndDropUsage);
96
+ customElements.define('d2l-demo-list-drag-and-drop-position', ListDemoDragAndDropPosition);
@@ -6,45 +6,38 @@
6
6
  <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
7
7
  <script type="module">
8
8
  import '../../demo/demo-page.js';
9
- import './list-demo-drag-and-drop-usage.js';
9
+ import './list-drag-and-drop-position.js';
10
+ import './list-drag-and-drop.js';
10
11
  </script>
11
12
  </head>
12
13
  <body unresolved>
13
14
 
14
15
  <d2l-demo-page page-title="d2l-list (with drag & drop)">
15
16
 
16
- <h2>Draggable</h2>
17
+ <h2>Position Change Event</h2>
17
18
 
18
19
  <d2l-demo-snippet>
19
20
  <template>
20
- <d2l-list-demo-drag-and-drop-usage></d2l-list-demo-drag-and-drop-usage>
21
+ <d2l-demo-list-drag-and-drop-position grid selectable hrefs></d2l-demo-list-drag-and-drop-position>
21
22
  </template>
22
23
  </d2l-demo-snippet>
23
24
 
24
- <h2>Draggable and Selectable</h2>
25
+ <h2>Move Event</h2>
25
26
 
26
27
  <d2l-demo-snippet>
27
28
  <template>
28
- <d2l-list-demo-drag-and-drop-usage selectable></d2l-list-demo-drag-and-drop-usage>
29
+ <d2l-demo-list-drag-and-drop></d2l-demo-list-drag-and-drop>
29
30
  </template>
30
31
  </d2l-demo-snippet>
31
32
 
32
- <h2>Draggable with Grid and Selectable</h2>
33
33
 
34
- <d2l-demo-snippet>
35
- <template>
36
- <d2l-list-demo-drag-and-drop-usage grid selectable></d2l-list-demo-drag-and-drop-usage>
37
- </template>
38
- </d2l-demo-snippet>
39
-
40
- <h2>All the Fixins (grid, draggable, selectable, hrefs)</h2>
34
+ </d2l-demo-page>
41
35
 
42
- <d2l-demo-snippet>
43
- <template>
44
- <d2l-list-demo-drag-and-drop-usage grid selectable hrefs></d2l-list-demo-drag-and-drop-usage>
45
- </template>
46
- </d2l-demo-snippet>
36
+ <script>
37
+ document.body.addEventListener('d2l-list-items-move', e => {
38
+ console.log('d2l-list-items-move', e.detail);
39
+ });
40
+ </script>
47
41
 
48
- </d2l-demo-page>
49
42
  </body>
50
43
  </html>
@@ -0,0 +1,171 @@
1
+ import '../list-item-content.js';
2
+ import '../list-item.js';
3
+ import '../list.js';
4
+ import { html, LitElement } from 'lit-element/lit-element.js';
5
+ import { ifDefined } from 'lit-html/directives/if-defined.js';
6
+ import { moveLocations } from '../list-item-drag-drop-mixin.js';
7
+ import { repeat } from 'lit-html/directives/repeat.js';
8
+
9
+ class ListDemoDragAndDrop extends LitElement {
10
+
11
+ static get properties() {
12
+ return {
13
+ items: { type: Array }
14
+ };
15
+ }
16
+
17
+ constructor() {
18
+ super();
19
+ this.items = [{
20
+ key: '1',
21
+ primaryText: 'Introductory Earth Sciences',
22
+ supportingText: 'This course explores the geological processes of the Earth\'s interior and surface. These include volcanism, earthquakes, mountain building, glaciation and weathering.',
23
+ imgSrc: 'https://s.brightspace.com/course-images/images/63b162ab-b582-4bf9-8c1d-1dad04714121/tile-high-density-max-size.jpg',
24
+ items: [{
25
+ key: '1-1',
26
+ primaryText: 'Glaciation',
27
+ supportingText: 'Supporting Info',
28
+ imgSrc: '',
29
+ items: []
30
+ }, {
31
+ key: '1-2',
32
+ primaryText: 'Weathering',
33
+ supportingText: 'Supporting Info',
34
+ imgSrc: '',
35
+ items: []
36
+ }, {
37
+ key: '1-3',
38
+ primaryText: 'Volcanism',
39
+ supportingText: 'Supporting Info',
40
+ imgSrc: '',
41
+ items: []
42
+ }]
43
+ }, {
44
+ key: '2',
45
+ primaryText: 'Flow and Transport Through Fractured Rocks',
46
+ supportingText: 'Fractures are ubiquitous in geologic media and important in disciplines such as physical and contaminant hydrogeology, geotechnical engineering, civil and environmental engineering, petroleum engineering among other areas.',
47
+ imgSrc: 'https://s.brightspace.com/course-images/images/e5fd575a-bc14-4a80-89e1-46f349a76178/tile-high-density-max-size.jpg',
48
+ items: [{
49
+ key: '2-1',
50
+ primaryText: 'Contaminant Transport',
51
+ supportingText: 'Supporting Info',
52
+ imgSrc: '',
53
+ items: []
54
+ }, {
55
+ key: '2-2',
56
+ primaryText: 'Modelling Flow in Fractured Media',
57
+ supportingText: 'Supporting Info',
58
+ imgSrc: '',
59
+ items: []
60
+ }]
61
+ }, {
62
+ key: '3',
63
+ primaryText: 'Applied Wetland Science',
64
+ supportingText: 'Advanced concepts on wetland ecosystems in the context of regional and global earth systems processes such as carbon and nitrogen cycling and climate change, applications of wetland paleoecology, use of isotopes and other geochemical tools in wetland science, and wetland engineering in landscape rehabilitation and ecotechnology.',
65
+ imgSrc: 'https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg',
66
+ items: [{
67
+ key: '3-1',
68
+ primaryText: 'Carbon & Nitrogen Cycling',
69
+ supportingText: 'Supporting Info',
70
+ imgSrc: '',
71
+ items: []
72
+ }, {
73
+ key: '3-2',
74
+ primaryText: 'Wetland Engineering',
75
+ supportingText: 'Supporting Info',
76
+ imgSrc: '',
77
+ items: []
78
+ }]
79
+ }];
80
+ }
81
+
82
+ render() {
83
+ const renderList = (items, nested) => {
84
+ return html`
85
+ <d2l-list grid slot="${ifDefined(nested ? 'nested' : undefined)}">
86
+ ${repeat(items, item => item.key, item => html`
87
+ <d2l-list-item
88
+ action-href="http://www.d2l.com"
89
+ draggable
90
+ drag-handle-text="${item.primaryText}"
91
+ drop-nested
92
+ key="${item.key}"
93
+ label="${item.primaryText}"
94
+ selectable>
95
+ ${nested ? null : html`<img slot="illustration" src="${item.imgSrc}">`}
96
+ <d2l-list-item-content>
97
+ <div>${item.primaryText}</div>
98
+ <div slot="supporting-info">${item.supportingText}</div>
99
+ </d2l-list-item-content>
100
+ ${item.items.length > 0 ? renderList(item.items, true) : null}
101
+ </d2l-list-item>
102
+ `)}
103
+ </d2l-list>
104
+ `;
105
+ };
106
+
107
+ return html`
108
+ <div @d2l-list-items-move="${this._handleListItemsMove}">
109
+ ${renderList(this.items, false)}
110
+ </div>
111
+ `;
112
+ }
113
+
114
+ async _handleListItemsMove(e) {
115
+
116
+ const sourceListItems = e.detail.sourceItems;
117
+ const target = e.detail.target;
118
+
119
+ // helper that gets the array containing item data, the item data, and the index within the array
120
+ const getItemInfo = (items, key) => {
121
+ for (let i = 0; i < items.length; i++) {
122
+ if (items[i].key === key) {
123
+ return { owner: items, item: items[i], index: i };
124
+ }
125
+ if (items[i].items && items[i].items.length > 0) {
126
+ const tempItemData = getItemInfo(items[i].items, key);
127
+ if (tempItemData) return tempItemData;
128
+ }
129
+ }
130
+ };
131
+
132
+ const dataToMove = [];
133
+
134
+ // remove data elements from original locations
135
+ sourceListItems.forEach(sourceListItem => {
136
+ const info = getItemInfo(this.items, sourceListItem.key);
137
+ info.owner.splice(info.index, 1);
138
+ dataToMove.push(info.item);
139
+ });
140
+
141
+ // append data elements to new location
142
+ const targetInfo = getItemInfo(this.items, target.item.key);
143
+ let targetItems;
144
+ let targetIndex;
145
+ if (target.location === moveLocations.nest) {
146
+ if (!targetInfo.item.items) targetInfo.item.items = [];
147
+ targetItems = targetInfo.item.items;
148
+ targetIndex = targetItems.length;
149
+ } else {
150
+ targetItems = targetInfo.owner;
151
+ if (target.location === moveLocations.above) targetIndex = targetInfo.index;
152
+ else if (target.location === moveLocations.below) targetIndex = targetInfo.index + 1;
153
+ }
154
+ for (let i = dataToMove.length - 1; i >= 0; i--) {
155
+ targetItems.splice(targetIndex, 0, dataToMove[i]);
156
+ }
157
+
158
+ await this.requestUpdate();
159
+
160
+ if (e.detail.keyboardActive) {
161
+ requestAnimationFrame(() => {
162
+ const newItem = this.shadowRoot.querySelector('d2l-list').getListItemByKey(sourceListItems[0].key);
163
+ newItem.activateDragHandle();
164
+ });
165
+ }
166
+
167
+ }
168
+
169
+ }
170
+
171
+ customElements.define('d2l-demo-list-drag-and-drop', ListDemoDragAndDrop);
@@ -113,16 +113,6 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(L
113
113
  }));
114
114
  }
115
115
 
116
- _getNestedList() {
117
- const nestedSlot = this.shadowRoot.querySelector('slot[name="nested"]');
118
- let nestedNodes = nestedSlot.assignedNodes();
119
- if (nestedNodes.length === 0) {
120
- nestedNodes = [...nestedSlot.childNodes];
121
- }
122
-
123
- return nestedNodes.find(node => (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'D2L-LIST'));
124
- }
125
-
126
116
  _onCheckboxActionClick(event) {
127
117
  event.preventDefault();
128
118
  if (this.disabled) return;
@@ -140,6 +130,10 @@ export const ListItemCheckboxMixin = superclass => class extends SkeletonMixin(L
140
130
  }
141
131
  }
142
132
 
133
+ _onNestedSlotChange() {
134
+ this._updateNestedSelectionProvider();
135
+ }
136
+
143
137
  _onSelectionProviderConnected(e) {
144
138
  e.stopPropagation();
145
139
  this._updateNestedSelectionProvider();