Send test functionality for templates and campaigns

This commit is contained in:
Tomas Bures 2018-11-14 22:29:31 +01:00
parent 7e52000219
commit 2c73c536b7
22 changed files with 719 additions and 69 deletions

View file

@ -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">

View file

@ -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}
/>

View file

@ -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>

View file

@ -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>

View file

@ -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}/>

View file

@ -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;
}

View file

@ -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>

View file

@ -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>

View file

@ -145,4 +145,16 @@
.dependenciesList {
margin-bottom: 0px;
}
}
:global .modal-dialog {
@media (min-width: 768px) {
width: 700px;
}
@media (min-width: 1000px) {
width: 900px;
}
}