From 1e0b846961c3ae2be75fe1488a39fc3cbf805acc Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:59:39 +0100 Subject: [PATCH 1/7] add rows and columns on paste --- lib/events/index.js | 46 +++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/lib/events/index.js b/lib/events/index.js index 3d092c5f..b7a4e559 100644 --- a/lib/events/index.js +++ b/lib/events/index.js @@ -2007,7 +2007,7 @@ export default function (self) { alwaysFilling = false, direction = 'both', }) { - var schema = self.getSchema(); + // var schema = self.getSchema(); const rowsLength = Math.max(rows.length, minRowsLength); const fillCellCallback = self.fillCellCallback; const filledCells = []; @@ -2029,12 +2029,18 @@ export default function (self) { : rowPosReal; // Rows may have been moved by user, so get the actual row index // (instead of the row index at which the row is rendered): - var realRowIndex = self.orders.rows[startRowIndex + rowPosition]; - var cells = rows[rowDataPos]; + if (self.originalData[startRowIndex + rowPosition] === undefined) { + self.addRow({}); + console.log('addRow'); + } + const realRowIndex = self.orders.rows[startRowIndex + rowPosition]; + console.log(realRowIndex); + + const cells = rows[rowDataPos]; const cellsLength = Math.max(cells.length, minColumnsLength); - var existingRowData = self.viewData[realRowIndex]; - var newRowData = Object.assign({}, existingRowData); + const existingRowData = self.viewData[realRowIndex]; + const newRowData = Object.assign({}, existingRowData); const fillArgs = fillCellCallback ? { rows: rows, @@ -2060,7 +2066,7 @@ export default function (self) { : undefined; for ( - var colPosReal = 0, cellDataPos = 0; + let colPosReal = 0, cellDataPos = 0; colPosReal < cellsLength; colPosReal++, cellDataPos++ ) { @@ -2073,15 +2079,27 @@ export default function (self) { ? cellsLength - colPosReal - 1 : colPosReal; const columnIndex = startColumnIndex + colPosition; - var column = schema[columnIndex]; - - if (!column) { - console.warn('Paste data exceeded grid bounds. Skipping.'); - continue; + if (!self.getSchema()[columnIndex]) { + const lastColSchema = self.getSchema()[ + self.orders.columns[self.getSchema().length - 1] + ]; + const newColSchema = { + ...lastColSchema, + name: `col${self.schema.length + 1}:${Date.now()}`, + // title: ' ', + }; + self.addColumn(newColSchema); + console.log('addColumn'); } - - var columnName = column.name; - var cellData = cells[cellDataPos]; + const column = self.getSchema()[self.orders.columns[columnIndex]]; + + // if (!column) { + // console.log('Sanity check'); + // console.warn('Paste data exceeded grid bounds. Skipping.'); + // // continue; + // } + const columnName = column.name; + let cellData = cells[cellDataPos]; if (cellData && cellData.value) { cellData = cellData.value.map((item) => item.value).join(''); } From 46191f0a10725e301f47e61c4ea380bbcd3f515e Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:21:23 +0100 Subject: [PATCH 2/7] add allowGridSizeChangeOnPaste attribute --- lib/defaults.js | 1 + lib/docs.js | 1 + lib/events/index.js | 35 +++++++++++++++++++++++------------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/defaults.js b/lib/defaults.js index 8bc00c4c..b6a6c42c 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -18,6 +18,7 @@ export default function (self) { ['allowSorting', true], ['allowGroupingRows', true], ['allowGroupingColumns', true], + ['allowGridSizeChangeOnPaste', false], ['animationDurationShowContextMenu', 50], ['animationDurationHideContextMenu', 50], ['autoGenerateSchema', false], diff --git a/lib/docs.js b/lib/docs.js index e2df9895..5b3e1eec 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -39,6 +39,7 @@ * @param {string} [args.blanksText=(Blanks)] - The text that appears on the context menu for filtering blank values (i.e. `undefined`, `null`, `''`). * @param {string} [args.ellipsisText=...] - The text used as ellipsis text on truncated values. * @param {boolean} [args.allowSorting=true] - Allow user to sort rows by clicking on column headers. + * @param {boolean} [args.allowGridSizeChangeOnPaste=false] - Allow adding new rows and columns if pasted data dimentions are bigger than existing grid dimentions. * @param {boolean} [args.allowGroupingColumns=true] - Allow user to group columns by clicking on column headers. * @param {boolean} [args.allowGroupingRows=true] - Allow user to group rows by clicking on rows headers. * @param {boolean} [args.showFilter=true] - When true, filter will be an option in the context menu. diff --git a/lib/events/index.js b/lib/events/index.js index b7a4e559..c701772d 100644 --- a/lib/events/index.js +++ b/lib/events/index.js @@ -2030,8 +2030,14 @@ export default function (self) { // Rows may have been moved by user, so get the actual row index // (instead of the row index at which the row is rendered): if (self.originalData[startRowIndex + rowPosition] === undefined) { - self.addRow({}); - console.log('addRow'); + if (self.attributes.allowGridSizeChangeOnPaste) { + // This needs to be optimized because .addRow() calls .refresh() + self.addRow({}); + console.log('addRow'); + } else { + console.warn('Paste data exceeded grid bounds. Skipping.'); + break; + } } const realRowIndex = self.orders.rows[startRowIndex + rowPosition]; console.log(realRowIndex); @@ -2080,16 +2086,21 @@ export default function (self) { : colPosReal; const columnIndex = startColumnIndex + colPosition; if (!self.getSchema()[columnIndex]) { - const lastColSchema = self.getSchema()[ - self.orders.columns[self.getSchema().length - 1] - ]; - const newColSchema = { - ...lastColSchema, - name: `col${self.schema.length + 1}:${Date.now()}`, - // title: ' ', - }; - self.addColumn(newColSchema); - console.log('addColumn'); + if (self.attributes.allowGridSizeChangeOnPaste) { + const lastColSchema = self.getSchema()[ + self.orders.columns[self.getSchema().length - 1] + ]; + const newColSchema = { + ...lastColSchema, + name: `col${self.schema.length + 1}:${Date.now()}`, + // title: ' ', + }; + self.addColumn(newColSchema); + console.log('addColumn'); + } else { + console.warn('Paste data exceeded grid bounds. Skipping.'); + continue; + } } const column = self.getSchema()[self.orders.columns[columnIndex]]; From 5141effa1eebc8c755b7bd6334009c83c5420dad Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:31:16 +0100 Subject: [PATCH 3/7] add tests --- test/editing.js | 220 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 206 insertions(+), 14 deletions(-) diff --git a/test/editing.js b/test/editing.js index 89acb387..e4fedc0b 100644 --- a/test/editing.js +++ b/test/editing.js @@ -113,8 +113,8 @@ export default function () { done( assertIf( grid.input.childNodes[0].innerHTML === 'A' && - grid.input.childNodes.length === 3 && - grid.input.tagName !== 'SELECT', + grid.input.childNodes.length === 3 && + grid.input.tagName !== 'SELECT', 'Expected an input to have appeared', ), ); @@ -438,6 +438,192 @@ export default function () { ); }, 10); }); + + it('paste a 2x3 table into 2x2 grid should add a new column, if allowGridSizeChangeOnPaste === true', function (done) { + var grid = g({ + test: this.test, + data: [ + { a: 'a', b: 'b' }, + { a: 'c', b: 'd' }, + ], + autoGenerateSchema: true, + allowGridSizeChangeOnPaste: true, + }); + + grid.focus(); + grid.setActiveCell(0, 0); + grid.selectArea({ top: 0, left: 0, bottom: 0, right: 0 }); + + grid.paste({ + clipboardData: { + items: [ + { + type: 'text/plain', + getAsString: function (callback) { + callback('1\t2\t3\n4\t5\t6'); + }, + }, + ], + }, + }); + + setTimeout(function () { + const colName = Object.keys(grid.viewData[0])[2]; + const cellData1 = grid.viewData[0][colName]; + const cellData2 = grid.viewData[1][colName]; + + try { + doAssert( + grid.viewData.length === 2 && + Object.keys(grid.viewData[0]).length === 3, + 'New column was not added to the grid', + ); + doAssert( + cellData1 === '3' && cellData2 === '6', + 'Correct data was not added to the new column', + ); + done(); + } catch (error) { + done(error); + } + }, 10); + }); + + it('paste a 3x2 table into 2x2 grid should add a new row, if allowGridSizeChangeOnPaste === true', function (done) { + var grid = g({ + test: this.test, + data: [ + { a: 'a', b: 'b' }, + { a: 'c', b: 'd' }, + ], + autoGenerateSchema: true, + allowGridSizeChangeOnPaste: true, + }); + + grid.focus(); + grid.setActiveCell(0, 0); + grid.selectArea({ top: 0, left: 0, bottom: 0, right: 0 }); + + grid.paste({ + clipboardData: { + items: [ + { + type: 'text/plain', + getAsString: function (callback) { + callback('1\t2\n3\t4\n5\t6'); + }, + }, + ], + }, + }); + + setTimeout(function () { + const lastRow = grid.viewData[2]; + const cellData1 = lastRow['a']; + const cellData2 = lastRow['b']; + + try { + doAssert( + grid.viewData.length === 3 && + Object.keys(grid.viewData[0]).length === 2, + 'New row was not added to the grid', + ); + doAssert( + cellData1 === '5' && cellData2 === '6', + 'Correct data was not added to the new row', + ); + done(); + } catch (error) { + done(error); + } + }, 10); + }); + + it('paste a 2x2 table into 1x1 grid should add a new row and a new column, if allowGridSizeChangeOnPaste === true', function (done) { + var grid = g({ + test: this.test, + data: [{ a: 'a' }], + autoGenerateSchema: true, + allowGridSizeChangeOnPaste: true, + }); + + grid.focus(); + grid.setActiveCell(0, 0); + grid.selectArea({ top: 0, left: 0, bottom: 0, right: 0 }); + + grid.paste({ + clipboardData: { + items: [ + { + type: 'text/plain', + getAsString: function (callback) { + callback('1\t2\n3\t4'); + }, + }, + ], + }, + }); + + setTimeout(function () { + const [firstRow, lastRow] = grid.viewData; + const colName = Object.keys(firstRow)[1]; + const cellData1 = firstRow[colName]; + const cellData2 = lastRow['a']; + const cellData3 = lastRow[colName]; + + try { + doAssert( + grid.viewData.length === 2 && + Object.keys(grid.viewData[0]).length === 2, + 'New row or new column was not added to the grid', + ); + doAssert( + cellData1 === '2' && cellData2 === '3' && cellData3 === '4', + 'Correct data was not added to the new row', + ); + done(); + } catch (error) { + done(error); + } + }, 10); + }); + + it('paste a 1x1 table into 1x1 grid should not add any new rows or columns, if allowGridSizeChangeOnPaste === true', function (done) { + var grid = g({ + test: this.test, + data: [{ a: 'a' }], + autoGenerateSchema: true, + allowGridSizeChangeOnPaste: true, + }); + + grid.focus(); + grid.setActiveCell(0, 0); + grid.selectArea({ top: 0, left: 0, bottom: 0, right: 0 }); + + grid.paste({ + clipboardData: { + items: [ + { + type: 'text/plain', + getAsString: function (callback) { + callback('1'); + }, + }, + ], + }, + }); + + setTimeout(function () { + done( + doAssert( + grid.viewData.length === 1 && + Object.keys(grid.viewData[0]).length === 1, + 'New row or new column was not added to the grid', + ), + ); + }, 10); + }); + it('paste a Excel table single row / single cell value from the clipboard into a cell', function (done) { var grid = g({ test: this.test, @@ -558,14 +744,20 @@ export default function () { setTimeout(function () { try { doAssert(grid.viewData.length == 3, 'Should have 3 rows exactly'); - doAssert(Object.keys(grid.viewData[0]).length == 3, 'Should have 3 columns exactly'); + doAssert( + Object.keys(grid.viewData[0]).length == 3, + 'Should have 3 columns exactly', + ); for (let i = 0; i < grid.viewData.length; i++) { for (const columnKey in grid.viewData[i]) { const currentValue = grid.viewData[i][columnKey]; doAssert( currentValue === 'New value', - 'Value for "' + columnKey + '" should be "New value", but got ' + currentValue + 'Value for "' + + columnKey + + '" should be "New value", but got ' + + currentValue, ); } } @@ -617,7 +809,10 @@ export default function () { setTimeout(function () { try { doAssert(grid.viewData.length == 2, 'Should have 2 rows exactly'); - doAssert(Object.keys(grid.viewData[0]).length == 3, 'Should have 3 columns exactly'); + doAssert( + Object.keys(grid.viewData[0]).length == 3, + 'Should have 3 columns exactly', + ); const expectedResult = [ { @@ -638,7 +833,7 @@ export default function () { const currentValue = grid.viewData[i][columnKey]; doAssert( currentValue === expectedValue, - `Value for "${columnKey}" should be "${expectedValue}", but got "${currentValue}"` + `Value for "${columnKey}" should be "${expectedValue}", but got "${currentValue}"`, ); } } @@ -808,14 +1003,11 @@ export default function () { it('Moving handle on desktop fills the overlay region with selection data', function (done) { const grid = g({ test: this.test, - data: [ - { field1: 'value1' }, - { field1: 'value2' }, - { field1: 'value3' }, - ], + data: [{ field1: 'value1' }, { field1: 'value2' }, { field1: 'value3' }], fillCellCallback: function (args) { - return args.newCellData + ' ' + - (args.fillingRowPosition + 1).toString(); + return ( + args.newCellData + ' ' + (args.fillingRowPosition + 1).toString() + ); }, selectionHandleBehavior: 'single', }); @@ -839,7 +1031,7 @@ export default function () { const currentValue = grid.viewData[i][columnKey]; doAssert( currentValue === expectedValue, - `Value for "${columnKey}" should be "${expectedValue}", but got "${currentValue}"` + `Value for "${columnKey}" should be "${expectedValue}", but got "${currentValue}"`, ); } } From c38757daf51cef14ed0abbe3d59f40a86ff5faf9 Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:34:11 +0100 Subject: [PATCH 4/7] remove logs --- lib/events/index.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/events/index.js b/lib/events/index.js index c701772d..042ff20a 100644 --- a/lib/events/index.js +++ b/lib/events/index.js @@ -2033,14 +2033,12 @@ export default function (self) { if (self.attributes.allowGridSizeChangeOnPaste) { // This needs to be optimized because .addRow() calls .refresh() self.addRow({}); - console.log('addRow'); } else { console.warn('Paste data exceeded grid bounds. Skipping.'); break; } } const realRowIndex = self.orders.rows[startRowIndex + rowPosition]; - console.log(realRowIndex); const cells = rows[rowDataPos]; const cellsLength = Math.max(cells.length, minColumnsLength); @@ -2096,19 +2094,12 @@ export default function (self) { // title: ' ', }; self.addColumn(newColSchema); - console.log('addColumn'); } else { console.warn('Paste data exceeded grid bounds. Skipping.'); continue; } } const column = self.getSchema()[self.orders.columns[columnIndex]]; - - // if (!column) { - // console.log('Sanity check'); - // console.warn('Paste data exceeded grid bounds. Skipping.'); - // // continue; - // } const columnName = column.name; let cellData = cells[cellDataPos]; if (cellData && cellData.value) { From 51ff2035a98432d71eef9bc1f5d58ddd5d014eb6 Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:49:09 +0100 Subject: [PATCH 5/7] change titles of new columns to empty --- lib/events/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/events/index.js b/lib/events/index.js index 042ff20a..37169769 100644 --- a/lib/events/index.js +++ b/lib/events/index.js @@ -2091,7 +2091,7 @@ export default function (self) { const newColSchema = { ...lastColSchema, name: `col${self.schema.length + 1}:${Date.now()}`, - // title: ' ', + title: ' ', }; self.addColumn(newColSchema); } else { From b92b323c32a03c03818a5fa6a2b28984090ed106 Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Mon, 10 Apr 2023 18:53:37 +0100 Subject: [PATCH 6/7] rename attr to allowGridExpandOnPaste; fix typo --- lib/defaults.js | 2 +- lib/docs.js | 2 +- lib/events/index.js | 7 +++---- test/editing.js | 16 ++++++++-------- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/defaults.js b/lib/defaults.js index b6a6c42c..2db4a61b 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -18,7 +18,7 @@ export default function (self) { ['allowSorting', true], ['allowGroupingRows', true], ['allowGroupingColumns', true], - ['allowGridSizeChangeOnPaste', false], + ['allowGridExpandOnPaste', false], ['animationDurationShowContextMenu', 50], ['animationDurationHideContextMenu', 50], ['autoGenerateSchema', false], diff --git a/lib/docs.js b/lib/docs.js index 5b3e1eec..32a69743 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -39,7 +39,7 @@ * @param {string} [args.blanksText=(Blanks)] - The text that appears on the context menu for filtering blank values (i.e. `undefined`, `null`, `''`). * @param {string} [args.ellipsisText=...] - The text used as ellipsis text on truncated values. * @param {boolean} [args.allowSorting=true] - Allow user to sort rows by clicking on column headers. - * @param {boolean} [args.allowGridSizeChangeOnPaste=false] - Allow adding new rows and columns if pasted data dimentions are bigger than existing grid dimentions. + * @param {boolean} [args.allowGridExpandOnPaste=false] - Allow adding new rows and columns if pasted data dimensions are bigger than existing grid dimensions. * @param {boolean} [args.allowGroupingColumns=true] - Allow user to group columns by clicking on column headers. * @param {boolean} [args.allowGroupingRows=true] - Allow user to group rows by clicking on rows headers. * @param {boolean} [args.showFilter=true] - When true, filter will be an option in the context menu. diff --git a/lib/events/index.js b/lib/events/index.js index 37169769..0144b63f 100644 --- a/lib/events/index.js +++ b/lib/events/index.js @@ -2007,7 +2007,6 @@ export default function (self) { alwaysFilling = false, direction = 'both', }) { - // var schema = self.getSchema(); const rowsLength = Math.max(rows.length, minRowsLength); const fillCellCallback = self.fillCellCallback; const filledCells = []; @@ -2030,7 +2029,7 @@ export default function (self) { // Rows may have been moved by user, so get the actual row index // (instead of the row index at which the row is rendered): if (self.originalData[startRowIndex + rowPosition] === undefined) { - if (self.attributes.allowGridSizeChangeOnPaste) { + if (self.attributes.allowGridExpandOnPaste) { // This needs to be optimized because .addRow() calls .refresh() self.addRow({}); } else { @@ -2084,13 +2083,13 @@ export default function (self) { : colPosReal; const columnIndex = startColumnIndex + colPosition; if (!self.getSchema()[columnIndex]) { - if (self.attributes.allowGridSizeChangeOnPaste) { + if (self.attributes.allowGridExpandOnPaste) { const lastColSchema = self.getSchema()[ self.orders.columns[self.getSchema().length - 1] ]; const newColSchema = { ...lastColSchema, - name: `col${self.schema.length + 1}:${Date.now()}`, + name: `col${self.getSchema().length + 1}:${Date.now()}`, title: ' ', }; self.addColumn(newColSchema); diff --git a/test/editing.js b/test/editing.js index e4fedc0b..3916b7d9 100644 --- a/test/editing.js +++ b/test/editing.js @@ -439,7 +439,7 @@ export default function () { }, 10); }); - it('paste a 2x3 table into 2x2 grid should add a new column, if allowGridSizeChangeOnPaste === true', function (done) { + it('paste a 2x3 table into 2x2 grid should add a new column, if allowGridExpandOnPaste === true', function (done) { var grid = g({ test: this.test, data: [ @@ -447,7 +447,7 @@ export default function () { { a: 'c', b: 'd' }, ], autoGenerateSchema: true, - allowGridSizeChangeOnPaste: true, + allowGridExpandOnPaste: true, }); grid.focus(); @@ -489,7 +489,7 @@ export default function () { }, 10); }); - it('paste a 3x2 table into 2x2 grid should add a new row, if allowGridSizeChangeOnPaste === true', function (done) { + it('paste a 3x2 table into 2x2 grid should add a new row, if allowGridExpandOnPaste === true', function (done) { var grid = g({ test: this.test, data: [ @@ -497,7 +497,7 @@ export default function () { { a: 'c', b: 'd' }, ], autoGenerateSchema: true, - allowGridSizeChangeOnPaste: true, + allowGridExpandOnPaste: true, }); grid.focus(); @@ -539,12 +539,12 @@ export default function () { }, 10); }); - it('paste a 2x2 table into 1x1 grid should add a new row and a new column, if allowGridSizeChangeOnPaste === true', function (done) { + it('paste a 2x2 table into 1x1 grid should add a new row and a new column, if allowGridExpandOnPaste === true', function (done) { var grid = g({ test: this.test, data: [{ a: 'a' }], autoGenerateSchema: true, - allowGridSizeChangeOnPaste: true, + allowGridExpandOnPaste: true, }); grid.focus(); @@ -588,12 +588,12 @@ export default function () { }, 10); }); - it('paste a 1x1 table into 1x1 grid should not add any new rows or columns, if allowGridSizeChangeOnPaste === true', function (done) { + it('paste a 1x1 table into 1x1 grid should not add any new rows or columns, if allowGridExpandOnPaste === true', function (done) { var grid = g({ test: this.test, data: [{ a: 'a' }], autoGenerateSchema: true, - allowGridSizeChangeOnPaste: true, + allowGridExpandOnPaste: true, }); grid.focus(); From d8e28351cf5509ba5f8cdd2ed4f9db0f57e4a753 Mon Sep 17 00:00:00 2001 From: Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:30:39 +0100 Subject: [PATCH 7/7] add deduceColTypeFromData util function --- lib/events/index.js | 5 +++-- lib/events/util.js | 30 ++++++++++++++++++++++++++++++ test/editing.js | 16 ++++++++++++---- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/events/index.js b/lib/events/index.js index 0144b63f..915d75eb 100644 --- a/lib/events/index.js +++ b/lib/events/index.js @@ -3,7 +3,7 @@ 'use strict'; import isPrintableKeyEvent from 'is-printable-key-event'; -import { isSupportedHtml, parseData } from './util'; +import { isSupportedHtml, parseData, deduceColTypeFromData } from './util'; export default function (self) { var wheeling; @@ -2088,9 +2088,10 @@ export default function (self) { self.orders.columns[self.getSchema().length - 1] ]; const newColSchema = { - ...lastColSchema, name: `col${self.getSchema().length + 1}:${Date.now()}`, + type: deduceColTypeFromData(rows, colPosReal), title: ' ', + width: lastColSchema.width, }; self.addColumn(newColSchema); } else { diff --git a/lib/events/util.js b/lib/events/util.js index 186666ff..a6468814 100644 --- a/lib/events/util.js +++ b/lib/events/util.js @@ -122,6 +122,35 @@ const createHTMLString = function (selectedData, isNeat) { return htmlString; }; +/** + * deduce the type of values in the column 'colNum' of data represented by 'rows' + * if the column is not empty and all values in the column are numbers, then the type is 'number', + * otherwise the type is 'string'. + * Purpose: check the type of pasted data when a new column is created dynamically + * @param {Array} rows - array of rows, e.g. [ [{"value": [{"value": "a"}]},{"value": [{"value": "b"}]}], [{"value": [{"value": "1"}]},{"value": [{"value": "2"}]}] ] + * @param {number} colNum - Column index to check + * @returns {("string" | "number")} deduced type + */ +const deduceColTypeFromData = function (rows, colNum) { + const len = rows.length; + // the first row can represent column headings. + // if one row is pasted, check it, otherwise start from the second row. + const startRow = len === 1 ? 0 : 1; + let isColEmpty = true; + for (let i = startRow; i < len; i += 1) { + const cellData = rows[i][colNum]; + if (!cellData || cellData.value.length === 0) { + continue; + } + isColEmpty = false; + const val = cellData.value[0].value; + if (!Number.isFinite(Number(val))) { + return 'string'; + } + } + return isColEmpty ? 'string' : 'number'; +}; + export { createTextString, createHTMLString, @@ -132,4 +161,5 @@ export { parseHtmlText, parseText, sanitizeElementData, + deduceColTypeFromData, }; diff --git a/test/editing.js b/test/editing.js index 3916b7d9..1e778fed 100644 --- a/test/editing.js +++ b/test/editing.js @@ -439,7 +439,7 @@ export default function () { }, 10); }); - it('paste a 2x3 table into 2x2 grid should add a new column, if allowGridExpandOnPaste === true', function (done) { + it('paste a 2x3 numeric table into 2x2 grid should add a new column of type "number", if allowGridExpandOnPaste === true', function (done) { var grid = g({ test: this.test, data: [ @@ -482,6 +482,10 @@ export default function () { cellData1 === '3' && cellData2 === '6', 'Correct data was not added to the new column', ); + doAssert( + grid.schema.find((col) => col.name === colName).type === 'number', + 'New column has incorrect type', + ); done(); } catch (error) { done(error); @@ -539,7 +543,7 @@ export default function () { }, 10); }); - it('paste a 2x2 table into 1x1 grid should add a new row and a new column, if allowGridExpandOnPaste === true', function (done) { + it('paste a 2x2 table of text cells into 1x1 grid should add a new row and a new column of type "string", if allowGridExpandOnPaste === true', function (done) { var grid = g({ test: this.test, data: [{ a: 'a' }], @@ -557,7 +561,7 @@ export default function () { { type: 'text/plain', getAsString: function (callback) { - callback('1\t2\n3\t4'); + callback('a\tb\nc\td'); }, }, ], @@ -578,9 +582,13 @@ export default function () { 'New row or new column was not added to the grid', ); doAssert( - cellData1 === '2' && cellData2 === '3' && cellData3 === '4', + cellData1 === 'b' && cellData2 === 'c' && cellData3 === 'd', 'Correct data was not added to the new row', ); + doAssert( + grid.schema.find((col) => col.name === colName).type === 'string', + 'New column has incorrect type', + ); done(); } catch (error) { done(error);