Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions assets/css/overrides.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@
@apply !bg-none underline underline-offset-4 hover:decoration-2 text-new-primary;
}

.input {
@apply block w-full rounded-t text-base leading-6 py-2 px-4 text-gray-plain bg-gray-lower placeholder:opacity-100 placeholder:italic placeholder:text-gray-medium;
}

.close-button {
@apply w-10 leading-0 text-lg;
}
4 changes: 4 additions & 0 deletions datagouv-components/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@
}

@layer components {
.input {
@apply block w-full rounded-t text-base leading-6 py-2 px-4 text-gray-plain bg-gray-lower! placeholder:opacity-100 placeholder:italic placeholder:text-gray-medium;
}

.subtitle {
@apply text-sm! font-bold! leading-5!;
}
Expand Down
46 changes: 44 additions & 2 deletions datagouv-components/src/components/Search/GlobalSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,23 @@
</Sidemenu>
</div>

<div v-if="activeFilters.length > 0">
<div v-if="activeFilters.length > 0 || hasTagFilters">
<Sidemenu :button-text="t('Filtres')">
<template #title>
{{ t('Filtres') }}
</template>
<SearchableSelect
v-for="tagFilter in currentTypeConfig?.tagFilters"
:key="tagFilter.urlParam"
:model-value="tagFilter.values.find(v => v.value === tagFilterRefs[tagFilter.urlParam]!.value) ?? null"
:options="tagFilter.values"
:label="tagFilter.label"
:placeholder="tagFilter.defaultLabel"
:get-option-id="(opt) => opt.value"
:display-value="(opt) => opt.label"
:multiple="false"
@update:model-value="tagFilterRefs[tagFilter.urlParam]!.value = $event?.value"
/>
<BasicAndAdvancedFilters
v-slot="{ isEnabled, getOrder }"
:basic-filters="activeBasicFilters"
Expand Down Expand Up @@ -370,6 +382,7 @@ import SearchInput from './SearchInput.vue'
import Sidemenu from './Sidemenu.vue'
import BasicAndAdvancedFilters from './BasicAndAdvancedFilters.vue'
import OrganizationSelect from '../Form/OrganizationSelect.vue'
import SearchableSelect from '../Form/SearchableSelect.vue'
import OrganizationTypeSelect from '../Form/OrganizationTypeSelect.vue'
import TagSelect from '../Form/TagSelect.vue'
import FormatSelect from '../Form/FormatSelect.vue'
Expand Down Expand Up @@ -439,7 +452,13 @@ const activeFilters = computed(() => [
...(currentTypeConfig.value?.advancedFilters ?? []),
] as string[])

const showSidebar = computed(() => props.config.length > 1 || activeFilters.value.length > 0)
const hasTagFilters = computed(() => !!(currentTypeConfig.value?.tagFilters?.length))

const showSidebar = computed(() =>
props.config.length > 1
|| activeFilters.value.length > 0
|| hasTagFilters.value,
)

// URL query params
const q = useRouteQuery<string>('q', '')
Expand Down Expand Up @@ -468,6 +487,15 @@ const reuseType = useRouteQuery<string | undefined>('type')

const pageSize = 20

// Collect unique urlParams across all type configs: a param shared between
// types maps to a single ref so it persists correctly across type switches.
const tagFilterUrlParams = [...new Set(
props.config.flatMap(c => c.tagFilters ?? []).map(tf => tf.urlParam),
)]
const tagFilterRefs: Record<string, Ref<string | undefined>> = Object.fromEntries(
tagFilterUrlParams.map(p => [p, useRouteQuery<string | undefined>(p)]),
)

// All filter values as a record
const allFilters: Record<string, Ref<unknown>> = {
organization: organizationId,
Expand Down Expand Up @@ -501,6 +529,14 @@ watch(currentType, () => {
filterRef.value = undefined
}
}

// Reset tagFilterRefs not belonging to the new type
const newTypeTagParams = new Set((currentTypeConfig.value?.tagFilters ?? []).map(tf => tf.urlParam))
for (const [urlParam, ref] of Object.entries(tagFilterRefs)) {
if (!newTypeTagParams.has(urlParam)) {
ref.value = undefined
}
}
})

