UI: Improve TooltipTruncatedText and now underlying useCheckTruncatedElement (#29232)
## For #27667 - Have `TooltipTruncatedText` component use `useCheckTruncatedElement` to track its current state of truncation. - Update `useCheckTruncatedElement` to re-evaluate truncation state based on changes to the width of the element itself as opposed to changes to viewport width. This facilitates truncation when the width of the element is updated due to user interaction / change in UI state other than window resize, e.g. checking a policy in the policy software automations modal (see issue description for details reproduction instructions there). **Truncation with tooltip successful for UI state changes:**  Truncation with tooltip successful for viewport resizing:   - [x] Changes file added for user-visible changes in `changes/⁄ - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
co-authored by
Jacob Shandling
parent
28ba274f1f
commit
19fe0ff5ce
@@ -0,0 +1 @@
|
||||
- Improve effectiveness of app-wide text-truncation-into-tooltip functionality.
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useState, useRef, useLayoutEffect } from "react";
|
||||
import React, { useRef } from "react";
|
||||
import { uniqueId } from "lodash";
|
||||
import classnames from "classnames";
|
||||
|
||||
import ReactTooltip from "react-tooltip";
|
||||
import { COLORS } from "styles/var/colors";
|
||||
import { useCheckTruncatedElement } from "hooks/useCheckTruncatedElement";
|
||||
|
||||
interface ITooltipTruncatedTextCellProps {
|
||||
value: React.ReactNode;
|
||||
@@ -30,22 +31,15 @@ const TooltipTruncatedText = ({
|
||||
|
||||
// Tooltip visibility logic: Enable only when text is truncated
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const [tooltipDisabled, setTooltipDisabled] = useState(true);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (ref?.current !== null) {
|
||||
const scrollWidth = ref.current.scrollWidth;
|
||||
const offsetWidth = ref.current.offsetWidth;
|
||||
setTooltipDisabled(scrollWidth <= offsetWidth);
|
||||
}
|
||||
}, [ref]);
|
||||
// End
|
||||
const isTruncated = useCheckTruncatedElement(ref);
|
||||
|
||||
const tooltipId = uniqueId();
|
||||
return (
|
||||
<div ref={ref} className={classNames}>
|
||||
<div className={classNames}>
|
||||
<div className="tooltip-truncated" data-tip data-for={tooltipId}>
|
||||
<span className={tooltipDisabled ? "" : "truncated"}>{value}</span>
|
||||
<div ref={ref} className={isTruncated ? "truncated" : undefined}>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
<ReactTooltip
|
||||
place="top"
|
||||
@@ -56,7 +50,7 @@ const TooltipTruncatedText = ({
|
||||
className="truncated-tooltip" // responsive widths
|
||||
clickable
|
||||
delayHide={200} // need delay set to hover using clickable
|
||||
disable={tooltipDisabled}
|
||||
disable={!isTruncated}
|
||||
>
|
||||
<>
|
||||
{tooltip ?? value}
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
}
|
||||
|
||||
.truncated {
|
||||
display: inline-block;
|
||||
max-width: 99%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -9,17 +9,27 @@ export const useCheckTruncatedElement = <T extends HTMLElement>(
|
||||
) => {
|
||||
const [isTruncated, setIsTruncated] = useState(false);
|
||||
|
||||
const updateIsTruncated = (element: HTMLElement) => {
|
||||
const { scrollWidth, clientWidth } = element;
|
||||
setIsTruncated(scrollWidth > clientWidth);
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
updateIsTruncated(entry.target as HTMLElement);
|
||||
});
|
||||
});
|
||||
const element = ref.current;
|
||||
function updateIsTruncated() {
|
||||
if (element) {
|
||||
const { scrollWidth, clientWidth } = element;
|
||||
setIsTruncated(scrollWidth > clientWidth);
|
||||
}
|
||||
if (element) {
|
||||
updateIsTruncated(element);
|
||||
resizeObserver.observe(ref.current as HTMLElement);
|
||||
}
|
||||
window.addEventListener("resize", updateIsTruncated);
|
||||
updateIsTruncated();
|
||||
return () => window.removeEventListener("resize", updateIsTruncated);
|
||||
return () => {
|
||||
if (element) {
|
||||
resizeObserver.unobserve(element);
|
||||
}
|
||||
};
|
||||
}, [ref]);
|
||||
|
||||
return isTruncated;
|
||||
|
||||
Reference in New Issue
Block a user