Skip to content

feat(native-filters): add configurable display format for time filters - #44237

Merged
rusackas merged 9 commits into
apache:masterfrom
Ujjwaljain16:fix/native-filter-date-display-format-33724
Sep 22, 2026
Merged

rusackas merged 9 commits into
apache:masterfrom
Ujjwaljain16:fix/native-filter-date-display-format-33724

Conversation

@Ujjwaljain16

@Ujjwaljain16 Ujjwaljain16 commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

SUMMARY

Native Time Range filters currently display evaluated ranges using raw ISO-style values (e.g. 2019-01-14), with no way to configure how they're shown. Chart-level Timestamp Format does not flow into native-filter rendering — a native filter is not owned by any single chart, so inheriting one chart's format isn't architecturally sound.

This adds an optional, filter-level Display format setting to the Time filter's Filter Settings tab. It reuses the existing D3 time-format infrastructure (D3_TIME_FORMAT_OPTIONS / getTimeFormatter) that already powers chart-level Timestamp Format controls (e.g. the Table viz's table_timestamp_format) — no new formatting system is introduced.

Design notes:

  • When unset (the default for every existing filter), display behavior is byte-for-byte identical to today.
  • The format only affects the evaluated range shown in the pill/tooltip (e.g. for Custom/Advanced frames). Human-readable expressions such as Last week or previous calendar month are never passed through the formatter.
  • time_range serialization, query semantics, and the DAYJS_FORMAT-based CustomFrame datetime picker are untouched — this is a presentation-only change.
  • UTC handling is explicit: the backend returns naive ISO datetime strings, which are parsed with extendedDayjs.utc() (not local-time parsing) before formatting, so the formatted output preserves the original wall-clock digits regardless of the browser's timezone.

Fixes #33724

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Not included in this PR — happy to add one if a reviewer would like to see the new "Display format" field in the Filter Settings tab.

TESTING INSTRUCTIONS

  1. Create or edit a dashboard native filter of type Time range.
  2. In the Filter Settings tab, set Display format to e.g. %d-%m-%Y %H:%M:%S.
  3. Choose a Custom or Advanced time range and Apply.
  4. Confirm the filter pill on the dashboard now shows the range in the configured format, while a Common/Calendar/Current selection (e.g. "Last week") still shows the human-readable label, with the formatted range only in its tooltip.
  5. Leave Display format unset on an existing filter and confirm the display is unchanged from current behavior.

