I'm trying to create a Material UI 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>
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>
</>
);
}