Skip to content

feat(search): editable filter pills#2455

Open
alex-fedotyev wants to merge 2 commits into
mainfrom
alex/hdx-4326-editable-filter-pills
Open

feat(search): editable filter pills#2455
alex-fedotyev wants to merge 2 commits into
mainfrom
alex/hdx-4326-editable-filter-pills

Conversation

@alex-fedotyev

@alex-fedotyev alex-fedotyev commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Make the active filter pills under the search bar editable in place. Clicking an included or excluded pill opens a small action menu to copy the value, flip the filter's polarity (include vs exclude), or switch to a different value of the same field, reusing the EventTag popover pattern and the actions useSearchPageFilterState already exposes.

Implements HDX-4326. This is the full feature in one PR. It previously spanned this PR and #2457; I folded #2457 in here and closed it, since the pill menu and the value picker share the same component and hook and do not stand alone.

Summary

  • Clicking an included or excluded pill opens a Popover menu with three actions: copy the value, flip polarity (Exclude on an included pill, Include on an excluded one), and a searchable value picker to switch the filter to another value of the same field.
  • The polarity flip happens in place through setFilterValue(field, value, 'include' | 'exclude'), so a value moves between included and excluded without a remove and re-add.
  • The value swap goes through a new replaceFilterValue helper on useSearchPageFilterState, which does it in one state update so the search query runs once. Two setFilterValue calls would each emit onFilterChange and run the query twice for one action. Polarity is preserved across the swap.
  • Picker values come from the shared useGetKeyValues hook, fetched only while the menu is open (enabled: opened) and scoped to the field with where/filters cleared, so it lists all of the field's values (like the sidebar facet's "Show all values") rather than only the currently matching one.
  • The one-click remove (the x on each pill) is unchanged. Range and not-applied pills keep their remove-only behavior.
  • DBSearchPage passes the active source's filtersChartConfig to the pills for the value query.

Why

Today the only thing you can do to an active pill is remove it. Adjusting a filter, say flipping ServiceName = checkout to ServiceName != checkout or changing its value, means deleting the pill and rebuilding it from the sidebar. The menu reuses primitives that already ship in search, so it adds the affordance with no new state and no new design.

Test plan

  • make ci-lint (eslint + tsc) on @hyperdx/app
  • make ci-unit: ActiveFilterPills.test.tsx + searchFilters.test.ts (80 tests) cover open-on-click, both polarity flips, copy, the value picker populating from useGetKeyValues and selecting a value calling replaceFilterValue with the right args and polarity, the one-click remove not opening the menu, range pills not opening a menu, and replaceFilterValue emitting onFilterChange exactly once
  • Playwright: drove the menu (open, flip polarity so the operator switches between = and !=, copy, open the value picker and switch values) in light and dark themes
  • States: the value picker shows a loading message while fetching and an empty message when there are no values; the no-filters case renders nothing; a clipboard failure on copy surfaces the existing error notification
  • Layout: the pill row keeps its existing wrap at narrow viewport widths; the menu is an overlay popover

Note

This lands as one review/tier-3 PR. It is one cohesive feature (the menu and the value picker share ActiveFilterPills.tsx and useSearchPageFilterState), so I kept it together rather than as a stacked tier-2 pair. A separate commit also replaces six pre-existing em-dashes in searchFilters.tsx comments with commas; that is unrelated to the feature but sits in a file this PR edits.

@changeset-bot

changeset-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3467cf2

The changes in this PR will be included in the next version bump.

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

Not sure what this means? Click here to learn what changesets are.

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

@vercel

vercel Bot commented Jun 12, 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, Comment Jun 13, 2026 1:20am
hyperdx-storybook Ready Ready Preview, Comment Jun 13, 2026 1:20am

Request Review

@github-actions github-actions Bot added the review/tier-2 Low risk — AI review + quick human skim label Jun 12, 2026
@github-actions

github-actions Bot commented Jun 12, 2026

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: 271 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: 3
  • Production lines changed: 271 (+ 385 in test files, excluded from tier calculation)
  • Branch: alex/hdx-4326-editable-filter-pills
  • Author: alex-fedotyev

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

@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds in-place editing to active filter pills: clicking an included or excluded pill opens a Mantine Popover with a value picker (Select), a copy button, and a polarity-flip button. A new replaceFilterValue hook method handles atomic value swaps in one onFilterChange emission, avoiding a double query run that two sequential setFilterValue calls would cause.

  • ActiveFilterPills: now accepts chartConfig (required) to feed useGetKeyValues inside each pill; strips where/filters from the config so the value picker lists the full field vocabulary, not just currently-matching values.
  • searchFilters.tsx: adds replaceFilterValue — removes oldValue from the target set (included or excluded) and inserts newValue in a single Immer produce call.
  • DBSearchPage.tsx: passes filtersChartConfig as the new required chartConfig prop to ActiveFilterPills.