Automated coverage:

  • superset-frontend/packages/superset-ui-core/test/time-comparison/fetchTimeRange.test.ts — custom format applied to both range endpoints, -∞/∞ placeholders preserved, human-readable values left untouched, end-to-end fetchTimeRange() with a format, and a UTC-safety regression test (verified non-vacuous: reverting the UTC-safe parsing to local-time parsing makes this test fail with a reproducible timezone-shifted value under the suite's fixed America/New_York test timezone).
  • superset-frontend/src/explore/components/controls/DateFilterControl/tests/DateFilterLabel.test.tsx — the configured format reaches the evaluated-range tooltip while the human-readable pill stays untouched.
npm test -- packages/superset-ui-core/test/time-comparison/fetchTimeRange.test.ts src/explore/components/controls/DateFilterControl/tests/DateFilterLabel.test.tsx
npm test -- src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx src/explore/components/controls/DateFilterControl/tests/

All 83 tests pass. npx oxlint/npx oxfmt pass with no new warnings on the changed files. npm run type surfaces pre-existing TS6305 "stale project-reference build output" errors across the whole repo (reproducible on unrelated files on a clean checkout without this change) — unrelated to this PR.

ADDITIONAL INFORMATION

  • Has associated issue: Report - Date format in filters not using Timestamp Format #33724
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • 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

Native Time Range filters currently display evaluated ranges using
raw ISO-style values (e.g. 2019-01-14), with no way to configure how
they're shown. This adds an optional 'Display format' setting to the
Time filter's Filter Settings tab, reusing the existing D3
time-format infrastructure (D3_TIME_FORMAT_OPTIONS/getTimeFormatter)
that already powers chart-level Timestamp Format controls.

The format is presentation-only: time_range serialization, query
semantics, and DAYJS_FORMAT-based CustomFrame parsing are untouched.
When unset, display behavior is byte-for-byte identical to today.

Fixes apache#33724
@bito-code-review

bito-code-review Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #f0663d

Actionable Suggestions - 0
Additional Suggestions - 3
  • superset-frontend/packages/superset-ui-core/src/time-comparison/fetchTimeRange.ts - 1
    • dateFormat dropped in comparison branch · Line 110-110
      The new `dateFormat` is applied only in the non-shift branch (line 110). In the shift branch, `formatTimeRangeComparison` (lines 113-125) is called without it and its signature (lines 67-81) doesn't accept it, so the custom date format is silently ignored for comparison ranges. Thread `dateFormat` through `formatTimeRangeComparison` for consistent output.
  • superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx - 1
    • Missing data mask reset · Line 680-690
      `onDisplayFormatChanged` updates `controlValues.displayFormat` but, unlike the sibling `onOperatorTypeChanged` (line 674), does not reset `defaultDataMask: null`. The previously applied data mask retains the old formatted value, so the filter's applied value/display won't reflect the new format until the user manually re-applies it. Add `defaultDataMask: null` to the `setNativeFilterFieldValues` payload for consistency.
  • superset-frontend/src/filters/components/Time/controlPanel.ts - 1
    • Missing filterOption consistency · Line 63-77
      The new displayFormat SelectControl omits the `filterOption` that the shared `x_axis_time_format` control provides (sharedControls.tsx:344-345). Without it, `SelectControl.handleFilterOptions` returns `true` for all options (SelectControl.tsx:259), so this freeForm dropdown won't filter as the user types. Consider adding the same `filterOption` for consistency, using the shared control's signature: `filterOption: ({ data: option }, search) => option.label.includes(search) || option.value.includes(search)`.
Review Details
  • Files reviewed - 10 · Commit Range: 167740c..167740c
    • superset-frontend/packages/superset-ui-core/src/time-comparison/fetchTimeRange.ts
    • superset-frontend/packages/superset-ui-core/test/time-comparison/fetchTimeRange.test.ts
    • superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
    • superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx
    • superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
    • superset-frontend/src/explore/components/controls/DateFilterControl/tests/DateFilterLabel.test.tsx
    • superset-frontend/src/explore/components/controls/DateFilterControl/types.ts
    • superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx
    • superset-frontend/src/filters/components/Time/controlPanel.ts
    • superset-frontend/src/filters/components/Time/types.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ 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-frontend/src/filters/components/Time/TimeFilterPlugin.tsx
@codecov

codecov Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.00000% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.92%. Comparing base (9976e08) to head (4e405e6).

Files with missing lines Patch % Lines
...nts/controls/DateFilterControl/DateFilterLabel.tsx 69.44% 11 Missing ⚠️
...onfigModal/FiltersConfigForm/FiltersConfigForm.tsx 53.84% 6 Missing ⚠️
...ontend/src/filters/components/Time/controlPanel.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #44237      +/-   ##
==========================================
- Coverage   80.25%   75.92%   -4.33%     
==========================================
  Files        2927     2926       -1     
  Lines      173273   173048     -225     
  Branches    40173    40154      -19     
==========================================
- Hits       139057   131394    -7663     
- Misses      31621    38975    +7354     
- Partials     2595     2679      +84     
Flag Coverage Δ
hive ?
javascript 75.71% <70.00%> (+0.03%) ⬆️
mysql ?
postgres ?
presto ?
sqlite ?

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.

…d extension contract

Address review feedback on apache#44237:

- DateFilterLabel.tsx: both fetchTimeRange() call sites lacked
  protection against out-of-order resolution. If value/displayFormat
  changed again before a prior request resolved, the older result
  could overwrite the current pill/tooltip/validation state. Add a
  shared monotonic request id (useRef) and ignore any response whose
  request id is no longer the latest.
- ui-overrides/types.ts: the 'filter.dateFilterControl' extension
  point's DateFilterControlProps (a separate, independently
  maintained contract from the real component's props) did not
  declare displayFormat, so a custom registered DateFilterControl
  couldn't type or discover it. Add the field.
@bito-code-review

bito-code-review Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #e6003f

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 167740c..99bf4a7
    • superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts
    • superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ 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

@aminghadersohi aminghadersohi 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.

Verified at 99bf4a7: the shared request-id guard regresses the pill on an ordinary Apply (reproduced; correct pre-PR). The rest is additive — no existing fetchTimeRange caller passes a 4th positional arg, and the formatDateEndpoint rewrite is behaviour-preserving.

Comment thread superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx
… DateFilterLabel

The stale-fetch guard added a single shared latestRequestId ref for both
the value-driven fetch and the debounced draft-preview fetch. Editing the
draft and clicking Apply before the debounce fired let the leftover draft
fetch bump the shared counter, causing the Apply-triggered fetch's result
to be discarded and the pill to keep showing the stale value.

Split the guard into latestValueRequestId and latestDraftRequestId so each
effect only guards against staleness within itself.
@netlify

netlify Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

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

…ate guard

Existing displayFormat tests only covered human-readable values ('Last
week', 'No filter') that short-circuit before reaching the date-format
branch, so swapping the isValid() check for an unconditional true left
every test green while unparseable endpoints rendered as 'Invalid Date'.

Add a case with an unparseable colon-separated range to exercise the
isValid() guard directly.
Neither TimeFilterPlugin nor getControlItemsMap had a test guarding the
displayFormat prop's path: deleting displayFormat={props.formData.displayFormat}
in TimeFilterPlugin, or dropping its exclusion from the generic
renderTrigger checkbox loop in getControlItemsMap, left every existing
test green.

Add a TimeFilterPlugin test asserting the prop reaches the underlying
date filter control, and a getControlItemsMap test asserting displayFormat
is excluded from the generic checkbox control items (it needs its own
custom control, not an auto-generated checkbox).
…Data

The displayFormat wiring review only covered formData -> control (the
TimeFilterPlugin prop pass-through); the first hop, controlValues ->
formData via getFormData's generic spread, had no test. Dropping or
excluding displayFormat from that spread would have passed every
existing test silently.
@Ujjwaljain16

Ujjwaljain16 commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor Author

The E2E jobs (cypress-matrix, playwright-tests) are failing fast (~90s) during the Import test data step with:

ERROR [flask_migrate] Error: Multiple head revisions are present for given argument 'head'

This PR touches zero migration files, and enforce-single-migration-head already passes on this branch, so it doesn't look like a real migration conflict — more likely a transient cache/DB-state issue in that step. I don't have write access to apache/superset to re-run the failed jobs myself. @aminghadersohi could you re-run them when you get a chance?

@bito-code-review

bito-code-review Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #a27094

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx - 1
    • Stale draft fetch race · Line 244-248
      Splitting the shared counter into `latestValueRequestId`/`latestDraftRequestId` removes cross-effect ordering for the shared state both effects write (`evalResponse`, `validTimeRange`, `lastFetchedTimeRange`). A debounced draft fetch still in flight when the user clicks Apply can now resolve after the value fetch and overwrite its result. Capture `latestValueRequestId` at draft-fetch start and bail in `.then()` if it changed.
Review Details
  • Files reviewed - 6 · Commit Range: 99bf4a7..f43c0a6
    • superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
    • superset-frontend/src/explore/components/controls/DateFilterControl/tests/DateFilterLabel.test.tsx
    • superset-frontend/packages/superset-ui-core/test/time-comparison/fetchTimeRange.test.ts
    • superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
    • superset-frontend/src/filters/components/Time/TimeFilterPlugin.test.tsx
    • superset-frontend/src/dashboard/components/nativeFilters/utils.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ 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

@aminghadersohi aminghadersohi 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.

The 8 red E2E legs are a genuine double alembic head (88a01c781622, c7f53d184ea2) inherited from master — 4dc0b9fe0 had one. Not yours to fix and a re-run won't clear it, though enforce-single-migration-head passes anyway.

@gabotorresruiz gabotorresruiz 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.

LGTM, thanks for this feature Ujjwal, and for the quick, thorough follow ups on every review thread.

I checked out bbb537c and verified it locally rather than just reading the diff:

  • All six touched jest suites pass (fetchTimeRange, the full DateFilterControl/tests directory, TimeFilterPlugin, getControlItemsMap, nativeFilters/utils).
  • The new coverage is non vacuous. I mutation tested it: swapping extendedDayjs.utc() for local time parsing, dropping the displayFormat prop in TimeFilterPlugin, re sharing the request id counter between the two fetch effects, and relaxing the isValid() guard each make exactly one of the new tests fail.
  • This stays presentation only: onSave emits the raw timeRangeValue and the data mask carries only time_range, so extra_form_data and query semantics are untouched.
  • Backward compat and export/import are safe: with displayFormat unset the calls collapse to the pre PR defaults, and controlValues round trips through native_filter_configuration (fields.List(fields.Dict()) in superset/dashboards/schemas.py) with no schema change needed.

The red E2E legs are the master inherited double alembic head Amin already diagnosed, not this change.

@rusackas
rusackas merged commit c4943a0 into apache:master Sep 22, 2026
83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Report - Date format in filters not using Timestamp Format

4 participants