From d78344120c0908ded6a3e0e70e6f6c4486f0da4a Mon Sep 17 00:00:00 2001 From: Dmitry Kuzin Date: Wed, 21 Dec 2022 19:14:55 +0400 Subject: [PATCH] Work for #296: migrate to the newest tabulator version --- examples/tabulator.html | 5 ++-- package.json | 2 +- src/tables/table.ts | 26 ++++++++-------- src/tables/tabulator.scss | 4 +-- src/tables/tabulator.ts | 34 ++++++++++++++------- tests/tables/tabulator.test.ts | 54 ++++++++++++++++++++++------------ 6 files changed, 77 insertions(+), 48 deletions(-) diff --git a/examples/tabulator.html b/examples/tabulator.html index df9d5c47e..8cf2ad315 100644 --- a/examples/tabulator.html +++ b/examples/tabulator.html @@ -7,9 +7,8 @@ - - + + diff --git a/package.json b/package.json index d171d405a..ff5d31e16 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "muuri": "^0.8.0", "plotly.js-dist-min": "^2.11.1", "survey-core": "^1.9.52", - "tabulator-tables": "4.8.4", + "tabulator-tables": "5.4.0", "wordcloud": "^1.2.2" }, "devDependencies": { diff --git a/src/tables/table.ts b/src/tables/table.ts index c7897c315..fe05674d2 100644 --- a/src/tables/table.ts +++ b/src/tables/table.ts @@ -192,20 +192,20 @@ export abstract class Table { this.onStateChanged.fire(this, this.state); } - protected initTableData(data: Array) { - this.tableData = (data || []).map((item) => { - var dataItem: any = {}; - this._survey.data = item; - this._columns.forEach((column) => { - const opt = column.getCellData(this, item); - if (typeof this._options.onGetQuestionValue === "function") { - this._options.onGetQuestionValue(opt); - } - dataItem[column.name] = opt.displayValue; - }); - - return dataItem; + protected initTableData(data: Array): void { + this.tableData = (data || []).map((item, index) => this.initTableDataRow(item, index)); + } + protected initTableDataRow(item: any, index: number): void { + var dataItem: any = {}; + this._survey.data = item; + this._columns.forEach((column) => { + const opt = column.getCellData(this, item); + if (typeof this._options.onGetQuestionValue === "function") { + this._options.onGetQuestionValue(opt); + } + dataItem[column.name] = opt.displayValue; }); + return dataItem; } public moveColumn(from: number, to: number) { diff --git a/src/tables/tabulator.scss b/src/tables/tabulator.scss index bd650c006..ff2c86c4e 100755 --- a/src/tables/tabulator.scss +++ b/src/tables/tabulator.scss @@ -184,13 +184,13 @@ $root-font-size: 14px; padding-right: 0; } - .tabulator-tableHolder::-webkit-scrollbar { + .tabulator-tableholder::-webkit-scrollbar { height: 10px; width: 10px; background-color: $scroll-color; } - .tabulator-tableHolder::-webkit-scrollbar-thumb { + .tabulator-tableholder::-webkit-scrollbar-thumb { background: $main-color; } } diff --git a/src/tables/tabulator.ts b/src/tables/tabulator.ts index da6143bdf..d6cd5fd50 100755 --- a/src/tables/tabulator.ts +++ b/src/tables/tabulator.ts @@ -16,10 +16,12 @@ if (!!document) { document.head.appendChild(templateHolder); } +const TABULATOR_ROW_INDEX_FIELD = "tabulator_row_index"; interface ITabulatorOptions extends ITableOptions { tabulatorOptions?: any; downloadHiddenColumns?: boolean; actionsColumnWidth?: number; + columnMinWidth: number; downloadButtons: Array; downloadOptions?: { [type: string]: any }; /* @@ -41,7 +43,7 @@ export const defaultDownloadOptions = { styles: { font: "custom_helvetica", fontStyle: "normal", - cellWidth: 1, + minCellWidth: 100, }, margin: { top: 10, right: 10, bottom: 10, left: 10 }, }; @@ -56,6 +58,7 @@ export const defaultOptions: ITabulatorOptions = { actionsColumnWidth: 60, downloadHiddenColumns: false, downloadButtons: ["csv"], + columnMinWidth: 248, downloadOptions: defaultDownloadOptions, onDownloadCallbacks: { pdf: (tabulator: Tabulator, options) => { @@ -96,7 +99,7 @@ export class Tabulator extends Table { if(!!window && window["XLSX"] !== undefined && defaultOptions.downloadButtons.indexOf("xlsx") === -1) { defaultOptions.downloadButtons.unshift("xlsx"); } - if(!!window && window["jsPDF"] !== undefined && defaultOptions.downloadButtons.indexOf("pdf") === -1) { + if(!!window && window["jspdf"] !== undefined && defaultOptions.downloadButtons.indexOf("pdf") === -1) { defaultOptions.downloadButtons.unshift("pdf"); } this._options = Object.assign({}, defaultOptions, options); @@ -140,19 +143,24 @@ export class Tabulator extends Table { columns, rowFormatter: this.rowFormatter, paginationElement: paginationElement, - columnMoved: this.columnMovedCallback, - columnResized: this.columnResizedCallback, tooltipsHeader: true, - tooltips: (cell: any) => cell.getValue(), downloadRowRange: "all", - columnMinWidth: 248, paginationButtonCount: 3, nestedFieldSeparator: false, + columnDefaults: { + tooltip: (_: MouseEvent, cell: any) => { + const span = document.createElement("span"); + span.innerText = cell.getValue(); + return span.innerHTML; + } + } }, this._options.tabulatorOptions ); this.tabulatorTables = new TabulatorTables(this.tableContainer, config); + this.tabulatorTables.on("columnResized", this.columnResizedCallback); + this.tabulatorTables.on("columnMoved", this.columnMovedCallback); const extensionsContainer = DocumentHelper.createElement( "div", @@ -228,13 +236,12 @@ export class Tabulator extends Table { this._rows.push(tableRow); }; - private accessorDownload = (cellData: any, rowData: any, reason: string, _: any, columnComponent: any, rowComponent: any) => { + private accessorDownload = (cellData: any, _rowData: any, _reason: string, _: any, columnComponent: any, rowComponent: any) => { const columnDefinition = columnComponent.getDefinition(); const questionName = columnDefinition.field; const column = this.columns.filter(col => col.name === questionName)[0]; if (!!column && rowComponent) { - const dataRow = this.data[rowComponent.getPosition()]; - const dataCell = dataRow[questionName]; + const dataCell = this.data[rowComponent.getData()[TABULATOR_ROW_INDEX_FIELD]][questionName]; if (column.dataType === ColumnDataType.Image) { return questionName; } @@ -300,6 +307,7 @@ export class Tabulator extends Table { widthShrink: !column.width ? 1 : 0, visible: this.isColumnVisible(column), headerSort: false, + minWidth: this._options.columnMinWidth, download: this._options.downloadHiddenColumns ? true : undefined, formatter, accessorDownload: this.accessorDownload, @@ -427,6 +435,12 @@ export class Tabulator extends Table { public layout(hard: boolean = false): void { this.tabulatorTables.redraw(hard); } + + protected initTableDataRow(item: any, index: number) { + const dataItem = super.initTableDataRow(item, index); + dataItem[TABULATOR_ROW_INDEX_FIELD] = index; + return dataItem; + } } export class TabulatorRow extends TableRow { @@ -448,7 +462,7 @@ export class TabulatorRow extends TableRow { } public getDataPosition(): number { - return this.innerRow.getPosition(); + return this.innerRow.getData(TABULATOR_ROW_INDEX_FIELD); } public remove(): void { diff --git a/tests/tables/tabulator.test.ts b/tests/tables/tabulator.test.ts index de19b7dee..ccca3bf67 100644 --- a/tests/tables/tabulator.test.ts +++ b/tests/tables/tabulator.test.ts @@ -5,6 +5,15 @@ jest.mock("tabulator-tables", () => { return { default: jest.requireActual("tabulator-tables") }; }); +const trueTimeout = window.setTimeout; + +function mockTimeout() { + window.setTimeout = (callback) => { callback(); }; +} +function restoreTimeout() { + window.setTimeout = trueTimeout; +} + const json = { questions: [ { @@ -38,8 +47,8 @@ test("getColumns method", () => { const columns = tabulator["getColumns"](); - expect(JSON.stringify(columns)).toBe( - '[{"field":"","title":"","download":false,"resizable":false,"minWidth":60,"width":60},{"field":"car","title":"What car are you driving?","widthShrink":1,"visible":true,"headerSort":false,"formatter":"plaintext"},{"field":"photo","title":"photo","widthShrink":1,"visible":true,"headerSort":false,"formatter":"html"}]' + expect(JSON.parse(JSON.stringify(columns))).toEqual( + [{ "field": "", "title": "", "download": false, "resizable": false, "minWidth": 60, "width": 60 }, { "field": "car", "title": "What car are you driving?", "widthShrink": 1, "visible": true, "headerSort": false, "minWidth": 248, "formatter": "plaintext" }, { "field": "photo", "title": "photo", "widthShrink": 1, "visible": true, "headerSort": false, "minWidth": 248, "formatter": "html" }] ); }); @@ -66,11 +75,14 @@ test("move column callback", () => { }; const survey = new SurveyModel(json); const tabulator = new Tabulator(survey, [], null); - tabulator.render(document.createElement("table")); - (tabulator).tabulatorTables.moveColumn("q1", "q3", true); - var trueOrder = ["q2", "q3", "q1", "q4"]; - var order = tabulator.columns.map((column) => column.name); + const tableContainer = document.createElement("div"); + mockTimeout(); + tabulator.render(tableContainer); + tabulator["tabulatorTables"].moveColumn("q1", "q3", true); + const trueOrder = ["q2", "q3", "q1", "q4"]; + const order = tabulator.columns.map((column) => column.name); expect(order).toEqual(trueOrder); + restoreTimeout(); }); test("check that tabulator takes into account column's width", () => { @@ -113,13 +125,15 @@ test("check that tabulator take into account column's width after layout (check location: 0, }, ]; + mockTimeout(); tabulator.render(document.createElement("table")); - var columnDefinitions = tabulator.tabulatorTables.getColumnDefinitions(); + const columnDefinitions = tabulator.tabulatorTables.getColumnDefinitions(); expect(columnDefinitions[1].width).toBe(undefined); expect(columnDefinitions[1].widthShrink).toBe(1); tabulator.setColumnWidth("q1", 150); expect(columnDefinitions[1].width).toBe(150); expect(columnDefinitions[1].widthShrink).toBe(0); + restoreTimeout(); }); test("check that tabulator take into account downloadRowRange option", () => { @@ -143,21 +157,21 @@ test("check that tabulator take into account downloadHiddenColumns option", () = location: 0, }, ]; - + mockTimeout(); tabulator.render(document.createElement("table")); - expect(tabulator.tabulatorTables.getColumnDefinitions()[1].download).toBe( - undefined - ); - + expect(tabulator.tabulatorTables.getColumnDefinitions()[1].download).toBe(undefined); (tabulator).options.downloadHiddenColumns = true; tabulator.render(document.createElement("table")); + expect(tabulator.tabulatorTables.getColumnDefinitions()[1].download).toBe( true ); + restoreTimeout(); }); test("check that action column doesn't export", () => { const tabulator = new Tabulator(new SurveyModel(null), [], null); + mockTimeout(); tabulator.render(document.createElement("table")); expect(tabulator.tabulatorTables.getColumnDefinitions()[0].download).toBe( false @@ -167,6 +181,7 @@ test("check that action column doesn't export", () => { expect(tabulator.tabulatorTables.getColumnDefinitions()[0].download).toBe( false ); + restoreTimeout(); }); test("useNamesAsTitles option", () => { @@ -183,14 +198,14 @@ test("useNamesAsTitles option", () => { let tabulator = new Tabulator(survey, [], null); let columns = tabulator.getColumns(); - expect(JSON.stringify(columns)).toBe( - "[{\"field\":\"\",\"title\":\"\",\"download\":false,\"resizable\":false,\"minWidth\":60,\"width\":60},{\"field\":\"str\",\"title\":\"String\",\"widthShrink\":1,\"visible\":true,\"headerSort\":false,\"formatter\":\"plaintext\"}]" + expect(JSON.parse(JSON.stringify(columns))).toEqual( + [{ "field": "", "title": "", "download": false, "resizable": false, "minWidth": 60, "width": 60 }, { "field": "str", "title": "String", "widthShrink": 1, "visible": true, "headerSort": false, "formatter": "plaintext", minWidth: 248 }] ); tabulator = new Tabulator(survey, [], { useNamesAsTitles: true }); columns = tabulator.getColumns(); - expect(JSON.stringify(columns)).toBe( - "[{\"field\":\"\",\"title\":\"\",\"download\":false,\"resizable\":false,\"minWidth\":60,\"width\":60},{\"field\":\"str\",\"title\":\"str\",\"widthShrink\":1,\"visible\":true,\"headerSort\":false,\"formatter\":\"plaintext\"}]" + expect(JSON.parse(JSON.stringify(columns))).toEqual( + [{ "field": "", "title": "", "download": false, "resizable": false, "minWidth": 60, "width": 60 }, { "field": "str", "title": "str", "widthShrink": 1, "visible": true, "headerSort": false, "formatter": "plaintext", minWidth: 248 }] ); }); @@ -225,6 +240,7 @@ test("check pdf options before download", () => { }; const survey = new SurveyModel(surveyJson); const tabulator = new Tabulator(survey, [], null); + mockTimeout(); tabulator.render(document.createElement("div")); let options = tabulator["getDownloadOptions"]("pdf"); expect(options.jsPDF.format).toEqual([595.28, 1120.32]); @@ -237,6 +253,7 @@ test("check pdf options before download", () => { tabulator.setColumnVisibility("question 3", false); options = tabulator["getDownloadOptions"]("pdf"); expect(options.jsPDF).toEqual(undefined); //a4 default format + restoreTimeout(); }); test("image and file export formatter", () => { @@ -256,7 +273,6 @@ test("image and file export formatter", () => { ], }; const survey = new SurveyModel(surveyJson); - const data = [{ signature: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAADICAYAAABS39xVAA", "image": [ @@ -268,9 +284,9 @@ test("image and file export formatter", () => { const columns = tabulator.getColumns(); const accessorDownload: any = columns[1].accessorDownload; - const fileCell = accessorDownload(undefined, undefined, undefined, undefined, { getDefinition: () => ({ field: "image" }) }, { getPosition: () => 0 }); + const fileCell = accessorDownload(undefined, undefined, undefined, undefined, { getDefinition: () => ({ field: "image" }) }, { getData: () => { return { "tabulator_row_index": 0 }; } }); expect(fileCell).toBe("file1.png, file2.png"); - const imageCell = accessorDownload(undefined, undefined, undefined, undefined, { getDefinition: () => ({ field: "signature" }) }, { getPosition: () => 0 }); + const imageCell = accessorDownload(undefined, data[0], undefined, undefined, { getDefinition: () => ({ field: "signature" }) }, { getData: () => { return { "tabulator_row_index": 0 }; } }); expect(imageCell).toBe("signature"); }); \ No newline at end of file