Skip to content
Permalink
Browse files

EZP-26516: As an editor, I want to be able to add/edit tabular data i…

…n RichText editor (#772)

* Add Table button and disable embed and image inside table

* Add support for table headings

Missign alloy menu item, and as we support table headings now we can add this.

* Add Richtext table styling when border is used

* [Tests] Adjust image and embed tests

* [Tests] Add test for table button

* [Trasnlation] Add string for table button

* Fix the click test on table button

* Cleanup based on review, jshint & cleanup of jsdoc

* Refactor to avoid duplicated isDisabled logic
  • Loading branch information...
andrerom committed Jun 13, 2017
1 parent 9d81cab commit 27faf862d98f3f655dde615b3c95eee372c89cf0
@@ -81,6 +81,9 @@ system:
ez-alloyeditor-button-paragraph:
requires: ['ez-alloyeditor', 'ez-translator']
path: "%ez_platformui.public_dir%/js/alloyeditor/buttons/paragraph.js"
ez-alloyeditor-button-table:
requires: ['ez-alloyeditor', 'ez-translator']
path: "%ez_platformui.public_dir%/js/alloyeditor/buttons/table.js"
ez-alloyeditor-button-list:
requires: ['ez-alloyeditor', 'ez-translator']
path: "%ez_platformui.public_dir%/js/alloyeditor/buttons/list.js"
@@ -947,6 +950,7 @@ system:
- 'ez-alloyeditor-button-paragraph'
- 'ez-alloyeditor-button-list'
- 'ez-alloyeditor-button-embed'
- 'ez-alloyeditor-button-table'
- 'ez-alloyeditor-button-image'
- 'ez-alloyeditor-button-imagevariation'
- 'ez-alloyeditor-button-embedcenter'
@@ -7,3 +7,9 @@
margin: 1em 0;
line-height: 1.4;
}

/* To be able to put cursor in empty cells during edit, and more clearly see empty cells in view */
.ez-richtext-content table td,
.ez-richtext-content table th {
padding: 3px;
}
@@ -9,3 +9,11 @@
.ez-richtext-content table:not([border]) th {
border: 1px dotted #888;
}

