From 38c5c58f8a761187f34be88e1d98432dbaa7aedf Mon Sep 17 00:00:00 2001
From: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
Date: Tue, 22 Aug 2023 09:20:40 -0400
Subject: [PATCH] Fleet UI: [feature] All table links can open in a new tab
(#13349)
---
changes/13205-table-links-can-open-in-new-tab | 1 +
.../LiveQuery/TargetsInput/_styles.scss | 6 ---
.../DataTable/LinkCell/LinkCell.tests.tsx | 7 ++--
.../DataTable/LinkCell/LinkCell.tsx | 41 ++++++++++---------
.../TableContainer/DataTable/_styles.scss | 37 ++++++++++++++++-
.../components/FilterPill/_styles.scss | 1 -
.../cards/Software/SoftwareTableConfig.tsx | 11 +++--
.../policies/ManagePoliciesPage/_styles.scss | 28 ++++++++-----
.../PoliciesTable/PoliciesTableConfig.tsx | 2 +-
.../queries/ManageQueriesPage/_styles.scss | 34 ++++++++-------
.../QueriesTable/QueriesTableConfig.tsx | 2 +-
.../SoftwareTableConfig.tsx | 11 +++--
.../software/ManageSoftwarePage/_styles.scss | 16 ++++----
13 files changed, 119 insertions(+), 78 deletions(-)
create mode 100644 changes/13205-table-links-can-open-in-new-tab
diff --git a/changes/13205-table-links-can-open-in-new-tab b/changes/13205-table-links-can-open-in-new-tab
new file mode 100644
index 0000000000..b971b1f7fd
--- /dev/null
+++ b/changes/13205-table-links-can-open-in-new-tab
@@ -0,0 +1 @@
+* All table links are right-clickable
\ No newline at end of file
diff --git a/frontend/components/LiveQuery/TargetsInput/_styles.scss b/frontend/components/LiveQuery/TargetsInput/_styles.scss
index 94eabdd805..e2f14ce6e1 100644
--- a/frontend/components/LiveQuery/TargetsInput/_styles.scss
+++ b/frontend/components/LiveQuery/TargetsInput/_styles.scss
@@ -74,12 +74,6 @@
&__hosts-selected-table {
margin-top: 8px;
- img {
- cursor: pointer;
- transform: scale(0.5);
- position: relative;
- top: 2px;
- }
.data-table__wrapper {
width: 100%;
overflow: auto;
diff --git a/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tests.tsx b/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tests.tsx
index 64e578e41d..de5332e35e 100644
--- a/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tests.tsx
+++ b/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tests.tsx
@@ -7,13 +7,12 @@ import LinkCell from "./LinkCell";
const VALUE = "40 hosts";
describe("Link cell", () => {
- it("renders text and path", async () => {
+ it("renders text", async () => {
const { user } = renderWithSetup(
);
- await user.click(screen.getByText("40 hosts"));
-
- expect(window.location.pathname).toContain("/hosts");
+ expect(screen.getByText("40 hosts")).toBeInTheDocument();
+ // Note: Testing react-router Link would require Router or MemoryRouter wrapper which is app level
});
});
diff --git a/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx b/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx
index 5f23fbcc4f..3f8b8dd690 100644
--- a/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx
+++ b/frontend/components/TableContainer/DataTable/LinkCell/LinkCell.tsx
@@ -1,40 +1,43 @@
+// Utilizes Link over Button so we can right click links
import React from "react";
-// using browserHistory directly because "router"
-// is difficult to pass as a prop
-import { browserHistory } from "react-router";
-
-import Button from "components/buttons/Button/Button";
+import { Link } from "react-router";
+import classnames from "classnames";
interface ILinkCellProps {
value: string | JSX.Element;
path: string;
+ className?: string;
+ customOnClick?: (e: React.MouseEvent) => void;
+ /** allows viewing overflow for tooltip */
+ withTooltip?: boolean;
title?: string;
- classes?: string;
- customOnClick?: () => void;
}
+const baseClass = "link-cell";
+
const LinkCell = ({
value,
path,
- title,
- classes,
+ className,
customOnClick,
+ withTooltip,
+ title,
}: ILinkCellProps): JSX.Element => {
- const onClick = (): void => {
- customOnClick && customOnClick();
- browserHistory.push(path);
+ const cellClasses = classnames(
+ baseClass,
+ className,
+ withTooltip && "link-cell-tooltip"
+ );
+
+ const onClick = (e: React.MouseEvent): void => {
+ customOnClick && customOnClick(e);
};
return (
-
+
);
};
diff --git a/frontend/components/TableContainer/DataTable/_styles.scss b/frontend/components/TableContainer/DataTable/_styles.scss
index ca0cbd39f6..f38c51b54d 100644
--- a/frontend/components/TableContainer/DataTable/_styles.scss
+++ b/frontend/components/TableContainer/DataTable/_styles.scss
@@ -215,7 +215,6 @@ $shadow-transition-width: 10px;
}
.link-cell,
.text-cell {
- display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
@@ -224,6 +223,42 @@ $shadow-transition-width: 10px;
white-space: normal;
}
}
+ .text-cell {
+ display: inline-flex;
+ }
+ .link-cell {
+ display: block;
+ padding: $pad-small 0; // larger clickable area
+
+ &:hover {
+ text-decoration: underline;
+ }
+ > div {
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+ }
+ // css to properly style link-cell with tooltip
+ .link-cell-tooltip {
+ overflow: visible; // fixes tooltip overflow cut off by cell
+ .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 {
+ display: block;
+ white-space: nowrap; // single line
+ text-overflow: ellipsis; // truncates text
+ overflow: hidden;
+
+ .component__tooltip-wrapper__underline {
+ max-width: 100%; // fixes underline overflowing past truncated text
+ }
+ }
+ }
+ }
.w400 {
max-width: calc(400px - 48px);
min-width: 100%;
diff --git a/frontend/pages/hosts/ManageHostsPage/components/FilterPill/_styles.scss b/frontend/pages/hosts/ManageHostsPage/components/FilterPill/_styles.scss
index 45a308e5dd..f82d327308 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/FilterPill/_styles.scss
+++ b/frontend/pages/hosts/ManageHostsPage/components/FilterPill/_styles.scss
@@ -32,7 +32,6 @@
.premium-icon-tip {
.premium-feature-icon {
position: relative;
- top: 2px;
margin-right: 6px;
}
}
diff --git a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
index 0685071930..2b9763d685 100644
--- a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
@@ -7,9 +7,9 @@ import { formatDistanceToNow } from "date-fns";
import { ISoftware } from "interfaces/software";
import PATHS from "router/paths";
-import Button from "components/buttons/Button";
import HeaderCell from "components/TableContainer/DataTable/HeaderCell/HeaderCell";
import TextCell from "components/TableContainer/DataTable/TextCell";
+import LinkCell from "components/TableContainer/DataTable/LinkCell";
import TooltipWrapper from "components/TooltipWrapper";
import ViewAllHostsLink from "components/ViewAllHostsLink";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
@@ -215,9 +215,12 @@ export const generateSoftwareTableHeaders = ({
};
return (
-
+
);
},
sortType: "caseInsensitive",
diff --git a/frontend/pages/policies/ManagePoliciesPage/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/_styles.scss
index 3f7fb04346..345b183d97 100644
--- a/frontend/pages/policies/ManagePoliciesPage/_styles.scss
+++ b/frontend/pages/policies/ManagePoliciesPage/_styles.scss
@@ -157,19 +157,25 @@
position: relative;
}
- .policy-name-cell {
- .children-wrapper {
- display: flex;
- align-items: center;
- gap: 2px;
+ .policies-table {
+ .data-table-block {
+ .data-table {
+ tbody {
+ .name__cell {
+ .policy-name-cell {
+ display: flex; // required for inline icon
- .tooltip-base {
- display: inline-flex;
- }
+ .tooltip-base {
+ display: inline-flex;
+ }
- .policy-name-text {
- text-overflow: ellipsis;
- overflow: hidden;
+ .policy-name-text {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ }
+ }
+ }
}
}
}
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx
index 825f00db6f..2d3abf32bc 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx
@@ -117,7 +117,7 @@ const generateTableHeaders = (
accessor: "name",
Cell: (cellProps: ICellProps): JSX.Element => (
{cellProps.cell.value}
diff --git a/frontend/pages/queries/ManageQueriesPage/_styles.scss b/frontend/pages/queries/ManageQueriesPage/_styles.scss
index 52220fc54f..fb26c7918c 100644
--- a/frontend/pages/queries/ManageQueriesPage/_styles.scss
+++ b/frontend/pages/queries/ManageQueriesPage/_styles.scss
@@ -135,13 +135,25 @@
.name__cell {
max-width: $col-lg;
+ .query-name-cell {
+ display: flex; // required for inline icon
+ .children-wrapper {
+ .query-name-text {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ }
+ }
+ .query-icon {
+ display: block;
+ }
+
.children-wrapper {
display: flex;
gap: $pad-xsmall;
-
- .observer-can-run-tooltip {
- font-weight: $regular;
- }
+ }
+ .observer-can-run-tooltip {
+ font-weight: $regular;
}
}
@@ -177,19 +189,5 @@
}
}
}
-
- .query-name-cell {
- .children-wrapper {
- .query-name-text {
- text-overflow: ellipsis;
- overflow: hidden;
- }
- }
- }
- .query-icon {
- position: relative;
- top: 2px;
- display: block;
- }
}
}
diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
index 744a829bc6..f24c17af24 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
@@ -125,7 +125,7 @@ const generateTableHeaders = ({
Cell: (cellProps: ICellProps): JSX.Element => {
return (
{cellProps.cell.value}
diff --git a/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx b/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
index 7da6142dff..78490e4ecf 100644
--- a/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
+++ b/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
@@ -9,9 +9,9 @@ import PATHS from "router/paths";
import { formatFloatAsPercentage } from "utilities/helpers";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
-import Button from "components/buttons/Button";
import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
import TextCell from "components/TableContainer/DataTable/TextCell";
+import LinkCell from "components/TableContainer/DataTable/LinkCell/LinkCell";
import TooltipWrapper from "components/TooltipWrapper";
import ViewAllHostsLink from "components/ViewAllHostsLink";
import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
@@ -214,9 +214,12 @@ const generateTableHeaders = (
};
return (
-
+
);
},
sortType: "caseInsensitive",
diff --git a/frontend/pages/software/ManageSoftwarePage/_styles.scss b/frontend/pages/software/ManageSoftwarePage/_styles.scss
index 372af5a942..daf3b78ce2 100644
--- a/frontend/pages/software/ManageSoftwarePage/_styles.scss
+++ b/frontend/pages/software/ManageSoftwarePage/_styles.scss
@@ -143,12 +143,10 @@
display: table-cell;
}
}
- @media (min-width: $break-md) {
+ @media (min-width: $break-lg) {
.version__header {
width: $col-md;
}
- }
- @media (min-width: $break-lg) {
.source__header {
display: table-cell;
}
@@ -157,8 +155,7 @@
tbody {
.name__cell {
- width: $col-md;
-
+ max-width: $col-md;
// Tooltip does not get cut off
.children-wrapper {
overflow: initial;
@@ -193,9 +190,9 @@
}
}
}
- @media (min-width: $break-md) {
- .version_cell {
- width: $col-md;
+ @media (min-width: $break-sm) {
+ .name__cell {
+ max-width: $col-lg;
}
}
@media (min-width: $break-md) {
@@ -204,6 +201,9 @@
}
}
@media (min-width: $break-lg) {
+ .version_cell {
+ width: $col-md;
+ }
.source__cell {
display: table-cell;
}