Summary
The <media-viewer> component has several functional deficiencies that impact reliability and accessibility. These are grouped together as they all affect the same component.
Findings
1. No re-fetch on src property change
File: src/media-viewer/media-viewer.ts, lines 19-22
determineFileType() is only called in connectedCallback(). If the src property changes after the component is connected to the DOM, the MIME type is never re-determined and the component renders stale content.
Fix: Add a updated() lifecycle hook that watches for src changes:
override updated(changedProperties: PropertyValues) {
if (changedProperties.has('src') && this.src) {
this.mimeType = null;
this.determineFileType();
}
}
2. Blank error state — no visual feedback on failure
File: src/media-viewer/media-viewer.ts, lines 58-65
When the HEAD fetch() fails (network error, CORS restriction), mimeType stays null and the component renders <div class="loading"></div>. The .loading CSS class has no visible content — users see a blank space permanently.
Fix: Add an error state property and render meaningful fallback content:
@state() private _error = false;
// In render():
if (this._error) {
return html`<div class="error">Unable to load media</div>`;
}
3. Hardcoded alt="Image" — not configurable
File: src/media-viewer/media-viewer.ts, line 70
The <img> element has alt="Image" hardcoded. Consumers cannot provide meaningful alt text for accessibility.
Fix: Add an alt property:
@property() alt = 'Image';
// Use in render:
html`<img src=${this.src} alt=${this.alt} ... />`
4. CORS dependency without fallback
File: src/media-viewer/media-viewer.ts, lines 24-36
The fetch(this.src, { method: 'HEAD' }) call fails silently on CORS-restricted URLs. The component should fall back to file extension detection when the HEAD request fails:
catch (error) {
// Fallback: determine type from file extension
const ext = this.src?.split('.').pop()?.toLowerCase();
if (['mp4', 'webm', 'ogg'].includes(ext || '')) {
this.mimeType = `video/${ext}`;
} else {
this.mimeType = 'image/unknown'; // Default to image rendering
}
}
5. Duplicate HEAD requests on reconnection
File: src/media-viewer/media-viewer.ts, line 20
connectedCallback() always calls determineFileType() even if mimeType is already set. Add a guard:
override connectedCallback() {
super.connectedCallback();
if (!this.mimeType) {
this.determineFileType();
}
}
Summary
The
<media-viewer>component has several functional deficiencies that impact reliability and accessibility. These are grouped together as they all affect the same component.Findings
1. No re-fetch on
srcproperty changeFile:
src/media-viewer/media-viewer.ts, lines 19-22determineFileType()is only called inconnectedCallback(). If thesrcproperty changes after the component is connected to the DOM, the MIME type is never re-determined and the component renders stale content.Fix: Add a
updated()lifecycle hook that watches forsrcchanges:2. Blank error state — no visual feedback on failure
File:
src/media-viewer/media-viewer.ts, lines 58-65When the HEAD
fetch()fails (network error, CORS restriction),mimeTypestaysnulland the component renders<div class="loading"></div>. The.loadingCSS class has no visible content — users see a blank space permanently.Fix: Add an error state property and render meaningful fallback content:
3. Hardcoded
alt="Image"— not configurableFile:
src/media-viewer/media-viewer.ts, line 70The
<img>element hasalt="Image"hardcoded. Consumers cannot provide meaningful alt text for accessibility.Fix: Add an
altproperty:4. CORS dependency without fallback
File:
src/media-viewer/media-viewer.ts, lines 24-36The
fetch(this.src, { method: 'HEAD' })call fails silently on CORS-restricted URLs. The component should fall back to file extension detection when the HEAD request fails:5. Duplicate HEAD requests on reconnection
File:
src/media-viewer/media-viewer.ts, line 20connectedCallback()always callsdetermineFileType()even ifmimeTypeis already set. Add a guard: