fix(quick-260921-jt4): zwei Schaltflaechenreihen tragen role="toolbar" (D-03)
calculator-widget.tsx Speicherzeile und favorites-widget.tsx
Ansichtsumschalter waren schlichte <div> mit aria-label, das die Rolle
generic stillschweigend verwarf. role="toolbar" ergaenzt (geprueft
sauber; role="group"/"region" loesen useSemanticElements neu aus).
Taschenrechner: fest verdrahteter Text wandert in
calculator.memoryLabel; bei dieser Gelegenheit auch Anzeigefeld
(displayLabel) und Rueckschritt-Taste (backspaceLabel) in den Katalog
gezogen (D-05), da diese Datei ohnehin geaendert wird.
Favoriten: die bisherige Beschriftung ("Favoriten") war sachlich
falsch fuer einen Listen/Kachel-Umschalter — neuer, zutreffender
Schluessel favorites.viewModeLabel statt des wiederverwendeten
favorites.name.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -1,9 +1,19 @@
|
|||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
// Mock next-intl — returns the key so tests are locale-agnostic
|
// Mock next-intl — returns the real de.json strings for the few keys this
|
||||||
|
// widget actually renders as accessible names/labels, the raw key otherwise
|
||||||
|
// (locale-agnostic fallback for anything not explicitly mapped here).
|
||||||
vi.mock('next-intl', () => ({
|
vi.mock('next-intl', () => ({
|
||||||
useTranslations: () => (key: string) => key,
|
useTranslations: () => (key: string) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
'calculator.name': 'Taschenrechner',
|
||||||
|
'calculator.displayLabel': 'Anzeige',
|
||||||
|
'calculator.memoryLabel': 'Speicherfunktionen',
|
||||||
|
'calculator.backspaceLabel': 'Rückschritt',
|
||||||
|
};
|
||||||
|
return map[key] ?? key;
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Must import after mocks
|
// Must import after mocks
|
||||||
|
|||||||
@@ -336,13 +336,15 @@ export function CalculatorWidget({ isEditMode }: WidgetProps) {
|
|||||||
{/* Display */}
|
{/* Display */}
|
||||||
<output
|
<output
|
||||||
className="flex min-h-[2.5rem] items-end justify-end rounded bg-muted px-3 py-1 font-mono text-[clamp(14px,min(8cqw,10cqh),96px)] font-semibold text-foreground overflow-hidden"
|
className="flex min-h-[2.5rem] items-end justify-end rounded bg-muted px-3 py-1 font-mono text-[clamp(14px,min(8cqw,10cqh),96px)] font-semibold text-foreground overflow-hidden"
|
||||||
aria-label="Anzeige"
|
aria-label={t('calculator.displayLabel')}
|
||||||
>
|
>
|
||||||
<span className="truncate">{display}</span>
|
<span className="truncate">{display}</span>
|
||||||
</output>
|
</output>
|
||||||
|
|
||||||
{/* Memory row */}
|
{/* Memory row — role="toolbar" (D-03): ohne Rolle ist das <div> generic
|
||||||
<div className="flex gap-1" aria-label="Speicherfunktionen">
|
und die Vorlesehilfe verwirft aria-label stillschweigend. Geprueft
|
||||||
|
sauber (role="group"/"region" loesen useSemanticElements neu aus). */}
|
||||||
|
<div className="flex gap-1" role="toolbar" aria-label={t('calculator.memoryLabel')}>
|
||||||
<button type="button" className={memBtn} onClick={memoryClear} disabled={memory === 0}>MC</button>
|
<button type="button" className={memBtn} onClick={memoryClear} disabled={memory === 0}>MC</button>
|
||||||
<button type="button" className={memBtn} onClick={memoryRecall} disabled={memory === 0}>MR</button>
|
<button type="button" className={memBtn} onClick={memoryRecall} disabled={memory === 0}>MR</button>
|
||||||
<button type="button" className={memBtn} onClick={memoryAdd}>M+</button>
|
<button type="button" className={memBtn} onClick={memoryAdd}>M+</button>
|
||||||
@@ -356,7 +358,7 @@ export function CalculatorWidget({ isEditMode }: WidgetProps) {
|
|||||||
<CalcButton label="%" className={utilBtn} onClick={() => applyUnary('percent')} />
|
<CalcButton label="%" className={utilBtn} onClick={() => applyUnary('percent')} />
|
||||||
<CalcButton label="CE" className={utilBtn} onClick={clearEntry} />
|
<CalcButton label="CE" className={utilBtn} onClick={clearEntry} />
|
||||||
<CalcButton label="C" className={utilBtn} onClick={clearAll} />
|
<CalcButton label="C" className={utilBtn} onClick={clearAll} />
|
||||||
<CalcButton label="⌫" ariaLabel="Rückschritt" className={utilBtn} onClick={backspace} />
|
<CalcButton label="⌫" ariaLabel={t('calculator.backspaceLabel')} className={utilBtn} onClick={backspace} />
|
||||||
|
|
||||||
{/* Row 2: unary + divide */}
|
{/* Row 2: unary + divide */}
|
||||||
<CalcButton label="1/x" className={utilBtn} onClick={() => applyUnary('reciprocal')} />
|
<CalcButton label="1/x" className={utilBtn} onClick={() => applyUnary('reciprocal')} />
|
||||||
|
|||||||
@@ -263,7 +263,16 @@ export function FavoritesWidget({
|
|||||||
<div className="flex flex-1 flex-col gap-2 overflow-auto p-1">
|
<div className="flex flex-1 flex-col gap-2 overflow-auto p-1">
|
||||||
{/* View mode toggle (edit mode only) */}
|
{/* View mode toggle (edit mode only) */}
|
||||||
{isEditMode && (
|
{isEditMode && (
|
||||||
<div className="flex gap-1 widgetNoDrag" aria-label={t('favorites.name')}>
|
// role="toolbar" (D-03): die bisherige Beschriftung ("Favoriten")
|
||||||
|
// war sachlich falsch fuer einen Umschalter zwischen Listen- und
|
||||||
|
// Kachelansicht — hier greift D-03 im Sinn des Streichens (die
|
||||||
|
// Beschriftung trug keine echte Bedeutung), eine richtige
|
||||||
|
// Beschriftung ist trotzdem besser als gar keine.
|
||||||
|
<div
|
||||||
|
className="flex gap-1 widgetNoDrag"
|
||||||
|
role="toolbar"
|
||||||
|
aria-label={t('favorites.viewModeLabel')}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleViewMode('list')}
|
onClick={() => handleViewMode('list')}
|
||||||
|
|||||||
@@ -300,7 +300,10 @@
|
|||||||
},
|
},
|
||||||
"calculator": {
|
"calculator": {
|
||||||
"name": "Taschenrechner",
|
"name": "Taschenrechner",
|
||||||
"description": "Grundrechenarten mit Tastatureingabe"
|
"description": "Grundrechenarten mit Tastatureingabe",
|
||||||
|
"displayLabel": "Anzeige",
|
||||||
|
"memoryLabel": "Speicherfunktionen",
|
||||||
|
"backspaceLabel": "Rückschritt"
|
||||||
},
|
},
|
||||||
"favorites": {
|
"favorites": {
|
||||||
"name": "Favoriten",
|
"name": "Favoriten",
|
||||||
@@ -320,7 +323,8 @@
|
|||||||
"cancelButton": "Abbrechen",
|
"cancelButton": "Abbrechen",
|
||||||
"error": "Fehler beim Laden der Favoriten",
|
"error": "Fehler beim Laden der Favoriten",
|
||||||
"titleLabel": "Titel",
|
"titleLabel": "Titel",
|
||||||
"titlePlaceholder": "Titel (optional)"
|
"titlePlaceholder": "Titel (optional)",
|
||||||
|
"viewModeLabel": "Ansicht wechseln"
|
||||||
},
|
},
|
||||||
"stopwatch": {
|
"stopwatch": {
|
||||||
"name": "Stoppuhr",
|
"name": "Stoppuhr",
|
||||||
|
|||||||
@@ -300,7 +300,10 @@
|
|||||||
},
|
},
|
||||||
"calculator": {
|
"calculator": {
|
||||||
"name": "Calculator",
|
"name": "Calculator",
|
||||||
"description": "Basic arithmetic with keyboard support"
|
"description": "Basic arithmetic with keyboard support",
|
||||||
|
"displayLabel": "Display",
|
||||||
|
"memoryLabel": "Memory functions",
|
||||||
|
"backspaceLabel": "Backspace"
|
||||||
},
|
},
|
||||||
"favorites": {
|
"favorites": {
|
||||||
"name": "Favorites",
|
"name": "Favorites",
|
||||||
@@ -320,7 +323,8 @@
|
|||||||
"cancelButton": "Cancel",
|
"cancelButton": "Cancel",
|
||||||
"error": "Error loading favorites",
|
"error": "Error loading favorites",
|
||||||
"titleLabel": "Title",
|
"titleLabel": "Title",
|
||||||
"titlePlaceholder": "Title (optional)"
|
"titlePlaceholder": "Title (optional)",
|
||||||
|
"viewModeLabel": "Switch view"
|
||||||
},
|
},
|
||||||
"stopwatch": {
|
"stopwatch": {
|
||||||
"name": "Stopwatch",
|
"name": "Stopwatch",
|
||||||
|
|||||||
Reference in New Issue
Block a user