I'm trying to create a MUI ButtonGroup
with disabled buttons and tooltip.
The following code block shows the buttons correctly, but as described here (https://material-ui.com/components/tooltips/#disabled-elements) disabled elements cannot be provided with a tooltip.
<ButtonGroup>
<Tooltip title={"This is button A"}>
<Button>{"Button A"}</Button>
</Tooltip>
<Tooltip title={"This is button B"}>
<Button disabled>{"Button B"}</Button>
</Tooltip>
</ButtonGroup>
But if I add a span
around the disabled button the group layout will be destroyed.
<ButtonGroup>
<Tooltip title={"This is button A"}>
<Button>{"Button A"}</Button>
</Tooltip>
<Tooltip title={"This is button B"}>
<span>
<Button disabled>{"Button B"}</Button>
</span>
</Tooltip>
</ButtonGroup>
By default, tooltips will not be displayed on disabled elements. However, you can enable this behavior by using the following steps: Add a disabled element like the button element into a div whose display style is set to inline-block . Set the pointer event as none for the disabled element (button) through CSS.
To disable displaying tooltip on hover, we can use the disableHoverListener prop. The disableFocusListener prop lets us disable display tooltip when we focus on an element. disableTouchListener lets us disable tooltip display when we touch an element. to disable the focus and touch listeners.
import Tooltip, { tooltipClasses } from "@mui/material/Tooltip"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; import { styled } from "@mui/material/styles"; The tooltipClasses import is a useful way to get access to all the names of CSS classes applied by default to the tooltip.
There are two aspects about a disabled button that get in the way of the tooltip behavior:
<button>
element (independent of Material-UI) does not fire events in a manner to support proper behavior of the Tooltip.ButtonBase
component (leveraged by Button
) when it is disabled.The second problem can be addressed by overriding Material-UI's disabled styling to allow pointer events:
import MuiButton from "@material-ui/core/Button";
import { withStyles } from "@material-ui/core/styles";
const Button = withStyles({
root: {
"&.Mui-disabled": {
pointerEvents: "auto"
}
}
})(MuiButton);
The first problem can be addressed by using Button's component
prop to use a <div>
element instead of a <button>
element. Disabled button elements do not receive click events, so in order to have the Button still behave in a disabled fashion, this code removes the onClick
prop when it is disabled.
const ButtonWithTooltip = ({ tooltipText, disabled, onClick, ...other }) => {
const adjustedButtonProps = {
disabled: disabled,
component: disabled ? "div" : undefined,
onClick: disabled ? undefined : onClick
};
return (
<Tooltip title={tooltipText}>
<Button {...other} {...adjustedButtonProps} />
</Tooltip>
);
};
Below is a working demonstration with Buttons B and C both disabled. Buttons A and B use the approach outlined above and Button C is a regular Material-UI Button without a Tooltip for comparison. The additional Button below them toggles the disabled state of B and C.
import React from "react";
import Tooltip from "@material-ui/core/Tooltip";
import MuiButton from "@material-ui/core/Button";
import { withStyles } from "@material-ui/core/styles";
import ButtonGroup from "@material-ui/core/ButtonGroup";
const Button = withStyles({
root: {
"&.Mui-disabled": {
pointerEvents: "auto"
}
}
})(MuiButton);
const ButtonWithTooltip = ({ tooltipText, disabled, onClick, ...other }) => {
const adjustedButtonProps = {
disabled: disabled,
component: disabled ? "div" : undefined,
onClick: disabled ? undefined : onClick
};
return (
<Tooltip title={tooltipText}>
<Button {...other} {...adjustedButtonProps} />
</Tooltip>
);
};
export default function App() {
const [bAndCDisabled, setBAndCDisabled] = React.useState(true);
return (
<>
<ButtonGroup>
<ButtonWithTooltip
tooltipText="This is Button A"
onClick={() => console.log("A")}
>
{"Button A"}
</ButtonWithTooltip>
<ButtonWithTooltip
tooltipText="This is Button B"
onClick={() => console.log("B")}
disabled={bAndCDisabled}
>
{"Button B"}
</ButtonWithTooltip>
<MuiButton onClick={() => console.log("C")} disabled={bAndCDisabled}>
{"Button C"}
</MuiButton>
</ButtonGroup>
<br />
<br />
<MuiButton
variant="contained"
onClick={() => setBAndCDisabled(!bAndCDisabled)}
>
Toggle disabled for B and C
</MuiButton>
</>
);
}
Yes, it is possible. you would need to wrap your button in span tag example
<Tooltip title={YOUR_MESSAGE_HERE}>
<span>
<Button disabled>my button is disabled</Button>
</span>
</Tooltip>
Adding div/span around the buttons can mess with the button styling sometimes. So we should ideally use <>
or <React.Fragment>
. But the tool tips doesn't work with react fragments so we can use the <span>
like mentioned in the question. But in that case, since ButtonGroup
is used here and that works by cloning the child button elements and passing props for the styling, we'll have to send the style props to the "Button B" in this case.
import React from "react";
import "./styles.css";
import { Tooltip, Button } from "@material-ui/core";
import ButtonGroup from "@material-ui/core/ButtonGroup";
export default function App() {
const ButtonDemo = (props) => {
return (
<Tooltip title={"This is button B"}>
<span>
<Button {...props} disabled>
{"Button B"}
</Button>
</span>
</Tooltip>
);
};
return (
<ButtonGroup>
<Tooltip title={"This is button A"}>
<Button>{"Button A"}</Button>
</Tooltip>
<ButtonDemo />
</ButtonGroup>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With