Blueprint: Classes.POPOVER_DISMISS is not working with <Link /> (react-router-dom) in Popover

Created on 20 Aug 2018  路  14Comments  路  Source: palantir/blueprint

Environment

    "@blueprintjs/core": "^3.0.1",
    "@blueprintjs/datetime": "^3.0.0",
    "@blueprintjs/select": "^3.0.0",

Question

When I use Popover i would like to hide the box when I click the link. But it only works when I click the button without the text.

<Popover
            position={Position.BOTTOM}
        >
            <Button className={Classes.MINIMAL} icon="menu">
                MENU
            </Button>
            <SlideBar />
        </Popover>

const LinkItemView = ({ icon, text, path, match, location, history }: ILinkPropsIn) => {
    return (
        <MenuItem icon={icon} className={Classes.POPOVER_DISMISS}
            text={
                <Link
                    className={classnames('o-menu__link', Classes.POPOVER_DISMISS, { 'o-menu__link--selected': isRouteEqualPathname(location, path) })}
                    to={path}
                >
                {text}
                </Link>
            }
        />
    )
}
const LinkItem = compose<ILinkPropsIn, ILinkPropsOut>(withRouter)(LinkItemView)
const SlideBar = () => (
    <Menu className={'o-menu--vertical'}>
        <div>
            <MenuDivider title="NAVIGATION" />
            <LinkItem icon="lock" text="Setting" path="/setting"  />
            <LinkItem icon="lock" text="Dashboard" path="/"  />
        </div>
        <div>
            <MenuDivider title="Test" />
            <LinkItem icon="lock" text="Test Tab" path="/test" />
        </div>

    </Menu>
)
P2 core bug help wanted

All 14 comments

@tomzaku upgrade to the latest core. I think this is related to #2776

Still error. :( in version "@blueprintjs/core": "^3.3.0",

Any updates or workaround for this?

here is a codesandbox demoing the issue https://codesandbox.io/s/pyv8888r2m

@tgreen7 did you manage to find a solution to this?

@hachtman I have not. I would love a fix though.

So the problem comes from here

private handlePopoverClick = (e: React.MouseEvent<HTMLElement>) => {
        const eventTarget = e.target as HTMLElement;
        // an OVERRIDE inside a DISMISS does not dismiss, and a DISMISS inside an OVERRIDE will dismiss.
        const dismissElement = eventTarget.closest(`.${Classes.POPOVER_DISMISS}, .${Classes.POPOVER_DISMISS_OVERRIDE}`);
        const shouldDismiss = dismissElement != null && dismissElement.classList.contains(Classes.POPOVER_DISMISS);
        const isDisabled = eventTarget.closest(`:disabled, .${Classes.DISABLED}`) != null;
        if (shouldDismiss && !isDisabled && !e.isDefaultPrevented()) {
            this.setOpenState(false, e);
            if (this.props.captureDismiss) {
                e.preventDefault();
            }
        }
    };

because it is checking !e.isDefaultPrevented() before triggering the dismiss (and react-router Link prevents default) the dismiss does not happen.

Removing the check for e.isDefaultPrevented() would mean a breaking change... but shouldn't this check be opt-in?

bump

This is still an annoying issue for us. Is there any way to get around it?

bump again.

very annoying indeed. I've tried many hacks, none of which have worked.

I've had the same problem. For me the following did the trick:

function MenuItemLink(props) {
  const handleLinkClick = (e) => e.target.parentElement.parentElement.click()

  return (
    <li className={Classes.POPOVER_DISMISS}>
      <Link onClick={handleLinkClick} to={props.to} className="bp3-menu-item"><Icon icon={props.icon} />
        <div className="bp3-text-overflow-ellipsis bp3-fill">{props.text}</div>
      </Link>
    </li>
  )
}

and then in the popover:

<Popover content=
      {
        <Menu>
          <MenuItemLink icon="plus" text="Create Organiztion" to="/organization/new"/>
          <Menu.Divider />
          <MenuItem onClick={props.onLogout} className="logout-btn" icon="log-out" text="Logout" />
        </Menu>
      }>
</Popover>

Best Regards,
Oliver, working on MonsterWriter.app

I've had the same problem. For me the following did the trick:

function MenuItemLink(props) {
  const handleLinkClick = (e) => e.target.parentElement.parentElement.click()

  return (
    <li className={Classes.POPOVER_DISMISS}>
      <Link onClick={handleLinkClick} to={props.to} className="bp3-menu-item"><Icon icon={props.icon} />
        <div className="bp3-text-overflow-ellipsis bp3-fill">{props.text}</div>
      </Link>
    </li>
  )
}

and then in the popover:

<Popover content=
      {
        <Menu>
          <MenuItemLink icon="plus" text="Create Organiztion" to="/organization/new"/>
          <Menu.Divider />
          <MenuItem onClick={props.onLogout} className="logout-btn" icon="log-out" text="Logout" />
        </Menu>
      }>
</Popover>

Best Regards,
Oliver, working on MonsterWriter.app

This works for me, thanks

Without altering the MenuItem:

const { parentElement } = e.target
parentElement.classList.add(Classes.POPOVER_DISMISS)
parentElement.click()

Looking again at the PR which inadvertently caused the problem (https://github.com/palantir/blueprint/pull/2758), I think we should reconsider whether preventDefault is the right mechanism for the "capture dismiss" feature in the first place. The meaning of the "default" behavior becomes ambiguous in cases like this. So perhaps we should instead try to attach Blueprint-specific information to the click event which the Popover component can read to determine whether to "capture" a dismiss event.

Was this page helpful?
0 / 5 - 0 ratings