Skip to content

Commit a2b0dcf

Browse files
committed
fix: consider Text and Link components as rich text (#5334)
This prevents editing Text and Link components parent and instead enforces editing these components as rich text containers.
1 parent 837f6bd commit a2b0dcf

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

apps/builder/app/shared/content-model.test.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,24 @@ describe("rich text tree", () => {
768768
).toEqual(["paragraphId", "bodyId"]);
769769
});
770770

771+
test("treat Link component as container when look for closest rich text", () => {
772+
expect(
773+
findClosestRichText({
774+
...renderData(
775+
<ws.element ws:tag="body" ws:id="bodyId">
776+
<ws.element ws:tag="span" ws:id="spanId">
777+
<$.Link ws:id="linkId">
778+
<$.Bold ws:id="boldId">link</$.Bold>
779+
</$.Link>
780+
</ws.element>
781+
</ws.element>
782+
),
783+
metas: defaultMetas,
784+
instanceSelector: ["linkId", "spanId", "bodyId"],
785+
})
786+
).toEqual(["linkId", "spanId", "bodyId"]);
787+
});
788+
771789
test("treat body as rich text when has text inside", () => {
772790
expect(
773791
findClosestRichText({
@@ -1049,4 +1067,22 @@ describe("closest non textual container", () => {
10491067
})
10501068
).toEqual(["divId", "bodyId"]);
10511069
});
1070+
1071+
test("treat Link component as rich text container", () => {
1072+
expect(
1073+
findClosestNonTextualContainer({
1074+
...renderData(
1075+
<ws.element ws:tag="body" ws:id="bodyId">
1076+
<ws.element ws:tag="span" ws:id="spanId">
1077+
<$.Link ws:id="linkId">
1078+
<$.Bold ws:id="boldId">link</$.Bold>
1079+
</$.Link>
1080+
</ws.element>
1081+
</ws.element>
1082+
),
1083+
metas: defaultMetas,
1084+
instanceSelector: ["boldId", "linkId", "spanId", "bodyId"],
1085+
})
1086+
).toEqual(["spanId", "bodyId"]);
1087+
});
10521088
});

apps/builder/app/shared/content-model.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { elementsByTag } from "@webstudio-is/html-data";
22
import {
3+
elementComponent,
34
parseComponentName,
45
type ContentModel,
56
type Instance,
@@ -416,6 +417,15 @@ export const richTextContentTags = new Set<undefined | string>([
416417
"span",
417418
]);
418419

420+
export const richTextContentComponents = new Set<undefined | string>([
421+
elementComponent,
422+
"Subscript",
423+
"Bold",
424+
"Italic",
425+
"RichTextLink",
426+
"Span",
427+
]);
428+
419429
/**
420430
* textual placeholder is used when no content specified while in builder
421431
* also signals to not insert components inside unless dropped explicitly
@@ -470,6 +480,34 @@ const findContentTags = ({
470480
return tags;
471481
};
472482

483+
const findContentComponents = ({
484+
instances,
485+
instance,
486+
_components: components = new Set(),
487+
}: {
488+
instances: Instances;
489+
instance: Instance;
490+
_components?: Set<undefined | string>;
491+
}) => {
492+
for (const child of instance.children) {
493+
if (child.type === "id") {
494+
const childInstance = instances.get(child.value);
495+
// consider collection item as well
496+
if (childInstance === undefined) {
497+
components.add(undefined);
498+
continue;
499+
}
500+
components.add(childInstance.component);
501+
findContentComponents({
502+
instances,
503+
instance: childInstance,
504+
_components: components,
505+
});
506+
}
507+
}
508+
return components;
509+
};
510+
473511
export const isRichTextTree = ({
474512
instanceId,
475513
instances,
@@ -510,10 +548,15 @@ export const isRichTextTree = ({
510548
metas,
511549
instance,
512550
});
551+
const contentComponents = findContentComponents({
552+
instances,
553+
instance,
554+
});
513555
return (
514556
isRichText &&
515557
// rich text must contain only supported elements in editor
516558
setIsSubsetOf(contentTags, richTextContentTags) &&
559+
setIsSubsetOf(contentComponents, richTextContentComponents) &&
517560
// rich text cannot contain only span and only link
518561
// those links and spans are containers in such cases
519562
!setIsSubsetOf(contentTags, new Set(richTextPlaceholders.keys()))
@@ -675,7 +718,14 @@ export const findClosestNonTextualContainer = ({
675718
metas,
676719
instance,
677720
});
678-
if (setIsSubsetOf(contentTags, richTextContentTags)) {
721+
const contentComponents = findContentComponents({
722+
instances,
723+
instance,
724+
});
725+
if (
726+
setIsSubsetOf(contentTags, richTextContentTags) &&
727+
setIsSubsetOf(contentComponents, richTextContentComponents)
728+
) {
679729
hasText = true;
680730
}
681731
if (!hasText) {

0 commit comments

Comments
 (0)