Skip to content

Commit

Permalink
Merge pull request #237 from axonivy/fix-column-dnd
Browse files Browse the repository at this point in the history
Fix DataTableColumn dnd only in DataTable
  • Loading branch information
ivy-lli authored Dec 9, 2024
2 parents 663b8bc + 2089ba2 commit 5f8153e
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 39 deletions.
2 changes: 1 addition & 1 deletion packages/editor/src/editor/canvas/ComponentBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type ComponentBlockProps = Omit<DropZoneProps, 'id'> & {
};

export const ComponentBlock = ({ component, preId, ...props }: ComponentBlockProps) => (
<DropZone id={component.cid} preId={preId} {...props}>
<DropZone id={component.cid} type={component.type} preId={preId} {...props}>
<Draggable config={componentByName(component.type)} data={component} />
</DropZone>
);
Expand Down
7 changes: 4 additions & 3 deletions packages/editor/src/editor/canvas/DropZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import './DropZone.css';
import type { ComponentProps } from 'react';
import { isDropZoneDisabled } from './drag-data';
import { cn } from '@axonivy/ui-components';
import type { ComponentType } from '@axonivy/form-editor-protocol';

export type DropZoneProps = ComponentProps<'div'> & {
id: string;
type?: ComponentType;
preId?: string;
dragHint?: boolean;
};

