Skip to content

FE-614 - Quick filter every search attribute value in the configurable tables - #3939

Open
rossedfort wants to merge 10 commits into
mainfrom
rossedfort/fe-614-all-search-attribute-values-should-have-a-quick-filter
Open

rossedfort wants to merge 10 commits into
mainfrom
rossedfort/fe-614-all-search-attribute-values-should-have-a-quick-filter

Conversation

@rossedfort

@rossedfort rossedfort commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Description & motivation 💭

Only Keyword and Text cells offered a quick filter button, so a column like
StartTime, HistoryLength or TemporalChangeVersion could be read in the table
but not filtered from it. Every search attribute value in the configurable tables
can now seed a filter with an operator appropriate to its type.

The work is mostly about removing three near-duplicate implementations. Workflows,
activities and Nexus operations each had their own filterable-table-cell.svelte
that hardcoded the keyword case, and each table's body cell decided per-column
whether a filter button appeared. Those are replaced by:

  • $lib/utilities/query/quick-filter.ts — the pure logic. Picking the default
    conditional for a type (lifted out of search-attribute-menu.svelte, which now
    imports it rather than keeping a private copy), coercing a cell value into
    something the query grammar accepts, and toggling a filter on or off. Values
    reach a cell in whatever shape the API returned — a datetime may be an ISO
    string, an epoch number, a Date or a protobuf Timestamp — so normalizing is
    per-type rather than a String() call. A value that cannot be filtered on
    returns null, which is how a cell decides to render no button at all.
  • quick-filter-table-cell.svelte — one cell shared by all three tables plus the
    workers table.
  • A column-search-attributes.ts per table, mapping a column label to its search
    attribute and to the value to filter by. Keeping this as data makes the
    unfilterable columns explicit: Parent Namespace is not indexed and
    Execution Duration is derived from two timestamps, so neither has a value to
    filter on.

Filters are built on click rather than during render — constructing one eagerly
per cell would mint a filter id for every row that is never used. Toggling replaces
any existing filter on the same attribute instead of appending, so clicking down a
column moves the filter rather than accumulating contradictory clauses, and the
button reflects the active state only when the attribute has exactly one filter
matching that value.

Screenshots (if applicable) 📸

Design Considerations 🎨

Datetime columns filter with >= and keyword lists with in, matching what the
search attribute menu already picks for those types. Worth confirming >= is the
useful default for clicking a StartTime cell — the alternative is an exact =,
which is precise but rarely what someone wants from a timestamp.

Testing 🧪

How was this tested 👻

  • Manual testing
  • E2E tests added
  • Unit tests added

Unit tests cover quick-filter.ts (value coercion per attribute type, toggle and
active-state behavior) and the workflows column-search-attributes.ts mapping.

Steps for others to test: 🚶🏽‍♂️🚶🏽‍♀️

  1. Open the workflows list on a namespace with a few hundred executions.
  2. Add columns of varied types — Start, History Length, Versioning Behavior,
    Change Version, a custom bool or int search attribute.
  3. Hover a cell in each and click the filter button. The query should gain a clause
    using an operator suited to the type (>= for datetimes, in for keyword lists,
    = otherwise), and the button should render as active.
  4. Click the same button again — the clause should be removed.
  5. Click a different row in the same column — the clause should move to that value
    rather than a second clause being added.
  6. Confirm Parent Namespace and Execution Duration show no filter button.
  7. Repeat on the activities, Nexus operations and workers tables.

Checklists

Draft Checklist

  • Manual pass over all four tables
  • Screenshots
  • Confirm the >= default for datetime columns with Design

Merge Checklist

Issue(s) closed

Closes FE-614

Docs

Any docs updates needed?

None.

@vercel

vercel Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
holocene Ready Ready Preview Oct 1, 2026 8:45pm UTC

Request Review

@rossedfort
rossedfort force-pushed the rossedfort/fe-614-all-search-attribute-values-should-have-a-quick-filter branch from 692e1d1 to 89a24d9 Compare September 17, 2026 17:50
Base automatically changed from rossedfort/fe-734-add-resizable-columns-to-the-workflows-table to main September 17, 2026 19:48
Move getDefaultConditional out of search-attribute-menu.svelte and add the
value formatting, toggle and active-state helpers that table cells need to
build a filter for any search attribute type (FE-614).
Filterability is now derived from the column's search attribute and its type
rather than a hardcoded label list, so Datetime, Bool, Int, Double and
KeywordList values get a filter button with an operator that suits the type.
Archival keeps the columns it supported before (FE-614).
The workflows, activities and nexus tables each carried a near-identical
filterable cell, so the workflows one becomes a shared component the others can
use. Datetime values now normalize a protobuf Timestamp, Date or epoch number
to an ISO string, since a table cell can hold any of those and only the string
form belongs in a query (FE-614).
…us and workers

Activities, nexus operations and workers now derive filterability from the
column's search attribute and type against their own attribute store, so
Status, timestamps and counts get a filter button alongside the ids they
already had. Worker start time filters on the raw timestamp rather than the
string it renders (FE-614).
@rossedfort
rossedfort force-pushed the rossedfort/fe-614-all-search-attribute-values-should-have-a-quick-filter branch from 89a24d9 to 1e7725f Compare September 29, 2026 16:52
Painting the flex container that holds them meant the two round button
backdrops blended into one flat rectangle. The buttons already carry their own
backdrop, and dense cells truncate their values, so the container does not need
one (FE-614).
… type

Asserts the query each cell emits: a Status cell round trips and toggles back
off, a Start cell uses >= with the raw timestamp rather than the text the cell
renders, a multi-status group collapses to the status that was clicked, a Bool
goes in unquoted, an Int of zero is still a value, and a column with nothing in
it offers no button (FE-614).
@rossedfort
rossedfort marked this pull request as ready for review September 29, 2026 17:49
@rossedfort
rossedfort requested a review from a team as a code owner September 29, 2026 17:49
…ation

HistoryLength, HistorySizeBytes, StateTransitionCount and ExecutionDuration now
read as "at least this much" rather than an exact match, which is almost never
what is wanted of a measure. ExecutionDuration becomes filterable: it is typed
Int but carries a duration, built from each row's own data because the display
formatters delimit their units and one of them uses a comma (FE-614).
The chip's per-type operator toggles and value inputs are exactly what a
cell-anchored operator popup needs, so they move into a component both can use.
Input ids are now namespaced by the caller, since two editors can be open at
once; the chip keeps its own prefix so its selectors are unchanged (FE-614).
A Datetime, KeywordList or measure column now opens a popup on the cell with
the operators valid for its type and its own value prefilled, instead of the
table guessing. Keyword, Text and Bool still filter in one click.

The popup is anchored to the filter button, which only exists while the cell is
hovered, so the cell keeps it mounted while the popup is open. Dismissal checks
containment rather than propagation, since Svelte delegates events at the root
and the menu's stopPropagation runs on the same node as the listener. The
editor keeps a seeded timestamp as given unless the pickers are touched, which
would otherwise round it to the second (FE-614).
Each table repeated the same derivation — label to attribute, attribute to
type, row to value, value to filter — and its own copy of a label lookup that
was identical in all three. That moves into a pure resolver plus one cell that
takes a column config, so a new table needs a config and a value getter and
nothing else. The resolver gains direct tests it only had through components
before (FE-614).

This branch was successfully deployed

1 active deployment
Preview — 55146121 Deployed Oct 1, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant