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

[TypeScript] add setParams in ListContext #9340

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export const useReferenceManyFieldController = <
hasPreviousPage: pageInfo ? pageInfo.hasPreviousPage : page > 1,
setSort,
showFilter,
setParams,
total,
};
};
2 changes: 2 additions & 0 deletions packages/ra-core/src/controller/list/ListContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { ListControllerResult } from './useListController';
* @prop {string} defaultTitle the translated title based on the resource, e.g. 'Posts'
* @prop {string} resource the resource name, deduced from the location. e.g. 'posts'
* @prop {Function} refetch a function for triggering a refetch of the list data
* @prop {Function} setParams a callback to update the params namely page, perPage, sort and filters, e.g. setParams({sort: { field: 'name', order: 'ASC' }, page: 5, perPage: 20})
*
* @typedef Props
* @prop {ListControllerResult} value
Expand Down Expand Up @@ -78,6 +79,7 @@ export const ListContext = createContext<ListControllerResult>({
setSort: null,
showFilter: null,
total: null,
setParams: null,
});

ListContext.displayName = 'ListContext';
134 changes: 101 additions & 33 deletions packages/ra-core/src/controller/list/queryReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,45 @@ export const SET_FILTER = 'SET_FILTER';
export const SHOW_FILTER = 'SHOW_FILTER';
export const HIDE_FILTER = 'HIDE_FILTER';

const oppositeOrder = direction =>
export const SET_PARAMS = 'SET_PARAMS';

const oppositeOrder = (direction: OrderBy) =>
direction === SORT_DESC ? SORT_ASC : SORT_DESC;

type SetSortActionType = {
type: typeof SET_SORT;
payload: {
field: string;
order?: OrderBy;
};
};

type SetPageActionType = {
type: typeof SET_PAGE;
payload: number;
};

type SetPerPageActionType = {
type: typeof SET_PER_PAGE;
payload: number;
};

type SetParamsActionType = {
type: typeof SET_PARAMS;
payload: {
sort: {
field: string;
order?: OrderBy;
};
page: number;
perPage: number;
};
};

