Skip to content

Commit dad162c

Browse files
authored
fix: stop duplicating required children on reparent (#4537)
Turns our delete does not clear instances when detachable check fails. Good news: new child constraints were added only to tabs. Bad news: I think deleting pages kept instances in the build, oops. Fixed reparent and added checks for delete only where necessary. How to test - insert tabs content - delete second trigger and content - reposition trigger - it is duplicated
1 parent fb2c6b5 commit dad162c

File tree

4 files changed

+48
-30
lines changed

4 files changed

+48
-30
lines changed

apps/builder/app/builder/features/ai/apply-operations.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { isBaseBreakpoint } from "~/shared/breakpoints";
77
import {
88
deleteInstanceMutable,
99
insertTemplateData,
10+
isInstanceDetachable,
1011
updateWebstudioData,
1112
} from "~/shared/instance-utils";
1213
import {
@@ -101,7 +102,14 @@ const deleteInstanceByOp = (
101102
) => {
102103
const instanceSelector = computeSelectorForInstanceId(operation.wsId);
103104
if (instanceSelector) {
105+
// @todo tell user they can't delete root
106+
if (instanceSelector.length === 1) {
107+
return;
108+
}
104109
updateWebstudioData((data) => {
110+
if (isInstanceDetachable(data.instances, instanceSelector) === false) {
111+
return;
112+
}
105113
deleteInstanceMutable(data, instanceSelector);
106114
});
107115
}

apps/builder/app/builder/shared/commands.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { blockTemplateComponent } from "@webstudio-is/react-sdk";
2+
import { toast } from "@webstudio-is/design-system";
13
import { createCommandsEmitter, type Command } from "~/shared/commands-emitter";
24
import {
35
$editingItemSelector,
@@ -19,6 +21,7 @@ import {
1921
extractWebstudioFragment,
2022
insertWebstudioFragmentCopy,
2123
updateWebstudioData,
24+
isInstanceDetachable,
2225
} from "~/shared/instance-utils";
2326
import type { InstanceSelector } from "~/shared/tree-utils";
2427
import { serverSyncStore } from "~/shared/sync";
@@ -32,7 +35,6 @@ import { selectInstance } from "~/shared/awareness";
3235
import { openCommandPanel } from "../features/command-panel";
3336
import { builderApi } from "~/shared/builder-api";
3437
import { findBlockSelector } from "../features/workspace/canvas-tools/outline/block-instance-outline";
35-
import { blockTemplateComponent } from "@webstudio-is/react-sdk";
3638

3739
const makeBreakpointCommand = <CommandName extends string>(
3840
name: CommandName,
@@ -65,8 +67,13 @@ const deleteSelectedInstance = () => {
6567
if (selectedInstanceSelector.length === 1) {
6668
return;
6769
}
68-
6970
const instances = $instances.get();
71+
if (isInstanceDetachable(instances, selectedInstanceSelector) === false) {
72+
toast.error(
73+
"This instance can not be moved outside of its parent component."
74+
);
75+
return false;
76+
}
7077

7178
if ($isContentMode.get()) {
7279
// In content mode we are allowing to delete childen of the editable block

apps/builder/app/shared/instance-utils.test.tsx

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { $, ws, renderJsx, ExpressionValue } from "@webstudio-is/sdk/testing";
1111
import { parseCss } from "@webstudio-is/css-data";
1212
import { coreMetas } from "@webstudio-is/react-sdk";
1313
import * as defaultMetas from "@webstudio-is/sdk-components-react/metas";
14+
import * as radixMetas from "@webstudio-is/sdk-components-react-radix/metas";
1415
import type {
1516
Asset,
1617
Breakpoint,
@@ -844,6 +845,36 @@ describe("reparent instance", () => {
844845
])
845846
);
846847
});
848+
849+
test("reparent required child", () => {
850+
$instances.set(
851+
renderJsx(
852+
<$.Body ws:id="body">
853+
<$.Tooltip ws:id="tooltip">
854+
<$.TooltipTrigger ws:id="trigger"></$.TooltipTrigger>
855+
<$.TooltipContent ws:id="content"></$.TooltipContent>
856+
</$.Tooltip>
857+
</$.Body>
858+
).instances
859+
);
860+
$registeredComponentMetas.set(
861+
new Map(Object.entries({ ...defaultMetas, ...radixMetas }))
862+
);
863+
reparentInstance(["trigger", "tooltip", "body"], {
864+
parentSelector: ["tooltip", "body"],
865+
position: "end",
866+
});
867+
expect($instances.get()).toEqual(
868+
renderJsx(
869+
<$.Body ws:id="body">
870+
<$.Tooltip ws:id="tooltip">
871+
<$.TooltipContent ws:id="content"></$.TooltipContent>
872+
<$.TooltipTrigger ws:id={expect.any(String)}></$.TooltipTrigger>
873+
</$.Tooltip>
874+
</$.Body>
875+
).instances
876+
);
877+
});
847878
});
848879

849880
const getWebstudioDataStub = (
@@ -888,24 +919,6 @@ describe("delete instance", () => {
888919
);
889920
});
890921

891-
test("prevent deleting root instance", () => {
892-
// body
893-
// box1
894-
const instances = new Map([
895-
createInstancePair("body", "Body", [{ type: "id", value: "box1" }]),
896-
createInstancePair("box1", "Box", []),
897-
]);
898-
$registeredComponentMetas.set(createFakeComponentMetas({}));
899-
const data = getWebstudioDataStub({ instances });
900-
deleteInstanceMutable(data, ["body"]);
901-
expect(data.instances).toEqual(
902-
new Map([
903-
createInstancePair("body", "Body", [{ type: "id", value: "box1" }]),
904-
createInstancePair("box1", "Box", []),
905-
])
906-
);
907-
});
908-
909922
test("delete instance from collection item", () => {
910923
// body
911924
// list

apps/builder/app/shared/instance-utils.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -496,16 +496,6 @@ export const deleteInstanceMutable = (
496496
data: WebstudioData,
497497
instanceSelector: InstanceSelector
498498
) => {
499-
// @todo tell user they can't delete root
500-
if (instanceSelector.length === 1) {
501-
return false;
502-
}
503-
if (isInstanceDetachable(data.instances, instanceSelector) === false) {
504-
toast.error(
505-
"This instance can not be moved outside of its parent component."
506-
);
507-
return false;
508-
}
509499
const {
510500
instances,
511501
props,

0 commit comments

Comments
 (0)