From 2048efb75dc7f5a892d1d8f6b052f59bb2999cb4 Mon Sep 17 00:00:00 2001 From: Prakhar Tripathi <110407222+prakhartripath0001@users.noreply.github.com> Date: Tue, 2 Jun 2026 16:53:26 +0530 Subject: [PATCH] fix(record-table): keep column header dropdown open after Move Left/Right (#21015) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #20999 ## Summary Fixes a UX issue where clicking **Move left** or **Move right** in the column header dropdown immediately closed the menu, forcing users to reopen it for every single move. ## Problem `handleColumnMoveLeft` and `handleColumnMoveRight` both called `closeDropdownAndToggleScroll()` unconditionally at the top of their handlers — before even checking `canMoveLeft` / `canMoveRight`. This immediately set the Jotai atom `isDropdownOpenComponentState` to `false`, unmounting the dropdown. Since move actions are **repeatable** — a user might want to shift a column several positions — they were forced into a frustrating loop: click header → click move → click header → click move → repeat for every step. ## Fix Removed the two `closeDropdownAndToggleScroll()` calls from the move handlers in `RecordTableColumnHeadDropdownMenu.tsx`. ```diff const handleColumnMoveLeft = () => { - closeDropdownAndToggleScroll(); - if (!canMoveLeft) return; moveTableColumn('left', recordField.fieldMetadataItemId); }; const handleColumnMoveRight = () => { - closeDropdownAndToggleScroll(); - if (!canMoveRight) return; moveTableColumn('right', recordField.fieldMetadataItemId); }; ``` All other handlers — **Filter, Sort, Hide** — are untouched and still close the dropdown correctly, since those are one-shot or navigation actions. ## Changes | File | Change | |---|---| | `RecordTableColumnHeadDropdownMenu.tsx` | Remove 2 `closeDropdownAndToggleScroll()` calls from move handlers | | `RecordTable.stories.tsx` | Add `HeaderMenuStaysOpenAfterMoveRight` regression story | ## Testing **Storybook interaction test** — `HeaderMenuStaysOpenAfterMoveRight`: clicks "Move right" then asserts the menu is still visible. **Manual checklist:** - [x] Move right → menu stays open - [x] Move right again → column moves again, menu still open - [x] Move left → menu stays open - [x] Move rightmost column → "Move right" disappears, menu stays open showing "Move left" - [x] Filter → menu closes *(unchanged)* - [x] Sort → menu closes *(unchanged)* - [x] Hide → menu closes *(unchanged)* - [x] Click outside → menu closes *(unchanged)* - [x] Escape → menu closes *(unchanged)* - [x] TypeScript: zero new errors (`tsc --noEmit`) --------- Co-authored-by: Félix Malfait Co-authored-by: Charles Bochet --- .../__stories__/RecordTable.stories.tsx | 17 +++++++++++++++++ .../RecordTableColumnHeadDropdownMenu.tsx | 4 ---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/__stories__/RecordTable.stories.tsx b/packages/twenty-front/src/modules/object-record/record-table/__stories__/RecordTable.stories.tsx index 655b1f2dee..a5197924ce 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/__stories__/RecordTable.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/__stories__/RecordTable.stories.tsx @@ -65,6 +65,23 @@ export const HeaderMenuOpen: Story = { }, }; +export const HeaderMenuStaysOpenAfterMoveRight: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); + + await canvas.findAllByText('Linkedin', {}, { timeout: 3000 }); + + const headerMenuButton = await canvas.findByText('Domain Name'); + await userEvent.click(headerMenuButton); + + const moveRightButton = await body.findByText('Move right'); + await userEvent.click(moveRightButton); + + await body.findByText('Move right'); + }, +}; + export const ScrolledLeft: Story = { parameters: { container: { diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx index 22730d3271..d4377a1814 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableColumnHeadDropdownMenu.tsx @@ -83,16 +83,12 @@ export const RecordTableColumnHeadDropdownMenu = ({ }; const handleColumnMoveLeft = () => { - closeDropdownAndToggleScroll(); - if (!canMoveLeft) return; moveTableColumn('left', recordField.fieldMetadataItemId); }; const handleColumnMoveRight = () => { - closeDropdownAndToggleScroll(); - if (!canMoveRight) return; moveTableColumn('right', recordField.fieldMetadataItemId);