Fix merge records page accumulating duplicate morph items (#17705)
## Why When opening **Merge records** repeatedly, morph items for the same command-menu page were appended instead of replaced. This could produce duplicated IDs (e.g. `[A,B,B,A]`) in the merge flow and extra duplicate tabs in the UI. ## What - Update `useCommandMenuUpdateNavigationMorphItemsByPage` to replace page morph items instead of appending existing ones. - Add regression tests covering: - replacing existing morph items for the same page - keeping only the latest payload when called twice for the same page ## Notes I could not run the full workspace tests locally in this environment because of existing test/build setup issues unrelated to this change (missing `packages/twenty-front/tsconfig.spec.json` and `temporal-polyfill` resolution in dependent tasks). Co-authored-by: remi <remi@labox-apps.com>
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
import { useCommandMenuUpdateNavigationMorphItemsByPage } from '@/command-menu/hooks/useCommandMenuUpdateNavigationMorphItemsByPage';
|
||||
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
const pageId = 'merge-page-id';
|
||||
const objectMetadataId = 'company-metadata-id';
|
||||
|
||||
const Wrapper = ({
|
||||
children,
|
||||
initialRecordIds,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
initialRecordIds: string[];
|
||||
}) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
commandMenuNavigationMorphItemsByPageState,
|
||||
new Map([
|
||||
[
|
||||
pageId,
|
||||
initialRecordIds.map((recordId) => ({
|
||||
objectMetadataId,
|
||||
recordId,
|
||||
})),
|
||||
],
|
||||
]),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
const renderHooks = (initialRecordIds: string[]) =>
|
||||
renderHook(
|
||||
() => {
|
||||
const { updateCommandMenuNavigationMorphItemsByPage } =
|
||||
useCommandMenuUpdateNavigationMorphItemsByPage();
|
||||
const commandMenuNavigationMorphItemsByPage = useRecoilValue(
|
||||
commandMenuNavigationMorphItemsByPageState,
|
||||
);
|
||||
|
||||
return {
|
||||
updateCommandMenuNavigationMorphItemsByPage,
|
||||
commandMenuNavigationMorphItemsByPage,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper initialRecordIds={initialRecordIds}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
describe('useCommandMenuUpdateNavigationMorphItemsByPage', () => {
|
||||
it('should replace existing items for a page instead of appending', async () => {
|
||||
const { result } = renderHooks(['record-1', 'record-2']);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.updateCommandMenuNavigationMorphItemsByPage({
|
||||
pageId,
|
||||
objectMetadataId,
|
||||
objectRecordIds: ['record-2', 'record-1'],
|
||||
});
|
||||
});
|
||||
|
||||
expect(
|
||||
result.current.commandMenuNavigationMorphItemsByPage.get(pageId),
|
||||
).toEqual([
|
||||
{
|
||||
objectMetadataId,
|
||||
recordId: 'record-2',
|
||||
},
|
||||
{
|
||||
objectMetadataId,
|
||||
recordId: 'record-1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should keep only the latest payload when called twice for the same page', async () => {
|
||||
const { result } = renderHooks([]);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.updateCommandMenuNavigationMorphItemsByPage({
|
||||
pageId,
|
||||
objectMetadataId,
|
||||
objectRecordIds: ['record-1', 'record-2'],
|
||||
});
|
||||
await result.current.updateCommandMenuNavigationMorphItemsByPage({
|
||||
pageId,
|
||||
objectMetadataId,
|
||||
objectRecordIds: ['record-2', 'record-1'],
|
||||
});
|
||||
});
|
||||
|
||||
expect(
|
||||
result.current.commandMenuNavigationMorphItemsByPage.get(pageId),
|
||||
).toEqual([
|
||||
{
|
||||
objectMetadataId,
|
||||
recordId: 'record-2',
|
||||
},
|
||||
{
|
||||
objectMetadataId,
|
||||
recordId: 'record-1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
+4
-12
@@ -1,5 +1,4 @@
|
||||
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
type UpdateNavigationMorphItemsByPageParams = {
|
||||
@@ -20,17 +19,10 @@ export const useCommandMenuUpdateNavigationMorphItemsByPage = () => {
|
||||
.getLoadable(commandMenuNavigationMorphItemsByPageState)
|
||||
.getValue();
|
||||
|
||||
const currentMorphItemsForPage = currentMorphItems.get(pageId);
|
||||
|
||||
const newMorphItems = [
|
||||
...(isNonEmptyArray(currentMorphItemsForPage)
|
||||
? currentMorphItemsForPage
|
||||
: []),
|
||||
...objectRecordIds.map((recordId) => ({
|
||||
objectMetadataId,
|
||||
recordId,
|
||||
})),
|
||||
];
|
||||
const newMorphItems = objectRecordIds.map((recordId) => ({
|
||||
objectMetadataId,
|
||||
recordId,
|
||||
}));
|
||||
|
||||
const newMorphItemsMap = new Map(currentMorphItems);
|
||||
newMorphItemsMap.set(pageId, newMorphItems);
|
||||
|
||||
Reference in New Issue
Block a user