From 5f9c263e11eb9a06e2bbd722b3f80b3bc91bb226 Mon Sep 17 00:00:00 2001 From: edleeks87 Date: Fri, 8 Dec 2023 12:16:42 +0000 Subject: [PATCH] fix(filterable-select): ensure list does not reopen when user clicks option and openOnFocus prop set Ensures that the `SelectList` does not reopen when a user selects and `Option` via clicking and the `openOnFocus` prop is set. fix #6462 --- .../filterable-select.cy.tsx | 28 +++++++++++++++++ .../filterable-select.component.tsx | 10 +++++- .../filterable-select.spec.tsx | 31 ++++++++++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/cypress/components/select/filterable-select/filterable-select.cy.tsx b/cypress/components/select/filterable-select/filterable-select.cy.tsx index 4b8e8ebd81..d9698decdc 100644 --- a/cypress/components/select/filterable-select/filterable-select.cy.tsx +++ b/cypress/components/select/filterable-select/filterable-select.cy.tsx @@ -576,6 +576,34 @@ context("Tests for FilterableSelect component", () => { selectListWrapper().should("be.visible"); }); + it("should not reopen list when openOnFocus set and user selects an option via click", () => { + CypressMountWithProviders( + + ); + + commonDataElementInputPreview().focus(); + selectInput().should("have.attr", "aria-expanded", "true"); + selectListWrapper().should("be.visible"); + selectOption(positionOfElement("first")).click(); + selectListWrapper().should("not.be.visible"); + }); + + it("should open list when openOnFocus set, user selects an option via enter key and then input is blurred then focussed again", () => { + CypressMountWithProviders( + + ); + + commonDataElementInputPreview().focus(); + selectInput().should("have.attr", "aria-expanded", "true"); + selectListWrapper().should("be.visible"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectListWrapper().should("not.be.visible"); + commonDataElementInputPreview().blur(); + commonDataElementInputPreview().focus(); + selectListWrapper().should("be.visible"); + }); + it("should check list is open when input is clicked and openOnFocus is set", () => { CypressMountWithProviders( diff --git a/src/components/select/filterable-select/filterable-select.component.tsx b/src/components/select/filterable-select/filterable-select.component.tsx index d3ffb9dbd2..f93f0df505 100644 --- a/src/components/select/filterable-select/filterable-select.component.tsx +++ b/src/components/select/filterable-select/filterable-select.component.tsx @@ -153,6 +153,7 @@ export const FilterableSelect = React.forwardRef( label, }); const focusTimer = useRef>(null); + const openOnFocusFlagBlock = useRef(false); if (!deprecateInputRefWarnTriggered && inputRef) { deprecateInputRefWarnTriggered = true; @@ -478,12 +479,14 @@ export const FilterableSelect = React.forwardRef( setActiveDescendantId(selectedOptionId); if (selectionType !== "navigationKey") { + openOnFocusFlagBlock.current = + selectionType === "click" && !!openOnFocus; setOpen(false); textboxRef?.focus(); textboxRef?.select(); } }, - [textboxRef, triggerChange] + [textboxRef, triggerChange, openOnFocus] ); const onSelectListClose = useCallback(() => { @@ -521,6 +524,11 @@ export const FilterableSelect = React.forwardRef( clearTimeout(focusTimer.current); } + if (openOnFocusFlagBlock.current) { + openOnFocusFlagBlock.current = false; + return; + } + // we need to use a timeout here as there is a race condition when rendered in a modal // whereby the select list isn't visible when the select is auto focused straight away focusTimer.current = setTimeout(() => { diff --git a/src/components/select/filterable-select/filterable-select.spec.tsx b/src/components/select/filterable-select/filterable-select.spec.tsx index 49e71fa5d1..fd90b11754 100644 --- a/src/components/select/filterable-select/filterable-select.spec.tsx +++ b/src/components/select/filterable-select/filterable-select.spec.tsx @@ -1097,7 +1097,19 @@ describe("FilterableSelect", () => { describe('when the "openOnFocus" prop is set', () => { describe("and the Textbox Input is focused", () => { - it("the SelectList should be rendered", () => { + it("should render the SelectList", () => { + const wrapper = renderSelect({ openOnFocus: true }); + + act(() => { + wrapper.find("input").simulate("focus"); + jest.runOnlyPendingTimers(); + }); + wrapper + .find(Option) + .forEach((option) => expect(option.getDOMNode()).toBeVisible()); + }); + + it("should not reopen the SelectList when a user selects and Option by clicking", () => { const wrapper = renderSelect({ openOnFocus: true }); act(() => { @@ -1107,6 +1119,23 @@ describe("FilterableSelect", () => { wrapper .find(Option) .forEach((option) => expect(option.getDOMNode()).toBeVisible()); + act(() => { + wrapper.find(SelectList).prop("onSelect")({ + value: "opt1", + text: "red", + selectionType: "click", + selectionConfirmed: true, + }); + }); + act(() => { + // need to programatically focus again here to hit coverage + wrapper.find("input").simulate("focus"); + jest.runOnlyPendingTimers(); + }); + wrapper + .update() + .find(Option) + .forEach((option) => expect(option.getDOMNode()).not.toBeVisible()); }); describe.each(["readOnly", "disabled"])(