Skip to content

Commit

Permalink
fix: can't properly drag and drop in empty record group (#9039)
Browse files Browse the repository at this point in the history
Fix #8968 

Fix issue with drag and drop when record group are empty.
Happy to discuss the implementation, as it's limited to the
`@hello-pangea/dnd` api.

---------

Co-authored-by: Charles Bochet <[email protected]>
  • Loading branch information
magrinj and charlesBochet authored Dec 13, 2024
1 parent d56c815 commit 07aaf08
Show file tree
Hide file tree
Showing 29 changed files with 348 additions and 241 deletions.
18 changes: 5 additions & 13 deletions packages/twenty-front/src/hooks/useCombinedRefs.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import React, { Ref, RefCallback } from 'react';
import { isFunction } from '@sniptt/guards';
import { Ref, RefCallback } from 'react';
import { combineRefs } from '~/utils/combineRefs';

export const useCombinedRefs =
<T>(...refs: (Ref<T> | undefined)[]): RefCallback<T> =>
(node: T) => {
for (const ref of refs) {
if (isFunction(ref)) {
ref(node);
} else if (ref !== null && ref !== undefined) {
(ref as React.MutableRefObject<T | null>).current = node;
}
}
};
export const useCombinedRefs = <T>(
...refs: (Ref<T> | undefined)[]
): RefCallback<T> => combineRefs<T>(...refs);
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import {
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';

import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext';
import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { mockPerformance } from './mock';
Expand Down Expand Up @@ -87,7 +88,7 @@ const meta: Meta = {
onCellMouseEnter: () => {},
}}
>
<RecordTableRowContext.Provider
<RecordTableRowContextProvider
value={{
objectNameSingular:
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
Expand All @@ -99,43 +100,48 @@ const meta: Meta = {
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
}) + mockPerformance.recordId,
isSelected: false,
isDragging: false,
dragHandleProps: null,
inView: true,
isPendingRow: false,
inView: true,
}}
>
<RecordTableCellContext.Provider
<RecordTableRowDraggableContextProvider
value={{
columnDefinition: mockPerformance.fieldDefinition,
columnIndex: 0,
cellPosition: { row: 0, column: 0 },
hasSoftFocus: false,
isInEditMode: false,
isDragging: false,
dragHandleProps: null,
}}
>
<FieldContext.Provider
<RecordTableCellContext.Provider
value={{
recordId: mockPerformance.recordId,
basePathToShowPage: '/object-record/',
isLabelIdentifier: false,
fieldDefinition: {
...mockPerformance.fieldDefinition,
},
hotkeyScope: 'hotkey-scope',
columnDefinition: mockPerformance.fieldDefinition,
columnIndex: 0,
cellPosition: { row: 0, column: 0 },
hasSoftFocus: false,
isInEditMode: false,
}}
>
<RelationFieldValueSetterEffect />
<table>
<tbody>
<tr>
<Story />
</tr>
</tbody>
</table>
</FieldContext.Provider>
</RecordTableCellContext.Provider>
</RecordTableRowContext.Provider>
<FieldContext.Provider
value={{
recordId: mockPerformance.recordId,
basePathToShowPage: '/object-record/',
isLabelIdentifier: false,
fieldDefinition: {
...mockPerformance.fieldDefinition,
},
hotkeyScope: 'hotkey-scope',
}}
>
<RelationFieldValueSetterEffect />
<table>
<tbody>
<tr>
<Story />
</tr>
</tbody>
</table>
</FieldContext.Provider>
</RecordTableCellContext.Provider>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
</RecordTableBodyContextProvider>
</RecordTableComponentInstance>
</RecordTableContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';

