Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Add documentation for the <SavedQueriesList> component #8157

Merged
merged 2 commits into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 45 additions & 111 deletions docs/FilterList.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,130 +5,64 @@ title: "The FilterList Component"

# `<FilterList>`

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.

![Filter Sidebar](./img/filter-sidebar.gif)

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>`.
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>`.

## Usage

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:
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.

For instance, here is a filter sidebar for a post list, allowing users to filter on two fields:

{% raw %}
```jsx
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import MonetizationOnIcon from '@mui/icons-material/MonetizationOnOutlined';
import { SavedQueriesList, FilterLiveSearch, FilterList, FilterListItem } from 'react-admin';
import { Card, CardContent } from '@mui/material';
import MailIcon from '@mui/icons-material/MailOutline';
import LocalOfferIcon from '@mui/icons-material/LocalOfferOutlined';
import { FilterList, FilterListItem } from 'react-admin';
import {
endOfYesterday,
startOfWeek,
subWeeks,
startOfMonth,
subMonths,
} from 'date-fns';

import segments from '../segments/data';

const LastVisitedFilter = () => (
<FilterList label="Last visited" icon={<AccessTimeIcon />}>
<FilterListItem
label="Today"
value={{
last_seen_gte: endOfYesterday().toISOString(),
last_seen_lte: undefined,
}}
/>
<FilterListItem
label="This week"
value={{
last_seen_gte: startOfWeek(new Date()).toISOString(),
last_seen_lte: undefined,
}}
/>
<FilterListItem
label="Last week"
value={{
last_seen_gte: subWeeks(startOfWeek(new Date()), 1).toISOString(),
last_seen_lte: startOfWeek(new Date()).toISOString(),
}}
/>
<FilterListItem
label="This month"
value={{
last_seen_gte: startOfMonth(new Date()).toISOString(),
last_seen_lte: undefined,
}}
/>
<FilterListItem
label="Last month"
value={{
last_seen_gte: subMonths(startOfMonth(new Date()),1).toISOString(),
last_seen_lte: startOfMonth(new Date()).toISOString(),
}}
/>
<FilterListItem
label="Earlier"
value={{
last_seen_gte: undefined,
last_seen_lte: subMonths(startOfMonth(new Date()),1).toISOString(),
}}
/>
</FilterList>
);
const HasOrderedFilter = () => (
<FilterList
label="Has ordered"
icon={<MonetizationOnIcon />}
>
<FilterListItem
label="True"
value={{
nb_commands_gte: 1,
nb_commands_lte: undefined,
}}
/>
<FilterListItem
label="False"
value={{
nb_commands_gte: undefined,
nb_commands_lte: 0,
}}
/>
</FilterList>
);
const HasNewsletterFilter = () => (
<FilterList
label="Has newsletter"
icon={<MailIcon />}
>
<FilterListItem
label="True"
value={{ has_newsletter: true }}
/>
<FilterListItem
label="False"
value={{ has_newsletter: false }}
/>
</FilterList>
);
const SegmentFilter = () => (
<FilterList
label="Segment"
icon={<LocalOfferIcon />}
>
{segments.map(segment => (
<FilterListItem
label={segment.name}
key={segment.id}
value={{ groups: segment.id }}
/>
))}
</FilterList>
import CategoryIcon from '@mui/icons-material/LocalOffer';

export const PostFilterSidebar = () => (
<Card sx={{ order: -1, mr: 2, mt: 9, width: 200 }}>
<CardContent>
<SavedQueriesList />
<FilterLiveSearch >
<FilterList label="Subscribed to newsletter" icon={<MailIcon />}>
<FilterListItem label="Yes" value={{ has_newsletter: true }} />
<FilterListItem label="No" value={{ has_newsletter: false }} />
</FilterList>
<FilterList label="Category" icon={<CategoryIcon />}>
<FilterListItem label="Tests" value={{ category: 'tests' }} />
<FilterListItem label="News" value={{ category: 'news' }} />
<FilterListItem label="Deals" value={{ category: 'deals' }} />
<FilterListItem label="Tutorials" value={{ category: 'tutorials' }} />
</FilterList>
</CardContent>
</Card>
);
```
{% endraw %}

Add this component to the list view using [the `<List aside>` prop](./List.md#aside):

```jsx
import { PostFilterSidebar } from './PostFilterSidebar';

export const PostList = () => (
<List aside={<PostFilterSidebar />}>
...
</List>
);
```

**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.

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).

**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.

`<FilterList>` accepts 3 props:

