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] Make types more strict in ra-core #9741

Merged
merged 9 commits into from
Mar 25, 2024
Merged
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
4 changes: 2 additions & 2 deletions packages/ra-core/src/auth/useGetIdentity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ export interface UseGetIdentityOptions<ErrorType extends Error = Error>
> {
onSuccess?: (data: UserIdentity) => void;
onError?: (err: Error) => void;
onSettled?: (data?: UserIdentity, error?: Error) => void;
onSettled?: (data?: UserIdentity, error?: Error | null) => void;
}

export type UseGetIdentityResult<ErrorType = Error> = QueryObserverResult<
UserIdentity,
ErrorType
> & {
identity: UserIdentity | null;
identity: UserIdentity | undefined;
};

export default useGetIdentity;
Expand Down
10 changes: 5 additions & 5 deletions packages/ra-core/src/auth/useHandleAuthCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
import { useLocation } from 'react-router';
import { useRedirect } from '../routing';
import { AuthRedirectResult, AuthProvider } from '../types';
import { AuthRedirectResult } from '../types';
import useAuthProvider from './useAuthProvider';
import { useEvent } from '../util';

Expand Down Expand Up @@ -90,14 +90,14 @@ export const useHandleAuthCallback = (
export const PreviousLocationStorageKey = '@react-admin/nextPathname';

export type UseHandleAuthCallbackOptions = Omit<
UseQueryOptions<ReturnType<AuthProvider['handleCallback']>>,
UseQueryOptions<AuthRedirectResult | void>,
'queryKey' | 'queryFn'
> & {
onSuccess?: (data: ReturnType<AuthProvider['handleCallback']>) => void;
onSuccess?: (data: AuthRedirectResult | void) => void;
onError?: (err: Error) => void;
onSettled?: (
data?: ReturnType<AuthProvider['handleCallback']>,
error?: Error
data?: AuthRedirectResult | void,
error?: Error | null
) => void;
};

Expand Down
4 changes: 2 additions & 2 deletions packages/ra-core/src/auth/usePermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ export interface UsePermissionsOptions<PermissionsType = any, ErrorType = Error>
> {
onSuccess?: (data: PermissionsType) => void;
onError?: (err: ErrorType) => void;
onSettled?: (data?: PermissionsType, error?: ErrorType) => void;
onSettled?: (data?: PermissionsType, error?: ErrorType | null) => void;
}

export type UsePermissionsResult<
PermissionsType = any,
ErrorType = Error
> = QueryObserverResult<PermissionsType, ErrorType> & {
permissions: PermissionsType | null;
permissions: PermissionsType | undefined;
};

const noop = () => {};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ describe('<useReferenceArrayFieldController />', () => {
it('should set the isLoading prop to true when related records are not yet fetched', () => {
const children = jest.fn().mockReturnValue('child');
render(
<CoreAdminContext dataProvider={testDataProvider()}>
<CoreAdminContext
dataProvider={testDataProvider({
getMany: () => new Promise(() => {}),
})}
>
<ReferenceArrayFieldController
resource="foo"
reference="bar"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,11 @@ describe('useReferenceManyFieldController', () => {
}}
/>
);
const dataProvider = testDataProvider();
const getManyReference = jest.spyOn(dataProvider, 'getManyReference');
const dataProvider = testDataProvider({
getManyReference: jest
.fn()
.mockResolvedValue({ data: [], total: 0 }),
});
render(
<CoreAdminContext dataProvider={dataProvider}>
<ReferenceManyFieldController
Expand All @@ -312,8 +315,8 @@ describe('useReferenceManyFieldController', () => {
await waitFor(() => new Promise(resolve => setTimeout(resolve, 600)));

// Called twice: on load and on filter changes
expect(getManyReference).toHaveBeenCalledTimes(2);
expect(getManyReference).toHaveBeenCalledWith('books', {
expect(dataProvider.getManyReference).toHaveBeenCalledTimes(2);
expect(dataProvider.getManyReference).toHaveBeenCalledWith('books', {
target: 'author_id',
id: 123,
filter: { q: 'hello' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@ describe('useReferenceArrayInputController', () => {
<div>{isLoading.toString()}</div>
));
render(
<CoreAdminContext dataProvider={testDataProvider()}>
<CoreAdminContext
dataProvider={testDataProvider({
getMany: jest.fn().mockResolvedValue({ data: [] }),
getList: jest
.fn()
.mockResolvedValue({ data: [], total: 0 }),
})}
>
<Form
defaultValues={{ tag_ids: [1, 2] }}
onSubmit={jest.fn()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ describe('useInfiniteListController', () => {

it('should reset page when enabled is set to false', async () => {
const children = jest.fn().mockReturnValue(<span>children</span>);
const dataProvider = testDataProvider();
const dataProvider = testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
});
const props = { ...defaultProps, children };
render(
<CoreAdminContext dataProvider={dataProvider}>
Expand Down Expand Up @@ -150,7 +152,9 @@ describe('useInfiniteListController', () => {

render(
<CoreAdminContext
dataProvider={testDataProvider()}
dataProvider={testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
})}
store={store}
>
<InfiniteListController {...props} />
Expand Down Expand Up @@ -192,7 +196,10 @@ describe('useInfiniteListController', () => {
]}
>
<CoreAdminContext
dataProvider={testDataProvider()}
dataProvider={testDataProvider({
getList: () =>
Promise.resolve({ data: [], total: 0 }),
})}
store={store}
>
<InfiniteListController {...props} />
Expand Down Expand Up @@ -311,7 +318,11 @@ describe('useInfiniteListController', () => {
};

render(
<CoreAdminContext>
<CoreAdminContext
dataProvider={testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
})}
>
<InfiniteListController {...props} />
</CoreAdminContext>
);
Expand All @@ -333,7 +344,11 @@ describe('useInfiniteListController', () => {

it('should support to sync calls', async () => {
render(
<CoreAdminContext>
<CoreAdminContext
dataProvider={testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
})}
>
<InfiniteListController {...defaultProps}>
{({ displayedFilters, showFilter }) => (
<>
Expand Down
25 changes: 20 additions & 5 deletions packages/ra-core/src/controller/list/useListController.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ describe('useListController', () => {

it('should reset page when enabled is set to false', async () => {
const children = jest.fn().mockReturnValue(<span>children</span>);
const dataProvider = testDataProvider();
const dataProvider = testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
});
const props = { ...defaultProps, children };
render(
<CoreAdminContext dataProvider={dataProvider}>
Expand Down Expand Up @@ -134,7 +136,9 @@ describe('useListController', () => {

render(
<CoreAdminContext
dataProvider={testDataProvider()}
dataProvider={testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
})}
store={store}
>
<ListController {...props} />
Expand Down Expand Up @@ -176,7 +180,10 @@ describe('useListController', () => {
]}
>
<CoreAdminContext
dataProvider={testDataProvider()}
dataProvider={testDataProvider({
getList: () =>
Promise.resolve({ data: [], total: 0 }),
})}
store={store}
>
<ListController {...props} />
Expand Down Expand Up @@ -289,7 +296,11 @@ describe('useListController', () => {
};

render(
<CoreAdminContext>
<CoreAdminContext
dataProvider={testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
})}
>
<ListController {...props} />
</CoreAdminContext>
);
Expand All @@ -311,7 +322,11 @@ describe('useListController', () => {

it('should support to sync calls', async () => {
render(
<CoreAdminContext>
<CoreAdminContext
dataProvider={testDataProvider({
getList: () => Promise.resolve({ data: [], total: 0 }),
})}
>
<ListController {...defaultProps}>
{({ displayedFilters, showFilter }) => (
<>
Expand Down
5 changes: 4 additions & 1 deletion packages/ra-core/src/controller/list/useRecordSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export const useRecordSelection = <RecordType extends RaRecord = any>(
}
] => {
const storeKey = `${resource}.selectedIds`;
const [ids, setIds] = useStore(storeKey, defaultSelection);
const [ids, setIds] = useStore<RecordType['id'][]>(
storeKey,
defaultSelection
);
const reset = useRemoveFromStore(storeKey);

const selectionModifiers = useMemo(
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/core/CoreAdminUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ export const CoreAdminUI = (props: CoreAdminUIProps) => {
loginPage: LoginPage = false,
authCallbackPage: LoginCallbackPage = false,
ready = Ready,
title,
title = 'React Admin',
requireAuth = false,
} = props;

Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/core/HasDashboardContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext, useContext } from 'react';

export const HasDashboardContext = createContext<boolean>(undefined);
export const HasDashboardContext = createContext<boolean>(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you change the type instead and make useHasDashboard fail loudly (https://blog.codeminer42.com/you-are-using-react-context-wrong/)?


export const HasDashboardContextProvider = HasDashboardContext.Provider;

Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/core/ResourceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ import { createContext } from 'react';
*/
export const ResourceContext = createContext<ResourceContextValue>(undefined);

export type ResourceContextValue = string;
export type ResourceContextValue = string | undefined;
24 changes: 13 additions & 11 deletions packages/ra-core/src/core/SourceContext.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { createContext, useContext } from 'react';

export type SourceContextValue = {
/*
* Returns the source for a field or input, modified according to the context.
*/
getSource: (source: string) => string;
/*
* Returns the label for a field or input, modified according to the context. Returns a translation key.
*/
getLabel: (source: string) => string;
};
export type SourceContextValue =
| {
/*
* Returns the source for a field or input, modified according to the context.
*/
getSource: (source: string) => string;
/*
* Returns the label for a field or input, modified according to the context. Returns a translation key.
*/
getLabel: (source: string) => string;
}
| undefined;

/**
* Context that provides a function that accept a source and return a modified source (prefixed, suffixed, etc.) for fields and inputs.
Expand All @@ -28,7 +30,7 @@ export type SourceContextValue = {
* );
* };
*/
export const SourceContext = createContext<SourceContextValue>(null);
export const SourceContext = createContext<SourceContextValue>(undefined);

export const SourceContextProvider = SourceContext.Provider;

Expand Down
4 changes: 2 additions & 2 deletions packages/ra-core/src/core/useResourceDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const useResourceDefinition = <
OptionsType extends ResourceOptions = any
>(
props?: UseResourceDefinitionOptions
): ResourceDefinition<OptionsType> => {
): Omit<ResourceDefinition<OptionsType>, 'name'> => {
const resource = useResourceContext(props);
const resourceDefinitions = useResourceDefinitions();
const { hasCreate, hasEdit, hasList, hasShow, recordRepresentation } =
Expand All @@ -46,7 +46,7 @@ export const useResourceDefinition = <
hasShow,
recordRepresentation,
},
resourceDefinitions[resource]
resource ? resourceDefinitions[resource] : {}
);
}, [
resource,
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/dataProvider/DataProviderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createContext } from 'react';

import { DataProvider } from '../types';

const DataProviderContext = createContext<DataProvider>(null);
const DataProviderContext = createContext<DataProvider | null>(null);

DataProviderContext.displayName = 'DataProviderContext';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('convertLegacyDataProvider', () => {
it('should return a function allowing old style calls', () => {
const legacyProvider = jest.fn();
const convertedProvider = convertLegacyDataProvider(legacyProvider);
convertedProvider('GET_LIST', 'posts', { filter: { foo: 'bar' } });
convertedProvider.getList('posts', { filter: { foo: 'bar' } });
expect(legacyProvider).toHaveBeenCalledWith('GET_LIST', 'posts', {
filter: { foo: 'bar' },
});
Expand Down
14 changes: 2 additions & 12 deletions packages/ra-core/src/dataProvider/convertLegacyDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,7 @@ import {
UPDATE_MANY,
} from './dataFetchActions';
import { LegacyDataProvider, DataProvider } from '../types';

const defaultDataProvider = () => Promise.resolve();
defaultDataProvider.create = () => Promise.resolve(null);
defaultDataProvider.delete = () => Promise.resolve(null);
defaultDataProvider.deleteMany = () => Promise.resolve(null);
defaultDataProvider.getList = () => Promise.resolve(null);
defaultDataProvider.getMany = () => Promise.resolve(null);
defaultDataProvider.getManyReference = () => Promise.resolve(null);
defaultDataProvider.getOne = () => Promise.resolve(null);
defaultDataProvider.update = () => Promise.resolve(null);
defaultDataProvider.updateMany = () => Promise.resolve(null);
import { defaultDataProvider } from './defaultDataProvider';

const fetchMap = {
create: CREATE,
Expand All @@ -35,7 +25,7 @@ const fetchMap = {
};

interface ConvertedDataProvider extends DataProvider {
(type: string, resource: string, params: any): Promise<any>;
[key: string]: (...params: any) => Promise<any>;
}
/**
* Turn a function-based dataProvider to an object-based one
Expand Down
Loading
Loading