feat: refacto search with facets and multiple configs#847
Open
geoffreyaldebert wants to merge 8 commits intomainfrom
Open
feat: refacto search with facets and multiple configs#847geoffreyaldebert wants to merge 8 commits intomainfrom
geoffreyaldebert wants to merge 8 commits intomainfrom
Conversation
Contributor
Author
|
Relative to opendatateam/udata-search-service#63 and opendatateam/udata#3583 |
maudetes
reviewed
Jan 21, 2026
Contributor
maudetes
left a comment
There was a problem hiding this comment.
@geoffreyaldebert is there a small description of the changes (facets + single search, etc?)
Merged
ThibaudDauce
added a commit
that referenced
this pull request
Jan 28, 2026
Extracted from #847 - Created ObjectCard base component with badges, media and default slots to provide a consistent card layout (border, hover, flex structure, fr-enlarge-link) shared across all horizontal cards. Created sub-components ObjectCardBadge (single badge only, uses absolute positioning), ObjectCardHeader and ObjectCardShortDescription (CSS line-clamp-2 + JS truncation to limit DOM size). - Created new card components: TopicCard, PostCard, DiscussionMessageCard, and ReuseHorizontalCard (extracted from the horizontal/search mode of ReuseCard into its own component). - Refactored DatasetCard and DataserviceCard to use ObjectCard. Added organization logo / avatar / placeholder on the left side of DataserviceCard, matching the layout of DatasetCard. - Added consistent iconography across all cards using Remix Icons (RiDatabase2Line for datasets, RiTerminalLine for dataservices, RiLineChartLine for reuses, RiChat3Line for discussions, RiArticleLine for posts, RiBookShelfLine for topics), with aria-hidden="true" on all decorative icons. - Extended Placeholder component with Dataservice and Topic types (RiTerminalLine and RiBookShelfLine). - Fixed badge layout on DataserviceCard: removed conditional mt-2 that caused misalignment in grids when a card had badges next to one without. - Fixed DiscussionCard (components/Discussions/) to guard against null closed_by with v-else-if. - Used RiSubtractLine as separator consistently across all cards (replaced middle dots in TopicCard and DiscussionMessageCard). - Added removeMarkdown to TopicCard description truncation, consistent with other cards. - Updated the /design/cards page to showcase all card types in a 2-column grid, fetching data directly via useAPI. - Moved types to datagouv-components: Page, Post, Comment, Thread. - Changed Discussion types to use UserReference/OrganizationReference instead of full User/Organization. Adapted getOwnerEmails in utils/owner.ts to fetch user by ID since UserReference doesn't carry email. Fix #867 <img width="1678" height="2155" alt="image" src="https://github.com/user-attachments/assets/40795a85-9891-4163-a307-8357bc34b46a" />
# Conflicts: # datagouv-components/src/components/DataserviceCard.vue # datagouv-components/src/components/DatasetCard.vue # datagouv-components/src/components/PostCard.vue # datagouv-components/src/components/TopicCard.vue # datagouv-components/src/main.ts
- Remove DiscussionCard.vue (replaced by DiscussionMessageCard.vue in #903) - Reset ReuseCard.vue to main version Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
ThibaudDauce
added a commit
that referenced
this pull request
Feb 3, 2026
## Summary
Introduces a unified `GlobalSearch` component in `datagouv-components`
that replaces the separate search pages for datasets, dataservices, and
reuses.
### New components in `datagouv-components`
- `GlobalSearch` - Unified search component with configurable filters
per search type
- Form components: `OrganizationSelect`, `OrganizationTypeSelect`,
`TagSelect`, `FormatSelect`, `LicenseSelect`, `SchemaSelect`,
`GeozoneSelect`, `GranularitySelect`, `BadgeSelect`
- UI components: `RadioGroup`, `RadioInput`, `DoubleFilter`, `Sidemenu`
### New composables
- `useDebouncedRef` - Like VueUse's `refDebounced` but with a `flush()`
method for immediate sync (needed for "reset filters" to apply
instantly)
- `useRouteQueryBoolean` - Boolean handling for route query params
- `useStableQueryParams` - Creates stable query params that only update
when content changes (prevents infinite re-fetch loops)
- `useSelectModelSync` / `useAsyncSelectModelSync` - Bidirectional sync
between model (full object) and id (identifier)
### Other changes
- Switch from `useUrlSearchParams` to `useRouteQuery` from
`@vueuse/router`
- Select components now fetch their own data when given an unknown ID
- Explicit type annotations added to inline callbacks (e.g., `(option: {
text: string }) => option.text`). This is required because moving
`SelectGroup`/`SearchableSelect` to `datagouv-components` changed how
generics are inferred across package boundaries. TypeScript can't infer
the type from the `options` prop when the component comes from an
external package.
### Removed
- `components/Datasets/SearchPage.vue`
- `components/Dataservices/SearchPage.vue`
- `components/Reuses/ListPage.vue`
- `components/Reuses/ListFromOrganization.vue`
- `components/OrganizationSelect.vue`
---
## Breaking changes
### Atom feed link removed from dataset search
The atom feed was an external link to the API
(`/api/1/datasets/recent.atom?...`). Reintroducing it would require
mapping the unified filter state back to API v1 query params, which is
straightforward if needed.
### CSV export button removed from organization dataset pages
The CSV button used a manually constructed URL
(`/api/1/organizations/{slug}/datasets.csv`). With the new
`GlobalSearch` component using `hiddenFilters`, the organization slug is
not available in the component. Reintroducing it would require either
passing the slug as a prop or adding a slot for custom actions.
---
## Anticipated questions
### Why does GlobalSearch make 3 simultaneous API calls?
The component fetches datasets, dataservices, and reuses in parallel to
display result counts on each tab (e.g., "Datasets (1234)"). This is the
same behavior as the current data.gouv.fr site. Requests are optimized
via `useStableQueryParams` which prevents unnecessary re-fetches when
params don't actually change.
### Why does useStableQueryParams use JSON.stringify for comparison?
The params object is built deterministically (keys are always added in
the same order), so JSON.stringify is safe for comparison. Alternatives
like `lodash.isEqual` or `fast-deep-equal` would work but add a
dependency for a simple use case.
### Why switch from useUrlSearchParams to useRouteQuery?
`useUrlSearchParams` (VueUse core) directly manipulates
`window.location` and can cause issues with the Vue router.
`useRouteQuery` (VueUse router) integrates properly with vue-router and
Nuxt.
### Why create useDebouncedRef instead of using refDebounced?
VueUse's `refDebounced` doesn't allow flushing the value immediately. We
need `flush()` so that "Reset filters" applies the change instantly
instead of waiting for the debounce.
### How does hiddenFilters work?
`hiddenFilters` allows pre-setting filters that are sent to the API but
not displayed in the UI. Example: on `/organizations/{oid}/datasets`, we
use:
```ts
hiddenFilters: [{ key: 'organization', value: organization.id }]
```
This scopes the search to the organization without showing the
"Organization" filter in the sidebar.
### What does "Select components fetch their own data" mean?
Previously, if you loaded a page with `?organization=xxx` in the URL,
the parent had to provide the list of organizations to resolve the ID to
a full object. Now, each Select (OrganizationSelect, TagSelect, etc.)
can make an API call to fetch the object corresponding to the ID if it's
not in its options list.
---
## Not yet migrated from #847
The following features from PR #847 are not included in this PR and will
be added later:
- **Additional search types**: organizations, discussions, posts, topics
(this PR only covers datasets, dataservices, reuses)
- **Facet-based filters with counts**: Displaying result counts next to
each filter option (e.g., "Tabulaires (1234)")
- **Additional filter types**:
- `format_family` (tabular, machine_readable, geographical, documents,
other)
- `access_type` (open, open_with_constraint, restricted)
- `last_update_range` (last_30_days, last_12_months, last_3_years)
- `producer_type` (public-service, local-authority, company,
association, user)
- `type` and `topic` for reuses
- **Advanced filters accordion**: Collapsible section with
organization/tags search showing facet counts
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.
No description provided.