Skip to content

Commit 4099f22

Browse files
authored
Merge pull request #8157 from marmelab/Filterlist-doc
[Doc] Add documentation for the <SavedQueriesList> component
2 parents 21da316 + 1817ed4 commit 4099f22

File tree

6 files changed

+265
-118
lines changed

6 files changed

+265
-118
lines changed

docs/FilterList.md

+45-111
Original file line numberDiff line numberDiff line change
@@ -5,130 +5,64 @@ title: "The FilterList Component"
55

66
# `<FilterList>`
77

8+
An alternative UI to the Filter Button/Form Combo is the FilterList Sidebar. Similar to what users usually see on e-commerce websites, it's a panel with many simple filters that can be enabled and combined using the mouse.
9+
810
![Filter Sidebar](./img/filter-sidebar.gif)
911

10-
An alternative UI to the Filter Button/Form Combo is the FilterList Sidebar. Similar to what users usually see on e-commerce websites, it's a panel with many simple filters that can be enabled and combined using the mouse. The user experience is better than the Button/Form Combo, because the filter values are explicit, and it doesn't require typing anything in a form. But it's a bit less powerful, as only filters with a finite set of values (or intervals) can be used in the `<FilterList>`.
12+
The user experience is better than the Button/Form Combo, because the filter values are explicit, and it doesn't require typing anything in a form. But it's a bit less powerful, as only filters with a finite set of values (or intervals) can be used in the `<FilterList>`.
1113

1214
## Usage
1315

14-
The `<FilterList>` component expects a list of `<FilterListItem>` as children. Each `<FilterListItem>` defines a filter `label` and a `value`, which is merged with the current filter value when enabled by the user. Here is an example usage for a list of customers:
16+
Use the `<FilterList>` component in a sidebar for the `<List>` view. It expects a list of `<FilterListItem>` as children. Each `<FilterListItem>` defines a filter `label` and a `value`, which is merged with the current filter value when enabled by the user.
17+
18+
For instance, here is a filter sidebar for a post list, allowing users to filter on two fields:
1519

1620
{% raw %}
1721
```jsx
18-
import AccessTimeIcon from '@mui/icons-material/AccessTime';
19-
import MonetizationOnIcon from '@mui/icons-material/MonetizationOnOutlined';
22+
import { SavedQueriesList, FilterLiveSearch, FilterList, FilterListItem } from 'react-admin';
23+
import { Card, CardContent } from '@mui/material';
2024
import MailIcon from '@mui/icons-material/MailOutline';
21-
import LocalOfferIcon from '@mui/icons-material/LocalOfferOutlined';
22-
import { FilterList, FilterListItem } from 'react-admin';
23-
import {
24-
endOfYesterday,
25-
startOfWeek,
26-
subWeeks,
27-
startOfMonth,
28-
subMonths,
29-
} from 'date-fns';
30-
31-
import segments from '../segments/data';
32-
33-
const LastVisitedFilter = () => (
34-
<FilterList label="Last visited" icon={<AccessTimeIcon />}>
35-
<FilterListItem
36-
label="Today"
37-
value={{
38-
last_seen_gte: endOfYesterday().toISOString(),
39-
last_seen_lte: undefined,
40-
}}
41-
/>
42-
<FilterListItem
43-
label="This week"
44-
value={{
45-
last_seen_gte: startOfWeek(new Date()).toISOString(),
46-
last_seen_lte: undefined,
47-
}}
48-
/>
49-
<FilterListItem
50-
label="Last week"
51-
value={{
52-
last_seen_gte: subWeeks(startOfWeek(new Date()), 1).toISOString(),
53-
last_seen_lte: startOfWeek(new Date()).toISOString(),
54-
}}
55-
/>
56-
<FilterListItem
57-
label="This month"
58-
value={{
59-
last_seen_gte: startOfMonth(new Date()).toISOString(),
60-
last_seen_lte: undefined,
61-
}}
62-
/>
63-
<FilterListItem
64-
label="Last month"
65-
value={{
66-
last_seen_gte: subMonths(startOfMonth(new Date()),1).toISOString(),
67-
last_seen_lte: startOfMonth(new Date()).toISOString(),
68-
}}
69-
/>
70-
<FilterListItem
71-
label="Earlier"
72-
value={{
73-
last_seen_gte: undefined,
74-
last_seen_lte: subMonths(startOfMonth(new Date()),1).toISOString(),
75-
}}
76-
/>
77-
</FilterList>
78-
);
79-
const HasOrderedFilter = () => (
80-
<FilterList
81-
label="Has ordered"
82-
icon={<MonetizationOnIcon />}
83-
>
84-
<FilterListItem
85-
label="True"
86-
value={{
87-
nb_commands_gte: 1,
88-
nb_commands_lte: undefined,
89-
}}
90-
/>
91-
<FilterListItem
92-
label="False"
93-
value={{
94-
nb_commands_gte: undefined,
95-
nb_commands_lte: 0,
96-
}}
97-
/>
98-
</FilterList>
99-
);
100-
const HasNewsletterFilter = () => (
101-
<FilterList
102-
label="Has newsletter"
103-
icon={<MailIcon />}
104-
>
105-
<FilterListItem
106-
label="True"
107-
value={{ has_newsletter: true }}
108-
/>
109-
<FilterListItem
110-
label="False"
111-
value={{ has_newsletter: false }}
112-
/>
113-
</FilterList>
114-
);
115-
const SegmentFilter = () => (
116-
<FilterList
117-
label="Segment"
118-
icon={<LocalOfferIcon />}
119-
>
120-
{segments.map(segment => (
121-
<FilterListItem
122-
label={segment.name}
123-
key={segment.id}
124-
value={{ groups: segment.id }}
125-
/>
126-
))}
127-
</FilterList>
25+
import CategoryIcon from '@mui/icons-material/LocalOffer';
26+
27+
export const PostFilterSidebar = () => (
28+
<Card sx={{ order: -1, mr: 2, mt: 9, width: 200 }}>
29+
<CardContent>
30+
<SavedQueriesList />
31+
<FilterLiveSearch >
32+
<FilterList label="Subscribed to newsletter" icon={<MailIcon />}>
33+
<FilterListItem label="Yes" value={{ has_newsletter: true }} />
34+
<FilterListItem label="No" value={{ has_newsletter: false }} />
35+
</FilterList>
36+
<FilterList label="Category" icon={<CategoryIcon />}>
37+
<FilterListItem label="Tests" value={{ category: 'tests' }} />
38+
<FilterListItem label="News" value={{ category: 'news' }} />
39+
<FilterListItem label="Deals" value={{ category: 'deals' }} />
40+
<FilterListItem label="Tutorials" value={{ category: 'tutorials' }} />
41+
</FilterList>
42+
</CardContent>
43+
</Card>
12844
);
12945
```
13046
{% endraw %}
13147

