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:47 UTC

(superset) branch fix/issue-38555-followup created (now 3ea77a92dd3)

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

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


      at 3ea77a92dd3 fix(pivot-table): keep D3_FORMAT-styled numbers intact in pivoted Excel export

This branch includes the following new commits:

     new 3ea77a92dd3 fix(pivot-table): keep D3_FORMAT-styled numbers intact in pivoted Excel export

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by ru...@apache.org.
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`);
 }