From a611ae0216c6661d530cc5fce6c17f552b6edf17 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 28 May 2025 00:45:58 +0300 Subject: [PATCH] feat: wrap text with span element when drop into it Here changed insertion logic to wrap rich text with span element instead of Text component when drop into it. This is the last place which creates legacy components. --- .../app/shared/instance-utils.test.tsx | 286 +++++++++--------- apps/builder/app/shared/instance-utils.ts | 2 +- apps/builder/app/shared/tree-utils.ts | 25 +- 3 files changed, 154 insertions(+), 159 deletions(-) diff --git a/apps/builder/app/shared/instance-utils.test.tsx b/apps/builder/app/shared/instance-utils.test.tsx index cc2ebf23052b..a6db4c0d71e3 100644 --- a/apps/builder/app/shared/instance-utils.test.tsx +++ b/apps/builder/app/shared/instance-utils.test.tsx @@ -16,7 +16,6 @@ import type { Asset, Breakpoint, Instance, - Prop, StyleDecl, StyleDeclKey, StyleSource, @@ -172,175 +171,186 @@ const createFontAsset = (id: string, family: string): Asset => { describe("insert instance children", () => { test("insert instance children into empty target", () => { - const instances = toMap([createInstance("body", "Body", [])]); - const data = getWebstudioDataStub({ instances }); - insertInstanceChildrenMutable(data, [{ type: "id", value: "box" }], { - parentSelector: ["body"], + const data = renderData( + + ); + const [div] = renderTemplate( + + ).instances; + data.instances.set(div.id, div); + insertInstanceChildrenMutable(data, [{ type: "id", value: "divId" }], { + parentSelector: ["bodyId"], position: "end", }); - expect(data.instances).toEqual( - toMap([createInstance("body", "Body", [{ type: "id", value: "box" }])]) + expect(data).toEqual( + renderData( + + + + ) ); }); test("insert instance children into the end of target", () => { - const instances = toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - ]); - const data = getWebstudioDataStub({ instances }); - insertInstanceChildrenMutable(data, [{ type: "id", value: "box" }], { - parentSelector: ["body"], + const data = renderData( + + + + ); + const [div] = renderTemplate( + + ).instances; + data.instances.set(div.id, div); + insertInstanceChildrenMutable(data, [{ type: "id", value: "divId" }], { + parentSelector: ["bodyId"], position: "end", }); - expect(data.instances).toEqual( - toMap([ - createInstance("body", "Body", [ - { type: "id", value: "text" }, - { type: "id", value: "box" }, - ]), - ]) + expect(data).toEqual( + renderData( + + + + + ) ); }); test("insert instance children into the start of target", () => { - const instances = toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - ]); - const data = getWebstudioDataStub({ instances }); - insertInstanceChildrenMutable(data, [{ type: "id", value: "box" }], { - parentSelector: ["body"], + const data = renderData( + + + + ); + const [div] = renderTemplate( + + ).instances; + data.instances.set(div.id, div); + insertInstanceChildrenMutable(data, [{ type: "id", value: "divId" }], { + parentSelector: ["bodyId"], position: 0, }); - expect(data.instances).toEqual( - toMap([ - createInstance("body", "Body", [ - { type: "id", value: "box" }, - { type: "id", value: "text" }, - ]), - ]) + expect(data).toEqual( + renderData( + + + + + ) ); }); test("insert instance children at the start of text", () => { - const instances = toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - createInstance("text", "Text", [{ type: "text", value: "text" }]), - ]); - const data = getWebstudioDataStub({ instances }); - insertInstanceChildrenMutable(data, [{ type: "id", value: "box" }], { - parentSelector: ["text", "body"], + const data = renderData( + + + text + + + ); + const [div] = renderTemplate( + + ).instances; + data.instances.set(div.id, div); + insertInstanceChildrenMutable(data, [{ type: "id", value: "divId" }], { + parentSelector: ["textId", "bodyId"], position: 0, }); - const [_bodyId, _textId, spanId] = data.instances.keys(); - expect(data.instances).toEqual( - toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - createInstance("text", "Text", [ - { type: "id", value: "box" }, - { type: "id", value: spanId }, - ]), - createInstance(spanId, "Text", [{ type: "text", value: "text" }]), - ]) - ); - expect(data.props).toEqual( - toMap([ - { - id: expect.any(String) as unknown as string, - instanceId: spanId, - name: "tag", - type: "string", - value: "span", - }, - ]) + const [_bodyId, _textId, _divId, spanId] = data.instances.keys(); + expect(data).toEqual( + renderData( + + + + + text + + + + ) ); }); test("insert instance children at the end of text", () => { - const instances = toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - createInstance("text", "Text", [{ type: "text", value: "text" }]), - ]); - const data = getWebstudioDataStub({ instances }); - insertInstanceChildrenMutable(data, [{ type: "id", value: "box" }], { - parentSelector: ["text", "body"], + const data = renderData( + + + text + + + ); + const [div] = renderTemplate( + + ).instances; + data.instances.set(div.id, div); + insertInstanceChildrenMutable(data, [{ type: "id", value: "divId" }], { + parentSelector: ["textId", "bodyId"], position: "end", }); - const [_bodyId, _textId, spanId] = data.instances.keys(); - expect(data.instances).toEqual( - toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - createInstance("text", "Text", [ - { type: "id", value: spanId }, - { type: "id", value: "box" }, - ]), - createInstance(spanId, "Text", [{ type: "text", value: "text" }]), - ]) - ); - expect(data.props).toEqual( - toMap([ - { - id: expect.any(String) as unknown as string, - instanceId: spanId, - name: "tag", - type: "string", - value: "span", - }, - ]) + const [_bodyId, _textId, _divId, spanId] = data.instances.keys(); + expect(data).toEqual( + renderData( + + + + text + + + + + ) ); }); test("insert instance children between text children", () => { - const instances = toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - createInstance("text", "Text", [ - { type: "id", value: "bold" }, - { type: "text", value: "text" }, - { type: "id", value: "italic" }, - ]), - createInstance("bold", "Bold", [{ type: "text", value: "bold" }]), - createInstance("italic", "Italic", [{ type: "text", value: "italic" }]), - ]); - const data = getWebstudioDataStub({ instances }); - insertInstanceChildrenMutable(data, [{ type: "id", value: "box" }], { - parentSelector: ["text", "body"], + const data = renderData( + + + + strong + + text + + emphasis + + + + ); + const [div] = renderTemplate( + + ).instances; + data.instances.set(div.id, div); + insertInstanceChildrenMutable(data, [{ type: "id", value: "divId" }], { + parentSelector: ["textId", "bodyId"], position: 1, }); - const [_bodyId, _textId, _boldId, _italicId, leftSpanId, rightSpanId] = - data.instances.keys(); - expect(data.instances).toEqual( - toMap([ - createInstance("body", "Body", [{ type: "id", value: "text" }]), - createInstance("text", "Text", [ - { type: "id", value: leftSpanId }, - { type: "id", value: "box" }, - { type: "id", value: rightSpanId }, - ]), - createInstance("bold", "Bold", [{ type: "text", value: "bold" }]), - createInstance("italic", "Italic", [{ type: "text", value: "italic" }]), - createInstance(leftSpanId, "Text", [{ type: "id", value: "bold" }]), - createInstance(rightSpanId, "Text", [ - { type: "text", value: "text" }, - { type: "id", value: "italic" }, - ]), - ]) - ); - expect(data.props).toEqual( - toMap([ - { - id: expect.any(String) as unknown as string, - instanceId: leftSpanId, - name: "tag", - type: "string", - value: "span", - }, - { - id: expect.any(String) as unknown as string, - instanceId: rightSpanId, - name: "tag", - type: "string", - value: "span", - }, - ]) + const [ + _bodyId, + _textId, + _strongId, + _emId, + _divId, + leftSpanId, + rightSpanId, + ] = data.instances.keys(); + expect(data).toEqual( + renderData( + + + + + strong + + + + + text + + emphasis + + + + + ) ); }); }); diff --git a/apps/builder/app/shared/instance-utils.ts b/apps/builder/app/shared/instance-utils.ts index 303b9a6a33d7..0519e9bff493 100644 --- a/apps/builder/app/shared/instance-utils.ts +++ b/apps/builder/app/shared/instance-utils.ts @@ -207,7 +207,7 @@ export const findAllEditableInstanceSelector = ({ }; export const insertInstanceChildrenMutable = ( - data: WebstudioData, + data: Omit, children: Instance["children"], insertTarget: Insertable ) => { diff --git a/apps/builder/app/shared/tree-utils.ts b/apps/builder/app/shared/tree-utils.ts index 9a5fcf50179f..0a578532c1f7 100644 --- a/apps/builder/app/shared/tree-utils.ts +++ b/apps/builder/app/shared/tree-utils.ts @@ -3,7 +3,6 @@ import { shallowEqual } from "shallow-equal"; import type { Instance, Instances, - Prop, Props, StyleDecl, Styles, @@ -11,7 +10,7 @@ import type { StyleSourceSelection, WsComponentMeta, } from "@webstudio-is/sdk"; -import { collectionComponent } from "@webstudio-is/sdk"; +import { collectionComponent, elementComponent } from "@webstudio-is/sdk"; import { isRichTextTree } from "./content-model"; // slots can have multiple parents so instance should be addressed @@ -170,19 +169,12 @@ export const wrapEditableChildrenAroundDropTargetMutable = ( const leftSpan: Instance = { id: nanoid(), type: "instance", - component: "Text", + component: elementComponent, + tag: "span", children: parentInstance.children.slice(0, position), }; newChildren.push({ type: "id", value: leftSpan.id }); instances.set(leftSpan.id, leftSpan); - const tagProp: Prop = { - id: nanoid(), - instanceId: leftSpan.id, - type: "string", - name: "tag", - value: "span", - }; - props.set(tagProp.id, tagProp); newPosition = 1; } // create right span when not in the end @@ -190,19 +182,12 @@ export const wrapEditableChildrenAroundDropTargetMutable = ( const rightSpan: Instance = { id: nanoid(), type: "instance", - component: "Text", + component: elementComponent, + tag: "span", children: parentInstance.children.slice(position), }; newChildren.push({ type: "id", value: rightSpan.id }); instances.set(rightSpan.id, rightSpan); - const tagProp: Prop = { - id: nanoid(), - instanceId: rightSpan.id, - type: "string", - name: "tag", - value: "span", - }; - props.set(tagProp.id, tagProp); } parentInstance.children = newChildren; return {