* [`children`](#children), which must be a list of `<FilterListItem>`
Expand Down
48 changes: 48 additions & 0 deletions docs/FilteringTutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,60 @@ const postFilters = [

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>`.

Here is an example FilterList sidebar:

{% raw %}
```jsx
import { SavedQueriesList, FilterLiveSearch, FilterList, FilterListItem } from 'react-admin';
import { Card, CardContent } from '@mui/material';
import MailIcon from '@mui/icons-material/MailOutline';
import CategoryIcon from '@mui/icons-material/LocalOffer';

export const PostFilterSidebar = () => (
<Card sx={{ order: -1, mr: 2, mt: 9, width: 200 }}>
<CardContent>
<SavedQueriesList />
<FilterLiveSearch >
<FilterList label="Subscribed to newsletter" icon={<MailIcon />}>
<FilterListItem label="Yes" value={{ has_newsletter: true }} />
<FilterListItem label="No" value={{ has_newsletter: false }} />
</FilterList>
<FilterList label="Category" icon={<CategoryIcon />}>
<FilterListItem label="Tests" value={{ category: 'tests' }} />
<FilterListItem label="News" value={{ category: 'news' }} />
<FilterListItem label="Deals" value={{ category: 'deals' }} />
<FilterListItem label="Tutorials" value={{ category: 'tutorials' }} />
</FilterList>
</CardContent>
</Card>
);
```
{% endraw %}

Add it to the list view using the `<List aside>` prop:

```jsx
import { PostFilterSidebar } from './PostFilterSidebar';

export const PostList = () => (
<List aside={<PostFilterSidebar />}>
...
</List>
);
```

**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.

Check [the `<FilterList>` documentation](./FilterList.md) for more information.

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.

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

Finally, a filter sidebar is the ideal place to display the user's favorite filters. Check [the `<SavedQueriesList>` documentation](./SavedQueriesList.md) for more information.

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

## Filter Operators

The internal format for storing filters and sending them to the dataProvider is an object, e.g.:
Expand Down
46 changes: 45 additions & 1 deletion docs/List.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const PostList = () => (
```
{% endraw %}

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:
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:

{% raw %}
```jsx
Expand All @@ -159,6 +159,50 @@ const Aside = () => {
```
{% endraw %}

The `aside` prop is also the preferred way to add a [Filter Sidebar](./FilteringTutorial.md#the-filterlist-sidebar) to a list view:

{% raw %}
```jsx
// in src/PostFilterSidebar.js
import { SavedQueriesList, FilterLiveSearch, FilterList, FilterListItem } from 'react-admin';
import { Card, CardContent } from '@mui/material';
import MailIcon from '@mui/icons-material/MailOutline';
import CategoryIcon from '@mui/icons-material/LocalOffer';

export const PostFilterSidebar = () => (
<Card sx={{ order: -1, mr: 2, mt: 9, width: 200 }}>
<CardContent>
<SavedQueriesList />
<FilterLiveSearch >
<FilterList label="Subscribed to newsletter" icon={<MailIcon />}>
<FilterListItem label="Yes" value={{ has_newsletter: true }} />
<FilterListItem label="No" value={{ has_newsletter: false }} />
</FilterList>
<FilterList label="Category" icon={<CategoryIcon />}>
<FilterListItem label="Tests" value={{ category: 'tests' }} />
<FilterListItem label="News" value={{ category: 'news' }} />
<FilterListItem label="Deals" value={{ category: 'deals' }} />
<FilterListItem label="Tutorials" value={{ category: 'tutorials' }} />
</FilterList>
</CardContent>
</Card>
);
```
{% endraw %}

```jsx
// in src/PostList.js
import { PostFilterSidebar } from './PostFilterSidebar';

export const PostList = () => (
<List aside={<PostFilterSidebar />}>
...
</List>
);
```

**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.

## `children`: List Layout

`<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.
Expand Down
7 changes: 2 additions & 5 deletions docs/SavedQueriesList.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: "The SavedQueriesList Component"

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

`<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
`<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).

## Usage

Expand All @@ -17,7 +17,7 @@ import {
FilterListItem,
List,
Datagrid
+ SavedQueriesList
+ SavedQueriesList
} from 'react-admin';
import { Card, CardContent } from '@mui/material';

Expand Down Expand Up @@ -82,6 +82,3 @@ const SongList = props => (
```
{% endraw %}

## API

[`<SavedQueriesList>`]: https://github.com/marmelab/react-admin/blob/master/packages/ra-ui-materialui/src/list/filter/SavedQueriesList.tsx
1 change: 1 addition & 0 deletions docs/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<li {% if page.path == 'FilterButton.md' %} class="active" {% endif %}><a class="nav-link" href="./FilterButton.html"><code>&lt;FilterButton&gt;</code></a></li>
<li {% if page.path == 'FilterList.md' %} class="active" {% endif %}><a class="nav-link" href="./FilterList.html"><code>&lt;FilterList&gt;</code></a></li>
<li {% if page.path == 'FilterLiveSearch.md' %} class="active" {% endif %}><a class="nav-link" href="./FilterLiveSearch.html"><code>&lt;FilterLiveSearch&gt;</code></a></li>
<li {% if page.path == 'SavedQueriesList.md' %} class="active" {% endif %}><a class="nav-link" href="./SavedQueriesList.html"><code>&lt;SavedQueriesList&gt;</code></a></li>
<li {% if page.path == 'Pagination.md' %} class="active" {% endif %}><a class="nav-link" href="./Pagination.html"><code>&lt;Pagination&gt;</code></a></li>
<li {% if page.path == 'SortButton.md' %} class="active" {% endif %}><a class="nav-link" href="./SortButton.html"><code>&lt;SortButton&gt;</code></a></li>
<li {% if page.path == 'useList.md' %} class="active" {% endif %}><a class="nav-link" href="./useList.html"><code>useList</code></a></li>
Expand Down
Loading