diff --git a/app/frontend/controllers/search_controller.js b/app/frontend/controllers/search_controller.js index 69162c021..6d447f7f8 100644 --- a/app/frontend/controllers/search_controller.js +++ b/app/frontend/controllers/search_controller.js @@ -1,18 +1,26 @@ import { Controller } from '@hotwired/stimulus' import accessibleAutocomplete from 'accessible-autocomplete' import 'accessible-autocomplete/dist/accessible-autocomplete.min.css' -import { searchSuggestion, fetchAndPopulateSearchResults } from '../modules/search' +import { searchSuggestion, fetchAndPopulateSearchResults, confirmSelectedOption, searchableName } from '../modules/search' -let hints = [] -const populateHint = (results) => { - hints = results +const options = [] +const populateOptions = (results, selectEl) => { + selectEl.innerHTML = '' + + Object.keys(results).forEach((key) => { + const option = document.createElement('option') + option.value = key + option.innerHTML = searchableName(results[key]) + option.setAttribute('data-hint', results[key].hint) + option.textContent = searchableName(results[key]) + selectEl.appendChild(option) + options.push(option) + }) } export default class extends Controller { connect () { const selectEl = this.element - const selectOptions = Array.from(selectEl.options).filter(function (option, index, arr) { return option.value !== '' }) - const matches = /^(\w+)\[(\w+)\]$/.exec(selectEl.name) const rawFieldName = matches ? `${matches[1]}[${matches[2]}_raw]` : '' const relativeUrlRoute = JSON.parse(this.element.dataset.info).relative_url_route @@ -22,26 +30,13 @@ export default class extends Controller { selectElement: selectEl, minLength: 1, source: (query, populateResults) => { - fetchAndPopulateSearchResults(query, populateResults, populateHint, relativeUrlRoute) + fetchAndPopulateSearchResults(query, populateResults, relativeUrlRoute, populateOptions, selectEl) }, autoselect: true, placeholder: 'Start typing to search', - templates: { suggestion: (value) => searchSuggestion(value, hints) }, + templates: { suggestion: (value) => searchSuggestion(value, options) }, name: rawFieldName, - onConfirm: (val) => { - const selectedOption = [].filter.call( - Array.from(selectEl.options).filter(function (option, index, arr) { return option.value !== '' }), - (option) => option.value === val - )[0] - if (selectedOption) selectedOption.selected = true - } - }) - - const parentElement = selectEl.parentElement - const inputElement = parentElement.querySelector('[role=combobox]') - - inputElement.addEventListener('input', () => { - selectOptions.forEach((option) => { option.selected = false }) + onConfirm: (val) => confirmSelectedOption(selectEl, val) }) } } diff --git a/app/frontend/modules/search.js b/app/frontend/modules/search.js index b8fcf2e2c..b232c2a08 100644 --- a/app/frontend/modules/search.js +++ b/app/frontend/modules/search.js @@ -117,11 +117,11 @@ export const suggestion = (value, options) => { } } -export const searchSuggestion = (value, hints) => { +export const searchSuggestion = (value, options) => { try { - const result = hints[value.toString()] + const result = enhanceOption(options.find((o) => o.innerHTML === value)) if (result) { - const html = result.append ? `${result.value} ${result.append}` : `${result.value}` + const html = result.append ? `${result.text} ${result.append}` : `${result.text}` return result.hint ? `${html}
${result.hint}
` : html } else { return 'No results found' @@ -143,17 +143,22 @@ export const enhanceOption = (option) => { } } -export const fetchAndPopulateSearchResults = async (query, populateResults, populateHint, relativeUrlRoute) => { +export const fetchAndPopulateSearchResults = async (query, populateResults, relativeUrlRoute, populateOptions, selectEl) => { if (/\S/.test(query)) { const results = await fetchUserOptions(query, relativeUrlRoute) - populateResults(Object.keys(results)) - populateHint(results) + populateOptions(results, selectEl) + populateResults(Object.values(results).map((o) => searchableName(o))) } } export const fetchUserOptions = async (query, relativeUrlRoute) => { try { - const response = await fetch(`${relativeUrlRoute}/users/search?query=${encodeURIComponent(query)}`) + let response + if (relativeUrlRoute) { + response = await fetch(`${relativeUrlRoute}/users/search?query=${encodeURIComponent(query)}`) + } else { + response = await fetch(`/users/search?query=${encodeURIComponent(query)}`) + } const results = await response.json() return results } catch (error) { @@ -165,3 +170,17 @@ export const fetchUserOptions = async (query, relativeUrlRoute) => { export const getSearchableName = (option) => { return option.getAttribute('data-hint') ? option.text + ' ' + option.getAttribute('data-hint') : option.text } + +export const searchableName = (option) => { + return option.value + ' ' + option.hint +} + +export const confirmSelectedOption = (selectEl, val) => { + const arrayOfOptions = Array.from(selectEl.options).filter(function (option, index, arr) { return option.value !== '' }) + + const selectedOption = [].filter.call( + arrayOfOptions, + (option) => option.innerHTML === val + )[0] + if (selectedOption) selectedOption.selected = true +}