Skip to content

[Feature][Medium] Fix media-viewer component deficiencies: src change handling, error state, alt text, CORS fallback #87

@numbers-official

Description

@numbers-official

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();
  }
}

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions