From 418fd60e9ca4a2d41a0c19afba5f44bbce3f10ce Mon Sep 17 00:00:00 2001 From: LeAnn <97471894+Leanngove@users.noreply.github.com> Date: Tue, 28 Jul 2026 10:30:21 -0700 Subject: [PATCH] Clarify "Not supported" on Hosts page by adding tooltip (#49301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Related issue:** Resolves #39987 - Added tooltips to the "Agent," "Last restarted," and "Status" column headers on the Hosts page explaining which platforms are supported and why. - On the Host details page, vitals with a "Not supported" value are now hidden instead of shown. - Fixed the "Last restarted" vital showing on ChromeOS hosts, where it's not actually collected. - Updated the "Last opened" tooltip on the Host details Software table to explain why it's only supported for native macOS, Windows, and Linux apps and packages. - Remove cellProps.rows.length === 1 workaround (which suppresses the tooltip whenever the table has exactly one row) by adding the correct CSS which removes the tooltip overflowing if host table is only 1 row # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually ## Summary by CodeRabbit - **New Features** - Added/updated explanatory tooltips for Hosts table column headers (Agent, Last restarted, Status) with clearer supported-platform wording. - Clarified “Last opened” tooltip scope to native macOS, Windows, and Linux app/package entries. - **Bug Fixes** - Removed “Last restarted” from Host details for ChromeOS hosts when the value isn’t collected. - Prevented vitals rows from rendering when their values resolve to “Not supported,” and tightened “Last restarted” platform visibility. - Fixed tooltip overflow/positioning in the single-row Host software table case. - **Tests** - Updated and expanded vitals/header coverage to match the new display rules. --- changes/39987-clarify-not-supported-tooltips | 4 ++ .../hosts/ManageHostsPage/HostTableConfig.tsx | 58 +++++++++++++------ .../pages/hosts/ManageHostsPage/_styles.scss | 6 ++ .../details/cards/Software/HostSoftware.tsx | 3 +- .../HostSoftwareTableConfig.tests.tsx | 1 - .../Software/HostSoftwareTableConfig.tsx | 35 ++++------- .../details/cards/Vitals/Vitals.tests.tsx | 42 ++++++++++++++ .../hosts/details/cards/Vitals/Vitals.tsx | 2 +- 8 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 changes/39987-clarify-not-supported-tooltips diff --git a/changes/39987-clarify-not-supported-tooltips b/changes/39987-clarify-not-supported-tooltips new file mode 100644 index 0000000000..08ae8c07fb --- /dev/null +++ b/changes/39987-clarify-not-supported-tooltips @@ -0,0 +1,4 @@ +- Added tooltips to the "Agent", "Last restarted", and "Status" column headers on the Hosts page explaining which platforms are supported and why. +- Fixed the "Last restarted" vital showing on ChromeOS hosts, where it's not actually collected. +- Updated the "Last opened" tooltip on the Host details Software table to explain why it's only supported for native macOS, Windows, and Linux apps and packages. +- Removed the `cellProps.rows.length === 1` workaround (which suppressed the tooltip whenever the table had exactly one row) by adding the correct CSS, which removes the tooltip overflowing if the host table is only 1 row. diff --git a/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx b/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx index 677e3b622c..0a2ad02a7e 100644 --- a/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx +++ b/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx @@ -397,26 +397,23 @@ const allHostTableHeaders = (teamId?: number): IHostTableColumnConfig[] => [ // Status { title: "Status", - Header: (cellProps: IHostTableHeaderProps) => { + Header: () => { const titleWithToolTip = ( - Online hosts will respond to a live report. Currently only - supported for macOS, Windows, and Linux. + Only supported on hosts that run Fleet's agent: macOS, + Windows, Linux, and ChromeOS. } className="status-header" + tooltipClass="host-table-header-tooltip" + fixedPositionStrategy > Status ); - return ( - - ); + return ; }, disableSortBy: true, accessor: "status", @@ -592,9 +589,23 @@ const allHostTableHeaders = (teamId?: number): IHostTableColumnConfig[] => [ // Agent { title: "Agent", - Header: (cellProps: IHostTableHeaderProps) => ( - - ), + Header: (cellProps: IHostTableHeaderProps) => { + const titleWithToolTip = ( + + Agent + + ); + return ( + + ); + }, accessor: (row) => row.orbit_version || row.osquery_version, id: "agent", Cell: (cellProps: IHostTableStringCellProps) => { @@ -686,12 +697,23 @@ const allHostTableHeaders = (teamId?: number): IHostTableColumnConfig[] => [ // Last restarted { title: "Last restarted", - Header: (cellProps: IHostTableHeaderProps) => ( - - ), + Header: (cellProps: IHostTableHeaderProps) => { + const titleWithToolTip = ( + + Last restarted + + ); + return ( + + ); + }, accessor: "last_restarted_at", id: "last_restarted_at", Cell: (cellProps: IHostTableStringCellProps) => { diff --git a/frontend/pages/hosts/ManageHostsPage/_styles.scss b/frontend/pages/hosts/ManageHostsPage/_styles.scss index fa50d2cdab..049b5f6fd7 100644 --- a/frontend/pages/hosts/ManageHostsPage/_styles.scss +++ b/frontend/pages/hosts/ManageHostsPage/_styles.scss @@ -251,3 +251,9 @@ padding-bottom: $pad-large; } } + +// The tooltip renders in a portal outside .manage-hosts, so this can't be +// nested under that selector. +.host-table-header-tooltip { + text-align: left; +} diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx index 69e1e1ba9a..5945428274 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx @@ -305,9 +305,8 @@ const HostSoftware = ({ router, teamId: hostTeamId, onShowInventoryVersions, - platform, }); - }, [isMyDevicePage, router, hostTeamId, onShowInventoryVersions, platform]); + }, [isMyDevicePage, router, hostTeamId, onShowInventoryVersions]); const isLoading = isMyDevicePage ? deviceSoftwareLoading diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx index c7e27a2891..a5cb2f2649 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx @@ -13,7 +13,6 @@ describe("HostSoftwareTableConfig - Last opened column", () => { router: mockRouter, teamId: 1, onShowInventoryVersions: noop, - platform: "windows", }); const lastOpenedColumn = headers.find((h) => h.id === "Last opened") as any; diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx index ab2ae305a7..e5b09ea873 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx @@ -7,13 +7,6 @@ import { IHostSoftware, isIpadOrIphoneSoftwareSource, } from "interfaces/software"; -import { - HostPlatform, - isIPadOrIPhone, - isLinuxLike, - isMacOS, - isWindows, -} from "interfaces/platform"; import { IHeaderProps, IStringCellProps } from "interfaces/datatable_config"; import PATHS from "router/paths"; @@ -47,7 +40,6 @@ interface ISoftwareTableHeadersProps { router: InjectedRouter; teamId: number; onShowInventoryVersions: (software: IHostSoftware) => void; - platform: HostPlatform; } // NOTE: cellProps come from react-table @@ -56,7 +48,6 @@ export const generateSoftwareTableHeaders = ({ router, teamId, onShowInventoryVersions, - platform, }: ISoftwareTableHeadersProps): ISoftwareTableConfig[] => { const tableHeaders: ISoftwareTableConfig[] = [ { @@ -132,24 +123,18 @@ export const generateSoftwareTableHeaders = ({ }, { Header: (): JSX.Element => { - let tooltipContent = <>; - - if (isMacOS(platform)) { - tooltipContent = ( - <>When the version installed most recently was last opened. - ); - } else if (isLinuxLike(platform) || isWindows(platform)) { - tooltipContent = <>When any version was last opened.; - } else if (isIPadOrIPhone(platform)) { - tooltipContent = <>Date and time of last open.; - } - - const lastOpenedHeader = tooltipContent ? ( - + const lastOpenedHeader = ( + + Only supported for macOS, Windows, and Linux native apps and + packages. Browser extensions, other package managers, and mobile + apps don't report this information. + + } + > Last opened - ) : ( - "Last opened" ); return ; }, diff --git a/frontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsx b/frontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsx index ca1fc88f57..f3125aed96 100644 --- a/frontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsx +++ b/frontend/pages/hosts/details/cards/Vitals/Vitals.tests.tsx @@ -4,6 +4,7 @@ import { createCustomRenderer } from "test/test-utils"; import createMockHost, { createMockHostGeolocation } from "__mocks__/hostMock"; import { createMockHostMdmData } from "__mocks__/mdmMock"; +import { HostPlatform } from "interfaces/platform"; import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants"; import Vitals from "./Vitals"; @@ -578,6 +579,47 @@ describe("Agent data", () => { }); }); +describe("Last restarted vital", () => { + it.each(["darwin", "windows", "ubuntu"])( + "renders Last restarted for supported platform: %s", + (platform) => { + const mockHost = createMockHost({ + platform: platform as HostPlatform, + last_restarted_at: "2023-01-01T00:00:00Z", + }); + + render(); + + expect(screen.getByText("Last restarted")).toBeInTheDocument(); + } + ); + + it.each(["chrome", "ios", "ipados", "android"])( + "does not render Last restarted for unsupported platform: %s", + (platform) => { + const mockHost = createMockHost({ + platform: platform as HostPlatform, + last_restarted_at: "2023-01-01T00:00:00Z", + }); + + render(); + + expect(screen.queryByText("Last restarted")).not.toBeInTheDocument(); + } + ); +}); + +describe("Munki version vital", () => { + it("renders the Munki version vital when its value is a normal version string", () => { + const mockHost = createMockHost({ platform: "darwin" }); + + render(); + + expect(screen.getByText("Munki version")).toBeInTheDocument(); + expect(screen.getByText("5.5.1")).toBeInTheDocument(); + }); +}); + describe("Disk space field visibility", () => { it("hides disk space field when storage measurement is not supported (sentinel value -1)", () => { const mockHost = createMockHost({ diff --git a/frontend/pages/hosts/details/cards/Vitals/Vitals.tsx b/frontend/pages/hosts/details/cards/Vitals/Vitals.tsx index 24ef40b03d..8bd898dbf0 100644 --- a/frontend/pages/hosts/details/cards/Vitals/Vitals.tsx +++ b/frontend/pages/hosts/details/cards/Vitals/Vitals.tsx @@ -371,7 +371,7 @@ const Vitals = ({ }); // Last restarted - if (!isIosOrIpadosHost && !isAndroidHost) { + if (!isIosOrIpadosHost && !isAndroidHost && !isChromeHost) { vitals.push({ sortKey: "Last restarted", element: (