Skip to content

feat: Persist and read variable-keyed filter state - #2964

Open
pulpdrew wants to merge 1 commit into
drew/name-based-var-statefrom
drew/name-based-var-state-2
Open

feat: Persist and read variable-keyed filter state#2964
pulpdrew wants to merge 1 commit into
drew/name-based-var-statefrom
drew/name-based-var-state-2

Conversation

@pulpdrew

@pulpdrew pulpdrew commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Part 2/2 in enabling variable-name-based dashboard filter state. This part updates the dashboard filter hooks to read and write state in the new variable-keyed format.

Why

Historically, dashboard filter selection state has been persisted (in the URL, dashboard documents, and exports) as keyed by the filter's expression, in the same format used by search-page filters. This has two problems:

  1. Filters that share an expression (eg. ServiceName from two different sources) must necessarily share a selection state despite being two dropdowns in the UI.
  2. Future "static custom values" filters will have no expression (since they're not queried) and thus would have no way to key their state

What

This change will persist dashboard filter selection state (that is, what values are selected in the filter's drop-down) based on the variableName of the filter, when it has one.

  • Existing expression-keyed filter state will be migrated on write. Existing URLs and saved filter states continue working.
  • Filters that are not variable-enabled continue being written in expression-keyed format
  • Variable-keyed state takes precedence over expression-keyed state, when both may apply to a single filter.

Screenshots or video

Updated banner showing URL state that doesn't correspond to the declared filters

Screenshot 2026-08-21 at 12 59 06 PM

How to test

  • Create a dashboard and add some filters. Make some of them variable enabled and some of them not
  • Select values for the filters
  • Try saving the default filter values, importing/exporting the dashboard, sharing the link, etc

Use a URL Decoder if you want to inspect the URL

References

  • Linear Issue: HDX-5052
  • Related PRs:

@changeset-bot

changeset-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6eb83ac

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/common-utils Patch
@hyperdx/api Patch
@hyperdx/app Patch
@hyperdx/otel-collector Patch

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Aug 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 25, 2026 11:56am
hyperdx-storybook Ready Ready Preview Aug 25, 2026 11:56am

Request Review

@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates dashboard filter selections toward variable-name-keyed persistence while preserving expression-keyed compatibility. The disjoint-selection repair remains incomplete because an empty intersection is still serialized as no constraint.

  • Reads and writes variable-keyed filter state for variable-enabled filters.
  • Keeps legacy expression-keyed state compatible and migrates representable selections on write.
  • Keys UI selections by filter ID so filters sharing an expression can remain independent.
  • Extends ignored-state warnings and dashboard filter regression coverage.

Confidence Score: 4/5

The PR is not yet safe to merge because disjoint sibling filter selections still remove the intended constraint from dependent dropdown queries.

The attempted intersection fix produces an empty inclusion set for disjoint selections, and the downstream filter serializer drops that state as unconstrained, preserving the previously reported incorrect dropdown behavior.

Files Needing Attention: packages/app/src/hooks/useDashboardFilterValues.tsx

Important Files Changed

Filename Overview
packages/app/src/hooks/useDashboardFilters.tsx Implements variable-keyed persistence, legacy migration, per-filter selection state, and source-aware query broadcasting without a newly eligible blocking issue.
packages/app/src/hooks/useDashboardFilterValues.tsx Converts faceted filtering to filter-ID selections, but disjoint sibling inclusions still become an omitted constraint.
packages/app/src/DBDashboardPage.tsx Integrates the new filter-state API and expands ignored-filter warnings; the previously reported semantic warning variant is fixed.
packages/app/src/utils/queryParsers.ts Extends URL parsing for the variable-keyed filter-value representation and legacy compatibility.
packages/app/tests/e2e/features/dashboard-filter-value-format.spec.ts Adds end-to-end coverage for variable-keyed persistence and compatibility flows.
packages/app/tests/e2e/features/dashboard-table-linking.spec.ts Extends dashboard-linking coverage; the dynamic URL regex uses a hexadecimal ObjectId and does not create a realistic regex-security path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Filter selections keyed by filter ID] --> B[Combine sibling selections]
  B --> C{Inclusions overlap?}
  C -->|Yes| D[Emit intersected predicate]
  C -->|No| E[Empty included set]
  E --> F[Serializer drops constraint]
  F --> G[Dependent dropdown is unfiltered]
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (8): Last reviewed commit: "feat: Persist and read variable-keyed fi..." | Re-trigger Greptile

Comment thread packages/app/src/hooks/useDashboardFilterValues.tsx
Comment thread packages/app/src/hooks/useDashboardFilters.tsx
Comment thread packages/app/src/DBDashboardPage.tsx
@pulpdrew pulpdrew changed the title Drew/name based var state 2 feat: Persist and read variable-keyed filter state Aug 21, 2026
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 315 passed • 1 skipped • 1155s

Status Count
✅ Passed 315
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@github-actions

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 490 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 7
  • Production lines changed: 490 (+ 1326 in test files, excluded from tier calculation)
  • Branch: drew/name-based-var-state-2
  • Author: pulpdrew

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Part 2/2 of the variable-keyed dashboard filter state migration. The change is well-structured, carries good inline documentation, and ships substantial unit + e2e coverage. No ship-blockers found. Two correctness edge cases around filters that share a SQL expression remain, both narrow and both introduced by this diff's new intersectSelections / rebuildEntries logic — safe to merge, but worth addressing or explicitly deferring.

✅ No critical issues found.

🟡 P2 -- recommended

  • packages/app/src/hooks/useDashboardFilterValues.tsx:79 -- intersectSelections collapses two non-empty disjoint sibling inclusions to an empty included set, which downstream key-value lookups read as "no inclusion constraint" (match all) rather than "match nothing".
    • Fix: Distinguish "both empty → unconstrained" from "non-empty but disjoint → match nothing" (e.g. carry a match-nothing sentinel) so a dependent third dropdown shows no options instead of every option.
    • correctness, testing, previous-comments
  • packages/app/src/hooks/useDashboardFilters.tsx:82 -- On the first post-migration edit of a plain filter, rebuildEntries re-resolves a variable-enabled sibling on the same expression off the just-written byExpression value, so an untouched sibling dropdown silently adopts the edited filter's new selection.
    • Fix: Resolve each variable-enabled filter against the pre-mutation parsed state (or the current selectionByFilterId) so a plain-filter edit cannot bleed into a sibling that shares its expression.
    • correctness

Reviewers (3): correctness, testing, previous-comments.

Testing gaps:

  • No test drives the disjoint (empty-intersection) branch of intersectSelections; the only sibling-intersection test uses overlapping sets.
  • No test guards the load-bearing rebuildEntries two-loop ordering for a variable-enabled filter colliding with a plain filter on the same expression in byExpression.

Comment thread packages/app/src/hooks/useDashboardFilterValues.tsx
Comment on lines +79 to +84
included:
a.included.size === 0
? b.included
: b.included.size === 0
? a.included
: new Set([...a.included].filter(v => b.included.has(v))),

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.

P1 Disjoint selections drop the constraint

When linked sibling filters sharing an expression have disjoint non-empty selections, intersectSelections produces an empty inclusion set that downstream serialization treats as no constraint, causing the dependent dropdown to display unfiltered values instead of no matching values.

Knowledge Base Used:

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

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

Labels

review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant