diff --git a/packages/kg-default-nodes/lib/kg-default-nodes.js b/packages/kg-default-nodes/lib/kg-default-nodes.js index 7dfd4b0eb..48ff6493c 100644 --- a/packages/kg-default-nodes/lib/kg-default-nodes.js +++ b/packages/kg-default-nodes/lib/kg-default-nodes.js @@ -4,6 +4,7 @@ import * as markdown from './nodes/markdown/MarkdownNode'; import * as video from './nodes/video/VideoNode'; import * as audio from './nodes/audio/AudioNode'; import * as callout from './nodes/callout/CalloutNode'; +import * as callToAction from './nodes/call-to-action/CallToActionNode'; import * as aside from './nodes/aside/AsideNode'; import * as horizontalrule from './nodes/horizontalrule/HorizontalRuleNode'; import * as html from './nodes/html/HtmlNode'; @@ -54,6 +55,7 @@ export * from './nodes/gallery/GalleryNode'; export * from './nodes/email-cta/EmailCtaNode'; export * from './nodes/signup/SignupNode'; export * from './nodes/collection/CollectionNode'; +export * from './nodes/call-to-action/CallToActionNode'; export * from './nodes/ExtendedTextNode'; export * from './nodes/ExtendedHeadingNode'; export * from './nodes/ExtendedQuoteNode'; @@ -89,6 +91,7 @@ export const DEFAULT_NODES = [ video.VideoNode, audio.AudioNode, callout.CalloutNode, + callToAction.CallToActionNode, aside.AsideNode, horizontalrule.HorizontalRuleNode, html.HtmlNode, diff --git a/packages/kg-default-nodes/lib/nodes/call-to-action/CallToActionNode.js b/packages/kg-default-nodes/lib/nodes/call-to-action/CallToActionNode.js new file mode 100644 index 000000000..6f921848c --- /dev/null +++ b/packages/kg-default-nodes/lib/nodes/call-to-action/CallToActionNode.js @@ -0,0 +1,51 @@ +// eslint-disable-next-line ghost/filenames/match-exported-class +import {generateDecoratorNode} from '../../generate-decorator-node'; + +export class CallToActionNode extends generateDecoratorNode({nodeType: 'call-to-action', + properties: [ + {name: 'layout', default: 'immersive'}, + {name: 'textValue', default: '', wordCount: true}, + {name: 'showButton', default: false}, + {name: 'buttonText', default: ''}, + {name: 'buttonUrl', default: ''}, + {name: 'hasSponsorLabel', default: false}, + {name: 'hasBackground', default: false}, + {name: 'backgroundColor', default: '#123456'}, + {name: 'hasImage', default: false}, + {name: 'imageUrl', default: ''} + ]} +) { + /* override */ + constructor({ + layout, + textValue, + showButton, + buttonText, + buttonUrl, + hasSponsorLabel, + hasBackground, + backgroundColor, + hasImage, + imageUrl + } = {}, key) { + super(key); + this.__layout = layout || 'immersive'; + this.__textValue = textValue || ''; + this.__showButton = showButton || false; + this.__buttonText = buttonText || ''; + this.__buttonUrl = buttonUrl || ''; + this.__hasSponsorLabel = hasSponsorLabel || false; + this.__hasBackground = hasBackground || false; + this.__backgroundColor = backgroundColor || '#123456'; + this.__hasImage = hasImage || false; + this.__imageUrl = imageUrl || ''; + } +} + +export const $createCallToActionNode = (dataset) => { + return new CallToActionNode(dataset); +}; + +export const $isCallToActionNode = (node) => { + return node instanceof CallToActionNode; +}; diff --git a/packages/kg-default-nodes/test/nodes/call-to-action.test.js b/packages/kg-default-nodes/test/nodes/call-to-action.test.js new file mode 100644 index 000000000..706a7ccd4 --- /dev/null +++ b/packages/kg-default-nodes/test/nodes/call-to-action.test.js @@ -0,0 +1,175 @@ +const {dom} = require('../test-utils'); + +const {createHeadlessEditor} = require('@lexical/headless'); + +const {CallToActionNode, $isCallToActionNode} = require('../../'); + +const editorNodes = [CallToActionNode]; + +describe('CallToActionNode', function () { + let editor; + let dataset; + let exportOptions; // eslint-disable-line no-unused-vars + + // NOTE: all tests should use this function, without it you need manual + // try/catch and done handling to avoid assertion failures not triggering + // failed tests + const editorTest = testFn => function (done) { + editor.update(() => { + try { + testFn(); + done(); + } catch (e) { + done(e); + } + }); + }; + + beforeEach(function () { + editor = createHeadlessEditor({nodes: editorNodes}); + dataset = { + layout: 'immersive', + textValue: 'This is a cool advertisement', + showButton: true, + buttonText: 'click me', + buttonUrl: 'http://blog.com/post1', + hasSponsorLabel: true, + hasBackground: true, + backgroundColor: '#123456', + hasImage: true, + imageUrl: 'http://blog.com/image1.jpg' + }; + exportOptions = { + dom + }; + }); + + it('matches node with $isCallToActionNode', editorTest(function () { + const callToActionNode = new CallToActionNode(dataset); + $isCallToActionNode(callToActionNode).should.be.true(); + })); + + describe('data access', function () { + it('has getters for all properties', editorTest(function () { + const callToActionNode = new CallToActionNode(dataset); + + callToActionNode.layout.should.equal(dataset.layout); + callToActionNode.textValue.should.equal(dataset.textValue); + callToActionNode.showButton.should.equal(dataset.showButton); + callToActionNode.buttonText.should.equal(dataset.buttonText); + callToActionNode.buttonUrl.should.equal(dataset.buttonUrl); + callToActionNode.hasSponsorLabel.should.equal(dataset.hasSponsorLabel); + callToActionNode.hasBackground.should.equal(dataset.hasBackground); + callToActionNode.backgroundColor.should.equal(dataset.backgroundColor); + callToActionNode.hasImage.should.equal(dataset.hasImage); + callToActionNode.imageUrl.should.equal(dataset.imageUrl); + })); + + it('has setters for all properties', editorTest(function () { + const callToActionNode = new CallToActionNode(); + + callToActionNode.layout.should.equal('immersive'); + callToActionNode.layout = 'compact'; + callToActionNode.layout.should.equal('compact'); + + callToActionNode.textValue.should.equal(''); + callToActionNode.textValue = 'This is a cool advertisement'; + callToActionNode.textValue.should.equal('This is a cool advertisement'); + + callToActionNode.showButton.should.equal(false); + callToActionNode.showButton = true; + callToActionNode.showButton.should.equal(true); + + callToActionNode.buttonText.should.equal(''); + callToActionNode.buttonText = 'click me'; + callToActionNode.buttonText.should.equal('click me'); + + callToActionNode.buttonUrl.should.equal(''); + callToActionNode.buttonUrl = 'http://blog.com/post1'; + callToActionNode.buttonUrl.should.equal('http://blog.com/post1'); + + callToActionNode.hasSponsorLabel.should.equal(false); + callToActionNode.hasSponsorLabel = true; + + callToActionNode.hasBackground.should.equal(false); + callToActionNode.hasBackground = true; + + callToActionNode.backgroundColor.should.equal('#123456'); + callToActionNode.backgroundColor = '#654321'; + callToActionNode.backgroundColor.should.equal('#654321'); + + callToActionNode.hasImage.should.equal(false); + callToActionNode.hasImage = true; + + callToActionNode.imageUrl.should.equal(''); + callToActionNode.imageUrl = 'http://blog.com/image1.jpg'; + })); + + it('has getDataset() convenience method', editorTest(function () { + const callToActionNode = new CallToActionNode(dataset); + const callToActionNodeDataset = callToActionNode.getDataset(); + + callToActionNodeDataset.should.deepEqual({ + ...dataset + }); + })); + }); + + describe('getType', function () { + it('returns the correct node type', editorTest(function () { + CallToActionNode.getType().should.equal('call-to-action'); + })); + }); + + describe('clone', function () { + it('returns a copy of the current node', editorTest(function () { + const callToActionNode = new CallToActionNode(dataset); + const callToActionNodeDataset = callToActionNode.getDataset(); + const clone = CallToActionNode.clone(callToActionNode); + const cloneDataset = clone.getDataset(); + + cloneDataset.should.deepEqual({...callToActionNodeDataset}); + })); + }); + + describe('urlTransformMap', function () { + // not yet implemented + }); + + describe('hasEditMode', function () { + it('returns true', editorTest(function () { + const callToActionNode = new CallToActionNode(dataset); + callToActionNode.hasEditMode().should.be.true(); + })); + }); + + describe('exportDOM', function () { + // not yet implemented + }); + + describe('exportJSON', function () { + // not yet implemented + }); + + describe('importJSON', function () { + // not yet implemented + }); + + describe('static properties', function () { + it('getType', editorTest(function () { + CallToActionNode.getType().should.equal('call-to-action'); + })); + + it('urlTransformMap', editorTest(function () { + // not yet implemented + })); + }); + + describe('importDom', function () { + // not yet implemented + }); + + describe('getTextContent', function () { + // not yet implemented + }); +});