From 6dab069530abdbe108f856fd0bbcd3e78a5c34e9 Mon Sep 17 00:00:00 2001 From: Patrick Brown Date: Mon, 17 Apr 2023 14:34:33 -0700 Subject: [PATCH] [select] fix(Select2): check shouldDismissPopover on menu items (#6070) --- .../select/src/components/select/select2.tsx | 14 ++++++- packages/select/test/select2Tests.tsx | 38 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/select/src/components/select/select2.tsx b/packages/select/src/components/select/select2.tsx index 09d1fbdc2a..88adfb097e 100644 --- a/packages/select/src/components/select/select2.tsx +++ b/packages/select/src/components/select/select2.tsx @@ -31,7 +31,13 @@ import { setRef, Utils, } from "@blueprintjs/core"; -import { Popover2, Popover2ClickTargetHandlers, Popover2TargetProps, PopupKind } from "@blueprintjs/popover2"; +import { + Popover2, + Classes as Popover2Classes, + Popover2ClickTargetHandlers, + Popover2TargetProps, + PopupKind, +} from "@blueprintjs/popover2"; import { Classes, ListItemsProps, SelectPopoverProps } from "../../common"; import { QueryList, QueryListRendererProps } from "../query-list/queryList"; @@ -291,7 +297,11 @@ export class Select2 extends AbstractPureComponent2, Select2S }; private handleItemSelect = (item: T, event?: React.SyntheticEvent) => { - this.setState({ isOpen: false }); + const target = event?.target as HTMLElement; + const shouldDismiss = + target?.closest(`.${CoreClasses.MENU_ITEM}`)?.classList?.contains(Popover2Classes.POPOVER2_DISMISS) ?? true; + + this.setState({ isOpen: !shouldDismiss }); this.props.onItemSelect?.(item, event); }; diff --git a/packages/select/test/select2Tests.tsx b/packages/select/test/select2Tests.tsx index f61bc2cfa5..0a42c7ab70 100644 --- a/packages/select/test/select2Tests.tsx +++ b/packages/select/test/select2Tests.tsx @@ -20,7 +20,7 @@ import * as React from "react"; import * as sinon from "sinon"; import { InputGroup, Keys, MenuItem } from "@blueprintjs/core"; -import { Popover2 } from "@blueprintjs/popover2"; +import { MenuItem2, Popover2 } from "@blueprintjs/popover2"; import { ItemRendererProps, Select2, Select2Props, Select2State } from "../src"; import { Film, renderFilm, TOP_100_FILMS } from "../src/__examples__"; @@ -128,6 +128,42 @@ describe("", () => { assert.isTrue(handlers.onItemSelect.calledOnce); }); + it("closes the popover when selecting first MenuItem", () => { + const itemRenderer = (film: Film) => { + return ; + }; + const wrapper = select({ itemRenderer, popoverProps: { usePortal: false } }); + + // popover should start close + assert.strictEqual(wrapper.find(Popover2).prop("isOpen"), false); + + // popover should open after clicking the button + wrapper.find("[data-testid='target-button']").simulate("click"); + assert.strictEqual(wrapper.find(Popover2).prop("isOpen"), true); + + // and should close after the a menu item is clicked + wrapper.find(Popover2).find(".bp4-menu-item").first().simulate("click"); + assert.strictEqual(wrapper.find(Popover2).prop("isOpen"), false); + }); + + it("does not close the popover when selecting a MenuItem with shouldDismissPopover", () => { + const itemRenderer = (film: Film) => { + return ; + }; + const wrapper = select({ itemRenderer, popoverProps: { usePortal: false } }); + + // popover should start closed + assert.strictEqual(wrapper.find(Popover2).prop("isOpen"), false); + + // popover should open after clicking the button + wrapper.find("[data-testid='target-button']").simulate("click"); + assert.strictEqual(wrapper.find(Popover2).prop("isOpen"), true); + + // and should not close after the a menu item is clicked + wrapper.find(Popover2).find(".bp4-menu-item").first().simulate("click"); + assert.strictEqual(wrapper.find(Popover2).prop("isOpen"), true); + }); + function select(props: Partial> = {}, query?: string) { const wrapper = mount( {...defaultProps} {...handlers} {...props}>