-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(filters): refactor filters - add validation and array support
- Loading branch information
Showing
18 changed files
with
365 additions
and
125 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export * from './sectionListViewsConfig' | ||
export * from './sections' | ||
export * from './translatedModelConstants' | ||
export * from './translatedModelProperties' | ||
export * from './sectionListView' | ||
|
||
export const IDENTIFIABLE_KEY = 'identifiable' |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
export * from './viewConfigResolver' | ||
export * from './sectionListViewFilterKeys' | ||
// export * from './sectionListViewsConfig' | ||
export * from './sectionListViewsConfig' |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { | ||
DelimitedArrayParam, | ||
encodeDelimitedArray, | ||
decodeDelimitedArray, | ||
} from 'use-query-params' | ||
|
||
// default is "_" which breaks constants (delimited by _) | ||
const ARRAY_ENTRY_SEPERATOR = ',' | ||
|
||
export const CustomDelimitedArrayParam: typeof DelimitedArrayParam = { | ||
encode: (arr) => encodeDelimitedArray(arr, ARRAY_ENTRY_SEPERATOR), | ||
|
||
decode: (str) => decodeDelimitedArray(str, ARRAY_ENTRY_SEPERATOR), | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { StringParam } from 'use-query-params' | ||
import { z } from 'zod' | ||
import { DataElement } from '../../../types/generated' | ||
import { IDENTIFIABLE_KEY } from '../../constants' | ||
import { isValidUid } from '../../models' | ||
import { CustomDelimitedArrayParam } from './customParams' | ||
|
||
const zodArrayIds = z.array(z.string().refine((val) => isValidUid(val))) | ||
|
||
/* Zod schema for validation of the decoded params */ | ||
export const filterParamsSchema = z | ||
.object({ | ||
[IDENTIFIABLE_KEY]: z.string(), | ||
aggregationType: z.array(z.nativeEnum(DataElement.aggregationType)), | ||
domainType: z.array(z.nativeEnum(DataElement.domainType)), | ||
valueType: z.array(z.string()), | ||
dataSet: zodArrayIds, | ||
}) | ||
.partial() | ||
|
||
/* useQueryParams config-map object | ||
Mapping each filter to a config object that handles encoding/decoding */ | ||
export const filterQueryParamType = { | ||
[IDENTIFIABLE_KEY]: StringParam, | ||
aggregationType: CustomDelimitedArrayParam, | ||
domainType: CustomDelimitedArrayParam, | ||
valueType: CustomDelimitedArrayParam, | ||
dataSet: CustomDelimitedArrayParam, | ||
} as const satisfies QueryParamsConfigMap | ||
|
||
export const validFilterKeys = Object.keys(filterQueryParamType) | ||
|
||
export type ParsedFilterParams = z.infer<typeof filterParamsSchema> | ||
|
||
type MapZodTypeToQueryParamConfig<TZodResultType> = | ||
TZodResultType extends string | ||
? typeof StringParam | ||
: typeof CustomDelimitedArrayParam | ||
|
||
/* Type is just used to verify that the ParamType-config matches the zod schema | ||
Eg. that a value that is a string in zod-schema also uses StringParam for encode/decode */ | ||
type QueryParamsConfigMap = { | ||
[key in keyof ParsedFilterParams]-?: MapZodTypeToQueryParamConfig< | ||
ParsedFilterParams[key] | ||
> | ||
} | ||
|
||
export type FilterKey = keyof ParsedFilterParams | ||
// Identifiable is not configurable, and is always shown in the list | ||
export type ConfigurableFilterKey = Exclude<FilterKey, typeof IDENTIFIABLE_KEY> | ||
export type FilterKeys = FilterKey[] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './filterConfig' | ||
export * from './useSectionListFilters' |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { SECTIONS_MAP, Section } from '../../constants' | ||
import { FilterKey, ParsedFilterParams } from './filterConfig' | ||
|
||
type AllValues = ParsedFilterParams[keyof ParsedFilterParams] | ||
|
||
const defaultFilter = (key: FilterKey, value: AllValues): string => { | ||
const isArray = Array.isArray(value) | ||
const valuesString = isArray ? `[${value.join(',')}]` : value?.toString() | ||
const operator = isArray ? 'in' : 'eq' | ||
return `${key}:${operator}:${valuesString}` | ||
} | ||
|
||
const getQueryParamForFilter = ( | ||
key: FilterKey, | ||
value: AllValues, | ||
section?: Section | ||
): string => { | ||
if (!value) { | ||
return '' | ||
} | ||
if (key === 'identifiable') { | ||
return `identifiable:token:${value}` | ||
} | ||
if (key === 'dataSet') { | ||
const v = value as string[] | ||
if (section?.name === SECTIONS_MAP.dataElement.name) { | ||
return `dataSetElements.dataSet.id:in:[${v.join(',')}]` | ||
} | ||
} | ||
return defaultFilter(key, value) | ||
} | ||
|
||
export const parseFiltersToQueryParams = ( | ||
filters: ParsedFilterParams, | ||
section?: Section | ||
): string[] => { | ||
const queryFilters: string[] = [] | ||
for (const [key, value] of Object.entries(filters)) { | ||
if (!value) { | ||
continue | ||
} | ||
const filter = getQueryParamForFilter(key as FilterKey, value, section) | ||
queryFilters.push(filter) | ||
} | ||
return queryFilters | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { useMemo } from 'react' | ||
import { | ||
useModelSectionHandleOrThrow, | ||
useSectionHandle, | ||
} from './../../routeUtils/useSectionHandle' | ||
import { ParsedFilterParams } from './filtersQueryParamSimple' | ||
import { parseFiltersToQueryParams } from './parseFiltersToQueryParams' | ||
import { useSectionListFilters } from './useSectionListFilters' | ||
|
||
export const useFilterQueryParams = (): string[] => { | ||
const [filters] = useSectionListFilters() | ||
const section = useSectionHandle() | ||
|
||
return useMemo(() => { | ||
return parseFiltersToQueryParams(filters, section) | ||
}, [filters, section]) | ||
} |
Oops, something went wrong.