@brightspace-ui/core 1.208.2 → 1.210.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.
package/README.md CHANGED
@@ -119,29 +119,32 @@ Note: The axe tests require `prefers-reduced-motion` emulation to be turned on i
119
119
 
120
120
  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
121
 
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.
122
+ 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
123
 
124
- If you'd like to run the tests locally to help troubleshoot or develop new tests, you can use these commands:
124
+ To run the tests locally to help troubleshoot or develop new tests, first install these dependencies:
125
125
 
126
126
  ```shell
127
- # Install dependencies locally
128
- npm install esm mocha puppeteer @brightspace-ui/visual-diff --no-save
127
+ npm install @brightspace-ui/visual-diff@X mocha@Y puppeteer@Z --no-save
128
+ ```
129
129
 
130
- # run visual-diff tests
131
- npx mocha './**/*.visual-diff.js' -t 10000 --require esm
130
+ 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
131
 
133
- # subset of visual-diff tests:
134
- npx mocha './**/*.visual-diff.js' -t 10000 --require esm -g some-pattern
132
+ Then run the tests:
135
133
 
134
+ ```shell
135
+ # run visual-diff tests
136
+ npx mocha './**/*.visual-diff.js' -t 10000
137
+ # subset of visual-diff tests:
138
+ npx mocha './**/*.visual-diff.js' -t 10000 -g some-pattern
136
139
  # update visual-diff goldens
137
- npx mocha './**/*.visual-diff.js' -t 10000 --require esm --golden
140
+ npx mocha './**/*.visual-diff.js' -t 10000 --golden
138
141
  ```
139
142
 
140
143
  ## Versioning & Releasing
141
144
 
142
145
  > TL;DR: Commits prefixed with `fix:` and `feat:` will trigger patch and minor releases when merged to `main`. Read on for more details...
143
146
 
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.
147
+ 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
148
 
146
149
  ### Version Changes
147
150
 
@@ -4,21 +4,38 @@ Alerts communicate critical information relating to the state of the system and
4
4
  <!-- docs: demo autoSize:false align:start -->
5
5
  ```html
6
6
  <script type="module">
7
- import '@brightspace-ui/core/components/alert/alert-toast.js';
7
+ import '@brightspace-ui/core/components/alert/alert-toast.js';
8
+ import '@brightspace-ui/core/components/button/button.js';
9
+
10
+ var alert = document.querySelector('#alert');
11
+ var alertToast = document.querySelector('#alert-toast');
12
+ var button = document.querySelector('#open');
13
+
14
+ alert.addEventListener('d2l-alert-close', function() {
15
+ if (!alertToast.open) button.style.display = 'block';
16
+ });
17
+ alertToast.addEventListener('d2l-alert-toast-close', function() {
18
+ if (alert.hasAttribute('hidden')) button.style.display = 'block';
19
+ });
20
+
21
+ button.addEventListener('click', () => {
22
+ alert.removeAttribute('hidden');
23
+ alertToast.open = true;
24
+ button.style.display = 'none';
25
+ });
8
26
  </script>
9
- <!-- docs: start hidden content -->
10
27
  <style>
11
28
  d2l-alert-toast {
12
29
  margin-left: 0 !important;
13
30
  margin-right: 0 !important;
14
31
  }
15
32
  </style>
16
- <!-- docs: end hidden content -->
17
33
 
18
- <d2l-alert type="default" button-text="Undo" has-close-button>
34
+ <d2l-button id="open" style="align-self:center;display:none;">Show Alerts</d2l-button>
35
+ <d2l-alert id="alert" type="default" button-text="Undo" has-close-button>
19
36
  A message.
20
37
  </d2l-alert>
21
- <d2l-alert-toast type="success" open no-auto-close>
38
+ <d2l-alert-toast id="alert-toast" type="success" open no-auto-close>
22
39
  A message.
23
40
  </d2l-alert-toast>
24
41
  ```
