Search code examples
reactjsnext.jsantdpreventdefaultstoppropagation

stopPropagation doesn't work with Ant design Dropdown Menu


I have a card component wrapped inside a nextjs Link. The card consists of a body and footer. this is my cardFooter. inside cardFooter I have an Ant Design DropDown menu with two items. as you can see i have put preventDefault and stopPropagation almost everywhere that can cause the Link to trigger but still when i click on Share dropdown menu item it first shows the modal and when I close the modal the page navigates to the Link's address.

Next js 13.4 and ant design 5.12.5

const CardFooter: React.FC<CardFooterProps> = ({ adItem }) => {
  const [showShareDialog, setShowShareDialog] = useState<boolean>(false);
  const { token } = useToken();

  const handleModalChange = () => {
    setShowShareDialog((prev) => !prev);
  };


  return (
    <div className={`flex h-[32px] justify-between`}>
      
      <Space>
        <ActionMenu
          onShare={handleModalChange}
          onReport={() => console.log('reported')}
        />
        <ShareAd
          onChange={handleModalChange}
          openDialog={showShareDialog}
          adItem={adItem}
        />
      </Space>
    </div>
  );
};

this is my ActionMenu component:

const items: MenuProps['items'] = [
  {
    key: 'report',
    label: 'rep',
    danger: true,
    icon: <ExclamationCircleOutlined />,
  },
  {
    key: 'share',
    label: 'shr',
    icon: <ShareAltOutlined />,
    
  },
];

const ActionMenu: FC<ActionMenuProps> = ({ onReport, onShare }) => {
  const handleReportClick: MenuProps['onClick'] = (e) => {
    // doesn't trigger Link component
    onReport();
  };

  const handleShareClick: MenuProps['onClick'] = (e) => {
    e.domEvent.preventDefault();
    e.domEvent.stopPropagation();
    //even with stopPropagation and preventDefault it triggers Link component
    onShare();
  };

  const handleMenuClick: MenuProps['onClick'] = (e) => {
    e.domEvent.preventDefault();
    e.domEvent.stopPropagation();
    switch (e.key) {
      case 'report':
        handleReportClick(e);
        break;
      case 'share':
        handleShareClick(e);
        break;
    }
  };

  const menuProps = {
    items,
    onClick: handleMenuClick,
  };

  return (
    <Dropdown menu={menuProps} trigger={['click']}>
      <Button
        key={'more'}
        type={'text'}
        shape={'circle'}
        icon={<EllipsisOutlined />}
        onClick={(e) => {
          e.preventDefault();
          e.stopPropagation();
          e.nativeEvent.stopImmediatePropagation();
        }}
      />
    </Dropdown>
  );
};

this is my ShareAd component in case that it may help:

const ShareAd: React.FC<ShareAdProps> = ({ openDialog, onChange, adItem }) => {
  const [linkCopied, setLinkCopied] = useState<boolean>(false);

  const handleCopyLink = (e: React.MouseEvent<HTMLElement>) => {
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
    const itemLink = window.location.href + createLink('Item', adItem);
    navigator.clipboard.writeText(itemLink);
    setLinkCopied(true);
  };

  const modalOkHandler = () => {
    setLinkCopied(false);
    onChange();
  };

  const modalCancelHandler = () => {
    setLinkCopied(false);
    onChange();
  };

  return (
    <Modal
      title='share'
      open={openDialog}
      onCancel={modalCancelHandler}
      centered
      footer={
        <Button onClick={modalOkHandler} type='primary'>
          close
        </Button>
      }
    >
      <p className='mb-2'>share with friends</p>
      <Button onClick={handleCopyLink} type='default' className='w-full'>
        <CopyOutlined />
        {linkCopied ? 'copied' : 'copy'}
      </Button>
    </Modal>
  );
};

Solution

  • I finally found the answer and I share it here in case it can help someone else. in shareAd component, I added the modalRender prop to antd Modal component like below. also add e.stopPropagation() on modal's onCancel :

        const modalCancelHandler = (e: React.MouseEvent<HTMLElement>) => {
            e.stopPropagation();
            setLinkCopied(false);
            onChange();
      };
    
    
      return (
        <Modal
          title='share'
          open={openDialog}
          onCancel={modalCancelHandler}
          modalRender={(modal) => (
            <div onClick={(e) => e.stopPropagation()}>{modal}</div>
          )}
          centered
          footer={
            <Button onClick={modalOkHandler} type='primary'>
              close
            </Button>
          }
        >
          <p className='mb-2'>share with friends</p>
          <Button onClick={handleCopyLink} type='default' className='w-full'>
            <CopyOutlined />
            {linkCopied ? 'copied' : 'copy'}
          </Button>
        </Modal>
      );