Skip to content

Commit

Permalink
Merge pull request #977 from geoadmin/bug-PB-751-search-focus
Browse files Browse the repository at this point in the history
PB-751: Focus on search bar when starting the application - #patch
  • Loading branch information
ltshb authored Jul 4, 2024
2 parents 0b486bf + a8a98fa commit 39a4696
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/modules/menu/MenuModule.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function toggleMenu() {
<transition name="fade-in-out">
<BlackBackdrop v-if="isPhoneMode && isMenuShown" @click="toggleMenu" />
</transition>
<!-- NOTE: Below we need to use v-show and not v-if otherwise when the user toggle the full-screen while
editing a Report a problem window he will loose his content -->
<HeaderWithSearch v-show="isHeaderShown" class="header" />
<DebugToolbar v-if="hasDevSiteWarning" class="position-absolute end-0 debug-toolbar" />
<div
Expand Down
2 changes: 1 addition & 1 deletion src/modules/menu/components/header/HeaderWithSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/>
</div>
<div
class="search-bar-section d-flex-column flex-grow-1"
class="search-bar-section d-flex-column flex-grow-1 me-2"
:class="{ 'align-self-center': !hasDevSiteWarning }"
>
<SearchBar />
Expand Down
51 changes: 37 additions & 14 deletions src/modules/menu/components/search/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useStore } from 'vuex'
import SearchResultList from '@/modules/menu/components/search/SearchResultList.vue'
import log from '@/utils/logging'
const dispatcher = { dispatcher: 'SearchBar' }
const store = useStore()
const isPristine = ref(true)
const showResults = ref(false)
const searchInput = ref(null)
const searchValue = ref('')
Expand All @@ -20,8 +22,11 @@ const isPhoneMode = computed(() => store.getters.isPhoneMode)
watch(hasResults, (newValue) => {
// if an entry has been selected from the list, do not show the list again
// because the list has been hidden by onEntrySelected
if (!selectedEntry.value) {
// because the list has been hidden by onEntrySelected. Also if the search bar is pristine (not
// yet modified by the user) we don't want to show the result (e.g. at startup with a swisssearch
// query param)
if (!selectedEntry.value && !isPristine.value) {
log.debug(`Search has result changed to ${newValue}, change the show result to ${newValue}`)
showResults.value = newValue
}
})
Expand All @@ -38,30 +43,28 @@ watch(searchQuery, (newQuery) => {
onMounted(() => {
searchValue.value = searchQuery.value
searchInput.value.focus()
})
let debounceSearch = null
const updateSearchQuery = (event) => {
isPristine.value = false
selectedEntry.value = null
searchValue.value = event.target.value
if (hasResults.value) {
// we already have a result make sure to display it as soon as the user is typing
showResults.value = true
}
clearTimeout(debounceSearch)
debounceSearch = setTimeout(() => {
store.dispatch('setSearchQuery', { query: event.target.value, ...dispatcher })
}, 100)
}
const onSearchInputFocus = (event) => {
// When the focus event is due to a programatic focus event, the relatedTarget is not null
// and in this case we don't want to show the result. For example when selecting a result value
// we want to close the result and focus on the input, so that the user can directly change
// the search.
if (!event.relatedTarget && hasResults.value) {
showResults.value = true
}
}
const clearSearchQuery = () => {
hasResults.value = false
showResults.value = false
selectedEntry.value = null
searchValue.value = ''
Expand Down Expand Up @@ -110,6 +113,16 @@ const toggleResults = () => {
showResults.value = !showResults.value
}
}
const onInputClicked = () => {
if (hasResults.value) {
if (isPhoneMode.value) {
showResults.value = !showResults.value
} else {
showResults.value = true
}
}
}
</script>
<template>
Expand Down Expand Up @@ -137,12 +150,22 @@ const toggleResults = () => {
:value="searchValue"
data-cy="searchbar"
tabindex="0"
@click="onInputClicked"
@input="updateSearchQuery"
@focus="onSearchInputFocus"
@keydown.down.prevent="goToFirstResult"
@keydown.esc.prevent="toggleResults"
@keydown.esc.prevent="clearSearchQuery"
@keyup.enter.stop.prevent="goToFirstResult"
/>
<button
v-if="hasResults && !isPhoneMode"
class="btn btn-outline-group"
type="button"
tabindex="0"
data-cy="searchbar-toggle-result"
@click="toggleResults"
>
<FontAwesomeIcon :icon="showResults ? 'caret-up' : 'caret-down'" />
</button>
<button
v-show="searchValue"
id="clearSearchButton"
Expand Down
7 changes: 4 additions & 3 deletions src/modules/menu/components/search/SearchResultList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ defineExpose({ focusFirstEntry })
class="shadow-lg search-results bg-light"
:class="{
'border-top border-bottom': isPhoneMode,
'border rounded-bottom': !isPhoneMode,
'rounded-bottom': !isPhoneMode,
}"
data-cy="search-results"
>
<div class="search-results-inner">
<div class="search-results-inner" :class="{ 'rounded-bottom': !isPhoneMode }">
<SearchResultCategory
v-for="(category, index) in categories"
v-show="category.results.length > 0"
Expand All @@ -165,7 +165,8 @@ defineExpose({ focusFirstEntry })
.search-results-container {
position: fixed;
z-index: $zindex-menu-header;
// here we need -1 in order to have the focus highlight of the search on top of the result
z-index: calc($zindex-menu-header - 1);
top: $header-height;
// 45vh (so that previews are visible), but on small screen min 20rem (ca. 3 lines per category)
height: min(calc(100vh - $header-height), max(20rem, 45vh));
Expand Down
16 changes: 14 additions & 2 deletions tests/cypress/tests-e2e/search/search-results.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,11 @@ describe('Test the search bar result handling', () => {
'Checking that it reads the swisssearch URL param at startup and launch a search with its content'
)
cy.reload()
cy.waitMapIsReady()
cy.wait(['@search-locations', '@search-layers'])
cy.readStoreValue('state.search.query').should('eq', 'test')
cy.get('@locationSearchResults').should('not.be.visible')
cy.get(searchbarSelector).click()
cy.get('@locationSearchResults').should('be.visible')

cy.log('Checking that it displays layer results with info-buttons')
Expand Down Expand Up @@ -211,11 +214,20 @@ describe('Test the search bar result handling', () => {
cy.get(searchbarSelector).paste('test')
cy.wait(`@search-locations`)

cy.log('Toggling the search result with ESC')
cy.log('Clearing the search result with ESC')
cy.get(searchbarSelector).trigger('keydown', { key: 'Escape' })
cy.get('[data-cy="search-results"]').should('not.be.visible')
cy.get(searchbarSelector).trigger('keydown', { key: 'Escape' })
cy.get(searchbarSelector).should('have.value', '')

cy.log('Toggling the search result with caret button')
cy.viewport(600, 600)
cy.get(searchbarSelector).paste('test')
cy.wait(`@search-locations`)
cy.get('[data-cy="searchbar-toggle-result"]').should('be.visible').click()
cy.get('[data-cy="search-results"]').should('not.be.visible')
cy.get('[data-cy="searchbar-toggle-result"]').should('be.visible').click()
cy.get('[data-cy="search-results"]').should('be.visible')
cy.viewport('iphone-se2')

cy.log('Navigating with arrow UP/DOWN')
cy.get(searchbarSelector).trigger('keydown', { key: 'ArrowDown' })
Expand Down

0 comments on commit 39a4696

Please sign in to comment.