All files / src/browser/shared fallback-ui.ts

100% Statements 34/34
100% Branches 5/5
100% Functions 2/2
100% Lines 33/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81                                                        19x   19x 19x 19x 19x   19x 19x 19x 19x   19x 19x 19x   19x 19x   19x 13x 13x 13x 13x 13x 13x     19x               23x   23x 23x 23x 23x 23x   23x 69x 69x 69x     23x    
/**
 * @module Shared/FallbackUI
 * @description Centralized fallback UI renderers for dashboard error and loading states.
 * Provides consistent error and loading state presentation with optional retry capability.
 *
 * @intelligence Intelligence platform resilience layer — standardised fallback states
 * (error cards, loading skeletons) ensure uninterrupted intelligence consumption even when
 * individual data sources fail. Each component degrades gracefully without disrupting other
 * dashboard panels.
 *
 * @business User experience consistency — unified error and loading states prevent user
 * frustration and reduce support requests. Retry buttons maximise data recovery rate.
 *
 * @marketing Developer experience asset — reusable fallback components reduce time-to-market
 * for new dashboard panels and maintain visual consistency across all 14 language variants.
 */
 
/**
 * Replace the contents of `container` with an accessible error card.
 * An optional `retryFn` receives a retry button the user can click.
 * An optional `retryLabel` overrides the default "Retry" button text for localised UIs.
 */
export function renderErrorFallback(
  container: HTMLElement,
  message = 'Data temporarily unavailable',
  retryFn?: () => void,
  retryLabel = 'Retry',
): void {
  container.innerHTML = '';
 
  const card = document.createElement('div');
  card.className = 'fallback-error-card';
  card.setAttribute('role', 'alert');
  card.setAttribute('aria-live', 'assertive');
 
  const icon = document.createElement('span');
  icon.className = 'fallback-icon';
  icon.setAttribute('aria-hidden', 'true');
  icon.textContent = '⚠';
 
  const text = document.createElement('p');
  text.className = 'fallback-message';
  text.textContent = message;
 
  card.appendChild(icon);
  card.appendChild(text);
 
  if (retryFn) {
    const btn = document.createElement('button');
    btn.className = 'fallback-retry-btn';
    btn.type = 'button';
    btn.textContent = retryLabel;
    btn.addEventListener('click', retryFn);
    card.appendChild(btn);
  }
 
  container.appendChild(card);
}
 
/**
 * Replace the contents of `container` with a CSS-only skeleton loading animation.
 * The optional `loadingLabel` allows localized ARIA text for screen readers.
 */
export function renderLoadingFallback(container: HTMLElement, loadingLabel = 'Loading…'): void {
  container.innerHTML = '';
 
  const wrapper = document.createElement('div');
  wrapper.className = 'fallback-loading-skeleton';
  wrapper.setAttribute('role', 'status');
  wrapper.setAttribute('aria-live', 'polite');
  wrapper.setAttribute('aria-label', loadingLabel);
 
  for (let i = 0; i < 3; i++) {
    const bar = document.createElement('div');
    bar.className = 'skeleton-bar';
    wrapper.appendChild(bar);
  }
 
  container.appendChild(wrapper);
}