From 07c992c1781ab3d2b23705a757c965cfc22478dd Mon Sep 17 00:00:00 2001
From: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com>
Date: Tue, 21 May 2024 11:49:37 -0500
Subject: [PATCH] Fix unreleased UI bugs in device details software tab
(#19178)
---
.../DeviceUserPage/DeviceUserPage.tests.tsx | 4 +--
.../details/DeviceUserPage/DeviceUserPage.tsx | 35 ++++++++++++-------
.../Software/DeviceSoftwareTableConfig.tsx | 2 --
.../HostSoftwareTable/HostSoftwareTable.tsx | 12 ++++---
.../hosts/details/cards/Software/Software.tsx | 6 ++--
frontend/utilities/endpoints.ts | 2 +-
6 files changed, 36 insertions(+), 25 deletions(-)
diff --git a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx
index facfb4b2c1..f1f581bd90 100644
--- a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx
+++ b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx
@@ -33,7 +33,7 @@ const mockLocation = {
};
describe("Device User Page", () => {
- it("renders the software empty message if the device has no software", async () => {
+ it("hides the software tab if the device has no software", async () => {
const render = createCustomRenderer({
withBackendMock: true,
});
@@ -50,7 +50,7 @@ describe("Device User Page", () => {
// waiting for the device data to render
await screen.findByText("About");
- await user.click(screen.getByRole("tab", { name: "Software" }));
+ expect(screen.queryByText(/Software/)).not.toBeInTheDocument();
// TODO: Fix this to the new copy
// expect(screen.getByText("No software detected")).toBeInTheDocument();
diff --git a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
index c86d059d4d..f92209b82e 100644
--- a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
+++ b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
@@ -333,6 +333,14 @@ const DeviceUserPage = ({
const findSelectedTab = (pathname: string) =>
findIndex(tabPaths, (x) => x.startsWith(pathname.split("?")[0]));
+ // TODO: This is a temporary fix that conditionally shows the new software tab depending on
+ // whether software items returned in the device details response (legacy endpoint).
+ // If the tab is selected, we call the new host software endpoint and display those results.
+ // Software in the legacy response is only being used as a proxy for `iseSoftwareEnabled`.
+ // Ideally we should be checking the config for whether software is enabled to show/hide the tab,
+ // but it isn't available via device token authenticated API. And we need better specified empty states.
+ const isSoftwareEnabled = !!host?.software.length;
+
return (
{!host || isLoadingHost ? (
@@ -386,7 +394,7 @@ const DeviceUserPage = ({
>
Details
- Software
+ {isSoftwareEnabled && Software}
{isPremiumTier && (
@@ -405,17 +413,20 @@ const DeviceUserPage = ({
munki={deviceMacAdminsData?.munki}
/>
-
-
-
+ {isSoftwareEnabled && (
+
+
+
+ )}
{isPremiumTier && (
{
return DICT[source] || "Unknown";
};
-// interface ISoftwareTableHeadersProps {}
-
export const generateSoftwareTableData = (
software: IHostSoftware[]
): IHostSoftware[] => {
diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx
index caccee69b5..91a504ba66 100644
--- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx
+++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx
@@ -16,7 +16,7 @@ const baseClass = "host-software-table";
interface IHostSoftwareTableProps {
tableConfig: any; // TODO: type
- data: IGetHostSoftwareResponse | IGetDeviceSoftwareResponse;
+ data?: IGetHostSoftwareResponse | IGetDeviceSoftwareResponse;
isLoading: boolean;
router: InjectedRouter;
sortHeader: string;
@@ -26,7 +26,7 @@ interface IHostSoftwareTableProps {
pagePath: string;
}
-const SoftwareCount = (count: number) => {
+const SoftwareCount = ({ count }: { count: number }) => {
return (
@@ -107,8 +107,9 @@ const HostSoftwareTable = ({
);
const memoizedSoftwareCount = useCallback(() => {
- return SoftwareCount(data.count || data.software.length || 0);
- }, [data.count, data.software.length]);
+ const count = data?.count || data?.software.length || 0;
+ return ;
+ }, [data?.count, data?.software.length]);
const memoizedEmptyComponent = useCallback(() => {
return ;
@@ -120,12 +121,13 @@ const HostSoftwareTable = ({
renderCount={memoizedSoftwareCount}
resultsTitle="software items"
columnConfigs={tableConfig}
- data={data.software}
+ data={data?.software || []}
isLoading={isLoading}
defaultSortHeader={sortHeader}
defaultSortDirection={sortDirection}
defaultSearchQuery={searchQuery}
defaultPageIndex={page}
+ disableNextPage={data?.meta.has_next_results === false}
pageSize={DEFAULT_PAGE_SIZE}
inputPlaceHolder="Search by name"
onQueryChange={onQueryChange}
diff --git a/frontend/pages/hosts/details/cards/Software/Software.tsx b/frontend/pages/hosts/details/cards/Software/Software.tsx
index 1b92a1b6fa..df562a3dda 100644
--- a/frontend/pages/hosts/details/cards/Software/Software.tsx
+++ b/frontend/pages/hosts/details/cards/Software/Software.tsx
@@ -122,7 +122,7 @@ const SoftwareCard = ({
},
{
...DEFAULT_USE_QUERY_OPTIONS,
- enabled: isSoftwareEnabled && !isMyDevicePage,
+ enabled: isSoftwareEnabled && !isMyDevicePage, // if disabled, we'll always show a generic "No software detected" message
keepPreviousData: true,
staleTime: 7000,
}
@@ -250,8 +250,8 @@ const SoftwareCard = ({
) : (
<>
- {(isError || !data) && }
- {!isError && data && (
+ {isError && }
+ {!isError && (
- `/${API_VERSION}/fleet/devices/${token}/software`,
+ `/${API_VERSION}/fleet/device/${token}/software`,
DEVICE_USER_RESET_ENCRYPTION_KEY: (token: string): string => {
return `/${API_VERSION}/fleet/device/${token}/rotate_encryption_key`;
},