Import normalizer: offline tool to normalize the raw archive spreadsheets #663
@@ -28,13 +28,21 @@ const showRawLine = $derived(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="inline-flex flex-col">
|
<span class="inline-flex flex-col">
|
||||||
<span class="inline-flex items-center gap-1">
|
{#if isUnknown}
|
||||||
{#if isUnknown}
|
<!--
|
||||||
<!-- Non-color cue (WCAG 1.4.1): a calendar-with-question glyph. The visible
|
Neutral metadata chip (#668): an undated letter is an absence, NOT an error,
|
||||||
"Datum unbekannt" text is the redundant textual cue, so the icon is
|
so the chip is neutral (text-ink-3 on bg-surface, ≥4.5:1 in both themes) —
|
||||||
decorative and hidden from assistive tech (per Leonie's a11y note). -->
|
never red/amber. Matches the archive-metadata chip pattern in the row. The
|
||||||
|
non-color cue (WCAG 1.4.1) is the calendar-with-question glyph; the visible
|
||||||
|
"Datum unbekannt" text is the redundant textual cue and IS the accessibility,
|
||||||
|
so it is announced inline (never aria-hidden) while the icon is decorative.
|
||||||
|
-->
|
||||||
|
<span
|
||||||
|
data-testid="undated-badge"
|
||||||
|
class="inline-flex items-center gap-1 rounded border border-line px-1.5 py-0.5 font-sans text-[10px] tracking-widest text-ink-3 uppercase"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="h-3.5 w-3.5 shrink-0 text-ink-3"
|
class="h-3 w-3 shrink-0 text-ink-3"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -48,9 +56,13 @@ const showRawLine = $derived(
|
|||||||
<path d="M9 16a1.5 1.5 0 0 1 3 0c0 1-1.5 1.2-1.5 2.2" />
|
<path d="M9 16a1.5 1.5 0 0 1 3 0c0 1-1.5 1.2-1.5 2.2" />
|
||||||
<path d="M10.5 21h.01" />
|
<path d="M10.5 21h.01" />
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
{label}
|
||||||
<span>{label}</span>
|
</span>
|
||||||
</span>
|
{:else}
|
||||||
|
<span class="inline-flex items-center gap-1">
|
||||||
|
<span>{label}</span>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
{#if showRawLine}
|
{#if showRawLine}
|
||||||
<!-- Visible secondary line (WCAG 1.4.13 — not tooltip-only). raw is untrusted
|
<!-- Visible secondary line (WCAG 1.4.13 — not tooltip-only). raw is untrusted
|
||||||
verbatim spreadsheet text; rendered via default Svelte interpolation, which
|
verbatim spreadsheet text; rendered via default Svelte interpolation, which
|
||||||
|
|||||||
@@ -168,16 +168,12 @@ function safeTagColor(color: string | null | undefined): string {
|
|||||||
document DETAIL page, never in list/search rows — list rows surface only the
|
document DETAIL page, never in list/search rows — list rows surface only the
|
||||||
honest label to keep scan-rows compact. showRaw={false} enforces this; the
|
honest label to keep scan-rows compact. showRaw={false} enforces this; the
|
||||||
DocumentListItem payload also intentionally omits metaDateRaw. -->
|
DocumentListItem payload also intentionally omits metaDateRaw. -->
|
||||||
{#if doc.documentDate}
|
<DocumentDate
|
||||||
<DocumentDate
|
iso={doc.documentDate}
|
||||||
iso={doc.documentDate}
|
precision={doc.metaDatePrecision}
|
||||||
precision={doc.metaDatePrecision}
|
end={doc.metaDateEnd}
|
||||||
end={doc.metaDateEnd}
|
showRaw={false}
|
||||||
showRaw={false}
|
/>
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
—
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<ProgressRing percentage={item.completionPercentage} />
|
<ProgressRing percentage={item.completionPercentage} />
|
||||||
|
|||||||
@@ -73,6 +73,35 @@ describe('DocumentRow – title', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── Date rendering (#668) ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('DocumentRow – date rendering', () => {
|
||||||
|
it('renders a "Datum unbekannt" badge for an undated document', async () => {
|
||||||
|
const item = makeItem({ documentDate: undefined, metaDatePrecision: 'UNKNOWN' });
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
// The badge text appears (once per breakpoint block).
|
||||||
|
await expect.element(page.getByText('Datum unbekannt').first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render a bare em-dash for an undated document', async () => {
|
||||||
|
const item = makeItem({ documentDate: undefined, metaDatePrecision: 'UNKNOWN' });
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText('—', { exact: true }).first()).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the full date for a day-precision document', async () => {
|
||||||
|
const item = makeItem({ documentDate: '1943-12-24', metaDatePrecision: 'DAY' });
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText(/24\. Dezember 1943/).first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders month precision honestly without fabricating a day', async () => {
|
||||||
|
const item = makeItem({ documentDate: '1916-06-01', metaDatePrecision: 'MONTH' });
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText(/Juni 1916/).first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ─── Snippet ──────────────────────────────────────────────────────────────────
|
// ─── Snippet ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
describe('DocumentRow – snippet', () => {
|
describe('DocumentRow – snippet', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user