Fleet UI: Show tooltip for truncated vulnerabilities list in Update details modal (#49236)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Fixed an issue where the truncated vulnerabilities list in the Update details modal did not show a tooltip listing the remaining CVEs.
|
||||
@@ -119,10 +119,6 @@ const renderVisibleRow = ({
|
||||
|
||||
const isTruncatedFirst = visibleCount === 0;
|
||||
const content = isTruncatedFirst ? truncatedFirstContent : standardContent;
|
||||
const rowStyle: React.CSSProperties = {
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
};
|
||||
const rowClass = classnames(`${baseClass}__visible`, {
|
||||
[`${baseClass}__visible--truncated`]: isTruncatedFirst,
|
||||
});
|
||||
@@ -130,16 +126,12 @@ const renderVisibleRow = ({
|
||||
if (onClick) {
|
||||
return (
|
||||
<Button variant="link" className={rowClass} onClick={onClick}>
|
||||
<span style={rowStyle}>{content}</span>
|
||||
<span>{content}</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={rowClass} style={rowStyle}>
|
||||
{content}
|
||||
</span>
|
||||
);
|
||||
return <span className={rowClass}>{content}</span>;
|
||||
};
|
||||
|
||||
const TruncatedTextList = ({
|
||||
@@ -207,24 +199,9 @@ const TruncatedTextList = ({
|
||||
const hidden = items.slice(visibleCount);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={classnames(baseClass, className)}
|
||||
style={{ position: "relative", minWidth: 0 }}
|
||||
>
|
||||
<div ref={containerRef} className={classnames(baseClass, className)}>
|
||||
{/* Hidden measurement layer — same font/size as the visible row */}
|
||||
<div
|
||||
className={`${baseClass}__measure`}
|
||||
aria-hidden
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
visibility: "hidden",
|
||||
pointerEvents: "none",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<div className={`${baseClass}__measure`} aria-hidden>
|
||||
{items.map((item, i) => (
|
||||
<span
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
|
||||
@@ -14,20 +14,7 @@ import {
|
||||
import Card from "components/Card";
|
||||
import DataSet from "components/DataSet";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
|
||||
const generateVulnerabilitiesValue = (vulnerabilities: string[]) => {
|
||||
const first3 = vulnerabilities.slice(0, 3);
|
||||
const rest = vulnerabilities.slice(3);
|
||||
|
||||
const first3Text = first3.join(", ");
|
||||
const restText = `, +${rest.length} more`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>{`${first3Text}${rest.length > 0 ? restText : ""}`}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
import TruncatedTextList from "components/TruncatedTextList";
|
||||
|
||||
const baseClass = "inventory-versions";
|
||||
|
||||
@@ -97,15 +84,14 @@ const InventoryVersion = ({
|
||||
textOnly
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{vulnerabilities && vulnerabilities.length !== 0 && (
|
||||
<div className={`${baseClass}__row`}>
|
||||
{vulnerabilities && vulnerabilities.length !== 0 && (
|
||||
<DataSet
|
||||
className={`${baseClass}__vulnerabilities`}
|
||||
title="Vulnerabilities"
|
||||
value={generateVulnerabilitiesValue(vulnerabilities)}
|
||||
value={<TruncatedTextList items={vulnerabilities} />}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
{!!installedPaths?.length &&
|
||||
installedPaths.map((path) => {
|
||||
// Find the signature info for this path
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
flex-direction: column;
|
||||
gap: $pad-small;
|
||||
|
||||
.data-set dd {
|
||||
white-space: initial;
|
||||
dt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__versions {
|
||||
@@ -20,9 +20,22 @@
|
||||
}
|
||||
&__row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $pad-xxlarge;
|
||||
}
|
||||
|
||||
// Fill the row so TruncatedTextList has a bounded width to measure against.
|
||||
&__vulnerabilities {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
// dd is display:flex, so TruncatedTextList's outer div would size to its
|
||||
// content by default and truncate too early. Stretch it to fill.
|
||||
.truncated-text-list {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__sig-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
+3
-2
@@ -39,8 +39,9 @@ describe("SoftwareDetailsModal", () => {
|
||||
expect(screen.getByText("Hash:")).toBeVisible();
|
||||
expect(screen.getByText("mockhashhere")).toBeVisible();
|
||||
|
||||
// Vulnerabilities
|
||||
expect(screen.getByText(/CVE-2020-0001/)).toBeVisible();
|
||||
// Vulnerabilities — TruncatedTextList renders items in both a hidden
|
||||
// measure layer and the visible row, so match all occurrences.
|
||||
expect(screen.getAllByText(/CVE-2020-0001/).length).toBeGreaterThan(0);
|
||||
|
||||
// Close button
|
||||
expect(screen.getByRole("button", { name: "Close" })).toBeVisible();
|
||||
|
||||
Reference in New Issue
Block a user