mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-01 07:37:55 -05:00
Fix seasons page dropdown missing S0, only wrap Autocomplete when needed
This commit is contained in:
61
app/components/elements/Select.browser.test.tsx
Normal file
61
app/components/elements/Select.browser.test.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user