From 46b1db7850a99b7a211d36e9b95c41812ff0ced3 Mon Sep 17 00:00:00 2001
From: Jacob Shandling <61553566+jacobshandling@users.noreply.github.com>
Date: Wed, 13 Sep 2023 10:19:56 -0700
Subject: [PATCH] UI: Update tooltip/link structure & functionality in Software
tables (#13893)
---
.../12948-fix-software-bundle-clickability | 3 ++
.../DataTable/LinkCell/LinkCell.tsx | 30 ++++++++++----
.../TableContainer/DataTable/_styles.scss | 25 +++++++----
.../TooltipWrapper/TooltipWrapper.tsx | 10 ++++-
.../components/TooltipWrapper/_styles.scss | 41 +++++++++++--------
.../cards/Software/SoftwareTableConfig.tsx | 7 +++-
.../SoftwareTableConfig.tsx | 28 ++++---------
frontend/utilities/helpers.ts | 10 +++++
8 files changed, 96 insertions(+), 58 deletions(-)
create mode 100644 changes/12948-fix-software-bundle-clickability
diff --git a/changes/12948-fix-software-bundle-clickability b/changes/12948-fix-software-bundle-clickability
new file mode 100644
index 0000000000..1fbf8f8acd
--- /dev/null
+++ b/changes/12948-fix-software-bundle-clickability
@@ -0,0 +1,3 @@
+- Restored the ability to click on and select/copy text from software bundle tooltips while
+ maintaining the abilities to click the software's name to get more details and to click anywhere
+ else in the row to view all hosts with that software installed.
diff --git a/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx b/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx
index 3f8b8dd690..f57d6e7055 100644
--- a/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx
+++ b/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx
@@ -3,6 +3,7 @@ import React from "react";
import { Link } from "react-router";
import classnames from "classnames";
+import TooltipWrapper from "components/TooltipWrapper";
interface ILinkCellProps {
value: string | JSX.Element;
@@ -10,7 +11,7 @@ interface ILinkCellProps {
className?: string;
customOnClick?: (e: React.MouseEvent) => void;
/** allows viewing overflow for tooltip */
- withTooltip?: boolean;
+ tooltipContent?: string;
title?: string;
}
@@ -21,21 +22,32 @@ const LinkCell = ({
path,
className,
customOnClick,
- withTooltip,
title,
+ tooltipContent,
}: ILinkCellProps): JSX.Element => {
- const cellClasses = classnames(
- baseClass,
- className,
- withTooltip && "link-cell-tooltip"
- );
+ const cellClasses = classnames(baseClass, className);
const onClick = (e: React.MouseEvent): void => {
customOnClick && customOnClick(e);
};
- return (
-
+ return tooltipContent ? (
+
+
+ {value}
+
+
+ ) : (
+
{value}
);
diff --git a/frontend/components/TableContainer/DataTable/_styles.scss b/frontend/components/TableContainer/DataTable/_styles.scss
index 26b18e6f01..ea958e3924 100644
--- a/frontend/components/TableContainer/DataTable/_styles.scss
+++ b/frontend/components/TableContainer/DataTable/_styles.scss
@@ -241,21 +241,32 @@ $shadow-transition-width: 10px;
}
}
// css to properly style link-cell with tooltip
- .link-cell-tooltip {
+ .link-cell-tooltip-wrapper {
overflow: visible; // fixes tooltip overflow cut off by cell
+ white-space: nowrap; // single line
+ margin: 0; // padding applied to .link-cell for larger clickable area
.component__tooltip-wrapper {
- display: block;
- white-space: nowrap; // single line
- margin: 0; // padding applied to .link-cell for larger clickable area
- .component__tooltip-wrapper__element {
+ &__element {
display: block;
white-space: nowrap; // single line
text-overflow: ellipsis; // truncates text
overflow: hidden;
+ &__underline {
+ width: 100%;
- .component__tooltip-wrapper__underline {
- max-width: 100%; // fixes underline overflowing past truncated text
+ &::after {
+ bottom: 9px; // compensate for padding to make larger clickable area
+ }
}
+ // TODO – this naming is now confusing, as this .link-cell is not the outermost layer of
+ // the cell – it's a NameCell
+ .link-cell {
+ padding: 10px 0;
+ }
+ }
+
+ &__tip-text {
+ cursor: auto;
}
}
}
diff --git a/frontend/components/TooltipWrapper/TooltipWrapper.tsx b/frontend/components/TooltipWrapper/TooltipWrapper.tsx
index b49e88a471..0b48e54b91 100644
--- a/frontend/components/TooltipWrapper/TooltipWrapper.tsx
+++ b/frontend/components/TooltipWrapper/TooltipWrapper.tsx
@@ -4,7 +4,7 @@ import React from "react";
import * as DOMPurify from "dompurify";
interface ITooltipWrapperProps {
- children: string;
+ children: string | JSX.Element;
tipContent: string;
position?: "top" | "bottom";
isDelayed?: boolean;
@@ -33,11 +33,17 @@ const TooltipWrapper = ({
{
+ e.stopPropagation();
+ }}
/>
);
diff --git a/frontend/components/TooltipWrapper/_styles.scss b/frontend/components/TooltipWrapper/_styles.scss
index 63714cdd14..b06a53cbaa 100644
--- a/frontend/components/TooltipWrapper/_styles.scss
+++ b/frontend/components/TooltipWrapper/_styles.scss
@@ -18,26 +18,31 @@
position: static;
display: inline; // treat like a span but allow other tags as children
white-space: nowrap;
- }
- &__underline {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
-
- &::before {
- content: attr(data-text);
- opacity: 0;
- visibility: hidden;
- }
- &::after {
- content: "";
- width: 100%;
- height: 100%;
+ &__underline {
position: absolute;
- bottom: -2px;
+ top: 0;
left: 0;
- border-bottom: 1px dashed $ui-fleet-black-50;
+ bottom: 0;
+
+ &::before {
+ content: attr(data-text);
+ opacity: 0;
+ visibility: hidden;
+ }
+ &::after {
+ content: "";
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ bottom: -2px;
+ left: 0;
+ border-bottom: 1px dashed $ui-fleet-black-50;
+ }
+ }
+
+ a {
+ position: relative;
+ z-index: 99;
}
}
&__tip-text {
diff --git a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
index 2b9763d685..1dec977d81 100644
--- a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
@@ -14,6 +14,7 @@ import TooltipWrapper from "components/TooltipWrapper";
import ViewAllHostsLink from "components/ViewAllHostsLink";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import { COLORS } from "styles/var/colors";
+import { getSoftwareBundleTooltipMarkup } from "utilities/helpers";
interface IHeaderProps {
column: {
@@ -218,8 +219,10 @@ export const generateSoftwareTableHeaders = ({
);
},
diff --git a/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx b/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
index 78490e4ecf..58f7a8a3d4 100644
--- a/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
+++ b/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
@@ -6,7 +6,10 @@ import ReactTooltip from "react-tooltip";
import { formatSoftwareType, ISoftware } from "interfaces/software";
import { IVulnerability } from "interfaces/vulnerability";
import PATHS from "router/paths";
-import { formatFloatAsPercentage } from "utilities/helpers";
+import {
+ formatFloatAsPercentage,
+ getSoftwareBundleTooltipMarkup,
+} from "utilities/helpers";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
@@ -65,23 +68,6 @@ const condenseVulnerabilities = (
: condensed;
};
-const renderBundleTooltip = (name: string, bundle: string) => (
-
-
- Bundle identifier:
-
- ${bundle}
-
- `}
- >
- {name}
-
-
-);
-
const getMaxProbability = (vulns: IVulnerability[]) =>
vulns.reduce(
(max, { epss_probability }) => Math.max(max, epss_probability || 0),
@@ -217,8 +203,10 @@ const generateTableHeaders = (
);
},
diff --git a/frontend/utilities/helpers.ts b/frontend/utilities/helpers.ts
index 77d9b6dba7..5f2cb1987b 100644
--- a/frontend/utilities/helpers.ts
+++ b/frontend/utilities/helpers.ts
@@ -900,6 +900,16 @@ export const getNextLocationPath = ({
return queryString ? `/${nextLocation}?${queryString}` : `/${nextLocation}`;
};
+export const getSoftwareBundleTooltipMarkup = (bundle: string) => {
+ return `
+
+ Bundle identifier:
+
+ ${bundle}
+
+ `;
+};
+
export default {
addGravatarUrlToResource,
formatConfigDataForServer,