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>
);
};
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>
);