@@ -7,13 +7,17 @@ Dialogs interrupt the user to complete a set of tasks, confirm an action, or off
7
7
  <script type="module">
8
8
  import '@brightspace-ui/core/components/button/button.js';
9
9
  import '@brightspace-ui/core/components/dialog/dialog.js';
10
- </script>
11
10
 
11
+ document.querySelector('#open-demo').addEventListener('click', () => {
12
+ document.querySelector('#dialog-demo').opened = true;
13
+ });
14
+ </script>
12
15
  <d2l-dialog id="dialog-demo" title-text="Dialog Title">
13
16
  <div>Some dialog content</div>
14
17
  <d2l-button slot="footer" primary data-dialog-action="done">Done</d2l-button>
15
18
  <d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
16
19
  </d2l-dialog>
20
+ <d2l-button id="open-demo">Show Dialog</d2l-button>
17
21
  ```
18
22
 
19
23
  ## General Dialog [d2l-dialog]
@@ -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 */
@@ -97,6 +97,11 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
97
97
  * @ignore
98
98
  */
99
99
  noValidate: { type: Boolean, attribute: 'novalidate' },
100
+ /**
101
+ * @ignore
102
+ * Perform validation immediately instead of waiting for the user to make changes.
103
+ */
104
+ validateOnInit: { type: Boolean, attribute: 'validate-on-init' },
100
105
  /**
101
106
  * @ignore
102
107
  */
@@ -125,6 +130,8 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
125
130
  /** @ignore */
126
131
  this.noValidate = false;
127
132
  /** @ignore */
133
+ this.validateOnInit = false;
134
+ /** @ignore */
128
135
  this.validationError = null;
129
136
  /** @ignore */
130
137
  this.childErrors = new Map();
@@ -171,6 +178,9 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
171
178
  this.dispatchEvent(new CustomEvent('invalid-change'));
172
179
  }
173
180
  }
181
+ if (this.validateOnInit && (changedProperties.has('noValidate') || changedProperties.has('validateOnInit'))) {
182
+ this.requestValidate(true);
183
+ }
174
184
  }
175
185
 
176
186
  async requestValidate(showNewErrors = true) {
@@ -210,6 +220,9 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
210
220
 
211
221
  validationCustomConnected(custom) {
212
222
  this._validationCustoms.add(custom);
223
+ if (this.validateOnInit) {
224
+ this.requestValidate(true);
225
+ }
213
226
  }
214
227
 
215
228
  validationCustomDisconnected(custom) {
@@ -1,4 +1,6 @@
1
- export const MenuItemMixin = superclass => class extends superclass {
1
+
2
+ import { FocusVisiblePolyfillMixin } from '../../mixins/focus-visible-polyfill-mixin.js';
3
+ export const MenuItemMixin = superclass => class extends FocusVisiblePolyfillMixin(superclass) {
2
4
 
3
5
  static get properties() {
4
6
  return {
@@ -15,9 +15,10 @@ export const menuItemStyles = css`
15
15
  outline: none;
16
16
  width: 100%;
17
17
  }
18
- :host(:focus),
18
+
19
19
  :host(:hover),
20
- :host([first]:focus),
20
+ :host(.focus-visible),
21
+ :host(.focus-visible[first]),
21
22
  :host([first]:hover) {
22
23
  background-color: var(--d2l-menu-background-color-hover);
23
24
  border-bottom: 1px solid var(--d2l-menu-border-color-hover);
@@ -26,7 +27,7 @@ export const menuItemStyles = css`
26
27
  z-index: 2;
27
28
  }
28
29
 
29
- :host([disabled]), :host([disabled]:hover), :host([disabled]:focus) {
30
+ :host([disabled]), :host([disabled]:hover), :host([disabled].focus-visible) {
30
31
  cursor: default;
31
32
  opacity: 0.75;
32
33
  }
@@ -39,7 +40,7 @@ export const menuItemStyles = css`
39
40
  border-top-color: transparent;
40
41
  }
41
42
 
