SPB Git

Repository Public

dev-harness.tsx 3.5 KB 62 lines tsx

1// Development-only page that mounts the preview viewer on real files of the signed-in account, so that the viewer2// can be exercised (tests/e2e/preview.spec.ts) without the explorer. It is reached through /preview-harness.html on3// the Vite dev server only: the production build has a single entry (index.html) and never includes this module.4//   /preview-harness.html?ids=<fileId>,<fileId>…   viewer on the first file, previous/next through the list5//   &thumbs=1  a wall of thumbnails instead · &dark=1 dark theme · &lang=en · &nodownload=1 · &kind=<kind> forces the kind6import '../styles.css';7import { StrictMode, useEffect, useState } from 'react';8import { createRoot } from 'react-dom/client';9import { setLocale } from '../i18n.ts';10import { ownerSource } from '../sources.ts';11import type { FileDetail, FileItem, PreviewSource } from '../types.ts';12import { IconButton } from '../ui.tsx';13import { Thumb } from './Thumb.tsx';14import { Viewer } from './Viewer.tsx';1516if (!import.meta.env.DEV && !import.meta.env.VITE_PREVIEW_HARNESS) throw new Error('the preview harness only exists in development');17const q = new URLSearchParams(location.search);18if (q.get('dark')) document.documentElement.classList.add('dark');19setLocale(q.get('lang') === 'en' ? 'en' : 'fr');2021function Harness() {22  const [files, setFiles] = useState<FileItem[] | null>(null);23  const [index, setIndex] = useState(0);24  const [open, setOpen] = useState(true);25  const [flag, setFlag] = useState(0);26  useEffect(() => {27    const ids = (q.get('ids') ?? '').split(',').filter(Boolean);28    void Promise.all(ids.map((id) => fetch(`/api/v1/files/${id}`, { credentials: 'same-origin' }).then((r) => r.json() as Promise<FileDetail>).then((d) => d.file))).then(setFiles);29  }, []);30  if (!files) return <p data-testid="harness-loading">…</p>;31  if (q.get('thumbs')) {32    return (33      <div className="flex flex-wrap items-end gap-4 bg-bg p-6" data-testid="thumb-wall">34        {[32, 48, 96, 200].map((size) => (35          <div key={size} className="flex flex-wrap items-end gap-3">36            {files.map((f) => <span key={f.id} style={{ width: size, height: size }} className="inline-flex"><Thumb src={f.thumb || q.get('allthumbs') ? ownerSource(f).previewUrl('thumb') : undefined} name={f.name} kind={f.kind} className="h-full w-full" /></span>)}37            <span style={{ width: size, height: size }} className="inline-flex"><Thumb name="Dossier" kind="folder" className="h-full w-full" /></span>38          </div>39        ))}40      </div>41    );42  }43  const f = files[index];44  if (!f) return <p>no file</p>;45  let source: PreviewSource = ownerSource(f);46  if (q.get('nodownload')) source = { ...source, canDownload: false };47  if (q.get('kind')) source = { ...source, kind: q.get('kind') as PreviewSource['kind'] };48  return (49    <div className="p-6">50      <button type="button" data-testid="harness-open" onClick={() => setOpen(true)}>open</button>51      <p data-testid="harness-state">{open ? 'open' : 'closed'} {index} flag={flag}</p>52      <div style={{ height: 3000 }} />53      {open ? (54        <Viewer source={source} onClose={() => setOpen(false)}55          onPrev={files.length > 1 && index > 0 ? () => setIndex(index - 1) : undefined} onNext={files.length > 1 && index < files.length - 1 ? () => setIndex(index + 1) : undefined}56          actions={q.get('actions') ? <IconButton icon="star" label="action" data-testid="harness-action" onClick={() => setFlag((n) => n + 1)} /> : undefined} />57      ) : null}58    </div>59  );60}61createRoot(document.getElementById('root')!).render(<StrictMode><Harness /></StrictMode>);62
Files main
Menu

Theme