export const DropZone = ({ id, preId, className, children }: DropZoneProps) => {
export const DropZone = ({ id, type, preId, className, children }: DropZoneProps) => {
const dnd = useDndContext();
const { isOver, setNodeRef } = useDroppable({ id, disabled: isDropZoneDisabled(id, dnd.active, preId) });
const { isOver, setNodeRef } = useDroppable({ id, disabled: isDropZoneDisabled(id, type, dnd.active, preId) });
return (
<div ref={setNodeRef} className={cn('drop-zone', isOver && 'is-drop-target', className)}>
<div className='drop-zone-block' />
Expand Down
56 changes: 41 additions & 15 deletions packages/editor/src/editor/canvas/drag-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,72 @@ import type { FormData } from '@axonivy/form-editor-protocol';
import { dragData, isDropZoneDisabled, type DragData } from './drag-data';
import type { Active } from '@dnd-kit/core';
import type { DeepPartial } from '../../types/types';
import { STRUCTURE_DROPZONE_ID_PREFIX, TABLE_DROPZONE_ID_PREFIX } from '../../data/data';

describe('dragData', () => {
test('normal', () => {
const data = filledData();
expectDragData(dragData(data.components[0]), []);
expect(dragData(data.components[0])).toEqual<DragData>({ componentType: 'Input', disabledIds: [] });
});

test('layout', () => {
const data = filledData();
expectDragData(dragData(data.components[2]), ['31', '32', '33']);
expect(dragData(data.components[2])).toEqual<DragData>({ componentType: 'Layout', disabledIds: ['31', '32', '33'] });
});

test('fieldset', () => {
const data = filledData();
expectDragData(dragData(data.components[3]), ['41', '42', '43']);
expect(dragData(data.components[3])).toEqual<DragData>({ componentType: 'Fieldset', disabledIds: ['41', '42', '43'] });
});

test('panel', () => {
const data = filledData();
expectDragData(dragData(data.components[4]), ['51', '52', '53']);
expect(dragData(data.components[4])).toEqual<DragData>({ componentType: 'Panel', disabledIds: ['51', '52', '53'] });
});
});

describe('isDragZoneDisabled', () => {
test('false', () => {
describe('isDropZoneDisabled', () => {
test('inactive draggable', () => {
expect(isDropZoneDisabled('1', undefined, null)).toBeFalsy();
expect(isDropZoneDisabled('1', undefined, undefined)).toBeFalsy();
});

test('drop zone from draggable', () => {
const active: Partial<Active> = { id: '1', data: { current: undefined } };
expect(isDropZoneDisabled('', active as Active)).toBeFalsy();
expect(isDropZoneDisabled('2', active as Active)).toBeFalsy();
expect(isDropZoneDisabled('1', undefined, active as Active)).toBeTruthy();
});

test('true', () => {
test('drop zone from pre element', () => {
const active: Partial<Active> = { id: '1', data: { current: undefined } };
expect(isDropZoneDisabled('1', active as Active)).toBeTruthy();
expect(isDropZoneDisabled('2', active as Active, '1')).toBeTruthy();
expect(isDropZoneDisabled('2', undefined, active as Active, '1')).toBeTruthy();
});
});

const expectDragData = (data: DragData, ids: Array<string>) => {
expect(data).to.deep.equals({ disabledIds: ids });
};
test('empty drop zone from structure element', () => {
const active: Partial<Active> = { id: '1', data: { current: undefined } };
expect(isDropZoneDisabled(`${STRUCTURE_DROPZONE_ID_PREFIX}1`, undefined, active as Active)).toBeTruthy();
});

test('empty drop zone from table element', () => {
const active: Partial<Active> = { id: '1', data: { current: undefined } };
expect(isDropZoneDisabled(`${TABLE_DROPZONE_ID_PREFIX}1`, undefined, active as Active)).toBeTruthy();
});

test('disable all children for structure', () => {
const active: Partial<Active> = { id: '0', data: { current: { componentType: 'Layout', disabledIds: ['2', '3'] } } };
expect(isDropZoneDisabled('1', undefined, active as Active)).toBeFalsy();
expect(isDropZoneDisabled('2', undefined, active as Active)).toBeTruthy();
expect(isDropZoneDisabled('3', undefined, active as Active)).toBeTruthy();
});

test('disable columns', () => {
let active: Partial<Active> = { id: '0', data: { current: { componentType: 'Input', disabledIds: [] } } };
expect(isDropZoneDisabled('1', 'Input', active as Active)).toBeFalsy();
expect(isDropZoneDisabled('2', 'DataTableColumn', active as Active)).toBeTruthy();
active = { id: '0', data: { current: { componentType: 'DataTableColumn', disabledIds: [] } } };
expect(isDropZoneDisabled('1', 'Input', active as Active)).toBeTruthy();
expect(isDropZoneDisabled('2', 'DataTableColumn', active as Active)).toBeFalsy();
});
});

const filledData = () => {
const prefilledData: DeepPartial<FormData> = {
Expand Down
48 changes: 28 additions & 20 deletions packages/editor/src/editor/canvas/drag-data.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { isStructure, isTable, type Component, type ComponentData } from '@axonivy/form-editor-protocol';
import { isStructure, isTable, type Component, type ComponentData, type ComponentType } from '@axonivy/form-editor-protocol';
import type { Active } from '@dnd-kit/core';
import { STRUCTURE_DROPZONE_ID_PREFIX, TABLE_DROPZONE_ID_PREFIX } from '../../data/data';

export type DragData = { disabledIds: Array<string> };
export type DragData = {
componentType: ComponentType;
disabledIds: Array<string>;
};

const disabledIds = (data: Component | ComponentData): Array<string> => {
if (isStructure(data) || isTable(data)) {
Expand All @@ -17,40 +20,45 @@ const disabledIds = (data: Component | ComponentData): Array<string> => {
};

export const dragData = (data: Component | ComponentData): DragData => {
return { disabledIds: disabledIds(data) };
return { componentType: data.type, disabledIds: disabledIds(data) };
};

export const isDragData = (data: unknown): data is DragData => {
return typeof data === 'object' && data !== null && 'disabledIds' in data;
return typeof data === 'object' && data !== null && 'componentType' in data && 'disabledIds' in data;
};

export const isDropZoneDisabled = (id: string, active: Active | null, preId?: string) => {
const dropZoneId = active?.id;

if (disableDropZoneDataTableColumn(id, dropZoneId?.toString())) {
return true;
export const isDropZoneDisabled = (dropId: string, dropType?: ComponentType, active?: Active | null, preDropId?: string) => {
if (active === undefined || active === null) {
return false;
}
const dragId = active.id;
if (
dropZoneId === id ||
`${STRUCTURE_DROPZONE_ID_PREFIX}${dropZoneId}` === id ||
`${TABLE_DROPZONE_ID_PREFIX}${dropZoneId}` === id ||
dropZoneId === preId
dragId === dropId ||
`${STRUCTURE_DROPZONE_ID_PREFIX}${dragId}` === dropId ||
`${TABLE_DROPZONE_ID_PREFIX}${dragId}` === dropId ||
dragId === preDropId
) {
return true;
}
const data = active?.data.current;

const data = active.data.current;
if (isDragData(data)) {
return data.disabledIds.includes(id);
if (disableDataTableColumnDropZone(dropType, data.componentType)) {
return true;
}
return data.disabledIds.includes(dropId);
}
return false;
};

const disableDropZoneDataTableColumn = (id: string, dropZoneId: string | undefined) => {
const isColumn = id.startsWith('DataTableColumn');
const isColumnTarget = dropZoneId ? dropZoneId.includes('DataTableColumn') : false;

const disableDataTableColumnDropZone = (dropType: ComponentType | undefined, dragType: ComponentType) => {
const isColumn = dragType === 'DataTableColumn';
const isColumnTarget = dropType === 'DataTableColumn';
if (isColumn) {
return !isColumnTarget;
}
return isColumnTarget;
if (isColumnTarget) {
return !isColumn;
}
return false;
};

0 comments on commit 5f8153e

Please sign in to comment.