Send test functionality for templates and campaigns
This commit is contained in:
parent
7e52000219
commit
2c73c536b7
22 changed files with 719 additions and 69 deletions
9
client/src/lib/bootstrap-components.js
vendored
9
client/src/lib/bootstrap-components.js
vendored
|
|
@ -210,7 +210,8 @@ class ModalDialog extends Component {
|
|||
onCloseAsync: PropTypes.func,
|
||||
onButtonClickAsync: PropTypes.func,
|
||||
buttons: PropTypes.array,
|
||||
hidden: PropTypes.bool
|
||||
hidden: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -281,7 +282,11 @@ class ModalDialog extends Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<div ref={(domElem) => { this.domModal = domElem; }} className="modal fade" tabIndex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div
|
||||
ref={(domElem) => { this.domModal = domElem; }}
|
||||
className={'modal fade' + (props.className ? ' ' + props.className : '')}
|
||||
tabIndex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
|
|
|
|||
|
|
@ -43,25 +43,32 @@ class CKEditorSandbox extends Component {
|
|||
const trustedUrlBase = getTrustedUrl();
|
||||
const sandboxUrlBase = getSandboxUrl();
|
||||
const publicUrlBase = getPublicUrl();
|
||||
const html = this.props.initialHtml && base(this.props.initialHtml, trustedUrlBase, sandboxUrlBase, publicUrlBase);
|
||||
const source = this.props.initialSource && base(this.props.initialSource, trustedUrlBase, sandboxUrlBase, publicUrlBase);
|
||||
|
||||
this.state = {
|
||||
html
|
||||
source
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
entityTypeId: PropTypes.string,
|
||||
entityId: PropTypes.number,
|
||||
initialHtml: PropTypes.string
|
||||
initialSource: PropTypes.string
|
||||
}
|
||||
|
||||
async exportState(method, params) {
|
||||
const trustedUrlBase = getTrustedUrl();
|
||||
const sandboxUrlBase = getSandboxUrl();
|
||||
const publicUrlBase = getPublicUrl();
|
||||
|
||||
const preHtml = '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body>';
|
||||
const postHtml = '</body></html>';
|
||||
|
||||
const unbasedSource = unbase(this.state.source, trustedUrlBase, sandboxUrlBase, publicUrlBase, true);
|
||||
|
||||
return {
|
||||
html: unbase(this.state.html, trustedUrlBase, sandboxUrlBase, publicUrlBase, true)
|
||||
source: unbasedSource,
|
||||
html: preHtml + unbasedSource + postHtml
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -119,9 +126,9 @@ class CKEditorSandbox extends Component {
|
|||
return (
|
||||
<div className={styles.sandbox}>
|
||||
<CKEditor ref={node => this.node = node}
|
||||
content={this.state.html}
|
||||
content={this.state.source}
|
||||
events={{
|
||||
change: evt => this.setState({html: evt.editor.getData()}),
|
||||
change: evt => this.setState({source: evt.editor.getData()}),
|
||||
}}
|
||||
config={config}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -29,8 +29,9 @@ export class CKEditorHost extends Component {
|
|||
static propTypes = {
|
||||
entityTypeId: PropTypes.string,
|
||||
entity: PropTypes.object,
|
||||
initialHtml: PropTypes.string,
|
||||
initialSource: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onTestSend: PropTypes.func,
|
||||
onFullscreenAsync: PropTypes.func
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ export class CKEditorHost extends Component {
|
|||
const editorData = {
|
||||
entityTypeId: this.props.entityTypeId,
|
||||
entityId: this.props.entity.id,
|
||||
initialHtml: this.props.initialHtml
|
||||
initialSource: this.props.initialSource
|
||||
};
|
||||
|
||||
const tokenData = {
|
||||
|
|
@ -89,6 +90,7 @@ export class CKEditorHost extends Component {
|
|||
{this.state.fullscreen && <img className={styles.logo} src={getTrustedUrl('static/mailtrain-notext.png')}/>}
|
||||
<div className={styles.title}>{this.props.title}</div>
|
||||
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
|
||||
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
|
||||
</div>
|
||||
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="ckeditor/editor" tokenMethod="ckeditor" tokenParams={editorData}/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ import {CodeEditorSourceType} from "./sandboxed-codeeditor-shared";
|
|||
import mjml2html from "mjml4-in-browser";
|
||||
import juice from "juice";
|
||||
|
||||
const refreshTimeout = 1000;
|
||||
|
||||
@translate(null, { withRef: true })
|
||||
class CodeEditorSandbox extends Component {
|
||||
constructor(props) {
|
||||
|
|
@ -85,6 +87,12 @@ class CodeEditorSandbox extends Component {
|
|||
source,
|
||||
preview: props.initialPreview
|
||||
};
|
||||
this.state.previewContents = this.getHtml();
|
||||
|
||||
this.onCodeChangedHandler = ::this.onCodeChanged;
|
||||
|
||||
this.refreshHandler = ::this.refresh;
|
||||
this.refreshTimeoutId = null;
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
|
|
@ -100,6 +108,7 @@ class CodeEditorSandbox extends Component {
|
|||
const sandboxUrlBase = getSandboxUrl();
|
||||
const publicUrlBase = getPublicUrl();
|
||||
return {
|
||||
html: unbase(this.getHtml(), trustedUrlBase, sandboxUrlBase, publicUrlBase, true),
|
||||
source: unbase(this.state.source, trustedUrlBase, sandboxUrlBase, publicUrlBase, true)
|
||||
};
|
||||
}
|
||||
|
|
@ -115,9 +124,12 @@ class CodeEditorSandbox extends Component {
|
|||
parentRPC.setMethodHandler('setPreview', ::this.setPreview);
|
||||
}
|
||||
|
||||
render() {
|
||||
let previewContents;
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.refreshTimeoutId);
|
||||
}
|
||||
|
||||
getHtml() {
|
||||
let previewContents;
|
||||
if (this.props.sourceType === CodeEditorSourceType.MJML) {
|
||||
const res = mjml2html(this.state.source);
|
||||
previewContents = res.html;
|
||||
|
|
@ -125,6 +137,28 @@ class CodeEditorSandbox extends Component {
|
|||
previewContents = juice(this.state.source);
|
||||
}
|
||||
|
||||
return previewContents;
|
||||
}
|
||||
|
||||
onCodeChanged(data) {
|
||||
this.setState({
|
||||
source: data
|
||||
});
|
||||
|
||||
if (!this.refreshTimeoutId) {
|
||||
this.refreshTimeoutId = setTimeout(() => this.refresh(), refreshTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.refreshTimeoutId = null;
|
||||
|
||||
this.setState({
|
||||
previewContents: this.getHtml()
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.sandbox}>
|
||||
<div className={this.state.preview ? styles.aceEditorWithPreview : styles.aceEditorWithoutPreview}>
|
||||
|
|
@ -133,7 +167,7 @@ class CodeEditorSandbox extends Component {
|
|||
theme="github"
|
||||
width="100%"
|
||||
height="100%"
|
||||
onChange={data => this.setState({source: data})}
|
||||
onChange={this.onCodeChangedHandler}
|
||||
fontSize={12}
|
||||
showPrintMargin={false}
|
||||
value={this.state.source}
|
||||
|
|
@ -144,7 +178,7 @@ class CodeEditorSandbox extends Component {
|
|||
{
|
||||
this.state.preview &&
|
||||
<div className={styles.preview}>
|
||||
<iframe src={"data:text/html;charset=utf-8," + escape(previewContents)}></iframe>
|
||||
<iframe src={"data:text/html;charset=utf-8," + escape(this.state.previewContents)}></iframe>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export class CodeEditorHost extends Component {
|
|||
initialSource: PropTypes.string,
|
||||
sourceType: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onTestSend: PropTypes.func,
|
||||
onFullscreenAsync: PropTypes.func
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +48,7 @@ export class CodeEditorHost extends Component {
|
|||
|
||||
await this.contentNode.ask('setPreview', preview);
|
||||
}
|
||||
|
||||
async exportState() {
|
||||
return await this.contentNode.ask('exportState');
|
||||
}
|
||||
|
|
@ -73,6 +75,7 @@ export class CodeEditorHost extends Component {
|
|||
{this.state.fullscreen && <img className={styles.logo} src={getTrustedUrl('static/mailtrain-notext.png')}/>}
|
||||
<div className={styles.title}>{this.props.title}</div>
|
||||
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
|
||||
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
|
||||
<a className={styles.btn} onClick={::this.togglePreviewAsync}><Icon icon={this.state.preview ? 'eye-close': 'eye-open'}/></a>
|
||||
</div>
|
||||
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="codeeditor/editor" tokenMethod="codeeditor" tokenParams={tokenData}/>
|
||||
|
|
|
|||
|
|
@ -107,7 +107,11 @@ export class GrapesJSSandbox extends Component {
|
|||
const commandManager = editor.Commands;
|
||||
|
||||
const cmdGetCode = commandManager.get('gjs-get-inlined-html');
|
||||
html = cmdGetCode.run(editor);
|
||||
const htmlBody = cmdGetCode.run(editor);
|
||||
|
||||
const preHtml = '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body>';
|
||||
const postHtml = '</body></html>';
|
||||
html = preHtml + unbase(htmlBody, trustedUrlBase, sandboxUrlBase, publicUrlBase, true) + postHtml;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export class GrapesJSHost extends Component {
|
|||
initialStyle: PropTypes.string,
|
||||
sourceType: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onTestSend: PropTypes.func,
|
||||
onFullscreenAsync: PropTypes.func
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ export class GrapesJSHost extends Component {
|
|||
{this.state.fullscreen && <img className={styles.logo} src={getTrustedUrl('static/mailtrain-notext.png')}/>}
|
||||
<div className={styles.title}>{this.props.title}</div>
|
||||
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
|
||||
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
|
||||
</div>
|
||||
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="grapesjs/editor" tokenMethod="grapesjs" tokenParams={tokenData}/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export class MosaicoHost extends Component {
|
|||
entityTypeId: PropTypes.string,
|
||||
entity: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
onTestSend: PropTypes.func,
|
||||
onFullscreenAsync: PropTypes.func,
|
||||
templateId: PropTypes.number,
|
||||
templatePath: PropTypes.string,
|
||||
|
|
@ -68,6 +69,7 @@ export class MosaicoHost extends Component {
|
|||
{this.state.fullscreen && <img className={styles.logo} src={getTrustedUrl('static/mailtrain-notext.png')}/>}
|
||||
<div className={styles.title}>{this.props.title}</div>
|
||||
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
|
||||
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
|
||||
</div>
|
||||
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="mosaico/editor" tokenMethod="mosaico" tokenParams={tokenData}/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -145,4 +145,16 @@
|
|||
|
||||
.dependenciesList {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:global .modal-dialog {
|
||||
@media (min-width: 768px) {
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
width: 900px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue