Skip to content

Commit

Permalink
Merge pull request #1068 from dpc-sdp/feature/infinite-dependent-filters
Browse files Browse the repository at this point in the history
feat(@dpc-sdp/ripple-tide-search): allow multiple to be set per level
  • Loading branch information
dylankelly authored Mar 14, 2024
2 parents e4c42da + 6a5d2a5 commit 3fc81a6
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 18 deletions.
22 changes: 22 additions & 0 deletions examples/nuxt-app/test/features/search-listing/filters.feature
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,28 @@ Feature: Search listing - Filter
| dependentFilter | dependentFilter-2:Parrots,Dogs |
| dependentFilter | dependentFilter-3:Cockatoos,Budgerigars,Spaniel |

@mockserver
Example: Dependent filter (Mix) - users should be able to mix single and multiple dropdowns
Given the page endpoint for path "/filters" returns fixture "/search-listing/dependent-filters/page-mix" with status 200
And the search network request is stubbed with fixture "/search-listing/dependent-filters/response" and status 200

When I visit the page "/filters?dependentFilter=dependentFilter-1:Birds&dependentFilter=dependentFilter-2:Parrots&dependentFilter=dependentFilter-3:Cockatoos,Budgerigars"
Then the search listing page should have 2 results
And the search network request should be called with the "/search-listing/dependent-filters/request-birds-grandchildren" fixture
And the filters toggle should show 3 applied filters

Then the search listing dropdown field labelled "Terms dependent example" should have the value "Birds"
And I click the search listing dropdown field labelled "Terms dependent example"
Then the selected dropdown field should allow "single" selection

Then the search listing dropdown field labelled "Terms dependent child example" should have the value "Parrots"
And I click the search listing dropdown field labelled "Terms dependent child example"
Then the selected dropdown field should allow "single" selection

Then the search listing dropdown field labelled "Terms dependent grandchild example" should have the value "Cockatoos, Budgerigars"
And I click the search listing dropdown field labelled "Terms dependent grandchild example"
Then the selected dropdown field should allow "multi" selection