/* To get rid of the 3d effect on outer border and make it "flat" */
.ez-richtext-content table[border="1"],
.ez-richtext-content table[border="1"] tr,
.ez-richtext-content table[border="1"] td,
.ez-richtext-content table[border="1"] th {
border: 1px solid #888;
}
@@ -55,6 +55,16 @@ YUI.add('ez-alloyeditor-button-embed', function (Y) {
return Y.eZ.trans('select.a.content.to.embed', {}, 'onlineeditor');
},

/**
* Checks if the command is disabled in the current selection.
*
* @method isDisabled
* @return {Boolean} True if the command is disabled, false otherwise.
*/
isDisabled: function () {
return !this.props.editor.get('nativeEditor').ezembed.canBeAdded();
},

/**
* Executes the command generated by the ezembed plugin and set the
* correct value based on the choosen content.
@@ -75,10 +85,10 @@ YUI.add('ez-alloyeditor-button-embed', function (Y) {
},

render: function () {
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses();
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses(), disabled = this.isDisabled();

return (
React.createElement("button", {className: css, onClick: this._chooseContent, tabIndex: this.props.tabIndex},
React.createElement("button", {className: css, disabled: disabled, onClick: this._chooseContent, tabIndex: this.props.tabIndex},
React.createElement("span", {className: "ez-ae-icon ez-ae-icon-embed ez-font-icon"}),
React.createElement("p", {className: "ez-ae-label"}, Y.eZ.trans('embed', {}, 'onlineeditor'))
)
@@ -50,6 +50,16 @@ YUI.add('ez-alloyeditor-button-embed', function (Y) {
return Y.eZ.trans('select.a.content.to.embed', {}, 'onlineeditor');
},

/**
* Checks if the command is disabled in the current selection.
*
* @method isDisabled
* @return {Boolean} True if the command is disabled, false otherwise.
*/
isDisabled: function () {
return !this.props.editor.get('nativeEditor').ezembed.canBeAdded();
},

/**
* Executes the command generated by the ezembed plugin and set the
* correct value based on the choosen content.
@@ -70,10 +80,10 @@ YUI.add('ez-alloyeditor-button-embed', function (Y) {
},

render: function () {
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses();
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses(), disabled = this.isDisabled();

return (
<button className={css} onClick={this._chooseContent} tabIndex={this.props.tabIndex}>
<button className={css} disabled={disabled} onClick={this._chooseContent} tabIndex={this.props.tabIndex}>
<span className="ez-ae-icon ez-ae-icon-embed ez-font-icon"></span>
<p className="ez-ae-label">{Y.eZ.trans('embed', {}, 'onlineeditor')}</p>
</button>
@@ -59,6 +59,16 @@ YUI.add('ez-alloyeditor-button-image', function (Y) {
return Y.eZ.trans('select.an.image.to.embed', {}, 'onlineeditor');
},

/**
* Checks if the command is disabled in the current selection.
*
* @method isDisabled
* @return {Boolean} True if the command is disabled, false otherwise.
*/
isDisabled: function () {
return !this.props.editor.get('nativeEditor').ezembed.canBeAdded();
},

/**
* Executes the command generated by the ezembed plugin and set the
* correct value based on the choosen image.
@@ -82,10 +92,10 @@ YUI.add('ez-alloyeditor-button-image', function (Y) {
},

render: function () {
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses();
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses(), disabled = this.isDisabled();

return (
React.createElement("button", {className: css, onClick: this._chooseContent, tabIndex: this.props.tabIndex},
React.createElement("button", {className: css, disabled: disabled, onClick: this._chooseContent, tabIndex: this.props.tabIndex},
React.createElement("span", {className: "ez-ae-icon ez-ae-icon-image ez-font-icon"}),
React.createElement("p", {className: "ez-ae-label"}, Y.eZ.trans('image', {}, 'onlineeditor'))
)
@@ -54,6 +54,16 @@ YUI.add('ez-alloyeditor-button-image', function (Y) {
return Y.eZ.trans('select.an.image.to.embed', {}, 'onlineeditor');
},

/**
* Checks if the command is disabled in the current selection.
*
* @method isDisabled
* @return {Boolean} True if the command is disabled, false otherwise.
*/
isDisabled: function () {
return !this.props.editor.get('nativeEditor').ezembed.canBeAdded();
},

/**
* Executes the command generated by the ezembed plugin and set the
* correct value based on the choosen image.
@@ -77,10 +87,10 @@ YUI.add('ez-alloyeditor-button-image', function (Y) {
},

render: function () {
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses();
var css = "ae-button ez-ae-labeled-button" + this.getStateClasses(), disabled = this.isDisabled();

return (
<button className={css} onClick={this._chooseContent} tabIndex={this.props.tabIndex}>
<button className={css} disabled={disabled} onClick={this._chooseContent} tabIndex={this.props.tabIndex}>
<span className="ez-ae-icon ez-ae-icon-image ez-font-icon"></span>
<p className="ez-ae-label">{Y.eZ.trans('image', {}, 'onlineeditor')}</p>
</button>
@@ -0,0 +1,58 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
// **NOTICE:**
// THIS IS AN AUTO-GENERATED FILE
// DO YOUR MODIFICATIONS IN THE CORRESPONDING .jsx FILE
// AND REGENERATE IT WITH: grunt jsx
// END OF NOTICE
YUI.add('ez-alloyeditor-button-table', function (Y) {
"use strict";

var AlloyEditor = Y.eZ.AlloyEditor,
React = Y.eZ.React,
ButtonTable;

/**
* The ButtonTable class provides functionality for creating and editing a table in a document. ButtonTable
* renders in two different modes:
*
* - Normal: Just a button that allows to switch to the edition mode
* - Exclusive: The ButtonTableEdit UI with all the table edition controls.
*
* This component is a copy of AlloyEditor.ButtonTable, with slightly different render() logic.
*
* @class ButtonTable
*/
ButtonTable = React.createClass({displayName: "ButtonTable",
propTypes: {
editor: React.PropTypes.object.isRequired,
label: React.PropTypes.string,
tabIndex: React.PropTypes.number,
},

statics: {
key: 'eztable'
},

render: function () {
if (this.props.renderExclusive) {
return (
React.createElement(AlloyEditor.ButtonTableEdit, React.__spread({}, this.props))
);
}

var css = "ae-button ez-ae-labeled-button";

return (
React.createElement("button", {className: css, onClick: this.props.requestExclusive, tabIndex: this.props.tabIndex},
React.createElement("span", {className: "ez-ae-icon ez-ae-icon-table ae-icon-table"}),
React.createElement("p", {className: "ez-ae-label"}, Y.eZ.trans('table', {}, 'onlineeditor'))
)
);
},
});

AlloyEditor.Buttons[ButtonTable.key] = AlloyEditor.ButtonTable = ButtonTable;
});
@@ -0,0 +1,53 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-alloyeditor-button-table', function (Y) {
"use strict";

var AlloyEditor = Y.eZ.AlloyEditor,
React = Y.eZ.React,
ButtonTable;

