From c4e2c7c6a4a36d67499f67a858ea2b9e233e1c5f Mon Sep 17 00:00:00 2001 From: mmitiche <86681870+mmitiche@users.noreply.github.com> Date: Wed, 14 Aug 2024 07:09:58 -0400 Subject: [PATCH] fix(quantic): fix issue with persisting search box suggestion value on blur (#4279) ## [SFINT-5665](https://coveord.atlassian.net/jira/software/c/projects/SFINT/boards/1076?assignee=62e7739dec6b328032f09736&selectedIssue=SFINT-5665) The issue: - On blur and without submitting, the value of the suggestion or the recent query was not persisted as the value of the search box input. - On pressing the `Escape` key, the input content was automatically deleted. https://github.com/user-attachments/assets/8f55f144-f445-4d6b-9d65-418d4ca6ef48 The solution: - Updated the logic that handles the value of the input to allow persisting the values of the suggestions/recent queries before submitting them. - prevented the default behaviour of the input on Escape key click. - unit tests added. https://github.com/user-attachments/assets/cb0ebc17-d34f-407e-b091-3f3571bb38a4 [SFINT-5665]: https://coveord.atlassian.net/browse/SFINT-5665?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --- .../__tests__/quanticSearchBoxInput.test.js | 78 ++++++++++++++++++- .../quanticSearchBoxInput.js | 23 ++++-- .../templates/defaultSearchBoxInput.html | 2 +- .../templates/expandableSearchBoxInput.html | 1 + 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/__tests__/quanticSearchBoxInput.test.js b/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/__tests__/quanticSearchBoxInput.test.js index 142f5bcaeae..774c7f6d01f 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/__tests__/quanticSearchBoxInput.test.js +++ b/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/__tests__/quanticSearchBoxInput.test.js @@ -97,7 +97,7 @@ describe('c-quantic-search-box-input', () => { jest.clearAllMocks(); }); - [false].forEach((textareaValue) => { + [false, true].forEach((textareaValue) => { describe(`when the textarea property is set to ${textareaValue}`, () => { it(`should display the ${ textareaValue ? 'expandable' : 'default' @@ -260,6 +260,82 @@ describe('c-quantic-search-box-input', () => { mockSuggestions.length + exampleRecentQueries.length ); }); + + describe('when pressing the DOWN to select a suggestion', () => { + it('should persist the value of the suggestion on blur', async () => { + const element = createTestComponent({ + ...defaultOptions, + suggestions: mockSuggestions, + recentQueries: exampleRecentQueries, + textarea: textareaValue, + inputValue: '', + }); + await flushPromises(); + + const input = element.shadowRoot.querySelector( + textareaValue + ? selectors.searchBoxTextArea + : selectors.searchBoxInput + ); + expect(input).not.toBeNull(); + + await input.focus(); + // First keydown to navigate to the clear recent queries option. + input.dispatchEvent( + new KeyboardEvent('keydown', {key: 'ArrowDown'}) + ); + // Second keydown to navigate to the first recent query option. + input.dispatchEvent( + new KeyboardEvent('keydown', {key: 'ArrowDown'}) + ); + await flushPromises(); + + expect(input.value).toBe(exampleRecentQueries[0]); + + await input.blur(); + await flushPromises(); + + expect(input.value).toBe(exampleRecentQueries[0]); + }); + + it('should persist the value of the suggestion when pressing the Escape key', async () => { + const element = createTestComponent({ + ...defaultOptions, + suggestions: mockSuggestions, + recentQueries: exampleRecentQueries, + textarea: textareaValue, + inputValue: '', + }); + await flushPromises(); + + const input = element.shadowRoot.querySelector( + textareaValue + ? selectors.searchBoxTextArea + : selectors.searchBoxInput + ); + expect(input).not.toBeNull(); + + await input.focus(); + // First keydown to navigate to the clear recent queries option. + input.dispatchEvent( + new KeyboardEvent('keydown', {key: 'ArrowDown'}) + ); + // Second keydown to navigate to the first recent query option. + input.dispatchEvent( + new KeyboardEvent('keydown', {key: 'ArrowDown'}) + ); + await flushPromises(); + + expect(input.value).toBe(exampleRecentQueries[0]); + + input.dispatchEvent( + new KeyboardEvent('keydown', {key: 'Escape'}) + ); + await flushPromises(); + + expect(input.value).toBe(exampleRecentQueries[0]); + }); + }); }); }); diff --git a/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/quanticSearchBoxInput.js b/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/quanticSearchBoxInput.js index 75e9a6bbec0..d200636ff33 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/quanticSearchBoxInput.js +++ b/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/quanticSearchBoxInput.js @@ -110,7 +110,10 @@ export default class QuanticSearchBoxInput extends LightningElement { ignoreNextEnterKeyPress = false; /** @type {string} */ ariaActiveDescendant; + /** @type {boolean} */ inputIsFocused = false; + /** @type {string} */ + _inputValue = ''; connectedCallback() { this.addEventListener( @@ -127,11 +130,16 @@ export default class QuanticSearchBoxInput extends LightningElement { } renderedCallback() { - if (this.input.value !== this.inputValue) { - this.input.value = this.inputValue; + if (this._inputValue !== this.inputValue) { + this._inputValue = this.inputValue; + this.setDisplayedInputValue(this.inputValue); } } + setDisplayedInputValue(value) { + this.input.value = value; + } + /** * @returns {quanticSearchBoxSuggestionsList} */ @@ -168,6 +176,10 @@ export default class QuanticSearchBoxInput extends LightningElement { * Sends the "quantic__submitSearch" event. */ sendSubmitSearchEvent() { + if (this._inputValue !== this.input.value) { + this.sendInputValueChangeEvent(this.input.value); + } + this.dispatchEvent( new CustomEvent('quantic__submitsearch', { bubbles: true, @@ -234,6 +246,7 @@ export default class QuanticSearchBoxInput extends LightningElement { // eslint-disable-next-line default-case switch (event.key) { case keys.ESC: + event.preventDefault(); this.input.removeAttribute('aria-activedescendant'); this.input.blur(); break; @@ -246,7 +259,7 @@ export default class QuanticSearchBoxInput extends LightningElement { event.preventDefault(); const {id, value} = this.suggestionListElement.selectionUp(); if (value) { - this.input.value = value; + this.setDisplayedInputValue(value); } this.ariaActiveDescendant = id; this.input.setAttribute( @@ -260,7 +273,7 @@ export default class QuanticSearchBoxInput extends LightningElement { event.preventDefault(); const {id, value} = this.suggestionListElement.selectionDown(); if (value) { - this.input.value = value; + this.setDisplayedInputValue(value); } this.ariaActiveDescendant = id; this.input.setAttribute( @@ -345,7 +358,7 @@ export default class QuanticSearchBoxInput extends LightningElement { } get isQueryEmpty() { - return !this.inputValue?.length; + return !this.input?.value?.length; } /** diff --git a/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/templates/defaultSearchBoxInput.html b/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/templates/defaultSearchBoxInput.html index 7827e00b1eb..5d7458e0080 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/templates/defaultSearchBoxInput.html +++ b/packages/quantic/force-app/main/default/lwc/quanticSearchBoxInput/templates/defaultSearchBoxInput.html @@ -20,7 +20,6 @@ id="combobox-with-button-id" class={searchBoxInputClass} type="search" - value={inputValue} placeholder={placeholder} onkeydown={onKeyDown} onfocus={onFocus} @@ -33,6 +32,7 @@ aria-haspopup="listbox" aria-autocomplete="list" data-cy="search-box-input" + data-value={inputValue} />