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

Simplify the development of custom List views #4952

Merged
merged 36 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9668002
Add ListContext and migrate List to TypeScript
fzaninotto Jun 17, 2020
d8f552b
use useListContext instead of props in Empty
fzaninotto Jun 18, 2020
a43d879
use useListContext instead of props in ListToolbar
fzaninotto Jun 18, 2020
4034017
Update typescript, eslint & pritter to be able to use null coaelish o…
fzaninotto Jun 18, 2020
a69f573
add top, left, right, and bottom props to <List>
fzaninotto Jun 18, 2020
fe5fb04
Revert "Update typescript, eslint & pritter to be able to use null co…
fzaninotto Jun 18, 2020
79c7155
replace modern JS by nos so modern one
fzaninotto Jun 18, 2020
8307552
small fixes
fzaninotto Jun 18, 2020
e897f04
use useListContextinstead of props in BulkActionsToolbar
fzaninotto Jun 18, 2020
5c1c24b
Migrate Datagrid to TypeScript
fzaninotto Jun 18, 2020
a8c7a88
Make ReferenceManyField include a ListContext
fzaninotto Jun 18, 2020
8e091c4
Improve backward compatibility
fzaninotto Jun 19, 2020
1d4441c
use useListContext instead of props in ReferenceArrayField
fzaninotto Jun 19, 2020
f0eb78c
Merge branch 'next' into controller-context
fzaninotto Jun 19, 2020
4d65400
use useListContext instead of props in SimpleList
fzaninotto Jun 19, 2020
41fcc34
Remove top, bottom, left and right props
fzaninotto Jun 19, 2020
c7129e2
Test List context in simple example
fzaninotto Jun 22, 2020
097c20d
Fix warnings
fzaninotto Jun 22, 2020
8c9886a
Fix unit tests
fzaninotto Jun 22, 2020
441f835
Fix warnings
fzaninotto Jun 22, 2020
a4f2b25
Fix last test
fzaninotto Jun 22, 2020
c5a7e1a
Fix warnings in Pagination test
fzaninotto Jun 23, 2020
f62261c
Fix warnings in List tests
fzaninotto Jun 23, 2020
eb221e2
Fix warnings in Filter tests
fzaninotto Jun 23, 2020
3f84625
Convert Filter to TypeScript
fzaninotto Jun 23, 2020
ee7cf65
Try to make tests pass
fzaninotto Jun 23, 2020
60caa8b
Migrate SingleFieldList and ListGuesser to ListContext
fzaninotto Jun 24, 2020
bd11207
Rewrite List documentation to illustrate the new context
fzaninotto Jun 26, 2020
6016447
add basic integration tests for useListContext
fzaninotto Jun 28, 2020
aa8976a
improve jsDoc of ListContext and useListcontext
fzaninotto Jun 28, 2020
1d00e6f
review
fzaninotto Jun 28, 2020
ddad038
Apply suggestions from code review
fzaninotto Jun 29, 2020
152b2c8
use Existing state management hooks
fzaninotto Jun 29, 2020
34cebc4
review
fzaninotto Jun 29, 2020
4f05291
Merge branch 'controller-context' of github.com:marmelab/react-admin …
fzaninotto Jun 29, 2020
e6f67cd
Review
fzaninotto Jun 30, 2020
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
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
"@types/react": "^16.9.0",
"@types/react-redux": "^7.1.1",
"@types/recompose": "^0.27.0",
"@typescript-eslint/eslint-plugin": "^1.9.0",
"@typescript-eslint/parser": "^1.9.0",
"@typescript-eslint/eslint-plugin": "^3.3.0",
"@typescript-eslint/parser": "^3.3.0",
"babel-eslint": "^10.0.1",
"cheerio": "~1.0.0-rc.2",
"concurrently": "^5.1.0",
Expand All @@ -69,7 +69,7 @@
"lint-staged": "^8.1.7",
"lolex": "~2.3.2",
"mutationobserver-shim": "^0.3.3",
"prettier": "~1.17.1",
"prettier": "~1.19.1",
"raf": "~3.4.1",
"ts-jest": "^24.0.0",
"wait-on": "^3.2.0",
Expand All @@ -81,6 +81,6 @@
"cypress"
],
"dependencies": {
"typescript": "^3.5.3"
"typescript": "^3.7.0"
}
}
3 changes: 2 additions & 1 deletion packages/ra-core/src/auth/useAuthState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const emptyParams = {};
* );
*/
const useAuthState = (params: any = emptyParams): State => {
const [state, setState] = useSafeSetState({
const [state, setState] = useSafeSetState<State>({
loading: true,
loaded: false,
authenticated: true, // optimistic
Expand All @@ -64,6 +64,7 @@ const useAuthState = (params: any = emptyParams): State => {
setState({ loading: false, loaded: true, authenticated: false })
);
}, [checkAuth, params, setState]);

return state;
};

Expand Down
55 changes: 55 additions & 0 deletions packages/ra-core/src/controller/ListContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createContext } from 'react';
import { ListControllerProps } from './useListController';

/**
* Context to store the result of the useListController() hook.
*
* Use the useListContext() hook to read the context.
*
* @see useListController
* @see useListContext
*
* @example
*
* import { useListController, ListContext } from 'ra-core';
*
* const List = props => {
* const controllerProps = useListController(props);
* return (
* <ListContext.Provider value={controllerProps}>
* ...
* </ListContext.Provider>
* );
* };
*/
const ListContext = createContext<ListControllerProps>({
basePath: null,
currentSort: null,
data: null,
defaultTitle: null,
displayedFilters: null,
filterValues: null,
hasCreate: null,
hideFilter: null,
ids: null,
loaded: null,
loading: null,
onSelect: null,
onToggleItem: null,
onUnselectItems: null,
page: null,
perPage: null,
resource: null,
selectedIds: null,
setFilters: null,
setPage: null,
setPerPage: null,
setSort: null,
showFilter: null,
total: null,
version: null,
});

ListContext.displayName = 'ListContext';

export default ListContext;
4 changes: 4 additions & 0 deletions packages/ra-core/src/controller/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import CreateController from './CreateController';
import EditController from './EditController';
import ListController from './ListController';
import ListContext from './ListContext';
import ShowController from './ShowController';
import useRecordSelection from './useRecordSelection';
import useVersion from './useVersion';
Expand All @@ -13,6 +14,7 @@ import useListController, {
sanitizeListRestProps,
ListControllerProps,
} from './useListController';
import useListContext from './useListContext';
import useEditController, { EditControllerProps } from './useEditController';
import useCreateController, {
CreateControllerProps,
Expand All @@ -27,6 +29,7 @@ export {
CreateController,
EditController,
ListController,
ListContext,
ShowController,
useCheckMinimumRequiredProps,
useListController,
Expand All @@ -40,6 +43,7 @@ export {
useSortState,
usePaginationState,
useReference,
useListContext,
useListParams,
ListControllerProps,
EditControllerProps,
Expand Down
12 changes: 12 additions & 0 deletions packages/ra-core/src/controller/useListContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useContext } from 'react';

import ListContext from './ListContext';

/**
* Hook to read the list controller props.
*
* Must be used within a <ListContext.Provider>
*/
const useListContext = () => useContext(ListContext);
djhi marked this conversation as resolved.
Show resolved Hide resolved

export default useListContext;
6 changes: 2 additions & 4 deletions packages/ra-core/src/controller/useListController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,18 @@ export interface ListProps {
filter?: object;
filters?: ReactElement<any>;
filterDefaultValues?: object;
pagination?: ReactElement<any>;
perPage?: number;
sort?: Sort;
// the props managed by react-admin
basePath: string;
basePath?: string;
debounce?: number;
hasCreate?: boolean;
hasEdit?: boolean;
hasList?: boolean;
hasShow?: boolean;
location?: Location;
path?: string;
query: ListParams;
resource: string;
resource?: string;
[key: string]: any;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/dataProvider/useGetMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const useGetMany = (
const data = useSelector((state: ReduxState) =>
selectMany(state, resource, ids)
);
const [state, setState] = useSafeSetState({
const [state, setState] = useSafeSetState<UseGetManyResult>({
data,
error: null,
loading: ids.length !== 0,
Expand Down
18 changes: 10 additions & 8 deletions packages/ra-core/src/dataProvider/useMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const useMutation = (
query?: Mutation,
options?: MutationOptions
): UseMutationValue => {
const [state, setState] = useSafeSetState({
const [state, setState] = useSafeSetState<UseMutationState>({
data: null,
error: null,
total: null,
Expand Down Expand Up @@ -204,15 +204,17 @@ export interface MutationOptions {
withDeclarativeSideEffectsSupport?: boolean;
}

export interface UseMutationState {
data?: any;
total?: number;
error?: any;
loading: boolean;
loaded: boolean;
}

export type UseMutationValue = [
(query?: Partial<Mutation>, options?: Partial<MutationOptions>) => void,
{
data?: any;
total?: number;
error?: any;
loading: boolean;
loaded: boolean;
}
UseMutationState
];

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/export/ExporterContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createContext } from 'react';
import { Exporter } from '../types';
import defaultExporter from './defaultExporter';

const ExporterContext = createContext<Exporter>(defaultExporter);
const ExporterContext = createContext<Exporter | false>(defaultExporter);

ExporterContext.displayName = 'ExporterContext';

Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/auth/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const LoginForm: FunctionComponent<Props> = props => {
variant="contained"
type="submit"
color="primary"
disabled={loading}
disabled={!!loading}
className={classes.button}
>
{loading && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import * as React from 'react';
import { cloneElement } from 'react';
import { FC, cloneElement, ReactElement } from 'react';
import { createPortal } from 'react-dom';
import PropTypes from 'prop-types';
import { useTranslate, warning } from 'ra-core';
import { useTranslate, Record, warning } from 'ra-core';

const Title = ({ className, defaultTitle, locale, record, title, ...rest }) => {
export interface TitleProps {
className?: string;
defaultTitle: string;
record?: Record;
title?: string | ReactElement;
}

const Title: FC<TitleProps> = ({
className,
defaultTitle,
record,
title,
...rest
}) => {
const translate = useTranslate();
const container =
typeof document !== 'undefined'
Expand Down Expand Up @@ -35,8 +48,7 @@ export const TitlePropType = PropTypes.oneOfType([
Title.propTypes = {
defaultTitle: PropTypes.string,
className: PropTypes.string,
locale: PropTypes.string,
record: PropTypes.object,
record: PropTypes.any,
title: TitlePropType,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from 'react';
import { FC } from 'react';
import { Typography, makeStyles } from '@material-ui/core';
import Inbox from '@material-ui/icons/Inbox';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/styles';
import { useTranslate } from 'ra-core';
import { CreateButton } from '../button';
import { useTranslate, useListContext } from 'ra-core';
import inflection from 'inflection';

import { ClassesOverride } from '../types';
import { CreateButton } from '../button';

const useStyles = makeStyles(
theme => ({
message: {
Expand All @@ -29,8 +31,8 @@ const useStyles = makeStyles(
{ name: 'RaEmpty' }
);

const Empty = props => {
const { resource, basePath } = props;
const Empty: FC<EmptyProps> = props => {
const { resource, basePath } = useListContext();
const classes = useStyles(props);
const translate = useTranslate();

Expand Down Expand Up @@ -67,4 +69,8 @@ const Empty = props => {
);
};

export interface EmptyProps {
classes?: ClassesOverride<typeof useStyles>;
}

export default Empty;
Loading