// Check which types are enabled
Expand All @@ -513,6 +549,7 @@ const topicsEnabled = computed(() => props.config.some(c => c.class === 'topics'
// Create stable params for each type
const stableParamsOptions = {
allFilters,
tagFilterRefs,
q: qDebounced,
sort,
page,
Expand Down Expand Up @@ -565,6 +602,7 @@ const filtersForReset = computed(() => ({
last_update_range: lastUpdateRange.value,
producer_type: producerType.value,
type: reuseType.value,
tagFilters: Object.values(tagFilterRefs).map(r => r.value),
}))

watch(filtersForReset, () => page.value = 1)
Expand All @@ -587,6 +625,7 @@ const hasFilters = computed(() => {
|| lastUpdateRange.value
|| producerType.value
|| reuseType.value
|| Object.values(tagFilterRefs).some(r => r.value)
})

const showForumLink = computed(() => (currentType.value === 'datasets' || currentType.value === 'dataservices') && !!componentsConfig.forumUrl)
Expand All @@ -607,6 +646,9 @@ function resetFilters() {
lastUpdateRange.value = undefined
producerType.value = undefined
reuseType.value = undefined
for (const ref of Object.values(tagFilterRefs)) {
ref.value = undefined
}
q.value = ''
flushQ()
}
Expand Down
15 changes: 13 additions & 2 deletions datagouv-components/src/composables/useStableQueryParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type FilterRefs = Record<string, Ref<unknown>>
interface StableQueryParamsOptions {
typeConfig: SearchTypeConfig | undefined
allFilters: FilterRefs
tagFilterRefs?: FilterRefs
q: Ref<string>
sort: Ref<string | undefined>
page: Ref<number>
Expand All @@ -17,7 +18,7 @@ interface StableQueryParamsOptions {
* Applies hiddenFilters first, then user filters (which can override hiddenFilters).
*/
export function useStableQueryParams(options: StableQueryParamsOptions) {
const { typeConfig, allFilters, q, sort, page, pageSize } = options
const { typeConfig, allFilters, tagFilterRefs, q, sort, page, pageSize } = options
const stableParams = ref<Record<string, unknown>>({})

const buildParams = () => {
Expand Down Expand Up @@ -50,6 +51,16 @@ export function useStableQueryParams(options: StableQueryParamsOptions) {
}
}

// 3b. Apply tagFilters (each urlParam maps to the 'tag' API param)
if (tagFilterRefs) {
for (const tagFilter of typeConfig?.tagFilters ?? []) {
const value = tagFilterRefs[tagFilter.urlParam]?.value
if (value !== undefined && value !== '' && value !== null) {
params.tag = 'tag' in params ? ([] as unknown[]).concat(params.tag, value) : value
}
}
}

// 4. Always include q, sort (if valid for this type), page, page_size
if (q.value) {
params.q = q.value
Expand All @@ -68,7 +79,7 @@ export function useStableQueryParams(options: StableQueryParamsOptions) {

// Watch all dependencies and update only if content changed
watch(
[q, sort, page, ...Object.values(allFilters)],
[q, sort, page, ...Object.values(allFilters), ...Object.values(tagFilterRefs ?? {})],
() => {
const newParams = buildParams()
// JSON.stringify comparison is safe here because buildParams() builds the object deterministically
Expand Down
10 changes: 9 additions & 1 deletion datagouv-components/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type { Site } from './types/site'
import type { Weight, WellType } from './types/ui'
import type { User, UserReference } from './types/users'
import type { Report, ReportSubject, ReportReason } from './types/reports'
import type { GlobalSearchConfig, SearchType, SortOption } from './types/search'
import type { GlobalSearchConfig, SearchType, SortOption, TagFilterConfig, TagFilterValue, HiddenFilter, DatasetSearchFilters, DataserviceSearchFilters, ReuseSearchFilters, OrganizationSearchFilters, TopicSearchFilters } from './types/search'
import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'

import ActivityList from './components/ActivityList/ActivityList.vue'
Expand Down Expand Up @@ -123,9 +123,17 @@ export * from './functions/users'
export * from './types/access_types'

export type {
DatasetSearchFilters,
DataserviceSearchFilters,
GlobalSearchConfig,
HiddenFilter,
OrganizationSearchFilters,
ReuseSearchFilters,
SearchType,
SortOption,
TagFilterConfig,
TagFilterValue,
TopicSearchFilters,
UseFetchFunction,
AccessType,
AccessAudience,
Expand Down
17 changes: 17 additions & 0 deletions datagouv-components/src/types/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,18 @@ export type TopicSearchResponse<T> = SearchResponseWithFacets<T, TopicSearchFace

// GlobalSearch configuration types

export type TagFilterValue = {
value: string
label: string
}

export type TagFilterConfig = {
urlParam: string
label: string
defaultLabel: string
values: TagFilterValue[]
}

export type HiddenFilter<Filters> = {
[K in keyof Filters]: { key: K, value: Filters[K] }
}[keyof Filters]
Expand All @@ -296,6 +308,7 @@ export type DatasetSearchConfig = {
basicFilters?: (keyof DatasetSearchFilters)[]
advancedFilters?: (keyof DatasetSearchFilters)[]
sortOptions?: SortOption<DatasetSearchSort>[]
tagFilters?: TagFilterConfig[]
}

export type DataserviceSearchConfig = {
Expand All @@ -305,6 +318,7 @@ export type DataserviceSearchConfig = {
basicFilters?: (keyof DataserviceSearchFilters)[]
advancedFilters?: (keyof DataserviceSearchFilters)[]
sortOptions?: SortOption<DataserviceSearchSort>[]
tagFilters?: TagFilterConfig[]
}

export type ReuseSearchConfig = {
Expand All @@ -314,6 +328,7 @@ export type ReuseSearchConfig = {
basicFilters?: (keyof ReuseSearchFilters)[]
advancedFilters?: (keyof ReuseSearchFilters)[]
sortOptions?: SortOption<ReuseSearchSort>[]
tagFilters?: TagFilterConfig[]
}

export type OrganizationSearchConfig = {
Expand All @@ -323,6 +338,7 @@ export type OrganizationSearchConfig = {
basicFilters?: (keyof OrganizationSearchFilters)[]
advancedFilters?: (keyof OrganizationSearchFilters)[]
sortOptions?: SortOption<OrganizationSearchSort>[]
tagFilters?: TagFilterConfig[]
}

export type TopicSearchConfig = {
Expand All @@ -332,6 +348,7 @@ export type TopicSearchConfig = {
basicFilters?: (keyof TopicSearchFilters)[]
advancedFilters?: (keyof TopicSearchFilters)[]
sortOptions?: SortOption<TopicSearchSort>[]
tagFilters?: TagFilterConfig[]
}

export type SearchTypeConfig = DatasetSearchConfig | DataserviceSearchConfig | ReuseSearchConfig | OrganizationSearchConfig | TopicSearchConfig
Expand Down