type ActionTypes =
| {
type: typeof SET_SORT;
payload: {
field: string;
order?: typeof SORT_ASC | typeof SORT_DESC;
};
}
| {
type: typeof SET_PAGE;
payload: number;
}
| {
type: typeof SET_PER_PAGE;
payload: number;
}
| SetSortActionType
| SetPageActionType
| SetPerPageActionType
| {
type: typeof SET_FILTER;
payload: {
Expand All @@ -49,7 +69,63 @@ type ActionTypes =
| {
type: typeof HIDE_FILTER;
payload: string;
};
}
| SetParamsActionType;

const setSort = (
previousState: ListParams,
action: SetSortActionType
): ListParams => {
if (action.payload.field === previousState.sort) {
return {
...previousState,
order: oppositeOrder(previousState.order),
page: 1,
};
}

return {
...previousState,
sort: action.payload.field,
order: action.payload.order || SORT_ASC,
page: 1,
};
};

const setPage = (
previousState: ListParams,
action: SetPageActionType
): ListParams => {
return { ...previousState, page: action.payload };
};

const setPerPage = (
previousState: ListParams,
action: SetPerPageActionType
): ListParams => {
return { ...previousState, page: 1, perPage: action.payload };
};

const setParams = (
previousState: ListParams,
action: SetParamsActionType
): ListParams => {
const { page, perPage, sort } = action.payload;
let newState = previousState;
if (!!perPage) {
newState = setPerPage(previousState, {
type: SET_PER_PAGE,
payload: perPage,
});
}
if (!!page) {
newState = setPage(previousState, { type: SET_PAGE, payload: page });
}
if (!!sort) {
newState = setSort(previousState, { type: SET_SORT, payload: sort });
}
return newState;
};

/**
* This reducer is for the react-router query string.
Expand All @@ -60,26 +136,13 @@ export const queryReducer: Reducer<ListParams, ActionTypes> = (
) => {
switch (action.type) {
case SET_SORT:
if (action.payload.field === previousState.sort) {
return {
...previousState,
order: oppositeOrder(previousState.order),
page: 1,
};
}

return {
...previousState,
sort: action.payload.field,
order: action.payload.order || SORT_ASC,
page: 1,
};
return setSort(previousState, action);

case SET_PAGE:
return { ...previousState, page: action.payload };
return setPage(previousState, action);

case SET_PER_PAGE:
return { ...previousState, page: 1, perPage: action.payload };
return setPerPage(previousState, action);

case SET_FILTER: {
return {
Expand Down Expand Up @@ -140,9 +203,14 @@ export const queryReducer: Reducer<ListParams, ActionTypes> = (
};
}

case SET_PARAMS:
return setParams(previousState, action);

default:
return previousState;
}
};

type OrderBy = 'ASC' | 'DESC';

export default queryReducer;
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export const useInfiniteListController = <RecordType extends RaRecord = any>(
setPerPage: queryModifiers.setPerPage,
setSort: queryModifiers.setSort,
showFilter: queryModifiers.showFilter,
setParams: queryModifiers.setParams,
total: total,
hasNextPage,
hasPreviousPage,
Expand Down
1 change: 1 addition & 0 deletions packages/ra-core/src/controller/list/useList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ export const useList = <RecordType extends RaRecord = any>(
setPerPage,
setSort,
showFilter,
setParams,
total: finalItems?.total,
};
};
Expand Down
8 changes: 8 additions & 0 deletions packages/ra-core/src/controller/list/useListController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export const useListController = <RecordType extends RaRecord = any>(
setPerPage: queryModifiers.setPerPage,
setSort: queryModifiers.setSort,
showFilter: queryModifiers.showFilter,
setParams: queryModifiers.setParams,
total: total,
hasNextPage: pageInfo
? pageInfo.hasNextPage
Expand Down Expand Up @@ -428,6 +429,13 @@ export interface ListControllerResult<RecordType extends RaRecord = any> {
total: number;
hasNextPage: boolean;
hasPreviousPage: boolean;
setParams: (params: ParamsType) => void;
}

export interface ParamsType {
page: number;
perPage: number;
sort: SortPayload;
}

export const injectedProps = [
Expand Down
3 changes: 3 additions & 0 deletions packages/ra-core/src/controller/list/useListParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import queryReducer, {
import { SortPayload, FilterPayload } from '../../types';
import removeEmpty from '../../util/removeEmpty';
import { useIsMounted } from '../../util/hooks';
import { ParamsType } from './useListController';

export interface ListParams {
sort: string;
Expand Down Expand Up @@ -275,6 +276,7 @@ export const useListParams = ({
setFilters,
hideFilter,
showFilter,
setParams,
},
];
};
Expand Down Expand Up @@ -412,6 +414,7 @@ interface Modifiers {
setFilters: (filters: any, displayedFilters: any) => void;
hideFilter: (filterName: string) => void;
showFilter: (filterName: string, defaultValue: any) => void;
setParams: (params: ParamsType) => void;
}

const emptyObject = {};
Expand Down
123 changes: 123 additions & 0 deletions packages/ra-core/src/controller/useParamsState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useReducer, useEffect, useRef, useCallback } from 'react';

import queryReducer, { SORT_ASC, SORT_DESC } from './list/queryReducer';
import { ParamsPayload, SortPayload } from '../types';

export interface ParamsProps {
setParams: (field: SortPayload['field']) => void;
}

type Action = { type: 'SET_PARAMS'; payload: SortPayload };

const paramsReducer = (state: SortPayload, action: Action): SortPayload => {
switch (action.type) {
case 'SET_SORT':
return action.payload;
case 'SET_SORT_FIELD': {
const field = action.payload;
const order =
state.field === field
? state.order === SORT_ASC
? SORT_DESC
: SORT_ASC
: SORT_ASC;
return { field, order };
}
case 'SET_SORT_ORDER': {
const order = action.payload;
if (!state.field) {
throw new Error(
'cannot change the order on an undefined sort field'
);
}
return {
field: state.field,
order,
};
}
default:
return state;
}
};

export const defaultParams = {
page: 1,
perPage: 25,
sort: { field: '', order: 'ASC' } as const,
};

/**
* Set the sort { field, order }
* @name setSort
* @function
* @param {SortPayload} sort the sort object
*/

/**
* Set the sort field, swap the order if the field is the same
* @name setSortField
* @function
* @param {string} field the sort field
*/

/**
* Set the sort order
* @name setSortOrder
* @function
* @param {string} order The sort order, either ASC or DESC
*/

/**
* @typedef SortProps
* @type {Object}
* @property {Object} sort: the sort object.
* @property {string} sort.field: the sort object.
* @property {'ASC' | 'DESC'} sort.order: the sort object.
* @property {setSort} setSort
* @property {setSortField} setSortField
* @property {setSortOrder} setSortOrder
*/

/**
* Hooks to provide sort state
*
* @example
*
* const { sort, setSort, setSortField, setSortOrder } = useSort({
* field: 'name',
* order: 'ASC',
* });
*
* setSort({ field: 'name', order: 'ASC' });
* // is the same as
* setSortField('name');
* setSortOrder('ASC');
*
* @param {Object} initialSort
* @param {string} initialSort.field The initial sort field
* @param {string} initialSort.order The initial sort order
* @returns {SortProps} The sort props
*/
const useParamsState = (
initialParams: ParamsPayload = defaultParams
): SortProps => {
const [params, dispatch] = useReducer(queryReducer, initialParams);
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
dispatch({ type: 'SET_SORT', payload: initialSort });
}, [initialSort.field, initialSort.order]); // eslint-disable-line react-hooks/exhaustive-deps

return {
setParams: useCallback(
(sort: SortPayload) =>
dispatch({ type: 'SET_PARAMS', payload: sort }),
[dispatch]
),
};
};

export default useParamsState;
5 changes: 5 additions & 0 deletions packages/ra-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export interface PaginationPayload {
page: number;
perPage: number;
}

export interface ParamsPayload extends PaginationPayload {
sort: SortPayload;
}

export type ValidUntil = Date;
/**
* i18nProvider types
Expand Down