@brightspace-ui/core 3.227.8 → 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.
|
@@ -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:
|
|
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(/^
|
|
190
|
-
|
|
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
|
|
195
|
-
|
|
196
|
-
.replace(
|
|
197
|
-
.replace(
|
|
198
|
-
.replace(/<script
|
|
199
|
-
.replace(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|