diff --git a/src/useOnSubmit.test.tsx b/src/useOnSubmit.test.tsx index 6f8d4462..d2da8af2 100644 --- a/src/useOnSubmit.test.tsx +++ b/src/useOnSubmit.test.tsx @@ -2,11 +2,15 @@ import * as React from 'react'; import { jest } from '@jest/globals'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { render, waitFor } from '@testing-library/react'; -import type { CreateResult, RaRecord, UpdateResult } from 'react-admin'; -import { DataProviderContext, testDataProvider } from 'react-admin'; +import { + type CreateResult, + DataProviderContext, + type RaRecord, + type UpdateResult, + testDataProvider, +} from 'react-admin'; import { MemoryRouter, Route, Routes } from 'react-router-dom'; -import useOnSubmit from './useOnSubmit.js'; import schemaAnalyzer from './hydra/schemaAnalyzer.js'; import { API_FIELDS_DATA } from './__fixtures__/parsedData.js'; @@ -23,6 +27,16 @@ const onSubmitProps = { }; jest.mock('./getIdentifierValue.js'); +const notify = jest.fn(); +const reactAdminActual = jest.requireActual('react-admin') as Record< + string, + unknown +>; +jest.mock('react-admin', () => ({ + __esModule: true, + ...reactAdminActual, + useNotify: () => notify, +})); test.each([ { @@ -41,6 +55,7 @@ test.each([ ])( 'Call create with file input ($name)', async (values: Omit) => { + const { default: useOnSubmit } = await import('./useOnSubmit.js'); let save; const Dummy = () => { const onSubmit = useOnSubmit(onSubmitProps); @@ -93,6 +108,7 @@ test.each([ cover: null, }, ])('Call update without file inputs ($name)', async (values: RaRecord) => { + const { default: useOnSubmit } = await import('./useOnSubmit.js'); let save; const Dummy = () => { const onSubmit = useOnSubmit(onSubmitProps); @@ -125,3 +141,48 @@ test.each([ }); }); }); + +test.each([{ name: 'Required' }, null])( + 'notification handling on validation errors (%s)', + async (submissionErrors) => { + const { default: useOnSubmit } = await import('./useOnSubmit.js'); + notify.mockClear(); + dataProvider.create = jest.fn(() => + Promise.reject(new Error('Service Unavailable')), + ); + + let save; + const Dummy = () => { + const onSubmit = useOnSubmit({ + ...onSubmitProps, + schemaAnalyzer: { + ...onSubmitProps.schemaAnalyzer, + getSubmissionErrors: () => submissionErrors, + }, + }); + save = onSubmit; + return ; + }; + + render( + + + + + } /> + + + + , + ); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const errors = await save({ author: 'Author 1' }); + + await waitFor(() => { + expect(dataProvider.create).toHaveBeenCalled(); + }); + (submissionErrors ? expect(notify).not : expect(notify)).toHaveBeenCalled(); + expect(errors).toEqual(submissionErrors ?? {}); + }, +); diff --git a/src/useOnSubmit.ts b/src/useOnSubmit.ts index dbc2a8ef..9b4770bf 100644 --- a/src/useOnSubmit.ts +++ b/src/useOnSubmit.ts @@ -92,11 +92,14 @@ const useOnSubmit = ({ const failure = mutationOptions?.onError ?? ((error: string | Error) => { - let message = 'ra.notification.http_error'; - if (!submissionErrors) { - message = - typeof error === 'string' ? error : error.message || message; + // Notification will be handled by the useNotifyIsFormInvalid hook. + if (submissionErrors) { + return; } + const message = + typeof error === 'string' + ? error + : error.message || 'ra.notification.http_error'; let errorMessage; if (typeof error === 'string') { errorMessage = error; @@ -116,10 +119,7 @@ const useOnSubmit = ({ }, {}, ); - if (submissionErrors) { - return submissionErrors; - } - return {}; + return submissionErrors ?? {}; } }, [