feat(ui-core): add FilterSelect — unified filter dropdown primitive - #33011
Conversation
✅ PR checks passedThe linked issue has a description and all required Shipping project fields set. Thanks! |
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
✅ Playwright Results — workflow succeededValidated commit ✅ 576 passed · ❌ 0 failed · 🟡 0 flaky · ⏭️ 0 skipped · 🧰 0 lifecycle flaky PerformanceBlocking targets: ✅ met · Optimization targets: 🟡 in progress Shard-job maxima below are not the full workflow wall time; the linked run includes build, fixture, planning, and reporting. 🕒 Full workflow signal wall (to summary) 46m 27s ⏱️ Max setup 4m 24s · max shard execution 15m 17s · max shard-job elapsed before upload 20m 4s · reporting 4s 🌐 225.07 requests/attempt · 2.25 app boots/UI scenario · 26.50% common-shard skew Optimization targets still in progress:
How to debug locally# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip # view trace |
A single filter-dropdown component so app-level filter dropdowns
(SearchDropdown, QuickFilterDropdown, FilterChip, and the Collate-side
variants) can converge on one implementation. Built on react-aria via the
existing Dropdown, Input, Checkbox and Button primitives.
Features:
- trigger variants: chip (pill), button, input-style
- single/multiple selection; immediate or staged (Apply/Cancel) commits
- search box with async delegation (onSearch) or local filtering
- per-row leading icon, count addon, uniform CheckboxBase size=sm rows
- pinned null option ("No X") rendered through the same row component
- tri-state Select All over the currently displayed (filtered) rows
- merges persisted-but-unfetched selected values into the list
- resolves react-aria's 'all' selection sentinel to concrete values
Adds six core locale keys (apply, cancel, clear-all, no-data-found,
search, select-all) translated across all 20 language files; check-i18n-all
passes. 14 unit tests, Storybook stories, type-check/lint/build clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Staged selections were only reset inside react-aria's onOpenChange, which never fires for a programmatic open via the controlled isOpen prop. Move the resync into an effect on the resolved open state so both paths refresh staged from selectedValues. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Output of .github/scripts/generate_playwright_impact_map.py on current main; the committed map had drifted (Tag.spec.ts split, StyleModal spec pruning). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9680f58 to
8581c3b
Compare
App-side filter options carry already-rendered icon elements (getEntityIconWithBg, getServiceLogo), so the icon prop now takes an FC or a ReactNode (isReactComponent guard). helperText renders a muted line under the search box — the Explore page shows one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
isForwardRefComponent read component.$$typeof.toString() on any non-null object; arrays and fragments have no $$typeof, so a FilterSelect icon given as an array of nodes threw a TypeError. Optional-chain the read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy dropdown pinned its helper line under the scrollable list; only the options scroll, the helper stays visible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback: 14px rows plus a 256px popover truncated FQN-length labels; options render at 12px in a 320px popover. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed story Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The chip default made every story render the pill trigger; Explore and the migrated surfaces use the borderless button variant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- xs checkbox size on CheckboxBase/Checkbox; menu rows and Select all use it - typography prop: trigger label 'medium' (default) or 'regular'; menu rows always regular, secondary text turning primary when selected - selection count as a circular badge on the trigger instead of "Label: (N)"; bordered trigger turns brand (text + border) when a filter is active - row counts as bordered pills with locale separators (replaces Dropdown addon) - no background on selected rows — the checkbox alone conveys selection - footer: p-3, sm buttons, Clear all back to tertiary with regular weight - input variant: placeholder prop, count badge, and triggerDisplay="chips" — removable chips in the field, popover anchored to the whole field - stories rebuilt as product scenarios (trigger weight, filter bar, immediate, bordered, input forms); tests extended to 24 covering the new behavior Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MenuTrigger publishes its press-to-open props through ButtonContext, and every react-aria Button descendant consumes it. The chips field's remove buttons were aria Buttons, so each ✕ press ran onRemove *and* toggled the popover, and the trigger ref was claimed by whichever button mounted last. Make the remove controls native buttons — they never read ButtonContext — leaving the trailing aria button as the sole menu trigger. Cover both directions: removing a chip leaves the menu closed, and clicking the field trigger opens it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Immediate mode has nothing to apply, so the Apply footer never rendered and there was no way to drop a whole selection without unticking each row. Give it a quiet footer instead: the selection count on the left, Clear all on the right, disabled while nothing is selected. Adds label.count-selected and label.none-selected, translated across all 20 locales. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apply reads "Apply (2)" while values are staged and falls back to plain "Apply" once the staged set is empty, so the button says how much it is about to commit. Adds label.apply-count, translated across all 20 locales — a dedicated key rather than appending "(n)" in code, so RTL locales place the parenthetical themselves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the font-normal override on both footers' Clear all so it renders at the sm button's own medium weight, against the regular-weight selection count beside it. Both were already disabled while nothing is selected — pin that with a test over the staged footer's round trip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code Review ✅ Approved 3 resolved / 3 findingsIntroduces ✅ 3 resolved✅ Edge Case: Staged selection not resynced on controlled/programmatic open
✅ Edge Case: Array/fragment icon node can throw in isReactComponent
✅ Edge Case: Chips field nests multiple buttons inside one MenuTrigger
OptionsDisplay: compact → Counting what did not apply, without listing it. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
Describe your changes
Fixes #29569
Part of the filter-dropdown unification effort (#31999 and the Collate explore-filter polish epic). This adds the shared primitive; follow-up PRs swap the app's filter dropdowns (
SearchDropdown,QuickFilterDropdown,FilterChip, …) onto it.FilterSelect(ui-core-components/application/filter-select/) — a filter dropdown built on the existing react-aria primitives (Dropdown,Input,Checkbox,Button):chip(pill),button,input-style — the three shapes used across the app today.single/multiple; commitsimmediate(per toggle) orstaged(Apply / Cancel / Clear all footer).onSearchfor async option fetching, otherwise filters locally.nullOption, Select All — renders throughDropdown.Item+CheckboxBase size="sm", so checkbox size, alignment, and typography cannot drift per consumer.'all'selection sentinel (⌘A) to concrete values; merges persisted-but-unfetched selected values (e.g. restored from a URL) into the list viaresolveMissingLabel.hideCountsto suppress;isLoading/emptyStatehandling.Screen.Recording.2026-09-11.at.11.25.52.AM.mov
i18n
Adds six core-namespace keys (
apply,cancel,clear-all,no-data-found,search,select-all) with real translations in all 20 language files.yarn check-i18n-allpasses (548 translations, none English-fallback).Testing
yarn type-check,yarn lint,yarn buildall clean. The one failing table test onmain(can omit the selection cell for a full-width synthetic row) pre-exists and is unrelated.🤖 Generated with Claude Code