Skip to content

Commit 12c81b3

Browse files
committed
merge main
2 parents 92ac1e7 + 8c0d646 commit 12c81b3

File tree

6 files changed

+363
-29
lines changed

6 files changed

+363
-29
lines changed

.changeset/angry-doors-scream.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@builder.io/sdk-angular': patch
3+
---
4+
5+
Fix: New child blocks getting added at the top while visual editing. This issue occurred with components that use children for rendering, for example - Section.

packages/sdks-tests/src/e2e-tests/editing.spec.ts

+180
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ADD_A_TEXT_BLOCK } from '../specs/duplicated-content-using-nested-symbo
1919
import { EDITING_STYLES } from '../specs/editing-styles.js';
2020
import { ACCORDION_WITH_NO_DETAIL } from '../specs/accordion.js';
2121
import { NEW_BLOCK_ADD, NEW_BLOCK_ADD_2 } from '../specs/new-block-add.js';
22+
import { SECTION_CHILDREN } from '../specs/section-children.js';
2223

2324
const editorTests = ({
2425
noTrustedHosts,
@@ -644,4 +645,183 @@ test.describe('Visual Editing', () => {
644645
await page.frameLocator('iframe').getByText('new text').waitFor({ state: 'hidden' });
645646
});
646647
});
648+
649+
test.describe('New Block addition and deletion with components using props.children / slots', () => {
650+
test('should add new block below the last block', async ({
651+
page,
652+
basePort,
653+
sdk,
654+
packageName,
655+
}) => {
656+
test.skip(checkIsGen1React(sdk));
657+
test.skip(packageName === 'nextjs-sdk-next-app');
658+
test.skip(
659+
packageName === 'vue',
660+
`Failing on the CI: TypeError: Cannot read properties of null (reading 'namespaceURI')`
661+
);
662+
663+
await launchEmbedderAndWaitForSdk({ path: '/section-children', basePort, page, sdk });
664+
665+
const newContent = cloneContent(SECTION_CHILDREN);
666+
newContent.data.blocks[0].children.push({
667+
'@type': '@builder.io/sdk:Element',
668+
'@version': 2,
669+
id: 'builder-421fe741cdab4a5181fe83ffa0af7ff6',
670+
component: {
671+
name: 'Text',
672+
options: { text: 'new text' },
673+
},
674+
});
675+
676+
await sendContentUpdateMessage({
677+
page,
678+
newContent,
679+
model: 'page',
680+
});
681+
await page.frameLocator('iframe').getByText('new text').waitFor();
682+
683+
const textBlocks = await page.frameLocator('iframe').getByText('text 2').all();
684+
expect(textBlocks.length).toBe(1);
685+
const newTextBlockBox = await page.frameLocator('iframe').getByText('new text').boundingBox();
686+
expect(newTextBlockBox).toBeDefined();
687+
const textBlockBox = await textBlocks[0].boundingBox();
688+
expect(textBlockBox).toBeDefined();
689+
690+
if (!newTextBlockBox || !textBlockBox) {
691+
throw new Error('New text block or text block not found');
692+
}
693+
694+
expect(newTextBlockBox.y).toBeGreaterThan(textBlockBox.y);
695+
});
696+
697+
test('should add new block in the middle', async ({ page, basePort, sdk, packageName }) => {
698+
test.skip(checkIsGen1React(sdk));
699+
test.skip(packageName === 'nextjs-sdk-next-app');
700+
test.skip(
701+
packageName === 'vue',
702+
`Failing on the CI: TypeError: Cannot read properties of null (reading 'namespaceURI')`
703+
);
704+
705+
await launchEmbedderAndWaitForSdk({ path: '/section-children', basePort, page, sdk });
706+
707+
const newContent = cloneContent(SECTION_CHILDREN);
708+
709+
newContent.data.blocks[0].children.splice(1, 0, {
710+
'@type': '@builder.io/sdk:Element',
711+
'@version': 2,
712+
id: 'builder-421fe741cdab4a5181fe83ffa0af7ff6',
713+
component: { name: 'Text', options: { text: 'add to middle' } },
714+
});
715+
716+
await sendContentUpdateMessage({
717+
page,
718+
newContent,
719+
model: 'page',
720+
});
721+
await page.frameLocator('iframe').getByText('add to middle').waitFor();
722+
723+
const topTextBlockBox = await page.frameLocator('iframe').getByText('text 1').boundingBox();
724+
const endTextBlockBox = await page.frameLocator('iframe').getByText('text 2').boundingBox();
725+
const middleTextBlockBox = await page
726+
.frameLocator('iframe')
727+
.getByText('add to middle')
728+
.boundingBox();
729+
730+
expect(middleTextBlockBox).toBeDefined();
731+
expect(topTextBlockBox).toBeDefined();
732+
expect(endTextBlockBox).toBeDefined();
733+
734+
if (!middleTextBlockBox || !topTextBlockBox || !endTextBlockBox) {
735+
throw new Error('Middle text block or text block not found');
736+
}
737+
738+
expect(middleTextBlockBox.y).toBeGreaterThan(topTextBlockBox.y);
739+
expect(middleTextBlockBox.y).toBeLessThan(endTextBlockBox.y);
740+
});
741+
742+
test('should add new block at the top', async ({ page, basePort, sdk, packageName }) => {
743+
test.skip(checkIsGen1React(sdk));
744+
test.skip(packageName === 'nextjs-sdk-next-app');
745+
test.skip(
746+
packageName === 'vue',
747+
`Failing on the CI: TypeError: Cannot read properties of null (reading 'namespaceURI')`
748+
);
749+
750+
await launchEmbedderAndWaitForSdk({ path: '/section-children', basePort, page, sdk });
751+
752+
const newContent = cloneContent(SECTION_CHILDREN);
753+
newContent.data.blocks[0].children.unshift({
754+
'@type': '@builder.io/sdk:Element',
755+
'@version': 2,
756+
id: 'builder-421fe741cdab4a5181fe83ffa0af7ff6',
757+
component: {
758+
name: 'Text',
759+
options: { text: 'add to top' },
760+
},
761+
});
762+
763+
await sendContentUpdateMessage({
764+
page,
765+
newContent,
766+
model: 'page',
767+
});
768+
await page.frameLocator('iframe').getByText('add to top').waitFor();
769+
770+
const textBlocks = await page.frameLocator('iframe').getByText('text 1').all();
771+
expect(textBlocks.length).toBe(1);
772+
const newTextBlockBox = await page
773+
.frameLocator('iframe')
774+
.getByText('add to top')
775+
.boundingBox();
776+
expect(newTextBlockBox).toBeDefined();
777+
const textBlockBox = await textBlocks[0].boundingBox();
778+
expect(textBlockBox).toBeDefined();
779+
780+
if (!newTextBlockBox || !textBlockBox) {
781+
throw new Error('New text block or text block not found');
782+
}
783+
784+
expect(newTextBlockBox.y).toBeLessThan(textBlockBox.y);
785+
});
786+
787+
test('deleting a newly added block should remove it from the DOM', async ({
788+
page,
789+
basePort,
790+
sdk,
791+
packageName,
792+
}) => {
793+
test.skip(checkIsGen1React(sdk));
794+
test.skip(packageName === 'nextjs-sdk-next-app');
795+
test.skip(
796+
packageName === 'vue',
797+
`Failing on the CI: TypeError: Cannot read properties of null (reading 'namespaceURI')`
798+
);
799+
800+
await launchEmbedderAndWaitForSdk({ path: '/section-children', basePort, page, sdk });
801+
802+
const newContent = cloneContent(SECTION_CHILDREN);
803+
newContent.data.blocks[0].children.push({
804+
'@type': '@builder.io/sdk:Element',
805+
'@version': 2,
806+
id: 'builder-421fe741cdab4a5181fe83ffa0af7ff6',
807+
component: {
808+
name: 'Text',
809+
options: { text: 'new text' },
810+
},
811+
});
812+
813+
await sendContentUpdateMessage({
814+
page,
815+
newContent,
816+
model: 'page',
817+
});
818+
await page.frameLocator('iframe').getByText('new text').waitFor();
819+
820+
const updatedContent = cloneContent(SECTION_CHILDREN);
821+
updatedContent.data.blocks[0].children.pop();
822+
823+
await sendContentUpdateMessage({ page, newContent: updatedContent, model: 'page' });
824+
await page.frameLocator('iframe').getByText('new text').waitFor({ state: 'hidden' });
825+
});
826+
});
647827
});

