From 819cfd002e2492be1b9f415051f4d88c0279d903 Mon Sep 17 00:00:00 2001
From: Gildas <1122076+djhi@users.noreply.github.com>
Date: Wed, 16 Jul 2025 10:10:45 +0200
Subject: [PATCH 1/2] Fix `useEditController` does not pass all variables to
`useUpdate`
---
.../edit/useEditController.spec.tsx | 66 ++++++++++++++++---
.../src/controller/edit/useEditController.ts | 2 +
2 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/packages/ra-core/src/controller/edit/useEditController.spec.tsx b/packages/ra-core/src/controller/edit/useEditController.spec.tsx
index 221d4f9b41a..da67beec784 100644
--- a/packages/ra-core/src/controller/edit/useEditController.spec.tsx
+++ b/packages/ra-core/src/controller/edit/useEditController.spec.tsx
@@ -420,15 +420,31 @@ describe('useEditController', () => {
mutationMode="pessimistic"
mutationOptions={{ onSuccess }}
>
- {({ save }) => {
+ {({ record, save }) => {
saveCallback = save;
- return
;
+ return {record?.id}
;
}}
);
+ await screen.findByText('12');
await act(async () => saveCallback({ foo: 'bar' }));
- await waitFor(() => expect(onSuccess).toHaveBeenCalled());
+ await waitFor(() =>
+ expect(onSuccess).toHaveBeenCalledWith(
+ {
+ id: 12,
+ foo: 'bar',
+ },
+ {
+ id: 12,
+ data: { foo: 'bar' },
+ previousData: { id: 12 },
+ resource: 'posts',
+ meta: undefined,
+ },
+ { snapshot: expect.any(Array) }
+ )
+ );
expect(notificationsSpy).toEqual([]);
});
@@ -458,15 +474,31 @@ describe('useEditController', () => {
mutationMode="optimistic"
mutationOptions={{ onSuccess }}
>
- {({ save }) => {
+ {({ record, save }) => {
saveCallback = save;
- return ;
+ return {record?.id}
;
}}
);
+ await screen.findByText('12');
await act(async () => saveCallback({ foo: 'bar' }));
- await waitFor(() => expect(onSuccess).toHaveBeenCalled());
+ await waitFor(() =>
+ expect(onSuccess).toHaveBeenCalledWith(
+ {
+ id: 12,
+ foo: 'bar',
+ },
+ {
+ id: 12,
+ data: { foo: 'bar' },
+ previousData: { id: 12 },
+ resource: 'posts',
+ meta: undefined,
+ },
+ { snapshot: expect.any(Array) }
+ )
+ );
expect(notificationsSpy).toEqual([]);
});
@@ -495,15 +527,31 @@ describe('useEditController', () => {
{...defaultProps}
mutationOptions={{ onSuccess }}
>
- {({ save }) => {
+ {({ record, save }) => {
saveCallback = save;
- return ;
+ return {record?.id}
;
}}
);
+ await screen.findByText('12');
await act(async () => saveCallback({ foo: 'bar' }));
- await waitFor(() => expect(onSuccess).toHaveBeenCalled());
+ await waitFor(() =>
+ expect(onSuccess).toHaveBeenCalledWith(
+ {
+ id: 12,
+ foo: 'bar',
+ },
+ {
+ id: 12,
+ data: { foo: 'bar' },
+ previousData: { id: 12 },
+ resource: 'posts',
+ meta: undefined,
+ },
+ { snapshot: expect.any(Array) }
+ )
+ );
expect(notificationsSpy).toEqual([]);
});
diff --git a/packages/ra-core/src/controller/edit/useEditController.ts b/packages/ra-core/src/controller/edit/useEditController.ts
index d345d5227ab..96d8c25a7f1 100644
--- a/packages/ra-core/src/controller/edit/useEditController.ts
+++ b/packages/ra-core/src/controller/edit/useEditController.ts
@@ -262,6 +262,7 @@ export const useEditController = <
id,
data,
meta: metaFromSave ?? mutationMeta,
+ previousData: record,
},
{
onError: onErrorFromSave,
@@ -277,6 +278,7 @@ export const useEditController = <
[
id,
mutationMeta,
+ record,
resource,
transform,
update,
From cd4a7199e506d408481e6141e6751865d6423ce4 Mon Sep 17 00:00:00 2001
From: Gildas <1122076+djhi@users.noreply.github.com>
Date: Wed, 16 Jul 2025 10:29:34 +0200
Subject: [PATCH 2/2] Fix tests
---
.../src/controller/edit/EditBase.spec.tsx | 24 ++++++++++++-------
.../src/button/SaveButton.spec.tsx | 4 ++++
.../ra-ui-materialui/src/detail/Edit.spec.tsx | 10 +++++++-
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/packages/ra-core/src/controller/edit/EditBase.spec.tsx b/packages/ra-core/src/controller/edit/EditBase.spec.tsx
index ae241a8af1e..7635a69a072 100644
--- a/packages/ra-core/src/controller/edit/EditBase.spec.tsx
+++ b/packages/ra-core/src/controller/edit/EditBase.spec.tsx
@@ -13,8 +13,8 @@ import {
describe('EditBase', () => {
it('should give access to the save function', async () => {
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
update: jest.fn((_, { id, data, previousData }) =>
Promise.resolve({ data: { id, ...previousData, ...data } })
@@ -44,8 +44,8 @@ describe('EditBase', () => {
it('should allow to override the onSuccess function', async () => {
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
update: jest.fn((_, { id, data, previousData }) =>
Promise.resolve({ data: { id, ...previousData, ...data } })
@@ -75,7 +75,9 @@ describe('EditBase', () => {
{
id: 12,
data: { test: 'test' },
+ previousData: { id: 12, test: 'previous' },
resource: 'posts',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -84,8 +86,8 @@ describe('EditBase', () => {
it('should allow to override the onSuccess function at call time', async () => {
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
update: jest.fn((_, { id, data, previousData }) =>
Promise.resolve({ data: { id, ...previousData, ...data } })
@@ -117,7 +119,9 @@ describe('EditBase', () => {
{
id: 12,
data: { test: 'test' },
+ previousData: { id: 12, test: 'previous' },
resource: 'posts',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -128,10 +132,9 @@ describe('EditBase', () => {
it('should allow to override the onError function', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
- // @ts-ignore
update: jest.fn(() => Promise.reject({ message: 'test' })),
});
const onError = jest.fn();
@@ -153,7 +156,9 @@ describe('EditBase', () => {
{
id: 12,
data: { test: 'test' },
+ previousData: { id: 12, test: 'previous' },
resource: 'posts',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -162,10 +167,9 @@ describe('EditBase', () => {
it('should allow to override the onError function at call time', async () => {
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
- // @ts-ignore
update: jest.fn(() => Promise.reject({ message: 'test' })),
});
const onError = jest.fn();
@@ -189,7 +193,9 @@ describe('EditBase', () => {
{
id: 12,
data: { test: 'test' },
+ previousData: { id: 12, test: 'previous' },
resource: 'posts',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -199,8 +205,8 @@ describe('EditBase', () => {
it('should allow to override the transform function', async () => {
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
update: jest.fn((_, { id, data, previousData }) =>
Promise.resolve({ data: { id, ...previousData, ...data } })
@@ -239,8 +245,8 @@ describe('EditBase', () => {
it('should allow to override the transform function at call time', async () => {
const dataProvider = testDataProvider({
- // @ts-ignore
getOne: () =>
+ // @ts-ignore
Promise.resolve({ data: { id: 12, test: 'previous' } }),
update: jest.fn((_, { id, data, previousData }) =>
Promise.resolve({ data: { id, ...previousData, ...data } })
diff --git a/packages/ra-ui-materialui/src/button/SaveButton.spec.tsx b/packages/ra-ui-materialui/src/button/SaveButton.spec.tsx
index 3e1dea27c67..410cff37d48 100644
--- a/packages/ra-ui-materialui/src/button/SaveButton.spec.tsx
+++ b/packages/ra-ui-materialui/src/button/SaveButton.spec.tsx
@@ -173,7 +173,9 @@ describe('', () => {
{
id: '123',
data: { id: 123, title: 'ipsum' },
+ previousData: { id: 123, title: 'lorem' },
resource: 'posts',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -224,7 +226,9 @@ describe('', () => {
{
id: '123',
data: { id: 123, title: 'ipsum' },
+ previousData: { id: 123, title: 'lorem' },
resource: 'posts',
+ meta: undefined,
},
{ snapshot: [] }
);
diff --git a/packages/ra-ui-materialui/src/detail/Edit.spec.tsx b/packages/ra-ui-materialui/src/detail/Edit.spec.tsx
index 58c7fbd5845..0a5a8f77e31 100644
--- a/packages/ra-ui-materialui/src/detail/Edit.spec.tsx
+++ b/packages/ra-ui-materialui/src/detail/Edit.spec.tsx
@@ -108,7 +108,7 @@ describe('', () => {
});
});
- it("shoudln't display the Edit aside while loading with the emptyWhileLoading prop", async () => {
+ it("shouldn't display the Edit aside while loading with the emptyWhileLoading prop", async () => {
let resolveGetOne;
const RenderedComponent = () => {
const myDataProvider = {
@@ -390,7 +390,9 @@ describe('', () => {
{
id: '123',
data: { id: 123, title: 'ipsum' },
+ previousData: { id: 123, title: 'lorem' },
resource: 'foo',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -453,7 +455,9 @@ describe('', () => {
{
id: '123',
data: { id: 123, title: 'ipsum' },
+ previousData: { id: 123, title: 'lorem' },
resource: 'foo',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -510,7 +514,9 @@ describe('', () => {
{
id: '123',
data: { id: 123, title: 'ipsum' },
+ previousData: { id: 123, title: 'lorem' },
resource: 'foo',
+ meta: undefined,
},
{ snapshot: [] }
);
@@ -573,7 +579,9 @@ describe('', () => {
{
id: '123',
data: { id: 123, title: 'ipsum' },
+ previousData: { id: 123, title: 'lorem' },
resource: 'foo',
+ meta: undefined,
},
{ snapshot: [] }
);