export type RecordTableCellContextProps = {
export type RecordTableCellContextValue = {
columnDefinition: ColumnDefinition<FieldMetadata>;
columnIndex: number;
isInEditMode: boolean;
Expand All @@ -13,4 +13,4 @@ export type RecordTableCellContextProps = {
};

export const RecordTableCellContext =
createContext<RecordTableCellContextProps>({} as RecordTableCellContextProps);
createContext<RecordTableCellContextValue>({} as RecordTableCellContextValue);
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import { DraggableProvidedDragHandleProps } from '@hello-pangea/dnd';
import { createContext } from 'react';
import { createRequiredContext } from '~/utils/createRequiredContext';

export type RecordTableRowContextProps = {
export type RecordTableRowContextValue = {
pathToShowPage: string;
objectNameSingular: string;
recordId: string;
rowIndex: number;
isSelected: boolean;
inView: boolean;
isPendingRow?: boolean;
isDragging: boolean;
dragHandleProps: DraggableProvidedDragHandleProps | null;
inView?: boolean;
isDragDisabled?: boolean;
};

export const RecordTableRowContext = createContext<RecordTableRowContextProps>(
{} as RecordTableRowContextProps,
);
export const [RecordTableRowContextProvider, useRecordTableRowContextOrThrow] =
createRequiredContext<RecordTableRowContextValue>('RecordTableRowContext');
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { DraggableProvidedDragHandleProps } from '@hello-pangea/dnd';
import { createRequiredContext } from '~/utils/createRequiredContext';

export type RecordTableRowDraggableContextValue = {
isDragging: boolean;
dragHandleProps: DraggableProvidedDragHandleProps | null;
};

export const [
RecordTableRowDraggableContextProvider,
useRecordTableRowDraggableContextOrThrow,
] = createRequiredContext<RecordTableRowDraggableContextValue>(
'RecordTableRowDraggableContext',
);
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { RecordTableCellCheckbox } from '@/object-record/record-table/record-table-cell/components/RecordTableCellCheckbox';
import { RecordTableCellGrip } from '@/object-record/record-table/record-table-cell/components/RecordTableCellGrip';
import { RecordTableCellLoading } from '@/object-record/record-table/record-table-cell/components/RecordTableCellLoading';
Expand All @@ -13,18 +15,36 @@ export const RecordTableBodyLoading = () => {
return (
<tbody>
{Array.from({ length: 8 }).map((_, rowIndex) => (
<RecordTableTr
isDragging={false}
data-testid={`row-id-${rowIndex}`}
data-selectable-id={`row-id-${rowIndex}`}
<RecordTableRowContextProvider
key={rowIndex}
value={{
pathToShowPage: '',
objectNameSingular: '',
recordId: `${rowIndex}`,
rowIndex,
isSelected: false,
inView: true,
}}
>
<RecordTableCellGrip />
<RecordTableCellCheckbox />
{visibleTableColumns.map((column) => (
<RecordTableCellLoading key={column.fieldMetadataId} />
))}
</RecordTableTr>
<RecordTableRowDraggableContextProvider
value={{
dragHandleProps: {} as any,
isDragging: false,
}}
>
<RecordTableTr
isDragging={false}
data-testid={`row-id-${rowIndex}`}
data-selectable-id={`row-id-${rowIndex}`}
>
<RecordTableCellGrip />
<RecordTableCellCheckbox />
{visibleTableColumns.map((column) => (
<RecordTableCellLoading key={column.fieldMetadataId} />
))}
</RecordTableTr>
</RecordTableRowDraggableContextProvider>
</RecordTableRowContextProvider>
))}
</tbody>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export const RecordTableRecordGroupsBody = () => {
key={recordGroupId}
recordGroupId={recordGroupId}
>
{index > 0 && <RecordTableRecordGroupEmptyRow />}
<RecordGroupContext.Provider value={{ recordGroupId }}>
<RecordTableBodyDroppable recordGroupId={recordGroupId}>
{index > 0 && <RecordTableRecordGroupEmptyRow />}
<RecordTableRecordGroupSection />
<RecordTableRecordGroupRows />
</RecordTableBodyDroppable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { useCallback, useContext } from 'react';
import { useCallback } from 'react';

import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
import { useSetCurrentRowSelected } from '@/object-record/record-table/record-table-row/hooks/useSetCurrentRowSelected';
import { Checkbox } from 'twenty-ui';
Expand All @@ -16,7 +16,7 @@ const StyledContainer = styled.div`
`;

export const RecordTableCellCheckbox = () => {
const { isSelected } = useContext(RecordTableRowContext);
const { isSelected } = useRecordTableRowContextOrThrow();

const { setCurrentRowSelected } = useSetCurrentRowSelected();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { isFieldSelect } from '@/object-record/record-field/types/guards/isField
import { RecordUpdateContext } from '@/object-record/record-table/contexts/EntityUpdateMutationHookContext';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
Expand All @@ -22,7 +22,7 @@ export const RecordTableCellFieldContextWrapper = ({

const { columnDefinition } = useContext(RecordTableCellContext);

const { recordId, pathToShowPage } = useContext(RecordTableRowContext);
const { recordId, pathToShowPage } = useRecordTableRowContextOrThrow();

const updateRecord = useContext(RecordUpdateContext);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { useContext } from 'react';

import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowDraggableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
import { css } from '@emotion/react';
import { IconListViewGrip } from 'twenty-ui';
Expand Down Expand Up @@ -30,9 +30,10 @@ const StyledIconWrapper = styled.div<{ isDragging: boolean }>`
`;

export const RecordTableCellGrip = () => {
const { dragHandleProps, isDragging, isPendingRow } = useContext(
RecordTableRowContext,
);
const { isPendingRow } = useRecordTableRowContextOrThrow();

const { dragHandleProps, isDragging } =
useRecordTableRowDraggableContextOrThrow();

return (
<RecordTableTd
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useContext, useMemo } from 'react';
import { useMemo } from 'react';

import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper';
import { isSoftFocusOnTableCellComponentFamilyState } from '@/object-record/record-table/states/isSoftFocusOnTableCellComponentFamilyState';
import { isTableCellInEditModeComponentFamilyState } from '@/object-record/record-table/states/isTableCellInEditModeComponentFamilyState';
Expand All @@ -19,7 +19,7 @@ export const RecordTableCellWrapper = ({
columnIndex: number;
children: React.ReactNode;
}) => {
const { rowIndex } = useContext(RecordTableRowContext);
const { rowIndex } = useRecordTableRowContextOrThrow();

const currentTableCellPosition: TableCellPosition = useMemo(
() => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useContext } from 'react';

import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';

export const RecordTableLastEmptyCell = () => {
const { isSelected } = useContext(RecordTableRowContext);
const { isSelected } = useRecordTableRowContextOrThrow();

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { RecordTableCellContextProps } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { RecordTableRowContextProps } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { RecordTableCellContextValue } from '@/object-record/record-table/contexts/RecordTableCellContext';
import { RecordTableRowContextValue } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableRowDraggableContextValue } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { FieldMetadataType } from '~/generated/graphql';

export const recordTableRow: RecordTableRowContextProps = {
export const recordTableRowContextValue: RecordTableRowContextValue = {
rowIndex: 2,
isSelected: false,
recordId: 'recordId',
pathToShowPage: '/',
objectNameSingular: 'objectNameSingular',
isPendingRow: false,
inView: true,
};

export const recordTableRowDraggableContextValue: RecordTableRowDraggableContextValue = {
dragHandleProps: {} as any,
isDragging: false,
inView: true,
isPendingRow: false,
};

export const recordTableCell: RecordTableCellContextProps = {
export const recordTableCellContextValue: RecordTableCellContextValue = {
columnIndex: 3,
columnDefinition: {
size: 1,
Expand Down
Loading

0 comments on commit 07aaf08

Please sign in to comment.