@mockserver
Example: Should hide the search form when hideSearchForm is set
Given the page endpoint for path "/no-search-form" returns fixture "/search-listing/filters/page-no-search-form" with status 200
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
{
"title": "Depenedent filters",
"changed": "2022-11-02T12:47:29+11:00",
"created": "2022-11-02T12:47:29+11:00",
"type": "tide_search_listing",
"nid": "11dede11-10c0-111e1-1100-000000000330",
"showTopicTags": true,
"summary": "",
"config": {
"searchListingConfig": {
"resultsPerPage": 10,
"showFiltersOnLoad": true
},
"queryConfig": {
"multi_match": {
"query": "{{query}}",
"fields": [
"title^3",
"field_landing_page_summary^2",
"body",
"field_paragraph_body",
"summary_processed"
]
}
},
"results": {
"layout": {
"component": "TideSearchResultsList"
},
"item": {
"grant": {
"component": "TideGrantSearchResult"
}
}
},
"globalFilters": [
{
"terms": {
"type": [
"grant"
]
}
},
{
"terms": {
"field_node_site": [
8888
]
}
}
],
"userFilters": [
{
"id": "termFilter",
"component": "TideSearchFilterDropdown",
"filter": {
"type": "term",
"value": "termFilter.keyword"
},
"aggregations": {
"field": "termFilter",
"source": "taxonomy"
},
"props": {
"id": "termFilter",
"label": "Term filter example",
"placeholder": "Select a colour",
"multiple": true,
"options": [
{
"id": "1",
"label": "Red",
"value": "Red"
},
{
"id": "2",
"label": "Green",
"value": "Green"
},
{
"id": "3",
"label": "Blue",
"value": "Blue"
}
]
}
},
{
"id": "termsFilter",
"component": "TideSearchFilterDropdown",
"filter": {
"type": "terms",
"value": "termsFilter.keyword"
},
"aggregations": {
"field": "termsFilter",
"source": "taxonomy"
},
"props": {
"id": "termsFilter",
"label": "Terms filter example",
"placeholder": "Select a colour",
"multiple": true,
"options": [
{
"id": "1",
"label": "Orange",
"value": "Orange"
},
{
"id": "2",
"label": "Purple",
"value": "Purple"
},
{
"id": "3",
"label": "Yellow",
"value": "Yellow"
}
]
}
},
{
"id": "dependentFilter",
"component": "TideSearchFilterDependent",
"columns": "rpl-grid--inherit",
"filter": {
"type": "dependent",
"multiple": false,
"value": "field_species_name"
},
"aggregations": {
"field": "species",
"source": "taxonomy"
},
"props": {
"id": "dependentFilter",
"multiple": false,
"levels": [
{
"label": "Terms dependent example",
"placeholder": "Select a species"
},
{
"label": "Terms dependent child example",
"placeholder": "Select a sub species"
},
{
"label": "Terms dependent grandchild example",
"placeholder": "All sub sub species",
"multiple": true
}
],
"options": [
{
"id": 1,
"label": "Mammals",
"value": "Mammals",
"parent": null
},
{
"id": 2,
"label": "Dogs",
"value": "Dogs",
"parent": 1
},
{
"id": 3,
"label": "Birds",
"value": "Birds",
"parent": null
},
{
"id": 4,
"label": "Cats",
"value": "Cats",
"parent": 1
},
{
"id": 5,
"label": "Parrots",
"value": "Parrots",
"parent": 3
},
{
"id": 6,
"label": "Eagles",
"value": "Eagles",
"parent": 3
},
{
"id": 7,
"label": "Cockatoos",
"value": "Cockatoos",
"parent": 5
},
{
"id": 8,
"label": "Budgerigars",
"value": "Budgerigars",
"parent": 5
},
{
"id": 9,
"label": "Foxes",
"value": "Foxes",
"parent": 1
},
{
"id": 10,
"label": "Beagle",
"value": "Beagle",
"parent": 2
},
{
"id": 11,
"label": "Spaniel",
"value": "Spaniel",
"parent": 2
}
]
}
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ Then(
}
)

Then(
`the selected dropdown field should allow {string} selection`,
(type: string) => {
const isMultiSelect = type === 'multi' ? 'true' : 'false'

cy.get(`@selectedDropdown`)
.siblings('[role="listbox"]')
.should('have.attr', 'aria-multiselectable', isMultiSelect)
}
)

Then(
`the search listing checkbox field labelled {string} should be checked`,
(label: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface Props {
levels: {
label: string
placeholder: string
multiple?: boolean
}[]
}
Expand Down Expand Up @@ -47,7 +48,7 @@ const parentOptions = computed(() =>
props.options.filter((option) => !option.parent)
)
const getChildOptions = (parent): any[] => {
const getChildOptions = (parent: string | string[]): object[] => {
const selectedIds = props.options?.filter((option) =>
Array.isArray(parent)
? parent.includes(option.value)
Expand All @@ -60,19 +61,24 @@ const getChildOptions = (parent): any[] => {
.flat()
}
const getChildSelection = (key, value, options) => {
const getChildSelection = (value: string | string[], options: object[]) => {
if (Array.isArray(value)) {
return value.filter((selection) =>
options.some((options) => options.value === selection)
options.some((option: any) => option.value === selection)
)
}
return options.some((options) => options.value === value) ? value : null
return options.some((option: any) => option.value === value) ? value : null
}
const getUpdatedValues = (selected: object): object => {
const selection = {}
const options = {}
const getUpdatedValues = (
selected: object
): {
options: { [key: string]: object[] }
selection: { [key: string]: any }
} => {
const selection: { [key: string]: any } = {}
const options: { [key: string]: object[] } = {}
Object.entries(selected).forEach(([key, value]) => {
const depth = Number(key.match(/\d+$/)?.[0])
Expand All @@ -82,7 +88,7 @@ const getUpdatedValues = (selected: object): object => {
options[key] = parentOptions.value
} else {
options[key] = getChildOptions(selection[`${props.id}-${depth - 1}`])
selection[key] = getChildSelection(key, value, options[key])
selection[key] = getChildSelection(value, options[key])
}
})
Expand All @@ -98,21 +104,21 @@ watch(
Object.entries(options).forEach(([key, value]) => {
if (selectOptions[key]?.length) selectOptions[key].length = 0
value.forEach((val) => selectOptions[key].push(val))
value.forEach((val: object) => selectOptions[key].push(val))
})
// If the updated options results in a selection no longer being available, we need to remove that selection
Object.entries(selection).forEach(([key, value]) => {
let change = false
if (Array.isArray(curr[key])) {
change = curr[key].filter((val) => !value.includes(val)).length
change = curr[key].filter((val: string) => !value.includes(val)).length
} else {
change = curr[key] !== value
}
if (change) {
getNode(key)?.input(selection[key])
nextTick(() => getNode(key)?.input(selection[key]))
}
})
},
Expand All @@ -131,7 +137,7 @@ watch(
:id="`${id}-${i}`"
:name="`${id}-${i}`"
type="RplFormDropdown"
:multiple="multiple"
:multiple="levels?.[i - 1]?.multiple ?? multiple"
:label="levels?.[i - 1]?.label"
:placeholder="levels?.[i - 1]?.placeholder"
:options="selectOptions[`${id}-${i}`] || []"
Expand Down
11 changes: 7 additions & 4 deletions packages/ripple-tide-search/composables/useTideSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,13 +695,16 @@ export default ({
if (filterConfig?.component === 'TideSearchFilterDependent') {
parsedValue = Object.fromEntries(
(parsedValue as []).map((dep: string) => {
const [key, val] = (dep || '').split(':')
const [dependentKey, dependentValue] = (dep || '').split(':')

const depth = Number(dependentKey.match(/\d+$/)?.[0])

return [
key,
dependentKey,
filterConfig?.props?.levels?.[depth - 1]?.multiple ||
filterConfig?.props?.multiple
? val.split(',').map(decodeURIComponent)
: decodeURIComponent(val)
? dependentValue.split(',').map(decodeURIComponent)
: decodeURIComponent(dependentValue)
]
})
)
Expand Down
3 changes: 1 addition & 2 deletions packages/ripple-tide-search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ export interface FilterConfigItem {
* @description the relevant props that will be passed into the Vue component (see `component` key)
*/
props?: {
[key: string]: unknown
options?: any
[key: string]: any
}
}

Expand Down

0 comments on commit 3fc81a6

Please sign in to comment.