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: '' + + '' + + '' + + '' + + '
abcdef[]
', + stepFunction: async editor => editor.execCommand('addRowAbove'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '



abcdef[]
', + }); + }); + it('should add a row above the middle row', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef
abcdef[]
', + stepFunction: async editor => editor.execCommand('addRowAbove'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef



abcdef[]
', + }); + }); + }); + describe('below', () => { + it('should add a row below the bottom row', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '
abcdef[]
', + stepFunction: async editor => editor.execCommand('addRowBelow'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef[]



', + }); + }); + it('should add a row below the middle row', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef[]
abcdef
', + stepFunction: async editor => editor.execCommand('addRowBelow'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef[]



abcdef
', + }); + }); + }); + }); + describe('column', () => { + describe('left', () => { + it('should add a column left of the leftmost column', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
ab[]cdef
abcdef
', + stepFunction: async editor => editor.execCommand('addColumnLeft'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '

ab[]cdef

abcdef
', + }); + }); + it('should add a column left of the middle column', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef
abcd[]ef
abcdef
', + stepFunction: async editor => editor.execCommand('addColumnLeft'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
ab
cdef
ab
cd[]ef
ab
cdef
', + }); + }); + }); + describe('right', () => { + it('should add a column right of the rightmost column', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef[]
abcdef
', + stepFunction: async editor => editor.execCommand('addColumnRight'), + contentAfter: '' + + '' + + '' + + '' + + // size was slightly adjusted to + // preserve table width in view on + // fractional division results + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef[]
abcdef
', + }); + }); + it('should add a column right of the middle column', async () => { + await testEditor(BasicEditor, { + contentBefore: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcdef
abcd[]ef
abcdef
', + stepFunction: async editor => editor.execCommand('addColumnRight'), + contentAfter: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
abcd
ef
abcd[]
ef
abcd
ef
', + }); + }); + }); + }); + }); describe('tab', () => { it('should add a new row on press tab at the end of a table', async () => { await testEditor(BasicEditor, { - contentBefore: '
abcdef[]
', + contentBefore: '
abcdef[]
', stepFunction: async editor => triggerEvent(editor.editable, 'keydown', { key: 'Tab'}), - contentAfter: '
abcdef
[]


', + contentAfter: '
abcdef
[]


', }); }); });