diff --git a/src/modules/Exports.js b/src/modules/Exports.js index c3e449499..4b6f27411 100644 --- a/src/modules/Exports.js +++ b/src/modules/Exports.js @@ -345,6 +345,48 @@ class Exports { } } + const handleUnequalDatetimeSeries = () => { + const categories = new Set() + const data = {} + + series.forEach((s, sI) => { + s?.data.forEach((dataItem) => { + let cat, value + if (dataFormat.isFormatXY()) { + cat = dataItem.x + value = dataItem.y + } else if (dataFormat.isFormat2DArray()) { + cat = dataItem[0] + value = dataItem[1] + } else { + return + } + if (!data[cat]) { + data[cat] = Array(series.length).fill('') + } + data[cat][sI] = value + categories.add(cat) + }) + }) + + if (columns.length) { + rows.push(columns.join(columnDelimiter)) + } + + Array.from(categories) + .sort() + .forEach((cat) => { + rows.push([ + isTimeStamp(cat) + ? w.config.chart.toolbar.export.csv.dateFormatter(cat) + : Utils.isNumber(cat) + ? cat + : cat.split(columnDelimiter).join(''), + data[cat].join(columnDelimiter), + ]) + }) + } + columns.push(w.config.chart.toolbar.export.csv.headerCategory) if (w.config.chart.type === 'boxPlot') { @@ -378,17 +420,28 @@ class Exports { columns.push(w.config.chart.toolbar.export.csv.headerValue) rows.push(columns.join(columnDelimiter)) } - series.map((s, sI) => { - if (w.globals.axisCharts) { - handleAxisRowsColumns(s, sI) - } else { - columns = [] - columns.push(w.globals.labels[sI].split(columnDelimiter).join('')) - columns.push(gSeries[sI]) - rows.push(columns.join(columnDelimiter)) - } - }) + if ( + !w.globals.allSeriesHasEqualX && + w.globals.axisCharts && + w.config.xaxis.type === 'datetime' && + !w.config.xaxis.categories.length && + !w.config.labels.length + ) { + handleUnequalDatetimeSeries() + } else { + series.map((s, sI) => { + if (w.globals.axisCharts) { + handleAxisRowsColumns(s, sI) + } else { + columns = [] + + columns.push(w.globals.labels[sI].split(columnDelimiter).join('')) + columns.push(gSeries[sI]) + rows.push(columns.join(columnDelimiter)) + } + }) + } result += rows.join(lineDelimiter) diff --git a/src/modules/Toolbar.js b/src/modules/Toolbar.js index 16595f8da..fe06006cf 100644 --- a/src/modules/Toolbar.js +++ b/src/modules/Toolbar.js @@ -183,8 +183,17 @@ export default class Toolbar { }, ] - if (!this.w.globals.allSeriesHasEqualX) { + if ( + !this.w.globals.allSeriesHasEqualX && + !( + this.w.globals.axisCharts && + this.w.config.xaxis.type === 'datetime' && + !this.w.config.xaxis.categories.length && + !this.w.config.labels.length + ) + ) { // if it is a multi series, and all series have variable x values, export CSV won't work + // unless it is a simple datetime chart menuItems.splice(2, 1) } for (let i = 0; i < menuItems.length; i++) { diff --git a/tests/unit/download-csv.spec.js b/tests/unit/download-csv.spec.js index 80be20b80..01677e09e 100644 --- a/tests/unit/download-csv.spec.js +++ b/tests/unit/download-csv.spec.js @@ -181,6 +181,72 @@ describe('Export Csv', () => { expect.stringContaining('.csv') ) }) + it("export csv from simple line chart with two unequal datetime series should call triggerDownload with csv encoded file data", () => { + var options = { + chart: { + type: "line" + }, + series: [{ + name: 'series1', + data: [["2000-01-01T00:00:00.000", 1]] + }, { + name: 'series2', + data: [["2000-01-02T00:00:00.000", 1]] + }], + xaxis: { + type: 'datetime', + }, + }; + const csvData = "category,series1,series2\n" + + "Sat Jan 01 2000,1,\n" + + "Sun Jan 02 2000,,1" + const chart = createChartWithOptions(options) + const exports = new Exports(chart.ctx) + jest.spyOn(Exports.prototype,'triggerDownload') + exports.exportToCSV(chart.w.config.series,'fileName') + expect(Exports.prototype.triggerDownload).toHaveBeenCalledTimes(1) + expect(Exports.prototype.triggerDownload).toHaveBeenCalledWith( + expect.stringContaining(encodeURIComponent(csvData)), + expect.toBeUndefined, + expect.stringContaining('.csv') + ) + }) + it("export csv from simple line chart with two unequal datetime x y series should call triggerDownload with csv encoded file data", () => { + var options = { + chart: { + type: "line" + }, + series: [{ + name: 'series1', + data: [{ + x: "2000-01-01T00:00:00.000", + y: 1 + }] + }, { + name: 'series2', + data: [{ + x: "2000-01-02T00:00:00.000", + y: 1 + }] + }], + xaxis: { + type: 'datetime', + }, + }; + const csvData = "category,series1,series2\n" + + "Sat Jan 01 2000,1,\n" + + "Sun Jan 02 2000,,1" + const chart = createChartWithOptions(options) + const exports = new Exports(chart.ctx) + jest.spyOn(Exports.prototype,'triggerDownload') + exports.exportToCSV(chart.w.config.series,'fileName') + expect(Exports.prototype.triggerDownload).toHaveBeenCalledTimes(1) + expect(Exports.prototype.triggerDownload).toHaveBeenCalledWith( + expect.stringContaining(encodeURIComponent(csvData)), + expect.toBeUndefined, + expect.stringContaining('.csv') + ) + }) it("export csv from simple spline area chart with two series should call triggerDownload with csv encoded file data", () => { var options = { series: [{