Skip to content

feat(excel): export Explore conditional formatting as native Excel rules - #44664

Open
bazhil wants to merge 20 commits into
apache:masterfrom
bazhil:feature/excel-conditional-formatting-export
Open

bazhil wants to merge 20 commits into
apache:masterfrom
bazhil:feature/excel-conditional-formatting-export

Conversation

@bazhil

@bazhil bazhil commented Sep 25, 2026 •

Copy link
Copy Markdown

SUMMARY

Table chart XLSX download did not include Explore adaptive formatting. The export writes the workbook in QueryContextProcessor.get_data before client post-processing, so conditional_formatting never reached the file.

For Table charts, the export now writes native Excel conditional formatting: comparator rules as CellIs (fill or font), operator None as a color scale, range operators as formulas, and cell bar as Excel data bars. If there are no custom rules and show_cell_bars is on, numeric columns get data bars like the default Table gradient. Sheet bounds are refreshed so rules cover all data rows, not only the header.

BEFORE/AFTER SCREENSHOTS

Before. Explore Table shows cell bars / color formatters; the XLSX has values only.

изображение изображение

After. Matching columns in the XLSX have Excel data bars or CellIs/color-scale rules. Screenshots: screenshots/after/.

Снимок экрана от 2026-09-25 22-01-09 Снимок экрана от 2026-09-25 22-01-49 Снимок экрана от 2026-09-25 22-02-54 Снимок экрана от 2026-09-25 22-05-44

TESTING INSTRUCTIONS

pytest tests/unit_tests/utils/test_excel_conditional.py -q
pytest tests/unit_tests/charts/test_client_processing.py::test_apply_client_processing_xlsx_attaches_table_cell_bars -q

Manual:

  1. Table chart, Raw records, show_cell_bars on, no custom formatters. Download XLSX: numeric columns have data bars.
  2. Add a rule sales > 10 with background color. Download: Excel CellIs greater-than on sales.
  3. Add a rule with Formatting object cell bar on a numeric column. Download: Excel data bar on that column.
  4. Confirm text columns (e.g. region) do not get data bars from show_cell_bars.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

CICD Bot and others added 3 commits September 14, 2026 11:57
XLSX export kept raw numbers, so Table/Pivot downloads did not match
the chart. Map d3 specifiers to Excel formats after the workbook is written.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sheet-wide color scales paint labels and blanks. Fill only numeric cells
that satisfy the Explore comparator, including a value-based gradient.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@netlify

netlify Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 1899f3f
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ae9ec8d0e3eb14631cdd762
😎 Deploy Preview https://deploy-preview-44664--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.

Comment on lines +575 to +577
result = polish_explore_xlsx(
result, df, form_data, include_index=include_index
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Pivot XLSX exports using showValuesAs percentages skip display formatting here, leaving fractional values with General formatting instead of showing percentages.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Api mismatch

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/common/query_context_processor.py
**Line:** 575:577
**Comment:**
	*Api Mismatch: Pivot XLSX exports using `showValuesAs` percentages skip display formatting here, leaving fractional values with General formatting instead of showing percentages.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of c91cbe4.

The XLSX export now passes the generated workbook, dataframe, and form data to polish_explore_xlsx, which applies Explore display formats and conditional formatting after serialization.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of b811d26.

Pivot XLSX exports in percentage modes now pass number_format="0.0%" to excel.df_to_excel, keeping fractional values numeric while displaying them as percentages.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of efb539a.

Pivot XLSX exports in percentage modes now pass number_format="0.0%" to excel.df_to_excel, keeping fractional values numeric while displaying them as percentages.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1f72f99.

Pivot XLSX exports now pass number_format="0.0%" when showValuesAs is a percentage mode, preserving numeric fractions while displaying them as percentages.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e7def26.

Percentage-mode pivot XLSX exports now pass the 0.0% number format to the Excel writer, keeping fractional values numeric while displaying them as percentages.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1899f3f.

Pivot-table XLSX exports with a percentage showValuesAs mode now pass 0.0% as the Excel number format, keeping the values numeric while displaying them as percentages.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of ab40a53.

The XLSX export now sets number_format to 0.0% for pivot-table percentage modes and passes it to df_to_excel.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 10d5a50.

The XLSX export now sets number_format to 0.0% for pivot-table percentage modes and passes it to df_to_excel.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6f44786.

Pivot XLSX exports now pass 0.0% as the number format when showValuesAs is a percentage mode, keeping fractional values numeric while displaying them as percentages.

If that's not right, unresolve this thread and CodeAnt will leave it open.

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. In superset/utils/excel_conditional.py, the polish_explore_xlsx function explicitly skips display formatting (which includes number formatting) when viz_type is pivot_table_v2 and showValuesAs is set to a percentage mode. This is intended to prevent conflicts, but it results in fractional values being displayed with General formatting instead of percentage formatting.

To resolve this, you can modify polish_explore_xlsx to apply percentage formatting even when skip_display is true, or ensure that the percentage format is applied separately. Given the current implementation, you could update the logic to allow percentage formatting while still skipping other display styles if necessary.

Would you like me to fetch all other comments on this PR to validate and implement fixes for them as well?

superset/utils/excel_conditional.py

skip_display = viz_type == "pivot_table_v2" and form_data.get("showValuesAs") in (
        SHOW_VALUES_AS_PERCENT_MODES
    )
    if not skip_display:
        headers = [str(column) for column in df.columns]
        if viz_type == "table":
            styles = styles_from_table_form_data(headers, form_data)
        else:
            styles = styles_from_pivot_form_data(headers, form_data)
        workbook_bytes = apply_column_display(
            workbook_bytes, styles, header_rows=header_rows
        )

Comment on lines +1479 to +1485
query["data"] = _apply_excel_explore_formats(
query["data"],
processed_df,
form_data,
viz_type,
include_index,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: processed_df uses verbose column labels while conditional-formatting rules use raw column names, so _column_index cannot find many configured columns and emits no rules.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Api mismatch

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/charts/client_processing.py
**Line:** 1479:1485
**Comment:**
	*Api Mismatch: `processed_df` uses verbose column labels while conditional-formatting rules use raw column names, so `_column_index` cannot find many configured columns and emits no rules.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of b811d26.

Excel formatting now passes the datasource verbose_map alongside processed_df to _apply_excel_explore_formats, allowing configured raw column names to resolve against verbose headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of efb539a.

Excel formatting now passes the datasource verbose_map alongside processed_df to _apply_excel_explore_formats, allowing configured raw column names to resolve against verbose headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1f72f99.

Excel formatting now passes verbose_map to _apply_excel_explore_formats, allowing conditional-formatting rules to resolve configured raw column names against verbose export headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of fa97cb6.

The Excel formatting call now passes the datasource verbose_map alongside processed_df, allowing configured raw column names to resolve against verbose headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6cf5cf0.

The XLSX formatting path now passes the datasource verbose_map into _apply_excel_explore_formats, allowing configured raw column names to be resolved against verbose export headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6775ff6.

The XLSX formatting call now passes the datasource verbose_map to _apply_excel_explore_formats, allowing formatting rules to resolve verbose column labels.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e7def26.

The Excel formatting pipeline now receives the datasource verbose_map, allowing configured raw column names to be matched against exported headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1899f3f.

The XLSX formatting call now forwards the datasource verbose_map to _apply_excel_explore_formats so configured columns can be matched against displayed headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6f44786.

The Excel formatting helper now receives the datasource verbose_map and datasource data, allowing formatting rules to resolve verbose export headers.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Comment thread superset/utils/excel_conditional.py Outdated
Comment on lines +237 to +239
if object_fmt == _OBJECT_CELL_BAR:
_add_data_bar(sheet, cell_range, rgb)
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: A cell-bar rule's comparator is discarded, so a rule such as > 10 displays bars for every value instead of only matching values.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Logic error

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/utils/excel_conditional.py
**Line:** 237:239
**Comment:**
	*Logic Error: A cell-bar rule's comparator is discarded, so a rule such as `>` 10 displays bars for every value instead of only matching values.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of b811d26.

Cell-bar rules now derive a matching range via _matching_cell_range(sheet, col_idx, header_row, rule) before adding the data bar, so the rule comparator is applied.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of efb539a.

Cell-bar rules now derive a matching range via _matching_cell_range(sheet, col_idx, header_row, rule) before adding the data bar, so the rule comparator is applied.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1f72f99.

Cell-bar rules now derive bar_range through _matching_cell_range(..., rule), so the rule metadata, including its comparator and threshold, is applied when selecting cells for the bars.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of fa97cb6.

Cell-bar rules now derive their bar range through _matching_cell_range(..., rule), so the rule’s comparator and threshold are applied instead of formatting the entire column.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6775ff6.

Cell-bar rules now compute a matching cell range from the rule and add data bars only to that range.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e7def26.

Cell-bar rules now pass the rule to _matching_cell_range and apply bars only to the resulting matching range.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1899f3f.

Cell-bar rules now pass the full rule to _matching_cell_range and apply bars only to the range returned for that rule.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of ab40a53.

Cell-bar rules now pass through _matching_cell_range with the rule, and the data bar is applied only to the returned range.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 10d5a50.

Cell-bar rules now pass through _matching_cell_range with the rule, and the data bar is applied only to the returned range.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Comment on lines +279 to +286
sheet.conditional_formatting.add(
cell_range,
ColorScaleRule(
start_type="min",
start_color=_SCALE_LOW,
end_type="max",
end_color=rgb,
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Color-scale rules discard configured minBound and maxBound, causing Excel to scale against the data range instead of the bounds used by Explore.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Incomplete implementation

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/utils/excel_conditional.py
**Line:** 279:286
**Comment:**
	*Incomplete Implementation: Color-scale rules discard configured `minBound` and `maxBound`, causing Excel to scale against the data range instead of the bounds used by Explore.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of b811d26.

Color-scale rules now convert minBound and maxBound to numeric values and pass them as start_value and end_value, using min/max only when bounds are absent.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of efb539a.

Color-scale rules now convert minBound and maxBound to numeric values and pass them as start_value and end_value, using min/max only when bounds are absent.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1f72f99.

Color-scale rules now convert minBound and maxBound to numeric values and pass them as start_value and end_value with matching num bound types.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of fa97cb6.

Color-scale rules now parse minBound and maxBound and pass them as numeric start and end values to ColorScaleRule.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e7def26.

Color-scale rules now parse minBound and maxBound and use the configured values as numeric start and end bounds.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1899f3f.

Color-scale rules now use configured minBound and maxBound as numeric start and end values, falling back to Excel’s minimum and maximum when bounds are absent.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of ab40a53.

Color-scale rules now use configured minBound and maxBound as numeric start and end values when present.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 10d5a50.

Color-scale rules now use configured minBound and maxBound as numeric start and end values when present.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6f44786.

Color-scale rules now use configured maxBound for greater-than rules and minBound for less-than rules; rules without an operator are also passed to the color-scale helper.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Comment on lines +171 to +175
currency = config.get("currencyFormat")
currency = currency if isinstance(currency, dict) else None
number_format = d3_number_to_excel(
config.get("d3NumberFormat"), currency
) or d3_time_to_excel(config.get("d3TimeFormat"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Table exports ignore datasource-level columnFormats and currencyFormats, so columns without explicit column_config lose formats that the chart still displays.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Api mismatch

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/utils/excel_display.py
**Line:** 171:175
**Comment:**
	*Api Mismatch: Table exports ignore datasource-level `columnFormats` and `currencyFormats`, so columns without explicit `column_config` lose formats that the chart still displays.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e7def26.

When explicit column configuration lacks formats, datasource currencyFormats and columnFormats now supply the currency and number formats.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 1899f3f.

Datasource currencyFormats and columnFormats are now used as fallbacks when building each column’s Excel number format.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of ab40a53.

Datasource currencyFormats and columnFormats are now used as fallbacks when column-level currency or number formats are missing.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 10d5a50.

Datasource currencyFormats and columnFormats are now used as fallbacks when column-level currency or number formats are missing.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 6f44786.

Datasource currencyFormats and columnFormats are now used as fallbacks when column-level configuration does not provide those formats.

If that's not right, unresolve this thread and CodeAnt will leave it open.

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #3b06a2

Actionable Suggestions - 1
  • superset/utils/excel_conditional.py - 1
Additional Suggestions - 6
  • tests/unit_tests/utils/test_excel_conditional.py - 2
    • Missing helper docstring · Line 27-27
      The new helper `_cf_rules` (line 27) has type hints but no docstring. BITO.md rules [12147]/[12490] require every new helper function to document its purpose and behavior.
    • Missing test docstrings · Line 35-113
      The four new test functions (`test_apply_conditional_formatting_writes_cell_is_rule`, `test_apply_cell_bars_when_show_cell_bars_and_no_custom_rules`, `test_cell_bar_object_writes_data_bar_rule`, `test_color_scale_when_operator_is_none`) have no docstrings. Repo rule [12148]/[12490] requires every new test function to document its purpose and expected outcome.
  • superset/utils/excel_conditional.py - 4
    • Duplicated theme color literal · Line 125-125
      `_rule_color` hardcodes the fallback literal `"52C41A"`, duplicating `_NAMED_COLORS["success"]` (line 57). If the theme green changes in the map, this default silently keeps the old color and downloads diverge from Explore. Return `_NAMED_COLORS["success"]` or a named `_DEFAULT_COLOR` constant instead of the bare hex string.
    • Magic 20-row sampling window · Line 291-291
      `_is_numeric_header` samples only `min(sheet.max_row, header_row + 20)` rows; the literal 20 is an undocumented business threshold. A text column whose first 20 data cells are blank is misclassified as numeric and gets a data bar. Name the constant and document the tradeoff, or scan all data rows.
    • Inconsistent nlevels guarding · Line 353-353
      Line 353 guards the same hazard with `getattr(df.index, "nlevels", 1)` while line 351 accesses `df.columns.nlevels` unguarded — inconsistent handling of the identical pandas-attribute risk within one function. Unifying both on the `getattr(..., 1)` form removes the crash and prevents the next contributor from copying the unguarded pattern.
    • Silent header_rows coercion · Line 328-328
      `max(header_rows, 1)` silently coerces `header_rows=0` to 1. No current caller passes 0, so this only masks future caller bugs; raising `ValueError` for non-positive `header_rows` would surface mistakes at the call site. Low priority.
Review Details
  • Files reviewed - 7 · Commit Range: 467c610..f71ab3b
    • superset/charts/client_processing.py
    • superset/common/query_context_processor.py
    • superset/utils/excel_conditional.py
    • superset/utils/excel_display.py
    • tests/unit_tests/charts/test_client_processing.py
    • tests/unit_tests/utils/test_excel_conditional.py
    • tests/unit_tests/utils/test_excel_display.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/utils/excel_conditional.py Outdated
if viz_type not in ("table", "pivot_table_v2"):
return workbook_bytes

header_rows = df.columns.nlevels if isinstance(df.columns, pd.MultiIndex) else 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AttributeError on plain Index

polish_explore_xlsx evaluates df.columns.nlevels whenever df.columns is not a pd.MultiIndex, but a plain pandas Index (built when include_index adds index columns, as from query_context_processor.py and client_processing.py) has no nlevels attribute, so XLSX downloads raise AttributeError instead of returning a file. Use getattr(df.columns, "nlevels", 1) as line 353 already does for df.index.

Code Review Run #3b06a2


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

CICD Bot added 2 commits September 26, 2026 20:27
@codecov

codecov Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.41477% with 159 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.95%. Comparing base (5aacca7) to head (6f44786).
⚠️ Report is 31 commits behind head on master.

Files with missing lines Patch % Lines
superset/utils/excel_conditional.py 72.47% 89 Missing and 50 partials ⚠️
superset/utils/excel_display.py 90.34% 7 Missing and 10 partials ⚠️
superset/common/query_context_processor.py 80.00% 1 Missing and 1 partial ⚠️
superset/charts/client_processing.py 92.30% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #44664      +/-   ##
==========================================
+ Coverage   84.91%   84.95%   +0.03%     
==========================================
  Files        3075     3080       +5     
  Lines      207844   209635    +1791     
  Branches    48995    49318     +323     
==========================================
+ Hits       176494   178094    +1600     
- Misses      27989    28099     +110     
- Partials     3361     3442      +81     
Flag Coverage Δ
hive 33.99% <0.42%> (-0.25%) ⬇️
mysql 51.70% <0.42%> (-0.55%) ⬇️
postgres 51.72% <0.42%> (-0.55%) ⬇️
presto 35.64% <0.42%> (-0.31%) ⬇️
python 87.89% <77.41%> (+0.01%) ⬆️
sqlite 51.47% <0.42%> (-0.55%) ⬇️
unit 82.87% <77.41%> (+0.17%) ⬆️

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.

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #6fb400

Actionable Suggestions - 2
  • tests/unit_tests/utils/test_excel_conditional.py - 1
  • superset/utils/excel_conditional.py - 1
Additional Suggestions - 4
  • tests/unit_tests/utils/test_excel_conditional.py - 1
    • New helper missing docstring · Line 36-36
      `_workbook_with_sales` is a new helper but lacks a docstring, while `_cf_rules` and every test function in this file have one; BITO rule 12147 requires docstrings on all new Python functions including test-file helpers. Add a one-line docstring describing the workbook shape it builds.
  • superset/utils/excel_conditional.py - 3
    • Numeric string equality mismatch · Line 316-316
      In `_value_matches_rule`, the `str(value) == str(target)` fallback re-parses nothing but discards the already-computed `number`/`target_number`: for numeric-looking strings like "1,234" vs "1234" the string compare fails even though `_as_number` parsed both successfully earlier. Comparing the parsed floats (falling back to string equality only when parsing failed) matches the comparator's numeric intent.
    • Per-rule reverse map scan · Line 213-215
      `_rule_column_aliases` scans every entry of `verbose_map` for a reverse (verbose→raw) match on each `_apply_rule` call. With large datasource column counts this is O(columns) per rule per export; a forward lookup plus one reverse scan per export (or a prebuilt reverse map) would keep aliasing cheap. Minor, but it runs inside the per-rule loop.
    • getattr weakens type check · Line 480-480
      Replacing the `isinstance(df.columns, pd.MultiIndex)` check with `getattr(df.columns, "nlevels", 1)` changes behavior for non-MultiIndex columns: a flat `Index` also exposes `.nlevels` (always 1, so benign), but any object with an unrelated `nlevels` attribute would now be trusted. The explicit type check was stricter and self-documenting; the getattr form silently accepts duck-typed impostors.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/common/query_context_processor.py - 1
Review Details
  • Files reviewed - 5 · Commit Range: f71ab3b..efb539a
    • superset/charts/client_processing.py
    • superset/common/query_context_processor.py
    • superset/utils/excel_conditional.py
    • tests/unit_tests/common/test_query_context_processor.py
    • tests/unit_tests/utils/test_excel_conditional.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +102 to +104
assert "A2" in cf_range
assert "A4" in cf_range
assert "A3" not in cf_range

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weak substring range assertions

These substring assertions cannot detect the regression this test exists for: a fallback to the full column range A2:A4 (e.g. _matching_cell_range returning _data_range) passes all three checks, since "A3" is not a substring of "A2:A4", while "A2" also matches "A20:A25". Verified with an openpyxl 3.1.5 round-trip. Compare exact range tokens instead: assert cf_range.split() == ["A2", "A4"].

Code suggestion
Check the AI-generated fix before applying
Suggested change
assert "A2" in cf_range
assert "A4" in cf_range
assert "A3" not in cf_range
assert cf_range.split() == ["A2", "A4"]

Code Review Run #6fb400


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Comment thread superset/utils/excel_conditional.py Outdated
Comment on lines +399 to +410
min_bound = _as_number(rule.get("minBound"))
max_bound = _as_number(rule.get("maxBound"))
start_type = "num" if min_bound is not None else "min"
end_type = "num" if max_bound is not None else "max"
sheet.conditional_formatting.add(
cell_range,
ColorScaleRule(
start_type=start_type,
start_value=min_bound,
start_color=_SCALE_LOW,
end_type=end_type,
end_value=max_bound,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ColorScale num with None val

start_value=min_bound / end_value=max_bound are passed even when the bound is None and the type falls back to "min"/"max". openpyxl 3.1.5's ColorScaleRule wraps every non-None type in a FormatObject(type=..., val=...) with no validation, so a type="num" cfvo with val=None can be emitted (e.g. minBound absent but maxBound="abc"), producing a color scale Excel may reject or misread. Pass the value only when the type is "num".

Code Review Run #6fb400


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #214642

Actionable Suggestions - 1
  • tests/unit_tests/utils/test_excel_conditional.py - 1
Additional Suggestions - 1
  • tests/unit_tests/utils/test_excel_conditional.py - 1
    • Duplicated data-bar filter · Line 302-307
      The `data_bars = [rng for rng, rule in _cf_rules(...) if rule.type == "dataBar"]` comprehension is written out three times in this diff (lines 302-306, 322-326, 395) plus a tuple variant at 108. Extract a `_data_bar_ranges(sheet)` helper next to `_cf_rules` so the data-bar expectation stays consistent if `_cf_rules` output shape changes again.
Review Details
  • Files reviewed - 5 · Commit Range: efb539a..6cf5cf0
    • superset/common/query_context_processor.py
    • superset/utils/excel_conditional.py
    • superset/utils/excel_display.py
    • tests/unit_tests/utils/test_excel_conditional.py
    • tests/unit_tests/utils/test_excel_display.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +209 to +399
def test_returns_original_bytes_when_nothing_to_apply() -> None:
raw = _workbook_with_sales(10)
assert apply_conditional_formatting(raw, []) is raw


def test_missing_column_does_not_add_rules() -> None:
styled = apply_conditional_formatting(
_workbook_with_sales(10),
[{"column": "missing", "operator": ">", "targetValue": 1}],
)
assert _cf_rules(load_workbook(io.BytesIO(styled)).active) == []


def test_range_operator_writes_formula_rule() -> None:
styled = apply_conditional_formatting(
_workbook_with_sales(5),
[
{
"column": "sales",
"operator": "≤ x ≤",
"targetValueLeft": 0,
"targetValueRight": 10,
"colorScheme": "#00FF00",
}
],
)
cf_range, rule = _cf_rules(load_workbook(io.BytesIO(styled)).active)[0]
assert "A2" in cf_range
assert rule.type == "expression"
formula = "".join(str(part) for part in (rule.formula or []))
assert "AND(" in formula
assert "A2>=0" in formula
assert "A2<=10" in formula


def test_text_color_cell_is_uses_font_not_fill() -> None:
styled = apply_conditional_formatting(
_workbook_with_sales(12),
[
{
"column": "sales",
"operator": ">",
"targetValue": 8,
"colorScheme": "#FF0000",
"objectFormatting": "TEXT_COLOR",
}
],
)
_, rule = _cf_rules(load_workbook(io.BytesIO(styled)).active)[0]
assert rule.type == "cellIs"
assert rule.dxf is not None
assert rule.dxf.font is not None
assert str(rule.dxf.font.color.rgb).endswith("FF0000")
assert rule.dxf.fill is None


def test_pivot_sum_header_matches_rule_column() -> None:
workbook = Workbook()
sheet = workbook.active
sheet["A1"] = "SUM(sales)"
sheet["A2"] = 12
raw = io.BytesIO()
workbook.save(raw)
styled = apply_conditional_formatting(
raw.getvalue(),
[
{
"column": "sales",
"operator": ">",
"targetValue": 8,
"colorScheme": "#FF0000",
}
],
)
rules = _cf_rules(load_workbook(io.BytesIO(styled)).active)
assert len(rules) == 1
assert "A2" in rules[0][0]


def test_cell_bar_range_operator_covers_contiguous_matches() -> None:
styled = apply_conditional_formatting(
_workbook_with_sales(1, 5, 15),
[
{
"column": "sales",
"operator": "< x <",
"targetValueLeft": 0,
"targetValueRight": 10,
"objectFormatting": "CELL_BAR",
"colorScheme": "#5AC189",
}
],
)
data_bars = [
rng
for rng, rule in _cf_rules(load_workbook(io.BytesIO(styled)).active)
if rule.type == "dataBar"
]
assert data_bars == ["A2:A3"]


def test_cell_bar_without_operator_covers_full_column() -> None:
styled = apply_conditional_formatting(
_workbook_with_sales(12.5, 5, 20),
[
{
"column": "sales",
"operator": None,
"objectFormatting": "CELL_BAR",
"colorScheme": "#5AC189",
}
],
)
data_bars = [
rng
for rng, rule in _cf_rules(load_workbook(io.BytesIO(styled)).active)
if rule.type == "dataBar"
]
assert data_bars == ["A2:A4"]


def test_color_scale_without_bounds_uses_min_max() -> None:
styled = apply_conditional_formatting(
_workbook_with_sales(10),
[{"column": "sales", "operator": None, "colorScheme": "success"}],
)
scale = next(
rule
for _, rule in _cf_rules(load_workbook(io.BytesIO(styled)).active)
if rule.type == "colorScale"
)
assert scale.colorScale.cfvo[0].type == "min"
assert scale.colorScale.cfvo[1].type == "max"


def test_polish_explore_xlsx_table_cf_on_xlsxwriter_workbook() -> None:
"""Explore Table download writes xlsxwriter; polish must still see data rows."""
df = pd.DataFrame({"sales": [10, 50, 5], "region": ["west", "east", "north"]})
polished = polish_explore_xlsx(
df_to_excel(df, index=False),
df,
{
"viz_type": "table",
"conditionalFormatting": [
{
"column": "sales",
"operator": ">",
"targetValue": 8,
"colorScheme": "#FF0000",
}
],
"show_cell_bars": False,
"column_config": {
"sales": {
"d3NumberFormat": ",.1f",
"horizontalAlign": "right",
}
},
},
)
sheet = load_workbook(io.BytesIO(polished)).active
assert sheet.max_row == 4
assert sheet["A2"].value == 10
assert sheet["A3"].value == 50
rules = _cf_rules(sheet)
assert len(rules) == 1
assert rules[0][1].type == "cellIs"
assert rules[0][1].operator == "greaterThan"
assert "A2" in rules[0][0]
assert sheet["A2"].number_format == "#,##0.0"
assert sheet["A2"].alignment.horizontal == "right"
assert sheet["B2"].alignment.horizontal is None


def test_polish_explore_xlsx_default_cell_bars_on_xlsxwriter() -> None:
df = pd.DataFrame({"sales": [10, 20], "region": ["west", "east"]})
polished = polish_explore_xlsx(
df_to_excel(df, index=False),
df,
{
"viz_type": "table",
"show_cell_bars": True,
"conditional_formatting": [],
},
)
sheet = load_workbook(io.BytesIO(polished)).active
data_bars = [rng for rng, rule in _cf_rules(sheet) if rule.type == "dataBar"]
assert data_bars == ["A2:A3"]


def test_polish_explore_xlsx_skips_unrelated_viz() -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test docstrings

Ten new test functions (lines 209, 214, 222, 244, 265, 288, 310, 330, 383, 399) have no docstring, while sibling tests in this same file document intent (e.g. test_value_matches_rule_comparators, test_polish_explore_xlsx_table_cf_on_xlsxwriter_workbook). BITO rule 12148 requires docstrings on all new test functions. Add one-line docstrings stating scenario and expected outcome. (BITO.md)

Code Review Run #214642


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

@codeant-ai-for-open-source

codeant-ai-for-open-source Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

CodeAnt  CodeAnt PR Risk

🟢  Low Risk

  • The PR appears safe to merge; percentage display, verbose-header matching, comparator-filtered bars, configured scale bounds, and datasource format fallbacks are present.

Assessed commit: 6f447863fcd2

CICD Bot added 2 commits October 6, 2026 11:18
…ting-export' into feature/excel-conditional-formatting-export
@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 29, 2026
bazhil and others added 2 commits November 3, 2026 16:31
… export

Honor datasource number/currency formats when Table column_config is empty, and omit ColorScale cfvo values unless the bound type is numeric.
@github-actions github-actions Bot removed the requires:rebase Requires rebasing on top of current master label Nov 3, 2026
TypedDict from datasource.data is not dict[str, Any]; Mapping unblocks mypy on pre-commit.
…/excel-conditional-formatting-export

Keep Mapping types for ExplorableData so mypy still accepts datasource.data on the Excel XLSX path.
Use the default export and children render function instead of a named AutoSizer/renderProp, which crashed Explore and dashboard chart lists. Skip the purge-audit coordination seed when the sentinel already exists so db upgrade can run.
@github-actions github-actions Bot added the risk:db-migration PRs that require a DB migration label Nov 3, 2026
The default/children import crashed Explore on react-virtualized-auto-sizer v2. Also drop the unrelated purge_audit migration guard from this branch.
@github-actions github-actions Bot removed the risk:db-migration PRs that require a DB migration label Nov 4, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant