From 7597f9911ab9213454ad972698caac17fb567a0e Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:02:35 +0100 Subject: [PATCH 01/10] Introduce Resource Context and Hooks --- .../ra-core/src/actions/resourcesActions.ts | 12 +- packages/ra-core/src/core/Resource.tsx | 146 +++++++++--------- packages/ra-core/src/core/ResourceContext.ts | 28 ++++ .../src/core/ResourceContextProvider.tsx | 15 ++ .../ra-core/src/core/useResourceContext.ts | 38 +++++ .../ra-core/src/core/useResourceDefinition.ts | 11 ++ packages/ra-core/src/types.ts | 12 +- 7 files changed, 181 insertions(+), 81 deletions(-) create mode 100644 packages/ra-core/src/core/ResourceContext.ts create mode 100644 packages/ra-core/src/core/ResourceContextProvider.tsx create mode 100644 packages/ra-core/src/core/useResourceContext.ts create mode 100644 packages/ra-core/src/core/useResourceDefinition.ts diff --git a/packages/ra-core/src/actions/resourcesActions.ts b/packages/ra-core/src/actions/resourcesActions.ts index bbcd17c6676..c2f230baa88 100644 --- a/packages/ra-core/src/actions/resourcesActions.ts +++ b/packages/ra-core/src/actions/resourcesActions.ts @@ -1,14 +1,6 @@ -export const REGISTER_RESOURCE = 'RA/REGISTER_RESOURCE'; +import { ResourceDefinition } from '../types'; -export interface ResourceDefinition { - readonly name: string; - readonly options?: any; - readonly hasList?: boolean; - readonly hasEdit?: boolean; - readonly hasShow?: boolean; - readonly hasCreate?: boolean; - readonly icon?: any; -} +export const REGISTER_RESOURCE = 'RA/REGISTER_RESOURCE'; export interface RegisterResourceAction { readonly type: typeof REGISTER_RESOURCE; diff --git a/packages/ra-core/src/core/Resource.tsx b/packages/ra-core/src/core/Resource.tsx index 0e651cb9927..6ff431319bb 100644 --- a/packages/ra-core/src/core/Resource.tsx +++ b/packages/ra-core/src/core/Resource.tsx @@ -6,6 +6,7 @@ import { Route, Switch } from 'react-router-dom'; import WithPermissions from '../auth/WithPermissions'; import { registerResource, unregisterResource } from '../actions'; import { ResourceProps, ResourceMatch, ReduxState } from '../types'; +import { ResourceContextProvider } from './ResourceContextProvider'; const defaultOptions = {}; @@ -51,84 +52,89 @@ const ResourceRoutes: FunctionComponent = ({ const basePath = match ? match.path : ''; + const resourceData = useMemo( + () => ({ + resource: name, + hasList: !!list, + hasEdit: !!edit, + hasShow: !!show, + hasCreate: !!create, + }), + [name, create, edit, list, show] + ); + // match tends to change even on the same route ; using memo to avoid an extra render return useMemo(() => { // if the registration hasn't finished, no need to render if (!isRegistered) { return null; } - const props = { - resource: name, - options, - hasList: !!list, - hasEdit: !!edit, - hasShow: !!show, - hasCreate: !!create, - }; return ( - - {create && ( - ( - - )} - /> - )} - {show && ( - ( - - )} - /> - )} - {edit && ( - ( - - )} - /> - )} - {list && ( - ( - - )} - /> - )} - + + + {create && ( + ( + + )} + /> + )} + {show && ( + ( + + )} + /> + )} + {edit && ( + ( + + )} + /> + )} + {list && ( + ( + + )} + /> + )} + + ); }, [basePath, name, create, edit, list, show, options, isRegistered]); // eslint-disable-line react-hooks/exhaustive-deps }; diff --git a/packages/ra-core/src/core/ResourceContext.ts b/packages/ra-core/src/core/ResourceContext.ts new file mode 100644 index 00000000000..d34bf590696 --- /dev/null +++ b/packages/ra-core/src/core/ResourceContext.ts @@ -0,0 +1,28 @@ +import { createContext } from 'react'; + +/** + * Context to store the current resource informations. + * + * Use the useResource() hook to read the context. That's what most components do in react-admin. + * + * @example + * + * import { useResource, useTranslate } from 'ra-core'; + * + * const MyCustomEditTitle = props => { + * const { name } = useResource(props); + * + * return ( + *

{translate(`${name}.name`)}

+ * ); + * }; + */ +export const ResourceContext = createContext(undefined); + +export interface ResourceContextValue { + resource: string; + hasList: boolean; + hasEdit: boolean; + hasShow: boolean; + hasCreate: boolean; +} diff --git a/packages/ra-core/src/core/ResourceContextProvider.tsx b/packages/ra-core/src/core/ResourceContextProvider.tsx new file mode 100644 index 00000000000..7ddb1d6d68c --- /dev/null +++ b/packages/ra-core/src/core/ResourceContextProvider.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { ReactElement, ReactNode } from 'react'; +import { ResourceContext, ResourceContextValue } from './ResourceContext'; + +export const ResourceContextProvider = ({ + children, + value, +}: { + children: ReactNode; + value: ResourceContextValue; +}): ReactElement => ( + + {children} + +); diff --git a/packages/ra-core/src/core/useResourceContext.ts b/packages/ra-core/src/core/useResourceContext.ts new file mode 100644 index 00000000000..52c749c65cb --- /dev/null +++ b/packages/ra-core/src/core/useResourceContext.ts @@ -0,0 +1,38 @@ +import { useContext } from 'react'; +import { ResourceContext, ResourceContextValue } from './ResourceContext'; + +/** + * Hook to read the resource informations from the ResourceContext. + * + * Must be used within a (e.g. as a descendent of + * or any reference related components). + * + * @returns {ResourceContextValue} The resource informations + */ +export const useResource = < + ResourceInformationsType extends Partial< + ResourceContextValue + > = ResourceContextValue +>( + props: ResourceInformationsType +): ResourceContextValue => { + const context = useContext(ResourceContext); + + if (!context || !context.resource) { + /** + * The element isn't inside a + * + * @deprecated - to be removed in 4.0 + */ + if (process.env.NODE_ENV !== 'production') { + console.warn( + "Any react-admin components must be used inside a . Relying on props rather than context to get the resource data is deprecated and won't be supported in the next major version of react-admin." + ); + } + // Ignored because resource is often optional (as it is injected) in components which passes the props to this hook + // @ts-ignore + return props; + } + + return context; +}; diff --git a/packages/ra-core/src/core/useResourceDefinition.ts b/packages/ra-core/src/core/useResourceDefinition.ts new file mode 100644 index 00000000000..e23d1ddcc10 --- /dev/null +++ b/packages/ra-core/src/core/useResourceDefinition.ts @@ -0,0 +1,11 @@ +import { useSelector } from 'react-redux'; +import { getResources } from '../reducer'; +import { ResourceDefinition } from '../types'; + +/** + * Hook which returns the configuration of the requested resource + */ +export const useResourceConfig = (resource: string): ResourceDefinition => { + const resources = useSelector(getResources); + return resources.find(r => r.name === resource); +}; diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index 20fa4d8f4a2..60b159f537e 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -299,10 +299,19 @@ export type LegacyDataProvider = ( params: any ) => Promise; +export interface ResourceDefinition { + readonly name: string; + readonly options?: any; + readonly hasList?: boolean; + readonly hasEdit?: boolean; + readonly hasShow?: boolean; + readonly hasCreate?: boolean; + readonly icon?: any; +} + /** * Redux state type */ - export interface ReduxState { admin: { ui: { @@ -313,6 +322,7 @@ export interface ReduxState { }; resources: { [name: string]: { + props: ResourceDefinition; data: { [key: string]: Record; [key: number]: Record; From 88608513f6e0d30769a5831ba0a44723c3225e9d Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:36:57 +0100 Subject: [PATCH 02/10] Rename hooks and export from core --- packages/ra-core/src/core/index.ts | 4 ++++ packages/ra-core/src/core/useResourceContext.ts | 2 +- packages/ra-core/src/core/useResourceDefinition.ts | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/ra-core/src/core/index.ts b/packages/ra-core/src/core/index.ts index e2be1cdf107..6d9cf22f9a5 100644 --- a/packages/ra-core/src/core/index.ts +++ b/packages/ra-core/src/core/index.ts @@ -1,4 +1,8 @@ export * from './dataFetchActions'; export * from './components'; +export * from './ResourceContext'; +export * from './ResourceContextProvider'; +export * from './useResourceContext'; +export * from './useResourceDefinition'; // there seems to be a bug in TypeScript: this only works if the exports are in this order. // Swapping the two exports leads to the core module missing the dataFetchActions constants diff --git a/packages/ra-core/src/core/useResourceContext.ts b/packages/ra-core/src/core/useResourceContext.ts index 52c749c65cb..a9f507d51c9 100644 --- a/packages/ra-core/src/core/useResourceContext.ts +++ b/packages/ra-core/src/core/useResourceContext.ts @@ -9,7 +9,7 @@ import { ResourceContext, ResourceContextValue } from './ResourceContext'; * * @returns {ResourceContextValue} The resource informations */ -export const useResource = < +export const useResourceContext = < ResourceInformationsType extends Partial< ResourceContextValue > = ResourceContextValue diff --git a/packages/ra-core/src/core/useResourceDefinition.ts b/packages/ra-core/src/core/useResourceDefinition.ts index e23d1ddcc10..cbe8c7ac016 100644 --- a/packages/ra-core/src/core/useResourceDefinition.ts +++ b/packages/ra-core/src/core/useResourceDefinition.ts @@ -3,9 +3,13 @@ import { getResources } from '../reducer'; import { ResourceDefinition } from '../types'; /** - * Hook which returns the configuration of the requested resource + * Hook which returns the definition of the requested resource */ -export const useResourceConfig = (resource: string): ResourceDefinition => { +export const useResourceDefinition = ( + resource: string, + props +): ResourceDefinition => { const resources = useSelector(getResources); - return resources.find(r => r.name === resource); + + return resources.find(r => r?.name === resource) || props; }; From 7df15d8a8122727590d6ac69739588f1b7ed1b44 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:37:10 +0100 Subject: [PATCH 03/10] Use resource hooks in controllers --- .../src/controller/details/useCreateController.ts | 6 +++--- .../src/controller/details/useEditController.ts | 10 ++++++---- .../src/controller/details/useShowController.ts | 14 ++++++-------- .../ra-core/src/controller/useListController.ts | 5 +++-- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/ra-core/src/controller/details/useCreateController.ts b/packages/ra-core/src/controller/details/useCreateController.ts index 8884a137f84..a1746548a28 100644 --- a/packages/ra-core/src/controller/details/useCreateController.ts +++ b/packages/ra-core/src/controller/details/useCreateController.ts @@ -25,6 +25,7 @@ import { useTranslate } from '../../i18n'; import useVersion from '../useVersion'; import { CRUD_CREATE } from '../../actions'; import { Record } from '../../types'; +import { useResourceContext, useResourceDefinition } from '../../core'; export interface CreateProps = Record> { basePath?: string; @@ -101,16 +102,15 @@ export const useCreateController = < useCheckMinimumRequiredProps('Create', ['basePath', 'resource'], props); const { basePath, - resource, record = {}, - hasShow, - hasEdit, successMessage, onSuccess, onFailure, transform, } = props; + const { resource } = useResourceContext(props); + const { hasEdit, hasShow } = useResourceDefinition(resource, props); const location = useLocation(); const translate = useTranslate(); const notify = useNotify(); diff --git a/packages/ra-core/src/controller/details/useEditController.ts b/packages/ra-core/src/controller/details/useEditController.ts index 7c0ea914a5d..4219f76b1ee 100644 --- a/packages/ra-core/src/controller/details/useEditController.ts +++ b/packages/ra-core/src/controller/details/useEditController.ts @@ -22,6 +22,7 @@ import { SetTransformData, useSaveModifiers, } from '../saveModifiers'; +import { useResourceContext, useResourceDefinition } from '../../core'; export interface EditProps { basePath?: string; @@ -94,17 +95,18 @@ export const useEditController = ( const { basePath, hasCreate, - hasEdit, - hasList, - hasShow, id, - resource, successMessage, undoable = true, onSuccess, onFailure, transform, } = props; + const { resource } = useResourceContext(props); + const { hasEdit, hasList, hasShow } = useResourceDefinition( + resource, + props + ); const translate = useTranslate(); const notify = useNotify(); const redirect = useRedirect(); diff --git a/packages/ra-core/src/controller/details/useShowController.ts b/packages/ra-core/src/controller/details/useShowController.ts index 927051fd7e2..bd0d6d28dd7 100644 --- a/packages/ra-core/src/controller/details/useShowController.ts +++ b/packages/ra-core/src/controller/details/useShowController.ts @@ -7,6 +7,7 @@ import { useGetOne } from '../../dataProvider'; import { useTranslate } from '../../i18n'; import { useNotify, useRedirect, useRefresh } from '../../sideEffect'; import { CRUD_GET_ONE } from '../../actions'; +import { useResourceContext, useResourceDefinition } from '../../core'; export interface ShowProps { basePath?: string; @@ -57,15 +58,12 @@ export const useShowController = ( props: ShowProps ): ShowControllerProps => { useCheckMinimumRequiredProps('Show', ['basePath', 'resource'], props); - const { - basePath, - hasCreate, - hasEdit, - hasList, - hasShow, - id, + const { basePath, id } = props; + const { resource } = useResourceContext(props); + const { hasCreate, hasEdit, hasList, hasShow } = useResourceDefinition( resource, - } = props; + props + ); const translate = useTranslate(); const notify = useNotify(); const redirect = useRedirect(); diff --git a/packages/ra-core/src/controller/useListController.ts b/packages/ra-core/src/controller/useListController.ts index 45300225bd3..848bb24fc4d 100644 --- a/packages/ra-core/src/controller/useListController.ts +++ b/packages/ra-core/src/controller/useListController.ts @@ -23,6 +23,7 @@ import { Record, Exporter, } from '../types'; +import { useResourceContext, useResourceDefinition } from '../core'; export interface ListProps { // the props you can change @@ -110,14 +111,14 @@ const useListController = ( const { basePath, exporter = defaultExporter, - resource, - hasCreate, filterDefaultValues, sort = defaultSort, perPage = 10, filter, debounce = 500, } = props; + const { resource } = useResourceContext(props); + const { hasCreate } = useResourceDefinition(resource, props); if (filter && isValidElement(filter)) { throw new Error( From b4620fdfaa6e3fc33a220ffec87a4372d70ce76e Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Fri, 30 Oct 2020 11:16:37 +0100 Subject: [PATCH 04/10] Use resource hook in more controllers --- .../button/useDeleteWithConfirmController.tsx | 22 +++++++------ .../button/useDeleteWithUndoController.tsx | 22 +++++++------ .../field/useReferenceArrayFieldController.ts | 26 ++++++++------- .../field/useReferenceManyFieldController.ts | 28 +++++++++------- .../input/useGetMatchingReferences.ts | 24 ++++++++------ .../input/useReferenceArrayInputController.ts | 26 ++++++++------- .../input/useReferenceInputController.ts | 32 +++++++++++-------- 7 files changed, 104 insertions(+), 76 deletions(-) diff --git a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx index 245aa27e64d..bfc920e9970 100644 --- a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx +++ b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx @@ -14,6 +14,7 @@ import { } from '../../sideEffect'; import { Record } from '../../types'; import { OnFailure, OnSuccess } from '../saveModifiers'; +import { useResourceContext } from '../../core'; /** * Prepare a set of callbacks for a delete button guarded by confirmation dialog @@ -67,15 +68,18 @@ import { OnFailure, OnSuccess } from '../saveModifiers'; * ); * }; */ -const useDeleteWithConfirmController = ({ - resource, - record, - redirect: redirectTo, - basePath, - onClick, - onSuccess, - onFailure, -}: UseDeleteWithConfirmControllerParams): UseDeleteWithConfirmControllerReturn => { +const useDeleteWithConfirmController = ( + props: UseDeleteWithConfirmControllerParams +): UseDeleteWithConfirmControllerReturn => { + const { + record, + redirect: redirectTo, + basePath, + onClick, + onSuccess, + onFailure, + } = props; + const { resource } = useResourceContext(props); const [open, setOpen] = useState(false); const notify = useNotify(); const redirect = useRedirect(); diff --git a/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx b/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx index 850752e94f8..aa54c5a7797 100644 --- a/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx +++ b/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx @@ -9,6 +9,7 @@ import { } from '../../sideEffect'; import { Record } from '../../types'; import { OnFailure, OnSuccess } from '../saveModifiers'; +import { useResourceContext } from '../../core'; /** * Prepare callback for a Delete button with undo support @@ -47,15 +48,18 @@ import { OnFailure, OnSuccess } from '../saveModifiers'; * ); * }; */ -const useDeleteWithUndoController = ({ - resource, - record, - basePath, - redirect: redirectTo = 'list', - onClick, - onSuccess, - onFailure, -}: UseDeleteWithUndoControllerParams): UseDeleteWithUndoControllerReturn => { +const useDeleteWithUndoController = ( + props: UseDeleteWithUndoControllerParams +): UseDeleteWithUndoControllerReturn => { + const { + record, + basePath, + redirect: redirectTo = 'list', + onClick, + onSuccess, + onFailure, + } = props; + const { resource } = useResourceContext(props); const notify = useNotify(); const redirect = useRedirect(); const refresh = useRefresh(); diff --git a/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts b/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts index 20639f1a5c1..a07b2226cf7 100644 --- a/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts +++ b/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts @@ -10,6 +10,7 @@ import { useNotify } from '../../sideEffect'; import usePaginationState from '../usePaginationState'; import useSelectionState from '../useSelectionState'; import useSortState from '../useSortState'; +import { useResourceContext } from '../../core'; interface Option { basePath: string; @@ -49,17 +50,20 @@ const defaultSort = { field: null, order: null }; * * @returns {ReferenceArrayProps} The reference props */ -const useReferenceArrayFieldController = ({ - basePath, - filter = defaultFilter, - page: initialPage = 1, - perPage: initialPerPage = 1000, - record, - reference, - resource, - sort: initialSort = defaultSort, - source, -}: Option): ListControllerProps => { +const useReferenceArrayFieldController = ( + props: Option +): ListControllerProps => { + const { + basePath, + filter = defaultFilter, + page: initialPage = 1, + perPage: initialPerPage = 1000, + record, + reference, + sort: initialSort = defaultSort, + source, + } = props; + const { resource } = useResourceContext(props); const notify = useNotify(); const ids = get(record, source) || []; const { data, error, loading, loaded } = useGetMany(reference, ids, { diff --git a/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts b/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts index c3d1d25468d..a50b4d8bc5d 100644 --- a/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts +++ b/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts @@ -10,6 +10,7 @@ import { ListControllerProps } from '../useListController'; import usePaginationState from '../usePaginationState'; import useSelectionState from '../useSelectionState'; import useSortState from '../useSortState'; +import { useResourceContext } from '../../core'; interface Options { basePath: string; @@ -65,18 +66,21 @@ const defaultFilter = {}; * * @returns {ReferenceManyProps} The reference many props */ -const useReferenceManyFieldController = ({ - resource, - reference, - record, - target, - filter = defaultFilter, - source, - basePath, - page: initialPage, - perPage: initialPerPage, - sort: initialSort = { field: 'id', order: 'DESC' }, -}: Options): ListControllerProps => { +const useReferenceManyFieldController = ( + props: Options +): ListControllerProps => { + const { + reference, + record, + target, + filter = defaultFilter, + source, + basePath, + page: initialPage, + perPage: initialPerPage, + sort: initialSort = { field: 'id', order: 'DESC' }, + } = props; + const { resource } = useResourceContext(props); const notify = useNotify(); // pagination logic diff --git a/packages/ra-core/src/controller/input/useGetMatchingReferences.ts b/packages/ra-core/src/controller/input/useGetMatchingReferences.ts index 050dfd9543a..edd3a4b459f 100644 --- a/packages/ra-core/src/controller/input/useGetMatchingReferences.ts +++ b/packages/ra-core/src/controller/input/useGetMatchingReferences.ts @@ -2,6 +2,7 @@ import { useCallback } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { crudGetMatchingAccumulate } from '../../actions/accumulateActions'; +import { useResourceContext } from '../../core'; import { getPossibleReferences, getPossibleReferenceValues, @@ -35,16 +36,19 @@ interface UseMatchingReferencesProps { const defaultReferenceSource = (resource: string, source: string) => `${resource}@${source}`; -export default ({ - reference, - referenceSource = defaultReferenceSource, - resource, - source, - filter, - pagination, - sort, - id, -}: UseMatchingReferencesOption): UseMatchingReferencesProps => { +export default ( + props: UseMatchingReferencesOption +): UseMatchingReferencesProps => { + const { + reference, + referenceSource = defaultReferenceSource, + source, + filter, + pagination, + sort, + id, + } = props; + const { resource } = useResourceContext(props); const dispatch = useDispatch(); useDeepCompareEffect(() => { diff --git a/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts b/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts index dd0ffdc1236..536cb06f0a2 100644 --- a/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts +++ b/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts @@ -13,6 +13,7 @@ import { FieldInputProps } from 'react-final-form'; import useGetMatching from '../../dataProvider/useGetMatching'; import { useTranslate } from '../../i18n'; import { getStatusForArrayInput as getDataStatus } from './referenceDataStatus'; +import { useResourceContext } from '../../core'; /** * Prepare data for the ReferenceArrayInput components @@ -36,17 +37,20 @@ import { getStatusForArrayInput as getDataStatus } from './referenceDataStatus'; * * @return {Object} controllerProps Fetched data and callbacks for the ReferenceArrayInput components */ -const useReferenceArrayInputController = ({ - filter: defaultFilter, - filterToQuery = defaultFilterToQuery, - input, - perPage = 25, - sort: defaultSort = { field: 'id', order: 'DESC' }, - options, - reference, - resource, - source, -}: Option): ReferenceArrayInputProps => { +const useReferenceArrayInputController = ( + props: Option +): ReferenceArrayInputProps => { + const { + filter: defaultFilter, + filterToQuery = defaultFilterToQuery, + input, + perPage = 25, + sort: defaultSort = { field: 'id', order: 'DESC' }, + options, + reference, + source, + } = props; + const { resource } = useResourceContext(props); const translate = useTranslate(); // We store the current input value in a ref so that we are able to fetch diff --git a/packages/ra-core/src/controller/input/useReferenceInputController.ts b/packages/ra-core/src/controller/input/useReferenceInputController.ts index 35b4a950ece..2d71d1dab98 100644 --- a/packages/ra-core/src/controller/input/useReferenceInputController.ts +++ b/packages/ra-core/src/controller/input/useReferenceInputController.ts @@ -16,6 +16,7 @@ import usePaginationState from '../usePaginationState'; import { useSortState } from '..'; import useFilterState from '../useFilterState'; import useSelectionState from '../useSelectionState'; +import { useResourceContext } from '../../core'; const defaultReferenceSource = (resource: string, source: string) => `${resource}@${source}`; @@ -59,20 +60,23 @@ const defaultFilter = {}; * filterToQuery: searchText => ({ title: searchText }) * }); */ -export const useReferenceInputController = ({ - basePath, - input, - page: initialPage = 1, - perPage: initialPerPage = 25, - filter = defaultFilter, - reference, - filterToQuery, - // @deprecated - referenceSource = defaultReferenceSource, - resource, - sort: sortOverride, - source, -}: Option): ReferenceInputValue => { +export const useReferenceInputController = ( + props: Option +): ReferenceInputValue => { + const { + basePath, + input, + page: initialPage = 1, + perPage: initialPerPage = 25, + filter = defaultFilter, + reference, + filterToQuery, + // @deprecated + referenceSource = defaultReferenceSource, + sort: sortOverride, + source, + } = props; + const { resource } = useResourceContext(props); const translate = useTranslate(); // pagination logic From ee0edf29f2ec339aedde8df0a9a9e1356976b258 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Fri, 30 Oct 2020 14:24:36 +0100 Subject: [PATCH 05/10] Use resource hook in list components --- .../src/list/BulkDeleteAction.tsx | 7 +- packages/ra-ui-materialui/src/list/Empty.tsx | 6 +- .../ra-ui-materialui/src/list/ListGuesser.tsx | 4 +- .../src/list/SingleFieldList.tsx | 10 +- .../src/list/datagrid/Datagrid.tsx | 3 +- .../src/list/datagrid/DatagridHeaderCell.tsx | 9 +- .../src/list/datagrid/DatagridRow.tsx | 326 +++++++++--------- .../src/list/filter/FilterButtonMenuItem.tsx | 6 +- .../src/list/filter/FilterFormInput.tsx | 5 +- 9 files changed, 197 insertions(+), 179 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/BulkDeleteAction.tsx b/packages/ra-ui-materialui/src/list/BulkDeleteAction.tsx index 4784329e3b5..4939ed1443c 100644 --- a/packages/ra-ui-materialui/src/list/BulkDeleteAction.tsx +++ b/packages/ra-ui-materialui/src/list/BulkDeleteAction.tsx @@ -1,13 +1,14 @@ import { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useDispatch } from 'react-redux'; -import { crudDeleteMany, startUndoable } from 'ra-core'; +import { crudDeleteMany, startUndoable, useResourceContext } from 'ra-core'; /** *@deprecated use BulkDeleteButton instead */ const BulkDeleteAction = props => { const dispatch = useDispatch(); + const { resource } = useResourceContext(props); useEffect(() => { if (process.env.NODE_ENV !== 'production') { @@ -16,7 +17,7 @@ const BulkDeleteAction = props => { ' is deprecated. Use the component instead, via the bulkActionButton props.' ); } - const { basePath, resource, selectedIds, undoable, onExit } = props; + const { basePath, selectedIds, undoable, onExit } = props; if (undoable) { dispatch( startUndoable(crudDeleteMany(resource, selectedIds, basePath)) @@ -25,7 +26,7 @@ const BulkDeleteAction = props => { dispatch(crudDeleteMany(resource, selectedIds, basePath)); } onExit(); - }, [dispatch, props]); + }, [dispatch, props, resource]); return null; }; diff --git a/packages/ra-ui-materialui/src/list/Empty.tsx b/packages/ra-ui-materialui/src/list/Empty.tsx index ea24e185945..cc0a2cff208 100644 --- a/packages/ra-ui-materialui/src/list/Empty.tsx +++ b/packages/ra-ui-materialui/src/list/Empty.tsx @@ -3,7 +3,7 @@ import { FC } from 'react'; import { Typography } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import Inbox from '@material-ui/icons/Inbox'; -import { useTranslate, useListContext } from 'ra-core'; +import { useTranslate, useListContext, useResourceContext } from 'ra-core'; import inflection from 'inflection'; import { ClassesOverride } from '../types'; @@ -33,7 +33,8 @@ const useStyles = makeStyles( ); const Empty: FC = props => { - const { resource, basePath } = useListContext(props); + const { basePath } = useListContext(props); + const { resource } = useResourceContext(props); const classes = useStyles(props); const translate = useTranslate(); @@ -75,6 +76,7 @@ const Empty: FC = props => { export interface EmptyProps { classes?: ClassesOverride; + resource?: string; } export default Empty; diff --git a/packages/ra-ui-materialui/src/list/ListGuesser.tsx b/packages/ra-ui-materialui/src/list/ListGuesser.tsx index 5d74741ee78..dae5778ea12 100644 --- a/packages/ra-ui-materialui/src/list/ListGuesser.tsx +++ b/packages/ra-ui-materialui/src/list/ListGuesser.tsx @@ -6,6 +6,7 @@ import { getElementsFromRecords, InferredElement, ListContextProvider, + useResourceContext, } from 'ra-core'; import ListView, { ListViewProps } from './ListView'; @@ -42,7 +43,8 @@ const ListGuesser: FC = props => { }; const ListViewGuesser: FC = props => { - const { ids, data, resource } = props; + const { ids, data } = props; + const { resource } = useResourceContext(props); const [inferredChild, setInferredChild] = useState(null); useEffect(() => { if (ids.length > 0 && data && !inferredChild) { diff --git a/packages/ra-ui-materialui/src/list/SingleFieldList.tsx b/packages/ra-ui-materialui/src/list/SingleFieldList.tsx index bb726c2e466..d294d28f912 100644 --- a/packages/ra-ui-materialui/src/list/SingleFieldList.tsx +++ b/packages/ra-ui-materialui/src/list/SingleFieldList.tsx @@ -4,7 +4,12 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import LinearProgress from '@material-ui/core/LinearProgress'; import { makeStyles } from '@material-ui/core/styles'; -import { linkToRecord, sanitizeListRestProps, useListContext } from 'ra-core'; +import { + linkToRecord, + sanitizeListRestProps, + useListContext, + useResourceContext, +} from 'ra-core'; import Link from '../Link'; import { ClassesOverride } from '../types'; @@ -70,7 +75,8 @@ const SingleFieldList: FC = props => { linkType = 'edit', ...rest } = props; - const { ids, data, loaded, resource, basePath } = useListContext(props); + const { ids, data, loaded, basePath } = useListContext(props); + const { resource } = useResourceContext(props); const classes = useStyles(props); diff --git a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx index 606e07fa018..1f5124c9696 100644 --- a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx +++ b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx @@ -14,6 +14,7 @@ import { useVersion, Identifier, Record, + useResourceContext, } from 'ra-core'; import { Checkbox, @@ -75,6 +76,7 @@ const Datagrid: FC = React.forwardRef((props, ref) => { hasBulkActions = false, hover, isRowSelectable, + resource, rowClick, rowStyle, size = 'small', @@ -89,7 +91,6 @@ const Datagrid: FC = React.forwardRef((props, ref) => { loaded, onSelect, onToggleItem, - resource, selectedIds, setSort, total, diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridHeaderCell.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridHeaderCell.tsx index e1b52dbd950..c895fc8d57e 100644 --- a/packages/ra-ui-materialui/src/list/datagrid/DatagridHeaderCell.tsx +++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridHeaderCell.tsx @@ -5,7 +5,12 @@ import classnames from 'classnames'; import { TableCell, TableSortLabel, Tooltip } from '@material-ui/core'; import { TableCellProps } from '@material-ui/core/TableCell'; import { makeStyles } from '@material-ui/core/styles'; -import { FieldTitle, useTranslate, SortPayload } from 'ra-core'; +import { + FieldTitle, + useTranslate, + SortPayload, + useResourceContext, +} from 'ra-core'; import { ClassesOverride } from '../../types'; @@ -33,10 +38,10 @@ export const DatagridHeaderCell = ( field, currentSort, updateSort, - resource, isSorting, ...rest } = props; + const { resource } = useResourceContext(props); const classes = useStyles(props); const translate = useTranslate(); diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx index 96b9e268fd7..4098cf3f985 100644 --- a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx +++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx @@ -18,7 +18,13 @@ import { TableRowProps, Checkbox, } from '@material-ui/core'; -import { linkToRecord, useExpanded, Identifier, Record } from 'ra-core'; +import { + linkToRecord, + useExpanded, + Identifier, + Record, + useResourceContext, +} from 'ra-core'; import isEqual from 'lodash/isEqual'; import { useHistory } from 'react-router-dom'; @@ -35,177 +41,169 @@ const computeNbColumns = (expand, children, hasBulkActions) => const defaultClasses = { expandIconCell: '', checkbox: '', rowCell: '' }; -const DatagridRow: FC = React.forwardRef( - ( - { +const DatagridRow: FC = React.forwardRef((props, ref) => { + const { + basePath, + children, + classes = defaultClasses, + className, + expand, + hasBulkActions, + hover, + id, + onToggleItem, + record, + rowClick, + selected, + style, + selectable, + ...rest + } = props; + const { resource } = useResourceContext(props); + const [expanded, toggleExpanded] = useExpanded(resource, id); + const [nbColumns, setNbColumns] = useState( + computeNbColumns(expand, children, hasBulkActions) + ); + useEffect(() => { + // Fields can be hidden dynamically based on permissions; + // The expand panel must span over the remaining columns + // So we must recompute the number of columns to span on + const newNbColumns = computeNbColumns(expand, children, hasBulkActions); + if (newNbColumns !== nbColumns) { + setNbColumns(newNbColumns); + } + }, [expand, nbColumns, children, hasBulkActions]); + + const history = useHistory(); + + const handleToggleExpand = useCallback( + event => { + toggleExpanded(); + event.stopPropagation(); + }, + [toggleExpanded] + ); + const handleToggleSelection = useCallback( + event => { + if (!selectable) return; + onToggleItem(id); + event.stopPropagation(); + }, + [id, onToggleItem, selectable] + ); + const handleClick = useCallback( + async event => { + if (!rowClick) return; + event.persist(); + + const effect = + typeof rowClick === 'function' + ? await rowClick(id, basePath, record) + : rowClick; + switch (effect) { + case 'edit': + history.push(linkToRecord(basePath, id)); + return; + case 'show': + history.push(linkToRecord(basePath, id, 'show')); + return; + case 'expand': + handleToggleExpand(event); + return; + case 'toggleSelection': + handleToggleSelection(event); + return; + default: + if (effect) history.push(effect); + return; + } + }, + [ basePath, - children, - classes = defaultClasses, - className, - expand, - hasBulkActions, - hover, + history, + handleToggleExpand, + handleToggleSelection, id, - onToggleItem, record, - resource, rowClick, - selected, - style, - selectable, - ...rest - }, - ref - ) => { - const [expanded, toggleExpanded] = useExpanded(resource, id); - const [nbColumns, setNbColumns] = useState( - computeNbColumns(expand, children, hasBulkActions) - ); - useEffect(() => { - // Fields can be hidden dynamically based on permissions; - // The expand panel must span over the remaining columns - // So we must recompute the number of columns to span on - const newNbColumns = computeNbColumns( - expand, - children, - hasBulkActions - ); - if (newNbColumns !== nbColumns) { - setNbColumns(newNbColumns); - } - }, [expand, nbColumns, children, hasBulkActions]); - - const history = useHistory(); - - const handleToggleExpand = useCallback( - event => { - toggleExpanded(); - event.stopPropagation(); - }, - [toggleExpanded] - ); - const handleToggleSelection = useCallback( - event => { - if (!selectable) return; - onToggleItem(id); - event.stopPropagation(); - }, - [id, onToggleItem, selectable] - ); - const handleClick = useCallback( - async event => { - if (!rowClick) return; - event.persist(); + ] + ); - const effect = - typeof rowClick === 'function' - ? await rowClick(id, basePath, record) - : rowClick; - switch (effect) { - case 'edit': - history.push(linkToRecord(basePath, id)); - return; - case 'show': - history.push(linkToRecord(basePath, id, 'show')); - return; - case 'expand': - handleToggleExpand(event); - return; - case 'toggleSelection': - handleToggleSelection(event); - return; - default: - if (effect) history.push(effect); - return; - } - }, - [ - basePath, - history, - handleToggleExpand, - handleToggleSelection, - id, - record, - rowClick, - ] - ); - - return ( - - - {expand && ( - - + + {expand && ( + + + + )} + {hasBulkActions && ( + + {selectable && ( + - - )} - {hasBulkActions && ( - - {selectable && ( - + )} + + )} + {React.Children.map(children, (field, index) => + isValidElement(field) ? ( + - )} - {React.Children.map(children, (field, index) => - isValidElement(field) ? ( - - ) : null - )} - - {expand && expanded && ( - - - {isValidElement(expand) - ? cloneElement(expand, { - // @ts-ignore - record, - basePath, - resource, - id: String(id), - }) - : createElement(expand, { - record, - basePath, - resource, - id: String(id), - })} - - + record={record} + {...{ field, basePath, resource }} + /> + ) : null )} - - ); - } -); + + {expand && expanded && ( + + + {isValidElement(expand) + ? cloneElement(expand, { + // @ts-ignore + record, + basePath, + resource, + id: String(id), + }) + : createElement(expand, { + record, + basePath, + resource, + id: String(id), + })} + + + )} + + ); +}); DatagridRow.propTypes = { basePath: PropTypes.string, diff --git a/packages/ra-ui-materialui/src/list/filter/FilterButtonMenuItem.tsx b/packages/ra-ui-materialui/src/list/filter/FilterButtonMenuItem.tsx index c0259bd9e46..d4de8538ef1 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterButtonMenuItem.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterButtonMenuItem.tsx @@ -2,10 +2,12 @@ import * as React from 'react'; import { forwardRef, useCallback } from 'react'; import PropTypes from 'prop-types'; import MenuItem from '@material-ui/core/MenuItem'; -import { FieldTitle } from 'ra-core'; +import { FieldTitle, useResourceContext } from 'ra-core'; const FilterButtonMenuItem = forwardRef( - ({ filter, onShow, resource }, ref) => { + (props, ref) => { + const { filter, onShow } = props; + const { resource } = useResourceContext(props); const handleShow = useCallback(() => { onShow({ source: filter.props.source, diff --git a/packages/ra-ui-materialui/src/list/filter/FilterFormInput.tsx b/packages/ra-ui-materialui/src/list/filter/FilterFormInput.tsx index 80067c0e663..cac98443652 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterFormInput.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterFormInput.tsx @@ -4,7 +4,7 @@ import IconButton from '@material-ui/core/IconButton'; import ActionHide from '@material-ui/icons/HighlightOff'; import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; -import { useTranslate } from 'ra-core'; +import { useResourceContext, useTranslate } from 'ra-core'; const emptyRecord = {}; @@ -22,7 +22,8 @@ const useStyles = makeStyles( ); const FilterFormInput = props => { - const { filterElement, handleHide, resource, variant, margin } = props; + const { filterElement, handleHide, variant, margin } = props; + const { resource } = useResourceContext(props); const translate = useTranslate(); const classes = useStyles(props); return ( From 99f79a00ee8376fd5413f9d5787165570033812c Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Mon, 2 Nov 2020 16:04:01 +0100 Subject: [PATCH 06/10] Remove calls to useResourceDefinition --- .../src/controller/details/useCreateController.ts | 5 +++-- .../ra-core/src/controller/details/useEditController.ts | 9 ++++----- .../ra-core/src/controller/details/useShowController.ts | 8 ++------ packages/ra-core/src/controller/useListController.ts | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/ra-core/src/controller/details/useCreateController.ts b/packages/ra-core/src/controller/details/useCreateController.ts index a1746548a28..3824a5f3300 100644 --- a/packages/ra-core/src/controller/details/useCreateController.ts +++ b/packages/ra-core/src/controller/details/useCreateController.ts @@ -25,7 +25,7 @@ import { useTranslate } from '../../i18n'; import useVersion from '../useVersion'; import { CRUD_CREATE } from '../../actions'; import { Record } from '../../types'; -import { useResourceContext, useResourceDefinition } from '../../core'; +import { useResourceContext } from '../../core'; export interface CreateProps = Record> { basePath?: string; @@ -102,6 +102,8 @@ export const useCreateController = < useCheckMinimumRequiredProps('Create', ['basePath', 'resource'], props); const { basePath, + hasEdit, + hasShow, record = {}, successMessage, onSuccess, @@ -110,7 +112,6 @@ export const useCreateController = < } = props; const { resource } = useResourceContext(props); - const { hasEdit, hasShow } = useResourceDefinition(resource, props); const location = useLocation(); const translate = useTranslate(); const notify = useNotify(); diff --git a/packages/ra-core/src/controller/details/useEditController.ts b/packages/ra-core/src/controller/details/useEditController.ts index 4219f76b1ee..7e7ebea98b6 100644 --- a/packages/ra-core/src/controller/details/useEditController.ts +++ b/packages/ra-core/src/controller/details/useEditController.ts @@ -22,7 +22,7 @@ import { SetTransformData, useSaveModifiers, } from '../saveModifiers'; -import { useResourceContext, useResourceDefinition } from '../../core'; +import { useResourceContext } from '../../core'; export interface EditProps { basePath?: string; @@ -95,6 +95,9 @@ export const useEditController = ( const { basePath, hasCreate, + hasEdit, + hasList, + hasShow, id, successMessage, undoable = true, @@ -103,10 +106,6 @@ export const useEditController = ( transform, } = props; const { resource } = useResourceContext(props); - const { hasEdit, hasList, hasShow } = useResourceDefinition( - resource, - props - ); const translate = useTranslate(); const notify = useNotify(); const redirect = useRedirect(); diff --git a/packages/ra-core/src/controller/details/useShowController.ts b/packages/ra-core/src/controller/details/useShowController.ts index bd0d6d28dd7..a4dd8a33ce2 100644 --- a/packages/ra-core/src/controller/details/useShowController.ts +++ b/packages/ra-core/src/controller/details/useShowController.ts @@ -7,7 +7,7 @@ import { useGetOne } from '../../dataProvider'; import { useTranslate } from '../../i18n'; import { useNotify, useRedirect, useRefresh } from '../../sideEffect'; import { CRUD_GET_ONE } from '../../actions'; -import { useResourceContext, useResourceDefinition } from '../../core'; +import { useResourceContext } from '../../core'; export interface ShowProps { basePath?: string; @@ -58,12 +58,8 @@ export const useShowController = ( props: ShowProps ): ShowControllerProps => { useCheckMinimumRequiredProps('Show', ['basePath', 'resource'], props); - const { basePath, id } = props; + const { basePath, hasCreate, hasEdit, hasList, hasShow, id } = props; const { resource } = useResourceContext(props); - const { hasCreate, hasEdit, hasList, hasShow } = useResourceDefinition( - resource, - props - ); const translate = useTranslate(); const notify = useNotify(); const redirect = useRedirect(); diff --git a/packages/ra-core/src/controller/useListController.ts b/packages/ra-core/src/controller/useListController.ts index 848bb24fc4d..0fc933b2117 100644 --- a/packages/ra-core/src/controller/useListController.ts +++ b/packages/ra-core/src/controller/useListController.ts @@ -23,7 +23,7 @@ import { Record, Exporter, } from '../types'; -import { useResourceContext, useResourceDefinition } from '../core'; +import { useResourceContext } from '../core'; export interface ListProps { // the props you can change @@ -112,13 +112,13 @@ const useListController = ( basePath, exporter = defaultExporter, filterDefaultValues, + hasCreate, sort = defaultSort, perPage = 10, filter, debounce = 500, } = props; const { resource } = useResourceContext(props); - const { hasCreate } = useResourceDefinition(resource, props); if (filter && isValidElement(filter)) { throw new Error( From 6abc4a1dff50eec0f28f8450fa533eba1214f6ed Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Mon, 2 Nov 2020 16:20:17 +0100 Subject: [PATCH 07/10] Apply Review --- packages/ra-core/src/core/useResourceContext.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ra-core/src/core/useResourceContext.ts b/packages/ra-core/src/core/useResourceContext.ts index a9f507d51c9..342f8060a62 100644 --- a/packages/ra-core/src/core/useResourceContext.ts +++ b/packages/ra-core/src/core/useResourceContext.ts @@ -2,12 +2,12 @@ import { useContext } from 'react'; import { ResourceContext, ResourceContextValue } from './ResourceContext'; /** - * Hook to read the resource informations from the ResourceContext. + * Hook to read the resource from the ResourceContext. * * Must be used within a (e.g. as a descendent of * or any reference related components). * - * @returns {ResourceContextValue} The resource informations + * @returns {ResourceContextValue} The resource */ export const useResourceContext = < ResourceInformationsType extends Partial< From 4781841bb1d632f20c352647d1bfe879fc7277a7 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Mon, 2 Nov 2020 16:28:08 +0100 Subject: [PATCH 08/10] Use the resource hook where needed --- .../button/useDeleteWithConfirmController.tsx | 3 ++- .../controller/button/useDeleteWithUndoController.tsx | 3 ++- .../src/button/BulkDeleteWithConfirmButton.tsx | 3 ++- .../src/button/BulkDeleteWithUndoButton.tsx | 5 +++-- .../src/button/DeleteWithConfirmButton.tsx | 4 ++-- .../src/button/DeleteWithUndoButton.tsx | 4 ++-- packages/ra-ui-materialui/src/button/ExportButton.tsx | 3 ++- packages/ra-ui-materialui/src/detail/CreateActions.js | 10 ++++++++-- packages/ra-ui-materialui/src/detail/EditActions.tsx | 11 +++++++++-- packages/ra-ui-materialui/src/detail/ShowActions.tsx | 11 +++++++++-- packages/ra-ui-materialui/src/list/ListActions.tsx | 6 ++++-- 11 files changed, 45 insertions(+), 18 deletions(-) diff --git a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx index bfc920e9970..d2bab393971 100644 --- a/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx +++ b/packages/ra-core/src/controller/button/useDeleteWithConfirmController.tsx @@ -141,7 +141,8 @@ export interface UseDeleteWithConfirmControllerParams { basePath?: string; record?: Record; redirect?: RedirectionSideEffect; - resource: string; + // @deprecated. This hook get the resource from the context + resource?: string; onClick?: ReactEventHandler; onSuccess?: OnSuccess; onFailure?: OnFailure; diff --git a/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx b/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx index aa54c5a7797..6864bdd68e8 100644 --- a/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx +++ b/packages/ra-core/src/controller/button/useDeleteWithUndoController.tsx @@ -111,7 +111,8 @@ export interface UseDeleteWithUndoControllerParams { basePath?: string; record?: Record; redirect?: RedirectionSideEffect; - resource: string; + // @deprecated. This hook get the resource from the context + resource?: string; onClick?: ReactEventHandler; onSuccess?: OnSuccess; onFailure?: OnFailure; diff --git a/packages/ra-ui-materialui/src/button/BulkDeleteWithConfirmButton.tsx b/packages/ra-ui-materialui/src/button/BulkDeleteWithConfirmButton.tsx index d3ec2a50176..730b2f2d670 100644 --- a/packages/ra-ui-materialui/src/button/BulkDeleteWithConfirmButton.tsx +++ b/packages/ra-ui-materialui/src/button/BulkDeleteWithConfirmButton.tsx @@ -12,6 +12,7 @@ import { useNotify, useUnselectAll, CRUD_DELETE_MANY, + useResourceContext, } from 'ra-core'; import Confirm from '../layout/Confirm'; @@ -45,7 +46,6 @@ const BulkDeleteWithConfirmButton: FC = props icon = defaultIcon, label, onClick, - resource, selectedIds, ...rest } = props; @@ -55,6 +55,7 @@ const BulkDeleteWithConfirmButton: FC = props const unselectAll = useUnselectAll(); const refresh = useRefresh(); const translate = useTranslate(); + const { resource } = useResourceContext(props); const [deleteMany, { loading }] = useDeleteMany(resource, selectedIds, { action: CRUD_DELETE_MANY, onSuccess: () => { diff --git a/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx b/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx index d6988da46da..d05caf2b8f1 100644 --- a/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx +++ b/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx @@ -10,6 +10,7 @@ import { useNotify, useUnselectAll, CRUD_DELETE_MANY, + useResourceContext, } from 'ra-core'; import Button, { ButtonProps } from './Button'; @@ -38,7 +39,6 @@ const BulkDeleteWithUndoButton: FC = props => { icon, label, onClick, - resource, selectedIds, ...rest } = props; @@ -46,6 +46,7 @@ const BulkDeleteWithUndoButton: FC = props => { const notify = useNotify(); const unselectAll = useUnselectAll(); const refresh = useRefresh(); + const { resource } = useResourceContext(props); const [deleteMany, { loading }] = useDeleteMany(resource, selectedIds, { action: CRUD_DELETE_MANY, onSuccess: () => { @@ -107,7 +108,7 @@ BulkDeleteWithUndoButton.propTypes = { basePath: PropTypes.string, classes: PropTypes.object, label: PropTypes.string, - resource: PropTypes.string.isRequired, + resource: PropTypes.string, selectedIds: PropTypes.arrayOf(PropTypes.any).isRequired, icon: PropTypes.element, }; diff --git a/packages/ra-ui-materialui/src/button/DeleteWithConfirmButton.tsx b/packages/ra-ui-materialui/src/button/DeleteWithConfirmButton.tsx index 5f12ad2fdcd..dc1e3a4ba7d 100644 --- a/packages/ra-ui-materialui/src/button/DeleteWithConfirmButton.tsx +++ b/packages/ra-ui-materialui/src/button/DeleteWithConfirmButton.tsx @@ -18,6 +18,7 @@ import { useDeleteWithConfirmController, OnSuccess, OnFailure, + useResourceContext, } from 'ra-core'; import Confirm from '../layout/Confirm'; @@ -34,7 +35,6 @@ const DeleteWithConfirmButton: FC = props => { label = 'ra.action.delete', onClick, record, - resource, redirect = 'list', onSuccess, onFailure, @@ -49,7 +49,6 @@ const DeleteWithConfirmButton: FC = props => { handleDialogClose, handleDelete, } = useDeleteWithConfirmController({ - resource, record, redirect, basePath, @@ -57,6 +56,7 @@ const DeleteWithConfirmButton: FC = props => { onSuccess, onFailure, }); + const { resource } = useResourceContext(props); return ( diff --git a/packages/ra-ui-materialui/src/button/DeleteWithUndoButton.tsx b/packages/ra-ui-materialui/src/button/DeleteWithUndoButton.tsx index 1aaf80c8af8..d6835d0e094 100644 --- a/packages/ra-ui-materialui/src/button/DeleteWithUndoButton.tsx +++ b/packages/ra-ui-materialui/src/button/DeleteWithUndoButton.tsx @@ -11,6 +11,7 @@ import { useDeleteWithUndoController, OnSuccess, OnFailure, + useResourceContext, } from 'ra-core'; import Button, { ButtonProps } from './Button'; @@ -22,7 +23,6 @@ const DeleteWithUndoButton: FC = props => { className, icon = defaultIcon, onClick, - resource, record, basePath, redirect = 'list', @@ -32,7 +32,6 @@ const DeleteWithUndoButton: FC = props => { } = props; const classes = useStyles(props); const { loading, handleDelete } = useDeleteWithUndoController({ - resource, record, basePath, redirect, @@ -40,6 +39,7 @@ const DeleteWithUndoButton: FC = props => { onSuccess, onFailure, }); + const { resource } = useResourceContext(props); return (