diff --git a/package.json b/package.json index 3555c80139..1d843290b9 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "express": "^4.17.1", "front-matter": "^4.0.2", "fs-extra": "^11.1.1", - "fuse.js": "^3.6.1", + "fuse.js": "^7.0.0", "glob": "^10.2.3", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", diff --git a/src/components/address-input/autosuggest.address.spec.js b/src/components/address-input/autosuggest.address.spec.js index 9c2636d988..42f8e0c9d6 100644 --- a/src/components/address-input/autosuggest.address.spec.js +++ b/src/components/address-input/autosuggest.address.spec.js @@ -189,7 +189,7 @@ describe('script: address-input', () => { await setTestPage('/test', renderComponent('address-input', EXAMPLE_ADDRESS_INPUT_WITH_API)); await setTimeout(50); - expect(apiFaker.getRequestCount('/addresses/eq?input=cf142&limit=10')).toBe(1); + expect(await apiFaker.getRequestCount('/addresses/eq?input=cf142&limit=10')).toBe(1); }); describe('when api status is okay', () => { @@ -257,8 +257,9 @@ describe('script: address-input', () => { await page.$eval('.ons-js-autosuggest-input', (node) => (node.value = '196 coll')); await page.type('.ons-js-autosuggest-input', 'e'); + await setTimeout(50); - expect(apiFaker.getRequestCount('/addresses/eq?input=196%20colle&limit=10')).toBe(1); + expect(await apiFaker.getRequestCount('/addresses/eq?input=196%20colle&limit=10')).toBe(1); }); describe('when the value is a full postcode', () => { @@ -267,14 +268,15 @@ describe('script: address-input', () => { await page.$eval('.ons-js-autosuggest-input', (node) => (node.value = 'CF14 2N')); await page.type('.ons-js-autosuggest-input', 'T'); + + await setTimeout(100); }); it('provides expected parameters to the address API where `limit` is 100', async () => { - expect(apiFaker.getRequestCount('/addresses/eq?input=cf14%202nt&limit=100&groupfullpostcodes=combo')).toBe(1); + expect(await apiFaker.getRequestCount('/addresses/eq?input=cf14%202nt&limit=100&groupfullpostcodes=combo')).toBe(1); }); it('has expected suggestion entries', async () => { - await setTimeout(100); const suggestions = await page.$$eval('.ons-autosuggest__option', (nodes) => nodes.map((node) => node.textContent.trim())); expect(suggestions).toEqual(['196 College Road, Birmingham, B44 8HF', '196 College Road, Whitchurch, Cardiff, CF14 2NZ']); }); @@ -314,7 +316,7 @@ describe('script: address-input', () => { it('provides expected parameters to the address API', async () => { expect( - apiFaker.getRequestCount( + await apiFaker.getRequestCount( '/addresses/eq?input=penlline%20road%20whitchurch%20cardiff%20cf14%202nz&limit=100&groupfullpostcodes=combo', ), ).toBe(1); @@ -336,7 +338,7 @@ describe('script: address-input', () => { }); it('makes expected request when a suggestion is selected', async () => { - expect(apiFaker.getRequestCount('/addresses/eq/uprn/100070332099?addresstype=paf')).toBe(1); + expect(await apiFaker.getRequestCount('/addresses/eq/uprn/100070332099?addresstype=paf')).toBe(1); }); it('populates manual input fields with address from selection', async () => { @@ -360,7 +362,7 @@ describe('script: address-input', () => { }); it('provides expected parameters to the address API', async () => { - expect(apiFaker.getRequestCount('/addresses/eq?input=cf14%202&limit=10')).toBe(1); + expect(await apiFaker.getRequestCount('/addresses/eq?input=cf14%202&limit=10')).toBe(1); }); it('has expected suggestion entries', async () => { @@ -380,7 +382,7 @@ describe('script: address-input', () => { it('makes expected request', async () => { expect( - apiFaker.getRequestCount( + await apiFaker.getRequestCount( '/addresses/eq/bucket?postcode=CF14%202AA&streetname=Penlline%20Road&townname=Whitchurch&groupfullpostcodes=combo', ), ).toBe(1); @@ -611,7 +613,9 @@ describe('script: address-input', () => { await page.$eval('.ons-js-autosuggest-input', (node) => (node.value = '196 coll')); await page.type('.ons-js-autosuggest-input', 'e'); - expect(apiFaker.getRequestCount('/addresses/eq?input=196%20colle&limit=10&favourwelsh=true')).toBe(1); + await setTimeout(50); + + expect(await apiFaker.getRequestCount('/addresses/eq?input=196%20colle&limit=10&favourwelsh=true')).toBe(1); }); }); @@ -629,7 +633,7 @@ describe('script: address-input', () => { }); it('then the retrieveAddress function will be called', async () => { - expect(apiFaker.getRequestCount('/addresses/eq/uprn/100070332099?addresstype=paf')).toBe(1); + expect(await apiFaker.getRequestCount('/addresses/eq/uprn/100070332099?addresstype=paf')).toBe(1); }); }); @@ -735,10 +739,9 @@ describe('script: address-input', () => { it('provides expected parameters to the address API', async () => { await page.$eval('.ons-js-autosuggest-input', (node) => (node.value = '196 coll')); await page.type('.ons-js-autosuggest-input', 'e'); - await setTimeout(50); - expect(apiFaker.getRequestCount(searchEndpoint)).toBe(1); + expect(await apiFaker.getRequestCount(searchEndpoint)).toBe(1); }); it('requests further information for the selected address from the API with the expected parameters', async () => { @@ -746,10 +749,9 @@ describe('script: address-input', () => { await page.type('.ons-js-autosuggest-input', 'e', { delay: 20 }); await page.keyboard.press('ArrowDown'); await page.keyboard.press('Enter'); - await setTimeout(50); - expect(apiFaker.getRequestCount(uprnEndpoint)).toBe(1); + expect(await apiFaker.getRequestCount(uprnEndpoint)).toBe(1); }); }); }); diff --git a/src/components/address-output/_macro.spec.js b/src/components/address-output/_macro.spec.js index d94484c316..692cf12432 100644 --- a/src/components/address-output/_macro.spec.js +++ b/src/components/address-output/_macro.spec.js @@ -5,94 +5,141 @@ import * as cheerio from 'cheerio'; import axe from '../../tests/helpers/axe'; import { renderComponent } from '../../tests/helpers/rendering'; -const EXAMPLE_ADDRESS_OUTPUT_FULL = { - unit: 'Unit 5', - organisation: 'Trescos', - line1: 'Abingdon Road', - line2: 'Goathill', - town: 'Barry', - postcode: 'AB12 6UH', -}; - -const EXAMPLE_ADDRESS_OUTPUT_NONE = {}; - -describe('macro: address-output', () => { - it('passes jest-axe checks', async () => { - const $ = cheerio.load(renderComponent('address-output', EXAMPLE_ADDRESS_OUTPUT_FULL)); - - const results = await axe($.html()); - expect(results).toHaveNoViolations(); - }); +import { EXAMPLE_ADDRESS_OUTPUT_FULL } from './_test_examples'; - it('has additionally provided container style classes', () => { - const $ = cheerio.load( - renderComponent('address-output', { - ...EXAMPLE_ADDRESS_OUTPUT_FULL, - classes: 'extra-class another-extra-class', - }), - ); +describe('FOR: Macro: Address-output', () => { + describe('GIVEN: Params: none', () => { + describe('WHEN: no parameters are provided', () => { + const $ = cheerio.load(renderComponent('address-output', {})); - expect($('.ons-address-output').hasClass('extra-class')).toBe(true); - expect($('.ons-address-output').hasClass('another-extra-class')).toBe(true); + test('THEN: renders no lines', () => { + expect($('.ons-address-output__lines *').length).toBe(0); + }); + }); }); - it('renders no lines when no parameters are provided', () => { - const $ = cheerio.load(renderComponent('address-output', EXAMPLE_ADDRESS_OUTPUT_NONE)); - - expect($('.ons-address-output__lines *').length).toBe(0); + describe('GIVEN: Params: classes', () => { + describe('WHEN: classes are provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + ...EXAMPLE_ADDRESS_OUTPUT_FULL, + classes: 'extra-class another-extra-class', + }), + ); + + test('THEN: renders with additional classes provided', async () => { + expect($('.ons-address-output').hasClass('extra-class')).toBe(true); + expect($('.ons-address-output').hasClass('another-extra-class')).toBe(true); + }); + }); }); - it.each([ - ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL], - ['single line', { unit: 'Unit 5' }], - ])('renders `unit` with %s', (_, params) => { - const $ = cheerio.load(renderComponent('address-output', params)); + describe('GIVEN: Params: All params', () => { + describe('WHEN: all address line params are provided', () => { + const $ = cheerio.load(renderComponent('address-output', EXAMPLE_ADDRESS_OUTPUT_FULL)); - expect($('.ons-address-output__unit').text().trim()).toBe('Unit 5'); - }); + test('THEN: jest-axe tests pass', async () => { + const results = await axe($.html()); + expect(results).toHaveNoViolations(); + }); - it.each([ - ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL], - ['single line', { organisation: 'Trescos' }], - ])('renders `organisation` with %s', (_, params) => { - const $ = cheerio.load(renderComponent('address-output', params)); + test('THEN: renders unit with provided text', () => { + expect($('.ons-address-output__unit').text().trim()).toBe('Unit 5'); + }); - expect($('.ons-address-output__organisation').text().trim()).toBe('Trescos'); - }); + test('THEN: renders organisation line with correct text', () => { + expect($('.ons-address-output__organisation').text().trim()).toBe('Trescos'); + }); - it.each([ - ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL], - ['single line', { line1: 'Abingdon Road' }], - ])('renders `line1` with %s', (_, params) => { - const $ = cheerio.load(renderComponent('address-output', params)); + test('THEN: renders line1 line with correct text', () => { + expect($('.ons-address-output__line1').text().trim()).toBe('Abingdon Road'); + }); - expect($('.ons-address-output__line1').text().trim()).toBe('Abingdon Road'); - }); + test('THEN: renders line2 line with correct text', () => { + expect($('.ons-address-output__line2').text().trim()).toBe('Goathill'); + }); - it.each([ - ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL], - ['single line', { line2: 'Goathill' }], - ])('renders `line2` with %s', (_, params) => { - const $ = cheerio.load(renderComponent('address-output', params)); + test('THEN: renders the town line with correct text', () => { + expect($('.ons-address-output__town').text().trim()).toBe('Barry'); + }); - expect($('.ons-address-output__line2').text().trim()).toBe('Goathill'); + test('THEN: renders the postcode line with correct text', () => { + expect($('.ons-address-output__postcode').text().trim()).toBe('AB12 6UH'); + }); + }); }); - it.each([ - ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL], - ['single line', { town: 'Barry' }], - ])('renders `town` with %s', (_, params) => { - const $ = cheerio.load(renderComponent('address-output', params)); - - expect($('.ons-address-output__town').text().trim()).toBe('Barry'); - }); - - it.each([ - ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL], - ['single line', { postcode: 'AB12 6UH' }], - ])('renders `postcode` with %s', (_, params) => { - const $ = cheerio.load(renderComponent('address-output', params)); - - expect($('.ons-address-output__postcode').text().trim()).toBe('AB12 6UH'); + describe('GIVEN: Params: single param', () => { + describe('WHEN: the unit address line is the only parameter provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + unit: 'Unit 5', + }), + ); + + test('THEN: renders unit line with correct text', () => { + expect($('.ons-address-output__unit').text().trim()).toBe('Unit 5'); + }); + }); + + describe('WHEN: the organisation address line is the only parameter provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + organisation: 'Trescos', + }), + ); + + test('THEN: renders organisation line with correct text', () => { + expect($('.ons-address-output__organisation').text().trim()).toBe('Trescos'); + }); + }); + + describe('WHEN: the line1 address line is the only parameter provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + line1: 'Abingdon Road', + }), + ); + + test('THEN: renders line1 line with correct text', () => { + expect($('.ons-address-output__line1').text().trim()).toBe('Abingdon Road'); + }); + }); + + describe('WHEN: the line2 address line is the only parameter provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + line2: 'Goathill', + }), + ); + + test('THEN: renders line2 line with correct text', () => { + expect($('.ons-address-output__line2').text().trim()).toBe('Goathill'); + }); + }); + + describe('WHEN: the town address line is the only parameter provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + town: 'Barry', + }), + ); + + test('THEN: renders town line with correct text', () => { + expect($('.ons-address-output__town').text().trim()).toBe('Barry'); + }); + }); + + describe('WHEN: the postcode address line is the only parameter provided', () => { + const $ = cheerio.load( + renderComponent('address-output', { + postcode: 'AB12 6UH', + }), + ); + + test('THEN: renders postcode line with correct text', () => { + expect($('.ons-address-output__postcode').text().trim()).toBe('AB12 6UH'); + }); + }); }); }); diff --git a/src/components/address-output/_test_examples.js b/src/components/address-output/_test_examples.js new file mode 100644 index 0000000000..08bb96889f --- /dev/null +++ b/src/components/address-output/_test_examples.js @@ -0,0 +1,8 @@ +export const EXAMPLE_ADDRESS_OUTPUT_FULL = { + unit: 'Unit 5', + organisation: 'Trescos', + line1: 'Abingdon Road', + line2: 'Goathill', + town: 'Barry', + postcode: 'AB12 6UH', +}; diff --git a/src/components/autosuggest/_macro-options.md b/src/components/autosuggest/_macro-options.md index e169e62837..90f8b166ab 100644 --- a/src/components/autosuggest/_macro-options.md +++ b/src/components/autosuggest/_macro-options.md @@ -1,20 +1,21 @@ -| Name | Type | Required | Description | -| ------------------- | ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| autosuggestData | string | false | URL of the JSON file with the autosuggest data that needs to be searched. Required if not using the address index api | -| allowMultiple | boolean | false | Allows the component to accept multiple selections | -| instructions | string | true | Instructions on how to use the autosuggest that will be read out by screen readers | -| ariaYouHaveSelected | string | true | Aria message to tell the user that they have selected an answer | -| ariaMinChars | string | true | Aria message to tell the user how many characters they need to enter before autosuggest will start | -| minChars | integer | false | Minimum number of characters to run a query. Default is 3 | -| ariaOneResult | string | true | Aria message to tell the user there is only one suggestion left | -| ariaNResults | string | true | Aria message to tell the user how many suggestions are left | -| ariaLimitedResults | string | true | Aria message to tell the user if the results have been limited and what they are limited to | -| moreResults | string | true | Aria message to tell the user to continue to type to refine suggestions | -| noResults | string | true | message to tell the user there are no results | -| tooManyResults | string | false | message to tell the user there are too many results to display and the user should refine the search. This is only required when using the address index api | -| typeMore | string | true | message to encourage the user to enter more characters to get suggestions | -| resultsTitle | string | true | Title of results to be displayed on screen at the top of the results | -| resultsTitleId | string | true | ID for the results title. The ID is used in the results `aria-labelledby` to provide context for the results | -| input | `Input` [_(ref)_](/components/input) | true | Configuration object for the input | -| language | string | false | The ISO 639-1 Code will override the default language in page. Please note that only 'en', 'cy' and 'ni' is currently supported | -| id | string | false | The `id` of the input | +| Name | Type | Required | Description | +| ------------------- | ------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| autosuggestData | string | false | URL of the JSON file with the autosuggest data that needs to be searched. Required if not using the address index api | +| allowMultiple | boolean | false | Allows the component to accept multiple selections | +| instructions | string | true | Instructions on how to use the autosuggest that will be read out by screen readers | +| ariaYouHaveSelected | string | true | Aria message to tell the user that they have selected an answer | +| ariaMinChars | string | true | Aria message to tell the user how many characters they need to enter before autosuggest will start | +| minChars | integer | false | Minimum number of characters to run a query. Default is 3 | +| ariaOneResult | string | true | Aria message to tell the user there is only one suggestion left | +| ariaNResults | string | true | Aria message to tell the user how many suggestions are left | +| ariaLimitedResults | string | true | Aria message to tell the user if the results have been limited and what they are limited to | +| moreResults | string | true | Aria message to tell the user to continue to type to refine suggestions | +| noResults | string | true | message to tell the user there are no results | +| tooManyResults | string | false | message to tell the user there are too many results to display and the user should refine the search. This is only required when using the address index api | +| typeMore | string | true | message to encourage the user to enter more characters to get suggestions | +| resultsTitle | string | true | Title of results to be displayed on screen at the top of the results | +| resultsTitleId | string | true | ID for the results title. The ID is used in the results `aria-labelledby` to provide context for the results | +| input | `Input` [_(ref)_](/components/input) | true | Configuration object for the input | +| language | string | false | The ISO 639-1 Code will override the default language in page. Please note that only 'en', 'cy' and 'ni' is currently supported | +| resultsThreshold | float | false | Option to adjust the search threshold and fuzziness. Accepts a range from 0 to 1, where 0 provides the closest match and 1 allows for more distant matches. Defaults to 0.2. | +| id | string | false | The `id` of the input | diff --git a/src/components/autosuggest/_macro.njk b/src/components/autosuggest/_macro.njk index b8c2f4c52f..a5d18997e6 100644 --- a/src/components/autosuggest/_macro.njk +++ b/src/components/autosuggest/_macro.njk @@ -15,6 +15,7 @@ data-results-title="{{ params.resultsTitle }}" data-no-results="{{ params.noResults }}" data-type-more="{{ params.typeMore }}" + {% if params.resultsThreshold %}data-result-threshold="{{ params.resultsThreshold }}"{% endif %} {% if params.apiDomain %}data-api-domain="{{ params.apiDomain }}"{% endif %} {% if params.apiDomainBearerToken %}data-authorization-token="{{ params.apiDomainBearerToken }}"{% endif %} {% if params.apiManualQueryParams == true %}data-query-params=""{% endif %} diff --git a/src/components/autosuggest/_macro.spec.js b/src/components/autosuggest/_macro.spec.js index efac0908e7..ee9c897fdf 100644 --- a/src/components/autosuggest/_macro.spec.js +++ b/src/components/autosuggest/_macro.spec.js @@ -32,6 +32,11 @@ const EXAMPLE_AUTOSUGGEST = { typeMore: 'Continue entering to get suggestions', }; +const EXAMPLE_AUTOSUGGEST_WITH_RESULTS_THRESHOLD = { + ...EXAMPLE_AUTOSUGGEST, + resultsThreshold: 0.5, +}; + describe('macro: autosuggest', () => { it('passes jest-axe checks', async () => { const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST)); @@ -47,7 +52,7 @@ describe('macro: autosuggest', () => { }); it('has the provided data attributes', () => { - const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST)); + const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST_WITH_RESULTS_THRESHOLD)); const $element = $('.ons-autosuggest'); expect($element.attr('data-allow-multiple')).toBeUndefined(); @@ -63,6 +68,7 @@ describe('macro: autosuggest', () => { expect($element.attr('data-no-results')).toBe('No suggestions found. You can enter your own answer'); expect($element.attr('data-results-title')).toBe('Suggestions'); expect($element.attr('data-type-more')).toBe('Continue entering to get suggestions'); + expect($element.attr('data-result-threshold')).toBe('0.5'); }); it('has the `data-allow-multiple` attribute when `allowMultiple` is `true`', () => { diff --git a/src/components/autosuggest/autosuggest.ui.js b/src/components/autosuggest/autosuggest.ui.js index 1a19883df5..74cce5e8a0 100644 --- a/src/components/autosuggest/autosuggest.ui.js +++ b/src/components/autosuggest/autosuggest.ui.js @@ -37,6 +37,7 @@ export default class AutosuggestUI { errorAPI, errorAPILinkText, typeMore, + customResultsThreshold, }) { // DOM Elements this.context = context; @@ -65,6 +66,7 @@ export default class AutosuggestUI { this.errorAPI = errorAPI || context.getAttribute('data-error-api'); this.errorAPILinkText = errorAPILinkText || context.getAttribute('data-error-api-link-text'); this.typeMore = typeMore || context.getAttribute('data-type-more'); + this.customResultsThreshold = customResultsThreshold || context.getAttribute('data-result-threshold'); this.language = context.getAttribute('data-lang'); this.allowMultiple = context.getAttribute('data-allow-multiple') || false; this.listboxId = this.listbox.getAttribute('id'); @@ -293,9 +295,30 @@ export default class AutosuggestUI { async fetchSuggestions(sanitisedQuery, data) { this.abortFetch(); - const results = await runFuse(sanitisedQuery, data, this.lang, this.resultLimit); + + const threshold = + this.customResultsThreshold != null && this.customResultsThreshold >= 0 && this.customResultsThreshold <= 1 + ? this.customResultsThreshold + : 0.2; + + let distance; + if (threshold >= 0.6) { + distance = 500; + } else if (threshold >= 0.4) { + distance = 300; + } else { + distance = 100; + } + + const results = await runFuse(sanitisedQuery, data, this.lang, threshold, distance); + results.forEach((result) => { - result.sanitisedText = sanitiseAutosuggestText(result[this.lang], this.sanitisedQueryReplaceChars); + const resultItem = result.item ?? result; + + result.sanitisedText = sanitiseAutosuggestText( + resultItem[this.lang] ?? resultItem['formattedAddress'], + this.sanitisedQueryReplaceChars, + ); }); return { status: this.responseStatus, @@ -345,16 +368,18 @@ export default class AutosuggestUI { this.listbox.innerHTML = ''; if (this.results) { this.resultOptions = this.results.map((result, index) => { - let innerHTML = this.emboldenMatch(result[this.lang], this.query); + const resultItem = result.item ?? result; + + let innerHTML = this.emboldenMatch(resultItem[this.lang] ?? resultItem['formattedAddress'], this.query); const listElement = document.createElement('li'); listElement.className = classAutosuggestOption; listElement.setAttribute('id', `${this.listboxId}__option--${index}`); listElement.setAttribute('role', 'option'); - if (result.category) { + if (resultItem.category) { innerHTML = innerHTML + - `${result.category}`; + `${resultItem.category}`; } listElement.innerHTML = innerHTML; listElement.addEventListener('click', () => { @@ -485,16 +510,19 @@ export default class AutosuggestUI { if (this.results.length) { this.settingResult = true; const result = this.results[index || this.highlightedResultIndex || 0]; + const resultItem = result.item ?? result; + const resultValue = resultItem[this.lang] ?? resultItem['formattedAddress']; + this.resultSelected = true; if (this.allowMultiple === 'true') { - let value = this.storeExistingSelections(result[this.lang]); + let value = this.storeExistingSelections(resultValue); result.displayText = value; - } else if (result.url) { - result.displayText = result[this.lang]; - window.location = result.url; + } else if (resultItem.url) { + result.displayText = resultValue; + window.location = resultItem.url; } else { - result.displayText = result[this.lang]; + result.displayText = resultValue; } this.onSelect(result).then(() => (this.settingResult = false)); diff --git a/src/components/autosuggest/example-autosuggest-country.njk b/src/components/autosuggest/example-autosuggest-country.njk index ddf071ddb0..3358178ad1 100644 --- a/src/components/autosuggest/example-autosuggest-country.njk +++ b/src/components/autosuggest/example-autosuggest-country.njk @@ -24,7 +24,8 @@ "resultsTitleId": "country-of-birth-suggestions", "autosuggestData": "/examples/data/country-of-birth.json", "noResults": "No suggestions found. You can enter your own answer", - "typeMore": "Continue entering to get suggestions" + "typeMore": "Continue entering to get suggestions", + "resultsThreshold": 0.2 }) }} diff --git a/src/components/autosuggest/fuse-config.js b/src/components/autosuggest/fuse-config.js index 2b7724a47e..70dade58b6 100644 --- a/src/components/autosuggest/fuse-config.js +++ b/src/components/autosuggest/fuse-config.js @@ -1,14 +1,19 @@ import Fuse from 'fuse.js'; -export default function runFuse(query, data, searchFields) { +export default function runFuse(query, data, searchFields, threshold, distance) { const options = { shouldSort: true, - threshold: 0.2, + threshold: threshold, + distance: distance, keys: [ { name: searchFields, weight: 0.9, }, + { + name: 'formattedAddress', + weight: 0.9, + }, { name: 'tags', weight: 0.1, diff --git a/src/components/section-navigation/_macro.njk b/src/components/section-navigation/_macro.njk index 2c79b08f14..0c6c18b9b3 100644 --- a/src/components/section-navigation/_macro.njk +++ b/src/components/section-navigation/_macro.njk @@ -31,9 +31,12 @@ class="ons-section-nav__item{{ ' ' + item.classes if item.classes else '' }}{{ ' ons-section-nav__item--active' if isCurrent == true }}" > {% if isCurrent == true %} - {{ openingHeadingTag | replace(headingLevel, sectionItemHeadingLevel | string) | safe }} - class="ons-section-nav__link ons-section-nav__item-header" href="{{ item.url }}" - aria-current="location">{{ item.text }}{{ closingHeadingTag | replace(headingLevel, sectionItemHeadingLevel | string) | safe }} + {{ item.text }} {% else %} {{ item.text }} {% endif %} @@ -71,9 +74,9 @@ class="ons-section-nav__item{{ ' ' + item.classes if item.classes else '' }}{% if isCurrent == true %}{{ ' ' }}ons-section-nav__item--active{% endif %}" > {% if isCurrent == true %} - {{ openingHeadingTag | replace(headingLevel, sectionItemHeadingLevel | string) | safe }} - class="ons-section-nav__link ons-section-nav__item-header" href="{{ item.url }}" - aria-current="location">{{ item.text }}{{ closingHeadingTag | replace(headingLevel, sectionItemHeadingLevel | string) | safe }} + {{ item.text }} {% else %} {{ item.text }} {% endif %} diff --git a/src/components/timeout-modal/timeout-modal.spec.js b/src/components/timeout-modal/timeout-modal.spec.js index fa2538ff96..b5b59e070d 100644 --- a/src/components/timeout-modal/timeout-modal.spec.js +++ b/src/components/timeout-modal/timeout-modal.spec.js @@ -216,7 +216,7 @@ describe('script: timeout modal', () => { it('closes the modal', async () => { const modalIsVisible = await page.$eval('.ons-modal', (node) => node.classList.contains('ons-u-db')); - await setTimeout(50); + await setTimeout(100); expect(modalIsVisible).toBe(false); }); diff --git a/src/js/analytics.js b/src/js/analytics.js index 1e6ac1cd86..3bc1d3dad7 100644 --- a/src/js/analytics.js +++ b/src/js/analytics.js @@ -44,7 +44,7 @@ export default function initAnalytics() { document.body.addEventListener('click', ({ target }) => { if (target.getAttribute('data-ga') === 'click') { return trackElement(target, 'click'); - } else if (target.parentElement.getAttribute('data-ga') === 'click') { + } else if (target.parentElement?.getAttribute('data-ga') === 'click') { return trackElement(target.parentElement, 'click'); } }); diff --git a/yarn.lock b/yarn.lock index 208423613e..56930019ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5655,10 +5655,10 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -fuse.js@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" - integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== +fuse.js@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2" + integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -11308,7 +11308,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11326,15 +11326,6 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11403,7 +11394,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11424,13 +11415,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.0, strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12811,7 +12795,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12828,15 +12812,6 @@ wrap-ansi@^2.0.0: string-width "^1.0.1" strip-ansi "^3.0.1" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"