Repository navigation
FE-614 - Quick filter every search attribute value in the configurable tables - #3939
Open
rossedfort wants to merge 16 commits into
Open
rossedfort wants to merge 16 commits into
rossedfort wants to merge 16 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
rossedfort
force-pushed
the
rossedfort/fe-614-all-search-attribute-values-should-have-a-quick-filter
branch
from
September 17, 2026 17:50
692e1d1 to
89a24d9
Compare
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
force-pushed
the
rossedfort/fe-614-all-search-attribute-values-should-have-a-quick-filter
branch
from
September 29, 2026 16:52
89a24d9 to
1e7725f
Compare
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
marked this pull request as ready for review
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).
max-w-fit without min-w-0 resolved to the date picker's max-content width, so the popup opened at roughly 740px against the filter chip's 390px (FE-614).
Both conflicts came from FE-613's not in operator (#3961) landing in code this branch had moved. Its listConditionalOptions change applies to filter-editor, which now owns that form, and its KeywordList spec selects the list input by an id the extraction namespaced so two editors can be open at once.
Every filterable and copyable cell reserved 64px of right padding for buttons that only appear on hover, costing 328px of the 1598px workflows table. The buttons now portal into a small surface above the cell, so no column pads for them and the value is never covered. Leaving the cell for the buttons dispatches both events in one task and Svelte batches the updates, so they survive the reach without a hide delay. A delay would trail popovers behind the pointer across a row (FE-614).
The popup appeared only where the default operator was not `=`, which left Keyword, Text and Bool columns applying a guess in one click. It now appears everywhere except a status column, which keeps its single click because a status filter renders as a multi-select chip rather than the operator form - isTextFilter refuses status filters outright, so the editor would fall through to a generic equals select. The per-type conditional stays, but now only seeds the operator the popup opens with rather than deciding whether one opens (FE-614).
The buttons floated above the cell, which left it ambiguous which row and value they would act on. They sit over the right edge of their own cell again, which can cover the tail of a long value and is the clearer trade. They stay absolute, so no column reserves width for them, and being back inside the cell removes the portal and the hover bridging it needed. The popover opens without taking focus, so Enter could not reach the form on its own. It now applies from anywhere in the popover, leaving Enter alone where it already means something: a toggle, Apply itself, or committing a keyword chip (FE-614).
#3987 added custom search attribute rendering and filtering to the activities and Nexus tables using the per-column structure this branch replaced, so its intent is ported rather than its diff. Its SearchAttributeValue component now renders the per-type display all three cells were duplicating, the two value getters fall through to a row's indexed fields, and activities take the decoded ActivityExecutionListInfo. Its isFilterableCustomSearchAttribute is deliberately not adopted: it narrows custom attributes to Keyword and Text, which this branch supersedes by gating on the search attribute store so every custom type can be filtered.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description & motivation 💭
Only Keyword and Text cells offered a quick filter button, so a column like
StartTime,HistoryLengthorTemporalChangeVersioncould be read in the tablebut not filtered from it. Every search attribute value in the configurable tables
can now seed a filter from the cell it is displayed in.
Clicking a cell's filter button opens a popover anchored to that cell, showing the
operators valid for the attribute's type with the cell's own value prefilled.
Nothing is applied until Apply (or Enter). The exception is a status column, which
still applies
=in a single click: a status filter renders as the multi-selectchip rather than the operator form, and
isTextFilterrefuses status filtersoutright, so the editor would otherwise fall through to a generic equals select.
That is the whole rule —
requiresOperatorChoiceasks whether the attribute is astatus, not what its default operator happens to be.
The popover reuses the filter bar's own form rather than reimplementing it. The
per-type operator toggles, date and time pickers, chip input and timezone handling
lived inside
dropdown-filter-chip.svelte; they move tofilter-editor.svelte,which the chip and the cell popover both render. Input ids are namespaced by the
caller, since a chip and a cell popover can now be open at the same time.
The rest is removing near-duplicate implementations. Workflows, activities and
Nexus operations each had their own
filterable-table-cell.svelteand each table'sbody cell repeated the same derivation — label to attribute, attribute to type, row
to value, value to filter. Those collapse into:
$lib/utilities/query/quick-filter.ts— the pure logic. Choosing the operator apopover opens on, coercing a cell value into something the query grammar accepts,
and toggling a filter. Values arrive in whatever shape the API returned, so a
datetime may be an ISO string, an epoch number, a
Dateor a protobufTimestamp; normalizing is per-type rather than aString()call. A value thatcannot be filtered on returns
null, which is how a cell decides to render nobutton.
quick-filter-table.tsandquick-filter-cell.svelte— one resolver and onecell. A table supplies a column config and a value getter and needs nothing else.
column-search-attributes.tsper table, mapping a column label to its searchattribute and to the value to filter by.
ExecutionDurationis filterable, which needs care: it is typedIntbut carries aduration, and the displayed text is not reusable as a value.
formatDistanceAbbreviated(workflows, Nexus) delimits units with a space, while
formatDurationAbbreviated(activities) falls back to
', '— stripping spaces from the latter yields5m,10s,which is not a duration. The value is built from the row's own data instead, truncating
rather than rounding so a
>=filter on a row's duration still matches that row.Filters are built on click rather than during render, since constructing one eagerly
per cell would mint a filter id for every row that is never used. Applying replaces
any existing filter on the same attribute rather than appending, so clicking down a
column moves the filter instead of accumulating contradictory clauses, and a
multi-status OR group collapses to the status that was clicked.
The buttons themselves no longer cost layout. Every filterable and copyable cell
previously reserved 64px of right padding for buttons that only appear on hover —
328px of a 1598px workflows table, about a fifth of it, for something invisible until
you hover. They are absolutely positioned over the right edge of their own cell, so
no column reserves width and it stays obvious which value they act on, at the cost of
covering the tail of a long value.
Screenshots (if applicable) 📸
Design Considerations 🎨
The operator a popover opens on is still type-derived —
>=for datetimes,inforkeyword lists,
>=for measures likeHistoryLength,HistorySizeBytes,StateTransitionCountandExecutionDuration,=otherwise. Since the user nowconfirms before anything is applied, that is a starting point rather than a guess the
table makes on their behalf.
Two things worth a look in review:
unless the pickers are touched. The pickers only carry seconds, so recomputing from
them would round
…01.726484047Zto…01.000Z.separate implementation for the SDK column, which applies
SdkNameandSdkVersionas one click. Its
StartTimecolumn is therefore the one place a Datetime stillfilters in one click, with
>=.Testing 🧪
How was this tested 👻
Unit tests cover
quick-filter.ts(operator choice, value coercion per attributetype, toggle and active state, and a round trip from filter to query string and back
for every type), the duration conversion,
quick-filter-table.ts, and the workflowscolumn mapping. Integration tests cover the popover end to end in
workflows-search-attribute-filter.desktop.spec.ts: a column asking for an operatorand applying nothing until confirmed, applying with a chosen operator, Status
filtering in one click, Enter applying, and that no column reserves width for the
buttons.
Steps for others to test: 🚶🏽♂️🚶🏽♀️
Start,History Length,Execution Duration,Change Version, a custom bool or int search attribute.with the value prefilled and an operator preselected, and nothing should be
filtered yet.
Startcell with Before should yield<=on the raw timestamp.Statuscell and click. It should filter in one click with no popover,and clicking again should clear it.
second one being added.
Parent Namespaceshows no filter button, and that no column is paddedfor the buttons when nothing is hovered.
Checklists
Draft Checklist
Merge Checklist
mainafter FE-734 - Add resizable columns to the configurable tables #3925 mergedmainmerged in, including Add a NOT IN operator for KeywordList search attributes #3961'snot inoperatorIssue(s) closed
Closes FE-614
Docs
Any docs updates needed?
None.