diff --git a/examples/nuxt-app/test/features/landingpage/custom-collection.feature b/examples/nuxt-app/test/features/landingpage/custom-collection.feature index cfae917ec1..7be02c7e60 100644 --- a/examples/nuxt-app/test/features/landingpage/custom-collection.feature +++ b/examples/nuxt-app/test/features/landingpage/custom-collection.feature @@ -120,6 +120,33 @@ Feature: Custom Collection Then the custom collection component results count should read "Displaying 1-20 of 282 results" And only the search filters should be visible + @mockserver + Example: Should show filters on load when showFiltersOnLoad is set + Given I load the page fixture with "/landingpage/custom-collection/form-theme-default" + And the custom collection config has "showFiltersOnLoad" set to "true" + And the search network request is stubbed with fixture "/landingpage/custom-collection/response" and status 200 + Then the page endpoint for path "/filters-on-load" returns the loaded fixture + + When I visit the page "/filters-on-load" + Then the custom collection filters should be open + + When I toggle the content collection filters + Then the custom collection filters should not be open + + @mockserver + Example: Should collapse filters on mobile load when showFiltersOnLoad is set + Given I am using a "iphone-x" device + Then I load the page fixture with "/landingpage/custom-collection/form-theme-default" + And the custom collection config has "showFiltersOnLoad" set to "true" + And the search network request is stubbed with fixture "/landingpage/custom-collection/response" and status 200 + Then the page endpoint for path "/filters-on-load" returns the loaded fixture + + When I visit the page "/filters-on-load" + Then the custom collection filters should not be open + + When I toggle the content collection filters + Then the custom collection filters should be open + @mockserver Example: Should hide results count when hideResultsCount is set Given I load the page fixture with "/landingpage/custom-collection/page" diff --git a/examples/nuxt-app/test/features/search-listing/filters.feature b/examples/nuxt-app/test/features/search-listing/filters.feature index 0866a0d89a..f1e7510d6e 100644 --- a/examples/nuxt-app/test/features/search-listing/filters.feature +++ b/examples/nuxt-app/test/features/search-listing/filters.feature @@ -15,6 +15,21 @@ Feature: Search listing - Filter When I visit the page "/filters" Then the search listing filters section should be open + When I toggle the search listing filters section + Then the search listing filters section should not be open + + @mockserver + Example: Filter collapsed open on mobile page load + Given I am using a "iphone-x" device + Then the page endpoint for path "/filters" returns fixture "/search-listing/filters/filters-open" 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 + + When I toggle the search listing filters section + Then the search listing filters section should be open + @mockserver Example: Raw filter - Should reflect the value from the URL Given the page endpoint for path "/filters" returns fixture "/search-listing/filters/page" with status 200 @@ -218,6 +233,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 2bf2af3028..c3b732fe7f 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 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 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 218033fbbe..8c2c9b88cb 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,29 @@ 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'" + + When I type "Grants" into the search input + 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 0000000000..4c970114c3 --- /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/features/site/search.feature b/examples/nuxt-app/test/features/site/search.feature index d281a6d79f..7afb9a7117 100644 --- a/examples/nuxt-app/test/features/site/search.feature +++ b/examples/nuxt-app/test/features/site/search.feature @@ -14,6 +14,7 @@ Feature: Site search Then the search listing page should have 5 results And the filters toggle should show 0 applied filters And the search input should have the value "demo" + Then the search results heading should show "Search results for 'demo'" And the search listing results count should read "Displaying 1-5 of 5 results" And the search listing results should have following items: | title | content | url | component | @@ -32,9 +33,13 @@ Feature: Site search | filters[0][values][0] | Education | And the network request "siteSearchReq" should be called with the "/site/search-request" fixture + When I type "the" into the search input + Then the search results heading should show "Search results for 'demo'" + When I clear the search filters Then the filters toggle should show 0 applied filters And the search input should have the value "" + And the search results heading should not be displayed @mockserver Example: Overrides site search content types with feature flag @@ -55,3 +60,4 @@ Feature: Site search Given the "/api/tide/search/**" network request is stubbed with fixture "/site/search-response" and status 200 as alias "siteSearchReq" When I visit the page "/search" Then the search input should be have a max length of 128 + And the search results heading should not be displayed 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 0000000000..0a950c0129 --- /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 0000000000..b1fa16fbec --- /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 398959f507..b4bda3e804 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 0000000000..aca5d80b8c --- /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 0000000000..c49cdf6a26 --- /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 317479f368..e9eb7f4e52 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 f706ea837f..59591a3cbe 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,17 +118,25 @@ const emit = defineEmits<{ const { emitRplEvent } = useRippleEvent('tide-search', emit) -const filtersExpanded = ref( - props.searchListingConfig?.showFiltersOnLoad || +const breakpoints = useBreakpoints(bpMin) +const isMobile = breakpoints.smaller('m') + +const initialFiltersExpanded = Boolean( + props.searchListingConfig?.filtersInSidebar || + props.searchListingConfig?.showFiltersOnLoad || props.searchListingConfig?.showFiltersOnly ) +const filtersExpanded = ref(initialFiltersExpanded) +const filtersMobileClass = ref('hidden') + const { isBusy, searchError, getSuggestions, clearSuggestions, searchTerm, + appliedSearchTerm, results, suggestions, filterForm, @@ -206,7 +215,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 +249,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 +263,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 +330,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 +348,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 +363,15 @@ watch( } } ) + +onMounted(() => { + // If filters are shown on load for desktop, we still need to hide them on mobile + if (initialFiltersExpanded && isMobile.value) { + filtersExpanded.value = false + } + + nextTick(() => (filtersMobileClass.value = 'visible')) +})
+