Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code d9757c2da7 fix: move droppable placeholder back inside innerRef container in Kanban board
https://sonarly.com/issue/17390?type=bug

Kanban board drops only register near the top of target columns because the `@hello-pangea/dnd` placeholder was moved outside the `innerRef` container, breaking the library's droppable area measurement.

Fix: **What changed:**

Moved `droppableProvided.innerRef` and `droppableProvided.droppableProps` from `StyledColumnCardsContainer` (inside the memo boundary) up to `StyledColumn` in `RecordBoardColumn.tsx` (outside the memo boundary). This ensures both the card list and the placeholder are children of the `innerRef` element, which is required by `@hello-pangea/dnd` for correct droppable area measurement.

**RecordBoardColumn.tsx:**
- Added `ref={droppableProvided.innerRef}` and `{...droppableProvided.droppableProps}` to `StyledColumn`
- Removed `droppableProvided` from props passed to `RecordBoardColumnCardsContainer`

**RecordBoardColumnCardsContainer.tsx:**
- Removed `droppableProvided` from component props and type definition
- Removed `ref` and spread of `droppableProps` from `StyledColumnCardsContainer`
- Removed unused `DroppableProvided` type import

**Why this works:**
The `DragAndDropLibraryLegacyReRenderBreaker` (React.memo wrapper) blocks re-renders of the card list during drag — this is the performance optimization from PR #15714. Previously, to work around this, the placeholder was placed outside the `innerRef` container, breaking drop zone detection. By lifting `innerRef` to the parent `StyledColumn`, both the memo'd cards and the placeholder live inside the `innerRef` DOM element, satisfying the library's requirements while preserving the performance optimization.
2026-03-23 07:47:55 +00:00
2 changed files with 7 additions and 10 deletions
@@ -65,12 +65,15 @@ export const RecordBoardColumn = ({
>
<Droppable droppableId={recordBoardColumnId}>
{(droppableProvided) => (
<StyledColumn>
<StyledColumn
ref={droppableProvided.innerRef}
// oxlint-disable-next-line react/jsx-props-no-spreading
{...droppableProvided.droppableProps}
>
<DragAndDropLibraryLegacyReRenderBreaker
memoizationId={recordBoardColumnId}
>
<RecordBoardColumnCardsContainer
droppableProvided={droppableProvided}
recordBoardColumnId={recordBoardColumnId}
/>
</DragAndDropLibraryLegacyReRenderBreaker>
@@ -1,5 +1,5 @@
import { styled } from '@linaria/react';
import { Draggable, type DroppableProvided } from '@hello-pangea/dnd';
import { Draggable } from '@hello-pangea/dnd';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
@@ -25,12 +25,10 @@ const StyledNewButtonContainer = styled.div`
type RecordBoardColumnCardsContainerProps = {
recordBoardColumnId: string;
droppableProvided: DroppableProvided;
};
export const RecordBoardColumnCardsContainer = ({
recordBoardColumnId,
droppableProvided,
}: RecordBoardColumnCardsContainerProps) => {
const { columnDefinition } = useContext(RecordBoardColumnContext);
@@ -45,11 +43,7 @@ export const RecordBoardColumnCardsContainer = ({
);
return (
<StyledColumnCardsContainer
ref={droppableProvided?.innerRef}
// oxlint-disable-next-line react/jsx-props-no-spreading
{...droppableProvided?.droppableProps}
>
<StyledColumnCardsContainer>
{recordIndexRecordIdsByGroup.map((recordId, index) => (
<RecordBoardCardDraggableContainer
key={recordId}