You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/07/30 01:54:48 UTC

(superset) 01/01: fix(pivot-table): keep D3_FORMAT-styled numbers intact in pivoted Excel export

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/issue-38555-followup
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 3ea77a92dd3c8bdc0de8339f9c4b287c7f7ed843
Author: rusackas <ev...@rusackas.com>
AuthorDate: Wed Jul 29 18:50:24 2026 -0700

    fix(pivot-table): keep D3_FORMAT-styled numbers intact in pivoted Excel export
    
    The "Export to Pivoted Excel" download runs entirely client-side via
    SheetJS's utils.table_to_book(), reading the rendered pivot table DOM.
    By default SheetJS tries to infer numbers/dates from each cell's
    displayed text, which mis-parses non-US D3_FORMAT strings: "1.234,56"
    (Spanish decimal formatting) silently became the number 1.23456,
    "3.500" became 3.5, and "12,50%" became 12.5.
    
    Pass raw: true so every cell is kept as the literal text already
    formatted by D3_FORMAT, instead of being reinterpreted by SheetJS.
    
    Fixes #38555
---
 .../src/utils/downloadAsPivotExcel.test.ts         | 59 ++++++++++++++++++++++
 .../src/utils/downloadAsPivotExcel.ts              |  6 ++-
 2 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/utils/downloadAsPivotExcel.test.ts b/superset-frontend/src/utils/downloadAsPivotExcel.test.ts
new file mode 100644
index 00000000000..5b6bb74d10f
--- /dev/null
+++ b/superset-frontend/src/utils/downloadAsPivotExcel.test.ts
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { WorkBook } from 'xlsx';
+import exportPivotExcel from './downloadAsPivotExcel';
+
+const mockWriteFile = jest.fn();
+
+jest.mock('xlsx', () => {
+  const actual = jest.requireActual('xlsx');
+  return {
+    ...actual,
+    writeFile: (...args: unknown[]) => mockWriteFile(...args),
+  };
+});
+
+test('preserves locale-formatted numbers exactly as rendered, without SheetJS reinterpreting them', () => {
+  document.body.innerHTML = `
+    <table id="pivot-table">
+      <tbody>
+        <tr>
+          <td>1.234,56</td>
+          <td>12,50%</td>
+          <td>3.500</td>
+        </tr>
+      </tbody>
+    </table>
+  `;
+
+  exportPivotExcel('#pivot-table', 'export');
+
+  expect(mockWriteFile).toHaveBeenCalledTimes(1);
+  const workbook = mockWriteFile.mock.calls[0][0] as WorkBook;
+  const sheet = workbook.Sheets[workbook.SheetNames[0]];
+
+  // These are Spanish-locale D3_FORMAT strings ("." as thousands separator,
+  // "," as decimal separator). Each must survive the export untouched, as a
+  // text cell, rather than being silently reparsed as a different number
+  // (SheetJS's default HTML table parsing would otherwise turn "1.234,56"
+  // into the number 1.23456, "3.500" into 3.5, and "12,50%" into 12.5).
+  expect(sheet.A1).toMatchObject({ t: 's', v: '1.234,56' });
+  expect(sheet.B1).toMatchObject({ t: 's', v: '12,50%' });
+  expect(sheet.C1).toMatchObject({ t: 's', v: '3.500' });
+});
diff --git a/superset-frontend/src/utils/downloadAsPivotExcel.ts b/superset-frontend/src/utils/downloadAsPivotExcel.ts
index 40d10fe3492..6603ad30ea9 100644
--- a/superset-frontend/src/utils/downloadAsPivotExcel.ts
+++ b/superset-frontend/src/utils/downloadAsPivotExcel.ts
@@ -23,6 +23,10 @@ export default function exportPivotExcel(
   fileName: string,
 ) {
   const table = document.querySelector(tableSelector);
-  const workbook = utils.table_to_book(table);
+  // `raw: true` keeps every cell as the literal text rendered in the DOM.
+  // Without it, SheetJS tries to infer numbers/dates from the displayed
+  // string, which mangles values that were formatted using a non-US
+  // D3_FORMAT (e.g. "1.234,56" gets misread as a date or truncated number).
+  const workbook = utils.table_to_book(table, { raw: true });
   writeFile(workbook, `${fileName}.xlsx`);
 }