Fleet UI: [feature] All table links can open in a new tab (#13349)

This commit is contained in:
RachelElysia
2023-08-22 09:20:40 -04:00
committed by GitHub
parent 5f9a7df518
commit 38c5c58f8a
13 changed files with 119 additions and 78 deletions
@@ -0,0 +1 @@
* All table links are right-clickable
@@ -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;
@@ -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(
<LinkCell value={VALUE} path={PATHS.MANAGE_HOSTS} />
);
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
});
});
@@ -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 (
<Button
className={`link-cell ${classes}`}
onClick={onClick}
variant="text-link"
title={title}
>
<Link className={cellClasses} to={path} onClick={onClick} title={title}>
{value}
</Button>
</Link>
);
};
@@ -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%;
@@ -32,7 +32,6 @@
.premium-icon-tip {
.premium-feature-icon {
position: relative;
top: 2px;
margin-right: 6px;
}
}
@@ -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 (
<Button onClick={onClickSoftware} variant="text-link">
{bundle ? renderBundleTooltip(name, bundle) : name}
</Button>
<LinkCell
path={PATHS.SOFTWARE_DETAILS(id.toString())}
customOnClick={onClickSoftware}
value={bundle ? renderBundleTooltip(name, bundle) : name}
withTooltip={!!bundle}
/>
);
},
sortType: "caseInsensitive",
@@ -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;
}
}
}
}
}
}
}
@@ -117,7 +117,7 @@ const generateTableHeaders = (
accessor: "name",
Cell: (cellProps: ICellProps): JSX.Element => (
<LinkCell
classes="w250 policy-name-cell"
className="w250 policy-name-cell"
value={
<>
<div className="policy-name-text">{cellProps.cell.value}</div>
@@ -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;
}
}
}
@@ -125,7 +125,7 @@ const generateTableHeaders = ({
Cell: (cellProps: ICellProps): JSX.Element => {
return (
<LinkCell
classes="w400 query-name-cell"
className="w400 query-name-cell"
value={
<>
<div className="query-name-text">{cellProps.cell.value}</div>
@@ -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 (
<Button onClick={onClickSoftware} variant="text-link">
{bundle ? renderBundleTooltip(name, bundle) : name}
</Button>
<LinkCell
path={PATHS.SOFTWARE_DETAILS(id.toString())}
customOnClick={onClickSoftware}
value={bundle ? renderBundleTooltip(name, bundle) : name}
withTooltip={!!bundle}
/>
);
},
sortType: "caseInsensitive",
@@ -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;
}