Fleet UI: [feature] All table links can open in a new tab (#13349)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user