Skip to content

Commit b034378

Browse files
committed
test(data-table): add tests for DataTableSearch
1 parent b4b0552 commit b034378

File tree

2 files changed

+310
-0
lines changed

2 files changed

+310
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<script lang="ts">
2+
import {
3+
Button,
4+
DataTable,
5+
Toolbar,
6+
ToolbarContent,
7+
ToolbarSearch,
8+
Pagination,
9+
} from "carbon-components-svelte";
10+
import type { ComponentProps } from "svelte";
11+
12+
export let value = "";
13+
export let persistent = false;
14+
export let shouldFilterRows: ComponentProps<ToolbarSearch>["shouldFilterRows"] = true;
15+
16+
const initialRows = Array.from({ length: 10 }).map((_, i) => ({
17+
id: i,
18+
name: "Load Balancer " + (i + 1),
19+
protocol: "HTTP",
20+
port: 3000 + i * 10,
21+
rule: i % 2 ? "Round robin" : "DNS delegation",
22+
}));
23+
24+
let rows = initialRows;
25+
let pageSize = 5;
26+
let page = 1;
27+
let filteredRowIds: number[] = [];
28+
let toggleRows = false;
29+
</script>
30+
31+
<Button
32+
on:click={() => {
33+
toggleRows = !toggleRows;
34+
if (toggleRows) {
35+
rows = Array.from({ length: 4 }).map((_, i) => ({
36+
id: i,
37+
name: "Server instance " + (i + 1),
38+
protocol: "HTTP",
39+
port: 3000 + i * 10,
40+
rule: i % 2 ? "Round!" : "DNS!",
41+
}));
42+
} else {
43+
rows = initialRows;
44+
}
45+
}}
46+
>
47+
Toggle rows
48+
</Button>
49+
50+
<DataTable
51+
headers={[
52+
{ key: "name", value: "Name" },
53+
{ key: "protocol", value: "Protocol" },
54+
{ key: "port", value: "Port" },
55+
{ key: "rule", value: "Rule" },
56+
]}
57+
{rows}
58+
{pageSize}
59+
{page}
60+
>
61+
<Toolbar>
62+
<ToolbarContent>
63+
<ToolbarSearch
64+
{persistent}
65+
{value}
66+
{shouldFilterRows}
67+
bind:filteredRowIds
68+
/>
69+
</ToolbarContent>
70+
</Toolbar>
71+
</DataTable>
72+
73+
<Pagination
74+
bind:pageSize
75+
bind:page
76+
totalItems={filteredRowIds.length}
77+
pageSizeInputDisabled
78+
/>
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import { render, screen } from "@testing-library/svelte";
2+
import { user } from "../setup-tests";
3+
import DataTableSearch from "./DataTableSearch.test.svelte";
4+
5+
describe("DataTableSearch", () => {
6+
beforeEach(() => {
7+
vi.clearAllMocks();
8+
});
9+
10+
// Remove first row since it's the header
11+
const getTableRows = () => screen.getAllByRole("row").slice(1);
12+
const getNextPageButton = () =>
13+
screen.getByRole("button", { name: "Next page" });
14+
const getPrevPageButton = () =>
15+
screen.getByRole("button", { name: "Previous page" });
16+
const allRowsRendered = () => {
17+
const tableRows = getTableRows();
18+
expect(tableRows).toHaveLength(5);
19+
tableRows.forEach((row) => {
20+
expect(row).toHaveTextContent(/Round robin|DNS delegation/);
21+
});
22+
23+
expect(screen.getByText("1–5 of 10 items")).toBeInTheDocument();
24+
expect(screen.getByText("of 2 pages")).toBeInTheDocument();
25+
expect(getNextPageButton()).toBeEnabled();
26+
expect(getPrevPageButton()).toBeDisabled();
27+
};
28+
29+
it("renders non-persistent search input", async () => {
30+
render(DataTableSearch);
31+
32+
const searchBar = screen.getByRole("search");
33+
expect(searchBar).not.toHaveClass(
34+
"bx--toolbar-search-container-persistent",
35+
);
36+
expect(searchBar).not.toHaveClass("bx--toolbar-search-container-active");
37+
38+
const searchInput = screen.getByRole("searchbox");
39+
expect(searchInput).toHaveValue("");
40+
expect(searchInput).not.toHaveFocus();
41+
allRowsRendered();
42+
43+
await user.type(searchInput, "dns");
44+
expect(searchInput).toHaveValue("dns");
45+
expect(searchInput).toHaveFocus();
46+
expect(searchBar).toHaveClass("bx--toolbar-search-container-active");
47+
expect(screen.getByText("1–5 of 5 items")).toBeInTheDocument();
48+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
49+
expect(getNextPageButton()).toBeDisabled();
50+
expect(getPrevPageButton()).toBeDisabled();
51+
52+
let tableRows = getTableRows();
53+
expect(tableRows).toHaveLength(5);
54+
tableRows.forEach((row) => {
55+
expect(row).toHaveTextContent("DNS");
56+
});
57+
58+
await user.keyboard("{Tab}{Enter}");
59+
expect(searchInput).toHaveValue("");
60+
expect(searchInput).toHaveFocus();
61+
allRowsRendered();
62+
63+
await user.keyboard("{Tab}");
64+
expect(searchBar).not.toHaveClass("bx--toolbar-search-container-active");
65+
});
66+
67+
it("renders persistent search input", async () => {
68+
render(DataTableSearch, {
69+
props: {
70+
persistent: true,
71+
},
72+
});
73+
74+
const searchBar = screen.getByRole("search");
75+
expect(searchBar).toHaveClass("bx--toolbar-search-container-persistent");
76+
77+
const searchInput = screen.getByRole("searchbox");
78+
expect(searchInput).toHaveValue("");
79+
expect(searchInput).not.toHaveFocus();
80+
allRowsRendered();
81+
82+
await user.type(searchInput, "dns");
83+
expect(searchInput).toHaveValue("dns");
84+
expect(searchInput).toHaveFocus();
85+
expect(screen.getByText("1–5 of 5 items")).toBeInTheDocument();
86+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
87+
expect(getNextPageButton()).toBeDisabled();
88+
expect(getPrevPageButton()).toBeDisabled();
89+
90+
let tableRows = getTableRows();
91+
expect(tableRows).toHaveLength(5);
92+
tableRows.forEach((row) => {
93+
expect(row).toHaveTextContent("DNS");
94+
});
95+
96+
await user.keyboard("{Tab}{Enter}");
97+
expect(searchInput).toHaveValue("");
98+
expect(searchInput).toHaveFocus();
99+
allRowsRendered();
100+
});
101+
102+
it("renders with initial search value in non-persistent search input", async () => {
103+
render(DataTableSearch, {
104+
props: {
105+
value: "round",
106+
},
107+
});
108+
109+
const searchInput = screen.getByRole("searchbox");
110+
expect(searchInput).toHaveValue("round");
111+
expect(searchInput).not.toHaveFocus();
112+
113+
// Search bar should be active.
114+
const searchBar = screen.getByRole("search");
115+
expect(searchBar).toHaveClass("bx--toolbar-search-container-active");
116+
117+
expect(screen.getByText("1–5 of 5 items")).toBeInTheDocument();
118+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
119+
expect(getNextPageButton()).toBeDisabled();
120+
expect(getPrevPageButton()).toBeDisabled();
121+
122+
let tableRows = getTableRows();
123+
expect(tableRows).toHaveLength(5);
124+
tableRows.forEach((row) => {
125+
expect(row).toHaveTextContent("Round");
126+
});
127+
128+
await user.click(
129+
screen.getByRole("button", { name: "Clear search input" }),
130+
);
131+
expect(searchInput).toHaveValue("");
132+
expect(searchInput).toHaveFocus();
133+
allRowsRendered();
134+
135+
await user.type(searchInput, "rr");
136+
137+
tableRows = getTableRows();
138+
expect(tableRows).toHaveLength(0);
139+
140+
expect(screen.getByText("0–0 of 0 items")).toBeInTheDocument();
141+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
142+
expect(getNextPageButton()).toBeDisabled();
143+
expect(getPrevPageButton()).toBeDisabled();
144+
145+
await user.keyboard("{Escape}");
146+
expect(searchInput).toHaveValue("");
147+
expect(searchInput).toHaveFocus();
148+
allRowsRendered();
149+
150+
await user.keyboard("{Tab}");
151+
expect(searchBar).not.toHaveClass("bx--toolbar-search-container-active");
152+
});
153+
154+
it("can filter with a custom filter function", async () => {
155+
render(DataTableSearch, {
156+
props: {
157+
shouldFilterRows: (row, value) => {
158+
return (
159+
/(6|8)$/.test(row.name) &&
160+
row.rule.toLowerCase().includes((value + "").toLowerCase())
161+
);
162+
},
163+
},
164+
});
165+
166+
allRowsRendered();
167+
168+
const searchInput = screen.getByRole("searchbox");
169+
await user.type(searchInput, "round");
170+
expect(searchInput).toHaveValue("round");
171+
expect(searchInput).toHaveFocus();
172+
expect(screen.getByText("1–2 of 2 items")).toBeInTheDocument();
173+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
174+
expect(getNextPageButton()).toBeDisabled();
175+
expect(getPrevPageButton()).toBeDisabled();
176+
177+
let tableRows = getTableRows();
178+
expect(tableRows).toHaveLength(2);
179+
tableRows.forEach((row) => {
180+
expect(row).toHaveTextContent("Round");
181+
expect(row).toHaveTextContent(/Load Balancer 6|Load Balancer 8/);
182+
});
183+
});
184+
185+
// TODO: fix reactivity
186+
it.skip("re-filters rows when toggled", async () => {
187+
render(DataTableSearch);
188+
189+
allRowsRendered();
190+
191+
const searchInput = screen.getByRole("searchbox");
192+
await user.type(searchInput, "round");
193+
expect(searchInput).toHaveValue("round");
194+
expect(searchInput).toHaveFocus();
195+
expect(screen.getByText("1–5 of 5 items")).toBeInTheDocument();
196+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
197+
expect(getNextPageButton()).toBeDisabled();
198+
expect(getPrevPageButton()).toBeDisabled();
199+
200+
const toggleButton = screen.getByRole("button", { name: "Toggle rows" });
201+
await user.click(toggleButton);
202+
203+
expect(searchInput).toHaveValue("round");
204+
expect(searchInput).not.toHaveFocus();
205+
206+
expect(screen.getByText("1–2 of 2 items")).toBeInTheDocument();
207+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
208+
expect(getNextPageButton()).toBeDisabled();
209+
expect(getPrevPageButton()).toBeDisabled();
210+
211+
let tableRows = getTableRows();
212+
expect(tableRows).toHaveLength(2);
213+
tableRows.forEach((row) => {
214+
expect(row).toHaveTextContent("Round!");
215+
});
216+
217+
await user.click(toggleButton);
218+
expect(searchInput).toHaveValue("round");
219+
expect(searchInput).not.toHaveFocus();
220+
expect(screen.getByText("1–5 of 5 items")).toBeInTheDocument();
221+
expect(screen.getByText("of 1 page")).toBeInTheDocument();
222+
expect(getNextPageButton()).toBeDisabled();
223+
expect(getPrevPageButton()).toBeDisabled();
224+
225+
await user.click(
226+
screen.getByRole("button", { name: "Clear search input" }),
227+
);
228+
expect(searchInput).toHaveValue("");
229+
expect(searchInput).toHaveFocus();
230+
allRowsRendered();
231+
});
232+
});

0 commit comments

Comments
 (0)