Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions resource/css/skosmos.css
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ header .container-fluid .row {
color: var(--headerbar-text);
}

#search-wrapper .dropdown-menu li label:hover, #search-wrapper .dropdown-menu li a:hover {
#search-wrapper .dropdown-menu li label:hover, #search-wrapper .dropdown-menu li:hover {
color: var(--search-dropdown-hover-text);
}

Expand All @@ -291,7 +291,7 @@ header .container-fluid .row {
border: 1px solid var(--search-border);
border-radius: 0;
padding-left: 1rem;
padding-right: 1.5rem;
padding-right: 2.5rem;
}

#search-wrapper .dropdown-toggle:hover, #search-wrapper .dropdown-toggle.show {
Expand All @@ -309,9 +309,10 @@ header .container-fluid .row {

#search-wrapper .chevron {
position: absolute;
right: 0.5rem;
top: 33%;
display: block;
right: 1rem;
top: 50%;
transform: translate(0, -50%);

z-index: 2;
}

Expand Down
36 changes: 26 additions & 10 deletions resource/js/global-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,35 @@ function startGlobalSearchApp () {
},
computed: {
vocabSelectorPlaceholder () {
return $t('1. Choose vocabulary')
return $t('Choose vocabulary')
},
langSelectorPlaceholder () {
return $t('2. Choose language')
return $t('Choose language')
},
searchPlaceholder () {
return $t('3. Enter search term')
return $t('Enter search term')
},
anyLanguage () {
return $t('Any language')
},
noResults () {
return $t('No results')
},
selectSearchVocabAriaMessage () {
return $t('Select search vocabularies')
},
selectSearchLanguageAriaMessage () {
return $t('Select search language')
},
textInputWithDropdownButtonAriaMessage () {
return $t('Text input with dropdown button')
searchFieldAriaMessage () {
return $t('Enter search term')
},
searchAriaMessage () {
searchButtonAriaMessage () {
return $t('Search')
},
clearSearchAriaMessage () {
return $t('Clear search field')
},
getSelectedVocabs () {
return this.selectedVocabs.map(key => ({ key, value: this.vocabStrings[key] }))
},
Expand Down Expand Up @@ -261,6 +267,10 @@ function startGlobalSearchApp () {
this.searchTerm = ''
this.renderedResultsList = []
this.hideAutoComplete()

this.$nextTick(() => {
this.$refs.globalSearchInputField.focus()
})
},
onLangMenuKeydown (e) {
const items = Array.from(e.currentTarget.querySelectorAll('input'))
Expand Down Expand Up @@ -426,7 +436,7 @@ function startGlobalSearchApp () {
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
aria-expanded="false"
:aria-label="selectSearchLanguageAriaMessage"
:aria-label="selectSearchVocabAriaMessage"
v-if="languageStrings"
v-key-nav="dropdownKeyNav"
>
Expand Down Expand Up @@ -493,11 +503,12 @@ function startGlobalSearchApp () {
<div class="input-group flex-nowrap" id="search-form">
<span id="headerbar-search" class="dropdown">
<input type="search"
ref="globalSearchInputField"
class="form-control"
id="search-field"
autocomplete="off"
data-bs-toggle=""
:aria-label="textInputWithDropdownButtonAriaMessage"
:aria-label="searchFieldAriaMessage"
:placeholder="searchPlaceholder"
v-click-outside="hideAutoComplete"
v-model="searchTerm"
Expand Down Expand Up @@ -589,10 +600,15 @@ function startGlobalSearchApp () {
</li>
</ul>
</span>
<button id="clear-button" class="btn btn-danger" type="clear" v-if="searchTerm" @click="resetSearchTermAndHideDropdown()">
<button id="clear-button"
class="btn btn-danger"
:aria-label="clearSearchAriaMessage"
type="clear"
v-if="searchTerm"
@click="resetSearchTermAndHideDropdown()">
<i class="fa-solid fa-xmark"></i>
</button>
<button id="search-button" class="btn btn-outline-secondary" :aria-label="searchAriaMessage" @click="gotoSearchPage()">
<button id="search-button" class="btn btn-outline-secondary" :aria-label="searchButtonAriaMessage" @click="gotoSearchPage()">
<i class="fa-solid fa-magnifying-glass"></i>
</button>
</div>
Expand Down
167 changes: 134 additions & 33 deletions resource/js/vocab-search.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global Vue, $t, onTranslationReady */
/* global Vue, bootstrap, $t, onTranslationReady */

function startVocabSearchApp () {
const vocabSearch = Vue.createApp({
Expand All @@ -10,13 +10,14 @@
renderedResultsList: [],
languageStrings: null,
uriPrefixes: {},
showDropdown: false,
showAutoCompleteDropdown: false,
focusedLangIndex: -1,
showNotation: null
}
},
computed: {
searchPlaceholder () {
return $t('Search...')
return $t('Search in this vocabulary')
},
anyLanguages () {
return $t('Any language')
Expand All @@ -27,11 +28,14 @@
selectSearchLanguageAriaMessage () {
return $t('Select search language')
},
textInputWithDropdownButtonAriaMessage () {
return $t('Text input with dropdown button')
searchFieldAriaMessage () {
return $t('Search in this vocabulary')
},
searchAriaMessage () {
searchButtonAriaMessage () {
return $t('Search')
},
clearSearchAriaMessage () {
return $t('Clear search field')
}
},
mounted () {
Expand All @@ -41,6 +45,20 @@
this.renderedResultsList = []
this.uriPrefixes = {}
this.showNotation = window.SKOSMOS.showNotation

this.langMenuKeydownHandler = (e) => {
// Bypass Bootstrap event listener on window level
if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
if (e.target.closest('#language-selector') && e.target.className === 'dropdown-item') {
e.stopImmediatePropagation()
this.onLangMenuKeydown(e)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with high complexity (count = 5): langMenuKeydownHandler [qlty:function-complexity]

}
window.addEventListener('keydown', this.langMenuKeydownHandler, true)

Check warning on line 58 in resource/js/vocab-search.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=NatLibFi_Skosmos&issues=AZ219DfmewiKqKX_pT86&open=AZ219DfmewiKqKX_pT86&pullRequest=2017
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with high complexity (count = 5): mounted [qlty:function-complexity]

},
beforeUnmount () {
window.removeEventListener('keydown', this.langMenuKeydownHandler, true)

Check warning on line 61 in resource/js/vocab-search.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=NatLibFi_Skosmos&issues=AZ219DfmewiKqKX_pT87&open=AZ219DfmewiKqKX_pT87&pullRequest=2017
},
methods: {
autoComplete () {
Expand Down Expand Up @@ -198,7 +216,7 @@
this.showAutoComplete()
},
hideAutoComplete () {
this.showDropdown = false
this.showAutoCompleteDropdown = false
this.$forceUpdate()
},
gotoSearchPage () {
Expand Down Expand Up @@ -232,50 +250,128 @@
this.searchTerm = ''
this.renderedResultsList = []
this.hideAutoComplete()

this.$nextTick(() => {
this.$refs.searchInputField.focus()
})
},
/*
* Show the existing autocomplete list if it was hidden by onClickOutside()
*/
showAutoComplete () {
this.showDropdown = true
this.showAutoCompleteDropdown = true
this.$forceUpdate()
},
onLangMenuKeydown (event) {
const items = this.$refs.langMenu.querySelectorAll('[role="menuitemradio"]')
switch (event.key) {
case 'ArrowDown': {
event.preventDefault()
if (this.focusedLangIndex === items.length) {
this.focusedLangIndex = 0
items[this.focusedLangIndex].focus()
} else {
this.focusedLangIndex = (this.focusedLangIndex + 1) % items.length
items[this.focusedLangIndex].focus()
}
break
}
case 'ArrowUp': {
event.preventDefault()
if (this.focusedLangIndex === 0) {
this.closeLangMenu()
}
this.focusedLangIndex =
(this.focusedLangIndex - 1 + items.length) % items.length
items[this.focusedLangIndex].focus()
break
}
case 'Enter':
case ' ': {
event.preventDefault()
if (event.target.classList.contains('dropdown-toggle')) {
this.openLangMenu()
} else {
this.changeContentLangAndReload(Object.keys(this.languageStrings)[this.focusedLangIndex])
}
break
}
case 'Escape': {
this.closeLangMenu()
break
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with high complexity (count = 13): onLangMenuKeydown [qlty:function-complexity]

},
openLangMenu () {
const btn = this.$refs.langButton
const dropdown = bootstrap.Dropdown.getOrCreateInstance(btn)
dropdown.show()

this.$nextTick(() => {
const items = this.$refs.langMenu.querySelectorAll('[role="menuitemradio"]')

this.focusedLangIndex = Math.max(
0,
Object.keys(this.languageStrings).indexOf(this.selectedLanguage)
)

items[this.focusedLangIndex]?.focus()
})
},

closeLangMenu () {
const btn = this.$refs.langButton
const dropdown = bootstrap.Dropdown.getOrCreateInstance(btn)
dropdown.hide()

this.focusedLangIndex = -1
this.$nextTick(() => {
this.$refs.langButton.focus()
})
}
},
template: `
<div class="input-group ps-xl-2 flex-nowrap" id="search-wrapper">

<div class="dropdown" id="language-selector">
<button class="btn btn-outline-secondary dropdown-toggle"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
:aria-label="selectSearchLanguageAriaMessage"
v-if="languageStrings">
{{ languageStrings[selectedLanguage] }}
<i class="fa-solid fa-chevron-down"></i>
</button>
<ul class="dropdown-menu" id="language-list" role="menu">
<li v-for="(value, key) in languageStrings" :key="key" role="none">
<a
class="dropdown-item"
:value="key"
<button
ref="langButton"
class="btn btn-outline-secondary dropdown-toggle"
data-bs-toggle="dropdown"
@keydown="onLangMenuKeydown"
aria-haspopup="true"
:aria-label="selectSearchLanguageAriaMessage">
<template v-if="languageStrings">{{ languageStrings[selectedLanguage] }}</template>
<i class="chevron fa-solid fa-chevron-down"></i>
</button>

<ul
ref="langMenu"
id="language-list"
class="dropdown-menu"
role="menu">
<li
v-for="(value, key, index) in languageStrings"
:key="key"
role="menuitemradio"
:aria-checked="selectedLanguage === key"
:tabindex="focusedLangIndex === index ? 0 : -1"
@click="changeContentLangAndReload(key)"
@keydown.enter="changeContentLangAndReload(key)"
role="menuitem"
tabindex=0 >
@focus="focusedLangIndex = index"
class="dropdown-item">
{{ value }}
</a>
</li>
</ul>
</div>
</li>
</ul>
</div>

<span id="headerbar-search" class="dropdown">
<input type="search"
ref="searchInputField"
class="form-control"
id="search-field"
autocomplete="off"
data-bs-toggle=""
:aria-label="textInputWithDropdownButtonAriaMessage"
:aria-label="searchFieldAriaMessage"
:placeholder="searchPlaceholder"
v-click-outside="hideAutoComplete"
v-model="searchTerm"
Expand All @@ -284,7 +380,7 @@
@click="showAutoComplete()">
<ul id="search-autocomplete-results"
class="dropdown-menu w-100"
:class="{ 'show': showDropdown }"
:class="{ 'show': showAutoCompleteDropdown }"
aria-labelledby="search-field">
<li class="autocomplete-result container" v-for="result in renderedResultsList"
:key="result.prefLabel" >
Expand Down Expand Up @@ -367,10 +463,15 @@
</li>
</ul>
</span>
<button id="clear-button" class="btn btn-danger" type="clear" v-if="searchTerm" @click="resetSearchTermAndHideDropdown()">
<button id="clear-button"
class="btn btn-danger"
type="clear"
:aria-label="clearSearchAriaMessage"
v-if="searchTerm"
@click="resetSearchTermAndHideDropdown()">
<i class="fa-solid fa-xmark"></i>
</button>
<button id="search-button" class="btn btn-outline-secondary" :aria-label="searchAriaMessage" @click="gotoSearchPage()">
<button id="search-button" class="btn btn-outline-secondary" :aria-label="searchButtonAriaMessage" @click="gotoSearchPage()">
<i class="fa-solid fa-magnifying-glass"></i>
</button>
</div>
Expand Down
Loading
Loading