@financial-times/o-autocomplete 1.9.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ import Autocomplete from '../../../main.js';
2
+ import {data} from './data.js';
3
+ import oForms from '@financial-times/o-forms';
4
+ oForms.init();
5
+ /**
6
+ * @typedef {object} CustomOption
7
+ * @property {string} Continent_Code - 2 letter continent code
8
+ * @property {string} Continent_Name - name of continent
9
+ * @property {string} Country_Name - name of country
10
+ * @property {number} Country_Number - id of country
11
+ * @property {string} Three_Letter_Country_Code - three letter country code
12
+ * @property {string} Two_Letter_Country_Code - two letter country code
13
+ */
14
+
15
+ /**
16
+ * @param {CustomOption} option - The option to transform into a suggestion string
17
+ * @returns {string} The string to display in the suggestions dropdown for this option
18
+ */
19
+ function mapOptionToSuggestedValue(option) {
20
+ if (typeof option !== 'object') {
21
+ throw new Error(`Could not map option to suggested value, unexpected type: ${typeof option}.`);
22
+ }
23
+
24
+ if (typeof option.Country_Name !== 'string') {
25
+ throw new Error(`Could not map option to suggested value, option.Country_Name is not a string`);
26
+ }
27
+
28
+ return option.Country_Name;
29
+ }
30
+
31
+ /**
32
+ * @typedef CharacterHighlight - The character and whether it should be highlighted
33
+ * @type {Array}
34
+ * @property {string} 0 - the character in the suggestion
35
+ * @property {boolean} 1 - should it be highlighted?
36
+ */
37
+
38
+ /**
39
+ * @param {string} suggestion - Text which is going to be suggested to the user
40
+ * @param {string} query - Text which was typed into the autocomplete text input field by the user
41
+ * @returns {CharacterHighlight[]} An array of arrays which contain two items, the first is the character in the suggestion, the second is a boolean which indicates whether the character should be highlighted.
42
+ */
43
+ function highlightSuggestion(suggestion, query) {
44
+ const result = suggestion.split('');
45
+
46
+ const matchIndex = suggestion.toLocaleLowerCase().indexOf(query.toLocaleLowerCase());
47
+ return result.map(function(character, index) {
48
+ let shouldHighlight = true;
49
+ const hasMatched = matchIndex > -1;
50
+ const characterIsWithinMatch = index >= matchIndex && index <= matchIndex + query.length - 1;
51
+ if (hasMatched && characterIsWithinMatch) {
52
+ shouldHighlight = false;
53
+ }
54
+ return [character, shouldHighlight];
55
+ });
56
+ }
57
+
58
+ /**
59
+ * @param {CustomOption} option - The option to transform into a suggestion string
60
+ * @param {string} [query] - Text which was typed into the autocomplete text input field by the user
61
+ * @returns {string} The string to display in the suggestions dropdown for this option
62
+ */
63
+ function suggestionTemplate(option, query) {
64
+ if(typeof option === 'string') return option;
65
+
66
+ /**
67
+ * @type {CharacterHighlight[]} An array of arrays which contain two items, the first is the character in the suggestion, the second is a boolean which indicates whether the character should be highlighted.
68
+ */
69
+ const characters = highlightSuggestion(option.name, query || option.name);
70
+
71
+ let output = '';
72
+ for (const [character, shoudHighlight] of characters) {
73
+ if (shoudHighlight) {
74
+ output += `<span class="o-autocomplete__option--highlight">${character}</span>`;
75
+ } else {
76
+ output += `${character}`;
77
+ }
78
+ }
79
+ output += ` <span>(${option.Continent_Name})</span>`;
80
+
81
+ const span = document.createElement('span');
82
+ span.setAttribute('aria-label', option.Country_Name);
83
+ span.innerHTML = output;
84
+ return span.outerHTML;
85
+ }
86
+
87
+ /**
88
+ * @typedef {Function} PopulateOptions
89
+ * @property {Array<string>} options - The options which match the rext which was typed into the autocomplete by the user
90
+ */
91
+
92
+ /**
93
+ * @param {string} query - Text which was typed into the autocomplete by the user
94
+ * @param {PopulateOptions} populateOptions - Function to call when ready to update the suggestions dropdown
95
+ * @returns {void}
96
+ */
97
+ function customSuggestions(query, populateOptions) {
98
+ const suggestions = data;
99
+
100
+ if (!query) {
101
+ populateOptions([]);
102
+ return;
103
+ }
104
+ suggestions.sort(function(a,b) {
105
+ return a.Country_Name.localeCompare(b.Country_Name);
106
+ });
107
+
108
+ const filteredOptions = [];
109
+ for (const suggestion of suggestions) {
110
+ const lowercaseSuggestion = suggestion.Country_Name.toLocaleLowerCase();
111
+ if (lowercaseSuggestion.startsWith(query.toLocaleLowerCase())) {
112
+ filteredOptions.push(suggestion);
113
+ }
114
+ }
115
+ populateOptions(filteredOptions);
116
+ }
117
+
118
+ new Autocomplete(document.querySelector('[data-o-component="o-autocomplete"]'), {
119
+ source: customSuggestions,
120
+ suggestionTemplate,
121
+ mapOptionToSuggestedValue,
122
+ defaultValue: data.find((d) => d['Two_Letter_Country_Code'] === 'GB')?.Country_Name,
123
+ onConfirm: function (option) {
124
+ // eslint-disable-next-line no-console
125
+ console.log('You chose option', option);
126
+ }
127
+ });
@@ -0,0 +1,14 @@
1
+ <form data-o-component="o-forms">
2
+ <div class="o-forms-field">
3
+ <span class="o-forms-title">
4
+ <label for="my-autocomplete" class="o-forms-title__main">Select your country</label>
5
+ </span>
6
+ <span class="o-forms-input o-forms-input--text">
7
+ <span data-o-component="o-autocomplete" class="o-autocomplete">
8
+ <!-- If the JavaScript executes, then this input will be progressively enhanced to an autocomplete component -->
9
+ <input required type="text" name="text-example" id="my-autocomplete">
10
+ </span>
11
+ <span role="alert" class="o-forms-input__error">Please fill out this field</span>
12
+ </span>
13
+ </div>
14
+ </form>
@@ -29,9 +29,10 @@ function mapOptionToSuggestedValue(option) {
29
29
  }
