Fleet UI: Dropdown button options don't stay highlighted (#46740)

This commit is contained in:
RachelElysia
2026-06-03 15:16:37 -04:00
committed by GitHub
parent 6635bb7b27
commit 3663475263
3 changed files with 56 additions and 10 deletions
@@ -127,4 +127,36 @@ describe("DropdownWrapper Component", () => {
expect(screen.getByText(buttonText)).toBeInTheDocument();
expect(screen.queryByText(/option 2/i)).not.toBeInTheDocument();
});
// Regression test for #45853 — react-select tracks the last click as
// selectValue and re-focuses it on reopen, leaving the previously-clicked
// option visually highlighted. Button variant overrides this by forcing
// value to null.
test("button variant does not mark a previously-clicked option as selected on reopen", async () => {
const buttonText = "Actions";
render(
<DropdownWrapper
options={sampleOptions}
onChange={mockOnChange}
name="test-dropdown"
placeholder={buttonText}
variant="button"
/>
);
// Open, click Option 2, then reopen
await userEvent.click(screen.getByText(buttonText));
await userEvent.click(screen.getByText(/option 2/i));
await userEvent.click(screen.getByText(buttonText));
// On reopen, no menu option should carry the selected state.
// react-select adds `--is-selected` to the option matching `value`; the
// fix forces value to null for button variant so this class never appears.
const options = document.querySelectorAll(".react-select__option");
expect(options.length).toBeGreaterThan(0);
options.forEach((option) =>
expect(option.className).not.toMatch(/--is-selected/)
);
});
});
@@ -201,16 +201,12 @@ export const generateCustomDropdownStyles = (
stroke: COLORS["ui-fleet-black-75-over"],
},
},
...(state.isFocused && {
backgroundColor: COLORS["ui-fleet-black-5"],
boxShadow: "none",
".dropdown-wrapper__placeholder": {
color: COLORS["ui-fleet-black-75-down"],
},
".dropdown-wrapper__indicator path": {
stroke: COLORS["ui-fleet-black-75-down"],
},
}),
// Note: state.isFocused is intentionally not used as a highlight
// trigger here. react-select's internal input retains focus after
// the menu closes (outside click, post-modal-close, etc.), so
// styling on it would leave the trigger visually highlighted
// indefinitely (#45853). Hover + menuIsOpen cover the meaningful
// interactive states for an action button.
...(state.menuIsOpen && {
backgroundColor: COLORS["ui-fleet-black-5"],
".dropdown-wrapper__placeholder": {
@@ -459,6 +455,7 @@ const DropdownWrapper = ({
}: IDropdownWrapper) => {
const wrapperClassNames = classnames(baseClass, className, {
[`${baseClass}__table-filter`]: variant === "table-filter",
[`${baseClass}__button`]: variant === "button",
[`${wrapperClassname}`]: !!wrapperClassname,
});
@@ -477,6 +474,13 @@ const DropdownWrapper = ({
// Ability to handle value of type string or CustomOptionType
const getCurrentValue = () => {
// Button variant is a fire-and-forget action menu (selection isn't displayed
// via `controlShouldRenderValue`). Forcing null prevents react-select from
// tracking the last click as the selected value and re-focusing it on reopen
// (#45853).
if (variant === "button") {
return null;
}
if (typeof value === "string") {
return options.find((option) => option.value === value) || null;
}
@@ -20,4 +20,14 @@
height: 36px;
}
}
// Button-variant: background while focused so keyboard tabbing is
// visible. Mirrors the menuIsOpen background applied via emotion in
// DropdownWrapper.tsx. Inputs always match :focus-visible, so this
// also applies on click that's fine since it matches the open state.
&__button {
.react-select__control:has(input:focus-visible) {
background-color: $ui-fleet-black-5;
}
}
}