-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Add ReferenceManyCountBase #10808
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
Add ReferenceManyCountBase #10808
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| import * as React from 'react'; | ||
| import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; | ||
| import { RecordContextProvider } from '../record'; | ||
| import { DataProviderContext } from '../../dataProvider'; | ||
| import { ResourceContextProvider } from '../../core'; | ||
| import { TestMemoryRouter } from '../../routing'; | ||
| import { ReferenceManyCountBase } from './ReferenceManyCountBase'; | ||
|
|
||
| export default { | ||
| title: 'ra-core/controller/field/ReferenceManyCountBase', | ||
| excludeStories: ['Wrapper'], | ||
| }; | ||
|
|
||
| const post = { | ||
| id: 1, | ||
| title: 'Lorem Ipsum', | ||
| }; | ||
| const comments = [ | ||
| { id: 1, post_id: 1, is_published: true }, | ||
| { id: 2, post_id: 1, is_published: true }, | ||
| { id: 3, post_id: 1, is_published: false }, | ||
| { id: 4, post_id: 2, is_published: true }, | ||
| { id: 5, post_id: 2, is_published: false }, | ||
| ]; | ||
|
|
||
| export const Wrapper = ({ dataProvider, children }) => ( | ||
| <TestMemoryRouter> | ||
| <DataProviderContext.Provider value={dataProvider}> | ||
| <QueryClientProvider | ||
| client={ | ||
| new QueryClient({ | ||
| defaultOptions: { | ||
| queries: { | ||
| retry: false, | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
| > | ||
| <ResourceContextProvider value="posts"> | ||
| <RecordContextProvider value={post}> | ||
| {children} | ||
| </RecordContextProvider> | ||
| </ResourceContextProvider> | ||
| </QueryClientProvider> | ||
| </DataProviderContext.Provider> | ||
| </TestMemoryRouter> | ||
| ); | ||
|
|
||
| export const Basic = () => ( | ||
| <Wrapper | ||
| dataProvider={{ | ||
| getManyReference: () => | ||
| Promise.resolve({ | ||
| data: [comments.filter(c => c.post_id === 1)[0]], | ||
| total: comments.filter(c => c.post_id === 1).length, | ||
| }), | ||
| }} | ||
| > | ||
| <ReferenceManyCountBase reference="comments" target="post_id" /> | ||
| </Wrapper> | ||
| ); | ||
|
|
||
| export const LoadingState = () => ( | ||
| <Wrapper dataProvider={{ getManyReference: () => new Promise(() => {}) }}> | ||
| <ReferenceManyCountBase | ||
| reference="comments" | ||
| target="post_id" | ||
| loading="loading..." | ||
| /> | ||
| </Wrapper> | ||
| ); | ||
|
|
||
| export const ErrorState = () => ( | ||
| <Wrapper | ||
| dataProvider={{ | ||
| getManyReference: () => Promise.reject(new Error('problem')), | ||
| }} | ||
| > | ||
| <ReferenceManyCountBase | ||
| reference="comments" | ||
| target="post_id" | ||
| error="Error!" | ||
| /> | ||
| </Wrapper> | ||
| ); | ||
|
|
||
| export const Filter = () => ( | ||
| <Wrapper | ||
| dataProvider={{ | ||
| getManyReference: (resource, params) => | ||
| Promise.resolve({ | ||
| data: comments | ||
| .filter(c => c.post_id === 1) | ||
| .filter(post => | ||
| Object.keys(params.filter).every( | ||
| key => post[key] === params.filter[key] | ||
| ) | ||
| ), | ||
| total: comments | ||
| .filter(c => c.post_id === 1) | ||
| .filter(post => | ||
| Object.keys(params.filter).every( | ||
| key => post[key] === params.filter[key] | ||
| ) | ||
| ).length, | ||
| }), | ||
| }} | ||
| > | ||
| <ReferenceManyCountBase | ||
| reference="comments" | ||
| target="post_id" | ||
| filter={{ is_published: true }} | ||
| /> | ||
| </Wrapper> | ||
| ); | ||
|
|
||
| export const Slow = () => ( | ||
| <Wrapper | ||
| dataProvider={{ | ||
| getManyReference: () => | ||
| new Promise(resolve => | ||
| setTimeout( | ||
| () => | ||
| resolve({ | ||
| data: [ | ||
| comments.filter(c => c.post_id === 1)[0], | ||
| ], | ||
| total: comments.filter(c => c.post_id === 1) | ||
| .length, | ||
| }), | ||
| 2000 | ||
| ) | ||
| ), | ||
| }} | ||
| > | ||
| <ReferenceManyCountBase | ||
| reference="comments" | ||
| target="post_id" | ||
| loading="Loading..." | ||
| /> | ||
| </Wrapper> | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import React from 'react'; | ||
| import { | ||
| useReferenceManyFieldController, | ||
| type UseReferenceManyFieldControllerParams, | ||
| } from './useReferenceManyFieldController'; | ||
| import { useTimeout } from '../../util/hooks'; | ||
|
|
||
| /** | ||
| * Fetch and render the number of records related to the current one | ||
| * | ||
| * Relies on dataProvider.getManyReference() returning a total property | ||
| * | ||
| * @example // Display the number of comments for the current post | ||
| * <ReferenceManyCountBase reference="comments" target="post_id" /> | ||
| * | ||
| * @example // Display the number of published comments for the current post | ||
| * <ReferenceManyCountBase reference="comments" target="post_id" filter={{ is_published: true }} /> | ||
| */ | ||
| export const ReferenceManyCountBase = (props: ReferenceManyCountBaseProps) => { | ||
| const { loading = null, error = null, timeout = 1000, ...rest } = props; | ||
| const oneSecondHasPassed = useTimeout(timeout); | ||
|
|
||
| const { | ||
| isPending, | ||
| error: fetchError, | ||
| total, | ||
| } = useReferenceManyFieldController<any, any>({ | ||
| ...rest, | ||
| page: 1, | ||
| perPage: 1, | ||
| }); | ||
|
|
||
| return ( | ||
| <> | ||
| {isPending | ||
| ? oneSecondHasPassed | ||
| ? loading | ||
| : null | ||
| : fetchError | ||
| ? error | ||
| : total} | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export interface ReferenceManyCountBaseProps | ||
| extends UseReferenceManyFieldControllerParams { | ||
| timeout?: number; | ||
| loading?: React.ReactNode; | ||
| error?: React.ReactNode; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,8 @@ | ||
| import React from 'react'; | ||
| import { | ||
| useReferenceManyFieldController, | ||
| useRecordContext, | ||
| useTimeout, | ||
| useCreatePath, | ||
| ReferenceManyCountBase, | ||
| SortPayload, | ||
| RaRecord, | ||
| } from 'ra-core'; | ||
|
|
@@ -15,6 +14,7 @@ import { | |
| useThemeProps, | ||
| } from '@mui/material/styles'; | ||
| import ErrorIcon from '@mui/icons-material/Error'; | ||
| import get from 'lodash/get'; | ||
|
|
||
| import { FieldProps } from './types'; | ||
| import { sanitizeFieldRestProps } from './sanitizeFieldRestProps'; | ||
|
|
@@ -51,39 +51,20 @@ export const ReferenceManyCount = <RecordType extends RaRecord = RaRecord>( | |
| link, | ||
| resource, | ||
| source = 'id', | ||
| timeout = 1000, | ||
| ...rest | ||
| } = props; | ||
| const record = useRecordContext(props); | ||
| const oneSecondHasPassed = useTimeout(timeout); | ||
| const createPath = useCreatePath(); | ||
|
|
||
| const { isPending, error, total } = | ||
| useReferenceManyFieldController<RecordType>({ | ||
| filter, | ||
| sort, | ||
| page: 1, | ||
| perPage: 1, | ||
| record, | ||
| reference, | ||
| // @ts-ignore remove when #8491 is released | ||
| resource, | ||
| source, | ||
| target, | ||
| }); | ||
|
|
||
| const body = isPending ? ( | ||
| oneSecondHasPassed ? ( | ||
| <CircularProgress size={14} /> | ||
| ) : ( | ||
| '' | ||
| ) | ||
| ) : error ? ( | ||
| <ErrorIcon color="error" fontSize="small" titleAccess="error" /> | ||
| ) : ( | ||
| total | ||
| const body = ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just put it inside the main JSX directly? This
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's used twice |
||
| <ReferenceManyCountBase | ||
| {...props} | ||
| loading={<CircularProgress size={14} />} | ||
| error={ | ||
| <ErrorIcon color="error" fontSize="small" titleAccess="error" /> | ||
| } | ||
| /> | ||
| ); | ||
|
|
||
| return ( | ||
| <StyledTypography | ||
| className={clsx(className, ReferenceManyCountClasses.root)} | ||
|
|
@@ -101,7 +82,7 @@ export const ReferenceManyCount = <RecordType extends RaRecord = RaRecord>( | |
| }), | ||
| search: `filter=${JSON.stringify({ | ||
| ...(filter || {}), | ||
| [target]: record[source], | ||
| [target]: get(record, source), | ||
| })}`, | ||
| }} | ||
| onClick={e => e.stopPropagation()} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,6 @@ import { | |
| useReferenceManyFieldController, | ||
| ListContextProvider, | ||
| ResourceContextProvider, | ||
| useRecordContext, | ||
| RaRecord, | ||
| UseReferenceManyFieldControllerParams, | ||
| } from 'ra-core'; | ||
|
|
@@ -69,6 +68,7 @@ export const ReferenceManyField = < | |
| page = 1, | ||
| pagination = null, | ||
| perPage = 25, | ||
| record, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lint warning now
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
| reference, | ||
| resource, | ||
| sort = defaultSort, | ||
|
|
@@ -77,7 +77,6 @@ export const ReferenceManyField = < | |
| target, | ||
| queryOptions, | ||
| } = props; | ||
| const record = useRecordContext(props); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no longer necessary since the controller hook does it. |
||
|
|
||
| const controllerProps = useReferenceManyFieldController< | ||
| RecordType, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was done by callers of the hook. This logic should better be in ra-core.