@mui/x-tree-view-pro 7.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +11 -0
- package/README.md +26 -0
- package/build/CHANGELOG.md +4336 -0
- package/build/LICENSE +11 -0
- package/build/README.md +26 -0
- package/build/RichTreeViewPro/RichTreeViewPro.d.ts +20 -0
- package/build/RichTreeViewPro/RichTreeViewPro.js +359 -0
- package/build/RichTreeViewPro/RichTreeViewPro.plugins.d.ts +8 -0
- package/build/RichTreeViewPro/RichTreeViewPro.plugins.js +5 -0
- package/build/RichTreeViewPro/RichTreeViewPro.types.d.ts +65 -0
- package/build/RichTreeViewPro/RichTreeViewPro.types.js +1 -0
- package/build/RichTreeViewPro/index.d.ts +3 -0
- package/build/RichTreeViewPro/index.js +3 -0
- package/build/RichTreeViewPro/package.json +6 -0
- package/build/RichTreeViewPro/richTreeViewProClasses.d.ts +7 -0
- package/build/RichTreeViewPro/richTreeViewProClasses.js +6 -0
- package/build/index.d.ts +12 -0
- package/build/index.js +22 -0
- package/build/internals/index.d.ts +1 -0
- package/build/internals/index.js +1 -0
- package/build/internals/package.json +6 -0
- package/build/internals/plugins/useTreeViewItemsReordering/index.d.ts +2 -0
- package/build/internals/plugins/useTreeViewItemsReordering/index.js +1 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.d.ts +3 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.itemPlugin.d.ts +4 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.itemPlugin.js +128 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.js +193 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.types.d.ts +140 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.types.js +1 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.utils.d.ts +26 -0
- package/build/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.utils.js +150 -0
- package/build/internals/utils/releaseInfo.d.ts +1 -0
- package/build/internals/utils/releaseInfo.js +13 -0
- package/build/internals/zero-styled/index.d.ts +3 -0
- package/build/internals/zero-styled/index.js +7 -0
- package/build/modern/RichTreeViewPro/RichTreeViewPro.js +359 -0
- package/build/modern/RichTreeViewPro/RichTreeViewPro.plugins.js +5 -0
- package/build/modern/RichTreeViewPro/RichTreeViewPro.types.js +1 -0
- package/build/modern/RichTreeViewPro/index.js +3 -0
- package/build/modern/RichTreeViewPro/richTreeViewProClasses.js +6 -0
- package/build/modern/index.js +22 -0
- package/build/modern/internals/index.js +1 -0
- package/build/modern/internals/plugins/useTreeViewItemsReordering/index.js +1 -0
- package/build/modern/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.itemPlugin.js +128 -0
- package/build/modern/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.js +193 -0
- package/build/modern/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.types.js +1 -0
- package/build/modern/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.utils.js +150 -0
- package/build/modern/internals/utils/releaseInfo.js +13 -0
- package/build/modern/internals/zero-styled/index.js +7 -0
- package/build/modern/themeAugmentation/index.js +3 -0
- package/build/node/RichTreeViewPro/RichTreeViewPro.js +367 -0
- package/build/node/RichTreeViewPro/RichTreeViewPro.plugins.js +11 -0
- package/build/node/RichTreeViewPro/RichTreeViewPro.types.js +5 -0
- package/build/node/RichTreeViewPro/index.js +27 -0
- package/build/node/RichTreeViewPro/richTreeViewProClasses.js +14 -0
- package/build/node/index.js +154 -0
- package/build/node/internals/index.js +12 -0
- package/build/node/internals/plugins/useTreeViewItemsReordering/index.js +12 -0
- package/build/node/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.itemPlugin.js +138 -0
- package/build/node/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.js +203 -0
- package/build/node/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.types.js +5 -0
- package/build/node/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.utils.js +160 -0
- package/build/node/internals/utils/releaseInfo.js +20 -0
- package/build/node/internals/zero-styled/index.js +17 -0
- package/build/node/themeAugmentation/index.js +38 -0
- package/build/package.json +59 -0
- package/build/themeAugmentation/components.d.ts +13 -0
- package/build/themeAugmentation/index.d.ts +3 -0
- package/build/themeAugmentation/index.js +3 -0
- package/build/themeAugmentation/overrides.d.ts +13 -0
- package/build/themeAugmentation/package.json +6 -0
- package/build/themeAugmentation/props.d.ts +12 -0
- package/package.json +71 -0
- package/src/RichTreeViewPro/RichTreeViewPro.plugins.ts +54 -0
- package/src/RichTreeViewPro/RichTreeViewPro.test.tsx +20 -0
- package/src/RichTreeViewPro/RichTreeViewPro.tsx +383 -0
- package/src/RichTreeViewPro/RichTreeViewPro.types.ts +80 -0
- package/src/RichTreeViewPro/index.ts +8 -0
- package/src/RichTreeViewPro/richTreeViewProClasses.ts +18 -0
- package/src/index.ts +17 -0
- package/src/internals/index.ts +1 -0
- package/src/internals/plugins/useTreeViewItemsReordering/index.ts +7 -0
- package/src/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.itemPlugin.ts +155 -0
- package/src/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.test.tsx +370 -0
- package/src/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.ts +278 -0
- package/src/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.types.ts +160 -0
- package/src/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.utils.ts +199 -0
- package/src/internals/utils/releaseInfo.ts +15 -0
- package/src/internals/zero-styled/index.ts +8 -0
- package/src/themeAugmentation/components.d.ts +13 -0
- package/src/themeAugmentation/index.js +1 -0
- package/src/themeAugmentation/index.ts +3 -0
- package/src/themeAugmentation/overrides.d.ts +13 -0
- package/src/themeAugmentation/props.d.ts +12 -0
- package/src/themeAugmentation/themeAugmentation.spec.ts +26 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +14 -0
package/src/internals/plugins/useTreeViewItemsReordering/useTreeViewItemsReordering.itemPlugin.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
MuiCancellableEvent,
|
|
4
|
+
TreeViewItemPlugin,
|
|
5
|
+
useTreeViewContext,
|
|
6
|
+
UseTreeViewItemsSignature,
|
|
7
|
+
isTargetInDescendants,
|
|
8
|
+
} from '@mui/x-tree-view/internals';
|
|
9
|
+
import { TreeItem2Props } from '@mui/x-tree-view/TreeItem2';
|
|
10
|
+
import {
|
|
11
|
+
UseTreeItem2DragAndDropOverlaySlotPropsFromItemsReordering,
|
|
12
|
+
UseTreeItem2RootSlotPropsFromItemsReordering,
|
|
13
|
+
UseTreeViewItemsReorderingSignature,
|
|
14
|
+
TreeViewItemItemReorderingValidActions,
|
|
15
|
+
UseTreeItem2ContentSlotPropsFromItemsReordering,
|
|
16
|
+
} from './useTreeViewItemsReordering.types';
|
|
17
|
+
|
|
18
|
+
export const isAndroid = () => navigator.userAgent.toLowerCase().includes('android');
|
|
19
|
+
|
|
20
|
+
export const useTreeViewItemsReorderingItemPlugin: TreeViewItemPlugin<TreeItem2Props> = ({
|
|
21
|
+
props,
|
|
22
|
+
}) => {
|
|
23
|
+
const { itemsReordering, instance } =
|
|
24
|
+
useTreeViewContext<[UseTreeViewItemsSignature, UseTreeViewItemsReorderingSignature]>();
|
|
25
|
+
const { itemId } = props;
|
|
26
|
+
|
|
27
|
+
const validActionsRef = React.useRef<TreeViewItemItemReorderingValidActions | null>(null);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
propsEnhancers: {
|
|
31
|
+
root: ({
|
|
32
|
+
rootRefObject,
|
|
33
|
+
contentRefObject,
|
|
34
|
+
externalEventHandlers,
|
|
35
|
+
}): UseTreeItem2RootSlotPropsFromItemsReordering => {
|
|
36
|
+
const draggable = instance.canItemBeDragged(itemId);
|
|
37
|
+
if (!draggable) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const handleDragStart = (event: React.DragEvent & MuiCancellableEvent) => {
|
|
42
|
+
externalEventHandlers.onDragStart?.(event);
|
|
43
|
+
if (event.defaultMuiPrevented || event.defaultPrevented) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// We don't use `event.currentTarget` here.
|
|
48
|
+
// This is to allow people to pass `onDragStart` to another element than the root.
|
|
49
|
+
if (isTargetInDescendants(event.target as HTMLElement, rootRefObject.current)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Comment to show the children in the drag preview
|
|
54
|
+
// TODO: Improve the customization of the drag preview
|
|
55
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
56
|
+
event.dataTransfer.setDragImage(contentRefObject.current!, 0, 0);
|
|
57
|
+
|
|
58
|
+
const { types } = event.dataTransfer;
|
|
59
|
+
if (isAndroid() && !types.includes('text/plain') && !types.includes('text/uri-list')) {
|
|
60
|
+
event.dataTransfer.setData('text/plain', 'android-fallback');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// iOS requires a media type to be defined
|
|
64
|
+
event.dataTransfer.setData('application/mui-x', '');
|
|
65
|
+
|
|
66
|
+
instance.startDraggingItem(itemId);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleRootDragOver = (event: React.DragEvent & MuiCancellableEvent) => {
|
|
70
|
+
externalEventHandlers.onDragOver?.(event);
|
|
71
|
+
if (event.defaultMuiPrevented) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleRootDragEnd = (event: React.DragEvent & MuiCancellableEvent) => {
|
|
79
|
+
externalEventHandlers.onDragEnd?.(event);
|
|
80
|
+
if (event.defaultMuiPrevented) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
instance.stopDraggingItem(itemId);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
draggable: true,
|
|
89
|
+
onDragStart: handleDragStart,
|
|
90
|
+
onDragOver: handleRootDragOver,
|
|
91
|
+
onDragEnd: handleRootDragEnd,
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
content: ({
|
|
95
|
+
externalEventHandlers,
|
|
96
|
+
contentRefObject,
|
|
97
|
+
}): UseTreeItem2ContentSlotPropsFromItemsReordering => {
|
|
98
|
+
const currentDrag = itemsReordering.currentDrag;
|
|
99
|
+
if (!currentDrag || currentDrag.draggedItemId === itemId) {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const handleDragOver = (event: React.DragEvent & MuiCancellableEvent) => {
|
|
104
|
+
externalEventHandlers.onDragOver?.(event);
|
|
105
|
+
if (event.defaultMuiPrevented || validActionsRef.current == null) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const rect = (event.target as HTMLDivElement).getBoundingClientRect();
|
|
110
|
+
const y = event.clientY - rect.top;
|
|
111
|
+
const x = event.clientX - rect.left;
|
|
112
|
+
instance.setDragTargetItem({
|
|
113
|
+
itemId,
|
|
114
|
+
validActions: validActionsRef.current,
|
|
115
|
+
targetHeight: rect.height,
|
|
116
|
+
cursorY: y,
|
|
117
|
+
cursorX: x,
|
|
118
|
+
contentElement: contentRefObject.current!,
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const handleDragEnter = (event: React.DragEvent & MuiCancellableEvent) => {
|
|
123
|
+
externalEventHandlers.onDragEnter?.(event);
|
|
124
|
+
if (event.defaultMuiPrevented) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
validActionsRef.current = instance.getDroppingTargetValidActions(itemId);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
onDragEnter: handleDragEnter,
|
|
133
|
+
onDragOver: handleDragOver,
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
dragAndDropOverlay: (): UseTreeItem2DragAndDropOverlaySlotPropsFromItemsReordering => {
|
|
137
|
+
const currentDrag = itemsReordering.currentDrag;
|
|
138
|
+
if (!currentDrag || currentDrag.targetItemId !== itemId || currentDrag.action == null) {
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const targetDepth =
|
|
143
|
+
currentDrag.newPosition?.parentId == null
|
|
144
|
+
? 0
|
|
145
|
+
: // The depth is always defined because drag&drop is only usable with Rich Tree View components.
|
|
146
|
+
instance.getItemMeta(currentDrag.newPosition.parentId).depth! + 1;
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
action: currentDrag.action,
|
|
150
|
+
style: { '--TreeView-targetDepth': targetDepth } as React.CSSProperties,
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
};
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { describeTreeView } from 'test/utils/tree-view/describeTreeView';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { spy } from 'sinon';
|
|
4
|
+
import { fireEvent, createEvent } from '@mui/internal-test-utils';
|
|
5
|
+
import { UseTreeViewItemsReorderingSignature } from '@mui/x-tree-view-pro/internals';
|
|
6
|
+
import { DragEventTypes, MockedDataTransfer } from 'test/utils/dragAndDrop';
|
|
7
|
+
import {
|
|
8
|
+
UseTreeViewExpansionSignature,
|
|
9
|
+
UseTreeViewItemsSignature,
|
|
10
|
+
} from '@mui/x-tree-view/internals';
|
|
11
|
+
import { chooseActionToApply } from './useTreeViewItemsReordering.utils';
|
|
12
|
+
import { TreeViewItemItemReorderingValidActions } from './useTreeViewItemsReordering.types';
|
|
13
|
+
|
|
14
|
+
interface DragEventOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Coordinates of the mouse pointer relative to the target element.
|
|
17
|
+
* @default: { x: targetWidth / 2, y: targetHeight / 2 }
|
|
18
|
+
*/
|
|
19
|
+
coordinates?: { x: number; y: number };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const buildTreeViewDragInteractions = (dataTransfer: DataTransfer) => {
|
|
23
|
+
const createFireEvent =
|
|
24
|
+
(type: DragEventTypes) =>
|
|
25
|
+
(target: HTMLElement, options: DragEventOptions = {}) => {
|
|
26
|
+
const rect = target.getBoundingClientRect();
|
|
27
|
+
const coordinates = options.coordinates ?? { x: rect.width / 2, y: rect.height / 2 };
|
|
28
|
+
const createdEvent = createEvent[type](target, {
|
|
29
|
+
clientX: rect.left + coordinates.x,
|
|
30
|
+
clientY: rect.top + coordinates.y,
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(createdEvent, 'dataTransfer', {
|
|
33
|
+
value: dataTransfer,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return fireEvent(target, createdEvent);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const dragStart = createFireEvent('dragStart');
|
|
40
|
+
const dragEnter = createFireEvent('dragEnter');
|
|
41
|
+
const dragOver = createFireEvent('dragOver');
|
|
42
|
+
const dragEnd = createFireEvent('dragEnd');
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
fullDragSequence: (
|
|
46
|
+
draggedItem: HTMLElement,
|
|
47
|
+
targetItem: HTMLElement,
|
|
48
|
+
options: DragEventOptions = {},
|
|
49
|
+
) => {
|
|
50
|
+
dragStart(draggedItem);
|
|
51
|
+
dragEnter(targetItem);
|
|
52
|
+
dragOver(targetItem, { coordinates: options.coordinates });
|
|
53
|
+
dragEnd(draggedItem);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
describeTreeView<
|
|
59
|
+
[UseTreeViewItemsReorderingSignature, UseTreeViewItemsSignature, UseTreeViewExpansionSignature]
|
|
60
|
+
>('useTreeViewItemsReordering', ({ render, treeViewComponentName }) => {
|
|
61
|
+
if (treeViewComponentName === 'SimpleTreeView' || treeViewComponentName === 'RichTreeView') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let dragEvents: ReturnType<typeof buildTreeViewDragInteractions>;
|
|
66
|
+
// eslint-disable-next-line mocha/no-top-level-hooks
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
const dataTransfer = new MockedDataTransfer();
|
|
69
|
+
dragEvents = buildTreeViewDragInteractions(dataTransfer);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// eslint-disable-next-line mocha/no-top-level-hooks
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
dragEvents = {} as typeof dragEvents;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('itemReordering prop', () => {
|
|
78
|
+
it('should allow to drag and drop items when props.itemsReordering={true}', () => {
|
|
79
|
+
const response = render({
|
|
80
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
81
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
82
|
+
itemsReordering: true,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
86
|
+
expect(response.getItemIdTree()).to.deep.equal([
|
|
87
|
+
{ id: '2', children: [{ id: '1' }] },
|
|
88
|
+
{ id: '3' },
|
|
89
|
+
]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should not allow to drag and drop items when props.itemsReordering={false}', () => {
|
|
93
|
+
const response = render({
|
|
94
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
95
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
96
|
+
itemsReordering: false,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
100
|
+
expect(response.getItemIdTree()).to.deep.equal([{ id: '1' }, { id: '2' }, { id: '3' }]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should not allow to drag and drop items when props.itemsReordering is not defined', () => {
|
|
104
|
+
const response = render({
|
|
105
|
+
experimentalFeatures: { indentationAtItemLevel: true },
|
|
106
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
110
|
+
expect(response.getItemIdTree()).to.deep.equal([{ id: '1' }, { id: '2' }, { id: '3' }]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should allow to expand the new parent of the dragged item when it was not expandable before', () => {
|
|
114
|
+
const response = render({
|
|
115
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
116
|
+
items: [{ id: '1', children: [{ id: '1.1' }] }, { id: '2' }],
|
|
117
|
+
itemsReordering: true,
|
|
118
|
+
defaultExpandedItems: ['1'],
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
dragEvents.fullDragSequence(response.getItemRoot('1.1'), response.getItemContent('2'));
|
|
122
|
+
|
|
123
|
+
fireEvent.focus(response.getItemRoot('2'));
|
|
124
|
+
fireEvent.keyDown(response.getItemRoot('2'), { key: 'Enter' });
|
|
125
|
+
|
|
126
|
+
expect(response.getItemIdTree()).to.deep.equal([
|
|
127
|
+
{ id: '1', children: [] },
|
|
128
|
+
{ id: '2', children: [{ id: '1.1' }] },
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('onItemPositionChange prop', () => {
|
|
134
|
+
it('should call onItemPositionChange when an item is moved', () => {
|
|
135
|
+
const onItemPositionChange = spy();
|
|
136
|
+
const response = render({
|
|
137
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
138
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
139
|
+
itemsReordering: true,
|
|
140
|
+
onItemPositionChange,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
144
|
+
expect(onItemPositionChange.callCount).to.equal(1);
|
|
145
|
+
expect(onItemPositionChange.lastCall.firstArg).to.deep.equal({
|
|
146
|
+
itemId: '1',
|
|
147
|
+
oldPosition: { parentId: null, index: 0 },
|
|
148
|
+
newPosition: { parentId: '2', index: 0 },
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('isItemReorderable prop', () => {
|
|
154
|
+
it('should not allow to drag an item when isItemReorderable returns false', () => {
|
|
155
|
+
const response = render({
|
|
156
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
157
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
158
|
+
itemsReordering: true,
|
|
159
|
+
canMoveItemToNewPosition: () => false,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
163
|
+
expect(response.getItemIdTree()).to.deep.equal([{ id: '1' }, { id: '2' }, { id: '3' }]);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should allow to drag an item when isItemReorderable returns true', () => {
|
|
167
|
+
const response = render({
|
|
168
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
169
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
170
|
+
itemsReordering: true,
|
|
171
|
+
canMoveItemToNewPosition: () => true,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
175
|
+
expect(response.getItemIdTree()).to.deep.equal([
|
|
176
|
+
{ id: '2', children: [{ id: '1' }] },
|
|
177
|
+
{ id: '3' },
|
|
178
|
+
]);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('canMoveItemToNewPosition prop', () => {
|
|
183
|
+
it('should not allow to drop an item when canMoveItemToNewPosition returns false', () => {
|
|
184
|
+
const response = render({
|
|
185
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
186
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
187
|
+
itemsReordering: true,
|
|
188
|
+
canMoveItemToNewPosition: () => false,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
192
|
+
expect(response.getItemIdTree()).to.deep.equal([{ id: '1' }, { id: '2' }, { id: '3' }]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should allow to drop an item when canMoveItemToNewPosition returns true', () => {
|
|
196
|
+
const response = render({
|
|
197
|
+
experimentalFeatures: { indentationAtItemLevel: true, itemsReordering: true },
|
|
198
|
+
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
|
|
199
|
+
itemsReordering: true,
|
|
200
|
+
canMoveItemToNewPosition: () => true,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
dragEvents.fullDragSequence(response.getItemRoot('1'), response.getItemContent('2'));
|
|
204
|
+
expect(response.getItemIdTree()).to.deep.equal([
|
|
205
|
+
{ id: '2', children: [{ id: '1' }] },
|
|
206
|
+
{ id: '3' },
|
|
207
|
+
]);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('getNewPosition util', () => {
|
|
213
|
+
// The actions use the following tree when dropping "1.1" on "1.2":
|
|
214
|
+
// - 1
|
|
215
|
+
// - 1.1
|
|
216
|
+
// - 1.2
|
|
217
|
+
// - 1.3
|
|
218
|
+
// - 2
|
|
219
|
+
const ALL_ACTIONS: TreeViewItemItemReorderingValidActions = {
|
|
220
|
+
'reorder-above': { parentId: '1', index: 0 },
|
|
221
|
+
'reorder-below': { parentId: '1', index: 1 },
|
|
222
|
+
'make-child': { parentId: '1.2', index: 0 },
|
|
223
|
+
'move-to-parent': { parentId: null, index: 2 },
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const FAKE_CONTENT_ELEMENT = {} as HTMLDivElement;
|
|
227
|
+
|
|
228
|
+
const COMMON_PROPERTIES = {
|
|
229
|
+
itemChildrenIndentation: 12,
|
|
230
|
+
validActions: ALL_ACTIONS,
|
|
231
|
+
targetHeight: 100,
|
|
232
|
+
targetDepth: 1,
|
|
233
|
+
cursorY: 50,
|
|
234
|
+
cursorX: 100,
|
|
235
|
+
contentElement: FAKE_CONTENT_ELEMENT,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
it('should choose the "reorder-above" action when the cursor is in the top quarter of the target item', () => {
|
|
239
|
+
expect(
|
|
240
|
+
chooseActionToApply({
|
|
241
|
+
...COMMON_PROPERTIES,
|
|
242
|
+
cursorY: 1,
|
|
243
|
+
}),
|
|
244
|
+
).to.equal('reorder-above');
|
|
245
|
+
|
|
246
|
+
expect(
|
|
247
|
+
chooseActionToApply({
|
|
248
|
+
...COMMON_PROPERTIES,
|
|
249
|
+
cursorY: 24,
|
|
250
|
+
}),
|
|
251
|
+
).to.equal('reorder-above');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should choose the "reorder-above" action when the cursor is in the top half of the target item and the "make-child" action is not valid', () => {
|
|
255
|
+
expect(
|
|
256
|
+
chooseActionToApply({
|
|
257
|
+
...COMMON_PROPERTIES,
|
|
258
|
+
cursorY: 25,
|
|
259
|
+
validActions: { ...ALL_ACTIONS, 'make-child': undefined },
|
|
260
|
+
}),
|
|
261
|
+
).to.equal('reorder-above');
|
|
262
|
+
|
|
263
|
+
expect(
|
|
264
|
+
chooseActionToApply({
|
|
265
|
+
...COMMON_PROPERTIES,
|
|
266
|
+
cursorY: 49,
|
|
267
|
+
validActions: { ...ALL_ACTIONS, 'make-child': undefined },
|
|
268
|
+
}),
|
|
269
|
+
).to.equal('reorder-above');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should choose the "reorder-below" action when the cursor is in the bottom quarter of the target item', () => {
|
|
273
|
+
expect(
|
|
274
|
+
chooseActionToApply({
|
|
275
|
+
...COMMON_PROPERTIES,
|
|
276
|
+
cursorY: 99,
|
|
277
|
+
}),
|
|
278
|
+
).to.equal('reorder-below');
|
|
279
|
+
|
|
280
|
+
expect(
|
|
281
|
+
chooseActionToApply({
|
|
282
|
+
...COMMON_PROPERTIES,
|
|
283
|
+
cursorY: 76,
|
|
284
|
+
}),
|
|
285
|
+
).to.equal('reorder-below');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should choose the "reorder-below" action when the cursor is in the bottom half of the target item and the "make-child" action is not valid', () => {
|
|
289
|
+
expect(
|
|
290
|
+
chooseActionToApply({
|
|
291
|
+
...COMMON_PROPERTIES,
|
|
292
|
+
cursorY: 75,
|
|
293
|
+
validActions: { ...ALL_ACTIONS, 'make-child': undefined },
|
|
294
|
+
}),
|
|
295
|
+
).to.equal('reorder-below');
|
|
296
|
+
|
|
297
|
+
expect(
|
|
298
|
+
chooseActionToApply({
|
|
299
|
+
...COMMON_PROPERTIES,
|
|
300
|
+
cursorY: 51,
|
|
301
|
+
validActions: { ...ALL_ACTIONS, 'make-child': undefined },
|
|
302
|
+
}),
|
|
303
|
+
).to.equal('reorder-below');
|
|
304
|
+
|
|
305
|
+
expect(
|
|
306
|
+
chooseActionToApply({
|
|
307
|
+
...COMMON_PROPERTIES,
|
|
308
|
+
cursorY: 50,
|
|
309
|
+
validActions: { ...ALL_ACTIONS, 'make-child': undefined },
|
|
310
|
+
}),
|
|
311
|
+
).to.equal('reorder-below');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should choose the "make-child" action when the cursor is in the middle of the target item', () => {
|
|
315
|
+
expect(
|
|
316
|
+
chooseActionToApply({
|
|
317
|
+
...COMMON_PROPERTIES,
|
|
318
|
+
cursorY: 25,
|
|
319
|
+
}),
|
|
320
|
+
).to.equal('make-child');
|
|
321
|
+
|
|
322
|
+
expect(
|
|
323
|
+
chooseActionToApply({
|
|
324
|
+
...COMMON_PROPERTIES,
|
|
325
|
+
cursorY: 50,
|
|
326
|
+
}),
|
|
327
|
+
).to.equal('make-child');
|
|
328
|
+
|
|
329
|
+
expect(
|
|
330
|
+
chooseActionToApply({
|
|
331
|
+
...COMMON_PROPERTIES,
|
|
332
|
+
cursorY: 74,
|
|
333
|
+
}),
|
|
334
|
+
).to.equal('make-child');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should choose the "move-to-parent" action when the cursor is inside the depth-offset of the target item', () => {
|
|
338
|
+
expect(
|
|
339
|
+
chooseActionToApply({
|
|
340
|
+
...COMMON_PROPERTIES,
|
|
341
|
+
cursorX: 1,
|
|
342
|
+
cursorY: 1,
|
|
343
|
+
}),
|
|
344
|
+
).to.equal('move-to-parent');
|
|
345
|
+
|
|
346
|
+
expect(
|
|
347
|
+
chooseActionToApply({
|
|
348
|
+
...COMMON_PROPERTIES,
|
|
349
|
+
cursorX: 11,
|
|
350
|
+
cursorY: 1,
|
|
351
|
+
}),
|
|
352
|
+
).to.equal('move-to-parent');
|
|
353
|
+
|
|
354
|
+
expect(
|
|
355
|
+
chooseActionToApply({
|
|
356
|
+
...COMMON_PROPERTIES,
|
|
357
|
+
cursorX: 1,
|
|
358
|
+
cursorY: 50,
|
|
359
|
+
}),
|
|
360
|
+
).to.equal('move-to-parent');
|
|
361
|
+
|
|
362
|
+
expect(
|
|
363
|
+
chooseActionToApply({
|
|
364
|
+
...COMMON_PROPERTIES,
|
|
365
|
+
cursorX: 1,
|
|
366
|
+
cursorY: 99,
|
|
367
|
+
}),
|
|
368
|
+
).to.equal('move-to-parent');
|
|
369
|
+
});
|
|
370
|
+
});
|