Skip to content

fix(pivot-table): blank ambiguous cross-metric totals and null values in fraction mode - #42810

Merged
sadpandajoe merged 4 commits into
feat/pivot-table-fraction-offrom
fix/pivot-table-fraction-edge-cases
Aug 5, 2026
Merged

fix(pivot-table): blank ambiguous cross-metric totals and null values in fraction mode#42810
sadpandajoe merged 4 commits into
feat/pivot-table-fraction-offrom
fix/pivot-table-fraction-edge-cases

Conversation

@rusackas

@rusackas rusackas commented Aug 5, 2026

Copy link
Copy Markdown
Member

SUMMARY

Follow-up to #42761 (pivot table "Show values as" percent display), fixing two edge cases flagged in review that weren't small enough to fold into that PR without risking a rushed change to already-reviewed code:

  • Cross-metric total collision. processRecord's "Metric-collapse totals" mirrors each metric's grand-total record into a single shared allTotal/rowTotals/colTotals slot when no real dimension is left to key on (documented in-code as deliberately deferred "future work" for actual-values mode). In fraction mode this is worse than a stale display: metricAxis locks onto the first metric pushed into that slot, but the underlying value is always the last metric pushed, so a multi-metric table's grand-total corner cell could divide one metric's value by a different metric's total and render a wrong-but-plausible percentage (concretely: with metric A's grand total of 30 and metric B's of 300, the corner cell showed 1,000.0%). This PR detects the collision on push and blanks the cell instead of showing a misleading number. Actual-values mode is unchanged and still has the pre-existing "future work" limitation — this only prevents fraction mode from making it worse.
  • NULL metric value renders as "0.0%". A DB-computed value can be a genuine SQL NULL (e.g. AVG over an empty group), which renders blank in "Actual values" mode. In fraction mode, null / acc coerces to 0 in JS, so the same cell rendered a measured "0.0%" instead of staying blank. Now checked explicitly.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — covered by the new regression tests below; both were confirmed to fail against the pre-fix code (corner cell showed 1,000.0%, null cell showed 0.0%) before being fixed.

TESTING INSTRUCTIONS

npx jest plugins/plugin-chart-pivot-table/test/react-pivottable/tableRenders.test.tsx --runInBand — two new tests: blanks the grand-total corner cell when it mixes multiple metrics in fraction mode and keeps a null metric value blank in fraction mode instead of showing 0.0%.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

Base is feat/pivot-table-fraction-of (not master) since this fixes code introduced there and hasn't landed yet — will retarget to master once #42761 merges, or this can be merged into that branch directly if easier.

A third item from the same review round — CSV/XLSX exports and scheduled reports not respecting showValuesAs — needs a real design decision in the export/report query path rather than a quick fix, so it's tracked separately as #42809 instead of bundled here.

@dosubot dosubot Bot added the viz:charts:pivot Related to the Pivot Table charts label Aug 5, 2026
@bito-code-review

bito-code-review Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped - Branch Excluded

Bito didn't auto-review because the source or target branch is excluded from automatic reviews.
No action is needed if you didn't intend for the agent to review it. Otherwise, to manually trigger a review, type /review in a comment and save.
You can change the branch exclusion settings here, or contact your Bito workspace admin at evan@preset.io.

@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 4b7ac54
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a738a9a19a93b0008099731
😎 Deploy Preview https://deploy-preview-42810--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

⚠️ Translation Regression Detected

A source change in this PR renamed or reworded strings, invalidating existing translations (they are now #, fuzzy) in es. Please resolve the affected .po files before merging.

Note: neither intentionally deleting a translatable string nor filling a previously-untranslated entry with a fuzzy guess (e.g. an AI backfill) is a regression — only a confirmed translation that a renamed/reworded source string turned fuzzy is flagged here.

Language Invalidated translations
es 75

How to fix

1. Install dependencies (if not already set up):

pip install -r superset/translations/requirements.txt
sudo apt-get install gettext   # or: brew install gettext

2. Re-extract strings and sync .po files:

./scripts/translations/babel_update.sh

This rewrites superset/translations/messages.pot from the current source files and merges the changes into every .po file. Strings whose msgid changed will be marked #, fuzzy.

3. Resolve the fuzzy entries in the affected language files (es):

grep -n '#, fuzzy' superset/translations/<lang>/LC_MESSAGES/messages.po

For each fuzzy entry, either rewrite the msgstr to match the new string and remove the #, fuzzy line, or clear the msgstr to "" if you cannot provide a translation.

4. Commit your changes to the .po files.

… in fraction mode

Two edge cases in the "Show values as" percent display (introduced in #42761)
flagged in review, both in fractionOf's value():

- "Metric-collapse totals" (processRecord) mirrors each metric's grand-total
  record into a single shared allTotal/rowTotals/colTotals slot when no real
  dimension is left to key on. metricAxis locks onto the first metric pushed,
  but the underlying value is always the last metric pushed, so a multi-metric
  table's grand-total corner cell could divide one metric's value by another
  metric's total and render a wrong-but-plausible-looking percentage. Now
  detects the collision on push and blanks the cell instead.
- A DB-computed value can be a genuine SQL NULL (e.g. AVG over an empty
  group). `null / acc` coerces to `0` in JS, turning a cell that's blank in
  "Actual values" mode into a measured "0.0%". Now checks for null before
  dividing.

Follow-up to #42761.
…ricAxis

The rebase onto feat/pivot-table-fraction-of picked up a follow-up fix that
updates metricAxis on every push (not just the first) so it stays in sync
with the last-pushed metric, same as `inner`. The multi-metric corner cell
now divides the last metric by its own total (a self-consistent 100%, per
the documented "totals dividing by themselves" design) instead of blanking.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rusackas
rusackas force-pushed the fix/pivot-table-fraction-edge-cases branch from 4b7ac54 to 641de27 Compare August 5, 2026 20:06
@pull-request-size pull-request-size Bot added size/M and removed size/L labels Aug 5, 2026
A third issue flagged in review (codeant-ai): the denominator, not just the
numerator, can itself be a genuine SQL NULL (e.g. a row total that's an AVG
over an empty group). `numerator / null` coerces to `numerator / 0` in JS,
returning Infinity/NaN from value() instead of the blank the missing/null
contract already establishes for the other branches in this function.

The shared number formatter already blanks non-finite values, so this had
no visible effect on rendered text, but other code that reads .value()
directly (e.g. value-based row/column sorting) relies on the null contract
to treat it as "no value" -- fixed at the source, with both a DOM-level
regression test and one asserting value() directly (since the two null vs.
Infinity/NaN cases render identically and the DOM test alone can't tell them
apart).
@pull-request-size pull-request-size Bot added size/L and removed size/M labels Aug 5, 2026
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.77%. Comparing base (1681dad) to head (8d86dbd).

Additional details and impacted files
@@                      Coverage Diff                      @@
##           feat/pivot-table-fraction-of   #42810   +/-   ##
=============================================================
  Coverage                         65.76%   65.77%           
=============================================================
  Files                              2842     2842           
  Lines                            162314   162316    +2     
  Branches                          37197    37198    +1     
=============================================================
+ Hits                             106752   106756    +4     
+ Misses                            53494    53492    -2     
  Partials                           2068     2068           
Flag Coverage Δ
javascript 72.09% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sadpandajoe
sadpandajoe merged commit ad05389 into master Aug 5, 2026
75 checks passed
@sadpandajoe
sadpandajoe deleted the fix/pivot-table-fraction-edge-cases branch August 5, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugins preset-io size/L viz:charts:pivot Related to the Pivot Table charts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants