@tinymce/tinymce-react
Advanced tools
Comparing version 3.2.0 to 3.3.0
@@ -0,1 +1,5 @@ | ||
## 3.3.0 (2019-07-29) | ||
* Fixed an issue that made the editor ignore new event handlers provided through props | ||
* Removed use of deprecated lifecycle hooks `componentWillMount` and `componentWillReceiveProps` | ||
## 3.2.0 (2019-06-04) | ||
@@ -2,0 +6,0 @@ * Changed the CDN URL to use `cdn.tiny.cloud` |
@@ -31,12 +31,16 @@ /** | ||
static defaultProps: Partial<IAllProps>; | ||
private element; | ||
private id?; | ||
private id; | ||
private elementRef; | ||
private editor?; | ||
private inline?; | ||
private inline; | ||
private currentContent?; | ||
componentWillMount(): void; | ||
private boundHandlers; | ||
constructor(props: Partial<IAllProps>); | ||
componentDidUpdate(prevProps: Partial<IAllProps>): void; | ||
componentDidMount(): void; | ||
componentWillUnmount(): void; | ||
componentWillReceiveProps(nextProps: Partial<IProps>): void; | ||
render(): JSX.Element; | ||
render(): React.ReactElement<{ | ||
ref: React.RefObject<Element>; | ||
id: string; | ||
}, string | ((props: any) => React.ReactElement<any, string | any | (new (props: any) => React.Component<any, any, any>)> | null) | (new (props: any) => React.Component<any, any, any>)>; | ||
private initialise; | ||
@@ -43,0 +47,0 @@ private initEditor; |
@@ -42,7 +42,6 @@ "use strict"; | ||
__extends(Editor, _super); | ||
function Editor() { | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.element = null; | ||
function Editor(props) { | ||
var _this = _super.call(this, props) || this; | ||
_this.initialise = function () { | ||
var finalInit = __assign({}, _this.props.init, { target: _this.element, readonly: _this.props.disabled, inline: _this.inline, plugins: Utils_1.mergePlugins(_this.props.init && _this.props.init.plugins, _this.props.plugins), toolbar: _this.props.toolbar || (_this.props.init && _this.props.init.toolbar), setup: function (editor) { | ||
var finalInit = __assign({}, _this.props.init, { target: _this.elementRef.current, readonly: _this.props.disabled, inline: _this.inline, plugins: Utils_1.mergePlugins(_this.props.init && _this.props.init.plugins, _this.props.plugins), toolbar: _this.props.toolbar || (_this.props.init && _this.props.init.toolbar), setup: function (editor) { | ||
_this.editor = editor; | ||
@@ -56,12 +55,24 @@ editor.on('init', function (e) { | ||
} }); | ||
if (Utils_1.isTextarea(_this.element)) { | ||
_this.element.style.visibility = ''; | ||
if (Utils_1.isTextarea(_this.elementRef.current)) { | ||
_this.elementRef.current.style.visibility = ''; | ||
} | ||
TinyMCE_1.getTinymce().init(finalInit); | ||
}; | ||
_this.id = _this.props.id || Utils_1.uuid('tiny-react'); | ||
_this.elementRef = React.createRef(); | ||
_this.inline = _this.props.inline ? _this.props.inline : _this.props.init && _this.props.init.inline; | ||
_this.boundHandlers = {}; | ||
return _this; | ||
} | ||
Editor.prototype.componentWillMount = function () { | ||
this.id = this.id || this.props.id || Utils_1.uuid('tiny-react'); | ||
this.inline = this.props.inline ? this.props.inline : this.props.init && this.props.init.inline; | ||
Editor.prototype.componentDidUpdate = function (prevProps) { | ||
if (this.editor && this.editor.initialized) { | ||
Utils_1.bindHandlers(this.editor, this.props, this.boundHandlers); | ||
this.currentContent = this.currentContent || this.editor.getContent(); | ||
if (typeof this.props.value === 'string' && this.props.value !== prevProps.value && this.props.value !== this.currentContent) { | ||
this.editor.setContent(this.props.value); | ||
} | ||
if (typeof this.props.disabled === 'boolean' && this.props.disabled !== prevProps.disabled) { | ||
this.editor.setMode(this.props.disabled ? 'readonly' : 'design'); | ||
} | ||
} | ||
}; | ||
@@ -72,4 +83,4 @@ Editor.prototype.componentDidMount = function () { | ||
} | ||
else if (this.element && this.element.ownerDocument) { | ||
var doc = this.element.ownerDocument; | ||
else if (this.elementRef.current && this.elementRef.current.ownerDocument) { | ||
var doc = this.elementRef.current.ownerDocument; | ||
var channel = this.props.cloudChannel; | ||
@@ -85,13 +96,2 @@ var apiKey = this.props.apiKey ? this.props.apiKey : 'no-api-key'; | ||
}; | ||
Editor.prototype.componentWillReceiveProps = function (nextProps) { | ||
if (this.editor && this.editor.initialized) { | ||
this.currentContent = this.currentContent || this.editor.getContent(); | ||
if (typeof nextProps.value === 'string' && nextProps.value !== this.props.value && nextProps.value !== this.currentContent) { | ||
this.editor.setContent(nextProps.value); | ||
} | ||
if (typeof nextProps.disabled === 'boolean' && nextProps.disabled !== this.props.disabled) { | ||
this.editor.setMode(nextProps.disabled ? 'readonly' : 'design'); | ||
} | ||
} | ||
}; | ||
Editor.prototype.render = function () { | ||
@@ -112,9 +112,11 @@ return this.inline ? this.renderInline() : this.renderIframe(); | ||
} | ||
Utils_1.bindHandlers(this.props, editor, initEvent); | ||
if (Utils_1.isFunction(this.props.onInit)) { | ||
this.props.onInit(initEvent, editor); | ||
} | ||
Utils_1.bindHandlers(editor, this.props, this.boundHandlers); | ||
}; | ||
Editor.prototype.renderInline = function () { | ||
var _this = this; | ||
var _a = this.props.tagName, tagName = _a === void 0 ? 'div' : _a; | ||
return React.createElement(tagName, { | ||
ref: function (elm) { return (_this.element = elm); }, | ||
ref: this.elementRef, | ||
id: this.id | ||
@@ -124,4 +126,8 @@ }); | ||
Editor.prototype.renderIframe = function () { | ||
var _this = this; | ||
return React.createElement("textarea", { ref: function (elm) { return (_this.element = elm); }, style: { visibility: 'hidden' }, id: this.id, name: this.props.textareaName }); | ||
return React.createElement('textarea', { | ||
ref: this.elementRef, | ||
style: { visibility: 'hidden' }, | ||
name: this.props.textareaName, | ||
id: this.id | ||
}); | ||
}; | ||
@@ -128,0 +134,0 @@ Editor.propTypes = EditorPropTypes_1.EditorPropTypes; |
@@ -14,68 +14,6 @@ /** | ||
}; | ||
export interface IEditorPropTypes extends CopyProps<IEvents>, CopyProps<IProps> { | ||
export declare type IEventPropTypes = CopyProps<IEvents>; | ||
export interface IEditorPropTypes extends IEventPropTypes, CopyProps<IProps> { | ||
} | ||
export declare const eventPropTypes: { | ||
onActivate: PropTypes.Requireable<(...args: any[]) => any>; | ||
onAddUndo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeAddUndo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeExecCommand: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeGetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeRenderUI: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeSetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforePaste: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBlur: PropTypes.Requireable<(...args: any[]) => any>; | ||
onChange: PropTypes.Requireable<(...args: any[]) => any>; | ||
onClearUndos: PropTypes.Requireable<(...args: any[]) => any>; | ||
onClick: PropTypes.Requireable<(...args: any[]) => any>; | ||
onContextMenu: PropTypes.Requireable<(...args: any[]) => any>; | ||
onCopy: PropTypes.Requireable<(...args: any[]) => any>; | ||
onCut: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDblclick: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDeactivate: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDirty: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDrag: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragDrop: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragEnd: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragGesture: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragOver: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDrop: PropTypes.Requireable<(...args: any[]) => any>; | ||
onExecCommand: PropTypes.Requireable<(...args: any[]) => any>; | ||
onFocus: PropTypes.Requireable<(...args: any[]) => any>; | ||
onFocusIn: PropTypes.Requireable<(...args: any[]) => any>; | ||
onFocusOut: PropTypes.Requireable<(...args: any[]) => any>; | ||
onGetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onHide: PropTypes.Requireable<(...args: any[]) => any>; | ||
onInit: PropTypes.Requireable<(...args: any[]) => any>; | ||
onKeyDown: PropTypes.Requireable<(...args: any[]) => any>; | ||
onKeyPress: PropTypes.Requireable<(...args: any[]) => any>; | ||
onKeyUp: PropTypes.Requireable<(...args: any[]) => any>; | ||
onLoadContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseDown: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseEnter: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseLeave: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseMove: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseOut: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseOver: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseUp: PropTypes.Requireable<(...args: any[]) => any>; | ||
onNodeChange: PropTypes.Requireable<(...args: any[]) => any>; | ||
onObjectResizeStart: PropTypes.Requireable<(...args: any[]) => any>; | ||
onObjectResized: PropTypes.Requireable<(...args: any[]) => any>; | ||
onObjectSelected: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPaste: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPostProcess: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPostRender: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPreProcess: PropTypes.Requireable<(...args: any[]) => any>; | ||
onProgressState: PropTypes.Requireable<(...args: any[]) => any>; | ||
onRedo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onRemove: PropTypes.Requireable<(...args: any[]) => any>; | ||
onReset: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSaveContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSelectionChange: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSetAttrib: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onShow: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSubmit: PropTypes.Requireable<(...args: any[]) => any>; | ||
onUndo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onVisualAid: PropTypes.Requireable<(...args: any[]) => any>; | ||
}; | ||
export declare const eventPropTypes: IEventPropTypes; | ||
export declare const EditorPropTypes: IEditorPropTypes; |
@@ -8,6 +8,7 @@ /** | ||
*/ | ||
import { IAllProps } from './components/Editor'; | ||
export declare const isFunction: (x: any) => x is Function; | ||
export declare const bindHandlers: (props: any, editor: any, initEvent: Event) => void; | ||
export declare const bindHandlers: (editor: any, props: Partial<IAllProps>, boundHandlers: Record<string, Function>) => void; | ||
export declare const uuid: (prefix: string) => string; | ||
export declare const isTextarea: (element: Element | null) => element is HTMLTextAreaElement; | ||
export declare const mergePlugins: (initPlugins: string | string[], inputPlugins?: string | string[] | undefined) => string[]; |
@@ -11,18 +11,26 @@ "use strict"; | ||
var EditorPropTypes_1 = require("./components/EditorPropTypes"); | ||
var isValidKey = function (keys) { return function (key) { return keys.indexOf(key) !== -1; }; }; | ||
// tslint:disable-next-line:ban-types | ||
exports.isFunction = function (x) { return typeof x === 'function'; }; | ||
exports.bindHandlers = function (props, editor, initEvent) { | ||
Object.keys(props) | ||
.filter(isValidKey(Object.keys(EditorPropTypes_1.eventPropTypes))) | ||
.forEach(function (key) { | ||
var handler = props[key]; | ||
if (exports.isFunction(handler)) { | ||
if (key === 'onInit') { | ||
handler(initEvent, editor); | ||
} | ||
else { | ||
editor.on(key.substring(2), function (e) { return handler(e, editor); }); | ||
} | ||
var isEventProp = function (name) { | ||
return name in EditorPropTypes_1.eventPropTypes; | ||
}; | ||
var findEventHandlers = function (props) { | ||
return Object.keys(props) | ||
.filter(isEventProp) | ||
.filter(function (name) { return exports.isFunction(props[name]); }) | ||
.map(function (name) { return ({ | ||
handler: props[name], | ||
eventName: name.substring(2) | ||
}); }); | ||
}; | ||
exports.bindHandlers = function (editor, props, boundHandlers) { | ||
findEventHandlers(props).forEach(function (found) { | ||
// Unbind old handler | ||
var oldHandler = boundHandlers[found.eventName]; | ||
if (exports.isFunction(oldHandler)) { | ||
editor.off(found.eventName, oldHandler); | ||
} | ||
// Bind new handler | ||
var newHandler = function (e) { return found.handler(e, editor); }; | ||
boundHandlers[found.eventName] = newHandler; | ||
editor.on(found.eventName, newHandler); | ||
}); | ||
@@ -29,0 +37,0 @@ }; |
@@ -9,10 +9,9 @@ import { Chain } from '@ephox/agar'; | ||
ref: React.RefObject<Editor>; | ||
root: HTMLElement; | ||
} | ||
declare type TestEditor = (props: IAllProps) => JSX.Element; | ||
declare const cSetup: (createElement: (Ed: TestEditor) => JSX.Element) => Chain<Payload, Payload>; | ||
declare const cRemove: Chain<Payload, Payload>; | ||
declare const cNamedChainDirect: (name: "ref" | "DOMNode" | "editor" | "root") => Chain<Record<string, any>, Record<string, any>>; | ||
declare const cRender: (props: IAllProps) => Chain<Payload, Payload>; | ||
declare const cReRender: (props: IAllProps) => Chain<Payload, Payload>; | ||
declare const cRemove: Chain<{}, {}>; | ||
declare const cNamedChainDirect: (name: "ref" | "DOMNode" | "editor") => Chain<Record<string, any>, Record<string, any>>; | ||
declare const cDOMNode: (chain: Chain<any, any>) => Chain<{}, any>; | ||
declare const cEditor: (chain: Chain<any, any>) => Chain<{}, any>; | ||
export { cSetup, cRemove, cNamedChainDirect, cDOMNode, cEditor }; | ||
export { cRender, cReRender, cRemove, cNamedChainDirect, cDOMNode, cEditor }; |
@@ -18,53 +18,47 @@ "use strict"; | ||
var ReactDOM = require("react-dom"); | ||
var TinyMCE_1 = require("src/main/ts/TinyMCE"); | ||
var Editor_1 = require("../../../main/ts/components/Editor"); | ||
require("tinymce/tinymce"); | ||
var setTinymceBaseUrl = function (baseUrl) { | ||
var tinymce = TinyMCE_1.getTinymce(); | ||
var prefix = document.location.protocol + '//' + document.location.host; | ||
tinymce.baseURL = baseUrl.indexOf('://') === -1 ? prefix + baseUrl : baseUrl; | ||
tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL); | ||
var getRoot = function () { | ||
return katamari_1.Option.from(document.getElementById('root')).getOrThunk(function () { | ||
var root = document.createElement('div'); | ||
root.id = 'root'; | ||
document.body.appendChild(root); | ||
return root; | ||
}); | ||
}; | ||
var getTestEditor = function (onLoaded) { | ||
return function (props) { | ||
var cRender = function (props) { | ||
return agar_1.Chain.async(function (_, next, die) { | ||
var originalInit = props.init || {}; | ||
var originalSetup = originalInit.setup || katamari_1.Fun.noop; | ||
var ref = React.createRef(); | ||
var init = __assign({}, originalInit, { setup: function (editor) { | ||
var init = __assign({}, originalInit, { base_url: "/project/node_modules/tinymce", setup: function (editor) { | ||
originalSetup(editor); | ||
editor.on('SkinLoaded', function () { | ||
setTimeout(function () { | ||
onLoaded(editor, ref); | ||
katamari_1.Option.from(ref.current) | ||
.map(ReactDOM.findDOMNode) | ||
.filter(function (val) { return val instanceof Element; }) | ||
.fold(function () { return die('Could not find DOMNode'); }, function (DOMNode) { | ||
next({ | ||
ref: ref, | ||
editor: editor, | ||
DOMNode: DOMNode | ||
}); | ||
}); | ||
}, 0); | ||
}); | ||
} }); | ||
setTinymceBaseUrl(init.base_url || "/project/node_modules/tinymce"); | ||
return React.createElement(Editor_1.Editor, __assign({ ref: ref }, props, { init: init })); | ||
}; | ||
ReactDOM.render(React.createElement(Editor_1.Editor, __assign({ ref: ref }, props, { init: init })), getRoot()); | ||
}); | ||
}; | ||
var cSetup = function (createElement) { | ||
return agar_1.Chain.async(function (_, next, die) { | ||
var root = document.createElement('div'); | ||
document.body.appendChild(root); | ||
var onEditorLoaded = function (editor, ref) { | ||
katamari_1.Option.from(ref.current) | ||
.map(ReactDOM.findDOMNode) | ||
.filter(function (val) { return val instanceof Element; }) | ||
.fold(function () { return die('Could not find DOMNode'); }, function (DOMNode) { | ||
next({ | ||
ref: ref, | ||
root: root, | ||
editor: editor, | ||
DOMNode: DOMNode | ||
}); | ||
}); | ||
}; | ||
var testEditor = getTestEditor(onEditorLoaded); | ||
var editorElement = createElement(testEditor); | ||
ReactDOM.render(editorElement, root); | ||
exports.cRender = cRender; | ||
// By rendering the Editor into the same root, React will perform a diff and update. | ||
var cReRender = function (props) { | ||
return agar_1.Chain.op(function (payload) { | ||
ReactDOM.render(React.createElement(Editor_1.Editor, __assign({ ref: payload.ref }, props)), getRoot()); | ||
}); | ||
}; | ||
exports.cSetup = cSetup; | ||
var cRemove = agar_1.Chain.op(function (res) { | ||
ReactDOM.unmountComponentAtNode(res.root); | ||
exports.cReRender = cReRender; | ||
var cRemove = agar_1.Chain.op(function (_) { | ||
ReactDOM.unmountComponentAtNode(getRoot()); | ||
}); | ||
@@ -71,0 +65,0 @@ exports.cRemove = cRemove; |
import { Chain } from '@ephox/agar'; | ||
declare const EventState: () => { | ||
cEach: (name: string, doAssert: (args: any[]) => void) => Chain<any, any>; | ||
handler: (name: string) => (...args: any[]) => void; | ||
cEach: (name: string, assertState: (args: any[]) => void) => Chain<any, any>; | ||
createHandler: (name: string) => (...args: any[]) => void; | ||
get: (name: string) => any; | ||
cClearState: Chain<{}, {}>; | ||
}; | ||
export { EventState }; |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var agar_1 = require("@ephox/agar"); | ||
var katamari_1 = require("@ephox/katamari"); | ||
var EventState = function () { | ||
var state = {}; | ||
var handler = function (name) { | ||
var state = katamari_1.Cell({}); | ||
var createHandler = function (name) { | ||
return function () { | ||
@@ -12,22 +24,27 @@ var args = []; | ||
} | ||
state[name] = args; | ||
var _a; | ||
state.set(__assign({}, state.get(), (_a = {}, _a[name] = args, _a))); | ||
}; | ||
}; | ||
var get = function (name) { | ||
return state[name]; | ||
return state.get()[name]; | ||
}; | ||
var cEach = function (name, doAssert) { | ||
var cEach = function (name, assertState) { | ||
return agar_1.Chain.fromChains([ | ||
agar_1.Chain.op(function () { return agar_1.Assertions.assertEq(name + ' should exist', true, !!state[name]); }), | ||
agar_1.Chain.op(function () { | ||
doAssert(state[name]); | ||
agar_1.Assertions.assertEq('State from "' + name + '" handler should exist', true, name in state.get()); | ||
assertState(state.get()[name]); | ||
}) | ||
]); | ||
}; | ||
var cClearState = agar_1.Chain.op(function () { | ||
state.set({}); | ||
}); | ||
return { | ||
cEach: cEach, | ||
handler: handler, | ||
get: get | ||
createHandler: createHandler, | ||
get: get, | ||
cClearState: cClearState | ||
}; | ||
}; | ||
exports.EventState = EventState; |
@@ -5,3 +5,2 @@ "use strict"; | ||
var bedrock_1 = require("@ephox/bedrock"); | ||
var React = require("react"); | ||
var Loader_1 = require("../alien/Loader"); | ||
@@ -15,14 +14,61 @@ var mcagar_1 = require("@ephox/mcagar"); | ||
}; | ||
var state = TestHelpers_1.EventState(); | ||
var eventState = TestHelpers_1.EventState(); | ||
agar_1.Pipeline.async({}, [ | ||
agar_1.Logger.t('Assert structure of editor and react wrapper events', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return (React.createElement(Editor, { onEditorChange: state.handler('onEditorChange'), onSetContent: state.handler('onSetContent') })); }), | ||
agar_1.Logger.t('Assert structure of tinymce and tinymce-react events', agar_1.Chain.asStep({}, [ | ||
Loader_1.cRender({ | ||
onEditorChange: eventState.createHandler('onEditorChange'), | ||
onSetContent: eventState.createHandler('onSetContent') | ||
}), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cSetContent('<p>Initial Content</p>')), | ||
// tinymce native event | ||
eventState.cEach('onSetContent', function (args) { return agar_1.Assertions.assertEq('First arg should be event from Tiny', '<p>Initial Content</p>', args[0].content); }), | ||
eventState.cEach('onSetContent', function (args) { return agar_1.Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
// tinymce-react unique event | ||
eventState.cEach('onEditorChange', function (args) { return agar_1.Assertions.assertEq('First arg should be new content', '<p>Initial Content</p>', args[0]); }), | ||
eventState.cEach('onEditorChange', function (args) { return agar_1.Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
eventState.cClearState, | ||
Loader_1.cRemove | ||
])), | ||
agar_1.Logger.t('Should be able to register an event handler after initial render', agar_1.Chain.asStep({}, [ | ||
Loader_1.cRender({ initialValue: '<p>Initial Content</p>' }), | ||
Loader_1.cReRender({ onSetContent: eventState.createHandler('onSetContent') }), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cAssertContent('<p>Initial Content</p>')), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cSetContent('<p>New Content</p>')), | ||
state.cEach('onEditorChange', function (args) { return agar_1.Assertions.assertEq('First arg should be new content', '<p>New Content</p>', args[0]); }), | ||
state.cEach('onEditorChange', function (args) { return agar_1.Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
state.cEach('onSetContent', function (args) { return agar_1.Assertions.assertEq('First arg should be something', true, !!args[0]); }), | ||
state.cEach('onSetContent', function (args) { return agar_1.Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
eventState.cEach('onSetContent', function (args) { return agar_1.Assertions.assertEq('Should have bound handler, hence new content', '<p>New Content</p>', args[0].content); }), | ||
eventState.cClearState, | ||
Loader_1.cRemove | ||
])), | ||
agar_1.Logger.t('Providing a new event handler and re-rendering should unbind old handler and bind new handler', agar_1.Chain.asStep({}, [ | ||
Loader_1.cRender({ onSetContent: eventState.createHandler('InitialHandler') }), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cSetContent('<p>Initial Content</p>')), | ||
Loader_1.cReRender({ onSetContent: eventState.createHandler('NewHandler') }), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cSetContent('<p>New Content</p>')), | ||
eventState.cEach('InitialHandler', function (args) { | ||
return agar_1.Assertions.assertEq('Initial handler should have been unbound, hence initial content', '<p>Initial Content</p>', args[0].content); | ||
}), | ||
eventState.cEach('NewHandler', function (args) { | ||
return agar_1.Assertions.assertEq('New handler should have been bound, hence new content', '<p>New Content</p>', args[0].content); | ||
}), | ||
eventState.cClearState, | ||
Loader_1.cRemove | ||
])), | ||
agar_1.Logger.t('Test value prop', agar_1.Chain.asStep({}, [ | ||
Loader_1.cRender({ value: '<p>Initial Value</p>' }), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cAssertContent('<p>Initial Value</p>')), | ||
Loader_1.cReRender({ value: '<p>New Value</p>' }), | ||
Loader_1.cEditor(mcagar_1.ApiChains.cAssertContent('<p>New Value</p>')), | ||
Loader_1.cRemove | ||
])), | ||
agar_1.Logger.t('Test disabled prop', agar_1.Chain.asStep({}, [ | ||
Loader_1.cRender({}), | ||
Loader_1.cEditor(agar_1.Chain.op(function (editor) { | ||
agar_1.Assertions.assertEq('Should be design mode', 'design', editor.mode.get()); | ||
})), | ||
Loader_1.cReRender({ disabled: true }), | ||
Loader_1.cEditor(agar_1.Chain.op(function (editor) { | ||
agar_1.Assertions.assertEq('Should be readonly mode', 'readonly', editor.mode.get()); | ||
})), | ||
Loader_1.cRemove | ||
])) | ||
], success, failure); | ||
}); |
@@ -5,3 +5,2 @@ "use strict"; | ||
var bedrock_1 = require("@ephox/bedrock"); | ||
var React = require("react"); | ||
var Loader_1 = require("../alien/Loader"); | ||
@@ -17,3 +16,3 @@ bedrock_1.UnitTest.asynctest('Editor.test', function (success, failure) { | ||
agar_1.Logger.t('it is div by default for inline', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, { inline: true }); }), | ||
Loader_1.cRender({ inline: true }), | ||
Loader_1.cDOMNode(cAssertProperty('tagName', 'DIV')), | ||
@@ -23,3 +22,7 @@ Loader_1.cRemove | ||
agar_1.Logger.t('can be set to inline in init', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, { init: { inline: true } }); }), | ||
Loader_1.cRender({ | ||
init: { | ||
inline: true | ||
} | ||
}), | ||
Loader_1.cDOMNode(cAssertProperty('tagName', 'DIV')), | ||
@@ -29,3 +32,6 @@ Loader_1.cRemove | ||
agar_1.Logger.t('it can be changed to p', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, { inline: true, tagName: 'p' }); }), | ||
Loader_1.cRender({ | ||
inline: true, | ||
tagName: 'p' | ||
}), | ||
Loader_1.cDOMNode(cAssertProperty('tagName', 'P')), | ||
@@ -35,3 +41,3 @@ Loader_1.cRemove | ||
agar_1.Logger.t('iframe editor does not change element', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, { tagName: 'p' }); }), | ||
Loader_1.cRender({ tagName: 'p' }), | ||
Loader_1.cDOMNode(cAssertProperty('tagName', 'TEXTAREA')), | ||
@@ -43,3 +49,3 @@ Loader_1.cRemove | ||
agar_1.Logger.t('is set normally if prop is provided', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, { id: 'test' }); }), | ||
Loader_1.cRender({ id: 'test' }), | ||
Loader_1.cDOMNode(cAssertProperty('id', 'test')), | ||
@@ -49,3 +55,3 @@ Loader_1.cRemove | ||
agar_1.Logger.t('gets set automatically to uuid if not set', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, null); }), | ||
Loader_1.cRender({}), | ||
Loader_1.cDOMNode(agar_1.Chain.op(function (node) { | ||
@@ -59,3 +65,3 @@ agar_1.Assertions.assertEq('Should not be uuid', typeof node.id === 'string' && node.id.indexOf('tiny-react') !== -1, true); | ||
agar_1.Logger.t('is not set when prop is not provided', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, null); }), | ||
Loader_1.cRender({}), | ||
Loader_1.cDOMNode(cAssertProperty('name', '')), | ||
@@ -65,3 +71,3 @@ Loader_1.cRemove | ||
agar_1.Logger.t('is set when prop is provided', agar_1.Chain.asStep({}, [ | ||
Loader_1.cSetup(function (Editor) { return React.createElement(Editor, { textareaName: 'test' }); }), | ||
Loader_1.cRender({ textareaName: 'test' }), | ||
Loader_1.cDOMNode(cAssertProperty('name', 'test')), | ||
@@ -68,0 +74,0 @@ Loader_1.cRemove |
@@ -31,12 +31,16 @@ /** | ||
static defaultProps: Partial<IAllProps>; | ||
private element; | ||
private id?; | ||
private id; | ||
private elementRef; | ||
private editor?; | ||
private inline?; | ||
private inline; | ||
private currentContent?; | ||
componentWillMount(): void; | ||
private boundHandlers; | ||
constructor(props: Partial<IAllProps>); | ||
componentDidUpdate(prevProps: Partial<IAllProps>): void; | ||
componentDidMount(): void; | ||
componentWillUnmount(): void; | ||
componentWillReceiveProps(nextProps: Partial<IProps>): void; | ||
render(): JSX.Element; | ||
render(): React.ReactElement<{ | ||
ref: React.RefObject<Element>; | ||
id: string; | ||
}, string | ((props: any) => React.ReactElement<any, string | any | (new (props: any) => React.Component<any, any, any>)> | null) | (new (props: any) => React.Component<any, any, any>)>; | ||
private initialise; | ||
@@ -43,0 +47,0 @@ private initEditor; |
@@ -40,7 +40,6 @@ /** | ||
__extends(Editor, _super); | ||
function Editor() { | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.element = null; | ||
function Editor(props) { | ||
var _this = _super.call(this, props) || this; | ||
_this.initialise = function () { | ||
var finalInit = __assign({}, _this.props.init, { target: _this.element, readonly: _this.props.disabled, inline: _this.inline, plugins: mergePlugins(_this.props.init && _this.props.init.plugins, _this.props.plugins), toolbar: _this.props.toolbar || (_this.props.init && _this.props.init.toolbar), setup: function (editor) { | ||
var finalInit = __assign({}, _this.props.init, { target: _this.elementRef.current, readonly: _this.props.disabled, inline: _this.inline, plugins: mergePlugins(_this.props.init && _this.props.init.plugins, _this.props.plugins), toolbar: _this.props.toolbar || (_this.props.init && _this.props.init.toolbar), setup: function (editor) { | ||
_this.editor = editor; | ||
@@ -54,12 +53,24 @@ editor.on('init', function (e) { | ||
} }); | ||
if (isTextarea(_this.element)) { | ||
_this.element.style.visibility = ''; | ||
if (isTextarea(_this.elementRef.current)) { | ||
_this.elementRef.current.style.visibility = ''; | ||
} | ||
getTinymce().init(finalInit); | ||
}; | ||
_this.id = _this.props.id || uuid('tiny-react'); | ||
_this.elementRef = React.createRef(); | ||
_this.inline = _this.props.inline ? _this.props.inline : _this.props.init && _this.props.init.inline; | ||
_this.boundHandlers = {}; | ||
return _this; | ||
} | ||
Editor.prototype.componentWillMount = function () { | ||
this.id = this.id || this.props.id || uuid('tiny-react'); | ||
this.inline = this.props.inline ? this.props.inline : this.props.init && this.props.init.inline; | ||
Editor.prototype.componentDidUpdate = function (prevProps) { | ||
if (this.editor && this.editor.initialized) { | ||
bindHandlers(this.editor, this.props, this.boundHandlers); | ||
this.currentContent = this.currentContent || this.editor.getContent(); | ||
if (typeof this.props.value === 'string' && this.props.value !== prevProps.value && this.props.value !== this.currentContent) { | ||
this.editor.setContent(this.props.value); | ||
} | ||
if (typeof this.props.disabled === 'boolean' && this.props.disabled !== prevProps.disabled) { | ||
this.editor.setMode(this.props.disabled ? 'readonly' : 'design'); | ||
} | ||
} | ||
}; | ||
@@ -70,4 +81,4 @@ Editor.prototype.componentDidMount = function () { | ||
} | ||
else if (this.element && this.element.ownerDocument) { | ||
var doc = this.element.ownerDocument; | ||
else if (this.elementRef.current && this.elementRef.current.ownerDocument) { | ||
var doc = this.elementRef.current.ownerDocument; | ||
var channel = this.props.cloudChannel; | ||
@@ -83,13 +94,2 @@ var apiKey = this.props.apiKey ? this.props.apiKey : 'no-api-key'; | ||
}; | ||
Editor.prototype.componentWillReceiveProps = function (nextProps) { | ||
if (this.editor && this.editor.initialized) { | ||
this.currentContent = this.currentContent || this.editor.getContent(); | ||
if (typeof nextProps.value === 'string' && nextProps.value !== this.props.value && nextProps.value !== this.currentContent) { | ||
this.editor.setContent(nextProps.value); | ||
} | ||
if (typeof nextProps.disabled === 'boolean' && nextProps.disabled !== this.props.disabled) { | ||
this.editor.setMode(nextProps.disabled ? 'readonly' : 'design'); | ||
} | ||
} | ||
}; | ||
Editor.prototype.render = function () { | ||
@@ -110,9 +110,11 @@ return this.inline ? this.renderInline() : this.renderIframe(); | ||
} | ||
bindHandlers(this.props, editor, initEvent); | ||
if (isFunction(this.props.onInit)) { | ||
this.props.onInit(initEvent, editor); | ||
} | ||
bindHandlers(editor, this.props, this.boundHandlers); | ||
}; | ||
Editor.prototype.renderInline = function () { | ||
var _this = this; | ||
var _a = this.props.tagName, tagName = _a === void 0 ? 'div' : _a; | ||
return React.createElement(tagName, { | ||
ref: function (elm) { return (_this.element = elm); }, | ||
ref: this.elementRef, | ||
id: this.id | ||
@@ -122,4 +124,8 @@ }); | ||
Editor.prototype.renderIframe = function () { | ||
var _this = this; | ||
return React.createElement("textarea", { ref: function (elm) { return (_this.element = elm); }, style: { visibility: 'hidden' }, id: this.id, name: this.props.textareaName }); | ||
return React.createElement('textarea', { | ||
ref: this.elementRef, | ||
style: { visibility: 'hidden' }, | ||
name: this.props.textareaName, | ||
id: this.id | ||
}); | ||
}; | ||
@@ -126,0 +132,0 @@ Editor.propTypes = EditorPropTypes; |
@@ -14,68 +14,6 @@ /** | ||
}; | ||
export interface IEditorPropTypes extends CopyProps<IEvents>, CopyProps<IProps> { | ||
export declare type IEventPropTypes = CopyProps<IEvents>; | ||
export interface IEditorPropTypes extends IEventPropTypes, CopyProps<IProps> { | ||
} | ||
export declare const eventPropTypes: { | ||
onActivate: PropTypes.Requireable<(...args: any[]) => any>; | ||
onAddUndo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeAddUndo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeExecCommand: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeGetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeRenderUI: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforeSetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBeforePaste: PropTypes.Requireable<(...args: any[]) => any>; | ||
onBlur: PropTypes.Requireable<(...args: any[]) => any>; | ||
onChange: PropTypes.Requireable<(...args: any[]) => any>; | ||
onClearUndos: PropTypes.Requireable<(...args: any[]) => any>; | ||
onClick: PropTypes.Requireable<(...args: any[]) => any>; | ||
onContextMenu: PropTypes.Requireable<(...args: any[]) => any>; | ||
onCopy: PropTypes.Requireable<(...args: any[]) => any>; | ||
onCut: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDblclick: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDeactivate: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDirty: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDrag: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragDrop: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragEnd: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragGesture: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDragOver: PropTypes.Requireable<(...args: any[]) => any>; | ||
onDrop: PropTypes.Requireable<(...args: any[]) => any>; | ||
onExecCommand: PropTypes.Requireable<(...args: any[]) => any>; | ||
onFocus: PropTypes.Requireable<(...args: any[]) => any>; | ||
onFocusIn: PropTypes.Requireable<(...args: any[]) => any>; | ||
onFocusOut: PropTypes.Requireable<(...args: any[]) => any>; | ||
onGetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onHide: PropTypes.Requireable<(...args: any[]) => any>; | ||
onInit: PropTypes.Requireable<(...args: any[]) => any>; | ||
onKeyDown: PropTypes.Requireable<(...args: any[]) => any>; | ||
onKeyPress: PropTypes.Requireable<(...args: any[]) => any>; | ||
onKeyUp: PropTypes.Requireable<(...args: any[]) => any>; | ||
onLoadContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseDown: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseEnter: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseLeave: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseMove: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseOut: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseOver: PropTypes.Requireable<(...args: any[]) => any>; | ||
onMouseUp: PropTypes.Requireable<(...args: any[]) => any>; | ||
onNodeChange: PropTypes.Requireable<(...args: any[]) => any>; | ||
onObjectResizeStart: PropTypes.Requireable<(...args: any[]) => any>; | ||
onObjectResized: PropTypes.Requireable<(...args: any[]) => any>; | ||
onObjectSelected: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPaste: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPostProcess: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPostRender: PropTypes.Requireable<(...args: any[]) => any>; | ||
onPreProcess: PropTypes.Requireable<(...args: any[]) => any>; | ||
onProgressState: PropTypes.Requireable<(...args: any[]) => any>; | ||
onRedo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onRemove: PropTypes.Requireable<(...args: any[]) => any>; | ||
onReset: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSaveContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSelectionChange: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSetAttrib: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSetContent: PropTypes.Requireable<(...args: any[]) => any>; | ||
onShow: PropTypes.Requireable<(...args: any[]) => any>; | ||
onSubmit: PropTypes.Requireable<(...args: any[]) => any>; | ||
onUndo: PropTypes.Requireable<(...args: any[]) => any>; | ||
onVisualAid: PropTypes.Requireable<(...args: any[]) => any>; | ||
}; | ||
export declare const eventPropTypes: IEventPropTypes; | ||
export declare const EditorPropTypes: IEditorPropTypes; |
@@ -8,6 +8,7 @@ /** | ||
*/ | ||
import { IAllProps } from './components/Editor'; | ||
export declare const isFunction: (x: any) => x is Function; | ||
export declare const bindHandlers: (props: any, editor: any, initEvent: Event) => void; | ||
export declare const bindHandlers: (editor: any, props: Partial<IAllProps>, boundHandlers: Record<string, Function>) => void; | ||
export declare const uuid: (prefix: string) => string; | ||
export declare const isTextarea: (element: Element | null) => element is HTMLTextAreaElement; | ||
export declare const mergePlugins: (initPlugins: string | string[], inputPlugins?: string | string[] | undefined) => string[]; |
@@ -9,18 +9,26 @@ /** | ||
import { eventPropTypes } from './components/EditorPropTypes'; | ||
var isValidKey = function (keys) { return function (key) { return keys.indexOf(key) !== -1; }; }; | ||
// tslint:disable-next-line:ban-types | ||
export var isFunction = function (x) { return typeof x === 'function'; }; | ||
export var bindHandlers = function (props, editor, initEvent) { | ||
Object.keys(props) | ||
.filter(isValidKey(Object.keys(eventPropTypes))) | ||
.forEach(function (key) { | ||
var handler = props[key]; | ||
if (isFunction(handler)) { | ||
if (key === 'onInit') { | ||
handler(initEvent, editor); | ||
} | ||
else { | ||
editor.on(key.substring(2), function (e) { return handler(e, editor); }); | ||
} | ||
var isEventProp = function (name) { | ||
return name in eventPropTypes; | ||
}; | ||
var findEventHandlers = function (props) { | ||
return Object.keys(props) | ||
.filter(isEventProp) | ||
.filter(function (name) { return isFunction(props[name]); }) | ||
.map(function (name) { return ({ | ||
handler: props[name], | ||
eventName: name.substring(2) | ||
}); }); | ||
}; | ||
export var bindHandlers = function (editor, props, boundHandlers) { | ||
findEventHandlers(props).forEach(function (found) { | ||
// Unbind old handler | ||
var oldHandler = boundHandlers[found.eventName]; | ||
if (isFunction(oldHandler)) { | ||
editor.off(found.eventName, oldHandler); | ||
} | ||
// Bind new handler | ||
var newHandler = function (e) { return found.handler(e, editor); }; | ||
boundHandlers[found.eventName] = newHandler; | ||
editor.on(found.eventName, newHandler); | ||
}); | ||
@@ -27,0 +35,0 @@ }; |
@@ -9,10 +9,9 @@ import { Chain } from '@ephox/agar'; | ||
ref: React.RefObject<Editor>; | ||
root: HTMLElement; | ||
} | ||
declare type TestEditor = (props: IAllProps) => JSX.Element; | ||
declare const cSetup: (createElement: (Ed: TestEditor) => JSX.Element) => Chain<Payload, Payload>; | ||
declare const cRemove: Chain<Payload, Payload>; | ||
declare const cNamedChainDirect: (name: "ref" | "DOMNode" | "editor" | "root") => Chain<Record<string, any>, Record<string, any>>; | ||
declare const cRender: (props: IAllProps) => Chain<Payload, Payload>; | ||
declare const cReRender: (props: IAllProps) => Chain<Payload, Payload>; | ||
declare const cRemove: Chain<{}, {}>; | ||
declare const cNamedChainDirect: (name: "ref" | "DOMNode" | "editor") => Chain<Record<string, any>, Record<string, any>>; | ||
declare const cDOMNode: (chain: Chain<any, any>) => Chain<{}, any>; | ||
declare const cEditor: (chain: Chain<any, any>) => Chain<{}, any>; | ||
export { cSetup, cRemove, cNamedChainDirect, cDOMNode, cEditor }; | ||
export { cRender, cReRender, cRemove, cNamedChainDirect, cDOMNode, cEditor }; |
@@ -16,52 +16,45 @@ var __assign = (this && this.__assign) || function () { | ||
import * as ReactDOM from 'react-dom'; | ||
import { getTinymce } from 'src/main/ts/TinyMCE'; | ||
import { Editor } from '../../../main/ts/components/Editor'; | ||
import 'tinymce/tinymce'; | ||
var setTinymceBaseUrl = function (baseUrl) { | ||
var tinymce = getTinymce(); | ||
var prefix = document.location.protocol + '//' + document.location.host; | ||
tinymce.baseURL = baseUrl.indexOf('://') === -1 ? prefix + baseUrl : baseUrl; | ||
tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL); | ||
var getRoot = function () { | ||
return Option.from(document.getElementById('root')).getOrThunk(function () { | ||
var root = document.createElement('div'); | ||
root.id = 'root'; | ||
document.body.appendChild(root); | ||
return root; | ||
}); | ||
}; | ||
var getTestEditor = function (onLoaded) { | ||
return function (props) { | ||
var cRender = function (props) { | ||
return Chain.async(function (_, next, die) { | ||
var originalInit = props.init || {}; | ||
var originalSetup = originalInit.setup || Fun.noop; | ||
var ref = React.createRef(); | ||
var init = __assign({}, originalInit, { setup: function (editor) { | ||
var init = __assign({}, originalInit, { base_url: "/project/node_modules/tinymce", setup: function (editor) { | ||
originalSetup(editor); | ||
editor.on('SkinLoaded', function () { | ||
setTimeout(function () { | ||
onLoaded(editor, ref); | ||
Option.from(ref.current) | ||
.map(ReactDOM.findDOMNode) | ||
.filter(function (val) { return val instanceof Element; }) | ||
.fold(function () { return die('Could not find DOMNode'); }, function (DOMNode) { | ||
next({ | ||
ref: ref, | ||
editor: editor, | ||
DOMNode: DOMNode | ||
}); | ||
}); | ||
}, 0); | ||
}); | ||
} }); | ||
setTinymceBaseUrl(init.base_url || "/project/node_modules/tinymce"); | ||
return React.createElement(Editor, __assign({ ref: ref }, props, { init: init })); | ||
}; | ||
ReactDOM.render(React.createElement(Editor, __assign({ ref: ref }, props, { init: init })), getRoot()); | ||
}); | ||
}; | ||
var cSetup = function (createElement) { | ||
return Chain.async(function (_, next, die) { | ||
var root = document.createElement('div'); | ||
document.body.appendChild(root); | ||
var onEditorLoaded = function (editor, ref) { | ||
Option.from(ref.current) | ||
.map(ReactDOM.findDOMNode) | ||
.filter(function (val) { return val instanceof Element; }) | ||
.fold(function () { return die('Could not find DOMNode'); }, function (DOMNode) { | ||
next({ | ||
ref: ref, | ||
root: root, | ||
editor: editor, | ||
DOMNode: DOMNode | ||
}); | ||
}); | ||
}; | ||
var testEditor = getTestEditor(onEditorLoaded); | ||
var editorElement = createElement(testEditor); | ||
ReactDOM.render(editorElement, root); | ||
// By rendering the Editor into the same root, React will perform a diff and update. | ||
var cReRender = function (props) { | ||
return Chain.op(function (payload) { | ||
ReactDOM.render(React.createElement(Editor, __assign({ ref: payload.ref }, props)), getRoot()); | ||
}); | ||
}; | ||
var cRemove = Chain.op(function (res) { | ||
ReactDOM.unmountComponentAtNode(res.root); | ||
var cRemove = Chain.op(function (_) { | ||
ReactDOM.unmountComponentAtNode(getRoot()); | ||
}); | ||
@@ -83,2 +76,2 @@ var cNamedChainDirect = function (name) { return NamedChain.direct(NamedChain.inputName(), Chain.mapper(function (res) { return res[name]; }), name); }; | ||
}; | ||
export { cSetup, cRemove, cNamedChainDirect, cDOMNode, cEditor }; | ||
export { cRender, cReRender, cRemove, cNamedChainDirect, cDOMNode, cEditor }; |
import { Chain } from '@ephox/agar'; | ||
declare const EventState: () => { | ||
cEach: (name: string, doAssert: (args: any[]) => void) => Chain<any, any>; | ||
handler: (name: string) => (...args: any[]) => void; | ||
cEach: (name: string, assertState: (args: any[]) => void) => Chain<any, any>; | ||
createHandler: (name: string) => (...args: any[]) => void; | ||
get: (name: string) => any; | ||
cClearState: Chain<{}, {}>; | ||
}; | ||
export { EventState }; |
@@ -0,5 +1,17 @@ | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
import { Chain, Assertions } from '@ephox/agar'; | ||
import { Cell } from '@ephox/katamari'; | ||
var EventState = function () { | ||
var state = {}; | ||
var handler = function (name) { | ||
var state = Cell({}); | ||
var createHandler = function (name) { | ||
return function () { | ||
@@ -10,22 +22,27 @@ var args = []; | ||
} | ||
state[name] = args; | ||
var _a; | ||
state.set(__assign({}, state.get(), (_a = {}, _a[name] = args, _a))); | ||
}; | ||
}; | ||
var get = function (name) { | ||
return state[name]; | ||
return state.get()[name]; | ||
}; | ||
var cEach = function (name, doAssert) { | ||
var cEach = function (name, assertState) { | ||
return Chain.fromChains([ | ||
Chain.op(function () { return Assertions.assertEq(name + ' should exist', true, !!state[name]); }), | ||
Chain.op(function () { | ||
doAssert(state[name]); | ||
Assertions.assertEq('State from "' + name + '" handler should exist', true, name in state.get()); | ||
assertState(state.get()[name]); | ||
}) | ||
]); | ||
}; | ||
var cClearState = Chain.op(function () { | ||
state.set({}); | ||
}); | ||
return { | ||
cEach: cEach, | ||
handler: handler, | ||
get: get | ||
createHandler: createHandler, | ||
get: get, | ||
cClearState: cClearState | ||
}; | ||
}; | ||
export { EventState }; |
import { Assertions, Chain, Logger, Pipeline } from '@ephox/agar'; | ||
import { UnitTest } from '@ephox/bedrock'; | ||
import * as React from 'react'; | ||
import { cRemove, cSetup, cEditor } from '../alien/Loader'; | ||
import { cRemove, cRender, cEditor, cReRender } from '../alien/Loader'; | ||
import { ApiChains } from '@ephox/mcagar'; | ||
@@ -12,14 +11,61 @@ import { getTinymce } from '../../../main/ts/TinyMCE'; | ||
}; | ||
var state = EventState(); | ||
var eventState = EventState(); | ||
Pipeline.async({}, [ | ||
Logger.t('Assert structure of editor and react wrapper events', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return (React.createElement(Editor, { onEditorChange: state.handler('onEditorChange'), onSetContent: state.handler('onSetContent') })); }), | ||
Logger.t('Assert structure of tinymce and tinymce-react events', Chain.asStep({}, [ | ||
cRender({ | ||
onEditorChange: eventState.createHandler('onEditorChange'), | ||
onSetContent: eventState.createHandler('onSetContent') | ||
}), | ||
cEditor(ApiChains.cSetContent('<p>Initial Content</p>')), | ||
// tinymce native event | ||
eventState.cEach('onSetContent', function (args) { return Assertions.assertEq('First arg should be event from Tiny', '<p>Initial Content</p>', args[0].content); }), | ||
eventState.cEach('onSetContent', function (args) { return Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
// tinymce-react unique event | ||
eventState.cEach('onEditorChange', function (args) { return Assertions.assertEq('First arg should be new content', '<p>Initial Content</p>', args[0]); }), | ||
eventState.cEach('onEditorChange', function (args) { return Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
eventState.cClearState, | ||
cRemove | ||
])), | ||
Logger.t('Should be able to register an event handler after initial render', Chain.asStep({}, [ | ||
cRender({ initialValue: '<p>Initial Content</p>' }), | ||
cReRender({ onSetContent: eventState.createHandler('onSetContent') }), | ||
cEditor(ApiChains.cAssertContent('<p>Initial Content</p>')), | ||
cEditor(ApiChains.cSetContent('<p>New Content</p>')), | ||
state.cEach('onEditorChange', function (args) { return Assertions.assertEq('First arg should be new content', '<p>New Content</p>', args[0]); }), | ||
state.cEach('onEditorChange', function (args) { return Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
state.cEach('onSetContent', function (args) { return Assertions.assertEq('First arg should be something', true, !!args[0]); }), | ||
state.cEach('onSetContent', function (args) { return Assertions.assertEq('Second arg should be editor', true, isEditor(args[1])); }), | ||
eventState.cEach('onSetContent', function (args) { return Assertions.assertEq('Should have bound handler, hence new content', '<p>New Content</p>', args[0].content); }), | ||
eventState.cClearState, | ||
cRemove | ||
])), | ||
Logger.t('Providing a new event handler and re-rendering should unbind old handler and bind new handler', Chain.asStep({}, [ | ||
cRender({ onSetContent: eventState.createHandler('InitialHandler') }), | ||
cEditor(ApiChains.cSetContent('<p>Initial Content</p>')), | ||
cReRender({ onSetContent: eventState.createHandler('NewHandler') }), | ||
cEditor(ApiChains.cSetContent('<p>New Content</p>')), | ||
eventState.cEach('InitialHandler', function (args) { | ||
return Assertions.assertEq('Initial handler should have been unbound, hence initial content', '<p>Initial Content</p>', args[0].content); | ||
}), | ||
eventState.cEach('NewHandler', function (args) { | ||
return Assertions.assertEq('New handler should have been bound, hence new content', '<p>New Content</p>', args[0].content); | ||
}), | ||
eventState.cClearState, | ||
cRemove | ||
])), | ||
Logger.t('Test value prop', Chain.asStep({}, [ | ||
cRender({ value: '<p>Initial Value</p>' }), | ||
cEditor(ApiChains.cAssertContent('<p>Initial Value</p>')), | ||
cReRender({ value: '<p>New Value</p>' }), | ||
cEditor(ApiChains.cAssertContent('<p>New Value</p>')), | ||
cRemove | ||
])), | ||
Logger.t('Test disabled prop', Chain.asStep({}, [ | ||
cRender({}), | ||
cEditor(Chain.op(function (editor) { | ||
Assertions.assertEq('Should be design mode', 'design', editor.mode.get()); | ||
})), | ||
cReRender({ disabled: true }), | ||
cEditor(Chain.op(function (editor) { | ||
Assertions.assertEq('Should be readonly mode', 'readonly', editor.mode.get()); | ||
})), | ||
cRemove | ||
])) | ||
], success, failure); | ||
}); |
import { Assertions, Chain, GeneralSteps, Logger, Pipeline } from '@ephox/agar'; | ||
import { UnitTest } from '@ephox/bedrock'; | ||
import * as React from 'react'; | ||
import { cRemove, cSetup, cDOMNode } from '../alien/Loader'; | ||
import { cRemove, cRender, cDOMNode } from '../alien/Loader'; | ||
UnitTest.asynctest('Editor.test', function (success, failure) { | ||
@@ -14,3 +13,3 @@ var cAssertProperty = function (propName, expected) { | ||
Logger.t('it is div by default for inline', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, { inline: true }); }), | ||
cRender({ inline: true }), | ||
cDOMNode(cAssertProperty('tagName', 'DIV')), | ||
@@ -20,3 +19,7 @@ cRemove | ||
Logger.t('can be set to inline in init', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, { init: { inline: true } }); }), | ||
cRender({ | ||
init: { | ||
inline: true | ||
} | ||
}), | ||
cDOMNode(cAssertProperty('tagName', 'DIV')), | ||
@@ -26,3 +29,6 @@ cRemove | ||
Logger.t('it can be changed to p', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, { inline: true, tagName: 'p' }); }), | ||
cRender({ | ||
inline: true, | ||
tagName: 'p' | ||
}), | ||
cDOMNode(cAssertProperty('tagName', 'P')), | ||
@@ -32,3 +38,3 @@ cRemove | ||
Logger.t('iframe editor does not change element', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, { tagName: 'p' }); }), | ||
cRender({ tagName: 'p' }), | ||
cDOMNode(cAssertProperty('tagName', 'TEXTAREA')), | ||
@@ -40,3 +46,3 @@ cRemove | ||
Logger.t('is set normally if prop is provided', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, { id: 'test' }); }), | ||
cRender({ id: 'test' }), | ||
cDOMNode(cAssertProperty('id', 'test')), | ||
@@ -46,3 +52,3 @@ cRemove | ||
Logger.t('gets set automatically to uuid if not set', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, null); }), | ||
cRender({}), | ||
cDOMNode(Chain.op(function (node) { | ||
@@ -56,3 +62,3 @@ Assertions.assertEq('Should not be uuid', typeof node.id === 'string' && node.id.indexOf('tiny-react') !== -1, true); | ||
Logger.t('is not set when prop is not provided', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, null); }), | ||
cRender({}), | ||
cDOMNode(cAssertProperty('name', '')), | ||
@@ -62,3 +68,3 @@ cRemove | ||
Logger.t('is set when prop is provided', Chain.asStep({}, [ | ||
cSetup(function (Editor) { return React.createElement(Editor, { textareaName: 'test' }); }), | ||
cRender({ textareaName: 'test' }), | ||
cDOMNode(cAssertProperty('name', 'test')), | ||
@@ -65,0 +71,0 @@ cRemove |
{ | ||
"name": "@tinymce/tinymce-react", | ||
"version": "3.2.0", | ||
"version": "3.3.0", | ||
"description": "Official TinyMCE React Component", | ||
@@ -38,2 +38,3 @@ "repository": { | ||
"devDependencies": { | ||
"@babel/core": "^7.0.0-0", | ||
"@ephox/agar": "^4.12", | ||
@@ -43,7 +44,7 @@ "@ephox/bedrock": "^4.2.8", | ||
"@ephox/tslint-rules": "^1.0.7", | ||
"@storybook/addon-actions": "^4.1.11", | ||
"@storybook/addon-console": "^1.1.0", | ||
"@storybook/addon-info": "^4.1.11", | ||
"@storybook/addon-notes": "^4.1.11", | ||
"@storybook/react": "^4.1.11", | ||
"@storybook/addon-actions": "^5.1.9", | ||
"@storybook/addon-console": "^1.1.1", | ||
"@storybook/addon-info": "^5.2.0-alpha.30", | ||
"@storybook/addon-notes": "^5.1.9", | ||
"@storybook/react": "^5.1.9", | ||
"@storybook/storybook-deployer": "^2.8.1", | ||
@@ -57,2 +58,3 @@ "@types/node": "^10.12.19", | ||
"awesome-typescript-loader": "^5.2.1", | ||
"babel-loader": "^8.0.0", | ||
"core-js": "^2.6.3", | ||
@@ -59,0 +61,0 @@ "raf": "^3.4.1", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1655
78365
29