@brightspace-ui/core 1.207.4 → 1.209.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.
@@ -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) {
@@ -10,6 +10,10 @@
10
10
  import { provideInstance } from '../../../mixins/provider-mixin.js';
11
11
 
12
12
  class DemoReplacementRenderer {
13
+ get canRenderInline() {
14
+ return true;
15
+ }
16
+
13
17
  async render(elem) {
14
18
  const elemsToReplace = elem.querySelectorAll('[data-replace-me-id]');
15
19
  if (elemsToReplace.length === 0) return elem;
@@ -38,7 +42,7 @@
38
42
 
39
43
  </script>
40
44
  <script>
41
- document.getElementsByTagName('html')[0].dataset.mathjaxContext = JSON.stringify({ renderLatex: window.location.search.indexOf('latex=true') !== -1 });
45
+ document.getElementsByTagName('html')[0].dataset.mathjaxContext = JSON.stringify({ outputScale: 1.1, renderLatex: window.location.search.indexOf('latex=true') !== -1 });
42
46
  </script>
43
47
  </head>
44
48
  <body unresolved>
@@ -188,6 +192,119 @@
188
192
  </template>
189
193
  </d2l-demo-snippet>
190
194
 
195
+ <h2>HTML Block (math, no deferred rendering)</h2>
196
+
197
+ <d2l-demo-snippet>
198
+ <template>
199
+ <d2l-html-block no-deferred-rendering>
200
+ <div>
201
+ <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
202
+ <mi>x</mi>
203
+ <mo>=</mo>
204
+ <mrow>
205
+ <mfrac>
206
+ <mrow>
207
+ <mo>&#x2212;</mo>
208
+ <mi>b</mi>
209
+ <mo>&#xB1;</mo>
210
+ <msqrt>
211
+ <msup>
212
+ <mi>b</mi>
213
+ <mn>2</mn>
214
+ </msup>
215
+ <mo>&#x2212;</mo>
216
+ <mn>4</mn>
217
+ <mi>a</mi>
218
+ <mi>c</mi>
219
+ </msqrt>
220
+ </mrow>
221
+ <mrow>
222
+ <mn>2</mn>
223
+ <mi>a</mi>
224
+ </mrow>
225
+ </mfrac>
226
+ </mrow>
227
+ <mtext>.</mtext>
228
+ </math>
229
+ <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
230
+ <msup>
231
+ <mrow>
232
+ <mo>(</mo>
233
+ <mrow>
234
+ <munderover>
235
+ <mo>∑<!-- ∑ --></mo>
236
+ <mrow class="MJX-TeXAtom-ORD">
237
+ <mi>k</mi>
238
+ <mo>=</mo>
239
+ <mn>1</mn>
240
+ </mrow>
241
+ <mi>n</mi>
242
+ </munderover>
243
+ <msub>
244
+ <mi>a</mi>
245
+ <mi>k</mi>
246
+ </msub>
247
+ <msub>
248
+ <mi>b</mi>
249
+ <mi>k</mi>
250
+ </msub>
251
+ </mrow>
252
+ <mo>)</mo>
253
+ </mrow>
254
+ <mrow class="MJX-TeXAtom-ORD">
255
+ <mspace width="negativethinmathspace"></mspace>
256
+ <mspace width="negativethinmathspace"></mspace>
257
+ <mn>2</mn>
258
+ </mrow>
259
+ </msup>
260
+ <mo>≤<!-- ≤ --></mo>
261
+ <mrow>
262
+ <mo>(</mo>
263
+ <mrow>
264
+ <munderover>
265
+ <mo>∑<!-- ∑ --></mo>
266
+ <mrow class="MJX-TeXAtom-ORD">
267
+ <mi>k</mi>
268
+ <mo>=</mo>
269
+ <mn>1</mn>
270
+ </mrow>
271
+ <mi>n</mi>
272
+ </munderover>
273
+ <msubsup>
274
+ <mi>a</mi>
275
+ <mi>k</mi>
276
+ <mn>2</mn>
277
+ </msubsup>
278
+ </mrow>
279
+ <mo>)</mo>
280
+ </mrow>
281
+ <mrow>
282
+ <mo>(</mo>
283
+ <mrow>
284
+ <munderover>
285
+ <mo>∑<!-- ∑ --></mo>
286
+ <mrow class="MJX-TeXAtom-ORD">
287
+ <mi>k</mi>
288
+ <mo>=</mo>
289
+ <mn>1</mn>
290
+ </mrow>
291
+ <mi>n</mi>
292
+ </munderover>
293
+ <msubsup>
294
+ <mi>b</mi>
295
+ <mi>k</mi>
296
+ <mn>2</mn>
297
+ </msubsup>
298
+ </mrow>
299
+ <mo>)</mo>
300
+ </mrow>
301
+ </math>
302
+ <p>The wizard (<span data-replace-me-id="0">Elmer Fudd</span>) quickly jinxed the gnomes before they vaporized.</p>
303
+ </div>
304
+ </d2l-html-block>
305
+ </template>
306
+ </d2l-demo-snippet>
307
+
191
308
  <h2>HTML Block (inline math)</h2>
192
309
 
193
310
  <d2l-demo-snippet>
@@ -118,6 +118,17 @@ const getRenderers = () => {
118
118
  */
119
119
  class HtmlBlock extends LitElement {
120
120
 
121
+ static get properties() {
122
+ return {
123
+ /**
124
+ * Whether to disable deferred rendering of the user-authored HTML. Do *not* set this
125
+ * unless your HTML relies on script executions that may break upon stamping.
126
+ * @type {Boolean}
127
+ */
128
+ noDeferredRendering: { type: Boolean, attribute: 'no-deferred-rendering' }
129
+ };
130
+ }
131
+
121
132
  static get styles() {
122
133
  return [ htmlBlockContentStyles, css`
123
134
  :host {
@@ -131,7 +142,10 @@ class HtmlBlock extends LitElement {
131
142
  :host([hidden]) {
132
143
  display: none;
133
144
  }
134
- ::slotted(*) {
145
+ :host([no-deferred-rendering]) div.d2l-html-block-rendered {
146
+ display: none;
147
+ }
148
+ :host(:not([no-deferred-rendering])) ::slotted(*) {
135
149
  display: none;
136
150
  }
137
151
  `];
@@ -139,6 +153,7 @@ class HtmlBlock extends LitElement {
139
153
 
140
154
  constructor() {
141
155
  super();
156
+ this.noDeferredRendering = false;
142
157
 
143
158
  const rendererContextAttributes = getRenderers().reduce((attrs, currentRenderer) => {
144
159
  if (currentRenderer.contextAttributes) currentRenderer.contextAttributes.forEach(attr => attrs.push(attr));
@@ -153,9 +168,9 @@ class HtmlBlock extends LitElement {
153
168
  super.connectedCallback();
154
169
  if (this._contextObserverController) this._contextObserverController.hostConnected();
155
170
 
156
- if (!this._templateObserver) return;
171
+ if (!this._templateObserver || this.noDeferredRendering) return;
157
172
 
158
- const template = this.querySelector('template');
173
+ const template = this._findSlottedElement('TEMPLATE');
159
174
  if (template) this._templateObserver.observe(template.content, { attributes: true, childList: true, subtree: true });
160
175
  }
161
176
 
@@ -172,24 +187,14 @@ class HtmlBlock extends LitElement {
172
187
 
173
188
  this.shadowRoot.innerHTML += '<div class="d2l-html-block-rendered"></div><slot></slot>';
174
189
 
175
- this.shadowRoot.querySelector('slot').addEventListener('slotchange', async e => {
176
-
177
- const template = e.target.assignedNodes({ flatten: true })
178
- .find(node => (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'TEMPLATE'));
179
-
180
- this._stamp(template);
181
-
182
- });
190
+ this.shadowRoot.querySelector('slot').addEventListener('slotchange', async e => await this._render(e.target));
183
191
  this._renderContainer = this.shadowRoot.querySelector('.d2l-html-block-rendered');
184
192
  this._context = this._contextObserverController ? { ...this._contextObserverController.values } : {};
185
193
  }
186
194
 
187
195
  updated() {
188
196
  super.updated();
189
- if (this._contextObserverController && this._contextObjectHasChanged()) {
190
- const template = this.querySelector('template');
191
- this._stamp(template);
192
- }
197
+ if (this._contextObserverController && this._contextObjectHasChanged()) this._render();
193
198
  }
194
199
 
195
200
  _contextObjectHasChanged() {
@@ -201,23 +206,50 @@ class HtmlBlock extends LitElement {
201
206
  return false;
202
207
  }
203
208
 
204
- _stamp(template) {
209
+ _findSlottedElement(tagName, slot) {
210
+ if (!slot) slot = this.shadowRoot.querySelector('slot');
211
+ return slot.assignedNodes({ flatten: true })
212
+ .find(node => (node.nodeType === Node.ELEMENT_NODE && node.tagName === tagName.toUpperCase()));
213
+ }
214
+
215
+ async _processRenderers(elem) {
216
+ for (const renderer of getRenderers()) {
217
+ if (this.noDeferredRendering && !renderer.canRenderInline) continue;
218
+
219
+ if (this._contextObserverController && renderer.contextAttributes) {
220
+ const contextValues = new Map();
221
+ renderer.contextAttributes.forEach(attr => contextValues.set(attr, this._contextObserverController.values.get(attr)));
222
+ elem = await renderer.render(elem, contextValues);
223
+ } else {
224
+ elem = await renderer.render(elem);
225
+ }
226
+ }
227
+
228
+ return elem;
229
+ }
230
+
231
+ async _render(slot) {
232
+ if (this.noDeferredRendering) await this._renderInline(slot);
233
+ else this._stamp(slot);
234
+ }
235
+
236
+ async _renderInline(slot) {
237
+ const noDeferredRenderingContainer = this._findSlottedElement('DIV', slot);
238
+ if (!noDeferredRenderingContainer) return;
239
+ await this._processRenderers(noDeferredRenderingContainer);
240
+ }
241
+
242
+ _stamp(slot) {
205
243
  const stampHTML = async template => {
206
244
  const fragment = template ? document.importNode(template.content, true) : null;
207
245
  if (fragment) {
208
246
 
209
247
  let temp = document.createElement('div');
248
+ temp.style.display = 'none';
210
249
  temp.appendChild(fragment);
211
250
 
212
- for (const renderer of getRenderers()) {
213
- if (this._contextObserverController && renderer.contextAttributes) {
214
- const contextValues = new Map();
215
- renderer.contextAttributes.forEach(attr => contextValues.set(attr, this._contextObserverController.values.get(attr)));
216
- temp = await renderer.render(temp, contextValues);
217
- } else {
218
- temp = await renderer.render(temp);
219
- }
220
- }
251
+ this._renderContainer.appendChild(temp);
252
+ temp = await this._processRenderers(temp);
221
253
  this._renderContainer.innerHTML = temp.innerHTML;
222
254
 
223
255
  } else {
@@ -225,6 +257,8 @@ class HtmlBlock extends LitElement {
225
257
  }
226
258
  };
227
259
 
260
+ const template = this._findSlottedElement('TEMPLATE', slot);
261
+
228
262
  if (this._templateObserver) this._templateObserver.disconnect();
229
263
  if (template) {
230
264
  this._templateObserver = new MutationObserver(() => stampHTML(template));
@@ -15,6 +15,7 @@ import { formatDateInISO,
15
15
  parseISOTime } from '../../helpers/dateTime.js';
16
16
  import { FormElementMixin } from '../form/form-element-mixin.js';
17
17
  import { getDefaultTime } from './input-time.js';
18
+ import { getDocumentLocaleSettings } from '@brightspace-ui/intl/lib/common.js';
18
19
  import { getUniqueId } from '../../helpers/uniqueId.js';
19
20
  import { ifDefined } from 'lit-html/directives/if-defined.js';
20
21
  import { LabelledMixin } from '../../mixins/labelled-mixin.js';
@@ -119,6 +120,7 @@ class InputDateTime extends LabelledMixin(SkeletonMixin(FormElementMixin(Localiz
119
120
  this.opened = false;
120
121
  this.required = false;
121
122
  this.timeDefaultValue = 'startOfDay';
123
+ this._documentLocaleSettings = getDocumentLocaleSettings();
122
124
  this._inputId = getUniqueId();
123
125
  this._namespace = 'components.input-date-time';
124
126
  this._preventDefaultValidation = false;
@@ -190,6 +192,16 @@ class InputDateTime extends LabelledMixin(SkeletonMixin(FormElementMixin(Localiz
190
192
  return super.validationMessage;
191
193
  }
192
194
 
195
+ connectedCallback() {
196
+ super.connectedCallback();
197
+ this._documentLocaleSettings.addChangeListener(this._handleLocaleChange.bind(this));
198
+ }
199
+
200
+ disconnectedCallback() {
201
+ super.disconnectedCallback();
202
+ this._documentLocaleSettings.removeChangeListener(this._handleLocaleChange.bind(this));
203
+ }
204
+
193
205
  firstUpdated(changedProperties) {
194
206
  super.firstUpdated(changedProperties);
195
207
 
@@ -343,6 +355,10 @@ class InputDateTime extends LabelledMixin(SkeletonMixin(FormElementMixin(Localiz
343
355
  if (tooltip) tooltip.show();
344
356
  }
345
357
 
358
+ _handleLocaleChange() {
359
+ this.requestUpdate();
360
+ }
361
+
346
362
  async _handleTimeChange(e) {
347
363
  const date = this.shadowRoot.querySelector('d2l-input-date').value;
348
364
  const time = e.target.value;
@@ -3377,6 +3377,23 @@
3377
3377
  "name": "d2l-html-block",
3378
3378
  "path": "./components/html-block/html-block.js",
3379
3379
  "description": "A component for displaying user-authored HTML.",
3380
+ "attributes": [
3381
+ {
3382
+ "name": "no-deferred-rendering",
3383
+ "description": "Whether to disable deferred rendering of the user-authored HTML. Do *not* set this\nunless your HTML relies on script executions that may break upon stamping.",
3384
+ "type": "Boolean",
3385
+ "default": "false"
3386
+ }
3387
+ ],
3388
+ "properties": [
3389
+ {
3390
+ "name": "noDeferredRendering",
3391
+ "attribute": "no-deferred-rendering",
3392
+ "description": "Whether to disable deferred rendering of the user-authored HTML. Do *not* set this\nunless your HTML relies on script executions that may break upon stamping.",
3393
+ "type": "Boolean",
3394
+ "default": "false"
3395
+ }
3396
+ ],
3380
3397
  "slots": [
3381
3398
  {
3382
3399
  "name": "",
@@ -4,6 +4,12 @@ let mathJaxLoaded;
4
4
 
5
5
  export class HtmlBlockMathRenderer {
6
6
 
7
+ get canRenderInline() {
8
+ // The custom MathJax ShadowAdaptor creates a new document and renders
9
+ // its contents to the DOM.
10
+ return false;
11
+ }
12
+
7
13
  get contextAttributes() {
8
14
  return [mathjaxContextAttribute];
9
15
  }
@@ -26,9 +32,11 @@ export class HtmlBlockMathRenderer {
26
32
  await loadMathJax(mathJaxConfig);
27
33
 
28
34
  const temp = document.createElement('div');
35
+ temp.style.display = 'none';
29
36
  temp.attachShadow({ mode: 'open' });
30
37
  temp.shadowRoot.innerHTML = `<div><mjx-doc><mjx-head></mjx-head><mjx-body>${elem.innerHTML}</mjx-body></mjx-doc></div>`;
31
38
 
39
+ elem.appendChild(temp);
32
40
  window.MathJax.typesetShadow(temp.shadowRoot);
33
41
  return temp.shadowRoot.firstChild;
34
42
  }
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "1.207.4",
3
+ "version": "1.209.0",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
5
  "repository": "https://github.com/BrightspaceUI/core.git",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
9
  "scripts": {
10
- "build:clean": "node ./cli/clean.js",
11
- "build:icons": "node ./cli/icon-generator.js",
10
+ "build:clean": "node ./cli/clean.mjs",
11
+ "build:icons": "node ./cli/icon-generator.mjs",
12
12
  "build:sass": "node-sass --output-style expanded ./test/sass.scss > ./test/sass.output.css",
13
13
  "build": "npm run build:clean && npm run build:icons && npm run build:sass",
14
14
  "lint": "npm run lint:eslint && npm run lint:style && npm run lint:lit",
15
- "lint:eslint": "eslint . --ext .js,.html",
15
+ "lint:eslint": "eslint . --ext .js,.mjs,.html",
16
16
  "lint:lit": "lit-analyzer \"{components,directives,helpers,mixins,templates,test,tools}/**/*.js\" --strict",
17
17
  "lint:style": "stylelint \"**/*.{js,html}\"",
18
18
  "start": "web-dev-server --node-resolve --watch --open",
@@ -48,7 +48,7 @@
48
48
  "@web/test-runner": "^0.13",
49
49
  "@web/test-runner-playwright": "^0.8.8",
50
50
  "axe-core": "^4",
51
- "chalk": "^4",
51
+ "chalk": "^5",
52
52
  "eslint": "^7",
53
53
  "eslint-config-brightspace": "^0.16",
54
54
  "eslint-plugin-html": "^6",