fix(quick-260921-pi9): Grossansicht per Portal, "1 Minute" statt "1 Minuten", Kachel-Vorgabe 8x12

Befunde aus dem Browser-Rundgang am 21.09.2026:

- Die Grossansicht lag in einem `react-grid-item` mit CSS-`transform`; ein
  transformierter Vorfahr wird fuer `position: fixed` zum Bezugsrahmen, der
  Dialog war deshalb auf die Kachelflaeche (531x216) beschraenkt statt den
  Viewport zu fuellen. Jetzt per `createPortal` in `document.body`, wie der
  Kalender-Tooltip.
- Wechselintervall "1 Minuten" -> ICU-Plural (`one {# Minute}`), de + en; der
  Formular-Test nutzt dafuer den echten `createTranslator` von next-intl auf
  der echten de.json statt eines `{n}`-Ersatzes.
- Standardgroesse 8x8 (216 px hoch) war zu flach fuer ein Foto -> 8x12 wie die
  Kalender-Vorgabe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-21 19:10:52 +02:00
parent c3b45974f5
commit 8bf3601a18
6 changed files with 28 additions and 21 deletions
@@ -8,19 +8,14 @@ import de from '@/messages/de.json';
* Eintragsliste mit Vorschau/Unterschrift/Pfeilen/Entfernen, Upload-Kette,
* https-Pruefung, Grenze 30.
*/
vi.mock('next-intl', async () => {
const messages = (await import('@/messages/de.json')).default as Record<string, unknown>;
const lookup = (path: string): string | undefined =>
path.split('.').reduce<unknown>((o, k) => (o && typeof o === 'object' ? (o as Record<string, unknown>)[k] : undefined), messages) as
| string
| undefined;
vi.mock('next-intl', async (importOriginal) => {
// Echter ICU-Uebersetzer von next-intl auf der echten de.json: die
// Intervall-Beschriftung nutzt `{n, plural, one {…} other {…}}`, ein
// handgeschriebener `{n}`-Ersatz wuerde das Muster nicht verstehen.
const actual = await importOriginal<typeof import('next-intl')>();
const messages = (await import('@/messages/de.json')).default;
return {
useTranslations:
(ns?: string) =>
(key: string, values?: Record<string, unknown>) => {
const raw = lookup(ns ? `${ns}.${key}` : key) ?? key;
return values ? raw.replace(/\{(\w+)\}/g, (_: string, n: string) => String(values[n] ?? '')) : raw;
},
useTranslations: (ns?: string) => actual.createTranslator({ locale: 'de', messages, namespace: ns }),
};
});
@@ -70,6 +65,7 @@ describe('PictureFrameConfigForm (quick-260921-pi9)', () => {
expect(Array.from(interval.options).map((o) => o.textContent)).toContain('Kein Wechsel');
expect(Array.from(interval.options).map((o) => o.textContent)).toContain('5 Sekunden');
expect(Array.from(interval.options).map((o) => o.textContent)).toContain('30 Minuten');
expect(Array.from(interval.options).map((o) => o.textContent)).toContain('1 Minute');
fireEvent.change(fit, { target: { value: 'cover' } });
expect(onChange).toHaveBeenLastCalledWith({ fit: 'cover' });