fix: preserve order of widgets during multiple widget drag and drop (#31273)
## Description Issues: 1. Order of widgets is not preserved during DnD. 2. DnD of small and large widgets together results in them being added to the same row. Fix: 1. Reuse zone utils to manage DnD of small and large widgets. 2. Account for the number of entities being added to preserve the order of addition. #### Media https://github.com/appsmithorg/appsmith/assets/5424788/cc936242-4ea0-412e-b1bf-e62a196e228f #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced widget movement logic for improved layout management. - Introduced tracking for the number of items added to a section, optimizing layout adjustments. - **Refactor** - Updated widget movement and relationship management functions for better efficiency and clarity. - **Documentation** - Added explanatory comments in paste functionality for better understanding of the logic involved. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
88f8078500
commit
c1207db0f5
|
|
@ -11,7 +11,6 @@ import type {
|
|||
} from "../../utils/anvilTypes";
|
||||
import { getWidget, getWidgets } from "sagas/selectors";
|
||||
import { addWidgetsToPreset } from "../../utils/layouts/update/additionUtils";
|
||||
import { moveWidgets } from "../../utils/layouts/update/moveUtils";
|
||||
import type {
|
||||
AnvilMoveWidgetsPayload,
|
||||
AnvilNewWidgetsPayload,
|
||||
|
|
@ -41,6 +40,7 @@ import {
|
|||
getCreateWidgetPayload,
|
||||
} from "layoutSystems/anvil/utils/widgetAdditionUtils";
|
||||
import { updateAndSaveAnvilLayout } from "../../utils/anvilChecksUtils";
|
||||
import { moveWidgetsToZone } from "layoutSystems/anvil/utils/layouts/update/zoneUtils";
|
||||
|
||||
// Function to retrieve highlighting information for the last row in the main canvas layout
|
||||
export function* getMainCanvasLastRowHighlight() {
|
||||
|
|
@ -363,8 +363,14 @@ export function* handleWidgetMovement(
|
|||
highlight,
|
||||
);
|
||||
} else {
|
||||
updatedWidgets = moveWidgets(allWidgets, movedWidgetIds, highlight);
|
||||
updatedWidgets = yield call(
|
||||
moveWidgetsToZone,
|
||||
allWidgets,
|
||||
movedWidgetIds,
|
||||
highlight,
|
||||
);
|
||||
}
|
||||
|
||||
return updatedWidgets;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ export function* addWidgetsToSection(
|
|||
* Can this be prevent during DnD itself? i.e. Don't show highlights for sections that can't handle so many zones.
|
||||
*/
|
||||
const [zones, nonZones] = splitWidgets(draggedWidgets);
|
||||
let itemsAdded = 0;
|
||||
/**
|
||||
* Step 2: Add zones to the section layout.
|
||||
*/
|
||||
|
|
@ -175,13 +176,14 @@ export function* addWidgetsToSection(
|
|||
sectionProps,
|
||||
sectionLayout,
|
||||
sectionComp,
|
||||
highlight,
|
||||
{ ...highlight, rowIndex: highlight.rowIndex + itemsAdded },
|
||||
zone,
|
||||
);
|
||||
|
||||
sectionProps = res.canvasWidgets[sectionProps.widgetId];
|
||||
sectionLayout = res.section;
|
||||
canvasWidgets = res.canvasWidgets;
|
||||
itemsAdded += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -198,7 +200,7 @@ export function* addWidgetsToSection(
|
|||
createZoneAndAddWidgets,
|
||||
canvasWidgets,
|
||||
nonZones,
|
||||
highlight,
|
||||
{ ...highlight, rowIndex: highlight.rowIndex + itemsAdded },
|
||||
sectionProps.widgetId,
|
||||
);
|
||||
sectionProps.children = [
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import {
|
|||
} from "../../widgetAdditionUtils";
|
||||
import { isLargeWidget } from "../../widgetUtils";
|
||||
import { anvilWidgets } from "widgets/anvil/constants";
|
||||
import { severTiesFromParents, transformMovedWidgets } from "./moveUtils";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
|
||||
export function* createZoneAndAddWidgets(
|
||||
allWidgets: CanvasWidgetsReduxState,
|
||||
|
|
@ -27,7 +29,7 @@ export function* createZoneAndAddWidgets(
|
|||
* Create Zone widget.
|
||||
*/
|
||||
const widgetId: string = generateReactKey();
|
||||
let updatedWidgets: CanvasWidgetsReduxState = yield call(
|
||||
const updatedWidgets: CanvasWidgetsReduxState = yield call(
|
||||
addNewWidgetToDsl,
|
||||
allWidgets,
|
||||
getCreateWidgetPayload(widgetId, anvilWidgets.ZONE_WIDGET, parentId),
|
||||
|
|
@ -37,9 +39,33 @@ export function* createZoneAndAddWidgets(
|
|||
* Extract zone layout.
|
||||
*/
|
||||
const zoneProps: FlattenedWidgetProps = updatedWidgets[widgetId];
|
||||
const { widgetId: zoneWidgetId } = zoneProps;
|
||||
|
||||
/**
|
||||
* Add widgets to zone. and update relationships.
|
||||
*/
|
||||
const res: { canvasWidgets: CanvasWidgetsReduxState; zone: WidgetProps } =
|
||||
yield call(
|
||||
addWidgetsToZone,
|
||||
updatedWidgets,
|
||||
draggedWidgets,
|
||||
highlight,
|
||||
zoneProps,
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export function* addWidgetsToZone(
|
||||
allWidgets: CanvasWidgetsReduxState,
|
||||
draggedWidgets: WidgetLayoutProps[],
|
||||
highlight: AnvilHighlightInfo,
|
||||
zone: WidgetProps,
|
||||
) {
|
||||
let updatedWidgets: CanvasWidgetsReduxState = { ...allWidgets };
|
||||
const zoneProps = { ...zone };
|
||||
const preset: LayoutProps[] = zoneProps.layout;
|
||||
let zoneLayout: LayoutProps = preset[0];
|
||||
const { widgetId: zoneWidgetId } = zoneProps;
|
||||
|
||||
/**
|
||||
* If dragged widget is a new widget,
|
||||
|
|
@ -67,6 +93,7 @@ export function* createZoneAndAddWidgets(
|
|||
zoneLayout.layoutType,
|
||||
);
|
||||
|
||||
let rowsAdded = 0;
|
||||
if (smallWidgets.length) {
|
||||
zoneLayout = addWidgetsToChildTemplate(
|
||||
zoneLayout,
|
||||
|
|
@ -74,34 +101,28 @@ export function* createZoneAndAddWidgets(
|
|||
smallWidgets,
|
||||
highlight,
|
||||
);
|
||||
rowsAdded += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add large widgets to the zone layout.
|
||||
*/
|
||||
largeWidgets.forEach((widget: WidgetLayoutProps) => {
|
||||
zoneLayout = addWidgetsToChildTemplate(
|
||||
zoneLayout,
|
||||
zoneComp,
|
||||
[widget],
|
||||
highlight,
|
||||
);
|
||||
largeWidgets.forEach((widget: WidgetLayoutProps, index: number) => {
|
||||
zoneLayout = addWidgetsToChildTemplate(zoneLayout, zoneComp, [widget], {
|
||||
...highlight,
|
||||
rowIndex: highlight.rowIndex + rowsAdded + index,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Update zone preset with the updated zone layout.
|
||||
*/
|
||||
preset[0] = zoneLayout;
|
||||
|
||||
/**
|
||||
* Update zone widget with the updated preset.
|
||||
*/
|
||||
zoneProps.layout = preset;
|
||||
zoneProps.layout = [zoneLayout];
|
||||
|
||||
return {
|
||||
canvasWidgets: {
|
||||
...updatedWidgets,
|
||||
[zoneWidgetId]: zoneProps,
|
||||
[zoneProps.widgetId]: zoneProps,
|
||||
},
|
||||
zone: zoneProps,
|
||||
};
|
||||
|
|
@ -154,3 +175,36 @@ function* updateDraggedWidgets(
|
|||
}
|
||||
return updatedWidgets;
|
||||
}
|
||||
|
||||
export function* moveWidgetsToZone(
|
||||
allWidgets: CanvasWidgetsReduxState,
|
||||
movedWidgets: string[],
|
||||
highlight: AnvilHighlightInfo,
|
||||
) {
|
||||
let widgets: CanvasWidgetsReduxState = { ...allWidgets };
|
||||
|
||||
/**
|
||||
* Remove moved widgets from previous parents.
|
||||
*/
|
||||
widgets = severTiesFromParents(widgets, movedWidgets);
|
||||
|
||||
/**
|
||||
* Get the new Zone parent and its Canvas.
|
||||
*/
|
||||
const { canvasId } = highlight;
|
||||
|
||||
const zone: FlattenedWidgetProps = widgets[canvasId];
|
||||
|
||||
/**
|
||||
* Add moved widgets to the section.
|
||||
*/
|
||||
const { canvasWidgets } = yield call(
|
||||
addWidgetsToZone,
|
||||
widgets,
|
||||
transformMovedWidgets(widgets, movedWidgets, highlight),
|
||||
highlight,
|
||||
zone,
|
||||
);
|
||||
|
||||
return canvasWidgets;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ function getPastingInfo(
|
|||
};
|
||||
}
|
||||
const layout: LayoutProps = parent.layout[0];
|
||||
/**
|
||||
* If parentOrder.length === 1 => add the copied widgets at the end of the layout.
|
||||
* Else find index of the child (usually selected widget) in the layout, and add the copied widgets after it.
|
||||
*/
|
||||
const info: Omit<PasteDestinationInfo, "parentOrder"> =
|
||||
parentOrder.length === 1
|
||||
? getLastIndexInLayout(parent)
|
||||
|
|
@ -105,6 +109,10 @@ function getChildIndexInLayout(
|
|||
const Comp: typeof BaseLayoutComponent = LayoutFactory.get(layout.layoutType);
|
||||
|
||||
if (Comp.rendersWidgets) {
|
||||
/**
|
||||
* if layout renders widgets, then find the index of the child in the layout.
|
||||
* => If child is not found, then return -1.
|
||||
*/
|
||||
const index = (layout.layout as WidgetLayoutProps[]).findIndex(
|
||||
(w: WidgetLayoutProps) => w.widgetId === childId,
|
||||
);
|
||||
|
|
@ -120,6 +128,9 @@ function getChildIndexInLayout(
|
|||
rowIndex: [...rowIndex, index + 1],
|
||||
};
|
||||
} else {
|
||||
/**
|
||||
* If layout renders other layouts, then find the index of the child in the nested layouts.
|
||||
*/
|
||||
let res: Omit<PasteDestinationInfo, "parentOrder"> = {
|
||||
alignment: FlexLayerAlignment.Start,
|
||||
layoutOrder,
|
||||
|
|
@ -134,6 +145,9 @@ function getChildIndexInLayout(
|
|||
[...layoutOrder, each],
|
||||
[...rowIndex, index + 1],
|
||||
);
|
||||
/**
|
||||
* Acknowledge the result only if the child is found in the layout.
|
||||
*/
|
||||
if (temp.rowIndex[temp.rowIndex.length - 1] !== -1) res = temp;
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user