Confidence Score: 5/5

This PR is safe to merge. Changes are well-scoped UI additions with no mutations to query execution paths beyond the new single-emission replaceFilterValue helper.

All three mutation paths (polarity flip, value replace, one-click remove) route through well-tested hook methods. The new replaceFilterValue correctly performs a single Immer produce call so onFilterChange fires exactly once. The valueChartConfig stripping of where/filters is sound and directly tested. The chartConfig prop is required and the only real caller (DBSearchPage) was updated. Twenty-two unit tests plus hook-level tests cover the new behavior comprehensively.

No files require special attention.

Important Files Changed

Filename Overview
packages/app/src/components/ActiveFilterPills.tsx Core UI change — adds Popover/Select/copy/polarity-flip affordances; correctly gates on isEditable, strips query scope from valueChartConfig, and guards rawValue nullability.
packages/app/src/searchFilters.tsx Adds replaceFilterValue — single atomic Immer update that deletes oldValue and inserts newValue in the correct set, emitting onFilterChange exactly once.
packages/app/src/DBSearchPage.tsx Passes the pre-existing filtersChartConfig to ActiveFilterPills; the fallback empty-string config when no source is selected is safe (useGetKeyValues won't fire until a pill menu is opened).
packages/app/src/components/tests/ActiveFilterPills.test.tsx Comprehensive test expansion covering menu open/close, polarity flip, copy, remove-without-open, range pill no-menu, value picker data, and replace-value preserving polarity.
packages/app/src/searchFilters.test.ts New unit tests for replaceFilterValue covering include/exclude polarity preservation and single onFilterChange emission per replace.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant FP as FilterPill
    participant AFP as ActiveFilterPills
    participant SF as useSearchPageFilterState
    participant GKV as useGetKeyValues

    U->>FP: click pill (included/excluded)
    FP->>FP: setOpened(true)
    FP->>GKV: "fetch values (enabled=true, where='', filters=[])"
    GKV-->>FP: valueOptions[]

    alt Copy value
        U->>FP: click Copy
        FP->>FP: copyTextToClipboard(pill.value)
    else Flip polarity
        U->>FP: click Include / Exclude
        FP->>AFP: onTogglePolarity()
        AFP->>SF: "setFilterValue(field, rawValue, 'include'|'exclude')"
        SF-->>AFP: filters updated
        FP->>FP: setOpened(false)
    else Replace value
        U->>FP: select new value from Select
        FP->>AFP: onReplaceValue(newValue)
        AFP->>SF: replaceFilterValue(field, oldRaw, newValue, polarity)
        SF-->>AFP: filters updated (single onFilterChange emission)
        FP->>FP: setOpened(false)
    else Remove (x button)
        U->>FP: click x (stopPropagation)
        FP->>AFP: onRemove()
        AFP->>SF: setFilterValue / clearFilter
        SF-->>AFP: filters updated
    end
Loading

Reviews (3): Last reviewed commit: "chore: replace pre-existing em-dashes in..." | Re-trigger Greptile

Comment thread packages/app/src/components/ActiveFilterPills.tsx Outdated
Comment thread packages/app/src/components/ActiveFilterPills.tsx Outdated
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 198 passed • 3 skipped • 1352s

Status Count
✅ Passed 198
❌ Failed 0
⚠️ Flaky 5
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

alex-fedotyev and others added 2 commits June 13, 2026 01:10
Make the active filter pills under the search bar editable in place,
reusing existing search primitives. Clicking an included or excluded
pill opens a small action menu (the EventTag popover pattern) with:

- Copy the value.
- Flip the filter polarity (include vs exclude) in place via
  setFilterValue, with no remove and re-add.
- Switch to a different value of the same field via a searchable
  value picker. Values come from the shared useGetKeyValues hook,
  fetched only while the menu is open and scoped to the field with
  where/filters cleared so the full value list shows. The swap goes
  through a new replaceFilterValue helper on useSearchPageFilterState
  so the query runs once and the polarity is preserved.

The one-click remove (x) on each pill is unchanged. Range and
not-applied pills keep their remove-only behavior. DBSearchPage
passes the active source's filtersChartConfig to the pills for the
value query.

Implements HDX-4326.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
prose-lint flags these on any change to the file; converting to commas.
@alex-fedotyev alex-fedotyev force-pushed the alex/hdx-4326-editable-filter-pills branch from 0993367 to 3467cf2 Compare June 13, 2026 01:17
@github-actions github-actions Bot added review/tier-3 Standard — full human review required and removed review/tier-2 Low risk — AI review + quick human skim labels Jun 13, 2026
@alex-fedotyev alex-fedotyev self-assigned this Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant