-
-
Notifications
You must be signed in to change notification settings - Fork 80
Updated html markup for CTA card frontend rendering #1442
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
Changes from 9 commits
57244a7
8eec344
37897fb
aece70e
059302d
ac64145
c5c512d
96df53b
19c7141
3eb9140
3777a50
31dcacd
b8ee167
d028e71
8e66d4f
dc8346b
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 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,63 +3,142 @@ import {renderWithVisibility} from '../../utils/visibility'; | |||||||||||||||||
|
|
||||||||||||||||||
| // TODO - this is a placeholder for the cta card web template | ||||||||||||||||||
| function ctaCardTemplate(dataset) { | ||||||||||||||||||
| const backgroundAccent = dataset.backgroundColor === 'accent' ? 'kg-style-accent' : ''; | ||||||||||||||||||
| // Add validation for buttonColor | ||||||||||||||||||
| if (!dataset.buttonColor || !dataset.buttonColor.match(/^[a-zA-Z\d-]+|#([a-fA-F\d]{3}|[a-fA-F\d]{6})$/)) { | ||||||||||||||||||
| dataset.buttonColor = 'accent'; | ||||||||||||||||||
| } | ||||||||||||||||||
| const buttonAccent = dataset.buttonColor === 'accent' ? 'kg-style-accent' : ''; | ||||||||||||||||||
| const buttonStyle = dataset.buttonColor !== 'accent' ? `background-color: ${dataset.buttonColor};` : ''; | ||||||||||||||||||
|
|
||||||||||||||||||
| const buttonStyle = dataset.buttonColor === 'accent' | ||||||||||||||||||
| ? `style="color: ${dataset.buttonTextColor};"` | ||||||||||||||||||
| : `style="background-color: ${dataset.buttonColor}; color: ${dataset.buttonTextColor};"`; | ||||||||||||||||||
| return ` | ||||||||||||||||||
| <div class="cta-card ${backgroundAccent}" data-layout="${dataset.layout}" style="background-color: ${dataset.backgroundColor};"> | ||||||||||||||||||
| ${dataset.hasImage ? `<img src="${dataset.imageUrl}" alt="CTA Image">` : ''} | ||||||||||||||||||
| <div> | ||||||||||||||||||
| ${dataset.textValue} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ${dataset.showButton ? ` | ||||||||||||||||||
| <a href="${dataset.buttonUrl}" class="kg-cta-button ${buttonAccent}" | ||||||||||||||||||
| style="${buttonStyle} color: ${dataset.buttonTextColor};"> | ||||||||||||||||||
| ${dataset.buttonText} | ||||||||||||||||||
| </a> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| <div class="kg-card kg-cta-card kg-cta-bg-${dataset.backgroundColor} kg-cta-${dataset.layout}" data-layout="${dataset.layout}"> | ||||||||||||||||||
| ${dataset.hasSponsorLabel ? ` | ||||||||||||||||||
| <div class="kg-sponsor-label"> | ||||||||||||||||||
| <div class="kg-cta-sponsor-label"> | ||||||||||||||||||
| ${dataset.sponsorLabel} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| <div class="kg-cta-content"> | ||||||||||||||||||
| ${dataset.hasImage ? ` | ||||||||||||||||||
| <div class="kg-cta-image-container"> | ||||||||||||||||||
| <img src="${dataset.imageUrl}" alt="CTA Image"> | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| <div class="kg-cta-content-inner"> | ||||||||||||||||||
| <div class="kg-cta-text"> | ||||||||||||||||||
| ${dataset.textValue} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ${dataset.showButton ? ` | ||||||||||||||||||
| <a href="${dataset.buttonUrl}" class="kg-cta-button ${buttonAccent}" | ||||||||||||||||||
| ${buttonStyle}> | ||||||||||||||||||
| ${dataset.buttonText} | ||||||||||||||||||
| </a> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| </div> | ||||||||||||||||||
| </div> | ||||||||||||||||||
| `; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // TODO - this is a placeholder for the email template | ||||||||||||||||||
| function emailCTATemplate(dataset) { | ||||||||||||||||||
| const buttonStyle = dataset.buttonColor !== 'accent' ? `background-color: ${dataset.buttonColor};` : ''; | ||||||||||||||||||
| const backgroundStyle = `background-color: ${dataset.backgroundColor};`; | ||||||||||||||||||
| const buttonStyle = dataset.buttonColor === 'accent' | ||||||||||||||||||
| ? `color: ${dataset.buttonTextColor};` | ||||||||||||||||||
| : `background-color: ${dataset.buttonColor}; color: ${dataset.buttonTextColor};`; | ||||||||||||||||||
|
|
||||||||||||||||||
| const renderContent = () => { | ||||||||||||||||||
| if (dataset.layout === 'minimal') { | ||||||||||||||||||
| return ` | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td class="kg-cta-content"> | ||||||||||||||||||
| <table border="0" cellpadding="0" cellspacing="0" width="100%" class="kg-cta-content-wrapper"> | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| ${dataset.hasImage ? ` | ||||||||||||||||||
| <td class="kg-cta-image-container" width="64"> | ||||||||||||||||||
| <img src="${dataset.imageUrl}" alt="CTA Image" class="kg-cta-image" width="64" height="64"> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| <td class="kg-cta-content-inner"> | ||||||||||||||||||
| <div class="kg-cta-text"> | ||||||||||||||||||
| ${dataset.textValue} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ${dataset.showButton ? ` | ||||||||||||||||||
| <a href="${dataset.buttonUrl}" | ||||||||||||||||||
| class="kg-cta-button ${dataset.buttonColor === 'accent' ? 'kg-style-accent' : ''}" | ||||||||||||||||||
| style="${buttonStyle}"> | ||||||||||||||||||
| ${dataset.buttonText} | ||||||||||||||||||
| </a> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| </table> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| `; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return ` | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td class="kg-cta-content"> | ||||||||||||||||||
| <table border="0" cellpadding="0" cellspacing="0" width="100%" class="kg-cta-content-wrapper"> | ||||||||||||||||||
| ${dataset.hasImage ? ` | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td class="kg-cta-image-container"> | ||||||||||||||||||
| <table border="0" cellpadding="0" cellspacing="0" width="100%"> | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td> | ||||||||||||||||||
| <img src="${dataset.imageUrl}" alt="CTA Image" class="kg-cta-image"> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| </table> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td class="kg-cta-text"> | ||||||||||||||||||
| ${dataset.textValue} | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| ${dataset.showButton ? ` | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td> | ||||||||||||||||||
| <table border="0" cellpadding="0" cellspacing="0" width="100%"> | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td class="kg-cta-button-container" style="${buttonStyle}"> | ||||||||||||||||||
| <a href="${dataset.buttonUrl}" | ||||||||||||||||||
| class="kg-cta-button ${dataset.buttonColor === 'accent' ? 'kg-style-accent' : ''}" | ||||||||||||||||||
| style="${buttonStyle}"> | ||||||||||||||||||
| ${dataset.buttonText} | ||||||||||||||||||
| </a> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| </table> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| </table> | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| `; | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| return ` | ||||||||||||||||||
| <div class="cta-card-email" style="${backgroundStyle} padding: 16px; text-align: center; border-radius: 8px;"> | ||||||||||||||||||
| ${dataset.hasImage ? `<img src="${dataset.imageUrl}" alt="CTA Image" style="max-width: 100%; border-radius: 4px;">` : ''} | ||||||||||||||||||
| <div class="cta-text" style="margin-top: 12px; color: ${dataset.textColor};"> | ||||||||||||||||||
| ${dataset.textValue} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ${dataset.showButton ? ` | ||||||||||||||||||
| <a href="${dataset.buttonUrl}" class="cta-button" | ||||||||||||||||||
| style="display: inline-block; margin-top: 12px; padding: 10px 16px; | ||||||||||||||||||
| ${buttonStyle} color: ${dataset.buttonTextColor}; text-decoration: none; | ||||||||||||||||||
| border-radius: 4px;"> | ||||||||||||||||||
| ${dataset.buttonText} | ||||||||||||||||||
| </a> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| <table class="kg-card kg-cta-card kg-cta-bg-${dataset.backgroundColor} kg-cta-${dataset.layout}" border="0" cellpadding="0" cellspacing="0" width="100%"> | ||||||||||||||||||
| ${dataset.hasSponsorLabel ? ` | ||||||||||||||||||
| <div class="sponsor-label" style="margin-top: 8px; font-size: 12px; color: #888;"> | ||||||||||||||||||
| ${dataset.sponsorLabel} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| <tr> | ||||||||||||||||||
| <td class="kg-cta-sponsor-label"> | ||||||||||||||||||
| ${dataset.sponsorLabel} | ||||||||||||||||||
| </td> | ||||||||||||||||||
| </tr> | ||||||||||||||||||
| ` : ''} | ||||||||||||||||||
| </div> | ||||||||||||||||||
| ${renderContent()} | ||||||||||||||||||
| </table> | ||||||||||||||||||
| `; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| export function renderCallToActionNode(node, options = {}) { | ||||||||||||||||||
| addCreateDocumentOption(options); | ||||||||||||||||||
| const document = options.createDocument(); | ||||||||||||||||||
|
|
||||||||||||||||||
| const dataset = { | ||||||||||||||||||
| layout: node.layout, | ||||||||||||||||||
| textValue: node.textValue, | ||||||||||||||||||
|
|
@@ -72,10 +151,14 @@ export function renderCallToActionNode(node, options = {}) { | |||||||||||||||||
| backgroundColor: node.backgroundColor, | ||||||||||||||||||
| sponsorLabel: node.sponsorLabel, | ||||||||||||||||||
| hasImage: node.hasImage, | ||||||||||||||||||
| imageUrl: node.imageUrl, | ||||||||||||||||||
| textColor: node.textColor | ||||||||||||||||||
| imageUrl: node.imageUrl | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Add validation for backgroundColor | ||||||||||||||||||
| if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+$/)) { | ||||||||||||||||||
| dataset.backgroundColor = 'white'; | ||||||||||||||||||
| } | ||||||||||||||||||
|
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. 🛠️ Refactor suggestion Align backgroundColor validation with buttonColor validation. The validation pattern for backgroundColor is more restrictive than buttonColor. Consider using the same pattern for consistency. -if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+$/)) {
+if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+|#([a-fA-F\d]{3}|[a-fA-F\d]{6})$/)) {📝 Committable suggestion
Suggested change
|
||||||||||||||||||
|
|
||||||||||||||||||
| if (options.target === 'email') { | ||||||||||||||||||
| const emailDoc = options.createDocument(); | ||||||||||||||||||
| const emailDiv = emailDoc.createElement('div'); | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,8 @@ describe('CallToActionNode', function () { | |
| backgroundColor: 'none', | ||
| hasImage: true, | ||
| imageUrl: 'http://blog.com/image1.jpg', | ||
| imageWidth: 200, | ||
| imageHeight: 100, | ||
| href: '' | ||
| }; | ||
| exportOptions = { | ||
|
|
@@ -69,11 +71,12 @@ describe('CallToActionNode', function () { | |
| callToActionNode.imageUrl.should.equal(dataset.imageUrl); | ||
| callToActionNode.visibility.should.deepEqual(utils.visibility.buildDefaultVisibility()); | ||
| callToActionNode.href.should.equal(dataset.href); | ||
| callToActionNode.imageHeight.should.equal(dataset.imageHeight); | ||
| callToActionNode.imageWidth.should.equal(dataset.imageWidth); | ||
| })); | ||
|
|
||
| it('has setters for all properties', editorTest(function () { | ||
| const callToActionNode = new CallToActionNode(); | ||
|
|
||
| callToActionNode.layout.should.equal('minimal'); | ||
| callToActionNode.layout = 'compact'; | ||
| callToActionNode.layout.should.equal('compact'); | ||
|
|
@@ -122,6 +125,14 @@ describe('CallToActionNode', function () { | |
| callToActionNode.imageUrl = 'http://blog.com/image1.jpg'; | ||
| callToActionNode.imageUrl.should.equal('http://blog.com/image1.jpg'); | ||
|
|
||
| should(callToActionNode.imageHeight).be.null(); | ||
| callToActionNode.imageHeight = 100; | ||
| callToActionNode.imageHeight.should.equal(100); | ||
|
|
||
| should(callToActionNode.imageWidth).be.null(); | ||
| callToActionNode.imageWidth = 200; | ||
| callToActionNode.imageWidth.should.equal(200); | ||
|
|
||
| callToActionNode.href.should.equal(''); | ||
| callToActionNode.href = 'http://blog.com/post1'; | ||
| callToActionNode.href.should.equal('http://blog.com/post1'); | ||
|
|
@@ -208,7 +219,7 @@ describe('CallToActionNode', function () { | |
|
|
||
| const html = element.outerHTML.toString(); | ||
| html.should.containEql('data-layout="minimal"'); | ||
| html.should.containEql('background-color: green'); | ||
| html.should.containEql('kg-cta-bg-green'); | ||
|
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. 🛠️ Refactor suggestion Add test coverage for color validation. The test suite should include cases for color validation, including:
it('validates buttonColor format', editorTest(function () {
const testCases = [
{input: '#123', expected: '#123'},
{input: '#123456', expected: '#123456'},
{input: 'invalid<script>', expected: 'accent'},
{input: undefined, expected: 'accent'}
];
testCases.forEach(({input, expected}) => {
const node = new CallToActionNode({...dataset, buttonColor: input});
const {element} = node.exportDOM(exportOptions);
if (expected === 'accent') {
element.outerHTML.should.containEql('kg-style-accent');
} else {
element.outerHTML.should.containEql(`background-color: ${expected}`);
}
});
})); |
||
| html.should.containEql('background-color: #F0F0F0'); | ||
| html.should.containEql('Get access now'); | ||
| html.should.containEql('http://someblog.com/somepost'); | ||
|
|
@@ -238,8 +249,7 @@ describe('CallToActionNode', function () { | |
| const {element} = callToActionNode.exportDOM(exportOptions); | ||
|
|
||
| const html = element.outerHTML.toString(); | ||
| html.should.containEql('cta-card-email'); | ||
| html.should.containEql('background-color: green'); | ||
| html.should.containEql('kg-cta-bg-green'); | ||
| html.should.containEql('background-color: #F0F0F0'); | ||
| html.should.containEql('Get access now'); | ||
| html.should.containEql('http://someblog.com/somepost'); | ||
|
|
@@ -311,6 +321,8 @@ describe('CallToActionNode', function () { | |
| hasSponsorLabel: true, | ||
| sponsorLabel: '<p>This post is brought to you by our sponsors</p>', | ||
| imageUrl: '/content/images/2022/11/koenig-lexical.jpg', | ||
| imageWidth: 200, | ||
| imageHeight: 100, | ||
| layout: 'minimal', | ||
| showButton: true, | ||
| textValue: '<p><span style="white-space: pre-wrap;">This is a new CTA Card.</span></p>', | ||
|
|
@@ -331,6 +343,8 @@ describe('CallToActionNode', function () { | |
| hasSponsorLabel: true, | ||
| sponsorLabel: '<p>This post is brought to you by our sponsors</p>', | ||
| imageUrl: '/content/images/2022/11/koenig-lexical.jpg', | ||
| imageWidth: 200, | ||
| imageHeight: 100, | ||
| layout: 'minimal', | ||
| showButton: true, | ||
| textValue: '<p><span style="white-space: pre-wrap;">This is a new CTA Card.</span></p>', | ||
|
|
||
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.
I think this can be removed now 😄