kempo-ui 0.0.2
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/.github/copilot-instructions.md +149 -0
- package/.github/workflows/publish-npm.yml +45 -0
- package/CONTRIBUTING.md +149 -0
- package/config/development.json +14 -0
- package/config/production.json +16 -0
- package/docs/components/HybridComponent.js +1 -0
- package/docs/components/LightComponent.js +1 -0
- package/docs/components/ShadowComponent.js +1 -0
- package/docs/index.html +21 -0
- package/docs/kempo-vars.css +0 -0
- package/docs/kempo.css +1 -0
- package/docs/kempo.min.css +1 -0
- package/docs/utils/cli.js +1 -0
- package/docs/utils/fs-utils.js +1 -0
- package/package.json +35 -0
- package/scripts/build.js +139 -0
- package/scripts/docs.js +61 -0
- package/src/components/HybridComponent.js +40 -0
- package/src/components/LightComponent.js +32 -0
- package/src/components/ShadowComponent.js +17 -0
- package/src/kempo-vars.css +0 -0
- package/src/lit-all.min.js +120 -0
- package/src/utils/cli.js +43 -0
- package/src/utils/fs-utils.js +41 -0
- package/tests/HybridComponent.browser-test.js +214 -0
- package/tests/LightComponent.browser-test.js +169 -0
- package/tests/ShadowComponent.browser-test.js +130 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import HybridComponent from '/src/components/HybridComponent.js';
|
|
2
|
+
import { html } from '/src/lit-all.min.js';
|
|
3
|
+
|
|
4
|
+
class TestHybridComponent extends HybridComponent {
|
|
5
|
+
render() {
|
|
6
|
+
return html`<p>Shadow content</p>`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
renderLightDom() {
|
|
10
|
+
return html`<span>Light content</span>`;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
customElements.define('test-hybrid-component', TestHybridComponent);
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
'should inherit shadow root with stylesheet from ShadowComponent': async ({pass, fail, log}) => {
|
|
18
|
+
const component = new TestHybridComponent();
|
|
19
|
+
document.body.appendChild(component);
|
|
20
|
+
|
|
21
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
22
|
+
|
|
23
|
+
if(!component.shadowRoot) {
|
|
24
|
+
document.body.removeChild(component);
|
|
25
|
+
fail('Component should have shadow root');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const stylesheetLink = component.shadowRoot.querySelector('link[href="/kempo.min.css"]');
|
|
30
|
+
if(!stylesheetLink) {
|
|
31
|
+
document.body.removeChild(component);
|
|
32
|
+
fail('Shadow root should contain kempo.min.css link');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
log('✓ Inherited shadow root with stylesheet from ShadowComponent');
|
|
37
|
+
document.body.removeChild(component);
|
|
38
|
+
pass('HybridComponent inherits ShadowComponent functionality');
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
'should create light root for light DOM rendering': async ({pass, fail, log}) => {
|
|
42
|
+
const component = new TestHybridComponent();
|
|
43
|
+
document.body.appendChild(component);
|
|
44
|
+
|
|
45
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
46
|
+
|
|
47
|
+
if(!component.lightRoot) {
|
|
48
|
+
document.body.removeChild(component);
|
|
49
|
+
fail('Component should have lightRoot property');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if(component.lightRoot.tagName !== 'DIV') {
|
|
54
|
+
document.body.removeChild(component);
|
|
55
|
+
fail('lightRoot should be a div element');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if(component.lightRoot.style.display !== 'contents') {
|
|
60
|
+
document.body.removeChild(component);
|
|
61
|
+
fail('lightRoot should have display: contents');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if(component.lightRoot.slot !== 'lightRoot') {
|
|
66
|
+
document.body.removeChild(component);
|
|
67
|
+
fail('lightRoot should have slot="lightRoot"');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
log('✓ Light root properly configured for hybrid rendering');
|
|
72
|
+
document.body.removeChild(component);
|
|
73
|
+
pass('HybridComponent creates proper light root');
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
'should render shadow DOM content': async ({pass, fail, log}) => {
|
|
77
|
+
const component = new TestHybridComponent();
|
|
78
|
+
document.body.appendChild(component);
|
|
79
|
+
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
81
|
+
|
|
82
|
+
const shadowParagraph = component.renderRoot.querySelector('p');
|
|
83
|
+
if(!shadowParagraph) {
|
|
84
|
+
document.body.removeChild(component);
|
|
85
|
+
fail('Shadow DOM should contain paragraph element');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if(shadowParagraph.textContent !== 'Shadow content') {
|
|
90
|
+
document.body.removeChild(component);
|
|
91
|
+
fail(`Expected "Shadow content", got "${shadowParagraph.textContent}"`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
log('✓ Shadow DOM content rendered correctly');
|
|
96
|
+
document.body.removeChild(component);
|
|
97
|
+
pass('HybridComponent renders shadow DOM content');
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
'should render light DOM content': async ({pass, fail, log}) => {
|
|
101
|
+
const component = new TestHybridComponent();
|
|
102
|
+
document.body.appendChild(component);
|
|
103
|
+
|
|
104
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
105
|
+
|
|
106
|
+
const lightSpan = component.lightRoot.querySelector('span');
|
|
107
|
+
if(!lightSpan) {
|
|
108
|
+
document.body.removeChild(component);
|
|
109
|
+
fail('Light DOM should contain span element');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if(lightSpan.textContent !== 'Light content') {
|
|
114
|
+
document.body.removeChild(component);
|
|
115
|
+
fail(`Expected "Light content", got "${lightSpan.textContent}"`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
log('✓ Light DOM content rendered correctly');
|
|
120
|
+
document.body.removeChild(component);
|
|
121
|
+
pass('HybridComponent renders light DOM content');
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
'should render both shadow and light DOM simultaneously': async ({pass, fail, log}) => {
|
|
125
|
+
const component = new TestHybridComponent();
|
|
126
|
+
document.body.appendChild(component);
|
|
127
|
+
|
|
128
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
129
|
+
|
|
130
|
+
const shadowContent = component.renderRoot.querySelector('p');
|
|
131
|
+
const lightContent = component.lightRoot.querySelector('span');
|
|
132
|
+
|
|
133
|
+
if(!shadowContent || shadowContent.textContent !== 'Shadow content') {
|
|
134
|
+
document.body.removeChild(component);
|
|
135
|
+
fail('Shadow content should be present');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if(!lightContent || lightContent.textContent !== 'Light content') {
|
|
140
|
+
document.body.removeChild(component);
|
|
141
|
+
fail('Light content should be present');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
log('✓ Both shadow and light content rendered simultaneously');
|
|
146
|
+
document.body.removeChild(component);
|
|
147
|
+
pass('HybridComponent renders both DOM types');
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
'should preserve natural children alongside hybrid content': async ({pass, fail, log}) => {
|
|
151
|
+
const component = new TestHybridComponent();
|
|
152
|
+
const naturalChild = document.createElement('div');
|
|
153
|
+
naturalChild.textContent = 'Natural child';
|
|
154
|
+
component.appendChild(naturalChild);
|
|
155
|
+
document.body.appendChild(component);
|
|
156
|
+
|
|
157
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
158
|
+
|
|
159
|
+
const foundNaturalChild = component.querySelector('div');
|
|
160
|
+
if(!foundNaturalChild || foundNaturalChild.textContent !== 'Natural child') {
|
|
161
|
+
document.body.removeChild(component);
|
|
162
|
+
fail('Natural child should be preserved');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const shadowContent = component.renderRoot.querySelector('p');
|
|
167
|
+
const lightContent = component.lightRoot.querySelector('span');
|
|
168
|
+
|
|
169
|
+
if(!shadowContent || !lightContent) {
|
|
170
|
+
document.body.removeChild(component);
|
|
171
|
+
fail('Both rendered contents should be present with natural children');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
log('✓ Natural children preserved alongside hybrid rendering');
|
|
176
|
+
document.body.removeChild(component);
|
|
177
|
+
pass('HybridComponent preserves natural children');
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
'should handle slot system for light root': async ({pass, fail, log}) => {
|
|
181
|
+
const component = new TestHybridComponent();
|
|
182
|
+
document.body.appendChild(component);
|
|
183
|
+
|
|
184
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
185
|
+
await component.updateComplete;
|
|
186
|
+
|
|
187
|
+
// The slot should be in the render root (which is the div container)
|
|
188
|
+
const slot = component.renderRoot.querySelector('slot[name="lightRoot"]');
|
|
189
|
+
const lightContent = component.lightRoot.querySelector('span');
|
|
190
|
+
|
|
191
|
+
if(!slot && !lightContent) {
|
|
192
|
+
document.body.removeChild(component);
|
|
193
|
+
fail('Either slot should exist or light content should be rendered via slotting');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if(component.lightRoot.slot !== 'lightRoot') {
|
|
198
|
+
document.body.removeChild(component);
|
|
199
|
+
fail('lightRoot should be assigned to lightRoot slot');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Check that light content is actually rendered
|
|
204
|
+
if(!lightContent || lightContent.textContent !== 'Light content') {
|
|
205
|
+
document.body.removeChild(component);
|
|
206
|
+
fail('Light content should be rendered through slot system');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
log('✓ Slot system working - light content rendered');
|
|
211
|
+
document.body.removeChild(component);
|
|
212
|
+
pass('HybridComponent implements proper slot system');
|
|
213
|
+
}
|
|
214
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import LightComponent from '/src/components/LightComponent.js';
|
|
2
|
+
import { html } from '/src/lit-all.min.js';
|
|
3
|
+
|
|
4
|
+
class TestLightComponent extends LightComponent {
|
|
5
|
+
renderLightDom() {
|
|
6
|
+
return html`<p>Test light content</p>`;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
customElements.define('test-light-component', TestLightComponent);
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
'should not create shadow root': async ({pass, fail, log}) => {
|
|
14
|
+
const component = new TestLightComponent();
|
|
15
|
+
document.body.appendChild(component);
|
|
16
|
+
|
|
17
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
18
|
+
|
|
19
|
+
if(component.shadowRoot) {
|
|
20
|
+
document.body.removeChild(component);
|
|
21
|
+
fail('LightComponent should not have shadow root');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
log('✓ No shadow root created as expected');
|
|
26
|
+
document.body.removeChild(component);
|
|
27
|
+
pass('LightComponent does not create shadow root');
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
'should have itself as render root': async ({pass, fail, log}) => {
|
|
31
|
+
const component = new TestLightComponent();
|
|
32
|
+
document.body.appendChild(component);
|
|
33
|
+
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
35
|
+
|
|
36
|
+
if(component.renderRoot !== component) {
|
|
37
|
+
document.body.removeChild(component);
|
|
38
|
+
fail('Render root should be the component itself');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
log('✓ Component is its own render root');
|
|
43
|
+
document.body.removeChild(component);
|
|
44
|
+
pass('LightComponent uses itself as render root');
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
'should create light root container': async ({pass, fail, log}) => {
|
|
48
|
+
const component = new TestLightComponent();
|
|
49
|
+
document.body.appendChild(component);
|
|
50
|
+
|
|
51
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
52
|
+
|
|
53
|
+
if(!component.lightRoot) {
|
|
54
|
+
document.body.removeChild(component);
|
|
55
|
+
fail('Component should have lightRoot property');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if(component.lightRoot.tagName !== 'DIV') {
|
|
60
|
+
document.body.removeChild(component);
|
|
61
|
+
fail('lightRoot should be a div element');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if(component.lightRoot.style.display !== 'contents') {
|
|
66
|
+
document.body.removeChild(component);
|
|
67
|
+
fail('lightRoot should have display: contents');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if(component.lightRoot.parentNode !== component) {
|
|
72
|
+
document.body.removeChild(component);
|
|
73
|
+
fail('lightRoot should be child of component');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
log('✓ Light root container properly configured');
|
|
78
|
+
document.body.removeChild(component);
|
|
79
|
+
pass('LightComponent creates proper light root container');
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
'should render content in light root': async ({pass, fail, log}) => {
|
|
83
|
+
const component = new TestLightComponent();
|
|
84
|
+
document.body.appendChild(component);
|
|
85
|
+
|
|
86
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
87
|
+
|
|
88
|
+
const paragraph = component.lightRoot.querySelector('p');
|
|
89
|
+
if(!paragraph) {
|
|
90
|
+
document.body.removeChild(component);
|
|
91
|
+
fail('Component should render paragraph in light root');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if(paragraph.textContent !== 'Test light content') {
|
|
96
|
+
document.body.removeChild(component);
|
|
97
|
+
fail(`Expected "Test light content", got "${paragraph.textContent}"`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
log('✓ Content rendered properly in light root');
|
|
102
|
+
document.body.removeChild(component);
|
|
103
|
+
pass('LightComponent renders content in light DOM');
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
'should preserve natural children': async ({pass, fail, log}) => {
|
|
107
|
+
const component = new TestLightComponent();
|
|
108
|
+
const naturalChild = document.createElement('span');
|
|
109
|
+
naturalChild.textContent = 'Natural child';
|
|
110
|
+
component.appendChild(naturalChild);
|
|
111
|
+
document.body.appendChild(component);
|
|
112
|
+
|
|
113
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
114
|
+
|
|
115
|
+
const foundNaturalChild = component.querySelector('span');
|
|
116
|
+
if(!foundNaturalChild) {
|
|
117
|
+
document.body.removeChild(component);
|
|
118
|
+
fail('Natural child should be preserved');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if(foundNaturalChild.textContent !== 'Natural child') {
|
|
123
|
+
document.body.removeChild(component);
|
|
124
|
+
fail('Natural child content should be preserved');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const renderedContent = component.lightRoot.querySelector('p');
|
|
129
|
+
if(!renderedContent) {
|
|
130
|
+
document.body.removeChild(component);
|
|
131
|
+
fail('Rendered content should also be present');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
log('✓ Natural children preserved alongside rendered content');
|
|
136
|
+
document.body.removeChild(component);
|
|
137
|
+
pass('LightComponent preserves natural children');
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
'should call super.updated() requirement': async ({pass, fail, log}) => {
|
|
141
|
+
class BadLightComponent extends LightComponent {
|
|
142
|
+
updated() {
|
|
143
|
+
// Intentionally not calling super.updated()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
renderLightDom() {
|
|
147
|
+
return html`<p>Should not render</p>`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
customElements.define('bad-light-component', BadLightComponent);
|
|
152
|
+
|
|
153
|
+
const component = new BadLightComponent();
|
|
154
|
+
document.body.appendChild(component);
|
|
155
|
+
|
|
156
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
157
|
+
|
|
158
|
+
const paragraph = component.lightRoot?.querySelector('p');
|
|
159
|
+
if(paragraph) {
|
|
160
|
+
document.body.removeChild(component);
|
|
161
|
+
fail('Content should not render without super.updated() call');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
log('✓ Content does not render without super.updated()');
|
|
166
|
+
document.body.removeChild(component);
|
|
167
|
+
pass('LightComponent requires super.updated() to function');
|
|
168
|
+
}
|
|
169
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import ShadowComponent from '/src/components/ShadowComponent.js';
|
|
2
|
+
import { html } from '/src/lit-all.min.js';
|
|
3
|
+
|
|
4
|
+
class TestShadowComponent extends ShadowComponent {
|
|
5
|
+
render() {
|
|
6
|
+
return html`<p>Test shadow content</p>`;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
customElements.define('test-shadow-component', TestShadowComponent);
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
'should create shadow root with stylesheet': async ({pass, fail, log}) => {
|
|
14
|
+
const component = new TestShadowComponent();
|
|
15
|
+
document.body.appendChild(component);
|
|
16
|
+
|
|
17
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
18
|
+
|
|
19
|
+
if(!component.shadowRoot) {
|
|
20
|
+
document.body.removeChild(component);
|
|
21
|
+
fail('Component should have shadow root');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const stylesheetLink = component.shadowRoot.querySelector('link[href="/kempo.min.css"]');
|
|
26
|
+
if(!stylesheetLink) {
|
|
27
|
+
document.body.removeChild(component);
|
|
28
|
+
fail('Shadow root should contain kempo.min.css link');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if(stylesheetLink.rel !== 'stylesheet') {
|
|
33
|
+
document.body.removeChild(component);
|
|
34
|
+
fail('Link should have rel="stylesheet"');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
log('✓ Shadow root created with proper stylesheet');
|
|
39
|
+
document.body.removeChild(component);
|
|
40
|
+
pass('ShadowComponent creates shadow root with stylesheet injection');
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
'should have render container as render root': async ({pass, fail, log}) => {
|
|
44
|
+
const component = new TestShadowComponent();
|
|
45
|
+
document.body.appendChild(component);
|
|
46
|
+
|
|
47
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
48
|
+
|
|
49
|
+
const renderRoot = component.renderRoot;
|
|
50
|
+
if(!renderRoot) {
|
|
51
|
+
document.body.removeChild(component);
|
|
52
|
+
fail('Component should have render root');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if(renderRoot.tagName !== 'DIV') {
|
|
57
|
+
document.body.removeChild(component);
|
|
58
|
+
fail('Render root should be a div element');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if(renderRoot.parentNode !== component.shadowRoot) {
|
|
63
|
+
document.body.removeChild(component);
|
|
64
|
+
fail('Render root should be child of shadow root');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
log('✓ Render root is properly configured div element');
|
|
69
|
+
document.body.removeChild(component);
|
|
70
|
+
pass('ShadowComponent has proper render container setup');
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
'should render content inside render container': async ({pass, fail, log}) => {
|
|
74
|
+
const component = new TestShadowComponent();
|
|
75
|
+
document.body.appendChild(component);
|
|
76
|
+
|
|
77
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
78
|
+
|
|
79
|
+
const paragraph = component.renderRoot.querySelector('p');
|
|
80
|
+
if(!paragraph) {
|
|
81
|
+
document.body.removeChild(component);
|
|
82
|
+
fail('Component should render paragraph element');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if(paragraph.textContent !== 'Test shadow content') {
|
|
87
|
+
document.body.removeChild(component);
|
|
88
|
+
fail(`Expected "Test shadow content", got "${paragraph.textContent}"`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
log('✓ Content rendered properly inside render container');
|
|
93
|
+
document.body.removeChild(component);
|
|
94
|
+
pass('ShadowComponent renders content correctly');
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
'should maintain stylesheet after re-rendering': async ({pass, fail, log}) => {
|
|
98
|
+
const component = new TestShadowComponent();
|
|
99
|
+
document.body.appendChild(component);
|
|
100
|
+
|
|
101
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
102
|
+
|
|
103
|
+
const stylesheetBefore = component.shadowRoot.querySelector('link[href="/kempo.min.css"]');
|
|
104
|
+
if(!stylesheetBefore) {
|
|
105
|
+
document.body.removeChild(component);
|
|
106
|
+
fail('Stylesheet should exist before re-render');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
component.requestUpdate();
|
|
111
|
+
await component.updateComplete;
|
|
112
|
+
|
|
113
|
+
const stylesheetAfter = component.shadowRoot.querySelector('link[href="/kempo.min.css"]');
|
|
114
|
+
if(!stylesheetAfter) {
|
|
115
|
+
document.body.removeChild(component);
|
|
116
|
+
fail('Stylesheet should persist after re-render');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if(stylesheetBefore !== stylesheetAfter) {
|
|
121
|
+
document.body.removeChild(component);
|
|
122
|
+
fail('Stylesheet should be the same element after re-render');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
log('✓ Stylesheet persists through re-renders');
|
|
127
|
+
document.body.removeChild(component);
|
|
128
|
+
pass('ShadowComponent maintains stylesheet across updates');
|
|
129
|
+
}
|
|
130
|
+
};
|