From 4a87fec57cb92408b149b1cf378bf1362fd0998f Mon Sep 17 00:00:00 2001 From: David Featherston Date: Fri, 27 Dec 2024 09:32:53 +1100 Subject: [PATCH 01/18] fix(@dpc-sdp/ripple-tide-search): stop navigation when there's nothing to navigate to --- .../components/global/TideSearchStandaloneLocationBar.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/ripple-tide-search/components/global/TideSearchStandaloneLocationBar.vue b/packages/ripple-tide-search/components/global/TideSearchStandaloneLocationBar.vue index 251639f56..6f8f21c0e 100644 --- a/packages/ripple-tide-search/components/global/TideSearchStandaloneLocationBar.vue +++ b/packages/ripple-tide-search/components/global/TideSearchStandaloneLocationBar.vue @@ -55,6 +55,8 @@ const props = withDefaults(defineProps(), { }) const handleSelect = async (val: any) => { + if (!val) return + const locationParams = getScopedQueryParams('location', val) await navigateTo( From ad8d96a04d710643d249a86efe63360bb6904426 Mon Sep 17 00:00:00 2001 From: David Featherston Date: Fri, 27 Dec 2024 11:09:29 +1100 Subject: [PATCH 02/18] fix(@dpc-sdp/ripple-tide-grant): remove unused updated variable --- .../components/global/TideGrantSearchResult.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue b/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue index bcb991063..0ca07da70 100644 --- a/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue +++ b/packages/ripple-tide-grant/components/global/TideGrantSearchResult.vue @@ -13,7 +13,7 @@ interface Props { const props = defineProps() -const { title, url, updated, summary } = useSearchResult(props.result) +const { title, url, summary } = useSearchResult(props.result) const audience = computed(() => formatGrantAudiences( From cd5f7b09db8278811c79cafd06b3810e3aa5a1f8 Mon Sep 17 00:00:00 2001 From: David Featherston Date: Fri, 27 Dec 2024 15:50:09 +1100 Subject: [PATCH 03/18] feat(@dpc-sdp/ripple-tide-search): search listing updates add filters sidebar and results heading, update filters toggle layout --- .../features/search-listing/filters.feature | 1 + .../search-listing/no-results.feature | 2 + .../search-listing/search-query.feature | 20 ++ .../search-listing/sidebar-filters.feature | 129 +++++++ .../filters/filters-sidebar.json | 274 ++++++++++++++ .../filters/request-sidebar.json | 90 +++++ .../nuxt-ripple/components/TideBaseLayout.vue | 5 +- .../step_definitions/content-types/listing.ts | 80 ++++- .../components/TideSearchAboveResults.vue | 25 +- .../components/TideSearchFilterHeader.vue | 40 +++ .../components/TideSearchFilterToggle.vue | 45 +++ .../components/TideSearchFilters.vue | 27 +- .../components/TideSearchListingPage.vue | 164 ++++++--- .../components/TideSearchPage.vue | 51 +-- .../components/TideSearchResultsHeading.vue | 22 ++ .../global/TideCustomCollection.vue | 339 ++++++++++-------- .../global/TideSearchFilterDateRange.vue | 6 + .../global/TideSearchFilterDependent.vue | 18 +- .../composables/useTideSearch.ts | 9 + packages/ripple-tide-search/types.ts | 6 +- .../src/components/layout/RplLayout.css | 10 +- .../src/components/layout/RplLayout.vue | 16 +- .../RplFormActions/RplFormActions.css | 9 + .../RplFormActions/RplFormActions.vue | 8 +- .../RplFormDateRange/RplFormDateRange.css | 8 + .../RplFormDateRange.stories.mdx | 12 + .../RplFormDateRange/RplFormDateRange.vue | 19 +- .../RplFormDropdown/MultiValueLabel.vue | 2 +- .../RplFormDropdown/MultiValueTagList.vue | 2 +- .../RplFormOptions/RplFormCheckboxGroup.vue | 3 +- .../RplFormOptions/RplFormOption.vue | 12 +- .../ripple-ui-forms/src/inputs/dateRange.ts | 14 +- 32 files changed, 1188 insertions(+), 280 deletions(-) create mode 100644 examples/nuxt-app/test/features/search-listing/sidebar-filters.feature create mode 100644 examples/nuxt-app/test/fixtures/search-listing/filters/filters-sidebar.json create mode 100644 examples/nuxt-app/test/fixtures/search-listing/filters/request-sidebar.json create mode 100644 packages/ripple-tide-search/components/TideSearchFilterHeader.vue create mode 100644 packages/ripple-tide-search/components/TideSearchFilterToggle.vue create mode 100644 packages/ripple-tide-search/components/TideSearchResultsHeading.vue diff --git a/examples/nuxt-app/test/features/search-listing/filters.feature b/examples/nuxt-app/test/features/search-listing/filters.feature index 0866a0d89..4c26ded77 100644 --- a/examples/nuxt-app/test/features/search-listing/filters.feature +++ b/examples/nuxt-app/test/features/search-listing/filters.feature @@ -218,6 +218,7 @@ Feature: Search listing - Filter Then the search listing dropdown field labelled "Terms filter example" should have the value "Purple" Then the search listing dropdown field labelled "Custom function filter example" should have the value "Open" And the search listing checkbox field labelled "Show archived content" should be checked + And the search listing checkbox field labelled "Weekdays" should be checked When I clear the search filters diff --git a/examples/nuxt-app/test/features/search-listing/no-results.feature b/examples/nuxt-app/test/features/search-listing/no-results.feature index 2bf2af302..8395a54f8 100644 --- a/examples/nuxt-app/test/features/search-listing/no-results.feature +++ b/examples/nuxt-app/test/features/search-listing/no-results.feature @@ -13,6 +13,7 @@ Feature: No results And the search network request is stubbed with fixture "/search-listing/errors/response-empty" and status 200 When I visit the page "/search-list-grid" Then the search listing page should have 0 results + And the no results component should be display "Sorry! We couldn't find any matches" @mockserver Example: Empty response with custom component @@ -20,3 +21,4 @@ Feature: No results And the search network request is stubbed with fixture "/search-listing/errors/response-empty" and status 200 When I visit the page "/search-list-custom" Then the search listing page should have 0 results + And the no results component should be display "This is a custom component" diff --git a/examples/nuxt-app/test/features/search-listing/search-query.feature b/examples/nuxt-app/test/features/search-listing/search-query.feature index 218033fbb..c64b88578 100644 --- a/examples/nuxt-app/test/features/search-listing/search-query.feature +++ b/examples/nuxt-app/test/features/search-listing/search-query.feature @@ -4,6 +4,26 @@ Feature: Search Queries Given the site endpoint returns fixture "/site/reference" with status 200 And I am using a "macbook-16" device + @mockserver + Example: The search term is displayed after submitting a search with results + Given the page endpoint for path "/" returns fixture "/search-listing/search-query/page" with status 200 + And the search network request is stubbed with fixture "/search-listing/search-query/response" and status 200 + + When I visit the page "/" + Then I type "Grant" into the search input + And I click the search button + Then the search results heading should show "Search results for 'Grant'" + + @mockserver + Example: The search term is not displayed after submitting a search with no results + Given the page endpoint for path "/" returns fixture "/search-listing/search-query/page" with status 200 + And the search network request is stubbed with fixture "/search-listing/errors/response-empty" and status 200 + + When I visit the page "/" + Then I type "Zoo" into the search input + And I click the search button + Then the search results heading should not be displayed + @mockserver Example: The search term query can be extended and a custom query config supplied Given the page endpoint for path "/" returns fixture "/search-listing/search-query/page" with status 200 diff --git a/examples/nuxt-app/test/features/search-listing/sidebar-filters.feature b/examples/nuxt-app/test/features/search-listing/sidebar-filters.feature new file mode 100644 index 000000000..4c970114c --- /dev/null +++ b/examples/nuxt-app/test/features/search-listing/sidebar-filters.feature @@ -0,0 +1,129 @@ +Feature: Search listing - Sidebar filters + + Background: + Given the site endpoint returns fixture "/site/reference" with status 200 + And the search autocomplete request is stubbed with "/search-listing/suggestions/none" fixture + + @mockserver + Example: Sidebar filters are visible open on page load (desktop) + Given I am using a "macbook-16" device + Then the page endpoint for path "/filters" returns fixture "/search-listing/filters/filters-sidebar" with status 200 + And the search network request is stubbed with fixture "/search-listing/filters/response" and status 200 + + When I visit the page "/filters" + Then the search listing filters section should be open + And the search listing filters should be within the sidebar + + @mockserver + Example: Sidebar filters are collapsed open on page load (mobile) + Given I am using a "iphone-x" device + Then the page endpoint for path "/filters" returns fixture "/search-listing/filters/filters-sidebar" with status 200 + And the search network request is stubbed with fixture "/search-listing/filters/response" and status 200 + + When I visit the page "/filters" + Then the search listing filters section should not be open + + @mockserver + Example: Submitting the search bar scrolls to results + Given the page endpoint for path "/filters" returns fixture "/search-listing/filters/filters-sidebar" with status 200 + And the search network request is stubbed with fixture "/search-listing/search-query/response" and status 200 + + When I visit the page "/filters" + And I type "The" into the search input + And I click the search button + Then I should be scrolled to the search results with an offset of 32 + + @mockserver + Example: Sidebar filters update the URL and tally when the filters are applied + Given the page endpoint for path "/filters" returns fixture "/search-listing/filters/filters-sidebar" with status 200 + And the search network request is stubbed with fixture "/search-listing/filters/response" and status 200 + + When I visit the page "/filters" + Then the search listing page should have 2 results + And the sidebar filters heading should show 0 applied filters + And the search network request should be called with the "/search-listing/filters/request-clear-empty" fixture + + When I type "the" into the search input + And I click the search listing dropdown field labelled "Term filter example" + Then I click the option labelled "Blue" in the selected dropdown + + When I click the search listing dropdown field labelled "Terms filter example" + Then I click the option labelled "Purple" in the selected dropdown + And I click the option labelled "Yellow" in the selected dropdown + And I click the search listing dropdown field labelled "Terms filter example" + + When I click the search listing dropdown field labelled "Terms dependent example" + And I click the option labelled "Mammals" in the selected dropdown + When I click the search listing dropdown field labelled "Terms dependent child example" + And I click the option labelled "Dogs" in the selected dropdown + When I click the search listing dropdown field labelled "Terms dependent grandchild example" + And I click the option labelled "Beagle" in the selected dropdown + And I click the option labelled "Spaniel" in the selected dropdown + Then I click the search listing dropdown field labelled "Terms dependent grandchild example" + + And I click the search listing checkbox field labelled "Show archived content" + And I click the search listing checkbox field labelled "Weekdays" + And I click the search listing checkbox field labelled "Weekends" + + Then I enter the range from "2025-02-22" to "2026-06-03" in the date range field labelled "Date range example" + + When I submit the search filters + Then the URL should reflect that the current active filters are as follows: + | id | value | index | + | q | the | 0 | + | termFilter | Blue | 0 | + | termsFilter | Purple | 0 | + | termsFilter | Yellow | 1 | + | dependentFilter | dependentFilter-1:Mammals | 0 | + | dependentFilter | dependentFilter-2:Dogs | 1 | + | dependentFilter | dependentFilter-3:Beagle,Spaniel | 2 | + | checkboxFilter | Archived | | + | checkboxFilterGroup | Weekdays | 0 | + | checkboxFilterGroup | Weekends | 1 | + | dateRangeFilter | from:2025-02-22 | 0 | + | dateRangeFilter | to:2026-06-03 | 1 | + And the sidebar filters heading should show 8 applied filters + + @mockserver + Example: Sidebar filters reflect values from the URL + Given the page endpoint for path "/filters" returns fixture "/search-listing/filters/filters-sidebar" with status 200 + And the search network request is stubbed with fixture "/search-listing/filters/response" and status 200 + + When I visit the page "/filters?q=the&termFilter=Blue&termsFilter=Purple&termsFilter=Yellow&dependentFilter=dependentFilter-1:Mammals&dependentFilter=dependentFilter-2:Dogs&dependentFilter=dependentFilter-3:Beagle,Spaniel&checkboxFilter=Archived&checkboxFilterGroup=Weekends&dateRangeFilter=from:2025-02-22&dateRangeFilter=to:2026-06-03" + And the search network request should be called with the "/search-listing/filters/request-sidebar" fixture + And the sidebar filters heading should show 8 applied filters + + Then the search input should have the value "the" + Then the search listing dropdown field labelled "Term filter example" should have the value "Blue" + Then the search listing dropdown field labelled "Terms filter example" should have the value "Purple, Yellow" + Then the search listing dropdown field labelled "Terms dependent example" should have the value "Mammals" + Then the search listing dropdown field labelled "Terms dependent child example" should have the value "Dogs" + Then the search listing dropdown field labelled "Terms dependent grandchild example" should have the value "Beagle, Spaniel" + And the search listing checkbox field labelled "Show archived content" should be checked + And the search listing checkbox field labelled "Weekends" should be checked + And the search listing date range field labelled "Date range example" should have the values + | from | to | + | 2025-02-22 | 2026-06-03 | + + When I clear the search filters + And the sidebar filters heading should show 0 applied filters + Then the URL should reflect that the current active filters are as follows: + | id | + | q | + | termFilter | + | termsFilter | + | dependentFilter | + | checkboxFilter | + | checkboxFilterGroup | + | dateRangeFilter | + Then the search input should have the value "" + Then the search listing dropdown field labelled "Term filter example" should have the value "Select a colour" + Then the search listing dropdown field labelled "Terms filter example" should have the value "Select a colour" + Then the search listing dropdown field labelled "Terms dependent example" should have the value "Select a species" + Then the search listing dropdown field labelled "Terms dependent child example" should have the value "All sub species" + Then the search listing dropdown field labelled "Terms dependent grandchild example" should have the value "All sub sub species" + And the search listing checkbox field labelled "Show archived content" should not be checked + And the search listing checkbox group labelled "Checkbox group" should not have any options checked + And the search listing date range field labelled "Date range example" should have the values + | from | to | + | | | diff --git a/examples/nuxt-app/test/fixtures/search-listing/filters/filters-sidebar.json b/examples/nuxt-app/test/fixtures/search-listing/filters/filters-sidebar.json new file mode 100644 index 000000000..0a950c012 --- /dev/null +++ b/examples/nuxt-app/test/fixtures/search-listing/filters/filters-sidebar.json @@ -0,0 +1,274 @@ +{ + "title": "Filters sidebar", + "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, + "filtersInSidebar": 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", + "multiple": false, + "value": "termFilter.keyword" + }, + "aggregations": { + "field": "termFilter", + "source": "taxonomy" + }, + "props": { + "id": "termFilter", + "label": "Term filter example", + "placeholder": "Select a colour", + "multiple": false, + "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", + "levels": [ + { + "label": "Terms dependent example", + "placeholder": "Select a species", + "multiple": false + }, + { + "label": "Terms dependent child example", + "placeholder": "All sub species", + "multiple": false + }, + { + "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 + } + ] + } + }, + { + "id": "checkboxFilter", + "component": "TideSearchFilterCheckbox", + "filter": { + "type": "terms", + "value": "checkboxFilter.keyword", + "multiple": false + }, + "props": { + "id": "checkboxFilter", + "label": "Checkbox example", + "checkboxLabel": "Show archived content", + "onValue": "Archived" + } + }, + { + "id": "checkboxFilterGroup", + "component": "TideSearchFilterCheckboxGroup", + "filter": { + "type": "terms", + "value": "checkboxFilterGroup.keyword", + "multiple": true + }, + "props": { + "id": "checkboxFilterGroup", + "label": "Checkbox group", + "layout": "inline", + "options": [ + { + "id": "Weekdays", + "label": "Weekdays", + "value": "Weekdays" + }, + { + "id": "Weekends", + "label": "Weekends", + "value": "Weekends" + } + ] + } + }, + { + "id": "dateRangeFilter", + "component": "TideSearchFilterDateRange", + "filter": { + "type": "range", + "value": "changed", + "multiple": false, + "countAsSingle": true, + "valueIsObject": true + }, + "props": { + "id": "dateRangeFilter", + "label": "Date range example", + "min": "2024-07-01", + "max": "2084-06-30" + } + } + ] + } +} diff --git a/examples/nuxt-app/test/fixtures/search-listing/filters/request-sidebar.json b/examples/nuxt-app/test/fixtures/search-listing/filters/request-sidebar.json new file mode 100644 index 000000000..b1fa16fbe --- /dev/null +++ b/examples/nuxt-app/test/fixtures/search-listing/filters/request-sidebar.json @@ -0,0 +1,90 @@ +{ + "query": { + "bool": { + "must": { + "multi_match": { + "query": "the", + "fields": [ + "title^3", + "field_landing_page_summary^2", + "body", + "field_paragraph_body", + "summary_processed" + ] + } + }, + "filter": [ + { + "terms": { + "type": [ + "grant" + ] + } + }, + { + "terms": { + "field_node_site": [ + 8888 + ] + } + }, + { + "terms": { + "field_species_name": [ + "Beagle", + "Spaniel" + ] + } + }, + { + "range": { + "changed": { + "time_zone": "Australia/Melbourne", + "gte": "2025-02-22", + "lte": "2026-06-03" + } + } + }, + { + "terms": { + "termFilter.keyword": [ + "Blue" + ] + } + }, + { + "terms": { + "termsFilter.keyword": [ + "Purple", + "Yellow" + ] + } + }, + { + "terms": { + "checkboxFilter.keyword": [ + "Archived" + ] + } + }, + { + "terms": { + "checkboxFilterGroup.keyword": [ + "Weekends" + ] + } + } + ] + } + }, + "size": 10, + "from": 0, + "sort": [ + { + "_score": "desc" + }, + { + "_doc": "desc" + } + ] +} diff --git a/packages/nuxt-ripple/components/TideBaseLayout.vue b/packages/nuxt-ripple/components/TideBaseLayout.vue index 398959f50..b4bda3e80 100644 --- a/packages/nuxt-ripple/components/TideBaseLayout.vue +++ b/packages/nuxt-ripple/components/TideBaseLayout.vue @@ -3,6 +3,7 @@ :background="background" :direction="direction" :language="language" + :sideBarPlacement="sideBarPlacement" > @@ -36,12 +32,17 @@ withDefaults(defineProps(), { display: flex; flex-direction: column; gap: var(--rpl-sp-4); + margin-bottom: var(--rpl-sp-6); @media (--rpl-bp-m) { flex-direction: row; justify-content: space-between; align-items: baseline; } + + @media (--rpl-bp-xl) { + margin-bottom: var(--rpl-sp-7); + } } .tide-search-listing-above-result__left { @@ -52,17 +53,23 @@ withDefaults(defineProps(), { @media (--rpl-bp-m) { width: 360px; } + + &:empty { + display: none; + } } .tide-search-listing-above-result--compact { @media (--rpl-bp-m) { flex-direction: column; - .tide-search-listing-above-result__left, - .tide-search-listing-above-result__right { - flex-grow: 1; + .tide-search-listing-above-result__left { width: 100%; } + + .tide-search-listing-above-result__right { + align-self: flex-end; + } } } diff --git a/packages/ripple-tide-search/components/TideSearchFilterHeader.vue b/packages/ripple-tide-search/components/TideSearchFilterHeader.vue new file mode 100644 index 000000000..aca5d80b8 --- /dev/null +++ b/packages/ripple-tide-search/components/TideSearchFilterHeader.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/packages/ripple-tide-search/components/TideSearchFilterToggle.vue b/packages/ripple-tide-search/components/TideSearchFilterToggle.vue new file mode 100644 index 000000000..c49cdf6a2 --- /dev/null +++ b/packages/ripple-tide-search/components/TideSearchFilterToggle.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/packages/ripple-tide-search/components/TideSearchFilters.vue b/packages/ripple-tide-search/components/TideSearchFilters.vue index 317479f36..e9eb7f4e5 100644 --- a/packages/ripple-tide-search/components/TideSearchFilters.vue +++ b/packages/ripple-tide-search/components/TideSearchFilters.vue @@ -4,14 +4,19 @@ :title="title" @submit="handleFilterSubmit" > -
-
+
+
@@ -58,6 +65,7 @@ interface Props { resetLabel?: string | boolean reverseStyling?: boolean isBusy?: boolean + display?: 'inline' | 'block' } const emit = defineEmits<{ @@ -68,7 +76,8 @@ const emit = defineEmits<{ const props = withDefaults(defineProps(), { submitLabel: 'Apply search filters', resetLabel: 'Clear search filters', - reverseStyling: false + reverseStyling: false, + display: 'inline' }) const handleFilterReset = (event: rplEventPayload) => { @@ -83,3 +92,9 @@ const handleFilterSubmit = (formValues) => { }) } + + diff --git a/packages/ripple-tide-search/components/TideSearchListingPage.vue b/packages/ripple-tide-search/components/TideSearchListingPage.vue index f706ea837..b4807aa5a 100644 --- a/packages/ripple-tide-search/components/TideSearchListingPage.vue +++ b/packages/ripple-tide-search/components/TideSearchListingPage.vue @@ -7,7 +7,7 @@ import { computed } from '#imports' import { submitForm } from '@formkit/vue' -import { useDebounceFn } from '@vueuse/core' +import { useBreakpoints, useDebounceFn } from '@vueuse/core' import useTideSearch from './../composables/useTideSearch' import type { TidePageBase, TideSiteData } from '@dpc-sdp/ripple-tide-api/types' import type { @@ -16,7 +16,7 @@ import type { TideSearchListingConfig } from './../types' import type { ITideSecondaryCampaign } from '@dpc-sdp/ripple-tide-landing-page/mapping/secondary-campaign/secondary-campaign-mapping' -import { useRippleEvent } from '@dpc-sdp/ripple-ui-core' +import { bpMin, useRippleEvent } from '@dpc-sdp/ripple-ui-core' import type { rplEventPayload } from '@dpc-sdp/ripple-ui-core' import { watch } from 'vue' @@ -79,6 +79,7 @@ const props = withDefaults(defineProps(), { }, showFiltersOnLoad: false, showFiltersOnly: false, + filtersInSidebar: false, scrollToResultsOnSubmit: true }) as any, resultsLayout: () => ({ @@ -117,10 +118,15 @@ const emit = defineEmits<{ const { emitRplEvent } = useRippleEvent('tide-search', emit) -const filtersExpanded = ref( +const breakpoints = useBreakpoints(bpMin) +const isMobile = breakpoints.smaller('m') + +const initialFiltersState = + props.searchListingConfig?.filtersInSidebar || props.searchListingConfig?.showFiltersOnLoad || - props.searchListingConfig?.showFiltersOnly -) + props.searchListingConfig?.showFiltersOnly + +const filtersExpanded = ref(Boolean(initialFiltersState)) const { isBusy, @@ -128,6 +134,7 @@ const { getSuggestions, clearSuggestions, searchTerm, + appliedSearchTerm, results, suggestions, filterForm, @@ -206,7 +213,15 @@ onAggregationUpdateHook.value = (aggs: any) => { }) } -const resultsContainer = '.rpl-layout__body-wrap' +const resultsContainer = computed((): string => { + return props.searchListingConfig?.filtersInSidebar + ? '.rpl-layout__main' + : '.rpl-layout__body-wrap' +}) + +const resultsScrollOffset = computed((): number => { + return props.searchListingConfig?.filtersInSidebar ? 32 : 0 +}) const emitSearchEvent = (event: any) => { emitRplEvent( @@ -232,7 +247,7 @@ const handleSearchSubmit = (event: any) => { // If there's no filters in the form, we need to just do the search without submitting the filter form submitSearch() if (event?.type === 'button') { - scrollToResults(resultsContainer) + scrollToResults(resultsContainer.value, resultsScrollOffset.value) } emitSearchEvent({ ...event, ...baseEvent() }) } @@ -246,7 +261,7 @@ const handleFilterSubmit = (event: any) => { !cachedSubmitEvent.value?.type || cachedSubmitEvent.value?.type === 'button' ) { - scrollToResults(resultsContainer) + scrollToResults(resultsContainer.value, resultsScrollOffset.value) } emitSearchEvent({ ...event, ...cachedSubmitEvent.value, ...baseEvent() }) @@ -313,15 +328,15 @@ const handleSortChange = (sortId: any) => { changeSortOrder(sortId) } -const handleToggleFilters = () => { +const handleToggleFilters = (event: rplEventPayload) => { filtersExpanded.value = !filtersExpanded.value emitRplEvent( 'toggleFilters', { ...baseEvent(), - action: filtersExpanded.value ? 'open' : 'close', - text: toggleFiltersLabel.value + ...event, + action: filtersExpanded.value ? 'open' : 'close' }, { global: true } ) @@ -331,14 +346,6 @@ const numAppliedFilters = computed(() => { return getActiveFiltersTally(appliedFilters.value, props.userFilters) }) -const toggleFiltersLabel = computed(() => { - let label = 'Filters' - - return numAppliedFilters.value - ? `${label} (${numAppliedFilters.value})` - : label -}) - watch( () => isBusy.value, (loading, prevLoading) => { @@ -354,6 +361,12 @@ watch( } } ) + +onMounted(() => { + if (props.searchListingConfig?.filtersInSidebar && isMobile.value) { + filtersExpanded.value = false + } +})
+ diff --git a/packages/ripple-tide-search/components/global/TideSearchFilterDependent.vue b/packages/ripple-tide-search/components/global/TideSearchFilterDependent.vue index 656378f6d..5016c4cd7 100644 --- a/packages/ripple-tide-search/components/global/TideSearchFilterDependent.vue +++ b/packages/ripple-tide-search/components/global/TideSearchFilterDependent.vue @@ -7,6 +7,7 @@ interface Props { options?: any[] variant?: string columns?: string + display?: 'inline' | 'block' levels: { label: string placeholder: string @@ -18,7 +19,8 @@ const props = withDefaults(defineProps(), { options: () => [], multiple: false, variant: 'default', - columns: 'rpl-col-6-m', + columns: undefined, + display: 'inline', levels: () => [] }) @@ -124,15 +126,19 @@ watch( }, { deep: true } ) + +const columns = computed(() => { + if (props.columns) { + return `rpl-col-12 ${props.columns}` + } + + return props.display === 'block' ? 'rpl-col-12' : 'rpl-col-12 rpl-col-6-m' +})