Is it possible to show an Ant Design Tooltip conditionally only when the content has ellipsis?

Viewed 10498

I'm using the Ant Design component <Paragraph> to show a text with a variable size, and I use the ellipsis option to show "..." when that text exceeds the length of its container.

I also added a <Tooltip> to that <Paragraph> to show the entire text when it is collapsed:

<Tooltip title="This text is displayed, so I don't want to show a tooltip.">
  <Paragraph ellipsis>
    This text is displayed, so I don't want to show a tooltip.
  </Paragraph>
</Tooltip>

Unfortunately, I can't find a way to know when the ellipsis appears on the paragraph, so my Tooltip is always shown.

tooltip-shows-on-a-fully-displayed-text

Is it possible to show an Ant Design Tooltip only when the content has been affected by the ellipsis option?

2 Answers

Ant Design v4.2.0 made that possible by adding an "onEllipsis" function to the "ellipsis" object.

TooltipParagraph API

Working example

const TooltipParagraph: React.FC<ParagraphProps> = ({
  children,
  ellipsis,
  ...props
}) => {
  const [truncated, setTruncated] = useState(false);

  return (
    <Tooltip title={truncated ? children : undefined}>
      <Paragraph
        {...props}
        ellipsis={{ ...ellipsis, onEllipsis: setTruncated }}
      >
        {/* NOTE: Fragment is necessary to avoid showing the title */}
        <>{children}</>
      </Paragraph>
    </Tooltip>
  );
};

I do not know about antdegin, But I faced this problem once and I was able to resolve it.

To know if an text tag has overflown this check will be truthy:

element.offsetWidth < element.scrollWidth.

I tried to think about a generic way to solve this issue and ended up with this component (this is not production ready!)


function SmartText({
  TypographyComponent = "h1",
  TypographyComponentProps = {},
  TooltipComponent = ({ children }) => children, // just for demo purpose, it will do nothing basically if not passed
  ToolTipComponentProps = {},
  children = ""
}) {
  const textComponentRef = React.useRef(null);
  const [tooltipTitle, setTooltipTitle] = React.useState("");

  React.useEffect(() => {
    if (
      textComponentRef.current &&
      textComponentRef.current.offsetWidth <
        textComponentRef.current.scrollWidth
    ) {
      setTooltipTitle(textComponentRef.current.innerText);
    }
  }, [children]);

  return (
    <TooltipComponent {...ToolTipComponentProps} title={tooltipTitle}>
      <TypographyComponent {...TypographyComponentProps} ref={textComponentRef}>
        {children}
      </TypographyComponent>
    </TooltipComponent>
  );
}

The TypographyComponent in your case is the Paragraph, to use this method, the TypographyComponent has to forward the ref or to support it.

I used the SmartText with @material-ui tooltip like this:

<SmartText
  TypographyComponentProps={{
    style: {
      whiteSpace: "nowrap",
      maxWidth: 500,
      textOverflow: "ellipsis",
      overflow: "hidden",
      textAlign: "left"
    }
  }}
  TooltipComponent={Tooltip}
  ToolTipComponentProps={{
    placement: "right"
  }}
>
  Tooltip is needed because this text is long
</SmartText>
<SmartText
  TypographyComponentProps={{
    style: {
      whiteSpace: "nowrap",
      maxWidth: 300,
      textOverflow: "ellipsis",
      overflow: "hidden",
      textAlign: "left"
    }
  }}
  TooltipComponent={Tooltip}
  ToolTipComponentProps={{
    placement: "right"
  }}
>
  No tooltip needed
</SmartText>

Finally, here is working codesandbox demo

Related