packages/sdks-tests/src/specs/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import { CUSTOM_CODE_DOM_UPDATE } from './custom-code-dom-update.js';
9090
import { NEW_BLOCK_ADD } from './new-block-add.js';
9191
import { DYNAMIC_BUTTON } from './dynamic-button.js';
9292
import { COLUMNS_VERTICAL_CENTERING } from './columns-vertical-centering.js';
93+
import { SECTION_CHILDREN } from './section-children.js';
9394

9495
function isBrowser(): boolean {
9596
return typeof window !== 'undefined' && typeof document !== 'undefined';
@@ -285,6 +286,7 @@ export const PAGES: Record<string, Page> = {
285286
'/new-block-add': { content: NEW_BLOCK_ADD },
286287
'/dynamic-button': { content: DYNAMIC_BUTTON },
287288
'/columns-vertical-centering': { content: COLUMNS_VERTICAL_CENTERING },
289+
'/section-children': { content: SECTION_CHILDREN },
288290
} as const;
289291

290292
export type Path = keyof typeof PAGES;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
export const SECTION_CHILDREN = {
2+
ownerId: 'ad30f9a246614faaa6a03374f83554c9',
3+
lastUpdateBy: null,
4+
createdDate: 1742361329592,
5+
id: 'a6c1c51873a94f2d9d74939e3a995bc5',
6+
'@version': 4,
7+
name: 'section-children-angular',
8+
modelId: '17c6065109ef4062ba083f5741f4ee6a',
9+
published: 'draft',
10+
meta: {
11+
kind: 'page',
12+
lastPreviewUrl:
13+
'http://localhost:4200/section-children-angular?builder.space=ad30f9a246614faaa6a03374f83554c9&builder.user.permissions=read%2Ccreate%2Cpublish%2CeditCode%2CeditDesigns%2Cadmin%2CeditLayouts%2CeditLayers%2CeditContentPriority&builder.user.role.name=Admin&builder.user.role.id=admin&builder.cachebust=true&builder.preview=page&builder.noCache=true&builder.allowTextEdit=true&__builder_editing__=true&builder.overrides.page=a6c1c51873a94f2d9d74939e3a995bc5&builder.overrides.a6c1c51873a94f2d9d74939e3a995bc5=a6c1c51873a94f2d9d74939e3a995bc5&builder.overrides.page:/section-children-angular=a6c1c51873a94f2d9d74939e3a995bc5&builder.options.locale=Default',
14+
hasLinks: false,
15+
},
16+
priority: -1010,
17+
query: [
18+
{
19+
'@type': '@builder.io/core:Query',
20+
property: 'urlPath',
21+
operator: 'is',
22+
value: '/section-children-angular',
23+
},
24+
],
25+
data: {
26+
themeId: false,
27+
title: 'section-children-angular',
28+
blocks: [
29+
{
30+
'@type': '@builder.io/sdk:Element',
31+
'@version': 2,
32+
id: 'builder-4777cbd5400240fcb5ec3053147350ac',
33+
component: {
34+
name: 'Core:Section',
35+
options: {
36+
maxWidth: 1200,
37+
lazyLoad: false,
38+
},
39+
},
40+
children: [
41+
{
42+
'@type': '@builder.io/sdk:Element',
43+
'@version': 2,
44+
id: 'builder-f1ef479d4fc24c3db13f67f5972bbfa4',
45+
component: {
46+
name: 'Text',
47+
options: {
48+
text: 'text 1',
49+
},
50+
},
51+
responsiveStyles: {
52+
large: {
53+
display: 'flex',
54+
flexDirection: 'column',
55+
position: 'relative',
56+
flexShrink: '0',
57+
boxSizing: 'border-box',
58+
marginTop: '20px',
59+
lineHeight: 'normal',
60+
height: 'auto',
61+
},
62+
},
63+
},
64+
{
65+
'@type': '@builder.io/sdk:Element',
66+
'@version': 2,
67+
id: 'builder-ed79c46cb43848548933175fadb75344',
68+
component: {
69+
name: 'Text',
70+
options: {
71+
text: 'text 2',
72+
},
73+
},
74+
responsiveStyles: {
75+
large: {
76+
display: 'flex',
77+
flexDirection: 'column',
78+
position: 'relative',
79+
flexShrink: '0',
80+
boxSizing: 'border-box',
81+
marginTop: '20px',
82+
lineHeight: 'normal',
83+
height: 'auto',
84+
},
85+
},
86+
},
87+
],
88+
responsiveStyles: {
89+
large: {
90+
display: 'flex',
91+
flexDirection: 'column',
92+
position: 'relative',
93+
flexShrink: '0',
94+
boxSizing: 'border-box',
95+
marginTop: '0px',
96+
paddingLeft: '20px',
97+
paddingRight: '20px',
98+
paddingTop: '20px',
99+
paddingBottom: '20px',
100+
minHeight: '100px',
101+
},
102+
},
103+
},
104+
],
105+
},
106+
metrics: {
107+
clicks: 0,
108+
impressions: 0,
109+
},
110+
variations: {},
111+
lastUpdated: 1742365828625,
112+
testRatio: 1,
113+
createdBy: 'RuGeCLr9ryVt1xRazFYc72uWwIK2',
114+
lastUpdatedBy: 'RuGeCLr9ryVt1xRazFYc72uWwIK2',
115+
folders: [],
116+
};

0 commit comments

Comments
 (0)