mirror of
https://github.com/apache/superset.git
synced 2026-07-09 08:15:49 +00:00
refactor(explore): migrate Explore Controls from react-dnd to @dnd-kit (#37880)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
DroppableData,
|
||||
resolveDragEnd,
|
||||
} from 'src/explore/components/ExploreContainer/ExploreDndContext';
|
||||
import { DndItemType } from 'src/explore/components/DndItemType';
|
||||
import { DndItemValue } from 'src/explore/components/DatasourcePanel/types';
|
||||
|
||||
/**
|
||||
* @dnd-kit's PointerSensor only reacts to real pointer events, which jsdom
|
||||
* cannot meaningfully dispatch (it has no layout). To exercise drop behavior in
|
||||
* unit tests we capture the `data` object a control registers via
|
||||
* `useDroppable` and feed it through the same `resolveDragEnd` dispatcher the
|
||||
* live `ExploreDndContextProvider` runs on drag end.
|
||||
*
|
||||
* Usage: spy on `useDroppable` with `captureDroppableData`, render the control,
|
||||
* then call `simulateDrop` with the dragged item.
|
||||
*/
|
||||
export type CapturedDroppable = { current: DroppableData | undefined };
|
||||
|
||||
/**
|
||||
* Returns a `jest.fn` mock implementation for `@dnd-kit/core`'s `useDroppable`
|
||||
* that records the most recently registered droppable data into `captured`,
|
||||
* while returning an inert droppable shape so the control still renders.
|
||||
*/
|
||||
export function captureDroppableData(captured: CapturedDroppable) {
|
||||
return (args: { data?: DroppableData }) => {
|
||||
captured.current = args?.data;
|
||||
return {
|
||||
setNodeRef: () => {},
|
||||
isOver: false,
|
||||
active: null,
|
||||
rect: { current: null },
|
||||
node: { current: null },
|
||||
over: null,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Drives a single drag-and-drop of `item` onto the captured droppable through
|
||||
* the production `resolveDragEnd` dispatcher.
|
||||
*/
|
||||
export function simulateDrop(
|
||||
captured: CapturedDroppable,
|
||||
item: { type: DndItemType; value: DndItemValue },
|
||||
) {
|
||||
resolveDragEnd(
|
||||
{ id: 'drag-source', data: { current: item } },
|
||||
{ id: 'dropzone', data: { current: captured.current ?? {} } },
|
||||
);
|
||||
}
|
||||
|
||||
export type SortableItemData = {
|
||||
type: string;
|
||||
dragIndex: number;
|
||||
onShiftOptions?: (dragIndex: number, hoverIndex: number) => void;
|
||||
onMoveLabel?: (dragIndex: number, hoverIndex: number) => void;
|
||||
onDropLabel?: () => void;
|
||||
};
|
||||
|
||||
export type CapturedSortables = { items: SortableItemData[] };
|
||||
|
||||
/**
|
||||
* Returns a `jest.fn` implementation for `@dnd-kit/sortable`'s `useSortable`
|
||||
* that records each sortable item's registered data (carrying the reorder
|
||||
* callbacks) into `captured`, while returning an inert sortable shape so the
|
||||
* control still renders.
|
||||
*/
|
||||
export function captureSortableData(captured: CapturedSortables) {
|
||||
return (args: { data?: SortableItemData }) => {
|
||||
if (args?.data) {
|
||||
captured.items.push(args.data);
|
||||
}
|
||||
return {
|
||||
attributes: {},
|
||||
listeners: {},
|
||||
setNodeRef: () => {},
|
||||
transform: null,
|
||||
transition: undefined,
|
||||
isDragging: false,
|
||||
setActivatorNodeRef: () => {},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Drives an in-list reorder (drag item at `fromIndex` over item at `toIndex`)
|
||||
* through the production `resolveDragEnd` dispatcher, using the reorder
|
||||
* callbacks the control registered on its sortable items.
|
||||
*/
|
||||
export function simulateReorder(
|
||||
captured: CapturedSortables,
|
||||
fromIndex: number,
|
||||
toIndex: number,
|
||||
) {
|
||||
const from = captured.items.find(i => i.dragIndex === fromIndex);
|
||||
const to = captured.items.find(i => i.dragIndex === toIndex);
|
||||
resolveDragEnd(
|
||||
{ id: `from-${fromIndex}`, data: { current: from } },
|
||||
{ id: `to-${toIndex}`, data: { current: to } },
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user