30
30
  /**
31
31
  * @param {CustomOption} option - The option to transform into a suggestion string
32
+ * @param {string} [query] - Text which was typed into the autocomplete text input field by the user
32
33
  * @returns {string} The string to display in the suggestions dropdown for this option
33
34
  */
34
- function suggestionTemplate(option) {
35
+ function suggestionTemplate(option, query) { //eslint-disable-line no-unused-vars
35
36
  if(typeof option === 'string') return option;
36
37
  return `<div>
37
38
  <strong>${option.Country_Name}</strong>
package/origami.json CHANGED
@@ -61,6 +61,14 @@
61
61
  "js": "demos/src/dynamic-custom-suggestion/dynamic-custom-suggestion.js",
62
62
  "hidden": true
63
63
  },
64
+ {
65
+ "title": "Autocomplete with custom highlighted suggestion items",
66
+ "name": "dynamic-custom-highlighted-suggestions",
67
+ "template": "demos/src/dynamic-custom-highlighted-suggestion/dynamic-custom-highlighted-suggestion.mustache",
68
+ "description": "The source function returns objects and the suggestionTemplate transforms the objects into custom highlighted HTML suggestions",
69
+ "js": "demos/src/dynamic-custom-highlighted-suggestion/dynamic-custom-highlighted-suggestion.js",
70
+ "hidden": true
71
+ },
64
72
  {
65
73
  "title": "Pa11y",
66
74
  "name": "pa11y",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/o-autocomplete",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "description": "An origami component for autocomplete inputs",
5
5
  "keywords": [
6
6
  "autocomplete",
@@ -47,7 +47,7 @@
47
47
  "@financial-times/o-utils": "^2.1.0"
48
48
  },
49
49
  "dependencies": {
50
- "@financial-times/accessible-autocomplete": "^2.1.2"
50
+ "@financial-times/accessible-autocomplete": "^3.1.0"
51
51
  },
52
52
  "private": false
53
53
  }
@@ -1,9 +1,7 @@
1
1
  // We use our own fork of accessible-autocomplete because the main package is not being actively maintained and has bugs which we needed to fix
2
2
  // There is a changelog for the fixes we've added -- https://github.com/Financial-Times/accessible-autocomplete/blob/master/CHANGELOG.md#210---2021-05-24
3
3
  // Below are the pull-requests to accessible-autocomplete which would fix the bugs:
4
- // https://github.com/alphagov/accessible-autocomplete/pull/497
5
4
  // https://github.com/alphagov/accessible-autocomplete/pull/491
6
- // https://github.com/alphagov/accessible-autocomplete/pull/496
7
5
  // If the above pull-requests are merged and published, then we can stop using our fork
8
6
  import accessibleAutocomplete from '@financial-times/accessible-autocomplete';
9
7
 
@@ -17,7 +15,7 @@ import accessibleAutocomplete from '@financial-times/accessible-autocomplete';
17
15
 
18
16
  /**
19
17
  * @param {string} suggestion - Text which is going to be suggested to the user
20
- * @param {string} query - Text which was typed into the autocomplete by the user
18
+ * @param {string} query - Text which was typed into the autocomplete text input field by the user
21
19
  * @returns {CharacterHighlight[]} An array of arrays which contain two items, the first is the character in the suggestion, the second is a boolean which indicates whether the character should be highlighted.
22
20
  */
23
21
  function highlightSuggestion(suggestion, query) {
@@ -291,13 +289,14 @@ class Autocomplete {
291
289
  * Used when rendering suggestions, the return value of this will be used as the innerHTML for a single suggestion.
292
290
  *
293
291
  * @param {*} option The suggestion to apply the template with.
292
+ * @param {string} query Text which was typed into the autocomplete text input field by the user.
294
293
  * @returns {string|undefined} HTML string to represent a single suggestion.
295
294
  */
296
- suggestion: (option) => {
295
+ suggestion: (option, query) => {
297
296
  // If the suggestionTemplate override option is provided,
298
297
  // use that to render the suggestion.
299
298
  if(typeof this.options.suggestionTemplate === 'function') {
300
- return this.options.suggestionTemplate(option);
299
+ return this.options.suggestionTemplate(option, query);
301
300
  }
302
301
  if (typeof option === 'object') {
303
302
  // If the `mapOptionToSuggestedValue` function is defined