-
+
+
- {
- e.stopPropagation();
- }}
- />
-
+
+ {tipContent}
+
+
);
};
diff --git a/frontend/components/TooltipWrapper/_styles.scss b/frontend/components/TooltipWrapper/_styles.scss
index b06a53cbaa..3f6ca91977 100644
--- a/frontend/components/TooltipWrapper/_styles.scss
+++ b/frontend/components/TooltipWrapper/_styles.scss
@@ -1,92 +1,30 @@
.component__tooltip-wrapper {
display: inline-flex;
- position: relative;
- &:hover {
- .component__tooltip-wrapper__tip-text {
- visibility: visible;
- opacity: 1;
- }
-
- .delayed-tip {
- transition: 300ms all;
- transition-delay: 300ms;
- }
+ &__underline {
+ position: relative;
+ width: fit-content;
+ // compensate for bottom border and padding to maintain centering
+ top: 2px;
+ border-bottom: 1px dashed $ui-fleet-black-50;
+ padding-bottom: 1px;
}
- &__element {
- 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%;
- position: absolute;
- bottom: -2px;
- left: 0;
- border-bottom: 1px dashed $ui-fleet-black-50;
- }
- }
-
- a {
- position: relative;
- z-index: 99;
- }
- }
&__tip-text {
width: max-content;
max-width: 360px;
padding: 6px;
color: $core-white;
- background-color: $core-fleet-blue;
+ background-color: $tooltip-bg;
font-weight: $regular;
font-size: $xx-small;
border-radius: 4px;
- position: absolute;
- top: calc(100% + 6px);
- left: 0;
box-sizing: border-box;
z-index: 99; // not more than the site nav
- visibility: hidden;
- opacity: 0;
- transition: opacity 0.3s ease;
line-height: 1.375;
white-space: initial;
-
- // invisible block to cover space so
- // hover state can continue from text to bubble
- &::before {
- content: "";
- width: 100%;
- height: 6px;
- position: absolute;
- top: -6px;
- left: 0;
- }
p {
margin: 0;
}
}
- &[data-position="top"] {
- .component__tooltip-wrapper__tip-text {
- top: auto;
- bottom: 100%;
-
- &::before {
- display: none;
- }
- }
- }
}
diff --git a/frontend/components/buttons/RevealButton/RevealButton.tests.tsx b/frontend/components/buttons/RevealButton/RevealButton.tests.tsx
index ff9c566a0a..c18dd5c49a 100644
--- a/frontend/components/buttons/RevealButton/RevealButton.tests.tsx
+++ b/frontend/components/buttons/RevealButton/RevealButton.tests.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { render, screen } from "@testing-library/react";
+import { render, screen, fireEvent } from "@testing-library/react";
import { renderWithSetup } from "test/test-utils";
import RevealButton from "./RevealButton";
@@ -85,7 +85,7 @@ describe("Reveal button", () => {
/>
);
- await user.hover(screen.getByText(SHOW_TEXT));
+ await fireEvent.mouseEnter(screen.getByText(SHOW_TEXT));
expect(screen.getByText(TOOLTIP_HTML)).toBeInTheDocument();
});
diff --git a/frontend/components/forms/FormField/FormField.tsx b/frontend/components/forms/FormField/FormField.tsx
index 392ac516de..2f1a7fee53 100644
--- a/frontend/components/forms/FormField/FormField.tsx
+++ b/frontend/components/forms/FormField/FormField.tsx
@@ -14,7 +14,7 @@ export interface IFormFieldProps {
label: Array
| JSX.Element | string;
name: string;
type: string;
- tooltip?: string;
+ tooltip?: React.ReactNode;
}
const FormField = ({
diff --git a/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx b/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx
index c4886eaced..e07fe35124 100644
--- a/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx
+++ b/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx
@@ -57,11 +57,14 @@ class UserSettingsForm extends Component {
hint={renderEmailHint()}
disabled={!smtpConfigured}
tooltip={
- "\
- Editing your email address requires that SMTP or SES is configured in order to send a validation email.\
-
\
- Users with Admin role can configure SMTP in Settings > Organization settings.\
- "
+ <>
+ Editing your email address requires that SMTP or SES is
+ configured in order to send a validation email.
+
+
+ Users with Admin role can configure SMTP in{" "}
+ Settings > Organization settings.
+ >
}
/>
diff --git a/frontend/components/forms/fields/Checkbox/Checkbox.tsx b/frontend/components/forms/fields/Checkbox/Checkbox.tsx
index c8daf7bfea..b439235c00 100644
--- a/frontend/components/forms/fields/Checkbox/Checkbox.tsx
+++ b/frontend/components/forms/fields/Checkbox/Checkbox.tsx
@@ -19,7 +19,7 @@ export interface ICheckboxProps {
wrapperClassName?: string;
indeterminate?: boolean;
parseTarget?: boolean;
- tooltip?: string;
+ tooltipContent?: React.ReactNode;
isLeftLabel?: boolean;
}
@@ -35,7 +35,7 @@ const Checkbox = (props: ICheckboxProps) => {
wrapperClassName,
indeterminate,
parseTarget,
- tooltip,
+ tooltipContent,
isLeftLabel,
} = props;
@@ -78,9 +78,9 @@ const Checkbox = (props: ICheckboxProps) => {
type="checkbox"
/>
- {tooltip ? (
+ {tooltipContent ? (
-
+
{children as string}
diff --git a/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx b/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx
index 9e7db4d0ec..c051873e5e 100644
--- a/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx
+++ b/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx
@@ -45,7 +45,7 @@ class InputFieldWithIcon extends InputField {
data-has-tooltip={!!tooltip}
>
{tooltip && !error ? (
-
+
{label}
) : (
diff --git a/frontend/components/forms/fields/Radio/Radio.tests.tsx b/frontend/components/forms/fields/Radio/Radio.tests.tsx
index 23b36de311..0846d63524 100644
--- a/frontend/components/forms/fields/Radio/Radio.tests.tsx
+++ b/frontend/components/forms/fields/Radio/Radio.tests.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { noop } from "lodash";
-import { render, screen } from "@testing-library/react";
+import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Radio from "./Radio";
@@ -76,7 +76,7 @@ describe("Radio - component", () => {
expect(radioComponent).toHaveClass("disabled");
});
- it("render a tooltip from the tooltip prop", () => {
+ it("render a tooltip from the tooltip prop", async () => {
render(
{
/>
);
+ await fireEvent.mouseEnter(screen.getByText("Radio Label"));
const tooltip = screen.getByText("A Test Radio Tooltip");
expect(tooltip).toBeInTheDocument();
});
diff --git a/frontend/components/forms/fields/Radio/Radio.tsx b/frontend/components/forms/fields/Radio/Radio.tsx
index a595e4f4bc..cf38349bac 100644
--- a/frontend/components/forms/fields/Radio/Radio.tsx
+++ b/frontend/components/forms/fields/Radio/Radio.tsx
@@ -14,7 +14,7 @@ export interface IRadioProps {
name?: string;
className?: string;
disabled?: boolean;
- tooltip?: string;
+ tooltip?: React.ReactNode;
testId?: string;
}
diff --git a/frontend/components/queries/PackQueriesTable/PackQueriesTable/PackQueriesTableConfig.tsx b/frontend/components/queries/PackQueriesTable/PackQueriesTable/PackQueriesTableConfig.tsx
index 6a8772b309..84f4d3d5b8 100644
--- a/frontend/components/queries/PackQueriesTable/PackQueriesTable/PackQueriesTableConfig.tsx
+++ b/frontend/components/queries/PackQueriesTable/PackQueriesTable/PackQueriesTableConfig.tsx
@@ -152,7 +152,17 @@ const generateTableHeaders = (
Header: () => {
return (
-
+
+ This is the average performance
+
+ impact across all hosts where
+
+ this query was scheduled.
+ >
+ }
+ >
Performance impact
diff --git a/frontend/components/queries/queryResults/QueryResultsHeading/QueryResultsHeading.tsx b/frontend/components/queries/queryResults/QueryResultsHeading/QueryResultsHeading.tsx
index 68bb0207f8..74a8229bc8 100644
--- a/frontend/components/queries/queryResults/QueryResultsHeading/QueryResultsHeading.tsx
+++ b/frontend/components/queries/queryResults/QueryResultsHeading/QueryResultsHeading.tsx
@@ -101,8 +101,13 @@ const QuertResultsHeading = ({
({`${percentResponded}% `}
return results, errors, or
no results`}
+ tipContent={
+ <>
+ Hosts that respond may
+
return results, errors, or
+ no results
+ >
+ }
>
responded
@@ -120,7 +125,12 @@ const QuertResultsHeading = ({
{!isQueryFinished && (
impact live query response times.`}
+ tipContent={
+ <>
+ The hosts’ distributed interval can
+ impact live query response times.
+ >
+ }
>
Taking longer than 15 seconds?
diff --git a/frontend/components/side_panels/QuerySidePanel/QuerySidePanel.tests.tsx b/frontend/components/side_panels/QuerySidePanel/QuerySidePanel.tests.tsx
index 6c1c7b727a..25aa78ba5a 100644
--- a/frontend/components/side_panels/QuerySidePanel/QuerySidePanel.tests.tsx
+++ b/frontend/components/side_panels/QuerySidePanel/QuerySidePanel.tests.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { noop } from "lodash";
-import { render, screen } from "@testing-library/react";
+import { render, screen, fireEvent } from "@testing-library/react";
import createMockOsqueryTable from "__mocks__/osqueryTableMock";
import QuerySidePanel from "./QuerySidePanel";
@@ -10,7 +10,7 @@ describe("QuerySidePanel - component", () => {
render(
noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
@@ -23,7 +23,7 @@ describe("QuerySidePanel - component", () => {
const { container } = render(
noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
@@ -42,7 +42,7 @@ describe("QuerySidePanel - component", () => {
const { container } = render(
noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
@@ -51,14 +51,15 @@ describe("QuerySidePanel - component", () => {
expect(platformList.length).toBe(11); // 2 columns are set to hidden
});
- it("renders the platform specific column tooltip", () => {
+ it("renders the platform specific column tooltip", async () => {
render(
noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
+ await fireEvent.mouseEnter(screen.getByText("email"));
const tooltip = screen.getByText(/only available on chrome/i);
expect(tooltip).toBeInTheDocument();
@@ -68,7 +69,7 @@ describe("QuerySidePanel - component", () => {
render(
noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
@@ -87,7 +88,7 @@ describe("QuerySidePanel - component", () => {
selectedOsqueryTable={createMockOsqueryTable({
notes: "This table is being used for testing.",
})}
- onOsqueryTableSelect={(tableName: string) => noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
@@ -102,7 +103,7 @@ describe("QuerySidePanel - component", () => {
render(
noop}
+ onOsqueryTableSelect={() => noop}
onClose={noop}
/>
);
diff --git a/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx b/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx
index 45fc893401..7dd1ec2e06 100644
--- a/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx
+++ b/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx
@@ -2,9 +2,9 @@ import React from "react";
import classnames from "classnames";
import { ColumnType, IQueryTableColumn } from "interfaces/osquery_table";
-import { PLATFORM_DISPLAY_NAMES } from "utilities/constants";
import TooltipWrapper from "components/TooltipWrapper";
import { buildQueryStringFromParams } from "utilities/url";
+import { OsqueryPlatform } from "interfaces/platform";
interface IColumnListItemProps {
column: IQueryTableColumn;
@@ -24,22 +24,11 @@ const FOOTNOTES = {
* current tooltip only supports strings. we can change this when it support ReactNodes
* in the future.
*/
-const createTooltipHtml = (
+const renderTooltip = (
column: IQueryTableColumn,
selectedTableName: string
) => {
- const toolTipHtml = [];
-
- const descriptionHtml = `${column.description}`;
- toolTipHtml.push(descriptionHtml);
-
- if (column.required) {
- toolTipHtml.push(
- ``
- );
- }
-
- if (column.requires_user_context) {
+ const renderUserContextFootnote = () => {
const queryString = buildQueryStringFromParams({
utm_source: "fleet-ui",
utm_table: `table-${selectedTableName}`,
@@ -51,37 +40,47 @@ const createTooltipHtml = (
`${baseClass}__footnote-link`
);
- toolTipHtml.push(
- `${FOOTNOTES.requires_user_context}`
+ return (
+
+ ${FOOTNOTES.requires_user_context}
+
);
- }
+ };
- if (column.platforms?.length === 1) {
- const platform = column.platforms[0];
- toolTipHtml.push(
- ``
+ const renderPlatformFootnotes = (columnPlatforms: OsqueryPlatform[]) => {
+ let platformsCopy;
+ switch (columnPlatforms.length) {
+ case 1:
+ platformsCopy = columnPlatforms[0];
+ break;
+ case 2:
+ platformsCopy = `${columnPlatforms[0]} and ${columnPlatforms[1]}`;
+ break;
+ case 3:
+ platformsCopy = `${columnPlatforms[0]}, ${columnPlatforms[1]}, and ${columnPlatforms[2]}`;
+ break;
+ default:
+ platformsCopy = columnPlatforms.join(", ");
+ }
+ return (
+
+ {FOOTNOTES.platform} {platformsCopy}
+
);
- }
+ };
- if (column.platforms?.length === 2) {
- const platform1 = PLATFORM_DISPLAY_NAMES[column.platforms[0]];
- const platform2 = PLATFORM_DISPLAY_NAMES[column.platforms[1]];
- toolTipHtml.push(
- ``
- );
- }
-
- if (column.platforms?.length === 3) {
- const platform1 = PLATFORM_DISPLAY_NAMES[column.platforms[0]];
- const platform2 = PLATFORM_DISPLAY_NAMES[column.platforms[1]];
- const platform3 = PLATFORM_DISPLAY_NAMES[column.platforms[2]];
- toolTipHtml.push(
- ``
- );
- }
-
- const tooltip = toolTipHtml.join("");
- return tooltip;
+ return (
+ <>
+
+ {column.description}
+
+ {column.required && (
+ {FOOTNOTES.required}
+ )}
+ {column.requires_user_context && renderUserContextFootnote()}
+ {column.platforms && renderPlatformFootnotes(column.platforms)}
+ >
+ );
};
const hasFootnotes = (column: IQueryTableColumn) => {
@@ -113,7 +112,7 @@ const ColumnListItem = ({
{column.name}
diff --git a/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/_styles.scss b/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/_styles.scss
index 78495fbedb..71b8414307 100644
--- a/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/_styles.scss
+++ b/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/_styles.scss
@@ -52,7 +52,7 @@
margin: $pad-small 0;
display: block;
- &:last-child {
+ &:last-of-type {
margin-bottom: 0;
}
}
diff --git a/frontend/interfaces/datatable_config.ts b/frontend/interfaces/datatable_config.ts
index 8d8b781caa..79207ff83f 100644
--- a/frontend/interfaces/datatable_config.ts
+++ b/frontend/interfaces/datatable_config.ts
@@ -4,7 +4,6 @@ export type IDataColumn = Column & {
title?: string;
disableHidden?: boolean;
disableSortBy?: boolean;
- isLastColumn?: boolean;
filterValue?: any;
preFilteredRows?: any;
setFilter?: any;
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx
index 0b1fcdebe9..e5b9404476 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { render, screen } from "@testing-library/react";
+import { fireEvent, render, screen } from "@testing-library/react";
import { renderWithSetup } from "test/test-utils";
import paths from "router/paths";
import SummaryTile from "./SummaryTile";
@@ -102,7 +102,7 @@ describe("SummaryTile - component", () => {
/>
);
- await user.hover(screen.getByText("Windows hosts"));
+ await fireEvent.mouseEnter(screen.getByText("Windows hosts"));
expect(screen.getByText("Hosts on any Windows device")).toBeInTheDocument();
});
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx
index a6984654ea..55e5954ab3 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx
@@ -2,12 +2,12 @@ import React from "react";
import { Link } from "react-router";
import { kebabCase } from "lodash";
-import TooltipWrapper from "components/TooltipWrapper";
import Icon from "components/Icon";
import { IconNames } from "components/icons";
import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
import classnames from "classnames";
import { Colors } from "styles/var/colors";
+import TooltipWrapper from "components/TooltipWrapper";
interface ISummaryTileProps {
count: number;
@@ -52,7 +52,6 @@ const SummaryTile = ({
const classes = classnames(`${baseClass}__tile`, `${kebabCase(title)}-tile`, {
[`${baseClass}__not-supported`]: notSupported,
});
-
const tile = (
<>
diff --git a/frontend/pages/DashboardPage/cards/MDM/MDMStatusTableConfig.tsx b/frontend/pages/DashboardPage/cards/MDM/MDMStatusTableConfig.tsx
index c0c8d2bd95..1b39017259 100644
--- a/frontend/pages/DashboardPage/cards/MDM/MDMStatusTableConfig.tsx
+++ b/frontend/pages/DashboardPage/cards/MDM/MDMStatusTableConfig.tsx
@@ -54,7 +54,7 @@ export const generateStatusTableHeaders = (teamId?: number): IDataColumn[] => [
accessor: "status",
Cell: (cellProps: IStringCellProps) => (
{cellProps.cell.value}
diff --git a/frontend/pages/DashboardPage/cards/Munki/MunkiIssuesTableConfig.tsx b/frontend/pages/DashboardPage/cards/Munki/MunkiIssuesTableConfig.tsx
index c10dff38a3..8270dba22e 100644
--- a/frontend/pages/DashboardPage/cards/Munki/MunkiIssuesTableConfig.tsx
+++ b/frontend/pages/DashboardPage/cards/Munki/MunkiIssuesTableConfig.tsx
@@ -41,9 +41,9 @@ const generateMunkiIssuesTableHeaders = (teamId?: number): IDataColumn[] => [
Header: (): JSX.Element => {
const titleWithToolTip = (
Issues reported the last time Munki ran on each host.>
+ }
>
Issue
diff --git a/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/AppleBusinessManagerSection/AppleBusinessManagerSection.tsx b/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/AppleBusinessManagerSection/AppleBusinessManagerSection.tsx
index 205293d380..156c6fd5d9 100644
--- a/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/AppleBusinessManagerSection/AppleBusinessManagerSection.tsx
+++ b/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/AppleBusinessManagerSection/AppleBusinessManagerSection.tsx
@@ -201,7 +201,7 @@ const AppleBusinessManagerSection = ({
Team
diff --git a/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx b/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx
index 43b7658eab..aeb72fe200 100644
--- a/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx
+++ b/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx
@@ -226,12 +226,13 @@ const IntegrationForm = ({
parseTarget
value={projectKey}
tooltip={
- "\
- To find the Jira project key, head to your project in
\
- Jira. Your project key is located in the URL. For example, in
\
- “jira.example.com/projects/JRAEXAMPLE,”
\
- “JRAEXAMPLE” is your project key. \
- "
+ <>
+ To find the Jira project key, head to your project in
+ Jira. Your project key is located in the URL. For example, in{" "}
+
+ “jira.example.com/projects/JRAEXAMPLE,”
+ “JRAEXAMPLE” is your project key.
+ >
}
/>
) : (
@@ -244,11 +245,15 @@ const IntegrationForm = ({
parseTarget
value={groupId === 0 ? null : groupId}
tooltip={
- "\
- To find the Zendesk group ID, select Admin >
\
- People > Groups. Find the group and select it.
\
- The group ID will appear in the search field. \
- "
+ <>
+ To find the Zendesk group ID, select{" "}
+
+ Admin >
+ People > Groups
+
+ . Find the group and select it.
+ The group ID will appear in the search field.
+ >
}
/>
)}
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
index 62f6d4d4b5..bc0852f9e9 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
@@ -115,7 +115,13 @@ const Advanced = ({
value={domain}
parseTarget
tooltip={
- 'If you need to specify a HELO domain,
you can do it here (Default: Blank)
'
+
+ If you need to specify a HELO domain,
+ you can do it here{" "}
+
+ (Default: Blank)
+
+
}
/>
Turn this off (not recommended)
if you use a self-signed certificate
(Default: On)'
+ tooltipContent={
+
+ Turn this off (not recommended)
+ if you use a self-signed certificate{" "}
+
+
+ (Default: On)
+
+
}
>
Verify SSL certs
@@ -134,8 +147,15 @@ const Advanced = ({
name="enableStartTLS"
value={enableStartTLS}
parseTarget
- tooltip={
- 'Detects if STARTTLS is enabled
in your SMTP server and starts
to use it. (Default: On)
'
+ tooltipContent={
+
+ Detects if STARTTLS is enabled
+ in your SMTP server and starts
+ to use it.{" "}
+
+ (Default: On)
+
+
}
>
Enable STARTTLS
@@ -145,8 +165,15 @@ const Advanced = ({
name="enableHostExpiry"
value={enableHostExpiry}
parseTarget
- tooltip={
- 'When enabled, allows automatic cleanup
of hosts that have not communicated with Fleet
in some number of days. (Default: Off)
'
+ tooltipContent={
+
+ When enabled, allows automatic cleanup
+ of hosts that have not communicated with Fleet
+ in some number of days.{" "}
+
+ (Default: Off)
+
+
}
>
Host expiry
@@ -161,8 +188,11 @@ const Advanced = ({
parseTarget
onBlur={validateForm}
error={formErrors.host_expiry_window}
- tooltip={
- "If a host has not communicated with Fleet in the specified number of days, it will be removed.
"
+ tooltipContent={
+
+ If a host has not communicated with Fleet in the specified
+ number of days, it will be removed.
+
}
/>
When enabled, disables the ability to run live queries
(ad hoc queries executed via the UI or fleetctl). (Default: Off)'
+ tooltipContent={
+
+ When enabled, disables the ability to run live queries{" "}
+
+ (ad hoc queries executed via the UI or fleetctl).{" "}
+
+ (Default: Off)
+
+
}
>
Disable live queries
@@ -181,15 +218,23 @@ const Advanced = ({
name="disableQueryReports"
value={disableQueryReports}
parseTarget
- // TODO - update to JSX once tooltip wrapper refactor is merged
// TODO - once refactor is merged, have this and bove tooltips disappear more
// quickly to get out of users' way
- tooltip={
- 'Disabling query reports will decrease database usage,
\
- but will prevent you from accessing query results in
\
- Fleet and will delete existing reports. This can also be
\
- disabled on a per-query basis by enabling "Discard
\
- data". (Default: Off)
'
+ tooltipContent={
+ <>
+
+ Disabling query reports will decrease database usage,{" "}
+
\ but will prevent you from accessing query results
+ in
+
\ Fleet and will delete existing reports. This can
+ also be
+
\ disabled on a per-query basis by enabling
+ "Discard
\ data".{" "}
+
+ (Default: Off)
+
+
+ >
}
>
Disable query reports
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx b/frontend/pages/admin/OrgSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx
index 8dd1bab37e..fd149e1a34 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx
@@ -181,9 +181,10 @@ const HostStatusWebhook = ({
onBlur={validateForm}
error={formErrors.destination_url}
tooltip={
- "\
- Provide a URL to deliver
the webhook request to.
\
- "
+
+ Provide a URL to deliver
+ the webhook request to.
+
}
/>
@@ -197,9 +198,13 @@ const HostStatusWebhook = ({
parseTarget
onBlur={validateForm}
tooltip={
- "\
- Select the minimum percentage of hosts that
must fail to check into Fleet in order to trigger
the webhook request.
\
- "
+
+ Select the minimum percentage of hosts that
+
+ must fail to check into Fleet in order to trigger
+
+ the webhook request.
+
}
/>
@@ -213,9 +218,15 @@ const HostStatusWebhook = ({
parseTarget
onBlur={validateForm}
tooltip={
- "\
- Select the minimum number of days that the
configured Percentage of hosts must fail to
check into Fleet in order to trigger the
webhook request.
\
- "
+
+ Select the minimum number of days that the
+
+ configured Percentage of hosts must fail to
+
+ check into Fleet in order to trigger the
+
+ webhook request.
+
}
/>
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx
index e1ae833ec4..ecca349a3a 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx
@@ -248,11 +248,21 @@ const Smtp = ({
value={smtpAuthenticationType}
parseTarget
tooltip={
- "\
- If your mail server requires authentication, you need to specify the authentication type here.
\
- No Authentication - Select this if your SMTP is open.
\
- Username & Password - Select this if your SMTP server requires authentication with a username and password.
\
- "
+ <>
+
+ If your mail server requires authentication, you need to
+ specify the authentication type here.
+
+
+ No Authentication - Select this if your SMTP
+ is open.
+
+
+ Username & Password - Select this if your
+ SMTP server requires authentication with a username and
+ password.
+
+ >
}
/>
{renderSmtpSection()}
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx
index 6af41648d6..a20fe14418 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx
@@ -171,7 +171,8 @@ const Sso = ({
parseTarget
onBlur={validateForm}
error={formErrors.idp_image_url}
- tooltip="An optional link to an image such
as a logo for the identity provider."
+ tooltip={`An optional link to an image such
+ as a logo for the identity provider.`}
/>
@@ -184,7 +185,8 @@ const Sso = ({
parseTarget
onBlur={validateForm}
error={formErrors.metadata}
- tooltip="Metadata provided by the identity provider. Either
metadata or a metadata url must be provided."
+ tooltip={`Metadata provided by the identity provider. Either
+ metadata or a metadata url must be provided.`}
/>
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPageTableConfig.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPageTableConfig.tsx
index d2634c094b..e159e6449b 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPageTableConfig.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPageTableConfig.tsx
@@ -125,12 +125,16 @@ const generateTableHeaders = (
if (cellProps.cell.value === "GitOps") {
return (
- when creating an API-only user. This user has no
- access to the UI.
- `}
+ position="top-start"
+ tipContent={
+ <>
+ The GitOps role is only available on the command-line
+
+ when creating an API-only user. This user has no
+
+ access to the UI.
+ >
+ }
>
GitOps
@@ -139,12 +143,16 @@ const generateTableHeaders = (
if (cellProps.cell.value === "Observer+") {
return (
- the same functions as an Observer, with the added
- ability to run any live query against all hosts.
- `}
+ position="top-start"
+ tipContent={
+ <>
+ Users with the Observer+ role have access to all of
+
+ the same functions as an Observer, with the added
+
+ ability to run any live query against all hosts.
+ >
+ }
>
{cellProps.cell.value}
diff --git a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
index 9a35ecf208..353036bce2 100644
--- a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
@@ -5,7 +5,6 @@ import PATHS from "router/paths";
import { NotificationContext } from "context/notification";
import { ITeam } from "interfaces/team";
import { IUserFormErrors, UserRole } from "interfaces/user";
-import { IRole } from "interfaces/role";
import Button from "components/buttons/Button";
import validatePresence from "components/forms/validators/validate_presence";
@@ -398,11 +397,14 @@ const UserForm = ({
value={formData.email || ""}
disabled={!isNewUser && !(smtpConfigured || sesConfigured)}
tooltip={
- "\
- Editing an email address requires that SMTP or SES is configured in order to send a validation email. \
-
\
- Users with Admin role can configure SMTP in Settings > Organization settings. \
- "
+ <>
+ Editing an email address requires that SMTP or SES is configured in
+ order to send a validation email.
+
+
+ Users with Admin role can configure SMTP in{" "}
+ Settings > Organization settings.
+ >
}
/>
{!isNewUser &&
@@ -434,11 +436,16 @@ const UserForm = ({
value={canUseSso && formData.sso_enabled}
disabled={!canUseSso}
wrapperClassName={`${baseClass}__invite-admin`}
- tooltip={`
- Enabling single sign-on for a user requires that SSO is first enabled for the organization.
-
- Users with Admin role can configure SSO in Settings > Organization settings.
- `}
+ tooltipContent={
+ <>
+ Enabling single sign-on for a user requires that SSO is first
+ enabled for the organization.
+
+
+ Users with Admin role can configure SSO in{" "}
+ Settings > Organization settings.
+ >
+ }
>
Enable single sign-on
@@ -470,14 +477,18 @@ const UserForm = ({
name={"newUserType"}
onChange={onRadioChange("newUserType")}
tooltip={
- smtpConfigured || sesConfigured
- ? ""
- : `
- The "Invite user" feature requires that SMTP or SES
- is configured in order to send invitation emails.
-
- SMTP can be configured in Settings > Organization settings.
- `
+ smtpConfigured || sesConfigured ? (
+ ""
+ ) : (
+ <>
+ The "Invite user" feature requires that SMTP
+ or SES is configured in order to send invitation emails.
+
+
+ SMTP can be configured in Settings > Organization
+ settings.
+ >
+ )
}
/>
>
@@ -506,10 +517,16 @@ const UserForm = ({
"Must include 12 characters, at least 1 number (e.g. 0 - 9), and at least 1 symbol (e.g. &*#)",
]}
blockAutoComplete
- tooltip={`\
- This password is temporary. This user will be asked to set a new password after logging in to the Fleet UI.
\
- This user will not be asked to set a new password after logging in to fleetctl or the Fleet API.\
- `}
+ tooltip={
+ <>
+ This password is temporary. This user will be asked to
+ set a new password after logging in to the Fleet UI.
+
+
+ This user will not be asked to set a new password after
+ logging in to fleetctl or the Fleet API.
+ >
+ }
/>
>
diff --git a/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTableConfig.tsx b/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTableConfig.tsx
index 445dc42bec..96c6da6d2f 100644
--- a/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTableConfig.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTableConfig.tsx
@@ -131,12 +131,16 @@ const generateTableHeaders = (
if (cellProps.cell.value === "GitOps") {
return (
- when creating an API-only user. This user has no
- access to the UI.
- `}
+ position="top-start"
+ tipContent={
+ <>
+ The GitOps role is only available on the command-line
+
+ when creating an API-only user. This user has no
+
+ access to the UI.
+ >
+ }
>
GitOps
@@ -145,12 +149,16 @@ const generateTableHeaders = (
if (cellProps.cell.value === "Observer+") {
return (
- the same functions as an Observer, with the added
- ability to run any live query against all hosts.
- `}
+ position="top-start"
+ tipContent={
+ <>
+ Users with the Observer+ role have access to all of
+
+ the same functions as an Observer, with the added
+
+ ability to run any live query against all hosts.
+ >
+ }
>
{cellProps.cell.value}
diff --git a/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx b/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
index 183cb4c00b..66fa71a3cf 100644
--- a/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
@@ -47,7 +47,6 @@ interface IHeaderProps {
column: {
title: string;
isSortedDesc: boolean;
- isLastColumn?: boolean;
};
getToggleAllRowsSelectedProps: () => IGetToggleAllRowsSelectedProps;
toggleAllRowsSelected: () => void;
@@ -149,7 +148,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "display_name",
@@ -208,7 +206,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "hostname",
@@ -220,7 +217,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "computer_name",
@@ -232,7 +228,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "team_name",
@@ -245,11 +240,13 @@ const allHostTableHeaders: IDataColumn[] = [
Header: (cellProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
- hosts won’t respond to a live query because
- they may be shut down, asleep, or not
- connected to the internet.`}
+ tipContent={
+ <>
+ Online hosts will respond to a live query. Offline hosts won’t
+ respond to a live query because they may be shut down, asleep, or
+ not connected to the internet.
+ >
+ }
className="status-header"
>
Status
@@ -259,7 +256,6 @@ const allHostTableHeaders: IDataColumn[] = [
);
},
@@ -291,7 +287,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "gigs_disk_space_available",
@@ -321,7 +316,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "os_version",
@@ -382,7 +376,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "primary_ip",
@@ -393,21 +386,18 @@ const allHostTableHeaders: IDataColumn[] = [
Header: (cellProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
- on. To filter by MDM status, head to the Dashboard page.
- `}
+ tipContent={
+ <>
+ Settings can be updated remotely on hosts with MDM turned
+
+ on. To filter by MDM status, head to the Dashboard page.
+ >
+ }
>
MDM status
);
- return (
-
- );
+ return
;
},
disableSortBy: true,
accessor: "mdm.enrollment_status",
@@ -427,21 +417,18 @@ const allHostTableHeaders: IDataColumn[] = [
Header: (cellProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
- filter by MDM server URL, head to the Dashboard page.
- `}
+ tipContent={
+ <>
+ The MDM server that updates settings on the host. To
+
+ filter by MDM server URL, head to the Dashboard page.
+ >
+ }
>
MDM server URL
);
- return (
-
- );
+ return
;
},
disableSortBy: true,
accessor: "mdm.server_url",
@@ -462,7 +449,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "public_ip",
@@ -506,9 +492,12 @@ const allHostTableHeaders: IDataColumn[] = [
Header: (cellProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
reported vitals.
- `}
+ tipContent={
+ <>
+ The last time the host
+
reported vitals.
+ >
+ }
>
Last fetched
@@ -517,7 +506,6 @@ const allHostTableHeaders: IDataColumn[] = [
);
},
@@ -534,9 +522,12 @@ const allHostTableHeaders: IDataColumn[] = [
Header: (cellProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
host was online.
- `}
+ tipContent={
+ <>
+ The last time the
+ host was online.
+ >
+ }
>
Last seen
@@ -545,7 +536,6 @@ const allHostTableHeaders: IDataColumn[] = [
);
},
@@ -563,7 +553,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "uuid",
@@ -577,7 +566,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "uptime",
@@ -610,7 +598,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "memory",
@@ -624,7 +611,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "primary_mac",
@@ -636,7 +622,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "hardware_serial",
@@ -648,7 +633,6 @@ const allHostTableHeaders: IDataColumn[] = [
),
accessor: "hardware_model",
diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
index b832c48c64..677536b62b 100644
--- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
@@ -1412,12 +1412,6 @@ const ManageHostsPage = ({
isOnlyObserver || (!isOnGlobalTeam && !isTeamMaintainerOrTeamAdmin),
});
- // Update last column
- tableColumns.forEach((dataColumn) => {
- dataColumn.isLastColumn = false;
- });
- tableColumns[tableColumns.length - 1].isLastColumn = true;
-
const emptyState = () => {
const emptyHosts: IEmptyTableProps = {
header: "No hosts match the current criteria",
diff --git a/frontend/pages/hosts/ManageHostsPage/_styles.scss b/frontend/pages/hosts/ManageHostsPage/_styles.scss
index e4562695a4..c25f09502f 100644
--- a/frontend/pages/hosts/ManageHostsPage/_styles.scss
+++ b/frontend/pages/hosts/ManageHostsPage/_styles.scss
@@ -200,24 +200,6 @@
overflow-x: scroll;
}
&__table {
- thead {
- tr {
- th {
- .last-col-header-with-tip {
- min-width: 90px;
- .component__tooltip-wrapper__tip-text {
- left: -126px;
- }
- }
- .status-header {
- .component__tooltip-wrapper__tip-text {
- left: -220px;
- }
- }
- }
- }
- }
-
tbody {
.issues {
&__cell {
diff --git a/frontend/pages/hosts/details/cards/About/About.tsx b/frontend/pages/hosts/details/cards/About/About.tsx
index c63449ee80..f9de0d032b 100644
--- a/frontend/pages/hosts/details/cards/About/About.tsx
+++ b/frontend/pages/hosts/details/cards/About/About.tsx
@@ -103,7 +103,6 @@ const About = ({
MDM status
{mdm.enrollment_status}
diff --git a/frontend/pages/hosts/details/cards/AgentOptions/AgentOptions.tsx b/frontend/pages/hosts/details/cards/AgentOptions/AgentOptions.tsx
index d5884e4bc5..6bb1932c29 100644
--- a/frontend/pages/hosts/details/cards/AgentOptions/AgentOptions.tsx
+++ b/frontend/pages/hosts/details/cards/AgentOptions/AgentOptions.tsx
@@ -48,7 +48,6 @@ const AgentOptions = ({
{isChromeOS ? (
Agent options
diff --git a/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx b/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
index 789bc51c2a..a39ad9a23b 100644
--- a/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
+++ b/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
@@ -176,7 +176,7 @@ const HostSummary = ({
return (
Disk encryption
-
+
{statusText}
diff --git a/frontend/pages/hosts/details/cards/MunkiIssues/MunkiIssuesTableConfig.tsx b/frontend/pages/hosts/details/cards/MunkiIssues/MunkiIssuesTableConfig.tsx
index 24852d1ae2..eb8c31b602 100644
--- a/frontend/pages/hosts/details/cards/MunkiIssues/MunkiIssuesTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/MunkiIssues/MunkiIssuesTableConfig.tsx
@@ -60,9 +60,9 @@ export const munkiIssuesTableHeaders: IDataColumn[] = [
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
Issues reported the last time Munki ran on each host.>
+ }
>
Issue
@@ -95,9 +95,7 @@ export const munkiIssuesTableHeaders: IDataColumn[] = [
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
The first time Munki reported this issue.>}
>
Time
diff --git a/frontend/pages/hosts/details/cards/Packs/PackTable/PackTableConfig.tsx b/frontend/pages/hosts/details/cards/Packs/PackTable/PackTableConfig.tsx
index 36ca84584a..4f1a5101d3 100644
--- a/frontend/pages/hosts/details/cards/Packs/PackTable/PackTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Packs/PackTable/PackTableConfig.tsx
@@ -79,7 +79,16 @@ const generatePackTableHeaders = (): IDataColumn[] => {
{
Header: () => {
return (
-
+
+ The last time the query ran
+
+ since the last time osquery
+ started on this host.
+ >
+ }
+ >
Last run
);
@@ -93,7 +102,14 @@ const generatePackTableHeaders = (): IDataColumn[] => {
{
Header: () => {
return (
-
+
+ This is the performance
+ impact on this host.
+ >
+ }
+ >
Performance impact
);
diff --git a/frontend/pages/hosts/details/cards/Schedule/ScheduleTableConfig.tsx b/frontend/pages/hosts/details/cards/Schedule/ScheduleTableConfig.tsx
index c02374741b..1bada23c35 100644
--- a/frontend/pages/hosts/details/cards/Schedule/ScheduleTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Schedule/ScheduleTableConfig.tsx
@@ -76,7 +76,14 @@ const generateTableHeaders = (): IDataColumn[] => {
{
Header: () => {
return (
-
+
+ This is the performance
+ impact on this host.
+ >
+ }
+ >
Performance impact
);
diff --git a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
index 1dec977d81..8e7e244835 100644
--- a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
@@ -14,7 +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";
+import { getSoftwareBundleTooltipJSX } from "utilities/helpers";
interface IHeaderProps {
column: {
@@ -106,14 +106,13 @@ const condenseVulnerabilities = (vulns: string[]): string[] => {
const renderBundleTooltip = (name: string, bundle: string) => (
Bundle identifier:
-
- ${bundle}
+
${bundle}
- `}
+ }
>
{name}
@@ -221,7 +220,7 @@ export const generateSoftwareTableHeaders = ({
customOnClick={onClickSoftware}
value={name}
tooltipContent={
- bundle ? getSoftwareBundleTooltipMarkup(bundle) : undefined
+ bundle ? getSoftwareBundleTooltipJSX(bundle) : undefined
}
/>
);
@@ -356,7 +355,14 @@ export const generateSoftwareTableHeaders = ({
title: "File path",
Header: () => {
return (
-
+
+ This is where the software is
+ located on this host.
+ >
+ }
+ >
File path
);
diff --git a/frontend/pages/hosts/details/cards/Users/UsersTable/UsersTableConfig.tsx b/frontend/pages/hosts/details/cards/Users/UsersTable/UsersTableConfig.tsx
index 501789dbb2..7d5e2c857d 100644
--- a/frontend/pages/hosts/details/cards/Users/UsersTable/UsersTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Users/UsersTable/UsersTableConfig.tsx
@@ -49,7 +49,17 @@ const generateUsersTableHeaders = (): IDataColumn[] => {
{
Header: () => {
return (
-
+
+ The command line shell, such as bash,
+
+ that this user is equipped with by
+
+ default when they log in to the system.
+ >
+ }
+ >
Shell
);
diff --git a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
index 3f6c875c1e..bde8ac5a48 100644
--- a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
@@ -767,9 +767,9 @@ const ManagePolicyPage = ({
globalPoliciesCount
)}
caretPosition={"before"}
- tooltipHtml={
- '"All teams" policies are checked
for this team’s hosts.'
- }
+ tooltipHtml={`"All teams" policies are checked ${(
+
+ )} for this team's hosts.`}
onClick={toggleShowInheritedPolicies}
/>
)}
diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tests.tsx b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tests.tsx
index b72e4d954c..145908bb0c 100644
--- a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tests.tsx
+++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tests.tsx
@@ -130,7 +130,7 @@ describe("PolicyForm - component", () => {
);
});
- it("disables run button with tooltip for globally disabled queries", async () => {
+ it("disables run button with tooltip when live queries are globally disabled", async () => {
const render = createCustomRenderer({
context: {
policy: {
diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx
index 46a9b8a283..14d61f37e2 100644
--- a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx
+++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx
@@ -460,9 +460,11 @@ const PolicyForm = ({
>
If automations are turned on, this
information is included."
+
+ If automations are turned on, this
+
information is included.
+
}
- isDelayed
>
Critical:
diff --git a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx b/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx
index d4d69b8fac..3b0c8e976e 100644
--- a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx
+++ b/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx
@@ -153,9 +153,11 @@ const SaveNewPolicyModal = ({
>
If automations are turned on, this
information is included."
+
+ If automations are turned on, this
+
information is included.
+
}
- isDelayed
>
Critical:
diff --git a/frontend/pages/queries/ManageQueriesPage/components/PreviewDataModal/PreviewDataModal.tsx b/frontend/pages/queries/ManageQueriesPage/components/PreviewDataModal/PreviewDataModal.tsx
index 3156c73288..308679f1a0 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/PreviewDataModal/PreviewDataModal.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/PreviewDataModal/PreviewDataModal.tsx
@@ -39,7 +39,12 @@ const PreviewDataModal = ({
+ The "snapshot" key includes the query's results.
+ These will be unique to your query.
+ >
+ }
>
The data sent to your configured log destination will look similar
to the following JSON:
diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
index b577a6e266..052843d00f 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
@@ -197,9 +197,12 @@ const generateTableHeaders = ({
return (
- all hosts where this query was scheduled.`}
+ tipContent={
+ <>
+ This is the average performance impact across
+ all hosts where this query was scheduled.
+ >
+ }
>
Performance impact
diff --git a/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx b/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx
index 1ef27f8b0f..3d88a76f51 100644
--- a/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx
+++ b/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx
@@ -255,8 +255,15 @@ const QueryDetailsPage = ({
destination on a schedule. When automations are
on,
data is sent according to a query’s frequency.`}
+ tipContent={
+ <>
+ Query automations let you send data to your log
+ destination on a schedule. When automations are
+ on
+ ,
+ data is sent according to a query's frequency.
+ >
+ }
>
Automations:
diff --git a/frontend/pages/queries/details/QueryDetailsPage/_styles.scss b/frontend/pages/queries/details/QueryDetailsPage/_styles.scss
index cb560d4bf7..29374fb06a 100644
--- a/frontend/pages/queries/details/QueryDetailsPage/_styles.scss
+++ b/frontend/pages/queries/details/QueryDetailsPage/_styles.scss
@@ -38,10 +38,6 @@
display: flex;
gap: $pad-large;
font-size: $x-small;
- // TODO - remove once refactored tooltip wrapper is merged
- .component__tooltip-wrapper__element__underline::after {
- bottom: 0px;
- }
}
&__automations,
diff --git a/frontend/pages/queries/details/components/NoResults/NoResults.tsx b/frontend/pages/queries/details/components/NoResults/NoResults.tsx
index 3b46b6840c..4841acb24d 100644
--- a/frontend/pages/queries/details/components/NoResults/NoResults.tsx
+++ b/frontend/pages/queries/details/components/NoResults/NoResults.tsx
@@ -67,18 +67,50 @@ const NoResults = ({
// In order of empty page priority
if (disabledCaching) {
const tipContent = () => {
- // TODO - change to JSX with refactor tooltipwrapper merge
if (disabledCachingGlobally) {
- return `
The following setting prevents saving this query's results in Fleet:
\
-
• Query reports are globally disabled in organization settings.
`;
+ return (
+ <>
+ {" "}
+
+ The following setting prevents saving this query's results
+ in Fleet:
+
+ \
+
+ • Query reports are globally disabled in organization
+ settings.
+
+ >
+ );
}
if (discardDataEnabled) {
- return `
The following setting prevents saving this query's results in Fleet:
\
-
• This query has Discard data enabled.
`;
+ return (
+ <>
+
+ The following setting prevents saving this query's results
+ in Fleet:
+
+ \
+
+ • This query has Discard data enabled.
+
+ >
+ );
}
if (!loggingSnapshot) {
- return `
The following setting prevents saving this query's results in Fleet:
\
-
• The logging setting for this query is not Snapshot.
`;
+ return (
+ <>
+
+ The following setting prevents saving this query's results
+ in Fleet:
+
+ \
+
+ • The logging setting for this query is not{" "}
+ Snapshot.
+
+ >
+ );
}
return "Unknown";
};
diff --git a/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx b/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx
index 32516465e5..5467d97cce 100644
--- a/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx
+++ b/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx
@@ -114,10 +114,16 @@ const QueryReport = ({
return (
- You can reset this report by updating the query's SQL, or by
- temporarily enabling the discard data setting and disabling it again.`}
+ tipContent={
+ <>
+ Fleet has retained a sample of early results for reference.
+ Reporting is paused until existing data is deleted.
+
+ You can reset this report by updating the query's SQL, or
+ by temporarily enabling the discard data setting and
+ disabling it again.
+ >
+ }
>
{`${count} result${count === 1 ? "" : "s"}`}
diff --git a/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tests.tsx b/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tests.tsx
index 011e23d502..89cce972f1 100644
--- a/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tests.tsx
+++ b/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tests.tsx
@@ -30,10 +30,6 @@ describe("DiscardDataOption component", () => {
expect(screen.getByText(/Discard data/)).toBeInTheDocument();
expect(screen.getByText(/This setting is ignored/)).toBeInTheDocument();
-
- await fireEvent.mouseOver(screen.getByText(/globally disabled/));
-
- expect(screen.getByText(/A Fleet administrator/)).toBeInTheDocument();
});
it('Restores normal help text when disabled and then "Edit anyway" is clicked', async () => {
diff --git a/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tsx b/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tsx
index 6cfa573679..863fad7d40 100644
--- a/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tsx
+++ b/frontend/pages/queries/edit/components/DiscardDataOption/DiscardDataOption.tsx
@@ -32,12 +32,16 @@ const DiscardDataOption = ({
<>
This setting is ignored because query reports in Fleet have been{" "}
\
- Organization settings > Advanced options > Disable query reports."
+ <>
+ A Fleet administrator can enable query reports under
+
+ Organization settings > Advanced options > Disable query
+ reports
+
+ .
+ >
}
- position="bottom"
>
{"globally disabled."}
{" "}
diff --git a/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx b/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
index 58f7a8a3d4..531479d2eb 100644
--- a/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
+++ b/frontend/pages/software/ManageSoftwarePage/SoftwareTableConfig.tsx
@@ -8,7 +8,7 @@ import { IVulnerability } from "interfaces/vulnerability";
import PATHS from "router/paths";
import {
formatFloatAsPercentage,
- getSoftwareBundleTooltipMarkup,
+ getSoftwareBundleTooltipJSX,
} from "utilities/helpers";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
@@ -79,13 +79,15 @@ const generateEPSSColumnHeader = (isSandboxMode = false) => {
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
- in the next 30 days (EPSS probability). This data is
-
- reported by FIRST.org.
- `}
+ tipContent={
+ <>
+ The probability that this software will be exploited
+
+ in the next 30 days (EPSS probability). This data is
+
+ reported by FIRST.org.
+ >
+ }
>
Probability of exploit
@@ -205,7 +207,7 @@ const generateTableHeaders = (
customOnClick={onClickSoftware}
value={name}
tooltipContent={
- bundle ? getSoftwareBundleTooltipMarkup(bundle) : undefined
+ bundle ? getSoftwareBundleTooltipJSX(bundle) : undefined
}
/>
);
diff --git a/frontend/pages/software/SoftwareDetailsPage/components/Vulnerabilities/VulnTableConfig.tsx b/frontend/pages/software/SoftwareDetailsPage/components/Vulnerabilities/VulnTableConfig.tsx
index dd1556ff3e..518b8f58b5 100644
--- a/frontend/pages/software/SoftwareDetailsPage/components/Vulnerabilities/VulnTableConfig.tsx
+++ b/frontend/pages/software/SoftwareDetailsPage/components/Vulnerabilities/VulnTableConfig.tsx
@@ -92,10 +92,14 @@ const generateVulnTableHeaders = (
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
- This data is reported by FIRST.org.
- `}
+ tipContent={
+ <>
+ The probability that this vulnerability will be exploited in the
+ next 30 days (EPSS probability).
+
+ This data is reported by FIRST.org.
+ >
+ }
>
Probability of exploit
@@ -121,10 +125,15 @@ const generateVulnTableHeaders = (
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
- This data is reported by the National Vulnerability Database (NVD).
- `}
+ tipContent={
+ <>
+ The worst case impact across different environments (CVSS base
+ score).
+
+ This data is reported by the National Vulnerability Database
+ (NVD).
+ >
+ }
>
Severity
@@ -151,10 +160,13 @@ const generateVulnTableHeaders = (
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
+ The vulnerability has been actively exploited in the wild. This
+ data is reported by the Cybersecurity and Infrustructure
+ Security Agency (CISA).
+ >
+ }
>
Known exploit
@@ -181,7 +193,12 @@ const generateVulnTableHeaders = (
Header: (headerProps: IHeaderProps): JSX.Element => {
const titleWithToolTip = (
+ The date this vulnerability was published in the National
+ Vulnerability Database (NVD).
+ >
+ }
>
Published
diff --git a/frontend/styles/var/colors.scss b/frontend/styles/var/colors.scss
index a16b2fa847..fd64221d3f 100644
--- a/frontend/styles/var/colors.scss
+++ b/frontend/styles/var/colors.scss
@@ -24,6 +24,7 @@ $ui-shadow: #e9e9e9;
$ui-vibrant-blue-50: rgba(106, 103, 254, 0.5);
$ui-vibrant-blue-25: #d9d9fe;
$ui-vibrant-blue-10: #f1f0ff; // rgba(241, 240, 255, 1)
+$tooltip-bg: #3e4771;
// Notifications & status & specific messages
$ui-offline: #8b8fa2;
diff --git a/frontend/test/test-setup.ts b/frontend/test/test-setup.ts
index 32d6215065..fd4495869b 100644
--- a/frontend/test/test-setup.ts
+++ b/frontend/test/test-setup.ts
@@ -2,6 +2,14 @@ import "@testing-library/jest-dom";
import mockServer from "./mock-server";
+// Needed for testing react-tooltip-5
+window.CSS.supports = jest.fn();
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
+
// Mock server setup
beforeAll(() => mockServer.listen());
afterEach(() => mockServer.resetHandlers());
diff --git a/frontend/utilities/helpers.ts b/frontend/utilities/helpers.tsx
similarity index 95%
rename from frontend/utilities/helpers.ts
rename to frontend/utilities/helpers.tsx
index 3caddfbd26..4e5fa11932 100644
--- a/frontend/utilities/helpers.ts
+++ b/frontend/utilities/helpers.tsx
@@ -1,3 +1,4 @@
+import React from "react";
import {
isEmpty,
flatMap,
@@ -34,8 +35,8 @@ import {
ISelectedTargetsForApi,
IPackTargets,
} from "interfaces/target";
-import { ITeam, ITeamSummary } from "interfaces/team";
-import { IUser, UserRole } from "interfaces/user";
+import { ITeam } from "interfaces/team";
+import { UserRole } from "interfaces/user";
import stringUtils from "utilities/strings";
import sortUtils from "utilities/sort";
@@ -92,40 +93,6 @@ const labelSlug = (label: ILabel): string => {
return `labels/${id}`;
};
-const statusKey = [
- {
- id: "new",
- count: 0,
- description: "Hosts that have been enrolled to Fleet in the last 24 hours.",
- display_text: "New",
- title_description: "(added in last 24hrs)",
- type: "status",
- },
- {
- id: "online",
- count: 0,
- description: "Hosts that have recently checked-in to Fleet.",
- display_text: "Online",
- type: "status",
- },
- {
- id: "missing",
- count: 0,
- description: "Hosts that have not been online in 30 days or more.",
- display_text: "Missing",
- slug: "missing",
- statusLabelKey: "missing_count",
- type: "status",
- },
- {
- id: "offline",
- count: 0,
- description: "Hosts that have not checked-in to Fleet recently.",
- display_text: "Offline",
- type: "status",
- },
-];
-
const isLabel = (target: ISelectTargetsEntity) => {
return "label_type" in target;
};
@@ -801,7 +768,7 @@ export const syntaxHighlight = (json: any): string => {
/* eslint-disable no-useless-escape */
return jsonStr.replace(
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
- function (match) {
+ (match) => {
let cls = "number";
if (/^"/.test(match)) {
if (/:$/.test(match)) {
@@ -900,15 +867,13 @@ export const getNextLocationPath = ({
return queryString ? `/${nextLocation}?${queryString}` : `/${nextLocation}`;
};
-export const getSoftwareBundleTooltipMarkup = (bundle: string) => {
- return `
-
- Bundle identifier:
-
- ${bundle}
-
- `;
-};
+export const getSoftwareBundleTooltipJSX = (bundle: string) => (
+
+ Bundle identifier:
+
+ {bundle}
+
+);
export const TAGGED_TEMPLATES = {
queryByHostRoute: (hostId: number | undefined | null) => {
diff --git a/frontend/utilities/osquery_tables.ts b/frontend/utilities/osquery_tables.ts
index 375426a5a0..d9264d13f1 100644
--- a/frontend/utilities/osquery_tables.ts
+++ b/frontend/utilities/osquery_tables.ts
@@ -1,6 +1,6 @@
-import { flatMap, map } from "lodash";
+import { flatMap } from "lodash";
-import { IOsQueryTable, IQueryTableColumn } from "interfaces/osquery_table";
+import { IOsQueryTable } from "interfaces/osquery_table";
import osqueryFleetTablesJSON from "../../schema/osquery_fleet_schema.json";
diff --git a/package.json b/package.json
index 63a054c584..7ea919209d 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
"react-table": "7.7.0",
"react-tabs": "3.2.3",
"react-tooltip": "4.2.21",
+ "react-tooltip-5": "npm:react-tooltip@5.21.3",
"remark-gfm": "3.0.1",
"select": "1.1.2",
"sockjs-client": "1.6.1",
diff --git a/yarn.lock b/yarn.lock
index 73678900e3..a5ab594660 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2665,7 +2665,7 @@
dependencies:
"@floating-ui/utils" "^0.1.3"
-"@floating-ui/dom@^1.5.1":
+"@floating-ui/dom@^1.0.0", "@floating-ui/dom@^1.5.1":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa"
integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==
@@ -7271,6 +7271,11 @@ classnames@^2.2.4:
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz"
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
+classnames@^2.3.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
+ integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
+
clean-css@^5.2.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224"
@@ -15294,6 +15299,14 @@ react-tabs@3.2.3:
clsx "^1.1.0"
prop-types "^15.5.0"
+"react-tooltip-5@npm:react-tooltip@5.21.3":
+ version "5.21.3"
+ resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.21.3.tgz#131d578c7ea69f96c65dbd09f071880c34b4f83d"
+ integrity sha512-z3Q+Uka4D6uYxfsssPqfx1W8vw7NIHyC2ZMq+NJkWg4EpUD3w7Fwz/o+dezyUQMCHL7nO/2sFbtWIrkyxktq2Q==
+ dependencies:
+ "@floating-ui/dom" "^1.0.0"
+ classnames "^2.3.0"
+
react-tooltip@*, react-tooltip@4.2.21:
version "4.2.21"
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.21.tgz#840123ed86cf33d50ddde8ec8813b2960bfded7f"