diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js
index c5ae7abc294..eb5a287519e 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js
@@ -167,6 +167,16 @@ export const CLIPBOARD_WHITELISTS = {
}
};
+// Commands that don't require a DOM selection but take an argument instead.
+const SELECTIONLESS_COMMANDS = [
+ 'addRowAbove',
+ 'addRowBelow',
+ 'addColumnLeft',
+ 'addColumnRight',
+ 'removeRow',
+ 'removeColumn',
+];
+
function defaultOptions(defaultObject, object) {
const newObject = Object.assign({}, defaultObject, object);
for (const [key, value] of Object.entries(object)) {
@@ -316,7 +326,11 @@ export class OdooEditor extends EventTarget {
this._pluginCall('sanitizeElement', [editable]);
- // Create the Powerbox's table picker.
+ // ------
+ // Tables
+ // ------
+
+ // Create the table picker for the Powerbox.
this.powerboxTablePicker = new TablePicker({
document: this.document,
floating: true,
@@ -328,7 +342,78 @@ export class OdooEditor extends EventTarget {
colNumber: ev.detail.colNumber,
});
});
- // Initialize the Powerbox.
+ // Create the table picker for the toolbar.
+ this.toolbarTablePicker = new TablePicker({ document: this.document });
+ this.toolbarTablePicker.addEventListener('cell-selected', ev => {
+ this.execCommand('insertTable', {
+ rowNumber: ev.detail.rowNumber,
+ colNumber: ev.detail.colNumber,
+ });
+ });
+ // Create the table UI.
+ const parser = new DOMParser();
+ for (const direction of ['row', 'column']) {
+ // Create the containers and the menu toggler.
+ const ui = parser.parseFromString(`
`, 'text/html').body.firstElementChild;
+ const uiMenu = ui.querySelector('.o_table_ui_menu');
+
+ // Create the move buttons.
+ if (direction === 'column') {
+ uiMenu.append(...parser.parseFromString(`
+ Move left
+ Move right
+ `, 'text/html').body.children);
+ this.addDomListener(uiMenu.querySelector('.o_move_left'), 'click', this._onTableMoveLeftClick);
+ this.addDomListener(uiMenu.querySelector('.o_move_right'), 'click', this._onTableMoveRightClick);
+ } else {
+ uiMenu.append(...parser.parseFromString(`
+ Move up
+ Move down
+ `, 'text/html').body.children);
+ this.addDomListener(uiMenu.querySelector('.o_move_up'), 'click', this._onTableMoveUpClick);
+ this.addDomListener(uiMenu.querySelector('.o_move_down'), 'click', this._onTableMoveDownClick);
+ }
+
+ // Create the add buttons.
+ if (direction === 'column') {
+ uiMenu.append(...parser.parseFromString(`
+ Insert left
+ Insert right
+ `, 'text/html').body.children);
+ this.addDomListener(uiMenu.querySelector('.o_insert_left'), 'click', () => this.execCommand('addColumnLeft', this._columnUiTarget));
+ this.addDomListener(uiMenu.querySelector('.o_insert_right'), 'click', () => this.execCommand('addColumnRight', this._columnUiTarget));
+ } else {
+ uiMenu.append(...parser.parseFromString(`
+ Insert above
+ Insert below
+ `, 'text/html').body.children);
+ this.addDomListener(uiMenu.querySelector('.o_insert_above'), 'click', () => this.execCommand('addRowAbove', this._rowUiTarget));
+ this.addDomListener(uiMenu.querySelector('.o_insert_below'), 'click', () => this.execCommand('addRowBelow', this._rowUiTarget));
+ }
+
+ // Add the delete button.
+ if (direction === 'column') {
+ uiMenu.append(parser.parseFromString(`Delete
`, 'text/html').body.firstChild)
+ this.addDomListener(uiMenu.querySelector('.o_delete_column'), 'click', this._onTableDeleteColumnClick);
+ } else {
+ uiMenu.append(parser.parseFromString(`Delete
`, 'text/html').body.firstChild)
+ this.addDomListener(uiMenu.querySelector('.o_delete_row'), 'click', this._onTableDeleteRowClick);
+ }
+
+ this[`_${direction}Ui`] = ui;
+ this.document.body.append(ui);
+ this.addDomListener(ui.querySelector('.o_table_ui_menu_toggler'), 'click', this._onTableMenuTogglerClick);
+ }
+
+ // --------
+ // Powerbox
+ // --------
+
let beforeStepIndex;
this.powerbox = new Powerbox({
editable: this.editable,
@@ -493,14 +578,6 @@ export class OdooEditor extends EventTarget {
],
});
- this.toolbarTablePicker = new TablePicker({ document: this.document });
- this.toolbarTablePicker.addEventListener('cell-selected', ev => {
- this.execCommand('insertTable', {
- rowNumber: ev.detail.rowNumber,
- colNumber: ev.detail.colNumber,
- });
- });
-
// -----------
// Bind events
// -----------
@@ -522,6 +599,7 @@ export class OdooEditor extends EventTarget {
this.addDomListener(this.document, 'mousedown', this._onDocumentMousedown);
this.addDomListener(this.document, 'mouseup', this._onDocumentMouseup);
this.addDomListener(this.document, 'mousemove', this._onDocumentMousemove);
+ this.addDomListener(this.document, 'click', this._onDocumentClick);
this.multiselectionRefresh = this.multiselectionRefresh.bind(this);
this._resizeObserver = new ResizeObserver(this.multiselectionRefresh);
@@ -593,6 +671,9 @@ export class OdooEditor extends EventTarget {
clearInterval(this._snapshotInterval);
this._pluginCall('destroy', []);
this.isDestroyed = true;
+ // Remove table UI
+ this._rowUi.remove();
+ this._columnUi.remove();
}
sanitize() {
@@ -1764,8 +1845,10 @@ export class OdooEditor extends EventTarget {
_applyRawCommand(method, ...args) {
const sel = this.document.getSelection();
if (
- !this.editable.contains(sel.anchorNode) ||
- (sel.anchorNode !== sel.focusNode && !this.editable.contains(sel.focusNode))
+ !(SELECTIONLESS_COMMANDS.includes(method) && args.length) && (
+ !this.editable.contains(sel.anchorNode) ||
+ (sel.anchorNode !== sel.focusNode && !this.editable.contains(sel.focusNode))
+ )
) {
// Do not apply commands out of the editable area.
return false;
@@ -2154,6 +2237,58 @@ export class OdooEditor extends EventTarget {
}
}
}
+ /**
+ * Show/hide and position the table row/column manipulation UI.
+ *
+ * @private
+ * @param {HTMLTableRowElement} [row=false]
+ * @param {HTMLTableCellElement} [column=false]
+ */
+ _toggleTableUi(row=false, column=false) {
+ if (row) {
+ this._rowUi.style.visibility = 'visible';
+ this._rowUiTarget = row;
+ this._positionTableUi(row);
+ } else {
+ this._rowUi.style.visibility = 'hidden';
+ }
+ if (column) {
+ this._columnUi.style.visibility = 'visible';
+ this._columnUiTarget = column;
+ this._positionTableUi(column);
+ } else {
+ this._columnUi.style.visibility = 'hidden';
+ }
+ }
+ /**
+ * Position the table row/column tools (depending on whether a row or a cell
+ * is passed as argument).
+ *
+ * @private
+ * @param {HTMLTableRowElement|HTMLTableCellElement} element
+ */
+ _positionTableUi(element) {
+ const isRow = element.nodeName === 'TR';
+ const ui = isRow ? this._rowUi : this._columnUi;
+ const elementRect = element.getBoundingClientRect();
+ const tableRect = closestElement(element, 'table').getBoundingClientRect();
+ const wrappedUi = ui.firstElementChild;
+ const togglerRect = ui.querySelector('.o_table_ui_menu_toggler').getBoundingClientRect();
+ const props = {
+ xy: {left: 'x', top: 'y'},
+ size: {left: 'width', top: 'height'}
+ };
+
+ const side1 = isRow ? 'left' : 'top';
+ ui.style[side1] = (isRow ? elementRect : tableRect)[props.xy[side1]] - togglerRect[props.size[side1]] + 'px';
+ wrappedUi.style[side1] = (togglerRect[props.size[side1]] / 2) + 'px';
+ ui.style[props.size[side1]] = togglerRect[props.size[side1]] + 'px';
+
+ const side2 = isRow ? 'top' : 'left';
+ ui.style[side2] = elementRect[props.xy[side2]] + 'px';
+ wrappedUi.style[side2] = (elementRect[props.size[side2]] / 2) - (togglerRect[props.size[side2]] / 2) + 'px';
+ ui.style[props.size[side2]] = elementRect[props.size[side2]] + 'px';
+ }
// HISTORY
// =======
@@ -3514,12 +3649,29 @@ export class OdooEditor extends EventTarget {
if (this._currentMouseState === 'mousedown' && !this._isResizingTable) {
this._handleSelectionInTable(ev);
}
+ if (!this._rowUi.classList.contains('o_open') && !this._columnUi.classList.contains('o_open')) {
+ const column = closestElement(ev.target, 'td');
+ if (this._isResizingTable || !column || !ev.target || ev.target.nodeType !== Node.ELEMENT_NODE) {
+ this._toggleTableUi(false, false);
+ } else {
+ const row = closestElement(column, 'tr');
+ const isFirstColumn = column === row.querySelector('td');
+ const isFirstRow = row === closestElement(column, 'table').querySelector('tr');
+ this._toggleTableUi(isFirstColumn && row, isFirstRow && column);
+ }
+ }
const direction = {top: 'row', right: 'col', bottom: 'row', left: 'col'}[this._isHoveringTdBorder(ev)] || false;
if (direction || !this._isResizingTable) {
this._toggleTableResizeCursor(direction);
}
}
+ _onDocumentClick(ev) {
+ // Close Table UI.
+ this._rowUi.classList.remove('o_open');
+ this._columnUi.classList.remove('o_open');
+ }
+
/**
* Create a Link in the node text based on the given data
*
@@ -3796,6 +3948,70 @@ export class OdooEditor extends EventTarget {
this._onTabulationInTable(ev);
}
}
+ _onTableMenuTogglerClick(ev) {
+ const uiWrapper = closestElement(ev.target, '.o_table_ui');
+ uiWrapper.classList.toggle('o_open');
+ if (uiWrapper.classList.contains('o_column_ui')) {
+ const columnIndex = getColumnIndex(this._columnUiTarget);
+ uiWrapper.querySelector('.o_move_left').classList.toggle('o_hide', columnIndex === 0);
+ const shouldHideRight = columnIndex === [...this._columnUiTarget.parentElement.children].filter(child => child.nodeName === 'TD').length - 1;
+ uiWrapper.querySelector('.o_move_right').classList.toggle('o_hide', shouldHideRight);
+ } else {
+ const rowIndex = getRowIndex(this._rowUiTarget);
+ uiWrapper.querySelector('.o_move_up').classList.toggle('o_hide', rowIndex === 0);
+ const shouldHideDown = rowIndex === [...this._rowUiTarget.parentElement.children].filter(child => child.nodeName === 'TR').length - 1;
+ uiWrapper.querySelector('.o_move_down').classList.toggle('o_hide', shouldHideDown);
+ }
+ ev.stopPropagation();
+ }
+ _onTableMoveUpClick() {
+ if (this._rowUiTarget.previousSibling) {
+ this._rowUiTarget.previousSibling.before(this._rowUiTarget);
+ }
+ }
+ _onTableMoveDownClick() {
+ if (this._rowUiTarget.nextSibling) {
+ this._rowUiTarget.nextSibling.after(this._rowUiTarget);
+ }
+ }
+ _onTableMoveRightClick() {
+ const trs = [...this._columnUiTarget.parentElement.parentElement.children].filter(child => child.nodeName === 'TR');
+ const columnIndex = getColumnIndex(this._columnUiTarget);
+ const tdsToMove = trs.map(tr => [...tr.children].filter(child => child.nodeName === 'TD')[columnIndex]);
+ for (const tdToMove of tdsToMove) {
+ const target = [...tdToMove.parentElement.children].filter(child => child.nodeName === 'TD')[columnIndex + 1];
+ target.after(tdToMove);
+ }
+ }
+ _onTableMoveLeftClick() {
+ const trs = [...this._columnUiTarget.parentElement.parentElement.children].filter(child => child.nodeName === 'TR');
+ const columnIndex = getColumnIndex(this._columnUiTarget);
+ const tdsToMove = trs.map(tr => [...tr.children].filter(child => child.nodeName === 'TD')[columnIndex]);
+ for (const tdToMove of tdsToMove) {
+ const target = [...tdToMove.parentElement.children].filter(child => child.nodeName === 'TD')[columnIndex - 1];
+ target.before(tdToMove);
+ }
+ }
+ _onTableDeleteColumnClick() {
+ this.historyPauseSteps();
+ const rows = [...closestElement(this._columnUiTarget, 'tr').parentElement.children].filter(child => child.nodeName === 'TR');
+ this.execCommand('removeColumn', this._columnUiTarget);
+ if (rows.every(row => !row.parentElement)) {
+ this.execCommand('deleteTable', this.editable.querySelector('.o_selected_table'));
+ }
+ this.historyUnpauseSteps();
+ this.historyStep();
+ }
+ _onTableDeleteRowClick() {
+ this.historyPauseSteps();
+ const rows = [...this._rowUiTarget.parentElement.children].filter(child => child.nodeName === 'TR');
+ this.execCommand('removeRow', this._rowUiTarget);
+ if (rows.every(row => !row.parentElement)) {
+ this.execCommand('deleteTable', this.editable.querySelector('.o_selected_table'));
+ }
+ this.historyUnpauseSteps();
+ this.historyStep();
+ }
/**
* Fix the current selection range in case the range start or end inside a fontAwesome node
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js
index fece7d19fc9..ad847413fa9 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/commands/commands.js
@@ -42,6 +42,9 @@ import {
isUnbreakable,
makeContentsInline,
unwrapContents,
+ getColumnIndex,
+ pxToFloat,
+ getRowIndex,
} from '../utils/utils.js';
const TEXT_CLASSES_REGEX = /\btext-[^\s]*\b/g;
@@ -540,26 +543,72 @@ export function toggleFormat(editor, format) {
}
return changedElements;
}
-function addColumn(editor, beforeOrAfter) {
- getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding td.
- const c = getInSelection(editor.document, 'td');
- if (!c) return;
- const i = [...closestElement(c, 'tr').querySelectorAll('th, td')].findIndex(td => td === c);
- const column = closestElement(c, 'table').querySelectorAll(`tr td:nth-of-type(${i + 1})`);
- column.forEach(row => row[beforeOrAfter](document.createElement('td')));
+function addColumn(editor, beforeOrAfter, referenceCell) {
+ if (!referenceCell) {
+ getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding td.
+ referenceCell = getInSelection(editor.document, 'td');
+ if (!referenceCell) return;
+ }
+ const columnIndex = getColumnIndex(referenceCell);
+ const table = closestElement(referenceCell, 'table');
+ const tableWidth = table.style.width ? pxToFloat(table.style.width) : table.clientWidth;
+ const referenceColumn = table.querySelectorAll(`tr td:nth-of-type(${columnIndex + 1})`);
+ const referenceCellWidth = referenceCell.style.width ? pxToFloat(referenceCell.style.width) : referenceCell.clientWidth;
+ // Temporarily set widths so proportions are respected.
+ const firstRow = table.querySelector('tr');
+ const firstRowCells = [...firstRow.children].filter(child => child.nodeName === 'TD' || child.nodeName === 'TH');
+ let totalWidth = 0;
+ for (const cell of firstRowCells) {
+ const width = cell.style.width ? pxToFloat(cell.style.width) : cell.clientWidth;
+ cell.style.width = width + 'px';
+ // Spread the widths to preserve proportions.
+ // -1 for the width of the border of the new column.
+ const newWidth = Math.max(Math.round((width * tableWidth) / (tableWidth + referenceCellWidth - 1)), 13);
+ cell.style.width = newWidth + 'px';
+ totalWidth += newWidth;
+ }
+ referenceColumn.forEach((cell, rowIndex) => {
+ const newCell = document.createElement('td');
+ newCell.append(document.createElement('br'));
+ cell[beforeOrAfter](newCell);
+ if (rowIndex === 0) {
+ newCell.style.width = cell.style.width;
+ totalWidth += pxToFloat(cell.style.width);
+ }
+ });
+ if (totalWidth !== tableWidth - 1) { // -1 for the width of the border of the new column.
+ firstRowCells[firstRowCells.length - 1].style.width = pxToFloat(firstRowCells[firstRowCells.length - 1].style.width) + (tableWidth - totalWidth - 1) + 'px';
+ }
+ // Fix the table and row's width so it doesn't change.
+ table.style.width = tableWidth + 'px';
}
-function addRow(editor, beforeOrAfter) {
- getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding tr.
- const row = getInSelection(editor.document, 'tr');
- if (!row) return;
+function addRow(editor, beforeOrAfter, referenceRow) {
+ if (!referenceRow) {
+ getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding tr.
+ referenceRow = getInSelection(editor.document, 'tr');
+ if (!referenceRow) return;
+ }
+ const referenceRowHeight = referenceRow.style.height ? pxToFloat(referenceRow.style.height) : referenceRow.clientHeight;
const newRow = document.createElement('tr');
- const cells = row.querySelectorAll('td');
+ newRow.style.height = referenceRowHeight + 'px';
+ const cells = referenceRow.querySelectorAll('td');
+ const referenceRowWidths = [...cells].map(cell => cell.style.width || cell.clientWidth + 'px');
newRow.append(...Array.from(Array(cells.length)).map(() => {
const td = document.createElement('td');
td.append(document.createElement('br'));
return td;
}));
- row[beforeOrAfter](newRow);
+ referenceRow[beforeOrAfter](newRow);
+ newRow.style.height = referenceRowHeight + 'px';
+ // Preserve the width of the columns (applied only on the first row).
+ if (getRowIndex(newRow) === 0) {
+ let columnIndex = 0;
+ for (const column of newRow.children) {
+ column.style.width = referenceRowWidths[columnIndex];
+ cells[columnIndex].style.width = '';
+ columnIndex++;
+ }
+ }
}
function deleteTable(editor, table) {
table = table || getInSelection(editor.document, 'table');
@@ -880,22 +929,24 @@ export const editorCommands = {
const [table] = editorCommands.insertHTML(editor, tableHtml);
setCursorStart(table.querySelector('td'));
},
- addColumnLeft: editor => {
- addColumn(editor, 'before');
+ addColumnLeft: (editor, referenceColumn) => {
+ return addColumn(editor, 'before', referenceColumn);
},
- addColumnRight: editor => {
- addColumn(editor, 'after');
+ addColumnRight: (editor, referenceColumn) => {
+ return addColumn(editor, 'after', referenceColumn);
},
- addRowAbove: editor => {
- addRow(editor, 'before');
+ addRowAbove: (editor, referenceRow) => {
+ return addRow(editor, 'before', referenceRow);
},
- addRowBelow: editor => {
- addRow(editor, 'after');
+ addRowBelow: (editor, referenceRow) => {
+ return addRow(editor, 'after', referenceRow);
},
- removeColumn: editor => {
- getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding td.
- const cell = getInSelection(editor.document, 'td');
- if (!cell) return;
+ removeColumn: (editor, cell) => {
+ if (!cell) {
+ getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding td.
+ cell = getInSelection(editor.document, 'td');
+ if (!cell) return;
+ }
const table = closestElement(cell, 'table');
const cells = [...closestElement(cell, 'tr').querySelectorAll('th, td')];
const index = cells.findIndex(td => td === cell);
@@ -903,10 +954,12 @@ export const editorCommands = {
table.querySelectorAll(`tr td:nth-of-type(${index + 1})`).forEach(td => td.remove());
siblingCell ? setSelection(...startPos(siblingCell)) : deleteTable(editor, table);
},
- removeRow: editor => {
- getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding tr.
- const row = getInSelection(editor.document, 'tr');
- if (!row) return;
+ removeRow: (editor, row) => {
+ if (!row) {
+ getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding tr.
+ row = getInSelection(editor.document, 'tr');
+ if (!row) return;
+ }
const table = closestElement(row, 'table');
const rows = [...table.querySelectorAll('tr')];
const rowIndex = rows.findIndex(tr => tr === row);
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/style.scss b/addons/web_editor/static/src/js/editor/odoo-editor/src/style.scss
index 201ffd2ec44..fe1cccb22c5 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/src/style.scss
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/style.scss
@@ -34,6 +34,58 @@
cursor: pointer !important;
}
}
+.o_table_ui {
+ background-color: transparent;
+ position: absolute;
+ z-index: 10;
+ padding: 0;
+
+ &:hover {
+ visibility: visible !important;
+ }
+ > div {
+ position: absolute;
+ }
+ .o_table_ui_menu_toggler {
+ cursor: pointer;
+ background-color: white;
+ color: #6e727a;
+ border: 1px solid #C9CCD2;
+ border-radius: 5px;
+ padding: 2px 3px;
+ }
+ .o_table_ui_menu {
+ display: none;
+ cursor: pointer;
+ background-color: white;
+ width: fit-content;
+ border: 1px solid #C9CCD2;
+ padding: 5px 0;
+ white-space: nowrap;
+
+ > div:hover {
+ background-color: #ebebeb;
+ }
+ span {
+ margin-right: 8px;
+ color: #6e727a;
+ }
+ div {
+ padding: 0 8px;
+ }
+ }
+ &.o_open {
+ visibility: visible !important;
+
+ .o_table_ui_menu {
+ display: block;
+
+ > div.o_hide {
+ display: none;
+ }
+ }
+ }
+}
.oe-floating {
box-shadow: 0px 3px 18px rgba(0, 0, 0, .23);
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js
index 9d47da9183e..4d54c0ca085 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/editor.test.js
@@ -3920,12 +3920,259 @@ X[]
});
describe('tables', () => {
+ describe('add children', () => {
+ describe('row', () => {
+ describe('above', async () => {
+ it('should add a row above the top row', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addRowAbove'),
+ contentAfter: '' +
+ '
| ' +
+ '
| ' +
+ '
| ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
',
+ });
+ });
+ it('should add a row above the middle row', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addRowAbove'),
+ contentAfter: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '
| ' +
+ '
| ' +
+ '
| ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
',
+ });
+ });
+ });
+ describe('below', () => {
+ it('should add a row below the bottom row', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addRowBelow'),
+ contentAfter: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
' +
+ '' +
+ '
| ' +
+ '
| ' +
+ '
| ' +
+ '
',
+ });
+ });
+ it('should add a row below the middle row', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addRowBelow'),
+ contentAfter: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
' +
+ '' +
+ '
| ' +
+ '
| ' +
+ '
| ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ });
+ });
+ });
+ });
+ describe('column', () => {
+ describe('left', () => {
+ it('should add a column left of the leftmost column', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab[] | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addColumnLeft'),
+ contentAfter: '' +
+ '
| ' +
+ 'ab[] | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '
| ' +
+ 'ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ });
+ });
+ it('should add a column left of the middle column', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd[] | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addColumnLeft'),
+ contentAfter: '' +
+ '| ab | ' +
+ '
| ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ '
| ' +
+ 'cd[] | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ '
| ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ });
+ });
+ });
+ describe('right', () => {
+ it('should add a column right of the rightmost column', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addColumnRight'),
+ contentAfter: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef[] | ' +
+ // size was slightly adjusted to
+ // preserve table width in view on
+ // fractional division results
+ '
| ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
| ' +
+ '
',
+ });
+ });
+ it('should add a column right of the middle column', async () => {
+ await testEditor(BasicEditor, {
+ contentBefore: '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd[] | ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ 'ef | ' +
+ '
',
+ stepFunction: async editor => editor.execCommand('addColumnRight'),
+ contentAfter: '' +
+ '| ab | ' +
+ 'cd | ' +
+ '
| ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd[] | ' +
+ '
| ' +
+ 'ef | ' +
+ '
' +
+ '' +
+ '| ab | ' +
+ 'cd | ' +
+ '
| ' +
+ 'ef | ' +
+ '
',
+ });
+ });
+ });
+ });
+ });
describe('tab', () => {
it('should add a new row on press tab at the end of a table', async () => {
await testEditor(BasicEditor, {
- contentBefore: '',
+ contentBefore: '',
stepFunction: async editor => triggerEvent(editor.editable, 'keydown', { key: 'Tab'}),
- contentAfter: '',
+ contentAfter: '',
});
});
});