Skip to content

Commit

Permalink
Merge pull request #4518 from marmelab/default-order-in-sort
Browse files Browse the repository at this point in the history
Add a default order prop in List Fields
  • Loading branch information
fzaninotto authored May 8, 2020
2 parents 97b99b8 + 664274c commit fdb2cb1
Show file tree
Hide file tree
Showing 14 changed files with 90 additions and 11 deletions.
27 changes: 27 additions & 0 deletions docs/List.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,33 @@ export const PostList = (props) => (
```
{% endraw %}

### Specify Sort Order

By default, when the user clicks on a column header, the list becomes sorted in the ascending order. You change this behavior by setting the `sortByOrder` prop to `"DESC"`:

```jsx
// in src/posts.js
import React from 'react';
import { List, Datagrid, TextField } from 'react-admin';

export const PostList = (props) => (
<List {...props}>
<Datagrid>
<ReferenceField label="Post" source="id" reference="posts" sortByOrder="DESC">
<TextField source="title" />
</ReferenceField>
<FunctionField
label="Author"
sortBy="last_name"
sortByOrder="DESC"
render={record => `${record.author.first_name} ${record.author.last_name}`}
/>
<TextField source="body" />
</Datagrid>
</List>
);
```

### Permanent Filter

You can choose to always filter the list, without letting the user disable this filter - for instance to display only published posts. Write the filter to be passed to the REST client in the `filter` props:
Expand Down
3 changes: 2 additions & 1 deletion examples/simple/src/posts/PostList.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ const PostList = props => {
<TextField source="title" cellClassName={classes.title} />
<DateField
source="published_at"
sortByOrder="DESC"
cellClassName={classes.publishedAt}
/>

Expand All @@ -151,7 +152,7 @@ const PostList = props => {
label="resources.posts.fields.commentable_short"
sortable={false}
/>
<NumberField source="views" />
<NumberField source="views" sortByOrder="DESC" />
<ReferenceArrayField
label="Tags"
reference="tags"
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 @@ -67,7 +67,7 @@ export interface ListControllerProps<RecordType = Record> {
setFilters: (filters: any, displayedFilters: any) => void;
setPage: (page: number) => void;
setPerPage: (page: number) => void;
setSort: (sort: string) => void;
setSort: (sort: string, order?: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
total: number;
version: number;
Expand Down
9 changes: 6 additions & 3 deletions packages/ra-core/src/controller/useListParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ interface Modifiers {
changeParams: (action: any) => void;
setPage: (page: number) => void;
setPerPage: (pageSize: number) => void;
setSort: (sort: string) => void;
setSort: (sort: string, order?: string) => void;
setFilters: (filters: any, displayedFilters: any) => void;
hideFilter: (filterName: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
Expand Down Expand Up @@ -156,8 +156,11 @@ const useListParams = ({
}, requestSignature); // eslint-disable-line react-hooks/exhaustive-deps

const setSort = useCallback(
(newSort: string) =>
changeParams({ type: SET_SORT, payload: { sort: newSort } }),
(sort: string, order?: string) =>
changeParams({
type: SET_SORT,
payload: { sort, order },
}),
requestSignature // eslint-disable-line react-hooks/exhaustive-deps
);

Expand Down
5 changes: 3 additions & 2 deletions packages/ra-core/src/controller/useSortState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Sort } from '../types';
export interface SortProps {
setSortField: (field: string) => void;
setSortOrder: (order: string) => void;
setSort: (sort: Sort) => void;
setSort: (sort: Sort, order?: string) => void;
sort: Sort;
}

Expand Down Expand Up @@ -116,7 +116,8 @@ export default (initialSort: Sort = defaultSort): SortProps => {

return {
setSort: useCallback(
(sort: Sort) => dispatch({ type: 'SET_SORT', payload: { sort } }),
(sort: Sort, order?: string) =>
dispatch({ type: 'SET_SORT', payload: { sort, order } }),
[dispatch]
),
setSortField: useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ ReferenceArrayField.propTypes = {
reference: PropTypes.string.isRequired,
resource: PropTypes.string,
sortBy: PropTypes.string,
sortByOrder: fieldPropTypes.sortByOrder,
source: PropTypes.string.isRequired,
};

Expand Down
3 changes: 2 additions & 1 deletion packages/ra-ui-materialui/src/field/ReferenceField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import LinearProgress from '../layout/LinearProgress';
import Link from '../Link';
import sanitizeRestProps from './sanitizeRestProps';
import { ClassNameMap } from '@material-ui/styles';
import { FieldProps, InjectedFieldProps } from './types';
import { FieldProps, fieldPropTypes, InjectedFieldProps } from './types';

interface ReferenceFieldProps extends FieldProps, InjectedFieldProps {
children: ReactElement;
Expand Down Expand Up @@ -118,6 +118,7 @@ ReferenceField.propTypes = {
reference: PropTypes.string.isRequired,
resource: PropTypes.string,
sortBy: PropTypes.string,
sortByOrder: fieldPropTypes.sortByOrder,
source: PropTypes.string.isRequired,
translateChoice: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
linkType: PropTypes.oneOfType([
Expand Down
3 changes: 2 additions & 1 deletion packages/ra-ui-materialui/src/field/ReferenceManyField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
PaginationProps,
SortProps,
} from 'ra-core';
import { FieldProps, InjectedFieldProps } from './types';
import { FieldProps, fieldPropTypes, InjectedFieldProps } from './types';

/**
* Render related records to the current one.
Expand Down Expand Up @@ -138,6 +138,7 @@ ReferenceManyField.propTypes = {
reference: PropTypes.string.isRequired,
resource: PropTypes.string,
sortBy: PropTypes.string,
sortByOrder: fieldPropTypes.sortByOrder,
source: PropTypes.string.isRequired,
sort: PropTypes.exact({
field: PropTypes.string,
Expand Down
1 change: 1 addition & 0 deletions packages/ra-ui-materialui/src/field/sanitizeRestProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default (props: object): object =>
'resource',
'sortable',
'sortBy',
'sortByOrder',
'source',
'textAlign',
'translateChoice',
Expand Down
3 changes: 3 additions & 0 deletions packages/ra-ui-materialui/src/field/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { Record } from 'ra-core';
import PropTypes from 'prop-types';

type TextAlign = 'right' | 'left';
type SortOrder = 'ASC' | 'DESC';
export interface FieldProps {
addLabel?: boolean;
sortBy?: string;
sortByOrder?: SortOrder;
source?: string;
label?: string;
sortable?: boolean;
Expand All @@ -24,6 +26,7 @@ export interface InjectedFieldProps {
export const fieldPropTypes = {
addLabel: PropTypes.bool,
sortBy: PropTypes.string,
sortByOrder: PropTypes.oneOf<SortOrder>(['ASC', 'DESC']),
source: PropTypes.string,
label: PropTypes.string,
sortable: PropTypes.bool,
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/input/ReferenceInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ interface ReferenceInputViewProps {
resource: string;
setFilter: (v: string) => void;
setPagination: (pagination: Pagination) => void;
setSort: (sort: Sort) => void;
setSort: (sort: Sort, order?: string) => void;
source: string;
warning?: string;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/ra-ui-materialui/src/list/Datagrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ const Datagrid = props => {
const updateSort = useCallback(
event => {
event.stopPropagation();
setSort(event.currentTarget.dataset.sort);
setSort(
event.currentTarget.dataset.sort,
event.currentTarget.dataset.order
);
},
[setSort]
);
Expand Down
1 change: 1 addition & 0 deletions packages/ra-ui-materialui/src/list/DatagridHeaderCell.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const DatagridHeaderCell = props => {
}
direction={currentSort.order === 'ASC' ? 'asc' : 'desc'}
data-sort={field.props.sortBy || field.props.source}
data-order={field.props.sortByOrder || 'ASC'}
onClick={updateSort}
classes={classes}
>
Expand Down
36 changes: 36 additions & 0 deletions packages/ra-ui-materialui/src/list/DatagridHeaderCell.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,42 @@ describe('<DatagridHeaderCell />', () => {
expect(getByTitle('ra.action.sort').dataset.sort).toBe('title');
});

it('should be change order when field has a sortByOrder props', () => {
const { getByTitle } = render(
<table>
<tbody>
<tr>
<DatagridHeaderCell
currentSort={{}}
field={
<Field sortBy="title" sortByOrder="DESC" />
}
updateSort={() => true}
/>
</tr>
</tbody>
</table>
);
expect(getByTitle('ra.action.sort').dataset.order).toBe('DESC');
});

it('should be keep ASC order when field has not sortByOrder props', () => {
const { getByTitle } = render(
<table>
<tbody>
<tr>
<DatagridHeaderCell
currentSort={{}}
field={<Field source="title" />}
updateSort={() => true}
/>
</tr>
</tbody>
</table>
);
expect(getByTitle('ra.action.sort').dataset.order).toBe('ASC');
});

it('should be disabled when field has no sortby and no source', () => {
const { queryAllByTitle } = render(
<table>
Expand Down

0 comments on commit fdb2cb1

Please sign in to comment.