fix(a11y): MemoryInspectorPanel — sanitise bodyId, add aria-controls (#904)
Memory keys can contain characters like [ ] / : . # and spaces that make
invalid HTML id values (breaks CSS selectors and ARIA id-ref lookups).
- Add sanitizeId() helper: replaces non-alphanumeric chars with hyphens,
collapses consecutive hyphens, strips leading/trailing hyphens
- Compute bodyId = "mem-body-{sanitizeId(entry.key)}" in MemoryEntryRow
- Set id={bodyId} on the expanded body container
- Set aria-controls={bodyId} on the toggle button so AT can navigate
directly between the button and its controlled panel
Closes #904
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
377a0802b2
commit
5d1e297231
@ -27,6 +27,19 @@ interface Props {
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Sanitise a memory key for use in an HTML id attribute.
|
||||
* HTML IDs must not contain whitespace; many non-alphanumeric characters also
|
||||
* cause selector or ARIA failures. Replace every non-alphanumeric character
|
||||
* with a hyphen, collapse consecutive hyphens, then strip leading/trailing ones.
|
||||
*/
|
||||
function sanitizeId(key: string): string {
|
||||
return key
|
||||
.replace(/[^a-zA-Z0-9]/g, "-")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
}
|
||||
|
||||
function formatRelativeTime(iso: string): string {
|
||||
const diff = Date.now() - new Date(iso).getTime();
|
||||
if (diff < 60_000) return `${Math.floor(diff / 1000)}s`;
|
||||
@ -280,6 +293,10 @@ function MemoryEntryRow({
|
||||
onCancelEdit,
|
||||
onDelete,
|
||||
}: MemoryEntryRowProps) {
|
||||
// Sanitise the key so the generated id is a valid HTML id (no spaces or
|
||||
// special chars like [ ] / : . # that would break CSS selectors / ARIA).
|
||||
const bodyId = `mem-body-${sanitizeId(entry.key)}`;
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-zinc-800/60 bg-zinc-900/50 overflow-hidden">
|
||||
{/* Header row — click to expand/collapse */}
|
||||
@ -287,6 +304,7 @@ function MemoryEntryRow({
|
||||
className="w-full flex items-center gap-2 px-3 py-2.5 text-left hover:bg-zinc-800/30 transition-colors"
|
||||
onClick={onToggle}
|
||||
aria-expanded={isExpanded}
|
||||
aria-controls={bodyId}
|
||||
>
|
||||
<span className="text-[10px] font-mono text-blue-400 truncate flex-1 min-w-0">
|
||||
{entry.key}
|
||||
@ -304,7 +322,7 @@ function MemoryEntryRow({
|
||||
|
||||
{/* Expanded body */}
|
||||
{isExpanded && (
|
||||
<div className="border-t border-zinc-800/50 px-3 pb-3 pt-2 space-y-2">
|
||||
<div id={bodyId} className="border-t border-zinc-800/50 px-3 pb-3 pt-2 space-y-2">
|
||||
{entry.expires_at && (
|
||||
<p className="text-[9px] text-zinc-500">
|
||||
Expires: {new Date(entry.expires_at).toLocaleString()}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user