mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-07-21 08:15:23 -04:00
fix(frontend): add aria-labels to icon-only buttons
Resolves QA report BUG #12, #13, #14, #15, #18 — icon-only buttons across forms, sidebar, DataTable, and detail pages lacked accessible names. Applied handler-based naming convention so the label matches the prop / handler / state attribute, not the visual icon: - type="submit" + isSubmitting → "Submit" / "Submitting…" - onCancel + isCanceling → "Cancel" / "Cancelling…" - onToggleFavorite + isFavorite → "Toggle favorite" + aria-pressed - setTheme(value) + value → "<Value> theme" - Pagination via table.firstPage()/etc → "First/Previous/Next/Last page" - Row actions DropdownMenuTrigger → "Open menu" via existing sr-only convention Verified: 0 ESLint errors, 508/508 tests pass, regression sweep across 9 pages (/dashboard, /flows, /templates, /knowledges, /resources, /flows/:id, /settings/{providers,api-tokens,prompts}) returned 0 icon-only buttons without accessible name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -333,18 +333,21 @@ export function MainSidebar() {
|
||||
>
|
||||
<TabsList className="h-7 p-0.5">
|
||||
<TabsTrigger
|
||||
aria-label="System theme"
|
||||
className="h-6 px-2"
|
||||
value="system"
|
||||
>
|
||||
<Monitor className="size-4" />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
aria-label="Light theme"
|
||||
className="h-6 px-2"
|
||||
value="light"
|
||||
>
|
||||
<Sun className="size-4" />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
aria-label="Dark theme"
|
||||
className="h-6 px-2"
|
||||
value="dark"
|
||||
>
|
||||
@@ -418,6 +421,8 @@ function FlowMenuItem({ activeFlowId, flow, isFavorite, onToggleFavorite }: Flow
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
<SidebarMenuAction
|
||||
aria-label="Toggle favorite"
|
||||
aria-pressed={isFavorite}
|
||||
className="data-[state=open]:bg-accent rounded-sm"
|
||||
onClick={() => onToggleFavorite(flow.id)}
|
||||
showOnHover
|
||||
|
||||
@@ -820,6 +820,7 @@ function DataTable<TData, TValue = unknown>({
|
||||
)}
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
aria-label="First page"
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
onClick={() => table.firstPage()}
|
||||
size="icon-xs"
|
||||
@@ -828,6 +829,7 @@ function DataTable<TData, TValue = unknown>({
|
||||
<ChevronsLeft />
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Previous page"
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
onClick={() => table.previousPage()}
|
||||
size="icon-xs"
|
||||
@@ -836,6 +838,7 @@ function DataTable<TData, TValue = unknown>({
|
||||
<ChevronLeft />
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Next page"
|
||||
disabled={!table.getCanNextPage()}
|
||||
onClick={() => table.nextPage()}
|
||||
size="icon-xs"
|
||||
@@ -844,6 +847,7 @@ function DataTable<TData, TValue = unknown>({
|
||||
<ChevronRight />
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Last page"
|
||||
disabled={!table.getCanNextPage()}
|
||||
onClick={() => table.lastPage()}
|
||||
size="icon-xs"
|
||||
|
||||
@@ -761,6 +761,7 @@ export function FlowForm({
|
||||
|
||||
{!isLoading || isSubmitting ? (
|
||||
<InputGroupButton
|
||||
aria-label={isSubmitting ? 'Submitting…' : 'Submit'}
|
||||
className="shrink-0"
|
||||
disabled={isSubmitting || !isValid || upload.isUploading}
|
||||
size="icon-xs"
|
||||
@@ -771,6 +772,7 @@ export function FlowForm({
|
||||
</InputGroupButton>
|
||||
) : (
|
||||
<InputGroupButton
|
||||
aria-label={isCanceling ? 'Cancelling…' : 'Cancel'}
|
||||
className="shrink-0"
|
||||
disabled={isCanceling || !onCancel}
|
||||
onClick={() => onCancel?.()}
|
||||
|
||||
@@ -114,6 +114,8 @@ function FlowTasksDropdown({ disabled, onChange, value }: FlowTasksDropdownProps
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
aria-label="Filter tasks"
|
||||
aria-pressed={hasActiveFilters}
|
||||
disabled={disabled}
|
||||
size="icon"
|
||||
variant="outline"
|
||||
|
||||
@@ -269,6 +269,8 @@ function Flow() {
|
||||
)}
|
||||
{flowId && !isMobile && (
|
||||
<Button
|
||||
aria-label="Toggle favorite"
|
||||
aria-pressed={isFavoriteFlow(flowId)}
|
||||
className="shrink-0"
|
||||
onClick={() => toggleFavoriteFlow(flowId)}
|
||||
size="icon"
|
||||
|
||||
@@ -420,6 +420,7 @@ function Flows() {
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
variant="ghost"
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Ellipsis />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -328,6 +328,7 @@ function Knowledges() {
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
variant="ghost"
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Ellipsis />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -505,6 +505,7 @@ function Resources() {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
aria-label="Column settings"
|
||||
className="ml-auto"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
|
||||
@@ -211,6 +211,7 @@ function CreateRowActions({
|
||||
return (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
aria-label={isLoading ? 'Submitting…' : 'Submit'}
|
||||
className="shrink-0"
|
||||
disabled={isLoading || !isValid}
|
||||
onClick={onSubmit}
|
||||
@@ -220,6 +221,7 @@ function CreateRowActions({
|
||||
{isLoading ? <Loader2 className="animate-spin" /> : <Check />}
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Cancel"
|
||||
className="shrink-0"
|
||||
onClick={onCancel}
|
||||
size="icon-sm"
|
||||
@@ -247,6 +249,7 @@ function EditRowActions({
|
||||
return (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
aria-label={isLoading ? 'Submitting…' : 'Submit'}
|
||||
className="shrink-0"
|
||||
disabled={isLoading || !isValid}
|
||||
onClick={onSubmit}
|
||||
@@ -256,6 +259,7 @@ function EditRowActions({
|
||||
{isLoading ? <Loader2 className="animate-spin" /> : <Check />}
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Cancel"
|
||||
className="shrink-0"
|
||||
onClick={onCancel}
|
||||
size="icon-sm"
|
||||
@@ -530,6 +534,7 @@ function SettingsAPITokens() {
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="text-sm">{tokenId}</code>
|
||||
<Button
|
||||
aria-label="Copy token ID"
|
||||
className="size-6 p-0"
|
||||
onClick={() => handleCopyTokenId(tokenId)}
|
||||
variant="ghost"
|
||||
|
||||
@@ -175,6 +175,7 @@ function Templates() {
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
variant="ghost"
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Ellipsis />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
Reference in New Issue
Block a user