forked from molecule-ai/molecule-core
fix(canvas/a11y): add type=button to 24 buttons across DetailsTab, ConfigTab, FilesTab, MemoryTab
WCAG 4.1.2 / bug #1669 follow-up — DetailsTab, ConfigTab, FilesTab, and MemoryTab had buttons without explicit type="button", causing accidental form submission in any surrounding <form> context. Changes: - DetailsTab (9 buttons): Save, Cancel (edit), Restart/Retry, Edit, View console output, peer select, Confirm Delete, Cancel (delete), Delete Workspace - ConfigTab AgentCardSection (3): Save, Cancel, Edit Agent Card - ConfigTab footer (3): Save & Restart, Save, Reload - ConfigTab textareas (2): aria-label added to Agent Card JSON editor and Raw YAML editor - FilesTab (4): Delete All, Cancel, Delete, Cancel - MemoryTab (11): Expand/Collapse, Open, Expand (collapsed state), Advanced, Refresh, Add, Save, Cancel (add form), expand entry, Delete entry, Show Total: 32 interactive elements corrected across 4 tab components. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4597ab06fc
commit
59feb65252
@ -51,17 +51,18 @@ function AgentCardSection({ workspaceId }: { workspaceId: string }) {
|
||||
) : editing ? (
|
||||
<div className="space-y-2">
|
||||
<textarea
|
||||
aria-label="Agent card JSON editor"
|
||||
value={draft} onChange={(e) => setDraft(e.target.value)}
|
||||
spellCheck={false} rows={12}
|
||||
className="w-full bg-zinc-800 border border-zinc-700 rounded p-2 text-[10px] font-mono text-zinc-200 focus:outline-none focus:border-blue-500 resize-none"
|
||||
/>
|
||||
{error && <div className="px-2 py-1 bg-red-900/30 border border-red-800 rounded text-[10px] text-red-400">{error}</div>}
|
||||
<div className="flex gap-2">
|
||||
<button onClick={handleSave} disabled={saving}
|
||||
<button type="button" onClick={handleSave} disabled={saving}
|
||||
className="px-2 py-1 bg-blue-600 hover:bg-blue-500 text-[10px] rounded text-white disabled:opacity-50">
|
||||
{saving ? "Saving..." : "Save"}
|
||||
</button>
|
||||
<button onClick={() => setEditing(false)}
|
||||
<button type="button" onClick={() => setEditing(false)}
|
||||
className="px-2 py-1 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -75,7 +76,7 @@ function AgentCardSection({ workspaceId }: { workspaceId: string }) {
|
||||
<div className="text-[10px] text-zinc-500">No agent card</div>
|
||||
)}
|
||||
{success && <div className="mt-2 px-2 py-1 bg-green-900/30 border border-green-800 rounded text-[10px] text-green-400">Updated</div>}
|
||||
<button onClick={() => { setDraft(JSON.stringify(card || {}, null, 2)); setEditing(true); setError(null); setSuccess(false); }}
|
||||
<button type="button" onClick={() => { setDraft(JSON.stringify(card || {}, null, 2)); setEditing(true); setError(null); setSuccess(false); }}
|
||||
className="mt-2 text-[10px] text-blue-400 hover:text-blue-300">Edit Agent Card</button>
|
||||
</div>
|
||||
)}
|
||||
@ -372,6 +373,7 @@ export function ConfigTab({ workspaceId }: Props) {
|
||||
{rawMode ? (
|
||||
<div className="flex-1 p-3">
|
||||
<textarea
|
||||
aria-label="Raw YAML editor"
|
||||
value={rawDraft}
|
||||
onChange={(e) => setRawDraft(e.target.value)}
|
||||
spellCheck={false}
|
||||
@ -633,6 +635,7 @@ export function ConfigTab({ workspaceId }: Props) {
|
||||
|
||||
<div className="p-3 border-t border-zinc-800 flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleSave(true)}
|
||||
disabled={!isDirty || saving}
|
||||
className="px-3 py-1.5 bg-blue-600 hover:bg-blue-500 text-xs rounded text-white disabled:opacity-30 transition-colors"
|
||||
@ -640,6 +643,7 @@ export function ConfigTab({ workspaceId }: Props) {
|
||||
{saving ? "Restarting..." : "Save & Restart"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleSave(false)}
|
||||
disabled={!isDirty || saving}
|
||||
className="px-3 py-1.5 bg-zinc-700 hover:bg-zinc-600 text-xs rounded text-zinc-300 disabled:opacity-30 transition-colors"
|
||||
@ -647,6 +651,7 @@ export function ConfigTab({ workspaceId }: Props) {
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={loadConfig}
|
||||
className="px-3 py-1.5 bg-zinc-700 hover:bg-zinc-600 text-xs rounded text-zinc-300 ml-auto"
|
||||
>
|
||||
|
||||
@ -159,6 +159,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
)}
|
||||
<div className="flex gap-2 pt-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="px-3 py-1 bg-blue-600 hover:bg-blue-500 text-xs rounded text-white disabled:opacity-50"
|
||||
@ -166,6 +167,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
{saving ? "Saving..." : "Save"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setEditing(false);
|
||||
setSaveError(null);
|
||||
@ -199,6 +201,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRestart}
|
||||
disabled={restarting}
|
||||
className="px-3 py-1 bg-green-700 hover:bg-green-600 text-xs rounded text-white disabled:opacity-50"
|
||||
@ -208,6 +211,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEditing(true)}
|
||||
className="mt-2 px-3 py-1 bg-zinc-700 hover:bg-zinc-600 text-xs rounded text-zinc-300"
|
||||
>
|
||||
@ -234,6 +238,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
<p className="text-xs text-zinc-500">No error detail recorded.</p>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConsoleOpen(true)}
|
||||
className="mt-2 px-3 py-1 bg-zinc-800 hover:bg-zinc-700 text-xs rounded text-zinc-300 border border-zinc-700"
|
||||
>
|
||||
@ -279,6 +284,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
{peers.map((p) => (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
onClick={() => selectNode(p.id)}
|
||||
className="w-full flex items-center gap-2 px-2 py-1 rounded hover:bg-zinc-800 text-left"
|
||||
>
|
||||
@ -310,12 +316,14 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
</h3>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDelete}
|
||||
className="px-3 py-1 bg-red-600 hover:bg-red-500 text-xs rounded text-white"
|
||||
>
|
||||
Confirm Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setConfirmDelete(false);
|
||||
setDeleteError(null);
|
||||
@ -330,6 +338,7 @@ export function DetailsTab({ workspaceId, data }: Props) {
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
ref={deleteButtonRef}
|
||||
onClick={() => setConfirmDelete(true)}
|
||||
className="px-3 py-1 bg-zinc-800 hover:bg-red-900 border border-zinc-700 hover:border-red-700 text-xs rounded text-zinc-400 hover:text-red-400 transition-colors"
|
||||
|
||||
@ -165,8 +165,8 @@ export function FilesTab({ workspaceId }: Props) {
|
||||
<div className="mx-3 mt-2 px-3 py-2 bg-red-950/30 border border-red-800/40 rounded space-y-1.5">
|
||||
<p className="text-xs text-red-300">Delete all {files.filter((f) => !f.dir).length} files? This cannot be undone.</p>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => { handleDeleteAll(); setShowDeleteAll(false); }} className="px-2 py-0.5 bg-red-600 hover:bg-red-500 text-[10px] rounded text-white">Delete All</button>
|
||||
<button onClick={() => setShowDeleteAll(false)} className="px-2 py-0.5 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300">Cancel</button>
|
||||
<button type="button" onClick={() => { handleDeleteAll(); setShowDeleteAll(false); }} className="px-2 py-0.5 bg-red-600 hover:bg-red-500 text-[10px] rounded text-white">Delete All</button>
|
||||
<button type="button" onClick={() => setShowDeleteAll(false)} className="px-2 py-0.5 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -179,8 +179,8 @@ export function FilesTab({ workspaceId }: Props) {
|
||||
<div className="mx-3 mt-2 px-3 py-2 bg-amber-950/30 border border-amber-800/40 rounded space-y-1.5">
|
||||
<p className="text-xs text-amber-300">Delete <span className="font-mono">{confirmDelete}</span>{files.find((f) => f.path === confirmDelete && f.dir) ? " and all its contents" : ""}?</p>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={confirmDeleteFile} className="px-2 py-0.5 bg-red-600 hover:bg-red-500 text-[10px] rounded text-white">Delete</button>
|
||||
<button onClick={() => setConfirmDelete(null)} className="px-2 py-0.5 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300">Cancel</button>
|
||||
<button type="button" onClick={confirmDeleteFile} className="px-2 py-0.5 bg-red-600 hover:bg-red-500 text-[10px] rounded text-white">Delete</button>
|
||||
<button type="button" onClick={() => setConfirmDelete(null)} className="px-2 py-0.5 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -135,12 +135,14 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAwareness((prev) => !prev)}
|
||||
className="shrink-0 px-2 py-1 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-200"
|
||||
>
|
||||
{showAwareness ? "Collapse" : "Expand"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={openAwareness}
|
||||
className="shrink-0 px-2 py-1 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-200"
|
||||
>
|
||||
@ -173,6 +175,7 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAwareness(true)}
|
||||
className="shrink-0 px-2 py-1 bg-blue-600 hover:bg-blue-500 text-[10px] rounded text-white"
|
||||
>
|
||||
@ -207,18 +210,21 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAdvanced((prev) => !prev)}
|
||||
className="px-2 py-1 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300"
|
||||
>
|
||||
{showAdvanced ? "Hide Advanced" : "Advanced"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={loadMemory}
|
||||
className="px-2 py-1 bg-zinc-700 hover:bg-zinc-600 text-[10px] rounded text-zinc-300"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setShowAdd(!showAdd); if (!showAdd) setShowAdvanced(true); }}
|
||||
className="px-2 py-1 bg-blue-600 hover:bg-blue-500 text-[10px] rounded text-white"
|
||||
>
|
||||
@ -254,12 +260,14 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
{error && <div role="alert" className="text-xs text-red-400">{error}</div>}
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAdd}
|
||||
className="px-3 py-1 bg-blue-600 hover:bg-blue-500 text-xs rounded text-white"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShowAdd(false);
|
||||
setError(null);
|
||||
@ -280,6 +288,7 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
{entries.map((entry) => (
|
||||
<div key={entry.key} className="bg-zinc-800 rounded border border-zinc-700">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded(expanded === entry.key ? null : entry.key)}
|
||||
className="w-full flex items-center justify-between px-3 py-2 text-left"
|
||||
aria-expanded={expanded === entry.key}
|
||||
@ -307,6 +316,7 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
Updated: {new Date(entry.updated_at).toLocaleString()}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDelete(entry.key)}
|
||||
className="text-[10px] text-red-400 hover:text-red-300"
|
||||
>
|
||||
@ -328,6 +338,7 @@ export function MemoryTab({ workspaceId }: Props) {
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAdvanced(true)}
|
||||
className="shrink-0 px-2 py-1 bg-blue-600 hover:bg-blue-500 text-[10px] rounded text-white"
|
||||
>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user