@brightspace-ui/core 3.227.7 → 3.227.9

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.
@@ -54,8 +54,8 @@ class ButtonSubtle extends SlottedIconMixin(ButtonMixin(LitElement)) {
54
54
  return [super.styles, labelStyles, buttonStyles,
55
55
  css`
56
56
  :host {
57
- --d2l-count-badge-background-color: var(--d2l-color-celestine);
58
- --d2l-count-badge-foreground-color: #ffffff;
57
+ --d2l-count-badge-background-color: var(--d2l-theme-background-color-interactive-primary-default);
58
+ --d2l-count-badge-foreground-color: var(--d2l-theme-text-color-static-inverted);
59
59
  display: inline-block;
60
60
  }
61
61
 
@@ -67,7 +67,7 @@ class ButtonSubtle extends SlottedIconMixin(ButtonMixin(LitElement)) {
67
67
  --d2l-button-subtle-padding-inline-start: 0.6rem;
68
68
  --d2l-button-subtle-padding-inline-end: 0.6rem;
69
69
  align-items: center;
70
- background-color: transparent;
70
+ background-color: var(--d2l-theme-background-color-interactive-tertiary-default);
71
71
  border-color: transparent;
72
72
  column-gap: 0.3rem;
73
73
  display: inline-flex;
@@ -110,34 +110,34 @@ class ButtonSubtle extends SlottedIconMixin(ButtonMixin(LitElement)) {
110
110
  button[disabled]:hover,
111
111
  button[disabled]:focus,
112
112
  :host([active]) button[disabled] {
113
- background-color: transparent;
113
+ background-color: var(--d2l-theme-background-color-interactive-tertiary-default);
114
114
  }
115
115
 
116
116
  button:hover,
117
117
  button:focus,
118
118
  :host([active]) button {
119
- background-color: var(--d2l-color-gypsum);
119
+ background-color: var(--d2l-theme-background-color-interactive-tertiary-hover);
120
120
  }
121
121
 
122
122
  .d2l-button-subtle-content {
123
- color: var(--d2l-color-celestine);
123
+ color: var(--d2l-theme-text-color-interactive-default);
124
124
  }
125
125
 
126
126
  button:hover:not([disabled]) .d2l-button-subtle-content,
127
127
  button:focus:not([disabled]) .d2l-button-subtle-content,
128
128
  :host([active]:not([disabled])) button .d2l-button-subtle-content {
129
- color: var(--d2l-color-celestine-minus-1);
129
+ color: var(--d2l-theme-text-color-interactive-hover);
130
130
  }
131
131
 
132
132
  button:hover:not([disabled]),
133
133
  button:focus:not([disabled]),
134
134
  :host([active]:not([disabled])) {
135
- --d2l-count-badge-background-color: var(--d2l-color-celestine-minus-1);
135
+ --d2l-count-badge-background-color: var(--d2l-theme-text-color-interactive-hover);
136
136
  }
137
137
 
138
138
  .property-icon,
139
139
  slot[name="icon"]::slotted(d2l-icon-custom) {
140
- color: var(--d2l-color-celestine);
140
+ color: var(--d2l-theme-text-color-interactive-default);
141
141
  }
142
142
 
143
143
  button:hover:not([disabled]) .property-icon,
@@ -146,7 +146,7 @@ class ButtonSubtle extends SlottedIconMixin(ButtonMixin(LitElement)) {
146
146
  button:hover:not([disabled]) slot[name="icon"]::slotted(d2l-icon-custom),
147
147
  button:focus:not([disabled]) slot[name="icon"]::slotted(d2l-icon-custom),
148
148
  :host([active]:not([disabled])) slot[name="icon"]::slotted(d2l-icon-custom) {
149
- color: var(--d2l-color-celestine-minus-1);
149
+ color: var(--d2l-theme-text-color-interactive-hover);
150
150
  }
151
151
 
152
152
  :host([icon-right]) .property-icon,
@@ -154,9 +154,13 @@ class ButtonSubtle extends SlottedIconMixin(ButtonMixin(LitElement)) {
154
154
  order: 1;
155
155
  }
156
156
 
157
+ .d2l-button-subtle-content-wrapper slot {
158
+ color: var(--d2l-theme-text-color-static-standard);
159
+ }
160
+
157
161
  :host([disabled]) button {
158
162
  cursor: default;
159
- opacity: 0.5;
163
+ opacity: var(--d2l-theme-opacity-disabled-control);
160
164
  }
161
165
  `
162
166
  ];
@@ -5,6 +5,14 @@ import '../dropdown/dropdown.js';
5
5
  import '../dropdown/dropdown-content.js';
6
6
  import { css, html, LitElement } from 'lit';
7
7
 
8
+ function setIndent(text, indent = 0, skipFirstLine = false) {
9
+ const lines = text.split('\n');
10
+ const indentedLines = lines.filter((l, i) => l.trim() !== '' && !(skipFirstLine && i === 0));
11
+ const minIndent = Math.min(...indentedLines.map(l => l.match(/^\s*/)[0].length));
12
+
13
+ return lines.map((l, i) => `${' '.repeat(indent)}${skipFirstLine && i === 0 ? l : l.substring(minIndent)}`).join('\n');
14
+ }
15
+
8
16
  class DemoSnippet extends LitElement {
9
17
 
10
18
  static get properties() {
@@ -27,7 +35,7 @@ class DemoSnippet extends LitElement {
27
35
  background-color: var(--d2l-theme-background-color-base);
28
36
  border: 1px solid var(--d2l-theme-border-color-standard);
29
37
  border-radius: 6px;
30
- box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
38
+ box-shadow: var(--d2l-theme-shadow-floating);
31
39
  display: block;
32
40
  max-width: 900px;
33
41
  }
@@ -100,13 +108,14 @@ class DemoSnippet extends LitElement {
100
108
  border: none;
101
109
  border-top-left-radius: 0;
102
110
  border-top-right-radius: 0;
111
+ box-shadow: none;
103
112
  margin: 0;
104
113
  max-width: 100%;
105
114
  }
106
115
  :host([code-view-hidden]) d2l-code-view {
107
116
  display: none;
108
117
  }
109
- `;
118
+ `;
110
119
  }
111
120
 
112
121
  constructor() {
@@ -152,73 +161,39 @@ class DemoSnippet extends LitElement {
152
161
  });
153
162
  }
154
163
 
155
- _applyAttr(name, value, applyToShadowRoot) {
156
- const query = this._isTemplate ? 'slot[name="_demo"]' : 'slot:not([name="_demo"])';
157
- if (!this.shadowRoot) return;
158
- const nodes = this.shadowRoot.querySelector(query).assignedNodes();
159
- if (nodes.length === 0) return;
160
- const doApply = (nodes, isRoot) => {
161
- for (let i = 0; i < nodes.length; i++) {
162
- if (nodes[i].nodeType === Node.ELEMENT_NODE) {
163
- if (isRoot || nodes[i].tagName.indexOf('-') !== -1) {
164
- if (typeof(value) === 'boolean') {
165
- if (value) {
166
- nodes[i].setAttribute(name, name);
167
- } else {
168
- nodes[i].removeAttribute(name);
169
- }
170
- } else {
171
- nodes[i].setAttribute(name, value);
172
- }
173
- }
174
- if (applyToShadowRoot && nodes[i].shadowRoot) {
175
- doApply(nodes[i].shadowRoot.children, false);
176
- }
177
- doApply(nodes[i].children, false);
178
- }
179
- }
180
- };
181
- doApply(nodes, true);
182
- }
183
-
184
164
  _formatCode(text) {
185
165
 
186
166
  if (!text) return text;
187
167
 
188
168
  // remove the leading and trailing template tags
189
- text = text.replace(/^[\t]*\n/, '').replace(/\n[\t]*$/, '');
190
- const templateMatch = text.match(/^[\t]*<template>[\n]*/);
191
- this._isTemplate = templateMatch && templateMatch.length > 0;
169
+ text = text.replace(/^(\t*\n)*/, '').replace(/(\n\t*)*$/, '');
170
+ this._isTemplate = /^\s*<template>/.test(text);
192
171
  text = text.replace(/^[\t]*<template>[\n]*/, '').replace(/[\n]*[\t]*<\/template>$/, '');
193
172
 
194
- // fix script whitespace (for some reason brower keeps <script> indent but not the rest)
195
- let lines = text
196
- .replace(/\t/g, ' ')
197
- .replace(/<\/script>/g, '\n</script>')
198
- .replace(/<script>/g, '<script>\n')
199
- .replace(/<script type="module">/g, '<script type="module">\n')
200
- .replace(/<script data-demo-hide(.+?)<\/script>/gis, '')
201
- .split('\n');
202
- let scriptIndent = 0;
203
- lines = lines.map((l) => {
204
- if (l.indexOf('<script') > -1) {
205
- scriptIndent = l.match(/^(\s*)/)[0].length;
206
- return l;
207
- } else if (l.indexOf('</script>') > -1) {
208
- const nl = this._repeat(' ', scriptIndent) + l ;
209
- scriptIndent = 0;
210
- return nl;
211
- } else if (scriptIndent && !this._isTemplate) {
212
- return this._repeat(' ', scriptIndent + 2) + l;
213
- } else {
214
- return l;
215
- }
216
- });
173
+ // fix script whitespace
174
+ text = setIndent(text.replace(/\t/g, ' '))
175
+ .replace(/( *)<script( type="module")?>([^\n]+?)<\/script>/g, '$1<script$2>\n$1 $3\n$1</script>') // convert single line scripts to multi-line
176
+ .replace(/( *)<\/script>/g, '\n$1</script>')
177
+ .replace(/<script( type="module")?>/g, '<script$1>\n')
178
+ .replace(/(\n *)?<script data-demo-hide(.+?)<\/script>/gis, '');
179
+
180
+ const startTags = new Set([...text.matchAll(/<[^/](.*?)>/g)].map(m => m[0]));
181
+ for (const tag of startTags) {
182
+ const formattedTag = tag
183
+ .replace(/ class=""/g, '') // replace empty class attributes (class="")
184
+ .replace(/\s+_[^\s/>"'=]*(=(?<q>['"]).*?(?<!\\)\k<q>)?/g, '') // replace private reflected properties (_attr, _attr="value", but not target="_blank")
185
+ .replace(/=""/g, ''); // replace empty strings for boolean attributes (="")
186
+ text = text.replace(tag, formattedTag);
187
+ }
188
+
189
+ return text;
217
190
 
