fix(bulk-upload): i18n hardcoded strings in BulkDropZone and FileSwitcherStrip
- Add bulk_drop_desc, bulk_select_files, bulk_drop_zone_label, bulk_remove_file keys to de/en/es message files - BulkDropZone: use m.bulk_drop_zone_label(), m.bulk_drop_desc(), m.bulk_select_files() — removes all hardcoded German - FileSwitcherStrip: use m.bulk_remove_file() on × button; move aria-live from <ul> to a dedicated visually-hidden region above the strip (screen readers now announce changes without coupling the live region to the list) - Spec: import FileEntry from component instead of re-declaring; use data-remove-id selector instead of hardcoded German aria-label Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -867,5 +867,9 @@
|
||||
"bulk_file_error_chip_label": "Fehler beim Hochladen",
|
||||
"bulk_upload_progress": "{done} von {total} hochgeladen",
|
||||
"bulk_partial_success": "{created} erstellt, {failed} fehlgeschlagen",
|
||||
"bulk_all_failed": "Alle Uploads fehlgeschlagen"
|
||||
"bulk_all_failed": "Alle Uploads fehlgeschlagen",
|
||||
"bulk_drop_desc": "Für jede Datei wird ein eigenes Dokument erstellt. Der Titel wird aus dem Dateinamen vorausgefüllt — alle anderen Felder gelten für alle gemeinsam.",
|
||||
"bulk_select_files": "Dateien auswählen",
|
||||
"bulk_drop_zone_label": "Dateien ablegen",
|
||||
"bulk_remove_file": "Entfernen"
|
||||
}
|
||||
|
||||
@@ -867,5 +867,9 @@
|
||||
"bulk_file_error_chip_label": "Upload failed",
|
||||
"bulk_upload_progress": "{done} of {total} uploaded",
|
||||
"bulk_partial_success": "{created} created, {failed} failed",
|
||||
"bulk_all_failed": "All uploads failed"
|
||||
"bulk_all_failed": "All uploads failed",
|
||||
"bulk_drop_desc": "A separate document is created for each file. The title is pre-filled from the filename — all other fields apply to all documents.",
|
||||
"bulk_select_files": "Select files",
|
||||
"bulk_drop_zone_label": "Drop files here",
|
||||
"bulk_remove_file": "Remove"
|
||||
}
|
||||
|
||||
@@ -867,5 +867,9 @@
|
||||
"bulk_file_error_chip_label": "Error al subir",
|
||||
"bulk_upload_progress": "{done} de {total} subidos",
|
||||
"bulk_partial_success": "{created} creados, {failed} fallidos",
|
||||
"bulk_all_failed": "Todos los uploads fallaron"
|
||||
"bulk_all_failed": "Todos los uploads fallaron",
|
||||
"bulk_drop_desc": "Se crea un documento separado por archivo. El título se rellena desde el nombre del archivo — el resto de campos se aplican a todos.",
|
||||
"bulk_select_files": "Seleccionar archivos",
|
||||
"bulk_drop_zone_label": "Soltar archivos aquí",
|
||||
"bulk_remove_file": "Eliminar"
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ describe('BulkDocumentEditLayout', () => {
|
||||
makeFile('file2.pdf')
|
||||
]);
|
||||
|
||||
// Remove the chip for file1 via its "Entfernen" remove button (second × button)
|
||||
// Remove the chip for file1 via its remove button (identified by data-remove-id)
|
||||
const removeButtons = container.querySelectorAll<HTMLButtonElement>(
|
||||
'[data-testid="file-switcher-strip"] button[aria-label="Entfernen"]'
|
||||
'[data-testid="file-switcher-strip"] button[data-remove-id]'
|
||||
);
|
||||
expect(removeButtons.length).toBe(3);
|
||||
removeButtons[1].click(); // remove file1
|
||||
|
||||
@@ -12,7 +12,7 @@ let isDragging = $state(false);
|
||||
|
||||
<div
|
||||
role="region"
|
||||
aria-label="Dateien ablegen"
|
||||
aria-label={m.bulk_drop_zone_label()}
|
||||
data-testid="bulk-drop-zone"
|
||||
class="flex flex-1 flex-col items-center justify-center p-6"
|
||||
ondragover={(e) => {
|
||||
@@ -54,17 +54,13 @@ let isDragging = $state(false);
|
||||
<p class="font-serif text-base font-bold text-ink">{m.bulk_drop_hint()}</p>
|
||||
|
||||
<!-- Sub description -->
|
||||
<p class="text-sm leading-relaxed text-ink-2">
|
||||
Für jede Datei wird ein eigenes Dokument erstellt.<br />
|
||||
<strong class="text-ink">Der Titel</strong> wird aus dem Dateinamen vorausgefüllt —
|
||||
<strong class="text-ink">alle anderen Felder</strong> gelten für alle gemeinsam.
|
||||
</p>
|
||||
<p class="text-sm leading-relaxed text-ink-2">{m.bulk_drop_desc()}</p>
|
||||
|
||||
<!-- CTA button -->
|
||||
<label
|
||||
class="flex min-h-[44px] cursor-pointer items-center rounded-sm bg-primary px-6 py-2 text-xs font-bold tracking-widest text-primary-fg uppercase transition-opacity hover:opacity-90"
|
||||
>
|
||||
Dateien auswählen
|
||||
{m.bulk_select_files()}
|
||||
<input
|
||||
type="file"
|
||||
multiple
|
||||
|
||||
@@ -58,6 +58,7 @@ $effect(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<div aria-live="polite" aria-atomic="true" class="sr-only"></div>
|
||||
<div
|
||||
data-testid="file-switcher-strip"
|
||||
class="flex h-11 shrink-0 items-center gap-1 border-t border-line bg-pdf-ctrl px-2"
|
||||
@@ -71,7 +72,7 @@ $effect(() => {
|
||||
>
|
||||
|
||||
<div bind:this={trackEl} class="flex flex-1 gap-1 overflow-x-auto" style="scrollbar-width:none">
|
||||
<ul bind:this={listEl} aria-live="polite" role="list" class="flex flex-row gap-1 py-1">
|
||||
<ul bind:this={listEl} role="list" class="flex flex-row gap-1 py-1">
|
||||
{#each files as entry, i (entry.id)}
|
||||
<li role="listitem" class="inline-flex shrink-0 items-center">
|
||||
<button
|
||||
@@ -102,7 +103,7 @@ $effect(() => {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Entfernen"
|
||||
aria-label={m.bulk_remove_file()}
|
||||
data-remove-id={entry.id}
|
||||
onclick={() => onRemove(entry.id)}
|
||||
class="ml-0.5 flex h-[44px] w-[44px] items-center justify-center text-base text-ink-3 hover:text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
|
||||
|
||||
@@ -2,17 +2,10 @@ import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page, userEvent } from 'vitest/browser';
|
||||
import FileSwitcherStrip from './FileSwitcherStrip.svelte';
|
||||
import type { FileEntry } from './FileSwitcherStrip.svelte';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
export interface FileEntry {
|
||||
id: string;
|
||||
file: File;
|
||||
title: string;
|
||||
status: 'idle' | 'error';
|
||||
previewUrl: string;
|
||||
}
|
||||
|
||||
function makeFiles(n: number): FileEntry[] {
|
||||
return Array.from({ length: n }, (_, i) => ({
|
||||
id: `id-${i}`,
|
||||
|
||||
Reference in New Issue
Block a user