diff --git a/.changeset/stupid-plums-hug.md b/.changeset/stupid-plums-hug.md new file mode 100644 index 0000000000..54496e3533 --- /dev/null +++ b/.changeset/stupid-plums-hug.md @@ -0,0 +1,12 @@ +--- +"@builder.io/sdk-angular": patch +"@builder.io/sdk-react-nextjs": patch +"@builder.io/sdk-qwik": patch +"@builder.io/sdk-react": patch +"@builder.io/sdk-react-native": patch +"@builder.io/sdk-solid": patch +"@builder.io/sdk-svelte": patch +"@builder.io/sdk-vue": patch +--- + +Fix: repeat items when they are Symbols diff --git a/packages/sdks-tests/src/e2e-tests/repeat-items-bindings.spec.ts b/packages/sdks-tests/src/e2e-tests/repeat-items-bindings.spec.ts index 360d6cb137..fcafe832cc 100644 --- a/packages/sdks-tests/src/e2e-tests/repeat-items-bindings.spec.ts +++ b/packages/sdks-tests/src/e2e-tests/repeat-items-bindings.spec.ts @@ -15,4 +15,17 @@ test.describe('Repeat items bindings', () => { await expect(page.locator('text=4')).toBeVisible(); await expect(page.locator('text=5')).toBeVisible(); }); + + test('repeat Symbols with content bound to content inputs should render correctly', async ({ + page, + }) => { + // here data prop is { products: [{ header: 'title1' }, { header: 'title2' }, { header: 'title3' }] } + await page.goto('/symbol-with-repeat-input-binding'); + + const promises = []; + for (let i = 1; i <= 3; i++) { + promises.push(expect(page.locator(`text=title${i}`)).toBeVisible()); + } + await Promise.all(promises); + }); }); diff --git a/packages/sdks-tests/src/specs/index.ts b/packages/sdks-tests/src/specs/index.ts index 3ea746978c..7830a652e0 100644 --- a/packages/sdks-tests/src/specs/index.ts +++ b/packages/sdks-tests/src/specs/index.ts @@ -58,6 +58,7 @@ import { CUSTOM_COMPONENTS_MODELS_RESTRICTION } from './custom-components-models import { EDITING_BOX_TO_COLUMN_INNER_LAYOUT } from './editing-columns-inner-layout.js'; import { REACT_NATIVE_STRICT_STYLE_MODE_CONTENT } from './react-native-strict-style-mode.js'; import type { Sdk } from '../helpers/sdk.js'; +import { SYMBOL_WITH_REPEAT_INPUT_BINDING } from './symbol-with-repeat-input-binding.js'; function isBrowser(): boolean { return typeof window !== 'undefined' && typeof document !== 'undefined'; @@ -133,6 +134,7 @@ export const PAGES = { '/custom-components-models-show': CUSTOM_COMPONENTS_MODELS_RESTRICTION, '/custom-components-models-not-show': CUSTOM_COMPONENTS_MODELS_RESTRICTION, '/editing-box-columns-inner-layout': EDITING_BOX_TO_COLUMN_INNER_LAYOUT, + '/symbol-with-repeat-input-binding': SYMBOL_WITH_REPEAT_INPUT_BINDING, } as const; const apiVersionPathToProp = { @@ -236,6 +238,11 @@ export const getProps = async (args: { strictStyleMode: true, }; break; + case '/symbol-with-repeat-input-binding': + extraProps = { + data: { products: [{ header: 'title1' }, { header: 'title2' }, { header: 'title3' }] }, + }; + break; default: break; } diff --git a/packages/sdks-tests/src/specs/symbol-with-repeat-input-binding.ts b/packages/sdks-tests/src/specs/symbol-with-repeat-input-binding.ts new file mode 100644 index 0000000000..890778f0bf --- /dev/null +++ b/packages/sdks-tests/src/specs/symbol-with-repeat-input-binding.ts @@ -0,0 +1,224 @@ +export const SYMBOL_WITH_REPEAT_INPUT_BINDING = { + ownerId: 'ad30f9a246614faaa6a03374f83554c9', + lastUpdateBy: null, + createdDate: 1725872832969, + id: '3413ca2078c24216be71bacf54adbfc9', + '@version': 4, + name: 'data-symbols', + modelId: '17c6065109ef4062ba083f5741f4ee6a', + published: 'published', + meta: { + symbolsUsed: { + f8f9e85ab8504e7db68c1d896d652c91: true, + }, + kind: 'page', + hasLinks: false, + lastPreviewUrl: + 'http://localhost:5173/data-symbols?builder.space=ad30f9a246614faaa6a03374f83554c9&builder.user.permissions=read%2Ccreate%2Cpublish%2CeditCode%2CeditDesigns%2Cadmin%2CeditLayouts%2CeditLayers&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=3413ca2078c24216be71bacf54adbfc9&builder.overrides.3413ca2078c24216be71bacf54adbfc9=3413ca2078c24216be71bacf54adbfc9&builder.overrides.page:/data-symbols=3413ca2078c24216be71bacf54adbfc9&builder.options.locale=Default', + }, + priority: -647, + stage: 'b3ee01559a244a078973f545ad475eba', + query: [ + { + '@type': '@builder.io/core:Query', + property: 'urlPath', + operator: 'is', + value: '/data-symbols', + }, + ], + data: { + themeId: false, + inputs: [], + title: 'data-symbols', + blocks: [ + { + '@type': '@builder.io/sdk:Element', + '@version': 2, + bindings: { + 'component.options.symbol.data.header': + 'var _virtual_index=state.productsItem.header;return _virtual_index', + }, + code: { + bindings: { + 'component.options.symbol.data.header': 'state.productsItem.header', + }, + }, + repeat: { + collection: 'state.products', + }, + id: 'builder-c4c338558fb747068e63cd6b97f037ba', + meta: { + bindingActions: { + component: { + options: { + symbol: { + data: { + header: null, + }, + }, + }, + }, + }, + }, + component: { + name: 'Symbol', + options: { + dataOnly: false, + inheritState: false, + renderToLiquid: false, + symbol: { + model: 'symbol', + entry: 'f8f9e85ab8504e7db68c1d896d652c91', + data: { + header: 'nothing', + }, + ownerId: 'ad30f9a246614faaa6a03374f83554c9', + content: { + ownerId: 'ad30f9a246614faaa6a03374f83554c9', + lastUpdateBy: null, + createdDate: 1725873478428, + id: 'f8f9e85ab8504e7db68c1d896d652c91', + '@version': 4, + name: 'data-symbol-symbol', + modelId: '5e1209efea0045f58d9b871d81b652fa', + published: 'published', + meta: { + hasLinks: false, + kind: 'component', + lastPreviewUrl: + 'https://preview.builder.codes?model=symbol&previewing=true&apiKey=ad30f9a246614faaa6a03374f83554c9&builder.space=ad30f9a246614faaa6a03374f83554c9&builder.user.permissions=read%2Ccreate%2Cpublish%2CeditCode%2CeditDesigns%2Cadmin%2CeditLayouts%2CeditLayers&builder.user.role.name=Admin&builder.user.role.id=admin&builder.cachebust=true&builder.preview=symbol&builder.noCache=true&builder.allowTextEdit=true&__builder_editing__=true&builder.overrides.symbol=f8f9e85ab8504e7db68c1d896d652c91&builder.overrides.f8f9e85ab8504e7db68c1d896d652c91=f8f9e85ab8504e7db68c1d896d652c91&builder.options.locale=Default', + }, + priority: -652, + stage: 'b3ee01559a244a078973f545ad475eba', + query: [], + data: { + inputs: [ + { + '@type': '@builder.io/core:Field', + meta: {}, + name: 'header', + type: 'text', + defaultValue: 'default', + required: false, + subFields: [], + helperText: '', + autoFocus: false, + simpleTextOnly: false, + disallowRemove: false, + broadcast: false, + bubble: false, + hideFromUI: false, + hideFromFieldsEditor: false, + showTemplatePicker: true, + permissionsRequiredToEdit: '', + advanced: false, + copyOnAdd: true, + onChange: '', + behavior: '', + showIf: '', + mandatory: false, + hidden: false, + noPhotoPicker: false, + model: '', + supportsAiGeneration: false, + defaultCollapsed: false, + }, + ], + blocks: [ + { + '@type': '@builder.io/sdk:Element', + '@version': 2, + id: 'builder-ad60c140982d45f58d7190fd84481967', + component: { + name: 'Text', + options: { + text: 'HEADER:
', + }, + }, + responsiveStyles: { + large: { + display: 'flex', + flexDirection: 'column', + position: 'relative', + flexShrink: '0', + boxSizing: 'border-box', + marginTop: '20px', + lineHeight: 'normal', + height: 'auto', + }, + }, + }, + { + '@type': '@builder.io/sdk:Element', + '@version': 2, + bindings: { + 'component.options.text': + 'var _virtual_index=state.header;return _virtual_index', + }, + code: { + bindings: { + 'component.options.text': 'state.header', + }, + }, + id: 'builder-3d3ef778f0734f259daa268388104ee0', + component: { + name: 'Text', + options: { + text: 'Enter some text...', + }, + }, + responsiveStyles: { + large: { + display: 'flex', + flexDirection: 'column', + position: 'relative', + flexShrink: '0', + boxSizing: 'border-box', + marginTop: '20px', + lineHeight: 'normal', + height: 'auto', + }, + }, + }, + ], + }, + metrics: { + clicks: 0, + impressions: 0, + }, + variations: {}, + lastUpdated: 1725874311648, + firstPublished: 1725873773459, + testRatio: 1, + createdBy: 'RuGeCLr9ryVt1xRazFYc72uWwIK2', + lastUpdatedBy: 'RuGeCLr9ryVt1xRazFYc72uWwIK2', + folders: [], + }, + }, + }, + }, + responsiveStyles: { + large: { + display: 'flex', + flexDirection: 'column', + position: 'relative', + flexShrink: '0', + boxSizing: 'border-box', + marginTop: '20px', + }, + }, + }, + ], + }, + metrics: { + clicks: 0, + impressions: 0, + }, + variations: {}, + lastUpdated: 1725874514331, + firstPublished: 1725874440040, + testRatio: 1, + createdBy: 'RuGeCLr9ryVt1xRazFYc72uWwIK2', + lastUpdatedBy: 'RuGeCLr9ryVt1xRazFYc72uWwIK2', + folders: [], +}; diff --git a/packages/sdks/e2e/angular-ssr/src/app/catch-all.component.ts b/packages/sdks/e2e/angular-ssr/src/app/catch-all.component.ts index de3d059b6b..abaaa3eaec 100644 --- a/packages/sdks/e2e/angular-ssr/src/app/catch-all.component.ts +++ b/packages/sdks/e2e/angular-ssr/src/app/catch-all.component.ts @@ -11,6 +11,7 @@ interface BuilderProps { apiKey: string; model: string; content: any; + data?: any; } @Component({ @@ -24,6 +25,7 @@ interface BuilderProps { [trustedHosts]="trustedHosts" [canTrack]="canTrack" [customComponents]="customComponents" + [data]="data" > @@ -38,6 +40,7 @@ export class CatchAllComponent { apiKey: BuilderProps['apiKey'] = 'abcd'; model: BuilderProps['model'] = 'page'; content: BuilderProps['content']; + data: BuilderProps['data']; customComponents = [ { @@ -54,6 +57,7 @@ export class CatchAllComponent { this.content = data.content?.content; this.canTrack = data.content?.canTrack; this.trustedHosts = data.content?.trustedHosts; + this.data = data.content?.data; }); } } diff --git a/packages/sdks/e2e/angular/src/app/app.component.ts b/packages/sdks/e2e/angular/src/app/app.component.ts index 14e11e5f09..6c485a8d07 100644 --- a/packages/sdks/e2e/angular/src/app/app.component.ts +++ b/packages/sdks/e2e/angular/src/app/app.component.ts @@ -14,6 +14,7 @@ interface BuilderProps { apiKey: string; model: string; content: any; + data?: any; } @Component({ @@ -27,6 +28,7 @@ interface BuilderProps { [trustedHosts]="trustedHosts" [canTrack]="canTrack" [customComponents]="customComponents" + [data]="data" > @@ -43,6 +45,7 @@ export class AppComponent { apiKey: BuilderProps['apiKey'] = 'abcd'; model: BuilderProps['model'] = 'page'; content: BuilderProps['content']; + data: BuilderProps['data']; customComponents = [ { @@ -74,5 +77,6 @@ export class AppComponent { this.apiKey = builderProps.apiKey; this.model = builderProps.model; this.apiVersion = builderProps.apiVersion; + this.data = builderProps.data; } } diff --git a/packages/sdks/e2e/react/src/App.tsx b/packages/sdks/e2e/react/src/App.tsx index be5f3776af..256c8c5fd5 100644 --- a/packages/sdks/e2e/react/src/App.tsx +++ b/packages/sdks/e2e/react/src/App.tsx @@ -66,7 +66,7 @@ function App() { {({ data }) => ( + + {(data, index) => ( + + )} + + } + > + + } >