Skip to content

Commit

Permalink
Editable: implement TinyMCE on the core Editable component (#330)
Browse files Browse the repository at this point in the history
* TinyMCE: V1 of the Editable Component initializing TinyMCE
* Drop the global contenteditable
* Expose package globals in test build
* Rename TinyMCE dependency to clarify nightly

More importantly to avoid potential naming clash with existing plugins.
“Nightly” might be more accurately a version description, but this is
all temporary anyways.

* Adding native browser spellcheck
  • Loading branch information
youknowriad authored Mar 28, 2017
1 parent 8e62d60 commit ca4111c
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
}
},
"globals": {
"wp": true
"wp": true,
"tinymce": true
},
"plugins": [
"react",
Expand Down
76 changes: 74 additions & 2 deletions blocks/components/editable/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,75 @@
export default function Editable( { value } ) {
return <p>{ value }</p>;
export default class Editable extends wp.element.Component {
constructor() {
super( ...arguments );
this.onInit = this.onInit.bind( this );
this.onSetup = this.onSetup.bind( this );
this.onChange = this.onChange.bind( this );
this.bindNode = this.bindNode.bind( this );
}

componentDidMount() {
this.initialize();
}

initialize() {
const config = {
target: this.node,
theme: false,
inline: true,
toolbar: false,
browser_spellcheck: true,
entity_encoding: 'raw',
setup: this.onSetup,
formats: {
strikethrough: { inline: 'del' }
}
};

tinymce.init( config );
}

onSetup( editor ) {
this.editor = editor;
editor.on( 'init', this.onInit );
editor.on( 'focusout', this.onChange );
}

onInit() {
this.editor.setContent( this.props.value );
}

onChange() {
if ( ! this.editor.isDirty() ) {
return;
}
const value = this.editor.getContent();
this.editor.save();
this.props.onChange( value );
}

bindNode( ref ) {
this.node = ref;
}

updateContent() {
const bookmark = this.editor.selection.getBookmark( 2, true );
this.editor.setContent( this.props.value );
this.editor.selection.moveToBookmark( bookmark );
}

componentWillUnmount() {
if ( this.editor ) {
this.editor.destroy();
}
}

componentDidUpdate( prevProps ) {
if ( this.props.value !== prevProps.value ) {
this.updateContent();
}
}

render() {
return <div ref={ this.bindNode } />;
}
}
4 changes: 2 additions & 2 deletions editor/blocks/text-block/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const { query, html } = wp.blocks.query;
const { html } = wp.blocks.query;
const Editable = wp.blocks.Editable;

wp.blocks.registerBlock( 'core/text', {
title: 'Text',
icon: 'text',

attributes: {
value: query( 'p', html() )
value: html()
},

edit( attributes, onChange ) {
Expand Down
2 changes: 1 addition & 1 deletion editor/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import InserterButton from '../inserter/button';
const Editor = ( { state: { blocks, inserter }, toggleInserter } ) => {
return (
<div>
<div contentEditable>
<div>
{ blocks.map( ( block, index ) =>
<div key={ index }>
{ wp.blocks.getBlockSettings( block.blockType ).edit( block.attributes ) }
Expand Down
17 changes: 9 additions & 8 deletions element/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* External dependencies
*/
import { createElement as reactCreateElement } from 'react';
import { render as reactRender } from 'react-dom';
import { createElement, Component } from 'react';
import { render } from 'react-dom';

/**
* Returns a new element of given type. Type can be either a string tag name or
Expand All @@ -15,16 +15,17 @@ import { render as reactRender } from 'react-dom';
* @param {...wp.Element} children Descendant elements
* @return {wp.Element} Element
*/
export function createElement( type, props, ...children ) {
return reactCreateElement( type, props, ...children );
}
export { createElement };

/**
* Renders a given element into the target DOM node.
*
* @param {wp.Element} element Element to render
* @param {Element} target DOM node into which element should be rendered
*/
export function render( element, target ) {
reactRender( element, target );
}
export { render };

/**
* A base class to create WordPress Components (Refs, state and lifecycle hooks)
*/
export { Component };
3 changes: 2 additions & 1 deletion index.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ function gutenberg_register_scripts() {
wp_register_script( 'react-dom', 'https://unpkg.com/react-dom@15/dist/react-dom' . $suffix . '.js', array( 'react' ) );

// Editor
wp_register_script( 'tinymce-nightly', 'https://fiddle.azurewebsites.net/tinymce/nightly/tinymce.min.js' );
wp_register_script( 'wp-element', plugins_url( 'element/build/index.js', __FILE__ ), array( 'react', 'react-dom' ) );
wp_register_script( 'wp-blocks', plugins_url( 'blocks/build/index.js', __FILE__ ), array( 'wp-element' ) );
wp_register_script( 'wp-blocks', plugins_url( 'blocks/build/index.js', __FILE__ ), array( 'wp-element', 'tinymce-nightly' ) );
}
add_action( 'init', 'gutenberg_register_scripts' );

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"eslint-config-wordpress": "^1.1.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.3",
"expose-loader": "^0.7.3",
"extract-text-webpack-plugin": "^2.1.0",
"glob": "^7.1.1",
"jsdom": "^9.12.0",
Expand All @@ -42,6 +43,8 @@
"pegjs-loader": "^0.5.1",
"postcss-loader": "^1.3.3",
"raw-loader": "^0.5.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"sass-loader": "^6.0.3",
"sinon": "^2.1.0",
"sinon-chai": "^2.9.0",
Expand Down
14 changes: 13 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,19 @@ switch ( process.env.NODE_ENV ) {

case 'test':
config.target = 'node';
config.entry = glob.sync( `./{${ Object.keys( config.entry ).join() }}/test/*.js` );
config.module.rules = [
...config.module.rules,
...[ 'element', 'blocks', 'editor' ].map( ( entry ) => ( {
test: require.resolve( './' + entry + '/index.js' ),
use: 'expose-loader?wp.' + entry
} ) )
];
config.entry = [
'./element/index.js',
'./blocks/index.js',
'./editor/index.js',
...glob.sync( `./{${ Object.keys( config.entry ).join() }}/test/*.js` )
];
config.externals = [ require( 'webpack-node-externals' )() ];
config.output = {
filename: 'build/test.js',
Expand Down

0 comments on commit ca4111c

Please sign in to comment.