fix(frontend): code-review polish — replaceState, arrow-key nav, TabId, comment

Fix 4: useHashTab navigate() uses history.replaceState instead of
window.location.hash assignment — no spurious history entries, no
anchor-jump scroll side-effect.
Fix 5: Tabs ArrowLeft/ArrowRight keyboard nav (WAI-ARIA tabs pattern).
Fix 6: TabId union type in EngagementDetailPage, cast from string for
type-safe switch on activeTab without breaking Tabs.onChange signature.
Fix 7: intentional comment on UsersAdminPage reset-password aerated row.
Tests: 236/236 (+2 arrow-key assertions in Tabs.test.tsx).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-06-21 22:20:51 +02:00
parent 790ced4204
commit 11ce3cfb86
6 changed files with 36 additions and 4 deletions

View File

@@ -1,3 +1,5 @@
import type { KeyboardEvent } from 'react';
interface TabItem {
id: string;
label: string;
@@ -11,12 +13,22 @@ interface TabsProps {
}
export function Tabs({ items, activeId, onChange }: TabsProps): JSX.Element {
function handleKeyDown(e: KeyboardEvent<HTMLButtonElement>, index: number) {
if (e.key === 'ArrowRight') {
e.preventDefault();
onChange(items[(index + 1) % items.length].id);
} else if (e.key === 'ArrowLeft') {
e.preventDefault();
onChange(items[(index - 1 + items.length) % items.length].id);
}
}
return (
<div
role="tablist"
className="flex items-end border-b border-hairline gap-xs"
>
{items.map((item) => {
{items.map((item, index) => {
const isActive = item.id === activeId;
return (
<button
@@ -27,6 +39,7 @@ export function Tabs({ items, activeId, onChange }: TabsProps): JSX.Element {
aria-controls={`tabpanel-${item.id}`}
type="button"
onClick={() => onChange(item.id)}
onKeyDown={(e) => handleKeyDown(e, index)}
className={`tab-underline${isActive ? ' tab-underline-active' : ''} pb-xs`}
>
{item.label}