@agregio-solutions/design-system 1.90.1 → 1.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/design-system.cjs +9 -5
- package/dist/design-system.js +14 -6
- package/dist/packages/components/Accordion/doc.md +342 -0
- package/dist/packages/components/Badge/doc.md +192 -0
- package/dist/packages/components/Breadcrumbs/doc.md +332 -0
- package/dist/packages/components/Button/doc.md +425 -0
- package/dist/packages/components/Calendar/doc.md +465 -0
- package/dist/packages/components/ChartLegend/doc.md +151 -0
- package/dist/packages/components/ChartTooltip/doc.md +124 -0
- package/dist/packages/components/Checkbox/doc.md +329 -0
- package/dist/packages/components/CheckboxGroup/doc.md +242 -0
- package/dist/packages/components/Chip/doc.md +99 -0
- package/dist/packages/components/Combobox/Combobox.d.ts +8 -0
- package/dist/packages/components/Combobox/doc.md +680 -0
- package/dist/packages/components/DataTable/doc.md +1124 -0
- package/dist/packages/components/DatePicker/doc.md +579 -0
- package/dist/packages/components/DateRangePicker/doc.md +638 -0
- package/dist/packages/components/Drawer/doc.md +338 -0
- package/dist/packages/components/Dropdown/Dropdown.d.ts +4 -0
- package/dist/packages/components/Dropdown/doc.md +205 -0
- package/dist/packages/components/EmptyState/doc.md +101 -0
- package/dist/packages/components/FileUpload/doc.md +449 -0
- package/dist/packages/components/Filter/doc.md +196 -0
- package/dist/packages/components/Header/doc.md +373 -0
- package/dist/packages/components/I18nProvider/doc.md +187 -0
- package/dist/packages/components/Icon/doc.md +63 -0
- package/dist/packages/components/Label/doc.md +60 -0
- package/dist/packages/components/LinearProgressBar/doc.md +148 -0
- package/dist/packages/components/Link/doc.md +206 -0
- package/dist/packages/components/List/doc.md +481 -0
- package/dist/packages/components/Loader/doc.md +53 -0
- package/dist/packages/components/Menu/Menu.d.ts +5 -1
- package/dist/packages/components/Menu/doc.md +231 -0
- package/dist/packages/components/Message/doc.md +166 -0
- package/dist/packages/components/Modal/doc.md +289 -0
- package/dist/packages/components/Navigation/doc.md +992 -0
- package/dist/packages/components/NavigationItem/doc.md +167 -0
- package/dist/packages/components/NotificationCard/doc.md +206 -0
- package/dist/packages/components/Notifications/doc.md +240 -0
- package/dist/packages/components/NumberField/doc.md +582 -0
- package/dist/packages/components/PageLayout/doc.md +651 -0
- package/dist/packages/components/Pagination/doc.md +227 -0
- package/dist/packages/components/Popover/doc.md +245 -0
- package/dist/packages/components/Radio/doc.md +370 -0
- package/dist/packages/components/RouterProvider/doc.md +64 -0
- package/dist/packages/components/SearchBar/doc.md +504 -0
- package/dist/packages/components/SegmentedControl/doc.md +398 -0
- package/dist/packages/components/Select/Select.d.ts +4 -0
- package/dist/packages/components/Select/doc.md +1133 -0
- package/dist/packages/components/Skeleton/doc.md +129 -0
- package/dist/packages/components/Slider/doc.md +362 -0
- package/dist/packages/components/Stepper/doc.md +104 -0
- package/dist/packages/components/Switch/doc.md +296 -0
- package/dist/packages/components/Tabs/doc.md +295 -0
- package/dist/packages/components/Tag/doc.md +81 -0
- package/dist/packages/components/TextInput/doc.md +490 -0
- package/dist/packages/components/TimeField/doc.md +353 -0
- package/dist/packages/components/Timeline/doc.md +1046 -0
- package/dist/packages/components/Toaster/doc.md +263 -0
- package/dist/packages/components/ToggleButton/doc.md +108 -0
- package/dist/packages/components/ToggleButtonGroup/doc.md +307 -0
- package/dist/packages/components/Tooltip/doc.md +206 -0
- package/dist/packages/components/YearMonthPicker/YearMonthPicker.d.ts +8 -0
- package/dist/packages/components/YearMonthPicker/doc.md +638 -0
- package/dist/public_docs/components.md +68 -0
- package/dist/public_docs/index.md +30 -0
- package/dist/public_docs/tokens.md +121 -0
- package/package.json +3 -2
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# SearchBar
|
|
2
|
+
|
|
3
|
+
## Props
|
|
4
|
+
|
|
5
|
+
The complete Props documentation with JS doc for this component is available at this path:
|
|
6
|
+
|
|
7
|
+
node_modules/@agregio-solutions/design-system/dist/packages/components/SearchBar/SearchBar.d.ts
|
|
8
|
+
|
|
9
|
+
## Example usage
|
|
10
|
+
|
|
11
|
+
Here are the Storybook Stories.
|
|
12
|
+
|
|
13
|
+
Base stories:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
17
|
+
import { userEvent, within } from "storybook/test";
|
|
18
|
+
import SearchBar from "./SearchBar";
|
|
19
|
+
import { useState } from "react";
|
|
20
|
+
import Button from "@components/Button/Button";
|
|
21
|
+
import List, { Props as ListProps } from "../List/List";
|
|
22
|
+
import EmptyState from "../EmptyState/EmptyState";
|
|
23
|
+
import Tabs from "../Tabs/Tabs/Tabs";
|
|
24
|
+
import TabList from "../Tabs/TabList/TabList";
|
|
25
|
+
import Tab from "../Tabs/Tab/Tab";
|
|
26
|
+
import TabPanel from "../Tabs/TabPanel/TabPanel";
|
|
27
|
+
import { I18nProvider } from "react-aria-components";
|
|
28
|
+
|
|
29
|
+
const meta: Meta<typeof SearchBar> = {
|
|
30
|
+
component: SearchBar,
|
|
31
|
+
decorators: [
|
|
32
|
+
(Story) => (
|
|
33
|
+
<I18nProvider locale="en">
|
|
34
|
+
<Story />
|
|
35
|
+
</I18nProvider>
|
|
36
|
+
),
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
export default meta;
|
|
40
|
+
|
|
41
|
+
type Story = StoryObj<typeof meta>;
|
|
42
|
+
|
|
43
|
+
export const ExampleUsage: Story = {
|
|
44
|
+
render: () => {
|
|
45
|
+
// Data, typically fetched from a database
|
|
46
|
+
const data = [
|
|
47
|
+
{
|
|
48
|
+
id: "id-1",
|
|
49
|
+
fullname: "Alice Johnson",
|
|
50
|
+
position: "Chief Executive Officer",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "id-2",
|
|
54
|
+
fullname: "Bob Smith",
|
|
55
|
+
position: "Lead Software Engineer",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "id-3",
|
|
59
|
+
fullname: "Carol Lee",
|
|
60
|
+
position: "Product Manager",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "id-4",
|
|
64
|
+
fullname: "David Kim",
|
|
65
|
+
position: "UX Designer",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "id-5",
|
|
69
|
+
fullname: "Eva Brown",
|
|
70
|
+
position: "HR Specialist",
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const Parent = () => {
|
|
75
|
+
const [searchValue, setSearchValue] = useState("");
|
|
76
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
77
|
+
|
|
78
|
+
// Manually filter the data based on the search value (have to be done on your side, not handled by the component)
|
|
79
|
+
const filteredData = data.filter((item) => {
|
|
80
|
+
if (!searchValue) return false; // Case where we want to show nothing if there is no search value
|
|
81
|
+
const searchValueLower = searchValue.toLowerCase();
|
|
82
|
+
return (
|
|
83
|
+
item.fullname.toLowerCase().includes(searchValueLower) ||
|
|
84
|
+
item.position.toLowerCase().includes(searchValueLower) ||
|
|
85
|
+
item.id.toLowerCase() === searchValueLower // It is recommended to use an exact match for the id, as it is used to identify the item in the list.
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div>
|
|
91
|
+
<Button onClick={() => setIsOpen(true)} text="Open" />
|
|
92
|
+
|
|
93
|
+
<SearchBar
|
|
94
|
+
inputProps={{
|
|
95
|
+
value: searchValue,
|
|
96
|
+
onChange: setSearchValue,
|
|
97
|
+
placeholder: "Search in the application",
|
|
98
|
+
}}
|
|
99
|
+
onClear={() => setSearchValue("")}
|
|
100
|
+
isOpen={isOpen}
|
|
101
|
+
onOpenChange={(newIsOpen) => {
|
|
102
|
+
setSearchValue(""); // Reset the search value when the modal is closed. This is not built-in, so you have the choice depending on your use case.
|
|
103
|
+
setIsOpen(newIsOpen);
|
|
104
|
+
}}
|
|
105
|
+
resultsCount={filteredData.length}
|
|
106
|
+
>
|
|
107
|
+
{!!searchValue && ( // Only render the list if there is a search value
|
|
108
|
+
<List
|
|
109
|
+
aria-label="Search results"
|
|
110
|
+
sections={[
|
|
111
|
+
{
|
|
112
|
+
id: "people",
|
|
113
|
+
items: filteredData.map((item) => ({
|
|
114
|
+
id: item.id,
|
|
115
|
+
title: item.fullname,
|
|
116
|
+
subtitle: item.position,
|
|
117
|
+
href: `https://www.google.com?q=${item.fullname}`,
|
|
118
|
+
target: "_blank",
|
|
119
|
+
})),
|
|
120
|
+
},
|
|
121
|
+
]}
|
|
122
|
+
renderEmptyState={() => (
|
|
123
|
+
<EmptyState
|
|
124
|
+
title="No results"
|
|
125
|
+
description="No results found"
|
|
126
|
+
icon={"help_outline"}
|
|
127
|
+
>
|
|
128
|
+
<Button
|
|
129
|
+
text="Clear search"
|
|
130
|
+
onClick={() => setSearchValue("")}
|
|
131
|
+
mode="secondary"
|
|
132
|
+
/>
|
|
133
|
+
<Button text="Close" onClick={() => setIsOpen(false)} />
|
|
134
|
+
</EmptyState>
|
|
135
|
+
)}
|
|
136
|
+
/>
|
|
137
|
+
)}
|
|
138
|
+
</SearchBar>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
return <Parent />;
|
|
143
|
+
},
|
|
144
|
+
play: async ({ canvasElement }) => {
|
|
145
|
+
const canvas = within(canvasElement.ownerDocument.body);
|
|
146
|
+
const user = userEvent.setup();
|
|
147
|
+
await user.click(canvas.getByText("Open"));
|
|
148
|
+
await canvas.findByPlaceholderText("Search in the application");
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const WithTabs: Story = {
|
|
153
|
+
render: () => {
|
|
154
|
+
// Data, typically fetched from a database
|
|
155
|
+
const users = [
|
|
156
|
+
{
|
|
157
|
+
id: "user-id-1",
|
|
158
|
+
fullname: "Alice Johnson",
|
|
159
|
+
position: "Chief Executive Officer",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "user-id-2",
|
|
163
|
+
fullname: "Bob Smith",
|
|
164
|
+
position: "Lead Software Engineer",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "user-id-3",
|
|
168
|
+
fullname: "Carol Lee",
|
|
169
|
+
position: "Product Manager",
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
const companies = [
|
|
174
|
+
{
|
|
175
|
+
id: "company-id-1",
|
|
176
|
+
name: "Agregio Solutions",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "company-id-2",
|
|
180
|
+
name: "EDF Store and Forecast",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "company-id-3",
|
|
184
|
+
name: "Dalkia",
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
const Empty = ({
|
|
189
|
+
setSearchValue,
|
|
190
|
+
setIsOpen,
|
|
191
|
+
}: {
|
|
192
|
+
setSearchValue: (value: string) => void;
|
|
193
|
+
setIsOpen: (value: boolean) => void;
|
|
194
|
+
}) => {
|
|
195
|
+
return (
|
|
196
|
+
<EmptyState
|
|
197
|
+
title="No results"
|
|
198
|
+
description="No results found"
|
|
199
|
+
icon={"help_outline"}
|
|
200
|
+
>
|
|
201
|
+
<Button
|
|
202
|
+
text="Clear search"
|
|
203
|
+
onClick={() => setSearchValue("")}
|
|
204
|
+
mode="secondary"
|
|
205
|
+
/>
|
|
206
|
+
<Button text="Close" onClick={() => setIsOpen(false)} />
|
|
207
|
+
</EmptyState>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const Parent = () => {
|
|
212
|
+
const [searchValue, setSearchValue] = useState("");
|
|
213
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
214
|
+
|
|
215
|
+
// Manually filter the data based on the search value (have to be done on your side, not handled by the component)
|
|
216
|
+
const filteredUsers = users.filter((user) => {
|
|
217
|
+
// Here we do not return an empty array if there is no search value, this is an example of how you can show all the data by default on SearchBar opening.
|
|
218
|
+
const searchValueLower = searchValue.toLowerCase();
|
|
219
|
+
return (
|
|
220
|
+
user.fullname.toLowerCase().includes(searchValueLower) ||
|
|
221
|
+
user.position.toLowerCase().includes(searchValueLower) ||
|
|
222
|
+
user.id.toLowerCase() === searchValueLower
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const filteredCompanies = companies.filter((company) => {
|
|
227
|
+
const searchValueLower = searchValue.toLowerCase();
|
|
228
|
+
return (
|
|
229
|
+
company.name.toLowerCase().includes(searchValueLower) ||
|
|
230
|
+
company.id.toLowerCase() === searchValueLower
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const sections: ListProps["sections"] = [
|
|
235
|
+
{
|
|
236
|
+
id: "users",
|
|
237
|
+
title: "Users",
|
|
238
|
+
items: filteredUsers.map((user) => ({
|
|
239
|
+
id: user.id,
|
|
240
|
+
title: user.fullname,
|
|
241
|
+
subtitle: user.position,
|
|
242
|
+
href: `https://www.google.com?q=${user.fullname}`,
|
|
243
|
+
target: "_blank",
|
|
244
|
+
})),
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "companies",
|
|
248
|
+
title: "Companies",
|
|
249
|
+
items: filteredCompanies.map((company) => ({
|
|
250
|
+
id: company.id,
|
|
251
|
+
title: company.name,
|
|
252
|
+
icon: "business",
|
|
253
|
+
})),
|
|
254
|
+
},
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div>
|
|
259
|
+
<Button onClick={() => setIsOpen(true)} text="Open" />
|
|
260
|
+
|
|
261
|
+
<SearchBar
|
|
262
|
+
inputProps={{
|
|
263
|
+
value: searchValue,
|
|
264
|
+
onChange: setSearchValue,
|
|
265
|
+
placeholder: "Search in the application",
|
|
266
|
+
}}
|
|
267
|
+
onClear={() => setSearchValue("")}
|
|
268
|
+
isOpen={isOpen}
|
|
269
|
+
onOpenChange={(newIsOpen) => {
|
|
270
|
+
setSearchValue(""); // Reset the search value when the modal is closed. This is not built-in, so you have the choice depending on your use case.
|
|
271
|
+
setIsOpen(newIsOpen);
|
|
272
|
+
}}
|
|
273
|
+
resultsCount={filteredUsers.length + filteredCompanies.length}
|
|
274
|
+
>
|
|
275
|
+
<Tabs>
|
|
276
|
+
<TabList>
|
|
277
|
+
<Tab id="all">All</Tab>
|
|
278
|
+
<Tab id="users">Users</Tab>
|
|
279
|
+
<Tab id="companies">Companies</Tab>
|
|
280
|
+
</TabList>
|
|
281
|
+
<TabPanel id="all">
|
|
282
|
+
<List
|
|
283
|
+
aria-label="All search results"
|
|
284
|
+
sections={sections}
|
|
285
|
+
renderEmptyState={() => (
|
|
286
|
+
<Empty
|
|
287
|
+
setSearchValue={setSearchValue}
|
|
288
|
+
setIsOpen={setIsOpen}
|
|
289
|
+
/>
|
|
290
|
+
)}
|
|
291
|
+
/>
|
|
292
|
+
</TabPanel>
|
|
293
|
+
<TabPanel id="users">
|
|
294
|
+
<List
|
|
295
|
+
aria-label="Users search results"
|
|
296
|
+
sections={sections.filter(
|
|
297
|
+
(section) => section.id === "users",
|
|
298
|
+
)}
|
|
299
|
+
renderEmptyState={() => (
|
|
300
|
+
<Empty
|
|
301
|
+
setSearchValue={setSearchValue}
|
|
302
|
+
setIsOpen={setIsOpen}
|
|
303
|
+
/>
|
|
304
|
+
)}
|
|
305
|
+
/>
|
|
306
|
+
</TabPanel>
|
|
307
|
+
<TabPanel id="companies">
|
|
308
|
+
<List
|
|
309
|
+
aria-label="Companies search results"
|
|
310
|
+
sections={sections.filter(
|
|
311
|
+
(section) => section.id === "companies",
|
|
312
|
+
)}
|
|
313
|
+
renderEmptyState={() => (
|
|
314
|
+
<Empty
|
|
315
|
+
setSearchValue={setSearchValue}
|
|
316
|
+
setIsOpen={setIsOpen}
|
|
317
|
+
/>
|
|
318
|
+
)}
|
|
319
|
+
/>
|
|
320
|
+
</TabPanel>
|
|
321
|
+
</Tabs>
|
|
322
|
+
</SearchBar>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
return <Parent />;
|
|
327
|
+
},
|
|
328
|
+
play: async ({ canvasElement }) => {
|
|
329
|
+
const canvas = within(canvasElement.ownerDocument.body);
|
|
330
|
+
const user = userEvent.setup();
|
|
331
|
+
await user.click(canvas.getByText("Open"));
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export const NoResultsEmptyState: Story = {
|
|
336
|
+
render: () => {
|
|
337
|
+
const Parent = () => {
|
|
338
|
+
const [searchValue, setSearchValue] = useState("Some search value");
|
|
339
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<div>
|
|
343
|
+
<Button onClick={() => setIsOpen(true)} text="Open" />
|
|
344
|
+
|
|
345
|
+
<SearchBar
|
|
346
|
+
inputProps={{
|
|
347
|
+
value: searchValue,
|
|
348
|
+
onChange: setSearchValue,
|
|
349
|
+
placeholder: "Search in the application",
|
|
350
|
+
}}
|
|
351
|
+
onClear={() => setSearchValue("")}
|
|
352
|
+
isOpen={isOpen}
|
|
353
|
+
onOpenChange={(newIsOpen) => {
|
|
354
|
+
setSearchValue(""); // Reset the search value when the modal is closed. This is not built-in, so you have the choice depending on your use case.
|
|
355
|
+
setIsOpen(newIsOpen);
|
|
356
|
+
}}
|
|
357
|
+
resultsCount={0}
|
|
358
|
+
>
|
|
359
|
+
{/* Here we want to display all the data by default */}
|
|
360
|
+
<List
|
|
361
|
+
aria-label="Search results"
|
|
362
|
+
sections={[]}
|
|
363
|
+
renderEmptyState={() => (
|
|
364
|
+
<EmptyState
|
|
365
|
+
title="No results"
|
|
366
|
+
description="No results found"
|
|
367
|
+
icon={"help_outline"}
|
|
368
|
+
>
|
|
369
|
+
<Button
|
|
370
|
+
text="Clear search"
|
|
371
|
+
onClick={() => setSearchValue("")}
|
|
372
|
+
mode="secondary"
|
|
373
|
+
/>
|
|
374
|
+
<Button text="Close" onClick={() => setIsOpen(false)} />
|
|
375
|
+
</EmptyState>
|
|
376
|
+
)}
|
|
377
|
+
/>
|
|
378
|
+
</SearchBar>
|
|
379
|
+
</div>
|
|
380
|
+
);
|
|
381
|
+
};
|
|
382
|
+
return <Parent />;
|
|
383
|
+
},
|
|
384
|
+
play: async ({ canvasElement }) => {
|
|
385
|
+
const canvas = within(canvasElement.ownerDocument.body);
|
|
386
|
+
const user = userEvent.setup();
|
|
387
|
+
await user.click(canvas.getByText("Open"));
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## How to test this component
|
|
393
|
+
|
|
394
|
+
Here are some more advanced stories with more testing coverage and examples that you can read to understand how to test this component.
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
398
|
+
import { expect, userEvent, within } from "storybook/test";
|
|
399
|
+
import SearchBar from "../SearchBar";
|
|
400
|
+
import * as SearchBarStories from "../SearchBar.stories";
|
|
401
|
+
|
|
402
|
+
const meta: Meta<typeof SearchBar> = {
|
|
403
|
+
...SearchBarStories.default,
|
|
404
|
+
component: SearchBar,
|
|
405
|
+
parameters: {
|
|
406
|
+
...SearchBarStories.default.parameters,
|
|
407
|
+
chromatic: { disableSnapshot: true },
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
export default meta;
|
|
411
|
+
|
|
412
|
+
type Story = StoryObj<typeof meta>;
|
|
413
|
+
|
|
414
|
+
export const ShouldShowTheFilteredItems: Story = {
|
|
415
|
+
render: SearchBarStories.ExampleUsage.render,
|
|
416
|
+
play: async ({ canvasElement }) => {
|
|
417
|
+
const canvas = within(canvasElement.ownerDocument.body);
|
|
418
|
+
const user = userEvent.setup({ delay: 25 });
|
|
419
|
+
await user.click(canvas.getByText("Open"));
|
|
420
|
+
|
|
421
|
+
const input = await canvas.findByPlaceholderText(
|
|
422
|
+
"Search in the application",
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
// Check that items are not displayed yet
|
|
426
|
+
await expect(canvas.queryByText(/result/i)).not.toBeInTheDocument();
|
|
427
|
+
await expect(canvas.queryByText("Alice Johnson")).not.toBeInTheDocument();
|
|
428
|
+
await expect(canvas.queryByText("Bob Smith")).not.toBeInTheDocument();
|
|
429
|
+
await expect(canvas.queryByText("Carol Lee")).not.toBeInTheDocument();
|
|
430
|
+
await expect(canvas.queryByText("David Kim")).not.toBeInTheDocument();
|
|
431
|
+
await expect(canvas.queryByText("Eva Brown")).not.toBeInTheDocument();
|
|
432
|
+
|
|
433
|
+
// Check that the legend is not displayed
|
|
434
|
+
await expect(canvas.queryByText("Navigation")).not.toBeInTheDocument();
|
|
435
|
+
await expect(canvas.queryByText("Choose")).not.toBeInTheDocument();
|
|
436
|
+
await expect(canvas.queryByText("Close")).not.toBeInTheDocument();
|
|
437
|
+
|
|
438
|
+
// Type "Alice" in the search bar
|
|
439
|
+
await user.type(input, "Alice");
|
|
440
|
+
|
|
441
|
+
// Check that items are displayed
|
|
442
|
+
await canvas.findByText("1 result");
|
|
443
|
+
await canvas.findByText("Alice Johnson");
|
|
444
|
+
await expect(canvas.queryByText("Bob Smith")).not.toBeInTheDocument();
|
|
445
|
+
await expect(canvas.queryByText("Carol Lee")).not.toBeInTheDocument();
|
|
446
|
+
await expect(canvas.queryByText("David Kim")).not.toBeInTheDocument();
|
|
447
|
+
await expect(canvas.queryByText("Eva Brown")).not.toBeInTheDocument();
|
|
448
|
+
await canvas.findByText("Navigation");
|
|
449
|
+
await canvas.findByText("Choose");
|
|
450
|
+
await canvas.findByText("Close");
|
|
451
|
+
|
|
452
|
+
// Clear the search bar
|
|
453
|
+
await user.click(canvas.getByLabelText("Clear"));
|
|
454
|
+
await expect(input).toHaveValue("");
|
|
455
|
+
await expect(canvas.queryByText(/result/i)).not.toBeInTheDocument();
|
|
456
|
+
await expect(canvas.queryByText("Alice Johnson")).not.toBeInTheDocument();
|
|
457
|
+
await expect(canvas.queryByText("Bob Smith")).not.toBeInTheDocument();
|
|
458
|
+
await expect(canvas.queryByText("Carol Lee")).not.toBeInTheDocument();
|
|
459
|
+
await expect(canvas.queryByText("David Kim")).not.toBeInTheDocument();
|
|
460
|
+
await expect(canvas.queryByText("Eva Brown")).not.toBeInTheDocument();
|
|
461
|
+
await expect(canvas.queryByText("Navigation")).not.toBeInTheDocument();
|
|
462
|
+
await expect(canvas.queryByText("Choose")).not.toBeInTheDocument();
|
|
463
|
+
await expect(canvas.queryByText("Close")).not.toBeInTheDocument();
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Developer notes
|
|
469
|
+
|
|
470
|
+
Here are the notes available for the developer on the built Storybook, you can read them to understand the component and how to use it.
|
|
471
|
+
|
|
472
|
+
```mdx
|
|
473
|
+
import { Meta, Controls, Source, Canvas } from "@storybook/addon-docs/blocks";
|
|
474
|
+
|
|
475
|
+
import * as SearchBar from "./SearchBar.stories";
|
|
476
|
+
|
|
477
|
+
<Meta of={SearchBar} />
|
|
478
|
+
|
|
479
|
+
# SearchBar
|
|
480
|
+
|
|
481
|
+
<Canvas of={SearchBar.ExampleUsage} />
|
|
482
|
+
<Controls of={SearchBar.ExampleUsage} />
|
|
483
|
+
|
|
484
|
+
## Basic example usage
|
|
485
|
+
|
|
486
|
+
Below you can find a basic example usage of the SearchBar component.
|
|
487
|
+
You will find that there is a lot of customization and flexibility, some UX aspects can totally be decided on your side.
|
|
488
|
+
|
|
489
|
+
For example, here we choose to not display items if there is no search value in the input. Feel free to change this behavior to your needs.
|
|
490
|
+
|
|
491
|
+
Also, the filtering is done on your side, so you can decide all the aspects of this feature (like which keys to filter on, how to filter them, add fuzzy search, etc.).
|
|
492
|
+
Even if there is no example usage for this use case, it should be really easy to implement a back-end filtering as well, based on the search value.
|
|
493
|
+
|
|
494
|
+
Click on "Show code" to see the code.
|
|
495
|
+
|
|
496
|
+
<Canvas of={SearchBar.ExampleUsage} />
|
|
497
|
+
|
|
498
|
+
### With tabs example usage
|
|
499
|
+
|
|
500
|
+
Here is another example where we use tabs to display different types of items.
|
|
501
|
+
Some of the UX decisions voluntary differ from the basic example usage, so you can see how easy it is to customize the component to your needs (i.e. here we display results even if there is no search value in the input).
|
|
502
|
+
|
|
503
|
+
<Canvas of={SearchBar.WithTabs} />
|
|
504
|
+
```
|