Skip to content

Commit 5ce0f51

Browse files
committed
feat(ui): improve a11y
1 parent 5eca281 commit 5ce0f51

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

webview-ui/src/components/chat/ApiConfigSelector.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,21 @@ export const ApiConfigSelector = ({
223223
<div
224224
key={config.id}
225225
data-config-item
226+
role="option"
227+
aria-selected={isCurrentConfig}
228+
aria-label={`${config.name}${config.modelId ? ` - ${config.modelId}` : ""}`}
226229
draggable={isReorderMode && sortMode === "custom"}
227230
onDragStart={(e) => isReorderMode && handleDragStart(e, index)}
228231
onDragOver={(e) => isReorderMode && handleDragOver(e, index)}
229232
onDragEnd={handleDragEnd}
230233
onClick={() => !isReorderMode && handleSelect(config.id)}
234+
onKeyDown={(e) => {
235+
if (!isReorderMode && (e.key === "Enter" || e.key === " ")) {
236+
e.preventDefault()
237+
handleSelect(config.id)
238+
}
239+
}}
240+
tabIndex={!isReorderMode ? 0 : -1}
231241
className={cn(
232242
"px-3 py-1.5 text-sm flex items-center group relative",
233243
!isReorderMode && "cursor-pointer hover:bg-vscode-list-hoverBackground",
@@ -270,6 +280,7 @@ export const ApiConfigSelector = ({
270280
variant="ghost"
271281
size="icon"
272282
tabIndex={-1}
283+
aria-label={isPinned ? t("apiConfigSelector.unpin") : t("apiConfigSelector.pin")}
273284
onClick={(e) => {
274285
e.stopPropagation()
275286
togglePinnedApiConfig(config.id)
@@ -307,6 +318,9 @@ export const ApiConfigSelector = ({
307318
<PopoverTrigger
308319
disabled={disabled}
309320
data-testid="dropdown-trigger"
321+
aria-label={title}
322+
aria-expanded={open}
323+
aria-haspopup="listbox"
310324
className={cn(
311325
"w-full min-w-0 max-w-full inline-flex items-center gap-1.5 relative whitespace-nowrap px-1.5 py-1 text-xs",
312326
"bg-transparent border border-[rgba(255,255,255,0.08)] rounded-md text-vscode-foreground",
@@ -347,6 +361,15 @@ export const ApiConfigSelector = ({
347361
<span
348362
className="codicon codicon-close text-vscode-input-foreground opacity-50 hover:opacity-100 text-xs cursor-pointer"
349363
onClick={() => setSearchValue("")}
364+
aria-label="Clear search"
365+
role="button"
366+
tabIndex={0}
367+
onKeyDown={(e) => {
368+
if (e.key === "Enter" || e.key === " ") {
369+
e.preventDefault()
370+
setSearchValue("")
371+
}
372+
}}
350373
/>
351374
</div>
352375
)}
@@ -360,7 +383,11 @@ export const ApiConfigSelector = ({
360383
)}
361384

362385
{/* Config list */}
363-
<div ref={scrollContainerRef} className="max-h-[300px] overflow-y-auto">
386+
<div
387+
ref={scrollContainerRef}
388+
className="max-h-[300px] overflow-y-auto"
389+
role="listbox"
390+
aria-label={t("prompts:apiConfiguration.select")}>
364391
{filteredConfigs.length === 0 && searchValue ? (
365392
<div className="py-2 px-3 text-sm text-vscode-foreground/70">
366393
{t("common:ui.no_results")}
@@ -398,6 +425,8 @@ export const ApiConfigSelector = ({
398425
key={mode}
399426
variant="ghost"
400427
size="sm"
428+
aria-label={`${t("apiConfigSelector.sort")} ${mode === "alphabetical" ? t("apiConfigSelector.alphabetical") : t("apiConfigSelector.custom")}`}
429+
aria-pressed={sortMode === mode}
401430
onClick={() => handleSortModeChange(mode)}
402431
className={cn(
403432
"h-6 px-2 text-xs",
@@ -415,6 +444,7 @@ export const ApiConfigSelector = ({
415444
<Button
416445
variant="ghost"
417446
size="sm"
447+
aria-label={t("apiConfigSelector.reorder")}
418448
onClick={handleReorderClick}
419449
className="h-6 px-2 text-xs">
420450
{t("apiConfigSelector.reorder")}
@@ -433,13 +463,15 @@ export const ApiConfigSelector = ({
433463
<Button
434464
variant="ghost"
435465
size="sm"
466+
aria-label={t("apiConfigSelector.cancel")}
436467
onClick={handleReorderCancel}
437468
className="h-6 px-2 text-xs">
438469
{t("apiConfigSelector.cancel")}
439470
</Button>
440471
<Button
441472
variant="ghost"
442473
size="sm"
474+
aria-label={t("apiConfigSelector.done")}
443475
onClick={handleReorderDone}
444476
className="h-6 px-2 text-xs bg-vscode-button-background text-vscode-button-foreground">
445477
{t("apiConfigSelector.done")}
@@ -456,14 +488,19 @@ export const ApiConfigSelector = ({
456488
title={t("apiConfigSelector.editConfigurations")}
457489
onClick={handleEditClick}
458490
tooltip={false}
491+
aria-label="chat:edit"
459492
/>
460493
</div>
461494

462495
{/* Info icon and title on the right with matching spacing */}
463496
<div className="flex items-center gap-1 pr-1">
464497
{listApiConfigMeta.length > 6 && (
465498
<StandardTooltip content={t("prompts:apiConfiguration.select")}>
466-
<span className="codicon codicon-info text-xs text-vscode-descriptionForeground opacity-70 hover:opacity-100 cursor-help" />
499+
<span
500+
className="codicon codicon-info text-xs text-vscode-descriptionForeground opacity-70 hover:opacity-100 cursor-help"
501+
aria-label={t("prompts:apiConfiguration.select")}
502+
role="img"
503+
/>
467504
</StandardTooltip>
468505
)}
469506
<h4 className="m-0 font-medium text-sm text-vscode-descriptionForeground">

webview-ui/src/components/chat/__tests__/ApiConfigSelector.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ describe("ApiConfigSelector", () => {
346346

347347
// Find the settings button by its icon class within the popover content
348348
const popoverContent = screen.getByTestId("popover-content")
349-
const settingsButton = popoverContent.querySelector(".codicon-settings-gear") as HTMLElement
349+
const settingsButton = popoverContent.querySelector('[aria-label="chat:edit"]') as HTMLElement
350350
expect(settingsButton).toBeInTheDocument()
351351
fireEvent.click(settingsButton)
352352

0 commit comments

Comments
 (0)