Skip to content

Commit 5316377

Browse files
authored
chore(site): enable role-has-required-aria-props oxlint rule (#29188)
Enables the `role-has-required-aria-props` oxlint rule (jsx-a11y/useAriaPropsForRole), moving it out of the migration backlog and into the enforced a11y rules. Fixes the two resulting violations: - `ModelSelector.tsx`: wire `aria-controls` on the combobox trigger to a `useId`-generated id set on the listbox `CommandList`. - `Autocomplete.tsx`: add `aria-selected` to each `option`, driven by the highlighted index. > [!NOTE] > This PR was generated by Coder Agents on behalf of @jeremyruppel.
1 parent 42fcd33 commit 5316377

3 files changed

Lines changed: 6 additions & 2 deletions

File tree

site/.oxlintrc.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"tabindex-no-positive": "error",
2121
"img-redundant-alt": "error",
2222
"no-redundant-roles": "error",
23+
"role-has-required-aria-props": "error",
2324
"alt-text": "error",
2425
"anchor-has-content": "error",
2526
"aria-activedescendant-has-tabindex": "error",
@@ -199,7 +200,6 @@
199200
"no-autofocus": "off", // a11y/noAutofocus (large)
200201
"no-invalid-void-type": "off", // suspicious/noConfusingVoidType (large)
201202
"no-redeclare": "off", // suspicious/noRedeclare (medium: TS declaration merging)
202-
"role-has-required-aria-props": "off", // a11y/useAriaPropsForRole (medium)
203203
"no-irregular-whitespace": "off", // suspicious/noIrregularWhitespace (medium)
204204
"no-inferrable-types": "off", // style/noInferrableTypes (medium, coder error rule)
205205
"consistent-type-imports": "off", // style/useImportType (medium)

site/src/components/Autocomplete/Autocomplete.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ export function Autocomplete<TOption>({
344344
return (
345345
<CommandItem
346346
role="option"
347+
aria-selected={index === highlightedIndex}
347348
id={`${listboxId}-option-${index}`}
348349
key={optionValue}
349350
value={optionValue}

site/src/modules/aiModels/ModelSelector.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { cn } from "cn";
22
import { CheckIcon, InfoIcon } from "lucide-react";
3-
import { type FC, useState } from "react";
3+
import { type FC, useId, useState } from "react";
44
import { ChevronDownIcon } from "#/components/AnimatedIcons/ChevronDown";
55
import { Button } from "#/components/Button/Button";
66
import {
@@ -125,6 +125,7 @@ export const ModelSelector: FC<ModelSelectorProps> = ({
125125
// With an unset option the selector stays usable even when no model
126126
// options exist, so a saved override can still be switched back.
127127
const isDisabled = disabled || (options.length === 0 && !unsetLabel);
128+
const listboxId = useId();
128129
const query = search.trim().toLowerCase();
129130
const optionsByProvider = (() => {
130131
const grouped = new Map<string, ModelSelectorOption[]>();
@@ -158,6 +159,7 @@ export const ModelSelector: FC<ModelSelectorProps> = ({
158159
}
159160
aria-expanded={open}
160161
aria-haspopup="listbox"
162+
aria-controls={open ? listboxId : undefined}
161163
disabled={isDisabled}
162164
role="combobox"
163165
type="button"
@@ -218,6 +220,7 @@ export const ModelSelector: FC<ModelSelectorProps> = ({
218220
className="h-auto py-0 text-xs font-normal leading-[18px] text-content-primary placeholder:text-content-disabled"
219221
/>
220222
<CommandList
223+
id={listboxId}
221224
role="listbox"
222225
className={cn(
223226
"max-h-80 border-t-0",

0 commit comments

Comments
 (0)