Fix seasons page dropdown missing S0, only wrap Autocomplete when needed

This commit is contained in:
Kalle
2026-06-04 20:42:02 +03:00
parent 8afcd8e137
commit 5766bd5373
2 changed files with 107 additions and 33 deletions

View File

@@ -0,0 +1,61 @@
import { describe, expect, test } from "vitest";
import { render } from "vitest-browser-react";
import {
SendouSelect,
SendouSelectItem,
SendouSelectItemSection,
} from "./Select";
const SECTIONS = [
{
heading: "2023",
key: "2023",
items: [
{ id: 2, name: "Season 2" },
{ id: 1, name: "Season 1" },
{ id: 0, name: "Season 0" },
],
},
];
function GroupedSelect(props: { search?: { placeholder?: string } }) {
return (
<SendouSelect label="Season" items={SECTIONS} search={props.search}>
{({ heading, items, key }: (typeof SECTIONS)[number]) => (
<SendouSelectItemSection heading={heading} key={key}>
{items.map((item) => (
<SendouSelectItem key={item.id} id={item.id}>
{item.name}
</SendouSelectItem>
))}
</SendouSelectItemSection>
)}
</SendouSelect>
);
}
describe("SendouSelect", () => {
test("renders an item with a falsy (0) key when there is no search", async () => {
const screen = await render(<GroupedSelect />);
await screen.getByRole("button").click();
await expect
.element(screen.getByRole("option", { name: "Season 0" }))
.toBeVisible();
});
test("filters items when search is enabled", async () => {
const screen = await render(<GroupedSelect search={{}} />);
await screen.getByRole("button").click();
await screen.getByRole("searchbox").fill("Season 1");
await expect
.element(screen.getByRole("option", { name: "Season 1" }))
.toBeVisible();
await expect
.element(screen.getByRole("option", { name: "Season 2" }))
.not.toBeInTheDocument();
});
});

View File

@@ -93,6 +93,25 @@ export function SendouSelect<T extends object>({
}
};
const listBox = (
<Virtualizer layout={ListLayout} layoutOptions={{ rowHeight: 33 }}>
<ListBox
items={items}
className={clsx(styles.listBox, "scrollbar")}
renderEmptyState={() => (
<div className={styles.noResults}>{t("common:noResults")}</div>
)}
>
{children}
</ListBox>
</Virtualizer>
);
// The Autocomplete wrapper filters the collection, but its filtering drops
// items with a falsy key (e.g. `0`). When there is nothing to filter we skip
// it entirely so such items always render.
const filterable = !!search || isControlled || !!filter;
return (
<Select
{...props}
@@ -109,39 +128,33 @@ export function SendouSelect<T extends object>({
{clearable ? <SelectClearButton /> : null}
<SendouBottomTexts bottomText={bottomText} errorText={errorText} />
<Popover className={clsx(popoverClassName, styles.popover)}>
<Autocomplete
filter={filter ? filter : isControlled ? undefined : contains}
inputValue={searchInputValue}
onInputChange={onSearchInputChange}
>
{search ? (
<SearchField
aria-label="Search"
autoFocus
className={styles.searchField}
>
<Search aria-hidden className={styles.icon} />
<Input
placeholder={search.placeholder}
className={clsx(styles.searchInput, "in-container")}
/>
<Button className={styles.searchClearButton}>
<X className={styles.icon} />
</Button>
</SearchField>
) : null}
<Virtualizer layout={ListLayout} layoutOptions={{ rowHeight: 33 }}>
<ListBox
items={items}
className={clsx(styles.listBox, "scrollbar")}
renderEmptyState={() => (
<div className={styles.noResults}>{t("common:noResults")}</div>
)}
>
{children}
</ListBox>
</Virtualizer>
</Autocomplete>
{filterable ? (
<Autocomplete
filter={filter ? filter : isControlled ? undefined : contains}
inputValue={searchInputValue}
onInputChange={onSearchInputChange}
>
{search ? (
<SearchField
aria-label="Search"
autoFocus
className={styles.searchField}
>
<Search aria-hidden className={styles.icon} />
<Input
placeholder={search.placeholder}
className={clsx(styles.searchInput, "in-container")}
/>
<Button className={styles.searchClearButton}>
<X className={styles.icon} />
</Button>
</SearchField>
) : null}
{listBox}
</Autocomplete>
) : (
listBox
)}
</Popover>
</Select>
);