/* global accessibleAutocomplete, fetch */ //= require accessible-autocomplete/dist/accessible-autocomplete.min.js window.GOVUK = window.GOVUK || {} window.GOVUK.Modules = window.GOVUK.Modules || {}; (function (Modules) { class GemSearchWithAutocomplete { constructor ($module) { this.$module = $module this.$originalInput = this.$module.querySelector('input') this.$inputWrapper = this.$module.querySelector('.js-search-input-wrapper') this.$form = this.$module.closest('form') this.sourceUrl = this.$module.getAttribute('data-source-url') this.sourceKey = this.$module.getAttribute('data-source-key') this.isSubmitting = false } init () { const configOptions = { element: this.$inputWrapper, id: this.$originalInput.id, name: this.$originalInput.name, inputClasses: this.$originalInput.classList, defaultValue: this.$originalInput.value, cssNamespace: 'gem-c-search-with-autocomplete', confirmOnBlur: false, minLength: 3, showNoOptionsFound: false, source: this.getResults.bind(this), onConfirm: this.onConfirm.bind(this), templates: { suggestion: this.constructSuggestionHTMLString.bind(this) }, tStatusNoResults: () => 'No search suggestions found', tStatusQueryTooShort: (minQueryLength) => `Type in ${minQueryLength} or more characters for search suggestions`, tStatusResults: (length, contentSelectedOption) => { const words = { result: (length === 1) ? 'search suggestion' : 'search suggestions', is: (length === 1) ? 'is' : 'are' } return `${length} ${words.result} ${words.is} available. ${contentSelectedOption}` }, tAssistiveHint: () => 'When search suggestions are available use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.' } accessibleAutocomplete(configOptions) // The accessible-autocomplete component is meant to generate a new input element rather than enhancing an existing one, so we need to do some cleanup here. this.$autocompleteInput = this.$inputWrapper.querySelector( '.gem-c-search-with-autocomplete__input' ) // Ensure the new input element generated by accessible-autocomplete has the correct type this.$autocompleteInput.setAttribute('type', 'search') // Remove the original input from the DOM this.$originalInput.parentNode.removeChild(this.$originalInput) // The accessible-autocomplete component has an edge case where when the menu is visible, it // prevents default on the Enter key event, even if the user hasn't put keyboard focus on a // suggestion. This results in a scenario where the user types something, does _not_ interact // with the autocomplete menu at all, and then hits Enter to try to submit the form - but it // isn't submitted. // // This manually triggers our form submission logic when the Enter key is pressed while the // dropdown is shown as a workaround (which will do nothing if the form is already in the // process of submitting through `onConfirm` because the user has accepted a suggestion). this.$autocompleteInput.addEventListener('keydown', (e) => { const dropdownVisible = this.$autocompleteInput.getAttribute('aria-expanded') === 'true' if (dropdownVisible && e.key === 'Enter') this.submitContainingForm() }) } // Callback used by accessible-autocomplete to generate the HTML for each suggestion based on // the values returned from the source constructSuggestionHTMLString (result) { const sanitizedResult = this.sanitizeResult(result) const inputValue = this.$inputWrapper.querySelector('input').value.toLowerCase() const index = sanitizedResult.toLowerCase().indexOf(inputValue) let html = sanitizedResult if (index !== -1) { const before = sanitizedResult.slice(0, index) const match = sanitizedResult.slice(index, index + inputValue.length) const after = sanitizedResult.slice(index + inputValue.length) html = `${before}${match}${after}` } return `