Skip to content
This repository was archived by the owner on Feb 18, 2026. It is now read-only.

Commit 9f3483c

Browse files
Merge pull request #31 from zeixcom/feature/refactor-lazy-load
test: refactor lazy-load example
2 parents d817e68 + 4c80062 commit 9f3483c

File tree

2 files changed

+43
-33
lines changed

2 files changed

+43
-33
lines changed

test/mock/lazy-load.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<style>
2-
p { color: red; }
2+
p { color: green; }
33
</style>
44
<p>Lazy loaded content</p>

test/ui-element-test.html

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
padding: 0;
2525

2626
& button[aria-pressed="true"] {
27-
color: red;
27+
color: purple;
2828
}
2929
}
3030

@@ -35,6 +35,10 @@
3535
display: block;
3636
}
3737
}
38+
39+
lazy-load .error {
40+
color: red;
41+
}
3842
</style>
3943

4044
<void-component id="void">
@@ -104,11 +108,11 @@ <h1>Hello from Server</h1>
104108
</greeting-configurator>
105109
<lazy-load src="/test/mock/lazy-load.html" id="lazy-success">
106110
<p class="loading" role="status">Loading...</p>
107-
<p class="error" role="alert" aria-live="polite"></p>
111+
<p class="error" role="alert" aria-live="polite" hidden></p>
108112
</lazy-load>
109113
<lazy-load src="/test/mock/404.html" id="lazy-error">
110114
<p class="loading" role="status">Loading...</p>
111-
<p class="error" role="alert" aria-live="polite"></p>
115+
<p class="error" role="alert" aria-live="polite" hidden></p>
112116
</lazy-load>
113117
<tab-list>
114118
<menu>
@@ -265,41 +269,46 @@ <h2>Tab 3</h2>
265269
class LazyLoad extends UIElement {
266270
static states = {
267271
content: '',
268-
error: ''
272+
error: '',
273+
loaded: false
269274
}
270275

271276
async connectedCallback() {
272277
super.connectedCallback()
273278

274-
fetch(this.getAttribute('src')) // TODO ensure 'src' attribute is a valid URL from a trusted source
275-
.then(async response => {
276-
let content = ''
277-
await wait(1500) // we wait a bit, otherwise Promise is already fulfilled when test runs
278-
if (response.ok) content = await response.text()
279-
else this.set('error', response.statusText)
280-
this.set('content', content)
281-
})
282-
.catch(error => this.set('error', error))
279+
const url = this.getAttribute('src') // ⚠️ Ensure 'src' is from a trusted source
280+
if (url) {
281+
try {
282+
const response = await fetch(url)
283+
this.set('loaded', true)
284+
if (response.ok) {
285+
const text = await response.text()
286+
this.set('content', text)
287+
} else {
288+
this.set('error', response.statusText)
289+
}
290+
} catch (error) {
291+
this.set('error', error.message)
292+
}
293+
} else {
294+
this.set('error', 'Missing required attribute: src')
295+
}
283296

284-
const loadingEl = this.querySelector('.loading')
285-
const errorEl = this.querySelector('.error')
297+
// Hide loading element when 'loaded' becomes true
298+
this.first('.loading').sync(toggleAttribute('hidden', 'loaded'))
286299

287-
effect(() => {
288-
const error = this.get('error')
289-
if (error) {
290-
loadingEl.remove() // remove placeholder for pending state
291-
errorEl.textContent = error // fill error message
292-
}
293-
})
300+
// Set error text when 'error' is set
301+
this.first('.error').sync(
302+
toggleAttribute('hidden', () => !this.get('error')),
303+
setText('error')
304+
)
294305

306+
// Set innerHTML of Shadow Root when 'content' is set
295307
effect(() => {
296308
const content = this.get('content')
297309
if (content) {
298-
// console.log(content)
299-
this.root = this.shadowRoot || this.attachShadow({ mode: 'open' }) // we use shadow DOM to enUIElementte styles
300-
this.root.innerHTML = content // UNSAFE!, use only trusted sources in 'src' attribute
301-
loadingEl.remove() // remove placeholder for pending state
302-
errorEl.remove() // won't be needed anymore as request was successful
310+
this.root = this.shadowRoot || this.attachShadow({ mode: 'open' })
311+
this.root.innerHTML = content
303312
}
304313
})
305314
}
@@ -711,20 +720,21 @@ <h2>Tab 3</h2>
711720

712721
it('should display lazy loaded content', async function () {
713722
const lazyComponent = document.getElementById('lazy-success')
714-
await wait(1000)
723+
await wait(100)
715724
const shadow = lazyComponent.shadowRoot
716725
assert.instanceOf(shadow, DocumentFragment, 'Should have a shadow root')
717-
// assert.equal(normalizeText(shadow.querySelector('p').textContent), 'Lazy loaded content', 'Should display lazy loaded content')
718-
// assert.equal(lazyComponent.querySelector('.loading'), null, 'Should no longer have a loading status')
719-
// assert.equal(lazyComponent.querySelector('.error'), null, 'Should no longer have an error container')
726+
assert.equal(normalizeText(shadow.querySelector('p').textContent), 'Lazy loaded content', 'Should display lazy loaded content')
727+
assert.equal(lazyComponent.querySelector('.error').hidden, true, 'Should hide error container')
728+
assert.equal(lazyComponent.querySelector('.loading').hidden, true, 'Should hide loading status')
720729
})
721730

722731
it('should display error message', async function () {
723732
const lazyComponent = document.getElementById('lazy-error')
724733
await wait(100)
725734
assert.equal(normalizeText(lazyComponent.querySelector('.error').textContent), 'Not Found', 'Should display error message')
726-
assert.equal(lazyComponent.querySelector('.loading'), null, 'Should no longer have a loading status')
735+
assert.equal(lazyComponent.querySelector('.loading').hidden, true, 'Should hide loading status')
727736
assert.equal(lazyComponent.shadowRoot, null, 'Should not have a shadow root')
737+
728738
})
729739

730740
})

0 commit comments

Comments
 (0)