+
+
TinyMCE Textarea
+
+
+
+
+
+
TinyMCE Inline
+
+
+
+
+
+ );
+ }
+}
+
+ReactDOM.render(, document.getElementById('root'));
diff --git a/example/index.html b/example/index.html
new file mode 100644
index 0000000..d589de7
--- /dev/null
+++ b/example/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ React TinyMCE Example
+
+
+
+
+
+
+
diff --git a/examples/basic/app.js b/examples/basic/app.js
deleted file mode 100644
index 08318be..0000000
--- a/examples/basic/app.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import TinyMCE from '../../lib/main';
-
-const STYLES = {
- container: {
- fontFamily: 'Helvetica Neue, sans-serif',
- padding: '0 25px'
- },
- output: {
- border: '1px solid #999',
- borderRadius: 5,
- fontFamily: 'Courier New, monospace',
- padding: 10,
- height: 250,
- overflow: 'auto'
- }
-};
-
-const App = React.createClass({
- getInitialState() {
- return {
- content: 'Welcome to react-tinymce
'
- };
- },
-
- handleEditorChange(e) {
- this.setState({
- content: e.target.getContent()
- });
- },
-
- render() {
- return (
-
-
react-tinymce
-
-
-
Output
-
{this.state.content}
-
- );
- }
-});
-
-ReactDOM.render(, document.getElementById('example'));
diff --git a/examples/basic/index.html b/examples/basic/index.html
deleted file mode 100644
index 0b930e9..0000000
--- a/examples/basic/index.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-React TinyMCE component
-
-
-
-
-
diff --git a/examples/index.html b/examples/index.html
deleted file mode 100644
index 6b9d0e2..0000000
--- a/examples/index.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..c11df62
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,52 @@
+const gulp = require('gulp');
+const gulpif = require('gulp-if');
+const babel = require('gulp-babel');
+const browserify = require('browserify');
+const source = require('vinyl-source-stream');
+const buffer = require('vinyl-buffer');
+const gutil = require('gulp-util');
+const uglify = require('gulp-uglify');
+const sourcemaps = require('gulp-sourcemaps');
+const babelify = require('babelify');
+var ghPages = require('gulp-gh-pages');
+
+gulp.task('build:meta', () => {
+ return gulp.src([ 'README.md', 'package.json' ])
+ .pipe(gulp.dest('build'));
+});
+
+gulp.task('build', [ 'build:meta' ], () => {
+ return gulp.src('lib/**/*.js')
+ .pipe(babel())
+ .pipe(gulp.dest('build/lib'));
+});
+
+gulp.task('example', () => {
+ return bundle('./example/example.js', 'main.js', './example', true);
+});
+
+gulp.task('pages', [ 'example' ], function() {
+ return gulp.src('./example/**/*')
+ .pipe(ghPages());
+});
+
+gulp.task('npm-release', [ 'build', 'pages' ]);
+
+function bundle(src, name, outDir, min) {
+ process.env.NODE_ENV = min ? 'production' : 'development';
+ var b = browserify({
+ entries: src,
+ debug: true,
+ transform: [ babelify ],
+ });
+ return b.bundle()
+ .pipe(source(name))
+ .pipe(buffer())
+ .pipe(sourcemaps.init({loadMaps: true}))
+ .pipe(gulpif(min, uglify({
+ output: { ascii_only: true, max_line_len: 100000 }
+ })))
+ .on('error', gutil.log)
+ .pipe(sourcemaps.write('./'))
+ .pipe(gulp.dest(outDir));
+}
diff --git a/karma.conf.js b/karma.conf.js
new file mode 100644
index 0000000..bcd332c
--- /dev/null
+++ b/karma.conf.js
@@ -0,0 +1,53 @@
+module.exports = function(config) {
+ config.set({
+
+ basePath: '',
+
+ frameworks: [
+ 'mocha',
+ 'browserify',
+ 'chai'
+ ],
+
+ files: [
+
+ // tinymce
+ 'node_modules/tinymce/tinymce.min.js',
+ 'node_modules/tinymce/plugins/**/*.min.js',
+ 'node_modules/tinymce/themes/**/*.min.js',
+ { pattern: 'node_modules/tinymce/skins/**', watched: false, included: false },
+
+ // tests
+ 'test/**/*Spec.js',
+ ],
+
+ exclude: [
+ ],
+
+ preprocessors: {
+ 'test/**/*Spec.js': [ 'browserify' ]
+ },
+
+ reporters: ['spec'],
+
+ port: 9876,
+
+ colors: true,
+
+ logLevel: config.LOG_INFO,
+
+ autoWatch: true,
+
+ browsers: ['PhantomJS'],
+
+ singleRun: false,
+
+ concurrency: Infinity,
+
+ // browserify configuration
+ browserify: {
+ debug: true,
+ transform: [ [ 'babelify' ] ]
+ }
+ })
+}
diff --git a/lib/components/TinyMCE.js b/lib/components/TinyMCE.js
index eb778e9..9b49f1c 100644
--- a/lib/components/TinyMCE.js
+++ b/lib/components/TinyMCE.js
@@ -1,4 +1,5 @@
import React from 'react';
+import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import isEqual from 'lodash/lang/isEqual';
import clone from 'lodash/lang/clone';
@@ -29,31 +30,24 @@ const HANDLER_NAMES = EVENTS.map((event) => {
return 'on' + ucFirst(event);
});
-const TinyMCE = React.createClass({
- displayName: 'TinyMCE',
+class TinyMCE extends React.Component {
- propTypes: {
- config: React.PropTypes.object,
- content: React.PropTypes.string,
- id: React.PropTypes.string,
- className: React.PropTypes.string
- },
-
- getDefaultProps() {
- return {
+ constructor() {
+ super();
+ this.state = {
config: {},
content: ''
};
- },
+ }
componentWillMount() {
this.id = this.id || this.props.id || uuid();
- },
+ }
componentDidMount() {
const config = clone(this.props.config);
this._init(config);
- },
+ }
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props.config, nextProps.config)) {
@@ -62,18 +56,18 @@ const TinyMCE = React.createClass({
if (!isEqual(this.props.id, nextProps.id)) {
this.id = nextProps.id;
}
- },
+ }
shouldComponentUpdate(nextProps) {
return (
!isEqual(this.props.content, nextProps.content) ||
!isEqual(this.props.config, nextProps.config)
);
- },
+ }
componentWillUnmount() {
this._remove();
- },
+ }
render() {
return this.props.config.inline ? (
@@ -89,7 +83,7 @@ const TinyMCE = React.createClass({
defaultValue={this.props.content}
/>
);
- },
+ }
_init(config, content) {
if (this._isInit) {
@@ -129,17 +123,26 @@ const TinyMCE = React.createClass({
findDOMNode(this).style.hidden = '';
this._isInit = true;
- },
+ }
_remove() {
tinymce.EditorManager.execCommand('mceRemoveEditor', true, this.id);
this._isInit = false;
}
-});
+}
+
+TinyMCE.displayName = 'TinyMCE',
+
+TinyMCE.propTypes = {
+ config: PropTypes.object,
+ content: PropTypes.string,
+ id: PropTypes.string,
+ className: PropTypes.string
+},
// add handler propTypes
HANDLER_NAMES.forEach((name) => {
- TinyMCE.propTypes[name] = React.PropTypes.func;
+ TinyMCE.propTypes[name] = PropTypes.func;
});
module.exports = TinyMCE;
diff --git a/lib/components/__tests__/TinyMCE-test.js b/lib/components/__tests__/TinyMCE-test.js
deleted file mode 100644
index c717ead..0000000
--- a/lib/components/__tests__/TinyMCE-test.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// import React 'react';
-// import TestUtils from 'react-addons-test-utils';
-// import TinyMCE from '../..//main';
-import { equal } from 'assert';
-
-/* eslint func-names:0 */
-describe('react-tinymce', function() {
- it('should render', function() {
- equal(true, true);
- });
-});
diff --git a/package.json b/package.json
index 54c5510..b64bed6 100644
--- a/package.json
+++ b/package.json
@@ -1,33 +1,66 @@
{
- "name": "react-tinymce",
+ "name": "react-mce",
"version": "0.5.1",
"description": "React TinyMCE component",
"main": "lib/main.js",
"scripts": {
- "test": "node_modules/.bin/rackt test --single-run --browsers Firefox",
- "start": "node_modules/.bin/rackt server"
+ "test": "karma start --single-run",
+ "test-watch": "karma start",
+ "release": "gulp npm-release && npm version $1"
},
"repository": {
"type": "git",
- "url": "https://github.com/instructure-react/react-tinymce.git"
+ "url": "https://github.com/janstuemmel/react-mce.git"
},
- "homepage": "https://github.com/instructure-react/react-tinymce",
+ "homepage": "https://github.com/janstuemmel/react-mce",
"keywords": [
"tinymce",
"react-component"
],
"author": "Matt Zabriskie",
+ "contributors": [
+ "Jan Stümmel"
+ ],
"license": "MIT",
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0",
"react-dom": "^0.14.0 || ^15.0.0"
},
"devDependencies": {
+ "babel-preset-es2015": "^6.24.1",
+ "babel-preset-react": "^6.24.1",
+ "babelify": "^7.3.0",
+ "browserify": "^14.4.0",
+ "chai": "^4.0.2",
+ "domify": "^1.4.0",
+ "gulp": "^3.9.1",
+ "gulp-babel": "^6.1.2",
+ "gulp-gh-pages": "^0.5.4",
+ "gulp-if": "^2.0.2",
+ "gulp-sourcemaps": "^2.6.0",
+ "gulp-uglify": "^3.0.0",
+ "gulp-util": "^3.0.8",
+ "karma": "^1.7.0",
+ "karma-browserify": "^5.1.1",
+ "karma-chai": "^0.1.0",
+ "karma-chrome-launcher": "^2.1.1",
+ "karma-firefox-launcher": "^1.0.1",
+ "karma-mocha": "^1.3.0",
+ "karma-phantomjs-launcher": "^1.0.4",
+ "karma-spec-reporter": "0.0.31",
+ "mocha": "^3.4.2",
+ "mocha-test-container-support": "^0.2.0",
"rackt-cli": "^0.5.3",
- "react": "^15.0.0",
- "react-dom": "^15.0.0"
+ "react": "^15.6.0",
+ "react-dom": "^15.6.0",
+ "sinon": "^2.3.4",
+ "tinymce": "^4.6.4",
+ "vinyl-buffer": "^1.0.0",
+ "vinyl-source-stream": "^1.1.0",
+ "watchify": "^3.9.0"
},
"dependencies": {
- "lodash": "^3.9.3"
+ "lodash": "^3.9.3",
+ "prop-types": "^15.5.10"
}
-}
\ No newline at end of file
+}
diff --git a/test/TinyMCESpec.js b/test/TinyMCESpec.js
new file mode 100644
index 0000000..a107a88
--- /dev/null
+++ b/test/TinyMCESpec.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import domify from 'domify';
+import TestContainer from 'mocha-test-container-support';
+
+import TinyMCE from '../';
+
+describe('TinyMCE component tests', () => {
+
+ var testContainer;
+
+ beforeEach( function () {
+ testContainer = TestContainer.get(this);
+ });
+
+
+ it('should render TinyMCE Component', () => {
+
+ ReactDOM.render(, testContainer);
+ });
+
+
+ it('should render TinyMCE Component inline', () => {
+
+ var div = document.createElement('div');
+ testContainer.appendChild(div);
+
+ ReactDOM.render(
+ ,
+ div
+ );
+ });
+
+
+});
diff --git a/test/basicSpec.js b/test/basicSpec.js
new file mode 100644
index 0000000..5504daf
--- /dev/null
+++ b/test/basicSpec.js
@@ -0,0 +1,50 @@
+const domify = require('domify');
+const TestContainer = require('mocha-test-container-support');
+
+describe('basic test with global tinymce', () => {
+
+ var testContainer;
+
+ beforeEach( function () {
+ testContainer = TestContainer.get(this);
+ });
+
+
+ it('should be defined', () => {
+
+ // when
+ var tm = global.tinymce
+
+ // then
+ expect(tm).to.be.not.undefined
+ });
+
+
+ it('should tinymceify textarea', () => {
+
+ // given
+ testContainer.appendChild(document.createElement('textarea'));
+
+ // when
+ tinymce.init({ selector: 'textarea' });
+
+ // then
+ expect(testContainer.childNodes[0].className).to.include('mce-tinymce');
+ });
+
+
+ it('should tinymceify textarea inline', () => {
+
+ // given
+ const div = domify('Hello World
')
+ testContainer.appendChild(div);
+
+ // when
+ tinymce.init({ selector: '#inline', inline: true });
+
+ // then
+ expect(div.contentEditable).to.equal('true');
+ expect(div.innerHTML).to.equal('Hello World
');
+ });
+
+});
diff --git a/test/fixtures/badCreateClass.js b/test/fixtures/badCreateClass.js
new file mode 100644
index 0000000..26a2711
--- /dev/null
+++ b/test/fixtures/badCreateClass.js
@@ -0,0 +1,5 @@
+const React = require('react');
+var test = React.createClass({
+ render: function(){ return null; }
+});
+module.exports = Test;
diff --git a/test/fixtures/badPropTypes.js b/test/fixtures/badPropTypes.js
new file mode 100644
index 0000000..802ff07
--- /dev/null
+++ b/test/fixtures/badPropTypes.js
@@ -0,0 +1,6 @@
+const React = require('react');
+class Test extends React.Component {
+ render() { return null; }
+}
+Test.propTypes = { foo: React.PropTypes.string };
+module.exports = Test;
diff --git a/test/reactWarningSpec.js b/test/reactWarningSpec.js
new file mode 100644
index 0000000..51c55a5
--- /dev/null
+++ b/test/reactWarningSpec.js
@@ -0,0 +1,55 @@
+
+describe('createClass/PropTypes Error', () => {
+
+ var warn;
+
+ before(() => {
+ // let console.warn throw an error
+ warn = console.warn;
+ console.warn = (msg) => { throw new Error(msg);
+ }});
+
+ after(() => console.warn = warn);
+
+
+ it('should throw PropTypes Warning', () => {
+
+ expect(() => {
+
+ // when
+ var BadPropTypesTest = require('./fixtures/badPropTypes');
+ })
+
+ // then
+ .to.throw(/^Warning: Accessing PropTypes via the main React/);
+
+ });
+
+
+ it('should throw createClass Warning', () => {
+
+ expect(() => {
+
+ // when
+ var BadPropTypesTest = require('./fixtures/badCreateClass');
+ })
+
+ // then
+ .to.throw(/^Warning: Accessing createClass via the main React/);
+
+ });
+
+
+ it('should should not throw creatClass or PropTypes Warning', () => {
+
+ expect(() => {
+
+ // when
+ var TinyMCE = require('../')
+ })
+
+ // then
+ .to.not.throw();
+ });
+
+});