Skip to content

Commit

Permalink
Add ability to define filters as array of Inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Jun 21, 2021
1 parent 4e8c356 commit 0d7e45b
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 127 deletions.
26 changes: 10 additions & 16 deletions examples/simple/src/posts/PostList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
DateField,
downloadCSV,
EditButton,
Filter,
List,
NumberField,
ReferenceArrayField,
Expand All @@ -41,20 +40,15 @@ const QuickFilter = ({ label, source, defaultValue }) => {
return <Chip className={classes.chip} label={translate(label)} />;
};

const PostFilter = props => (
<Filter {...props}>
<SearchInput source="q" alwaysOn />
<TextInput
source="title"
defaultValue="Qui tempore rerum et voluptates"
/>
<QuickFilter
label="resources.posts.fields.commentable"
source="commentable"
defaultValue
/>
</Filter>
);
const postFilter = [
<SearchInput source="q" alwaysOn />,
<TextInput source="title" defaultValue="Qui tempore rerum et voluptates" />,
<QuickFilter
label="resources.posts.fields.commentable"
source="commentable"
defaultValue
/>,
];

const exporter = posts => {
const data = posts.map(post => ({
Expand Down Expand Up @@ -126,7 +120,7 @@ const PostList = props => {
<List
{...props}
bulkActionButtons={<PostListBulkActions />}
filters={<PostFilter />}
filters={postFilter}
sort={{ field: 'published_at', order: 'DESC' }}
exporter={exporter}
>
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/controller/useListController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { useResourceContext, useGetResourceLabel } from '../core';
export interface ListProps {
// the props you can change
filter?: FilterPayload;
filters?: ReactElement<any>;
filters?: ReactElement | ReactElement[];
filterDefaultValues?: object;
perPage?: number;
sort?: SortPayload;
Expand Down
8 changes: 8 additions & 0 deletions packages/ra-ui-materialui/src/list/FilterContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as React from 'react';

export type FilterContextType = React.ReactNode[];

/**
* Make filters accessible to sub components
*/
export const FilterContext = React.createContext<FilterContextType>(undefined);
5 changes: 4 additions & 1 deletion packages/ra-ui-materialui/src/list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ List.propTypes = {
className: PropTypes.string,
filter: PropTypes.object,
filterDefaultValues: PropTypes.object,
filters: PropTypes.element,
filters: PropTypes.oneOfType([
PropTypes.element,
PropTypes.arrayOf(PropTypes.element),
]),
// @ts-ignore-line
pagination: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]),
perPage: PropTypes.number.isRequired,
Expand Down
24 changes: 14 additions & 10 deletions packages/ra-ui-materialui/src/list/ListActions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { cloneElement, useMemo, FC, ReactElement } from 'react';
import { cloneElement, useMemo, useContext, FC, ReactElement } from 'react';
import PropTypes from 'prop-types';
import {
sanitizeListRestProps,
Expand All @@ -14,6 +14,8 @@ import { ToolbarProps } from '@material-ui/core';

import TopToolbar from '../layout/TopToolbar';
import { CreateButton, ExportButton } from '../button';
import { FilterContext } from './FilterContext';
import FilterButton from './filter/FilterButton';

/**
* Action Toolbar for the List view
Expand Down Expand Up @@ -44,7 +46,7 @@ import { CreateButton, ExportButton } from '../button';
* );
*/
const ListActions: FC<ListActionsProps> = props => {
const { className, exporter, filters, ...rest } = props;
const { className, exporter, filters: filtersProp, ...rest } = props;
const {
currentSort,
displayedFilters,
Expand All @@ -56,17 +58,19 @@ const ListActions: FC<ListActionsProps> = props => {
} = useListContext(props);
const resource = useResourceContext(rest);
const { hasCreate } = useResourceDefinition(rest);
const filters = useContext(FilterContext) || filtersProp;
return useMemo(
() => (
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
{filters &&
cloneElement(filters, {
resource,
showFilter,
displayedFilters,
filterValues,
context: 'button',
})}
{filtersProp
? cloneElement(filtersProp, {
resource,
showFilter,
displayedFilters,
filterValues,
context: 'button',
})
: filters && <FilterButton />}
{hasCreate && <CreateButton basePath={basePath} />}
{exporter !== false && (
<ExportButton
Expand Down
27 changes: 23 additions & 4 deletions packages/ra-ui-materialui/src/list/ListToolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from 'react';
import { FC, ReactElement } from 'react';
import { FC, ReactElement, Children } from 'react';
import PropTypes from 'prop-types';
import { Toolbar, ToolbarProps } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { Exporter } from 'ra-core';

import { ClassesOverride } from '../types';
import { FilterForm } from './filter';
import { FilterContext } from './FilterContext';

const useStyles = makeStyles(
theme => ({
Expand Down Expand Up @@ -36,7 +38,21 @@ const useStyles = makeStyles(
const ListToolbar: FC<ListToolbarProps> = props => {
const { classes: classesOverride, filters, actions, ...rest } = props;
const classes = useStyles(props);
return (

return Array.isArray(filters) ? (
<FilterContext.Provider value={filters}>
<Toolbar className={classes.toolbar}>
<FilterForm />
<span />
{actions &&
React.cloneElement(actions, {
...rest,
className: classes.actions,
...actions.props,
})}
</Toolbar>
</FilterContext.Provider>
) : (
<Toolbar className={classes.toolbar}>
{filters &&
React.cloneElement(filters, {
Expand All @@ -57,7 +73,10 @@ const ListToolbar: FC<ListToolbarProps> = props => {

ListToolbar.propTypes = {
classes: PropTypes.object,
filters: PropTypes.element,
filters: PropTypes.oneOfType([
PropTypes.element,
PropTypes.arrayOf(PropTypes.element),
]),
// @ts-ignore
actions: PropTypes.oneOfType([PropTypes.bool, PropTypes.element]),
// @ts-ignore
Expand All @@ -68,7 +87,7 @@ export interface ListToolbarProps
extends Omit<ToolbarProps, 'classes' | 'onSelect'> {
actions?: ReactElement | false;
classes?: ClassesOverride<typeof useStyles>;
filters?: ReactElement;
filters?: ReactElement | ReactElement[];
exporter?: Exporter | false;
}

Expand Down
5 changes: 4 additions & 1 deletion packages/ra-ui-materialui/src/list/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ ListView.propTypes = {
// @ts-ignore-line
exporter: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
filterDefaultValues: PropTypes.object,
filters: PropTypes.element,
filters: PropTypes.oneOfType([
PropTypes.element,
PropTypes.arrayOf(PropTypes.element),
]),
filterValues: PropTypes.object,
hasCreate: PropTypes.bool,
hideFilter: PropTypes.func,
Expand Down
30 changes: 19 additions & 11 deletions packages/ra-ui-materialui/src/list/filter/FilterButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useRef,
ReactNode,
HtmlHTMLAttributes,
useContext,
} from 'react';
import PropTypes from 'prop-types';
import Menu from '@material-ui/core/Menu';
Expand All @@ -17,6 +18,7 @@ import { useListContext, useResourceContext } from 'ra-core';
import { FilterButtonMenuItem } from './FilterButtonMenuItem';
import Button from '../../button/Button';
import { ClassesOverride } from '../../types';
import { FilterContext } from '../FilterContext';

const useStyles = makeStyles(
{
Expand All @@ -26,7 +28,13 @@ const useStyles = makeStyles(
);

const FilterButton = (props: FilterButtonProps): JSX.Element => {
const { filters, classes: classesOverride, className, ...rest } = props;
const {
filters: filtersProp,
classes: classesOverride,
className,
...rest
} = props;
const filters = useContext(FilterContext) || filtersProp;
const resource = useResourceContext(props);
const { displayedFilters = {}, filterValues, showFilter } = useListContext(
props
Expand Down Expand Up @@ -97,18 +105,18 @@ const FilterButton = (props: FilterButtonProps): JSX.Element => {
};

const sanitizeRestProps = ({
displayedFilters,
filterValues,
showFilter,
displayedFilters = null,
filterValues = null,
showFilter = null,
...rest
}) => rest;

FilterButton.propTypes = {
resource: PropTypes.string,
filters: PropTypes.arrayOf(PropTypes.node).isRequired,
filters: PropTypes.arrayOf(PropTypes.node),
displayedFilters: PropTypes.object,
filterValues: PropTypes.object.isRequired,
showFilter: PropTypes.func.isRequired,
filterValues: PropTypes.object,
showFilter: PropTypes.func,
classes: PropTypes.object,
className: PropTypes.string,
};
Expand All @@ -117,10 +125,10 @@ export interface FilterButtonProps extends HtmlHTMLAttributes<HTMLDivElement> {
classes?: ClassesOverride<typeof useStyles>;
className?: string;
resource?: string;
filterValues: any;
showFilter: (filterName: string, defaultValue: any) => void;
displayedFilters: any;
filters: ReactNode[];
filterValues?: any;
showFilter?: (filterName: string, defaultValue: any) => void;
displayedFilters?: any;
filters?: ReactNode[];
}

export default FilterButton;
Loading

0 comments on commit 0d7e45b

Please sign in to comment.