Skip to content

Commit f3e5459

Browse files
committed
Added new CTA card component
Ref https://linear.app/ghost/issue/PLG-309/add-static-html-for-new-cta-card - This is a static card that will form the basis of the new CTA card
1 parent 29e24d7 commit f3e5459

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import KoenigNestedEditor from '../../KoenigNestedEditor';
2+
import PropTypes from 'prop-types';
3+
import React from 'react';
4+
import ReplacementStringsPlugin from '../../../plugins/ReplacementStringsPlugin';
5+
import {Button} from '../Button';
6+
import {ReadOnlyOverlay} from '../ReadOnlyOverlay';
7+
8+
export function CtaCard({
9+
buttonText,
10+
buttonUrl,
11+
htmlEditor,
12+
htmlEditorInitialState,
13+
isEditing,
14+
showButton
15+
}) {
16+
return (
17+
<>
18+
<div className="w-full">
19+
{/* Sponsor label */}
20+
<div className="not-kg-prose border-b border-grey-300 py-3 dark:border-grey-900">
21+
<p className="font-sans text-2xs font-semibold uppercase leading-8 tracking-normal text-grey dark:text-grey-800">Sponsored</p>
22+
</div>
23+
24+
<div className="flex flex-col gap-5 border-b border-grey-300 py-5 dark:border-grey-900">
25+
{/* HTML content */}
26+
<KoenigNestedEditor
27+
autoFocus={true}
28+
hasSettingsPanel={true}
29+
initialEditor={htmlEditor}
30+
initialEditorState={htmlEditorInitialState}
31+
nodes='basic'
32+
placeholderClassName={`bg-transparent whitespace-normal font-serif text-xl !text-grey-500 !dark:text-grey-800 ` }
33+
placeholderText="Write something worth clicking..."
34+
textClassName="w-full bg-transparent whitespace-normal font-serif text-xl text-grey-900 dark:text-grey-200"
35+
>
36+
<ReplacementStringsPlugin />
37+
</KoenigNestedEditor>
38+
39+
{/* Button */}
40+
{ (showButton && (isEditing || (buttonText && buttonUrl))) &&
41+
<div>
42+
<Button color={'accent'} dataTestId="cta-button" placeholder="Add button text" value={buttonText}/>
43+
</div>
44+
}
45+
</div>
46+
47+
{/* Read-only overlay */}
48+
{!isEditing && <ReadOnlyOverlay />}
49+
</div>
50+
</>
51+
);
52+
}
53+
54+
CtaCard.propTypes = {
55+
buttonText: PropTypes.string,
56+
buttonUrl: PropTypes.string,
57+
isEditing: PropTypes.bool,
58+
showButton: PropTypes.bool,
59+
htmlEditor: PropTypes.object,
60+
htmlEditorInitialState: PropTypes.object
61+
};
62+
63+
CtaCard.defaultProps = {
64+
buttonText: '',
65+
buttonUrl: '',
66+
isEditing: false
67+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import EmailIndicatorIcon from '../../../assets/icons/kg-indicator-email.svg?react';
2+
import React from 'react';
3+
import populateEditor from '../../../utils/storybook/populate-storybook-editor.js';
4+
import {BASIC_NODES} from '../../../index.js';
5+
import {CardWrapper} from './../CardWrapper';
6+
import {CtaCard} from './CtaCard';
7+
import {createEditor} from 'lexical';
8+
9+
const displayOptions = {
10+
Default: {isSelected: false, isEditing: false},
11+
Selected: {isSelected: true, isEditing: false},
12+
Editing: {isSelected: true, isEditing: true}
13+
};
14+
15+
const story = {
16+
title: 'Primary cards/CTA card',
17+
component: CtaCard,
18+
subcomponent: {CardWrapper},
19+
argTypes: {
20+
display: {
21+
options: Object.keys(displayOptions),
22+
mapping: displayOptions,
23+
control: {
24+
type: 'radio',
25+
labels: {
26+
Default: 'Default',
27+
Selected: 'Selected',
28+
Editing: 'Editing'
29+
},
30+
defaultValue: displayOptions.Default
31+
}
32+
}
33+
},
34+
parameters: {
35+
status: {
36+
type: 'uiReady'
37+
}
38+
}
39+
};
40+
export default story;
41+
42+
const Template = ({display, value, ...args}) => {
43+
const htmlEditor = createEditor({nodes: BASIC_NODES});
44+
populateEditor({editor: htmlEditor, initialHtml: `${value}`});
45+
return (
46+
<div>
47+
<div className="kg-prose">
48+
<div className="mx-auto my-8 min-w-[initial] max-w-[740px]">
49+
<CardWrapper IndicatorIcon={EmailIndicatorIcon} wrapperStyle='wide' {...display} {...args}>
50+
<CtaCard {...display} {...args} htmlEditor={htmlEditor} />
51+
</CardWrapper>
52+
</div>
53+
</div>
54+
<div className="kg-prose dark bg-black px-4 py-8">
55+
<div className="mx-auto my-8 min-w-[initial] max-w-[740px]">
56+
<CardWrapper IndicatorIcon={EmailIndicatorIcon} wrapperStyle='wide' {...display} {...args}>
57+
<CtaCard {...display} {...args} htmlEditor={htmlEditor} />
58+
</CardWrapper>
59+
</div>
60+
</div>
61+
</div>
62+
);
63+
};
64+
65+
export const Empty = Template.bind({});
66+
Empty.args = {
67+
display: 'Editing',
68+
value: '',
69+
showButton: false,
70+
buttonText: '',
71+
buttonUrl: '',
72+
suggestedUrls: []
73+
};
74+
75+
export const Populated = Template.bind({});
76+
Populated.args = {
77+
display: 'Editing',
78+
value: 'Want to get access to premium content?',
79+
showButton: true,
80+
buttonText: 'Upgrade',
81+
buttonUrl: 'https://ghost.org/',
82+
suggestedUrls: [{label: 'Homepage', value: 'https://localhost.org/'}]
83+
};
84+

0 commit comments

Comments
 (0)