diff --git a/packages/ckeditor5-table/docs/_snippets/features/table-default-properties.html b/packages/ckeditor5-table/docs/_snippets/features/table-default-properties.html
new file mode 100644
index 00000000000..8d2f6837ee2
--- /dev/null
+++ b/packages/ckeditor5-table/docs/_snippets/features/table-default-properties.html
@@ -0,0 +1 @@
+
diff --git a/packages/ckeditor5-table/docs/_snippets/features/table-default-properties.js b/packages/ckeditor5-table/docs/_snippets/features/table-default-properties.js
new file mode 100644
index 00000000000..c8805ba8cd0
--- /dev/null
+++ b/packages/ckeditor5-table/docs/_snippets/features/table-default-properties.js
@@ -0,0 +1,55 @@
+/**
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals ClassicEditor, CKEditorPlugins, console, window, document */
+
+ClassicEditor
+ .create( document.querySelector( '#snippet-default-properties' ), {
+ extraPlugins: [
+ CKEditorPlugins.TableProperties,
+ CKEditorPlugins.TableCellProperties
+ ],
+ table: {
+ contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties' ],
+ tableProperties: {
+ defaultProperties: {
+ borderStyle: 'dashed',
+ borderColor: 'hsl(0, 0%, 60%)',
+ borderWidth: '3px',
+ alignment: 'left'
+ }
+ },
+ tableCellProperties: {
+ defaultProperties: {
+ borderStyle: 'dotted',
+ borderColor: 'hsl(120, 75%, 60%)',
+ borderWidth: '2px',
+ horizontalAlignment: 'right',
+ verticalAlignment: 'bottom'
+ }
+ }
+ },
+ image: {
+ toolbar: [
+ 'imageStyle:full',
+ 'imageStyle:side',
+ '|',
+ 'imageTextAlternative'
+ ]
+ },
+ placeholder: 'Insert the new table with applied the default styles.'
+ } )
+ .then( editor => {
+ window.editorDefaultStyles = editor;
+
+ window.attachTourBalloon( {
+ target: window.findToolbarItem( editor.ui.view.toolbar, 0 ),
+ text: 'Click to insert the new table.',
+ editor
+ } );
+ } )
+ .catch( err => {
+ console.error( err.stack );
+ } );
diff --git a/packages/ckeditor5-table/docs/features/table.md b/packages/ckeditor5-table/docs/features/table.md
index be1827558a9..666a679be9f 100644
--- a/packages/ckeditor5-table/docs/features/table.md
+++ b/packages/ckeditor5-table/docs/features/table.md
@@ -205,6 +205,45 @@ ClassicEditor
.catch( ... );
```
+### Default table and cell styles
+
+You can specify the default styles for the table and cells that will be applied while creating a new table. To configure default table styles, pass the `defaultProperties` object to {@link module:table/table~TableConfig#tableProperties} configuration option. Similarly, cell styles can be configured by setting `defaultProperties` on {@link module:table/table~TableConfig#tableCellProperties} option.
+
+```js
+const tableConfig = {
+ table: {
+ tableProperties: {
+ // The default styles for new tables.
+ defaultProperties: {
+ borderStyle: 'dashed',
+ borderColor: 'hsl(0, 0%, 60%)',
+ borderWidth: '3px',
+ alignment: 'left'
+ }
+ },
+
+ tableCellProperties: {
+ // The default styles for each cell inside the created table.
+ defaultProperties: {
+ borderStyle: 'dotted',
+ borderColor: 'hsl(120, 75%, 60%)',
+ borderWidth: '2px',
+ horizontalAlignment: 'right',
+ verticalAlignment: 'bottom'
+ }
+ }
+ }
+};
+```
+
+Read more about supported values in {@link module:table/table~TableConfig}.
+
+{@snippet features/table-default-properties}
+
+
+ The default table and cell styles **do not** impact the {@link builds/guides/integration/basic-api#setting-the-editor-data data loaded into the editor}. They are used only when creating the new table.
+
+
## Block vs inline content in table cells
The table feature allows creating block content (like paragraphs, lists, headings, etc.) in table cells. However, if a table cell contains just one paragraph and this paragraph has no special attributes (like text alignment), the cell content is considered "inline" and the paragraph is not rendered.
diff --git a/packages/ckeditor5-table/src/commands/insertcolumncommand.js b/packages/ckeditor5-table/src/commands/insertcolumncommand.js
index b33cc8106d1..05c06a12be0 100644
--- a/packages/ckeditor5-table/src/commands/insertcolumncommand.js
+++ b/packages/ckeditor5-table/src/commands/insertcolumncommand.js
@@ -78,6 +78,10 @@ export default class InsertColumnCommand extends Command {
const column = insertBefore ? columnIndexes.first : columnIndexes.last;
const table = affectedTableCells[ 0 ].findAncestor( 'table' );
- tableUtils.insertColumns( table, { columns: 1, at: insertBefore ? column : column + 1 } );
+ // The `TableUtils` plugin fires the `#insertColumns` event. In order to having a single undo step,
+ // we need to wrap the code in the `editor.model.change()` block.
+ editor.model.change( () => {
+ tableUtils.insertColumns( table, { columns: 1, at: insertBefore ? column : column + 1 } );
+ } );
}
}
diff --git a/packages/ckeditor5-table/src/commands/insertrowcommand.js b/packages/ckeditor5-table/src/commands/insertrowcommand.js
index 14f95699e62..4c5be76e54d 100644
--- a/packages/ckeditor5-table/src/commands/insertrowcommand.js
+++ b/packages/ckeditor5-table/src/commands/insertrowcommand.js
@@ -77,6 +77,10 @@ export default class InsertRowCommand extends Command {
const row = insertAbove ? rowIndexes.first : rowIndexes.last;
const table = affectedTableCells[ 0 ].findAncestor( 'table' );
- tableUtils.insertRows( table, { at: insertAbove ? row : row + 1, copyStructureFromAbove: !insertAbove } );
+ // The `TableUtils` plugin fires the `#insertRows` event. In order to having a single undo step,
+ // we need to wrap the code in the `editor.model.change()` block.
+ editor.model.change( () => {
+ tableUtils.insertRows( table, { at: insertAbove ? row : row + 1, copyStructureFromAbove: !insertAbove } );
+ } );
}
}
diff --git a/packages/ckeditor5-table/src/converters/downcast.js b/packages/ckeditor5-table/src/converters/downcast.js
index 540b3ecf6e6..e35e45caa7a 100644
--- a/packages/ckeditor5-table/src/converters/downcast.js
+++ b/packages/ckeditor5-table/src/converters/downcast.js
@@ -7,7 +7,7 @@
* @module table/converters/downcast
*/
-import TableWalker from './../tablewalker';
+import TableWalker from '../tablewalker';
import { setHighlightHandling, toWidget, toWidgetEditable } from 'ckeditor5/src/widget';
import { toArray } from 'ckeditor5/src/utils';
diff --git a/packages/ckeditor5-table/src/converters/table-cell-default-properties-post-fixer.js b/packages/ckeditor5-table/src/converters/table-cell-default-properties-post-fixer.js
new file mode 100644
index 00000000000..0c52fe06d7d
--- /dev/null
+++ b/packages/ckeditor5-table/src/converters/table-cell-default-properties-post-fixer.js
@@ -0,0 +1,96 @@
+/**
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module table/converters/table-layout-post-fixer
+ */
+
+const TABLE_CELL_PROPERTIES = [
+ 'borderStyle',
+ 'borderColor',
+ 'borderWidth',
+ 'backgroundColor',
+ 'padding',
+ 'horizontalAlignment',
+ 'verticalAlignment',
+ 'width',
+ 'height'
+];
+
+/**
+ * Injects a table cell default properties post-fixer into the model.
+ *
+ * A table cell should have specified the default properties when a new cell was added into the table.
+ *
+ * @param {module:core/editor/editor~Editor} editor
+ */
+export default function injectTableCellDefaultPropertiesPostFixer( editor ) {
+ editor.model.document.registerPostFixer( writer => tableCellDefaultPropertiesPostFixer( writer, editor ) );
+}
+
+// The table cell default properties post-fixer.
+//
+// @param {module:engine/model/writer~Writer} writer
+// @param {module:core/editor/editor~Editor} editor
+function tableCellDefaultPropertiesPostFixer( writer, editor ) {
+ const model = editor.model;
+ const changes = model.document.differ.getChanges();
+ const cellProperties = editor.config.get( 'table.tableCellProperties.defaultProperties' );
+
+ // Do not check anything if the default cell properties are not specified.
+ if ( Object.keys( cellProperties ).length === 0 ) {
+ return false;
+ }
+
+ let wasFixed = false;
+
+ for ( const entry of changes ) {
+ if ( entry.type != 'insert' ) {
+ continue;
+ }
+
+ // Fix table on adding/removing table cells and rows.
+ if ( entry.name == 'tableRow' ) {
+ const tableRow = entry.position.nodeAfter;
+
+ // For each cell in the table row...
+ for ( const tableCell of tableRow.getChildren() ) {
+ // ...check its cell properties...
+ if ( shouldApplyDefaultCellProperties( tableCell ) ) {
+ // ...and if the cell has no properties, apply the default.
+ writer.setAttributes( cellProperties, tableCell );
+
+ wasFixed = true;
+ }
+ }
+ }
+
+ // Fix table cell on adding/removing table cells and rows.
+ if ( entry.name == 'tableCell' ) {
+ const tableCell = entry.position.nodeAfter;
+
+ if ( shouldApplyDefaultCellProperties( tableCell ) ) {
+ // ...and if the cell has no properties, apply the default.
+ writer.setAttributes( cellProperties, tableCell );
+
+ wasFixed = true;
+ }
+ }
+ }
+
+ return wasFixed;
+}
+
+// Checks whether the default properties should be applied for the specified cell.
+//
+// The default properties will be applied only if the cell does not contain any "visual" properties.
+//
+// @param {module:engine/model/element~Element} tableCell
+// @returns {Boolean}
+function shouldApplyDefaultCellProperties( tableCell ) {
+ const attrs = [ ...tableCell.getAttributeKeys() ];
+
+ return attrs.some( attributeName => TABLE_CELL_PROPERTIES.includes( attributeName ) ) === false;
+}
diff --git a/packages/ckeditor5-table/src/converters/table-layout-post-fixer.js b/packages/ckeditor5-table/src/converters/table-layout-post-fixer.js
index 7eb5fda0f52..5e62ed7606e 100644
--- a/packages/ckeditor5-table/src/converters/table-layout-post-fixer.js
+++ b/packages/ckeditor5-table/src/converters/table-layout-post-fixer.js
@@ -7,7 +7,7 @@
* @module table/converters/table-layout-post-fixer
*/
-import TableWalker from './../tablewalker';
+import TableWalker from '../tablewalker';
import { createEmptyTableCell, updateNumericAttribute } from '../utils/common';
/**
diff --git a/packages/ckeditor5-table/src/tablecellproperties.js b/packages/ckeditor5-table/src/tablecellproperties.js
index daffcb7d022..9de6f39f0c5 100644
--- a/packages/ckeditor5-table/src/tablecellproperties.js
+++ b/packages/ckeditor5-table/src/tablecellproperties.js
@@ -64,6 +64,32 @@ export default class TableCellProperties extends Plugin {
* }
* };
*
+ * * The default styles for the cell while creating a new table (`tableCellProperties.defaultProperties`):
+ *
+ * const tableConfig = {
+ * tableCellProperties: {
+ * defaultProperties: {
+ * borderStyle: 'dotted',
+ * borderColor: 'hsl(120, 75%, 60%)',
+ * borderWidth: '2px',
+ * horizontalAlignment: 'right',
+ * verticalAlignment: 'bottom'
+ * }
+ * }
+ * }
+ *
+ * The following properties are supported:
+ *
+ * * `backgroundColor` – sets the cell background color
+ * * `borderColor` – sets the border color
+ * * `borderStyle` – sets the border style
+ * * `borderWidth` – sets the border width
+ * * `height` – sets the cell height
+ * * `horizontalAlignment` – sets the cell horizontal alignment
+ * * `padding` – sets the cell padding
+ * * `verticalAlignment` – sets the cell vertical alignment
+ * * `width` – sets the cell width
+ *
* **Note**: The configurations do not impact the data loaded into the editor,
* i.e. they do not limit or filter the colors in the data. They are used only in the user interface
* allowing users to pick colors in a more convenient way.
diff --git a/packages/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js b/packages/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js
index 9944dacf349..16674b6224d 100644
--- a/packages/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js
+++ b/packages/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js
@@ -21,6 +21,9 @@ import TableCellHorizontalAlignmentCommand from './commands/tablecellhorizontala
import TableCellBorderStyleCommand from './commands/tablecellborderstylecommand';
import TableCellBorderColorCommand from './commands/tablecellbordercolorcommand';
import TableCellBorderWidthCommand from './commands/tablecellborderwidthcommand';
+import TableWalker from '../tablewalker';
+import TableUtils from '../tableutils';
+import injectTableCellDefaultPropertiesPostFixer from '../converters/table-cell-default-properties-post-fixer';
const VALIGN_VALUES_REG_EXP = /^(top|bottom)$/;
@@ -57,7 +60,7 @@ export default class TableCellPropertiesEditing extends Plugin {
* @inheritDoc
*/
static get requires() {
- return [ TableEditing ];
+ return [ TableEditing, TableUtils ];
}
/**
@@ -69,6 +72,8 @@ export default class TableCellPropertiesEditing extends Plugin {
const conversion = editor.conversion;
const locale = editor.locale;
+ editor.config.define( 'table.tableCellProperties.defaultProperties', {} );
+
editor.data.addStyleProcessorRules( addBorderRules );
enableBorderProperties( schema, conversion );
editor.commands.add( 'tableCellBorderStyle', new TableCellBorderStyleCommand( editor ) );
@@ -94,6 +99,36 @@ export default class TableCellPropertiesEditing extends Plugin {
enableVerticalAlignmentProperty( schema, conversion );
editor.commands.add( 'tableCellVerticalAlignment', new TableCellVerticalAlignmentCommand( editor ) );
+
+ this._enableDefaultCellProperties();
+
+ injectTableCellDefaultPropertiesPostFixer( editor );
+ }
+
+ /**
+ * Enables applying the default cell properties.
+ *
+ * @private
+ */
+ _enableDefaultCellProperties() {
+ const editor = this.editor;
+ const tableCellProperties = editor.config.get( 'table.tableCellProperties.defaultProperties' );
+ const tableUtils = editor.plugins.get( TableUtils );
+
+ // Apply default cell properties while creating a new table.
+ this.listenTo( tableUtils, 'createTable', ( evt, [ writer ] ) => {
+ const tableElement = evt.return;
+
+ for ( const item of new TableWalker( tableElement ) ) {
+ writer.setAttributes( tableCellProperties, item.cell );
+ }
+ }, { priority: 'low' } );
+
+ // Apply default cell properties while inserting new rows into the table.
+ this.listenTo( tableUtils, 'insertRows', applyDefaultCellProperties( editor, tableCellProperties ), { priority: 'low' } );
+
+ // Apply default cell properties while inserting new rows into the table.
+ this.listenTo( tableUtils, 'insertColumns', applyDefaultCellProperties( editor, tableCellProperties ), { priority: 'low' } );
}
}
@@ -202,3 +237,20 @@ function enableProperty( schema, conversion, modelAttribute, styleName ) {
upcastStyleToAttribute( conversion, 'tableCell', modelAttribute, styleName );
downcastAttributeToStyle( conversion, 'tableCell', modelAttribute, styleName );
}
+
+// A factory function that returns a handler which applies default cell properties for created cells.
+//
+// @param {module:core/editor/editor~Editor} editor
+// @param {module:table/table~TableConfig#tableCellProperties} tableCellProperties
+// @return {Function}
+function applyDefaultCellProperties( editor, tableCellProperties ) {
+ return evt => {
+ const createdCells = evt.return;
+
+ editor.model.change( writer => {
+ for ( const tableCell of createdCells ) {
+ writer.setAttributes( tableCellProperties, tableCell );
+ }
+ } );
+ };
+}
diff --git a/packages/ckeditor5-table/src/tableproperties.js b/packages/ckeditor5-table/src/tableproperties.js
index 25d23f8ffac..1ded0939aa7 100644
--- a/packages/ckeditor5-table/src/tableproperties.js
+++ b/packages/ckeditor5-table/src/tableproperties.js
@@ -65,6 +65,29 @@ export default class TableProperties extends Plugin {
* }
* };
*
+ * * The default styles for the new created table (`tableProperties.defaultProperties`):
+ *
+ * const tableConfig = {
+ * tableProperties: {
+ * defaultProperties: {
+ * borderStyle: 'dashed',
+ * borderColor: 'hsl(0, 0%, 90%)',
+ * borderWidth: '3px',
+ * alignment: 'left'
+ * }
+ * }
+ * }
+ *
+ * The following properties are supported:
+ *
+ * * `alignment` – sets the table alignment
+ * * `backgroundColor` – sets the table background color
+ * * `borderColor` – sets the border color
+ * * `borderStyle` – sets the border style
+ * * `borderWidth` – sets the border width
+ * * `height` – sets the table height
+ * * `width` – sets the table width
+ *
* **Note**: The configurations do not impact the data loaded into the editor,
* i.e. they do not limit or filter the colors in the data. They are used only in the user interface
* allowing users to pick colors in a more convenient way.
diff --git a/packages/ckeditor5-table/src/tableproperties/tablepropertiesediting.js b/packages/ckeditor5-table/src/tableproperties/tablepropertiesediting.js
index c0648cff5bd..e0576cbf11d 100644
--- a/packages/ckeditor5-table/src/tableproperties/tablepropertiesediting.js
+++ b/packages/ckeditor5-table/src/tableproperties/tablepropertiesediting.js
@@ -24,6 +24,7 @@ import TableBorderWidthCommand from './commands/tableborderwidthcommand';
import TableWidthCommand from './commands/tablewidthcommand';
import TableHeightCommand from './commands/tableheightcommand';
import TableAlignmentCommand from './commands/tablealignmentcommand';
+import TableUtils from '../tableutils';
const ALIGN_VALUES_REG_EXP = /^(left|right)$/;
@@ -58,7 +59,7 @@ export default class TablePropertiesEditing extends Plugin {
* @inheritDoc
*/
static get requires() {
- return [ TableEditing ];
+ return [ TableEditing, TableUtils ];
}
/**
@@ -69,6 +70,8 @@ export default class TablePropertiesEditing extends Plugin {
const schema = editor.model.schema;
const conversion = editor.conversion;
+ editor.config.define( 'table.tableProperties.defaultProperties', {} );
+
editor.data.addStyleProcessorRules( addBorderRules );
enableBorderProperties( schema, conversion );
editor.commands.add( 'tableBorderColor', new TableBorderColorCommand( editor ) );
@@ -87,6 +90,26 @@ export default class TablePropertiesEditing extends Plugin {
editor.data.addStyleProcessorRules( addBackgroundRules );
enableProperty( schema, conversion, 'backgroundColor', 'background-color' );
editor.commands.add( 'tableBackgroundColor', new TableBackgroundColorCommand( editor ) );
+
+ this._enableDefaultTableProperties();
+ }
+
+ /**
+ * Enables applying the default table properties.
+ *
+ * @private
+ */
+ _enableDefaultTableProperties() {
+ const editor = this.editor;
+ const tableProperties = editor.config.get( 'table.tableProperties.defaultProperties' );
+ const tableUtils = editor.plugins.get( TableUtils );
+
+ // Apply default table properties while creating a new table.
+ this.listenTo( tableUtils, 'createTable', ( evt, [ writer ] ) => {
+ const tableElement = evt.return;
+
+ writer.setAttributes( tableProperties, tableElement );
+ }, { priority: 'low' } );
}
}
diff --git a/packages/ckeditor5-table/src/tableutils.js b/packages/ckeditor5-table/src/tableutils.js
index 23b7ea9d8ca..c6fcd60b8b0 100644
--- a/packages/ckeditor5-table/src/tableutils.js
+++ b/packages/ckeditor5-table/src/tableutils.js
@@ -32,6 +32,7 @@ export default class TableUtils extends Plugin {
init() {
this.decorate( 'insertColumns' );
this.decorate( 'insertRows' );
+ this.decorate( 'createTable' );
}
/**
@@ -141,6 +142,7 @@ export default class TableUtils extends Plugin {
* @param {Number} [options.rows=1] The number of rows to insert.
* @param {Boolean|undefined} [options.copyStructureFromAbove] The flag for copying row structure. Note that
* the row structure will not be copied if this option is not provided.
+ * @returns {Array.} Created table cells.
*/
insertRows( table, options = {} ) {
const model = this.editor.model;
@@ -153,7 +155,7 @@ export default class TableUtils extends Plugin {
const rows = this.getRows( table );
const columns = this.getColumns( table );
- model.change( writer => {
+ return model.change( writer => {
const headingRows = table.getAttribute( 'headingRows' ) || 0;
// Inserting rows inside heading section requires to update `headingRows` attribute as the heading section will grow.
@@ -163,9 +165,7 @@ export default class TableUtils extends Plugin {
// Inserting at the end or at the beginning of a table doesn't require to calculate anything special.
if ( !isCopyStructure && ( insertAt === 0 || insertAt === rows ) ) {
- createEmptyRows( writer, table, insertAt, rowsToInsert, columns );
-
- return;
+ return createEmptyRows( writer, table, insertAt, rowsToInsert, columns );
}
// Iterate over all the rows above the inserted rows in order to check for the row-spanned cells.
@@ -195,6 +195,8 @@ export default class TableUtils extends Plugin {
}
}
+ const createdCells = [];
+
for ( let rowIndex = 0; rowIndex < rowsToInsert; rowIndex++ ) {
const tableRow = writer.createElement( 'tableRow' );
@@ -206,13 +208,17 @@ export default class TableUtils extends Plugin {
// Insert the empty cell only if this slot is not row-spanned from any other cell.
if ( colspan > 0 ) {
- createEmptyTableCell( writer, insertPosition, colspan > 1 ? { colspan } : null );
+ const tableCell = createEmptyTableCell( writer, insertPosition, colspan > 1 ? { colspan } : null );
+
+ createdCells.push( tableCell );
}
// Skip the col-spanned slots, there won't be any cells.
cellIndex += Math.abs( colspan ) - 1;
}
}
+
+ return createdCells;
} );
}
@@ -241,6 +247,7 @@ export default class TableUtils extends Plugin {
* @param {Object} options
* @param {Number} [options.at=0] The column index at which the columns will be inserted.
* @param {Number} [options.columns=1] The number of columns to insert.
+ * @returns {Array.} Created table cells.
*/
insertColumns( table, options = {} ) {
const model = this.editor.model;
@@ -248,8 +255,9 @@ export default class TableUtils extends Plugin {
const insertAt = options.at || 0;
const columnsToInsert = options.columns || 1;
- model.change( writer => {
+ return model.change( writer => {
const headingColumns = table.getAttribute( 'headingColumns' );
+ const createdCells = [];
// Inserting columns inside heading section requires to update `headingColumns` attribute as the heading section will grow.
if ( insertAt < headingColumns ) {
@@ -261,10 +269,12 @@ export default class TableUtils extends Plugin {
// Inserting at the end and at the beginning of a table doesn't require to calculate anything special.
if ( insertAt === 0 || tableColumns === insertAt ) {
for ( const tableRow of table.getChildren() ) {
- createCells( columnsToInsert, writer, writer.createPositionAt( tableRow, insertAt ? 'end' : 0 ) );
+ createdCells.push(
+ ...createCells( columnsToInsert, writer, writer.createPositionAt( tableRow, insertAt ? 'end' : 0 ) )
+ );
}
- return;
+ return createdCells;
}
const tableWalker = new TableWalker( table, { column: insertAt, includeAllSlots: true } );
@@ -291,9 +301,13 @@ export default class TableUtils extends Plugin {
} else {
// It's either cell at this column index or spanned cell by a row-spanned cell from row above.
// In table above it's cell "e" and a spanned position from row below (empty cell between cells "g" and "h")
- createCells( columnsToInsert, writer, tableSlot.getPositionBefore() );
+ createdCells.push(
+ ...createCells( columnsToInsert, writer, tableSlot.getPositionBefore() )
+ );
}
}
+
+ return createdCells;
} );
}
@@ -748,14 +762,21 @@ export default class TableUtils extends Plugin {
// @param {Number} insertAt The row index of row insertion.
// @param {Number} rows The number of rows to create.
// @param {Number} tableCellToInsert The number of cells to insert in each row.
+// @returns {Array.} Created table cells.
function createEmptyRows( writer, table, insertAt, rows, tableCellToInsert, attributes = {} ) {
+ const createdCells = [];
+
for ( let i = 0; i < rows; i++ ) {
const tableRow = writer.createElement( 'tableRow' );
writer.insert( tableRow, table, insertAt );
- createCells( tableCellToInsert, writer, writer.createPositionAt( tableRow, 'end' ), attributes );
+ createdCells.push(
+ ...createCells( tableCellToInsert, writer, writer.createPositionAt( tableRow, 'end' ), attributes )
+ );
}
+
+ return createdCells;
}
// Creates cells at a given position.
@@ -763,10 +784,15 @@ function createEmptyRows( writer, table, insertAt, rows, tableCellToInsert, attr
// @param {Number} columns The number of columns to create
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/position~Position} insertPosition
+// @returns {Array.} Created table cells.
function createCells( cells, writer, insertPosition, attributes = {} ) {
+ const createdCells = [];
+
for ( let i = 0; i < cells; i++ ) {
- createEmptyTableCell( writer, insertPosition, attributes );
+ createdCells.push( createEmptyTableCell( writer, insertPosition, attributes ) );
}
+
+ return createdCells;
}
// Evenly distributes the span of a cell to a number of provided cells.
diff --git a/packages/ckeditor5-table/src/utils/common.js b/packages/ckeditor5-table/src/utils/common.js
index 26ac1a071f5..c304d9cf1d2 100644
--- a/packages/ckeditor5-table/src/utils/common.js
+++ b/packages/ckeditor5-table/src/utils/common.js
@@ -29,7 +29,7 @@ export function updateNumericAttribute( key, value, item, writer, defaultValue =
*
* @param {module:engine/model/writer~Writer} writer The model writer.
* @param {module:engine/model/position~Position} insertPosition The position at which the table cell should be inserted.
- * @param {Object} attributes The element attributes.
+ * @param {Object} [attributes={}] The element attributes.
* @returns {module:engine/model/element~Element} Created table cell.
*/
export function createEmptyTableCell( writer, insertPosition, attributes = {} ) {
diff --git a/packages/ckeditor5-table/tests/_utils/utils.js b/packages/ckeditor5-table/tests/_utils/utils.js
index 36bd57d89db..500935825c1 100644
--- a/packages/ckeditor5-table/tests/_utils/utils.js
+++ b/packages/ckeditor5-table/tests/_utils/utils.js
@@ -32,20 +32,26 @@ const WIDGET_TABLE_CELL_CLASS = 'ck-editor__editable ck-editor__nested-editable'
* };
*
* @param {Array.|Object>} tableData
- * @param {Object} [attributes] Optional table attributes: `headingRows` and `headingColumns`.
+ * @param {Object} [options={}] Optional table attributes. Supports {@link module:table/table~TableConfig#tableProperties}.
+ * @param {Number} options.headingRows Number of heading rows.
+ * @param {Number} options.headingColumns Number of heading columns.
+ * @param {module:table/table~TableConfig#tableCellProperties} options.cellProperties Properties for all created cells.
*
* @returns {String}
*/
-export function modelTable( tableData, attributes ) {
+export function modelTable( tableData, options = {} ) {
const tableRows = makeRows( tableData, {
cellElement: 'tableCell',
rowElement: 'tableRow',
headingElement: 'tableCell',
wrappingElement: 'paragraph',
- enforceWrapping: true
+ enforceWrapping: true,
+ cellProperties: options.cellProperties
} );
- return ``;
+ delete options.cellProperties;
+
+ return ``;
}
/**
@@ -343,7 +349,7 @@ function formatAttributes( attributes ) {
// Formats passed table data to a set of table rows.
function makeRows( tableData, options ) {
- const { cellElement, rowElement, headingElement, wrappingElement, enforceWrapping, asWidget } = options;
+ const { cellElement, rowElement, headingElement, wrappingElement, enforceWrapping, asWidget, cellProperties } = options;
return tableData
.reduce( ( previousRowsString, tableRow ) => {
@@ -367,7 +373,8 @@ function makeRows( tableData, options ) {
delete tableCellData.isSelected;
}
- let attributes = {};
+ // Copy the default properties to avoid modifying references.
+ let attributes = Object.assign( {}, cellProperties );
if ( asWidget ) {
attributes.class = getClassToSet( attributes );
diff --git a/packages/ckeditor5-table/tests/commands/insertcolumncommand.js b/packages/ckeditor5-table/tests/commands/insertcolumncommand.js
index 8e25b0cd8ea..a043dcc6662 100644
--- a/packages/ckeditor5-table/tests/commands/insertcolumncommand.js
+++ b/packages/ckeditor5-table/tests/commands/insertcolumncommand.js
@@ -14,6 +14,7 @@ import TableEditing from '../../src/tableediting';
import { assertSelectedCells, modelTable } from '../_utils/utils';
import InsertColumnCommand from '../../src/commands/insertcolumncommand';
+import { UndoEditing } from '@ckeditor/ckeditor5-undo';
describe( 'InsertColumnCommand', () => {
let editor, model, command;
@@ -21,7 +22,7 @@ describe( 'InsertColumnCommand', () => {
beforeEach( () => {
return ModelTestEditor
.create( {
- plugins: [ Paragraph, TableEditing, TableSelection, HorizontalLineEditing ]
+ plugins: [ Paragraph, TableEditing, TableSelection, HorizontalLineEditing, UndoEditing ]
} )
.then( newEditor => {
editor = newEditor;
@@ -213,6 +214,43 @@ describe( 'InsertColumnCommand', () => {
[ '31', '', '' ]
] ) );
} );
+
+ it( 'should create a single undo step when inserting a column', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const tableUtils = editor.plugins.get( 'TableUtils' );
+ const spy = sinon.spy();
+
+ tableUtils.on( 'insertColumns', () => {
+ model.change( writer => {
+ const table = model.document.getRoot().getNodeByPath( [ 0 ] );
+ const paragraph = writer.createElement( 'paragraph' );
+
+ model.insertContent( paragraph, writer.createPositionBefore( table ) );
+
+ assertEqualMarkup( getData( model ), '' + modelTable( [
+ [ '11[]', '', '12' ],
+ [ '21', '', '22' ]
+ ] ) );
+ } );
+
+ spy();
+ }, { priority: 'low' } );
+
+ command.execute();
+
+ expect( spy.calledOnce ).to.equal( true );
+
+ editor.execute( 'undo' );
+
+ assertEqualMarkup( getData( model ), modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+ } );
} );
} );
diff --git a/packages/ckeditor5-table/tests/commands/insertrowcommand.js b/packages/ckeditor5-table/tests/commands/insertrowcommand.js
index c7c8b3c442b..ad470858f61 100644
--- a/packages/ckeditor5-table/tests/commands/insertrowcommand.js
+++ b/packages/ckeditor5-table/tests/commands/insertrowcommand.js
@@ -14,6 +14,7 @@ import TableSelection from '../../src/tableselection';
import { assertSelectedCells, modelTable } from '../_utils/utils';
import InsertRowCommand from '../../src/commands/insertrowcommand';
+import { UndoEditing } from '@ckeditor/ckeditor5-undo';
describe( 'InsertRowCommand', () => {
let editor, model, command;
@@ -21,7 +22,7 @@ describe( 'InsertRowCommand', () => {
beforeEach( () => {
return ModelTestEditor
.create( {
- plugins: [ Paragraph, TableEditing, TableSelection, HorizontalLineEditing ]
+ plugins: [ Paragraph, TableEditing, TableSelection, HorizontalLineEditing, UndoEditing ]
} )
.then( newEditor => {
editor = newEditor;
@@ -308,6 +309,44 @@ describe( 'InsertRowCommand', () => {
[ '10', '11', '12' ]
] ) );
} );
+
+ it( 'should create a single undo step when inserting a column', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const tableUtils = editor.plugins.get( 'TableUtils' );
+ const spy = sinon.spy();
+
+ tableUtils.on( 'insertRows', () => {
+ model.change( writer => {
+ const table = model.document.getRoot().getNodeByPath( [ 0 ] );
+ const paragraph = writer.createElement( 'paragraph' );
+
+ model.insertContent( paragraph, writer.createPositionBefore( table ) );
+
+ assertEqualMarkup( getData( model ), '' + modelTable( [
+ [ '11[]', '12' ],
+ [ '', '' ],
+ [ '21', '22' ]
+ ] ) );
+ } );
+
+ spy();
+ }, { priority: 'low' } );
+
+ command.execute();
+
+ expect( spy.calledOnce ).to.equal( true );
+
+ editor.execute( 'undo' );
+
+ assertEqualMarkup( getData( model ), modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+ } );
} );
} );
diff --git a/packages/ckeditor5-table/tests/converters/table-cell-default-properties-post-fixer.js b/packages/ckeditor5-table/tests/converters/table-cell-default-properties-post-fixer.js
new file mode 100644
index 00000000000..e049e1c6e71
--- /dev/null
+++ b/packages/ckeditor5-table/tests/converters/table-cell-default-properties-post-fixer.js
@@ -0,0 +1,245 @@
+/**
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import { getData as getModelData, parse, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+import TableEditing from '../../src/tableediting';
+import { modelTable } from './../_utils/utils';
+import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
+import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import TableCellPropertiesEditing from '../../src/tablecellproperties/tablecellpropertiesediting';
+
+describe( 'Table cell default properties post-fixer', () => {
+ let editor, model, root;
+
+ const defaultProperties = {
+ borderStyle: 'solid',
+ borderWidth: '2px',
+ borderColor: '#f00',
+ horizontalAlignment: 'right',
+ verticalAlignment: 'bottom'
+ };
+
+ beforeEach( () => {
+ return VirtualTestEditor
+ .create( {
+ plugins: [ TableEditing, Paragraph, UndoEditing, TableCellPropertiesEditing ],
+ table: {
+ tableCellProperties: {
+ defaultProperties
+ }
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+ model = editor.model;
+ root = model.document.getRoot();
+ } );
+ } );
+
+ afterEach( () => {
+ editor.destroy();
+ } );
+
+ describe( 'on collaboration', () => {
+ it( 'should add missing cells to columns (remove column vs insert row)', () => {
+ _testExternal(
+ modelTable( [
+ [ '00[]', '01' ],
+ [ '10', '11' ]
+ ], { cellProperties: defaultProperties } ),
+ writer => _removeColumn( writer, 1, [ 0, 1 ] ),
+ writer => _insertRow( writer, 1, [ 'a', 'b' ] ),
+ // Table should have added empty cells.
+ modelTable( [
+ [ '00', '' ],
+ [ 'a', 'b' ],
+ [ '10', '' ]
+ ], { cellProperties: defaultProperties } ),
+ // Table will have empty column after undo.
+ modelTable( [
+ [ '00', '01', '' ],
+ [ 'a', 'b', '' ],
+ [ '10', '11', '' ]
+ ], { cellProperties: defaultProperties } ) );
+ } );
+
+ it( 'should add missing cells to columns (insert row vs remove column)', () => {
+ _testExternal(
+ modelTable( [
+ [ '00[]', '01' ],
+ [ '10', '11' ]
+ ], { cellProperties: defaultProperties } ),
+ writer => _insertRow( writer, 1, [ 'a', 'b' ] ),
+ writer => _removeColumn( writer, 1, [ 0, 2 ] ),
+ // There should be empty cells added.
+ modelTable( [
+ [ '00', '' ],
+ [ 'a', 'b' ],
+ [ '10', '' ]
+ ], { cellProperties: defaultProperties } ),
+ // Table will have empty column after undo.
+ modelTable( [
+ [ '00', '' ],
+ [ '10', '' ]
+ ], { cellProperties: defaultProperties } ) );
+ } );
+
+ it( 'should add empty cell to an added row (insert row vs insert column)', () => {
+ _testExternal(
+ modelTable( [
+ [ '00[]', '01' ],
+ [ '10', '11' ]
+ ], { cellProperties: defaultProperties } ),
+ writer => _insertRow( writer, 1, [ 'a', 'b' ] ),
+ writer => _insertColumn( writer, 1, [ 0, 2 ] ),
+ // There should be empty cells added.
+ modelTable( [
+ [ '00', '', '01' ],
+ [ 'a', 'b', '' ],
+ [ '10', '', '11' ]
+ ], { cellProperties: defaultProperties } ),
+ // Table will have empty column after undo.
+ modelTable( [
+ [ '00', '', '01' ],
+ [ '10', '', '11' ]
+ ], { cellProperties: defaultProperties } ) );
+ } );
+
+ it( 'should add empty cell to an added row (insert column vs insert row)', () => {
+ _testExternal(
+ modelTable( [
+ [ '00[]', '01' ],
+ [ '10', '11' ]
+ ], { cellProperties: defaultProperties } ),
+ writer => _insertColumn( writer, 1, [ 0, 1 ] ),
+ writer => _insertRow( writer, 1, [ 'a', 'b' ] ),
+ // There should be empty cells added.
+ modelTable( [
+ [ '00', '', '01' ],
+ [ 'a', 'b', '' ],
+ [ '10', '', '11' ]
+ ], { cellProperties: defaultProperties } ),
+ // Table will have empty column after undo.
+ modelTable( [
+ [ '00', '01', '' ],
+ [ 'a', 'b', '' ],
+ [ '10', '11', '' ]
+ ], { cellProperties: defaultProperties } ) );
+ } );
+
+ it( 'should add empty cell when inserting column over a colspanned cell (insert column vs insert column)', () => {
+ _testExternal(
+ modelTable( [
+ [ { colspan: 3, contents: '00' } ],
+ [ '10', '11', '12' ]
+ ], { cellProperties: defaultProperties } ),
+ writer => {
+ _setAttribute( writer, 'colspan', 4, [ 0, 0, 0 ] );
+ _insertColumn( writer, 2, [ 1 ] );
+ },
+ writer => {
+ _setAttribute( writer, 'colspan', 4, [ 0, 0, 0 ] );
+ _insertColumn( writer, 1, [ 1 ] );
+ },
+ // There should be empty cells added.
+ modelTable( [
+ [ { colspan: 4, contents: '00' }, '' ],
+ [ '10', '', '11', '', '12' ]
+ ], { cellProperties: defaultProperties } ),
+ // Table will have empty column after undo.
+ modelTable( [
+ [ { colspan: 3, contents: '00' }, '' ],
+ [ '10', '', '11', '12' ]
+ ], { cellProperties: defaultProperties } ) );
+ } );
+
+ it( 'should add empty cell when inserting column over a colspanned cell (insert column vs insert column) - inverted', () => {
+ _testExternal(
+ modelTable( [
+ [ { colspan: 3, contents: '00' } ],
+ [ '10', '11', '12' ]
+ ], { cellProperties: defaultProperties } ),
+ writer => {
+ _setAttribute( writer, 'colspan', 4, [ 0, 0, 0 ] );
+ _insertColumn( writer, 1, [ 1 ] );
+ },
+ writer => {
+ _setAttribute( writer, 'colspan', 4, [ 0, 0, 0 ] );
+ _insertColumn( writer, 3, [ 1 ] );
+ },
+ // There should be empty cells added.
+ modelTable( [
+ [ { colspan: 4, contents: '00' }, '' ],
+ [ '10', '', '11', '', '12' ]
+ ], { cellProperties: defaultProperties } ),
+ // Table will have empty column after undo.
+ modelTable( [
+ [ { colspan: 3, contents: '00' }, '' ],
+ [ '10', '11', '', '12' ]
+ ], { cellProperties: defaultProperties } ) );
+ } );
+
+ function _testExternal( initialData, localCallback, externalCallback, modelAfter, modelAfterUndo ) {
+ setModelData( model, initialData );
+
+ model.change( localCallback );
+
+ model.enqueueChange( 'transparent', externalCallback );
+
+ assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelAfter );
+
+ editor.execute( 'undo' );
+
+ assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelAfterUndo );
+
+ editor.execute( 'redo' );
+
+ assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelAfter );
+ }
+
+ function _removeColumn( writer, columnIndex, rows ) {
+ const table = root.getChild( 0 );
+
+ for ( const index of rows ) {
+ const tableRow = table.getChild( index );
+ const tableCell = tableRow.getChild( columnIndex );
+
+ writer.remove( tableCell );
+ }
+ }
+
+ function _insertRow( writer, rowIndex, rowData ) {
+ const table = root.getChild( 0 );
+
+ const parsedTable = parse(
+ modelTable( [ rowData ] ),
+ model.schema
+ );
+
+ writer.insert( parsedTable.getChild( 0 ), table, rowIndex );
+ }
+
+ function _setAttribute( writer, attributeKey, attributeValue, path ) {
+ const node = root.getNodeByPath( path );
+
+ writer.setAttribute( attributeKey, attributeValue, node );
+ }
+
+ function _insertColumn( writer, columnIndex, rows ) {
+ const table = root.getChild( 0 );
+
+ for ( const index of rows ) {
+ const tableRow = table.getChild( index );
+
+ const tableCell = writer.createElement( 'tableCell' );
+ writer.insert( tableCell, tableRow, columnIndex );
+ writer.insertElement( 'paragraph', tableCell );
+ }
+ }
+ } );
+} );
diff --git a/packages/ckeditor5-table/tests/manual/tableproperties.js b/packages/ckeditor5-table/tests/manual/tableproperties.js
index 79b0d202ab5..fd5464f5eb5 100644
--- a/packages/ckeditor5-table/tests/manual/tableproperties.js
+++ b/packages/ckeditor5-table/tests/manual/tableproperties.js
@@ -27,7 +27,24 @@ ClassicEditor
],
table: {
contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties' ],
- tableToolbar: [ 'bold', 'italic' ]
+ tableToolbar: [ 'bold', 'italic' ],
+ tableProperties: {
+ defaultProperties: {
+ borderStyle: 'dashed',
+ borderColor: 'hsl(0, 0%, 60%)',
+ borderWidth: '3px',
+ alignment: 'left'
+ }
+ },
+ tableCellProperties: {
+ defaultProperties: {
+ borderStyle: 'dotted',
+ borderColor: 'hsl(120, 75%, 60%)',
+ borderWidth: '2px',
+ horizontalAlignment: 'right',
+ verticalAlignment: 'bottom'
+ }
+ }
}
} )
.then( editor => {
@@ -36,3 +53,4 @@ ClassicEditor
.catch( err => {
console.error( err.stack );
} );
+
diff --git a/packages/ckeditor5-table/tests/manual/tableproperties.md b/packages/ckeditor5-table/tests/manual/tableproperties.md
index fb5ab40b322..d19f0fd61b9 100644
--- a/packages/ckeditor5-table/tests/manual/tableproperties.md
+++ b/packages/ckeditor5-table/tests/manual/tableproperties.md
@@ -10,3 +10,36 @@ The editor should be loaded with tables:
Compare their visual styles with the ones beside the editor. Most of the styles should be preserved.
**Note**: Not all styles are preserved (i.e. the column height/width in the second GDocs table). Also note that the original table might have a different cell padding.
+
+After all, clear the editor before starting checking the default table and cell properties.
+
+### Default table properties
+
+After inserting a new table, the default styles (printed below) should be applied to the table automatically:
+
+```json
+{
+ "borderStyle": "dashed",
+ "borderColor": "hsl(0, 0%, 60%)",
+ "borderWidth": "3px",
+ "alignment": "left"
+}
+```
+
+Use the `Table properties` icon in the table toolbar and compare values.
+
+### Default cells properties
+
+After inserting a new table, the default styles (printed below) should be applied to all cells automatically:
+
+```json
+{
+ "borderStyle": "dotted",
+ "borderColor": "hsl(120, 75%, 60%)",
+ "borderWidth": "2px",
+ "horizontalAlignment": "right",
+ "verticalAlignment": "bottom"
+}
+```
+
+Use the `Cell properties` icon in the table toolbar and compare values.
diff --git a/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesediting.js b/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesediting.js
index 24fd7657336..c4a589528c7 100644
--- a/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesediting.js
+++ b/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesediting.js
@@ -19,9 +19,11 @@ import TableCellVerticalAlignmentCommand from '../../src/tablecellproperties/com
import TableCellPaddingCommand from '../../src/tablecellproperties/commands/tablecellpaddingcommand';
import TableCellBackgroundColorCommand from '../../src/tablecellproperties/commands/tablecellbackgroundcolorcommand';
-import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
-import { assertTableCellStyle, assertTRBLAttribute } from '../_utils/utils';
+import { assertTableCellStyle, assertTRBLAttribute, modelTable } from '../_utils/utils';
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import TableUtils from '../../src/tableutils';
describe( 'table cell properties', () => {
describe( 'TableCellPropertiesEditing', () => {
@@ -43,6 +45,10 @@ describe( 'table cell properties', () => {
expect( TableCellPropertiesEditing.pluginName ).to.equal( 'TableCellPropertiesEditing' );
} );
+ it( 'should require TableUtils', () => {
+ expect( TableCellPropertiesEditing.requires ).to.include( TableUtils );
+ } );
+
it( 'adds tableCellBorderColor command', () => {
expect( editor.commands.get( 'tableCellBorderColor' ) ).to.be.instanceOf( TableCellBorderColorCommand );
} );
@@ -79,6 +85,18 @@ describe( 'table cell properties', () => {
expect( editor.commands.get( 'tableCellHeight' ) ).to.be.instanceOf( TableCellHeightCommand );
} );
+ describe( 'config', () => {
+ let tableCellProperties;
+
+ beforeEach( () => {
+ tableCellProperties = editor.config.get( 'table.tableCellProperties' );
+ } );
+
+ it( 'should define the default properties for a table', () => {
+ expect( tableCellProperties.defaultProperties ).to.deep.equal( {} );
+ } );
+ } );
+
describe( 'border', () => {
it( 'should set proper schema rules', () => {
expect( model.schema.checkAttribute( [ '$root', 'tableCell' ], 'borderColor' ) ).to.be.true;
@@ -1244,5 +1262,99 @@ describe( 'table cell properties', () => {
} );
} );
} );
+
+ describe( 'default cell properties', () => {
+ let editor, model;
+
+ const defaultProperties = {
+ borderStyle: 'solid',
+ borderWidth: '2px',
+ borderColor: '#f00',
+ horizontalAlignment: 'right',
+ verticalAlignment: 'bottom'
+ };
+
+ beforeEach( () => {
+ return ModelTestEditor
+ .create( {
+ plugins: [ Paragraph, TableCellPropertiesEditing ],
+ table: {
+ tableCellProperties: {
+ defaultProperties
+ }
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+ model = editor.model;
+ setModelData( model, '[]' );
+ } );
+ } );
+
+ afterEach( () => {
+ return editor.destroy();
+ } );
+
+ it( 'should create a table and all cells should have applied the default cell properties', () => {
+ editor.execute( 'insertTable' );
+
+ assertEqualMarkup( getModelData( model ),
+ modelTable( [
+ [ '[]', '' ],
+ [ '', '' ]
+ ], { cellProperties: defaultProperties } )
+ );
+ } );
+
+ it( 'should apply default cell properties when inserting a new column (command=insertTableColumnRight)', () => {
+ editor.execute( 'insertTable' );
+ editor.execute( 'insertTableColumnRight' );
+
+ assertEqualMarkup( getModelData( model ),
+ modelTable( [
+ [ '[]', '', '' ],
+ [ '', '', '' ]
+ ], { cellProperties: defaultProperties } )
+ );
+ } );
+
+ it( 'should apply default cell properties when inserting a new column (command=insertTableColumnLeft)', () => {
+ editor.execute( 'insertTable' );
+ editor.execute( 'insertTableColumnLeft' );
+
+ assertEqualMarkup( getModelData( model ),
+ modelTable( [
+ [ '', '[]', '' ],
+ [ '', '', '' ]
+ ], { cellProperties: defaultProperties } )
+ );
+ } );
+
+ it( 'should apply default cell properties when inserting a new row (command=insertTableRowAbove)', () => {
+ editor.execute( 'insertTable' );
+ editor.execute( 'insertTableRowAbove' );
+
+ assertEqualMarkup( getModelData( model ),
+ modelTable( [
+ [ '', '' ],
+ [ '[]', '' ],
+ [ '', '' ]
+ ], { cellProperties: defaultProperties } )
+ );
+ } );
+
+ it( 'should apply default cell properties when inserting a new row (command=insertTableRowBelow)', () => {
+ editor.execute( 'insertTable' );
+ editor.execute( 'insertTableRowBelow' );
+
+ assertEqualMarkup( getModelData( model ),
+ modelTable( [
+ [ '[]', '' ],
+ [ '', '' ],
+ [ '', '' ]
+ ], { cellProperties: defaultProperties } )
+ );
+ } );
+ } );
} );
} );
diff --git a/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesui.js b/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesui.js
index f5538646802..b95be8a5d0d 100644
--- a/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesui.js
+++ b/packages/ckeditor5-table/tests/tablecellproperties/tablecellpropertiesui.js
@@ -74,7 +74,8 @@ describe( 'table cell properties', () => {
it( 'should define table.tableCellProperties config', () => {
expect( editor.config.get( 'table.tableCellProperties' ) ).to.deep.equal( {
borderColors: defaultColors,
- backgroundColors: defaultColors
+ backgroundColors: defaultColors,
+ defaultProperties: {}
} );
} );
} );
diff --git a/packages/ckeditor5-table/tests/tableproperties/tablepropertiesediting.js b/packages/ckeditor5-table/tests/tableproperties/tablepropertiesediting.js
index 98646113738..445e65c6ffd 100644
--- a/packages/ckeditor5-table/tests/tableproperties/tablepropertiesediting.js
+++ b/packages/ckeditor5-table/tests/tableproperties/tablepropertiesediting.js
@@ -19,7 +19,9 @@ import TableBackgroundColorCommand from '../../src/tableproperties/commands/tabl
import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
-import { assertTableStyle, assertTRBLAttribute } from '../_utils/utils';
+import { assertTableStyle, assertTRBLAttribute, modelTable } from '../_utils/utils';
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import TableUtils from '../../src/tableutils';
describe( 'table properties', () => {
describe( 'TablePropertiesEditing', () => {
@@ -45,6 +47,10 @@ describe( 'table properties', () => {
expect( TablePropertiesEditing.pluginName ).to.equal( 'TablePropertiesEditing' );
} );
+ it( 'should require TableUtils', () => {
+ expect( TablePropertiesEditing.requires ).to.include( TableUtils );
+ } );
+
it( 'adds tableBorderColor command', () => {
expect( editor.commands.get( 'tableBorderColor' ) ).to.be.instanceOf( TableBorderColorCommand );
} );
@@ -73,6 +79,18 @@ describe( 'table properties', () => {
expect( editor.commands.get( 'tableBackgroundColor' ) ).to.be.instanceOf( TableBackgroundColorCommand );
} );
+ describe( 'config', () => {
+ let tableProperties;
+
+ beforeEach( () => {
+ tableProperties = editor.config.get( 'table.tableProperties' );
+ } );
+
+ it( 'should define the default properties for a table', () => {
+ expect( tableProperties.defaultProperties ).to.deep.equal( {} );
+ } );
+ } );
+
describe( 'border', () => {
it( 'should set proper schema rules', () => {
expect( model.schema.checkAttribute( [ '$root', 'table' ], 'borderColor' ) ).to.be.true;
@@ -1238,15 +1256,58 @@ describe( 'table properties', () => {
} );
} );
+ describe( 'default table properties', () => {
+ let editor, model;
+
+ const defaultProperties = {
+ borderStyle: 'solid',
+ borderWidth: '2px',
+ borderColor: '#f00',
+ alignment: 'right'
+ };
+
+ beforeEach( () => {
+ return ModelTestEditor
+ .create( {
+ plugins: [ Paragraph, TablePropertiesEditing ],
+ table: {
+ tableProperties: {
+ defaultProperties
+ }
+ }
+ } )
+ .then( newEditor => {
+ editor = newEditor;
+ model = editor.model;
+ setModelData( model, '[]' );
+ } );
+ } );
+
+ afterEach( () => {
+ return editor.destroy();
+ } );
+
+ it( 'should create a table with applied the default properties', () => {
+ editor.execute( 'insertTable' );
+
+ assertEqualMarkup( getModelData( model ),
+ modelTable( [
+ [ '[]', '' ],
+ [ '', '' ]
+ ], { ...defaultProperties } )
+ );
+ } );
+ } );
+
function createEmptyTable() {
setModelData(
model,
'' +
- '' +
- '' +
- 'foo' +
- '' +
- '' +
+ '' +
+ '' +
+ 'foo' +
+ '' +
+ '' +
'
'
);
diff --git a/packages/ckeditor5-table/tests/tableproperties/tablepropertiesui.js b/packages/ckeditor5-table/tests/tableproperties/tablepropertiesui.js
index e0129f80e63..57e88ac03ed 100644
--- a/packages/ckeditor5-table/tests/tableproperties/tablepropertiesui.js
+++ b/packages/ckeditor5-table/tests/tableproperties/tablepropertiesui.js
@@ -73,7 +73,8 @@ describe( 'table properties', () => {
it( 'should define table.tableProperties config', () => {
expect( editor.config.get( 'table.tableProperties' ) ).to.deep.equal( {
borderColors: defaultColors,
- backgroundColors: defaultColors
+ backgroundColors: defaultColors,
+ defaultProperties: {}
} );
} );
} );
diff --git a/packages/ckeditor5-table/tests/tableutils.js b/packages/ckeditor5-table/tests/tableutils.js
index cfc03d379fa..693eaa1262f 100644
--- a/packages/ckeditor5-table/tests/tableutils.js
+++ b/packages/ckeditor5-table/tests/tableutils.js
@@ -275,6 +275,47 @@ describe( 'TableUtils', () => {
] ) );
} );
+ it( 'should return created tableCell elements (insert in the middle)', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const insertedCells = tableUtils.insertRows( root.getNodeByPath( [ 0 ] ), { at: 1 } );
+
+ expect( insertedCells.length ).to.equal( 2 );
+ expect( root.getNodeByPath( [ 0, 1, 0 ] ) ).to.equal( insertedCells[ 0 ] );
+ expect( root.getNodeByPath( [ 0, 1, 1 ] ) ).to.equal( insertedCells[ 1 ] );
+ } );
+
+ it( 'should return created tableCell elements (insert in the the beginning)', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const insertedCells = tableUtils.insertRows( root.getNodeByPath( [ 0 ] ), { rows: 2 } );
+
+ expect( insertedCells.length ).to.equal( 4 );
+ expect( root.getNodeByPath( [ 0, 1, 1 ] ) ).to.equal( insertedCells[ 0 ] );
+ expect( root.getNodeByPath( [ 0, 1, 0 ] ) ).to.equal( insertedCells[ 1 ] );
+ expect( root.getNodeByPath( [ 0, 0, 1 ] ) ).to.equal( insertedCells[ 2 ] );
+ expect( root.getNodeByPath( [ 0, 0, 0 ] ) ).to.equal( insertedCells[ 3 ] );
+ } );
+
+ it( 'should return created tableCell elements (insert in the the end)', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const insertedCells = tableUtils.insertRows( root.getNodeByPath( [ 0 ] ), { at: 2 } );
+
+ expect( insertedCells.length ).to.equal( 2 );
+ expect( root.getNodeByPath( [ 0, 2, 1 ] ) ).to.equal( insertedCells[ 0 ] );
+ expect( root.getNodeByPath( [ 0, 2, 0 ] ) ).to.equal( insertedCells[ 1 ] );
+ } );
+
describe( 'with copyStructureFrom enabled', () => {
beforeEach( () => {
// +----+----+----+----+----+----+
@@ -597,6 +638,47 @@ describe( 'TableUtils', () => {
[ '', '32' ]
], { headingColumns: 3 } ) );
} );
+
+ it( 'should return created tableCell elements (insert in the middle)', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const insertedCells = tableUtils.insertColumns( root.getNodeByPath( [ 0 ] ), { at: 1 } );
+
+ expect( insertedCells.length ).to.equal( 2 );
+ expect( root.getNodeByPath( [ 0, 0, 1 ] ) ).to.equal( insertedCells[ 0 ] );
+ expect( root.getNodeByPath( [ 0, 1, 1 ] ) ).to.equal( insertedCells[ 1 ] );
+ } );
+
+ it( 'should return created tableCell elements (insert in the the beginning)', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const insertedCells = tableUtils.insertColumns( root.getNodeByPath( [ 0 ] ), { columns: 2 } );
+
+ expect( insertedCells.length ).to.equal( 4 );
+ expect( root.getNodeByPath( [ 0, 0, 1 ] ) ).to.equal( insertedCells[ 0 ] );
+ expect( root.getNodeByPath( [ 0, 0, 0 ] ) ).to.equal( insertedCells[ 1 ] );
+ expect( root.getNodeByPath( [ 0, 1, 1 ] ) ).to.equal( insertedCells[ 2 ] );
+ expect( root.getNodeByPath( [ 0, 1, 0 ] ) ).to.equal( insertedCells[ 3 ] );
+ } );
+
+ it( 'should return created tableCell elements (insert in the the end)', () => {
+ setData( model, modelTable( [
+ [ '11[]', '12' ],
+ [ '21', '22' ]
+ ] ) );
+
+ const insertedCells = tableUtils.insertColumns( root.getNodeByPath( [ 0 ] ), { columns: 1, at: 2 } );
+
+ expect( insertedCells.length ).to.equal( 2 );
+ expect( root.getNodeByPath( [ 0, 0, 2 ] ) ).to.equal( insertedCells[ 0 ] );
+ expect( root.getNodeByPath( [ 0, 1, 2 ] ) ).to.equal( insertedCells[ 1 ] );
+ } );
} );
describe( 'splitCellVertically()', () => {
@@ -1572,6 +1654,25 @@ describe( 'TableUtils', () => {
} );
describe( 'createTable()', () => {
+ it( 'should be decorated', () => {
+ const spy = sinon.spy();
+
+ setData( model, '[]' );
+
+ tableUtils.on( 'createTable', spy );
+
+ model.change( writer => {
+ const table = tableUtils.createTable( writer, {
+ rows: 2,
+ columns: 2
+ } );
+
+ model.insertContent( table );
+ } );
+
+ expect( spy.calledOnce ).to.be.true;
+ } );
+
it( 'should create table', () => {
setData( model, '[]' );