/**
* The ButtonTable class provides functionality for creating and editing a table in a document. ButtonTable
* renders in two different modes:
*
* - Normal: Just a button that allows to switch to the edition mode
* - Exclusive: The ButtonTableEdit UI with all the table edition controls.
*
* This component is a copy of AlloyEditor.ButtonTable, with slightly different render() logic.
*
* @class ButtonTable
*/
ButtonTable = React.createClass({
propTypes: {
editor: React.PropTypes.object.isRequired,
label: React.PropTypes.string,
tabIndex: React.PropTypes.number,
},

statics: {
key: 'eztable'
},

render: function () {
if (this.props.renderExclusive) {
return (
<AlloyEditor.ButtonTableEdit {...this.props} />
);
}

var css = "ae-button ez-ae-labeled-button";

return (
<button className={css} onClick={this.props.requestExclusive} tabIndex={this.props.tabIndex}>
<span className="ez-ae-icon ez-ae-icon-table ae-icon-table"></span>
<p className="ez-ae-label">{Y.eZ.trans('table', {}, 'onlineeditor')}</p>
</button>
);
},
});

AlloyEditor.Buttons[ButtonTable.key] = AlloyEditor.ButtonTable = ButtonTable;
});
@@ -25,6 +25,14 @@ YUI.add('ez-alloyeditor-plugin-embed', function (Y) {
requires: 'widget,ezaddcontent',

init: function (editor) {
editor.ezembed = {
canBeAdded: function () {
var path = editor.elementPath();

return !path || path.contains('table', true) === null;
}
};

editor.widgets.add('ezembed', {
defaults: {
href: "ezcontent://57",
@@ -24,6 +24,7 @@ YUI.add('ez-alloyeditor-toolbar-config-table', function (Y) {
buttons: [
'ezmoveup',
'ezmovedown',
'tableHeading',
'tableRow',
'tableColumn',
'tableCell',
@@ -450,7 +450,7 @@ YUI.add('ez-richtext-editview', function (Y) {
tabIndex: 1
},
ezadd: {
buttons: ['ezheading', 'ezparagraph', 'ezlist', 'ezimage', 'ezembed'],
buttons: ['ezheading', 'ezparagraph', 'ezlist', 'ezimage', 'ezembed', 'eztable'],
tabIndex: 2,
},
};
@@ -142,6 +142,12 @@
<jms:reference-file>Resources/public/js/alloyeditor/toolbars/config/heading.js</jms:reference-file>
<jms:reference-file>Resources/public/js/alloyeditor/toolbars/config/paragraph.js</jms:reference-file>
</trans-unit>
<trans-unit id="6e5d0fc246621e17c8804d0304662f7791e58dec" resname="table">
<source>Table</source>
<target>Table</target>
<note>key: table</note>
<jms:reference-file>Resources/public/js/alloyeditor/buttons/table.js</jms:reference-file>
</trans-unit>
<trans-unit id="c5ef10db9a6a48b85cd15ce1050f42933a4305ae" resname="remove.this.block">
<source>Remove this block</source>
<target>Remove this block</target>
@@ -15,17 +15,60 @@ YUI.add('ez-alloyeditor-button-embed-tests', function (Y) {

setUp: function () {
this.container = Y.one('.container').getDOMNode();
this.editor = {};
this.editor = new Mock();
this.nativeEditor = new Mock();
this.nativeEditor.ezembed = new Mock();

Mock.expect(this.editor, {
method: 'get',
args: ['nativeEditor'],
returns: this.nativeEditor
});
},

tearDown: function () {
ReactDOM.unmountComponentAtNode(this.container);
delete this.editor;
delete this.nativeEditor;
},

"Should render a enabled button": function () {
var button;

Mock.expect(this.nativeEditor.ezembed, {
method: 'canBeAdded',
args: [],
returns: true,
});

button = ReactDOM.render(
<AlloyEditor.ButtonEmbed editor={this.editor} />,
this.container
);

Assert.isNotNull(
ReactDOM.findDOMNode(button),
"The button should be rendered"
);
Assert.areEqual(
"BUTTON", ReactDOM.findDOMNode(button).tagName,
"The component should generate a button"
);
Assert.areEqual(
false, ReactDOM.findDOMNode(button).disabled,
"The button should not be disabled"
);
},

"Should render a button": function () {
"Should render a disabled button": function () {
var button;

Mock.expect(this.nativeEditor.ezembed, {
method: 'canBeAdded',
args: [],
returns: false,
});

button = ReactDOM.render(
<AlloyEditor.ButtonEmbed editor={this.editor} />,
this.container
@@ -39,6 +82,10 @@ YUI.add('ez-alloyeditor-button-embed-tests', function (Y) {
"BUTTON", ReactDOM.findDOMNode(button).tagName,
"The component should generate a button"
);
Assert.areEqual(
true, ReactDOM.findDOMNode(button).disabled,
"The button should be disabled"
);
},
});

Oops, something went wrong.

0 comments on commit 27faf86

Please sign in to comment.
You can’t perform that action at this time.