feat(ui): Explore redesign - stacking browse+filter query bar, chips, cards#28892
feat(ui): Explore redesign - stacking browse+filter query bar, chips, cards#28892harshach wants to merge 1 commit into
Conversation
Browse location and quick filters now stack into one removable query: - New browsePath URL param holds the tree location (category/serviceType/ service/database/schema); it ANDs with the dropdown quickFilter so browsing never clears filters and vice versa (single combine point in performFetch; export + facet fetches included). - Persistent QUERY bar (ExploreQueryFilterChips): tinted browse chips + filter chips, each removable (browse removal truncates deeper levels), right-aligned Clear, empty-state placeholder. - Filter dropdowns apply immediately (no Update button) with helper text, compact menu, "+" selected state, and facet options that exclude their own field so unselecting a type reveals the other available types in scope. - Tree: categories that cannot contain the selected asset types gray out and collapse; counts render at every level; selection highlight follows the browse chips (deepest-loaded-ancestor fallback for deep links/reloads). - Cards: compact white layout (5-6 per viewport), single-line breadcrumb with middle-ellipsis click-to-expand, border-only selected state, stroke icon set (ExploreIconUtils) scoped to explore surfaces. - Human-readable entity-type labels via canonical-casing resolver (aggregations return lowercase keys, EntityType enum is camelCase). - Defensive aggregation access so malformed search responses degrade to empty lists instead of error toasts. - Tests: new ExploreQueryBar.spec (persistent bar, filter-survives-tree-click, graying); explore spec helpers switched to testid locators (count badges broke exact-text matching); 124 unit tests green across 11 suites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
| const selectedEntityTypes = useMemo(() => { | ||
| const entityTypeField = selectedQuickFilters.find( | ||
| (field) => | ||
| field.key === EntityFields.ENTITY_TYPE_KEYWORD || | ||
| field.key === EntityFields.ENTITY_TYPE | ||
| ); | ||
| const browseEntityTypeField = browseFields.find( | ||
| (field) => field.key === EntityFields.ENTITY_TYPE | ||
| ); | ||
|
|
||
| return [ | ||
| ...(entityTypeField?.value ?? []), | ||
| ...(browseEntityTypeField?.value ?? []), | ||
| ].map((option) => option.key); | ||
| }, [selectedQuickFilters, browseFields]); |
There was a problem hiding this comment.
💡 Edge Case: Browsing a category root grays out & collapses sibling categories
selectedEntityTypes (ExploreV1.component.tsx:472-486) is the union of the Data Assets quick-filter value AND the entityType browse field value. When the user clicks a category root in the tree (e.g. "Databases"), onNodeSelect builds a browse field whose value is the category's full childEntities list (database, databaseSchema, storedProcedure, table, tableColumn). That entire set then flows into selectedEntityTypes, so getDisabledExploreTreeKeys disables — and visibleExpandedKeys collapses — every sibling category that contains none of those types.
The result is that merely browsing into a category (not selecting a specific asset type) grays out and collapses all other top-level categories. The PR description scopes the gray-out/collapse behavior to "when an asset type is selected," so triggering it on a category-level browse click — including auto-collapsing an already-expanded sibling subtree — may be surprising. Please confirm this is intended; if not, exclude the browse-field category value (or only the multi-type category roots) from selectedEntityTypes.
Was this helpful? React with 👍 / 👎
| export const parseBrowsePathFields = ( | ||
| browsePath?: unknown | ||
| ): ExploreQuickFilterField[] => { | ||
| let result: ExploreQuickFilterField[] = []; | ||
| if (isString(browsePath) && !isEmpty(browsePath)) { | ||
| try { | ||
| const parsed = JSON.parse(browsePath); | ||
| if (Array.isArray(parsed)) { | ||
| result = parsed as ExploreQuickFilterField[]; | ||
| } | ||
| } catch { | ||
| result = []; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
💡 Quality: parseBrowsePathFields accepts any JSON array without shape validation
parseBrowsePathFields (ExplorePureUtils.ts:418-434) only checks Array.isArray(parsed) before casting the parsed JSON to ExploreQuickFilterField[]. A crafted/legacy browsePath URL param such as [1,2,3] or [{}] would be accepted and propagated into chip rendering and query building. I verified the downstream consumers (getExploreQueryFilterMust, getBrowsePathSignature, the chips component) are defensive (field.value ?? [], isEmpty(filter.value) guards) so this does not currently crash, but the cast silently trusts untrusted URL input. Consider validating that each element has at least a string key (and array/undefined value) and dropping the param otherwise, so malformed deep links degrade to an empty browse path rather than rendering empty/garbage chips.
Validate each element's shape before treating the array as a browse path.:
if (Array.isArray(parsed)) {
result = parsed.filter(
(field) =>
field &&
typeof field.key === 'string' &&
(field.value === undefined || Array.isArray(field.value))
) as ExploreQuickFilterField[];
}
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 0 resolved / 2 findingsRedesigns the Explore page with a composable query experience using stacked chips and immediate-apply filters. Consider implementing shape validation for 💡 Edge Case: Browsing a category root grays out & collapses sibling categories📄 openmetadata-ui/src/main/resources/ui/src/components/ExploreV1/ExploreV1.component.tsx:472-486 📄 openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx:303-315 📄 openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx:382-396
The result is that merely browsing into a category (not selecting a specific asset type) grays out and collapses all other top-level categories. The PR description scopes the gray-out/collapse behavior to "when an asset type is selected," so triggering it on a category-level browse click — including auto-collapsing an already-expanded sibling subtree — may be surprising. Please confirm this is intended; if not, exclude the browse-field category value (or only the multi-type category roots) from 💡 Quality: parseBrowsePathFields accepts any JSON array without shape validation📄 openmetadata-ui/src/main/resources/ui/src/utils/ExplorePureUtils.ts:418-432
Validate each element's shape before treating the array as a browse path.🤖 Prompt for agentsOptionsDisplay: compact → Showing less information. Comment with these commands to change:
Was this helpful? React with 👍 / 👎 | Gitar |
🔴 Playwright Results — 108 failure(s), 20 flaky✅ 4148 passed · ❌ 108 failed · 🟡 20 flaky · ⏭️ 99 skipped
Genuine Failures (failed on all attempts)❌
|
Describe your changes:
This PR redesigns the Explore page around one idea: browsing and filtering compose into a single, visible, removable query. The tree's browse location now lives in its own
browsePathURL param and ANDs with the dropdownquickFilter, so clicking a service never clears your Type filter (and vice versa); everything you pick shows in a persistent QUERY bar as removable chips. Filter dropdowns apply immediately (no Update button), show facet options that exclude their own field, and use human-readable labels; incompatible tree categories gray out and collapse when an asset type is selected. Result cards get a compact white layout (5–6 per viewport), single-line middle-ellipsized breadcrumbs, a border-only selected state, and a stroke icon set scoped to explore surfaces.Type of change:
High-level design:
query = browsePath (tree location) AND quickFilter (facets) AND queryFilter (advanced).browsePathserializes the exactExploreQuickFilterField[]shape the tree already builds per level; it is combined at a single injection point inExplorePageV1.performFetch(also export modal + facet fetches + the SWR cache key), keepingquickFilter100% backward compatible for deep links.ExploreTree): hierarchical levels (category/serviceType/service/database/schema) emit the browse path; entity-type leaves emit path prefix + a Type upsert into the Data Assets slot — both land in ONE navigation. Highlight follows the chips (deepest-loaded-ancestor fallback), disabled categories collapse via controlledexpandedKeys, counts render at every level.ExploreQueryFilterChips): persistent; tinted browse chips with hierarchical removal (truncateBrowsePath), filter chips, right-aligned Clear, empty-state placeholder.SearchDropdown/ExploreQuickFilters): opt-inimmediateApply(default off — 14 other consumers unchanged); per-facet filter excludes the facet's own field (design'sfacetOptionssemantics); canonical entity-type casing resolver bridges lowercase aggregation keys vs camelCase enum.quickFilterparam — rejected because a flat ES bool query loses level provenance/ordering, making browse chips and hierarchical removal unrecoverable, and DB/schema terms aren't dropdown items so they'd be silently dropped on the next facet apply./v1/search/query+/v1/search/aggregatecover everything (verified againstSearchResourceITcoverage).Tests:
Use cases covered
browsePathhighlight the deepest loaded ancestor; legacyquickFilter-only URLs behave unchanged.Unit tests
ExplorePureUtils.test.ts(browsePath parse/build/truncate, canonical casing, node-by-path, graying),ExploreQueryFilterChips.test.tsx,AdvancedSearchPureUtils.test.ts, extendedSearchDropdown.test.tsx,ExploreTree.test.tsx,ExploreSearchCard.test.tsx,ExploreQuickFilters.test.tsx— 124 tests green across 11 suites.Backend integration tests
SearchResourceITalready covers the aggregate/query_filter paths used).Ingestion integration tests
Playwright (UI) tests
playwright/e2e/Features/ExploreQueryBar.spec.ts(persistent bar, filter-survives-tree-click with chip removal + Clear, asset-type graying — 3/3 green locally). Updated explore helpers/specs for the immediate-apply UX and testid-based tree locators (count badges broke exact-text matching).Manual testing performed
yarn start; verified the full hero flow end-to-end in Chromium (scripted + interactive): persistent QUERY bar, no Update button, chip stacking/removal, graying + collapse, facet refresh scoped to the selected service, card density/breadcrumb ellipsis, icons.UI screen recording / screenshots:
Will attach before/after screenshots and a short recording as a PR comment.
Checklist:
Fixes <issue-number>: <short explanation>(no issue opened yet)Fixes #<issue-number>above (pending issue)🤖 Generated with Claude Code