218
- return lines.join('\n')
219
- .replace(/ class=""/g, '') // replace empty class attributes (class="")
220
- .replace(/\s+_[^\s/>"'=]*(=(?<q>['"]).*?(?<!\\)\k<q>)?/g, '') // replace private reflected properties (_attr, _attr="value", but not target="_blank")
221
- .replace(/=""/g, ''); // replace empty strings for boolean attributes (="")
191
+ }
192
+
193
+ _getDemoNodes() {
194
+ const query = this._isTemplate ? '[slot="_demo"], [slot="_demo"] *' : '*';
195
+ const elements = Array.from(this.querySelectorAll(query));
196
+ return elements;
222
197
  }
223
198
 
224
199
  async _handleFullscreenChange(e) {
@@ -233,27 +208,31 @@ class DemoSnippet extends LitElement {
233
208
 
234
209
  _handleSkeletonChange(e) {
235
210
  this._skeletonOn = e.target.on;
236
- this._applyAttr('skeleton', this._skeletonOn, false);
211
+ const nodes = this._getDemoNodes();
212
+ for (const node of nodes) {
213
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
214
+ if (node.tagName.indexOf('-') === -1) continue;
215
+ if (this._skeletonOn) {
216
+ node.setAttribute('skeleton', '');
217
+ } else {
218
+ node.removeAttribute('skeleton');
219
+ }
220
+ }
237
221
  }
238
222
 
239
223
  _handleSlotChange(e) {
240
224
  this._updateCode(e.target);
225
+ this._updateHasSkeleton();
241
226
  }
242
227
 
243
228
  _removeImportedDemo() {
244
229
  if (!this.shadowRoot) return;
245
230
  const nodes = this.shadowRoot.querySelector('slot[name="_demo"]').assignedNodes();
246
- for (let i = nodes.length - 1; i === 0; i--) {
231
+ for (let i = nodes.length - 1; i >= 0; i--) {
247
232
  nodes[i].parentNode.removeChild(nodes[i]);
248
233
  }
249
234
  }
250
235
 
251
- _repeat(value, times) {
252
- if (!value || !times) return '';
253
- if (!''.repeat) return Array(times).join(value); // for IE11
254
- return value.repeat(times);
255
- }
256
-
257
236
  _updateCode(slot) {
258
237
  this._removeImportedDemo();
259
238
  const nodes = slot.assignedNodes();
@@ -277,27 +256,12 @@ class DemoSnippet extends LitElement {
277
256
  }
278
257
  const textNode = document.createTextNode(this._formatCode(tempContainer.innerHTML));
279
258
  this._code = textNode.textContent;
280
-
281
- this._updateHasSkeleton();
282
259
  }
283
260
 
284
261
  _updateHasSkeleton() {
262
+ const nodes = this._getDemoNodes();
285
263
 
286
- const query = this._isTemplate ? 'slot[name="_demo"]' : 'slot:not([name="_demo"])';
287
- if (!this.shadowRoot) return;
288
- const nodes = this.shadowRoot.querySelector(query).assignedNodes();
289
-
290
- const doApply = (nodes) => {
291
- for (let i = 0; i < nodes.length; i++) {
292
- if (nodes[i].nodeType === Node.ELEMENT_NODE) {
293
- if (nodes[i].skeleton !== undefined) {
294
- this._hasSkeleton = true;
295
- }
296
- doApply(nodes[i].children);
297
- }
298
- }
299
- };
300
- doApply(nodes);
264
+ this._hasSkeleton = nodes.some(n => n.nodeType === Node.ELEMENT_NODE && n.tagName.indexOf('-') !== -1 && n.skeleton !== undefined);
301
265
 
302
266
  }
303
267
 
@@ -0,0 +1,116 @@
1
+ import '../demo-snippet.js';
2
+ import { defineCE, expect, fixture, html, nextFrame, runConstructor } from '@brightspace-ui/testing';
3
+ import { LitElement } from 'lit';
4
+ import { SkeletonMixin } from '../../skeleton/skeleton-mixin.js';
5
+
6
+ const skeletonTag = defineCE(class extends SkeletonMixin(LitElement) {
7
+ render() {
8
+ return html`<div>Skeleton element</div>`;
9
+ }
10
+ });
11
+ const scriptTestExpected = `<div>
12
+ <script>
13
+
14
+ console.log('hi');
15
+
16
+ </script>
17
+ <script type="module">
18
+
19
+ import { test } from './test.js';
20
+ if (window.test) {
21
+ console.log('test');
22
+ }
23
+
24
+ </script>
25
+ </div>`;
26
+ const tagTestExpected = `<div foo data-keep="ok"></div>
27
+ <another-tag bar target="_blank"></another-tag>
28
+ <script>
29
+
30
+ function _privateFunction() {console.log('Private function name is not removed');}
31
+
32
+ </script>`;
33
+
34
+ function addTemplate(inner) {
35
+ return `<template>${inner}</template>`;
36
+ }
37
+
38
+ describe('d2l-demo-snippet', () => {
39
+
40
+ describe('constructor', () => {
41
+ it('should construct', () => {
42
+ runConstructor('d2l-demo-snippet');
43
+ });
44
+ });
45
+
46
+ describe('code formatting', () => {
47
+ let elem;
48
+
49
+ beforeEach(async() => {
50
+ elem = await fixture(html`<d2l-demo-snippet></d2l-demo-snippet>`);
51
+ });
52
+
53
+ [true, false].forEach(useTemplate => {
54
+ it(`sets template flag to ${useTemplate ? 'true' : 'false'}`, async() => {
55
+ const inner = '<div>demo</div>';
56
+ const formatted = elem._formatCode(useTemplate ? addTemplate(inner) : inner);
57
+ expect(formatted).to.equal('<div>demo</div>');
58
+ expect(elem._isTemplate).to.equal(useTemplate);
59
+ });
60
+ it('parses scripts and removes hidden ones', async() => {
61
+ const inner = `
62
+ <div>
63
+ <script>console.log('hi');</script>
64
+ <script type="module">
65
+ import { test } from './test.js';
66
+ if (window.test) {
67
+ console.log('test');
68
+ }
69
+ </script>
70
+ <script data-demo-hide>hidden</script>
71
+ </div>`;
72
+ const formatted = elem._formatCode(useTemplate ? addTemplate(inner) : inner);
73
+ expect(formatted).to.equal(scriptTestExpected);
74
+ });
75
+ });
76
+
77
+ it('removes empty and private attributes but keeps normal ones', async() => {
78
+ const elem = await fixture(html`<d2l-demo-snippet></d2l-demo-snippet>`);
79
+ const formatted = elem._formatCode(`
80
+ <div class="" _private foo="" data-keep="ok"></div>
81
+ <another-tag _private="value" bar="" target="_blank"></another-tag>
82
+ <script>
83
+ function _privateFunction() {console.log('Private function name is not removed');}
84
+ </script>
85
+ `);
86
+ expect(formatted).to.equal(tagTestExpected);
87
+ });
88
+
89
+ });
90
+
91
+ describe('skeleton detection', () => {
92
+ [true, false].forEach(useTemplate => {
93
+
94
+ function snippetFixture(inner) {
95
+ return fixture(`<d2l-demo-snippet>${useTemplate ? addTemplate(inner) : inner}</d2l-demo-snippet>`, { awaitLoadingComplete: false });
96
+ }
97
+
98
+ it(`sets _hasSkeleton when a slotted element exposes skeleton property${useTemplate ? ' - template' : ''}`, async() => {
99
+ await nextFrame();
100
+ const elem = await snippetFixture(`<${skeletonTag}></${skeletonTag}>`);
101
+ expect(elem._hasSkeleton).to.be.true;
102
+ });
103
+
104
+ it(`sets _hasSkeleton when a nested slotted element exposes skeleton property${useTemplate ? ' - template' : ''}`, async() => {
105
+ const elem = await snippetFixture(`<div><${skeletonTag}></${skeletonTag}></div>`);
106
+ expect(elem._hasSkeleton).to.be.true;
107
+ });
108
+
109
+ it(`does not set _hasSkeleton when a slotted element does not expose skeleton property${useTemplate ? ' - template' : ''}`, async() => {
110
+ const elem = await snippetFixture('<div></div>');
111
+ expect(elem._hasSkeleton).to.be.false;
112
+ });
113
+ });
114
+ });
115
+
116
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.227.7",
3
+ "version": "3.227.9",
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",