48+
Add this component to the list view using [the `<List aside>` prop](./List.md#aside):
49+
50+
```jsx
51+
import { PostFilterSidebar } from './PostFilterSidebar';
52+
53+
export const PostList = () => (
54+
<List aside={<PostFilterSidebar />}>
55+
...
56+
</List>
57+
);
58+
```
59+
60+
**Tip**: The `<Card sx>` prop in the `PostFilterSidebar` component above is here to put the sidebar on the left side of the screen, instead of the default right side.
61+
62+
A more sophisticated example is the filter sidebar for the visitors list visible in the screencast at the beginning of this page. The code for this example is available in the [react-admin repository](https://github.com/marmelab/react-admin/blob/master/examples/demo/src/visitors/VisitorListAside.tsx).
63+
64+
**Tip**: In a Filter List sidebar, you can use [the `<FilterLiveSearch>` component](./FilterLiveSearch.md) to add a search input at the top of the sidebar, and [the `<SavedQueriesList>` component](./SavedQueriesList.md) to add a list of saved queries.
65+
13266
`<FilterList>` accepts 3 props:
13367

13468
* [`children`](#children), which must be a list of `<FilterListItem>`

docs/FilteringTutorial.md

+48
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,60 @@ const postFilters = [
161161

162162
An alternative UI to the Filter Button/Form Combo is the FilterList Sidebar. Similar to what users usually see on e-commerce websites, it's a panel with many simple filters that can be enabled and combined using the mouse. The user experience is better than the Button/Form Combo, because the filter values are explicit, and it doesn't require typing anything in a form. But it's a bit less powerful, as only filters with a finite set of values (or intervals) can be used in the `<FilterList>`.
163163

164+
Here is an example FilterList sidebar:
165+
166+
{% raw %}
167+
```jsx
168+
import { SavedQueriesList, FilterLiveSearch, FilterList, FilterListItem } from 'react-admin';
169+
import { Card, CardContent } from '@mui/material';
170+
import MailIcon from '@mui/icons-material/MailOutline';
171+
import CategoryIcon from '@mui/icons-material/LocalOffer';
172+
173+
export const PostFilterSidebar = () => (
174+
<Card sx={{ order: -1, mr: 2, mt: 9, width: 200 }}>
175+
<CardContent>
176+
<SavedQueriesList />
177+
<FilterLiveSearch >
178+
<FilterList label="Subscribed to newsletter" icon={<MailIcon />}>
179+
<FilterListItem label="Yes" value={{ has_newsletter: true }} />
180+
<FilterListItem label="No" value={{ has_newsletter: false }} />
181+
</FilterList>
182+
<FilterList label="Category" icon={<CategoryIcon />}>
183+
<FilterListItem label="Tests" value={{ category: 'tests' }} />
184+
<FilterListItem label="News" value={{ category: 'news' }} />
185+
<FilterListItem label="Deals" value={{ category: 'deals' }} />
186+
<FilterListItem label="Tutorials" value={{ category: 'tutorials' }} />
187+
</FilterList>
188+
</CardContent>
189+
</Card>
190+
);
191+
```
192+
{% endraw %}
193+
194+
Add it to the list view using the `<List aside>` prop:
195+
196+
```jsx
197+
import { PostFilterSidebar } from './PostFilterSidebar';
198+
199+
export const PostList = () => (
200+
<List aside={<PostFilterSidebar />}>
201+
...
202+
</List>
203+
);
204+
```
205+
206+
**Tip**: The `<Card sx>` prop in the `PostFilterSidebar` component above is here to put the sidebar on the left side of the screen, instead of the default right side.
207+
164208
Check [the `<FilterList>` documentation](./FilterList.md) for more information.
165209

166210
If you use the FilterList, you'll probably need a search input. As the FilterList sidebar is not a form, this requires a bit of extra work. Fortunately, react-admin provides a specialized search input component for that purpose: check [the `<FilterLiveSearch>` documentation](./FilterLiveSearch.md) for details.
167211

168212
![Filter Live Search](./img/filter-live-search.gif)
169213

214+
Finally, a filter sidebar is the ideal place to display the user's favorite filters. Check [the `<SavedQueriesList>` documentation](./SavedQueriesList.md) for more information.
215+
216+
![Filter Sidebar With SavedQueriesList](./img/SavedQueriesList.gif)
217+
170218
## Filter Operators
171219

172220
The internal format for storing filters and sending them to the dataProvider is an object, e.g.:

docs/List.md

+45-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ const PostList = () => (
137137
```
138138
{% endraw %}
139139

140-
The `aside` component can call the `useListContext()` hook to receive the same props as the `List` child component. This means you can display additional details of the current list in the aside component:
140+
The `aside` component can call the `useListContext()` hook to receive the same props as the `<List>` child component. This means you can display additional details of the current list in the aside component:
141141

142142
{% raw %}
143143
```jsx
@@ -159,6 +159,50 @@ const Aside = () => {
159159
```
160160
{% endraw %}
161161

162+
The `aside` prop is also the preferred way to add a [Filter Sidebar](./FilteringTutorial.md#the-filterlist-sidebar) to a list view:
163+
164+
{% raw %}
165+
```jsx
166+
// in src/PostFilterSidebar.js
167+
import { SavedQueriesList, FilterLiveSearch, FilterList, FilterListItem } from 'react-admin';
168+
import { Card, CardContent } from '@mui/material';
169+
import MailIcon from '@mui/icons-material/MailOutline';
170+
import CategoryIcon from '@mui/icons-material/LocalOffer';
171+
172+
export const PostFilterSidebar = () => (
173+
<Card sx={{ order: -1, mr: 2, mt: 9, width: 200 }}>
174+
<CardContent>
175+
<SavedQueriesList />
176+
<FilterLiveSearch >
177+
<FilterList label="Subscribed to newsletter" icon={<MailIcon />}>
178+
<FilterListItem label="Yes" value={{ has_newsletter: true }} />
179+
<FilterListItem label="No" value={{ has_newsletter: false }} />
180+
</FilterList>
181+
<FilterList label="Category" icon={<CategoryIcon />}>
182+
<FilterListItem label="Tests" value={{ category: 'tests' }} />
183+
<FilterListItem label="News" value={{ category: 'news' }} />
184+
<FilterListItem label="Deals" value={{ category: 'deals' }} />
185+
<FilterListItem label="Tutorials" value={{ category: 'tutorials' }} />
186+
</FilterList>
187+
</CardContent>
188+
</Card>
189+
);
190+
```
191+
{% endraw %}
192+
193+
```jsx
194+
// in src/PostList.js
195+
import { PostFilterSidebar } from './PostFilterSidebar';
196+
197+
export const PostList = () => (
198+
<List aside={<PostFilterSidebar />}>
199+
...
200+
</List>
201+
);
202+
```
203+
204+
**Tip**: the `<Card sx>` prop in the `PostFilterSidebar` component above is here to put the sidebar on the left side of the screen, instead of the default right side.
205+
162206
## `children`: List Layout
163207

164208
`<List>` doesn't render any content by default - it delegates this to its child. List layout components grab the `data` from the `ListContext` and render them on screen.

docs/SavedQueriesList.md

+2-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ title: "The SavedQueriesList Component"
77

88
![Filter Sidebar With SavedQueriesList](./img/SavedQueriesList.gif)
99

10-
`<SavedQueriesList>` renders a list of filters saved by the end user (and kept in [the Store](./Store.md)). It is a complement to `<FilterList>` sections for the filter sidebar
10+
`<SavedQueriesList>` renders a list of filters saved by the end user (and kept in [the Store](./Store.md)). It is a complement to `<FilterList>` sections for [the filter sidebar](./FilteringTutorial.md#the-filterlist-sidebar).
1111

1212
## Usage
1313

@@ -17,7 +17,7 @@ import {
1717
FilterListItem,
1818
List,
1919
Datagrid
20-
+ SavedQueriesList
20+
+ SavedQueriesList
2121
} from 'react-admin';
2222
import { Card, CardContent } from '@mui/material';
2323

@@ -82,6 +82,3 @@ const SongList = props => (
8282
```
8383
{% endraw %}
8484

85-
## API
86-
87-
[`<SavedQueriesList>`]: https://github.com/marmelab/react-admin/blob/master/packages/ra-ui-materialui/src/list/filter/SavedQueriesList.tsx

docs/navigation.html

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<li {% if page.path == 'FilterButton.md' %} class="active" {% endif %}><a class="nav-link" href="./FilterButton.html"><code>&lt;FilterButton&gt;</code></a></li>
7171
<li {% if page.path == 'FilterList.md' %} class="active" {% endif %}><a class="nav-link" href="./FilterList.html"><code>&lt;FilterList&gt;</code></a></li>
7272
<li {% if page.path == 'FilterLiveSearch.md' %} class="active" {% endif %}><a class="nav-link" href="./FilterLiveSearch.html"><code>&lt;FilterLiveSearch&gt;</code></a></li>
73+
<li {% if page.path == 'SavedQueriesList.md' %} class="active" {% endif %}><a class="nav-link" href="./SavedQueriesList.html"><code>&lt;SavedQueriesList&gt;</code></a></li>
7374
<li {% if page.path == 'Pagination.md' %} class="active" {% endif %}><a class="nav-link" href="./Pagination.html"><code>&lt;Pagination&gt;</code></a></li>
7475
<li {% if page.path == 'SortButton.md' %} class="active" {% endif %}><a class="nav-link" href="./SortButton.html"><code>&lt;SortButton&gt;</code></a></li>
7576
<li {% if page.path == 'useList.md' %} class="active" {% endif %}><a class="nav-link" href="./useList.html"><code>useList</code></a></li>

0 commit comments

Comments
 (0)