-
Notifications
You must be signed in to change notification settings - Fork 997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix[dynamic-renderer]: ENG-8440 Button links cannot be used in Angular Gen 2 SDK #3937
base: main
Are you sure you want to change the base?
Changes from all commits
872634a
6680128
bdb5aa1
1804a5c
7c1899f
3d6aa46
2f8ef6b
6d149d8
a922232
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@builder.io/sdk-angular": patch | ||
--- | ||
|
||
Resolved assertion error encountered when dynamically switching components | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { expect } from '@playwright/test'; | ||
import { checkIsRN, test } from '../helpers/index.js'; | ||
import { | ||
cloneContent, | ||
launchEmbedderAndWaitForSdk, | ||
sendPatchOrUpdateMessage, | ||
} from '../helpers/visual-editor.js'; | ||
import { DYNAMIC_BUTTON } from '../specs/dynamic-button.js'; | ||
|
||
test.describe('Dynamic Button', () => { | ||
test('should render a button', async ({ page, sdk, basePort, packageName }) => { | ||
|
||
test.fail(sdk === 'svelte' || sdk === 'oldReact', 'Not showing the href attribute in Svelte'); | ||
test.skip( | ||
packageName === 'nextjs-sdk-next-app' || | ||
packageName === 'gen1-next14-pages' || | ||
packageName === 'gen1-next15-app' || | ||
packageName === 'gen1-remix' | ||
); | ||
await launchEmbedderAndWaitForSdk({ | ||
path: '/dynamic-button', | ||
basePort, | ||
page, | ||
sdk, | ||
}); | ||
|
||
const buttonLocator = checkIsRN(sdk) ? page.frameLocator('iframe').locator('button') : page | ||
.frameLocator('iframe') | ||
.locator('[builder-id="builder-b53d1cc2bcbb481b869207fdd97ee1db"]'); | ||
|
||
await expect(buttonLocator).toHaveText('Click me!'); | ||
const newContent = cloneContent(DYNAMIC_BUTTON); | ||
|
||
// simulating typing in the link field | ||
await sendPatchOrUpdateMessage({ | ||
page, | ||
content: cloneContent(DYNAMIC_BUTTON), | ||
model: 'page', | ||
sdk, | ||
updateFn: () => '#', | ||
path: '/data/blocks/0/component/options/link', | ||
}); | ||
|
||
await sendPatchOrUpdateMessage({ | ||
page, | ||
content: newContent, | ||
model: 'page', | ||
sdk, | ||
updateFn: () => '#g', | ||
path: '/data/blocks/0/component/options/link', | ||
}); | ||
|
||
await sendPatchOrUpdateMessage({ | ||
page, | ||
content: newContent, | ||
model: 'page', | ||
sdk, | ||
updateFn: () => '#go', | ||
path: '/data/blocks/0/component/options/link', | ||
}); | ||
|
||
const updatedButtonLocator = checkIsRN(sdk) ? page.frameLocator('iframe').locator('a') : page | ||
.frameLocator('iframe') | ||
.locator('[builder-id="builder-b53d1cc2bcbb481b869207fdd97ee1db"]'); | ||
|
||
await expect(updatedButtonLocator).toBeVisible(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also can you check the href value? |
||
|
||
await expect(updatedButtonLocator).toHaveAttribute('href', '#go'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
export const DYNAMIC_BUTTON = { | ||
data: { | ||
title: 'dynamic-button-sdk-test', | ||
themeId: false, | ||
blocks: [ | ||
{ | ||
'@type': '@builder.io/sdk:Element', | ||
'@version': 2, | ||
id: 'builder-b53d1cc2bcbb481b869207fdd97ee1db', | ||
component: { | ||
name: 'Core:Button', | ||
options: { | ||
text: 'Click me!', | ||
openLinkInNewTab: false, | ||
}, | ||
}, | ||
responsiveStyles: { | ||
large: { | ||
display: 'flex', | ||
flexDirection: 'column', | ||
position: 'relative', | ||
flexShrink: '0', | ||
boxSizing: 'border-box', | ||
marginTop: '20px', | ||
appearance: 'none', | ||
paddingTop: '15px', | ||
paddingBottom: '15px', | ||
paddingLeft: '25px', | ||
paddingRight: '25px', | ||
backgroundColor: 'black', | ||
color: 'white', | ||
borderRadius: '4px', | ||
textAlign: 'center', | ||
cursor: 'pointer', | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,7 @@ const dynamicComponentTemplate = (tagName) => { | |
export class Dynamic${capitalize(tagName)} { | ||
@Input() attributes!: any; | ||
@Input() actionAttributes?: any; | ||
@Input() tagName?: string; | ||
@ViewChild('v', { read: ElementRef }) v!: ElementRef; | ||
_listenerFns = new Map<string, () => void>(); | ||
constructor(private renderer: Renderer2) {} | ||
|
@@ -121,7 +122,7 @@ const generateComponents = () => { | |
<ng-container *ngIf="useTypeOf(TagName) === 'string'"> | ||
<ng-container | ||
*ngComponentOutlet=" | ||
TagName; | ||
getComponentType(TagName); | ||
inputs: { | ||
attributes: attributes, | ||
actionAttributes: actionAttributes, | ||
|
@@ -196,14 +197,21 @@ export default class DynamicRenderer { | |
|
||
constructor(private vcRef: ViewContainerRef) {} | ||
|
||
private tagComponentMap: { [key: string]: any } = { | ||
${htmlElements.map((el) => `'${el}': Dynamic${capitalize(el)}`).join(',\n ')} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also can you check the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I missed this part. Thank you for adding it! Let me check it |
||
}; | ||
|
||
getComponentType(tagName: string): any { | ||
return this.tagComponentMap[tagName] || null; | ||
} | ||
|
||
ngOnInit() { | ||
if (typeof this.TagName === 'string') { | ||
switch (this.TagName) { | ||
${htmlElements.map((el) => `case '${el}': this.TagName = Dynamic${capitalize(el)}; break;`).join('\n ')} | ||
default: | ||
this.tagName = this.TagName; | ||
this.TagName = DynamicElement; | ||
break; | ||
if (this.tagComponentMap[this.TagName]) { | ||
this.TagName = this.tagComponentMap[this.TagName]; | ||
} else { | ||
Comment on lines
+210
to
+212
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand it correctly, map lookups are faster than switch case statements? and is that why angular was unable to createComponent quickly when we type? 🤯 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yess!! |
||
this.tagName = this.TagName; | ||
this.TagName = DynamicElement; | ||
} | ||
} | ||
this.myContent = [this.vcRef.createEmbeddedView(this.tagnameTemplateRef).rootNodes]; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changeset is too low-level. I prefer changesets to focus on what customer-facing impact they have. In this case, the fact that Visual Editing could cause a crash: