Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 39 additions & 22 deletions web/app/components/ProfileCombobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
const listboxId = `listbox-${id}`;

const selectedOption = options.find((o) => o.id === value);
const activeOption = open && activeIndex >= 0 ? options[activeIndex] : undefined;

Check warning on line 29 in web/app/components/ProfileCombobox.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

web/app/components/ProfileCombobox.tsx#L29

Generic Object Injection Sink
const activeDescendantId = activeOption ? `${listboxId}-option-${activeOption.id}` : undefined;

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
Expand Down Expand Up @@ -103,13 +105,15 @@
}
};

// Focus the active option button when activeIndex changes and list is open
// Scroll active option into view when activeIndex changes
useEffect(() => {
if (open && activeIndex >= 0 && listboxRef.current) {
const buttons = listboxRef.current.querySelectorAll("button");
(buttons[activeIndex] as HTMLElement)?.focus();
if (open && activeIndex >= 0 && listboxRef.current && activeOption) {
const activeEl = document.getElementById(`${listboxId}-option-${activeOption.id}`);
if (activeEl) {
activeEl.scrollIntoView({ block: "nearest" });
}
}
}, [activeIndex, open]);
}, [activeIndex, open, activeOption, listboxId]);

return (
<div className="relative" ref={containerRef}>
Expand All @@ -122,6 +126,7 @@
aria-haspopup="listbox"
aria-expanded={open}
aria-controls={open ? listboxId : undefined}
aria-activedescendant={activeDescendantId}
aria-label="Change search profile"
title="Change search profile"
>
Expand All @@ -137,23 +142,35 @@
role="listbox"
aria-label="Search profiles"
>
{options.map((option, index) => (
<button
key={option.id}
id={`${listboxId}-option-${option.id}`}
onClick={() => handleSelect(option.id)}
onKeyDown={handleKeyDown}
className={`w-full px-3 py-2 text-left hover:bg-accent hover:text-background transition-colors flex flex-col focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus:bg-accent focus:text-background ${
option.id === value ? "bg-[#222] text-accent" : "text-foreground"
} ${index === activeIndex ? "ring-inset ring-2 ring-accent" : ""}`}
role="option"
aria-selected={option.id === value}
tabIndex={-1}
>
<span className="text-[12px] font-bold">{option.label}</span>
{option.description && <div className="text-[10px] text-text-muted">{option.description}</div>}
</button>
))}
{options.map((option, index) => {
const isSelected = option.id === value;
const isActive = index === activeIndex;

return (
<div

Check warning on line 150 in web/app/components/ProfileCombobox.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

web/app/components/ProfileCombobox.tsx#L150

Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Check warning on line 150 in web/app/components/ProfileCombobox.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

web/app/components/ProfileCombobox.tsx#L150

The HTML element with the interactive role "option" is not focusable.
key={option.id}
id={`${listboxId}-option-${option.id}`}
onClick={() => handleSelect(option.id)}
onMouseEnter={() => setActiveIndex(index)}

Check warning on line 154 in web/app/components/ProfileCombobox.tsx

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

web/app/components/ProfileCombobox.tsx#L154

Returning a void expression from an arrow function shorthand is forbidden. Please add braces to the arrow function.
className={`w-full px-3 py-2 text-left transition-colors flex flex-col cursor-pointer ${
isActive
? "bg-accent text-background font-bold"
: isSelected
? "bg-[#222] text-accent"
: "text-foreground hover:bg-[#222]"
}`}
role="option"
aria-selected={isSelected}
>
<span className="text-[12px] font-bold">{option.label}</span>
{option.description && (
<div className={`text-[10px] ${isActive ? "text-background/80" : "text-text-muted"}`}>
{option.description}
</div>
)}
</div>
);
})}
</div>
)}
</div>
Expand Down
Loading
Loading