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