42
- :host([last]:focus),
43
+ :host([last].focus-visible),
43
44
  :host([last]:hover) {
44
45
  border-bottom-color: var(--d2l-menu-border-color-hover);
45
46
  }
@@ -2,6 +2,7 @@ import '../colors/colors.js';
2
2
  import '../icons/icon.js';
3
3
  import './menu-item-return.js';
4
4
  import { css, html, LitElement } from 'lit-element/lit-element.js';
5
+ import { FocusVisiblePolyfillMixin } from '../../mixins/focus-visible-polyfill-mixin.js';
5
6
  import { HierarchicalViewMixin } from '../hierarchical-view/hierarchical-view-mixin.js';
6
7
  import { ThemeMixin } from '../../mixins/theme-mixin.js';
7
8
 
@@ -20,7 +21,7 @@ const keyCodes = {
20
21
  * @slot - Menu items
21
22
  * @fires d2l-menu-resize - Dispatched when size of menu changes (e.g., when nested menu of a different size is opened)
22
23
  */
23
- class Menu extends ThemeMixin(HierarchicalViewMixin(LitElement)) {
24
+ class Menu extends ThemeMixin(HierarchicalViewMixin(FocusVisiblePolyfillMixin(LitElement))) {
24
25
 
25
26
  static get properties() {
26
27
  return {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "1.208.2",
3
+ "version": "1.210.1",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
+ "type": "module",
5
6
  "repository": "https://github.com/BrightspaceUI/core.git",
6
7
  "publishConfig": {
7
8
  "access": "public"
@@ -48,7 +49,7 @@
48
49
  "@web/test-runner": "^0.13",
49
50
  "@web/test-runner-playwright": "^0.8.8",
50
51
  "axe-core": "^4",
51
- "chalk": "^4",
52
+ "chalk": "^5",
52
53
  "eslint": "^7",
53
54
  "eslint-config-brightspace": "^0.16",
54
55
  "eslint-plugin-html": "^6",
@@ -56,9 +57,10 @@
56
57
  "eslint-plugin-lit": "^1",
57
58
  "eslint-plugin-sort-class-members": "^1",
58
59
  "lit-analyzer": "^1",
59
- "node-sass": "^6",
60
+ "node-sass": "^7",
60
61
  "sinon": "^12",
61
- "stylelint": "^14"
62
+ "stylelint": "^14",
63
+ "web-test-runner-performance": "^0.1.4"
62
64
  },
63
65
  "dependencies": {
64
66
  "@brightspace-ui/intl": "^3",
@@ -1,45 +0,0 @@
1
- import chai from '@open-wc/testing/import-wrappers/chai.js';
2
- import { fixture } from '@open-wc/testing';
3
-
4
- const numComponents = 1000;
5
-
6
- export async function runTest(element) {
7
- return new Promise((resolve) => {
8
-
9
- let total = 0;
10
- let count = 0;
11
- window.d2lPerfTestInProgress = true;
12
- document.body.addEventListener('d2l-component-perf', (e) => {
13
- count++;
14
- total += e.detail.value;
15
- if (count === numComponents) {
16
- const avg = Math.round(total / count);
17
- delete window.d2lPerfTestInProgress;
18
- resolve(avg);
19
- }
20
- });
21
-
22
- for (let i = 0; i < numComponents; i++) {
23
- fixture(element);
24
- }
25
-
26
- });
27
- }
28
-
29
- export const chaiPerf = (_chai, utils) => {
30
-
31
- utils.addMethod(_chai.Assertion.prototype, 'performant', function perfTest(baseline) {
32
- // eslint-disable-next-line no-invalid-this
33
- const fixture = this._obj;
34
- const result = runTest(fixture).then(results => {
35
- new _chai.Assertion(results).to.be.below(baseline);
36
- });
37
- // eslint-disable-next-line no-invalid-this
38
- this.then = result.then.bind(result);
39
- // eslint-disable-next-line no-invalid-this
40
- return this;
41
- });
42
-
43
- };
44
-
45
- chai.use(chaiPerf);