Skip to content
Snippets Groups Projects

Draft: Resolve #3248211 "Promise"

1 unresolved thread
Files
2
+ 76
69
@@ -121,33 +121,26 @@
*
* This is part of the public API and triggers semver increment on change.
*
* There is minimal error handling in the library. When the promise is rejected
* the result list is emptied and the suggestion box is closed. Displaying a
* user-visible message on failure is left to the author of the source callback.
*
* @public
* @callback A11yAutocomplete~sourceCallback
* @param {string} searchTerm
* The search term being searched for.
* @param {A11yAutocomplete~sourceResultsCallback} results
* A callback to populate the results list.
* @return {Promise.<A11yAutocomplete~SourceData[]>}
* A Promise holding the list of suggestions to display.
* @example @lang js <caption>Simple fetch call</caption>
* // Example querying an endpoint that provides JSON.
* source: (searchTerm, results) => {
* source: (searchTerm) => {
* // Query the remote source
* fetch(`https://an-endpoint/?q=${searchTerm}`)
* .then((response) => response.json())
* // Use the callback to send the results to the
* .then(results);
* return fetch(`https://an-endpoint/?q=${searchTerm}`)
* .then((response) => response.json());
* },
*/
/**
* A callback that will display the results passed according to the configured
* constraints.
*
* This is part of the public API and triggers semver increment on change.
*
* @public
* @callback A11yAutocomplete~sourceResultsCallback
* @param {A11yAutocomplete~SourceData[]} results
* A callback to populate the results list.
* @example @lang js <caption>jQuery Ajax call</caption>
* // Example querying an endpoint that provides JSON with jQuery.
* source: (searchTerm) => $.get(`https://an-endpoint/?q=${searchTerm}`),
*/
/**
@@ -1242,60 +1235,39 @@ class _A11yAutocomplete {
}
if (searchTerm && searchTerm.length > 0) {
let resultsPromise;
this.addClasses(this.input, this.options.classes.inputLoading);
if (typeof this.options.source === 'function') {
this.addClasses(this.input, this.options.classes.inputLoading);
this.options.source(searchTerm, (results) => {
this.removeClasses(this.input, this.options.classes.inputLoading);
/**
* Fires after suggestion items are retrieved, but before they are added to
* the DOM.
*
* @event A11yAutocomplete#autocomplete-response
* @type {CustomEvent}
* @prop {object} detail
* Detail property of the event.
* @prop {A11yAutocomplete~Api} detail.autocomplete
* The autocomplete instance.
* @prop {A11yAutocomplete~Suggestion[]} detail.list
* The normalized array of suggestions to display.
*/
this.triggerEvent('autocomplete-response', { list: results });
const normalizedResults = this.normalizeSuggestionItems(results);
const constrainedResults = this.applySelectionConstraints(
normalizedResults,
this.splitValues(),
resultsPromise = this.options.source(searchTerm);
// Validate that the resultsPromise object is Promise-like
if (
!resultsPromise ||
!('then' in resultsPromise && 'catch' in resultsPromise)
) {
throw new TypeError(
'options.source callback needs to return a Promise',
);
this.displayResults(constrainedResults);
});
}
// Process the results and display them.
resultsPromise
// When there is an error, use an empty result set and log a message
// in the console.
.catch((error) => {
// Using console.error() makes the stack trace available.
console.error(error);
// Returning an empty result will hide the suggestions.
return [];
})
.then(this.processResults.bind(this))
.then(this.displayResults.bind(this));
} else if (Array.isArray(this.options.source)) {
const filteredResults = this.options.source.filter((item) =>
this.filterResults(item, searchTerm),
);
/**
* Fires after suggestion items are retrieved, but before they are added to
* the DOM.
*
* @event A11yAutocomplete#autocomplete-response
* @type {CustomEvent}
* @prop {object} detail
* Detail property of the event.
* @prop {A11yAutocomplete~Api} detail.autocomplete
* The autocomplete instance.
* @prop {A11yAutocomplete~Suggestion[]} detail.list
* The normalized array of suggestions to display.
*/
this.triggerEvent('autocomplete-response', { list: filteredResults });
const normalizedResults =
this.normalizeSuggestionItems(filteredResults);
const constrainedResults = this.applySelectionConstraints(
normalizedResults,
this.splitValues(),
);
const filterCallback = (item) => this.filterResults(item, searchTerm);
// If a predefined list was provided as an option, make this the
// suggestion items.
this.displayResults(constrainedResults);
// suggestion items after arbitrary filtering.
const filteredResults = this.options.source.filter(filterCallback);
const processedResults = this.processResults(filteredResults);
this.displayResults(processedResults);
} else {
throw new TypeError('options.source is not an array or a function.');
}
@@ -1321,6 +1293,41 @@ class _A11yAutocomplete {
}
}
/**
* Transform a list of results in a list of normalized results after applying
* the autocomplete configuration on the results.
*
* @param {A11yAutocomplete~SourceData[]} results
* The raw source data.
* @return {A11yAutocomplete~Suggestion[]}
* An array of normalized suggestions.
*/
processResults(results) {
// Allow modifications of the result array before normalization.
/**
* Fires after suggestion items are retrieved, but before they are
* added to the DOM.
*
* @event A11yAutocomplete#autocomplete-response
* @type {CustomEvent}
* @prop {object} detail
* Detail property of the event.
* @prop {A11yAutocomplete~Api} detail.autocomplete
* The autocomplete instance.
* @prop {A11yAutocomplete~Suggestion[]} detail.list
* The normalized array of suggestions to display.
*/
this.triggerEvent('autocomplete-response', { list: results });
// Results are final, remove all loading styling and normalize items.
this.removeClasses(this.input, this.options.classes.inputLoading);
const normalizedResults = this.normalizeSuggestionItems(results);
// Once they are normalized we apply the configuration about results.
return this.applySelectionConstraints(
normalizedResults,
this.splitValues(),
);
// We are left with only the suggestions that should be displayed.
}
/**
* Converts all suggestions into an object with value and label properties.
*
Loading