69

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>
Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Nfinity
  • 718
  • 1
  • 6
  • 7

5 Answers5

83

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>
Aflatoon Singh
  • 855
  • 5
  • 2
  • 7
    Unfortunately as much as I was expecting this to work, Material-UI is brittle and this can mess with material-ui styles. – AJP Jan 20 '22 at 14:36
  • 11
    It seems like this is now the [officially documented way of doing it](https://mui.com/material-ui/react-tooltip/#disabled-elements). They also mention that to work in Safari, you will need to wrap it with a block display element such as a `
    `.
    – Ethan Apr 26 '22 at 06:50
  • this solution is simple and works to me – elVengador Nov 24 '22 at 14:18
  • 3
    This is fine for a single button, but interferes with the styles when dealing with a `ButtonGroup` which is what the question was about. The documentation linked to in @Ethan's comment does not address the `ButtonGroup` aspect. – Ryan Cogswell Dec 30 '22 at 16:46
64

There are two aspects about a disabled button that get in the way of the tooltip behavior:

  1. As mentioned in the docs you referenced, a disabled <button> element (independent of Material-UI) does not fire events in a manner to support proper behavior of the Tooltip.
  2. Material-UI specifically disables pointer-events in Material-UI's 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>
    </>
  );
}

Edit ButtonGroup with Tooltip on disabled Button

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
  • Oh. So providing extra structure in `component` is a trick to retaining styling. Awesome. – przemo_li Aug 06 '20 at 06:56
  • 1
    @przemo_li I'm not sure I understand your comment. My solution does not add any "extra structure", it just uses a "div" instead of a "button" element in order to avoid losing some of the hover events needed for Tooltip to work correctly (hover events are not triggered reliably for a disabled button element). The styling works as intended because my solution does **not** add any additional DOM structure. – Ryan Cogswell Aug 06 '20 at 17:23
  • this solution does not work for `"@material-ui/core": ^3.9.3`...any solution for older version material – Rahul Yadav Jun 16 '21 at 11:15
  • @RahulYadav `ButtonGroup` was introduced in [version 4.1.0](https://github.com/mui-org/material-ui/releases/tag/v4.1.0), so there is no solution for versions older than that since it didn't exist yet. – Ryan Cogswell Jun 16 '21 at 13:22
  • 2
    @RyanCogswell my bad...I was asking for a normal `
    ` – Rahul Yadav Jun 18 '21 at 05:42
12

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

    

Edit fragrant-night-3gqgd

Manu Sharma
  • 656
  • 1
  • 7
  • 22
  • 2
    This doesn't fully retain the ButtonGroup look. Here is a comparison (correct look on top, yours on bottom): https://codesandbox.io/s/span-in-buttongroup-cfvn5. Some of the [styling](https://github.com/mui-org/material-ui/blob/v4.11.0/packages/material-ui/src/ButtonGroup/ButtonGroup.js#L44) is based on `:not(:first-child)` of the group. The span prevents that styling from being applied correctly. – Ryan Cogswell Aug 05 '20 at 17:19
  • Hi @RyanCogswell I get the point that styling is affected by span. But in this case, I did not catch the difference b/w the the button groups. [link to the image](https://imgur.com/RxhyRF1) – Manu Sharma Aug 06 '20 at 05:12
  • 1
    Look at second button, please. Its rounded on all sides. It should only be rounded on external side. Extra wrapping cases styling from button group to be only partially applied :( – przemo_li Aug 06 '20 at 06:46
2

For MUI 5.11 and above you'll need to wrap the button in span tags instead.

<Tooltip title="tooltip text">
  <span>
    <Button disabled>Disabled Button</Button>
  </span>
</Tooltip>
saltonsea
  • 21
  • 2
0

Use this for mui > v5

<Tooltip title={YOUR_MESSAGE_HERE}>
  <Button disabled>
    <span>
     my button is disabled
    </span>
  </Button>
</Tooltip>
Priyanshu
  • 73
  • 6