I am having some weird issue which I want to know why that is happening.
I am using MUI and React. Here if I click on my menu icon (which opens the menu) even if I add the stopPropogation. It still fire’s my top most paper element onClick handler. Tried the preventDefault too but the immediate parents (Box & Stack element) onClick is not getting triggered. It’s kind of jumping directly to parent.
Code
// react
import { useState } from "react";
// mui
import {
Paper,
Card,
CardContent,
Typography,
CardActions,
Menu,
MenuItem,
Skeleton,
Stack,
Box,
IconButton,
} from "@mui/material";
import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
// components
import Chip from "../base/chip";
import { Button } from "../base";
import { ProjectInfo } from "@/utils/types";
import { format } from "date-fns";
import { MoreHoriz } from "@mui/icons-material";
function ProjectMenu({
anchorEl,
open,
handleClose,
handleOpenDeleteModal,
// handleOpenEditModal,
projectId,
}: {
anchorEl: Element | null;
projectId: string | number;
open: boolean;
handleClose: () => void;
handleOpenDeleteModal: (projectId: string | number) => void;
// handleOpenEditModal: (projectId: string | number) => void;
}) {
const menuOptions = [
{
title: "Delete Vault",
handler: (projectId: string | number) => {
handleOpenDeleteModal(projectId);
handleClose();
},
icon: (
<DeleteForeverIcon sx={{ mr: 2, color: "customColors.error.main" }} />
),
sx: { color: "customColors.error.main" },
},
];
return (
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
anchorOrigin={{
vertical: "top",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
slotProps={{
paper: {
sx: {
borderRadius: 2,
},
},
}}
>
{menuOptions.map((option, index) => (
<MenuItem
key={`option-${index}`}
onClick={(e) => {
e.stopPropagation();
option.handler(projectId);
}}
sx={{
fontSize: 14,
fontWeight: "normal",
lineHeight: "1.25rem",
borderBottom: "1px solid #F2F4F7",
":last-child": {
borderBottom: "1px solid transparent",
},
...option.sx,
}}
>
{option.icon} {option.title}
</MenuItem>
))}
</Menu>
);
}
export default function ProjectCard({
projectInfo,
disableMenu,
handleDelete,
handleEdit,
handleView,
onClick,
}: {
projectInfo: ProjectInfo;
disableMenu?: boolean;
onClick: () => void;
handleDelete: (projectId: string | number) => void;
handleEdit: (projectId: string | number) => void;
handleView: (projectId: string | number) => void;
}) {
const [anchorEl, setAnchorEl] = useState<null | Element>(null);
const open = Boolean(anchorEl);
return (
<Paper
elevation={0}
sx={{
boxShadow: "0px 2px 20px 0px rgba(0, 0, 0, 0.08)",
borderRadius: 2.5,
bgcolor: "background.paper",
cursor: "pointer",
transition: "transform .2s ease-in, border-color .2s ease-in",
border: "2px solid transparent",
":hover": {
transform: "scale(1.01)",
border: "2px solid",
borderColor: "customColors.primary.main",
},
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
flex: 1,
height: "100%",
}}
onClick={() => {
alert("bubblepaper");
}}
>
<Stack
sx={{
p: 3,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
position: "relative",
height: "100%",
}}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
alert("bubblestack");
}}
>
<Box
onClick={(e) => {
// setAnchorEl(e.currentTarget);
e.stopPropagation();
e.preventDefault();
alert("bubblebox");
}}
sx={{
position: "absolute",
top: 5,
right: 5,
border: "1px solid red",
}}
>
{!disableMenu && (
<IconButton
onClick={(e) => {
e.stopPropagation();
setAnchorEl(e.currentTarget);
}}
>
<MoreHoriz />
</IconButton>
)}
</Box>
<Stack>
<Typography
sx={{
fontSize: 22,
fontWeight: 500,
lineHeight: "normal",
letterSpacing: "-0.32px",
color: "customColors.text.greyBlack",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
width: "calc(100% - 30px)",
overflow: "hidden",
mr: 1,
}}
>
{projectInfo?.name
? projectInfo?.name?.charAt(0)?.toUpperCase() +
projectInfo?.name?.slice(1)
: "-"}
</Typography>
<Typography
mb={0.5}
sx={{
fontSize: 17,
fontWeight: 600,
lineHeight: "1.75rem",
letterSpacing: "-0.25px",
color: "customColors.text.greyBlack",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
width: "calc(100% - 30px)",
overflow: "hidden",
}}
>
{projectInfo?.metadata?.client_name
? projectInfo?.metadata?.client_name?.charAt(0)?.toUpperCase() +
projectInfo?.metadata?.client_name?.slice(1)
: "-"}
</Typography>
<Typography
mb={2}
sx={{ color: "customColors.text.grey2", flex: 1 }}
className="clip-multiline-text2"
>
{projectInfo?.description
? projectInfo?.description?.charAt(0)?.toUpperCase() +
projectInfo?.description?.slice(1)
: "-"}
</Typography>
</Stack>
<Stack gap={2}>
<Stack>
<Stack direction="row" alignItems="center" gap={1}>
<Typography
sx={{ color: "customColors.text.grey2", mb: 0.5 }}
component="span"
>
Status:{" "}
</Typography>{" "}
<Chip
label="Public"
size="small"
sx={{
bgcolor: "customColors.error.bg",
color: "customColors.error.main",
fontWeight: "bold",
borderRadius: "3px",
}}
iconColor="customColors.error.main"
/>
</Stack>
<Typography sx={{ color: "customColors.text.grey2", mt: 0.5 }}>
Last updated:{" "}
{projectInfo?.created_at
? format(projectInfo?.created_at, "dd/MM/yyyy")
: ""}
</Typography>
</Stack>
<Stack direction="row" gap={2}>
<Button
variant="outlined"
onClick={() => {
handleEdit(projectInfo.id);
}}
>
Edit{" "}
</Button>
<Button
variant="contained"
onClick={() => {
handleView(projectInfo.id);
}}
>
View{" "}
</Button>
</Stack>
</Stack>
</Stack>
<ProjectMenu
anchorEl={anchorEl}
open={open}
projectId={projectInfo?.id}
handleClose={() => setAnchorEl(null)}
handleOpenDeleteModal={(projectId) => handleDelete(projectId)}
// handleOpenEditModal={(projectId) => handleEdit(projectId)}
/>
</Paper>
);
}
export function ProjectCardSkeleton() {
return (
<Card
elevation={0}
sx={{
boxShadow: "0px 2px 20px 0px rgba(0, 0, 0, 0.08)",
borderRadius: 2.5,
bgcolor: "background.paper",
cursor: "pointer",
transition: "transform .2s ease-in, border-color .2s ease-in",
border: "2px solid transparent",
":hover": {
transform: "scale(1.01)",
border: "2px solid",
borderColor: "customColors.primary.main",
},
}}
>
<CardContent sx={{ p: 3, pb: 0 }}>
<Skeleton
variant="rounded"
width="70%
"
/>
<Skeleton variant="rounded" width="55%" sx={{ mt: 1 }} />
<Skeleton variant="rounded" width="100%" height={50} sx={{ mt: 2 }} />
<Stack direction="row" alignItems="center" gap={2}>
<Skeleton width="20%" height={30} />
<Skeleton width="30%" height={30} />
</Stack>
<Skeleton width="70%" height={30} />
</CardContent>
<CardActions sx={{ px: 3, pb: 4 }}>
<Skeleton width="20%" height={50} />
<Skeleton width="20%" height={50} />
</CardActions>
</Card>
);
}
I already tried using stopPropogation
and preventDefaults
on immidiate parents.