Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fleet UI: Update software empty states #19744

Merged
merged 8 commits into from
Jun 17, 2024
1 change: 1 addition & 0 deletions changes/19181-software-empty-states
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Clean up software empty states in the UI
19 changes: 10 additions & 9 deletions frontend/pages/DashboardPage/cards/Software/Software.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useContext, useMemo } from "react";
import React, { useMemo } from "react";
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
import { Row } from "react-table";
import { InjectedRouter } from "react-router";
import PATHS from "router/paths";
import { InjectedRouter } from "react-router";

import { AppContext } from "context/app";
import { buildQueryStringFromParams } from "utilities/url";
import { ISoftwareResponse } from "interfaces/software";

import { ITableQueryData } from "components/TableContainer/TableContainer";
import TabsWrapper from "components/TabsWrapper";
import TableContainer from "components/TableContainer";
import TableDataError from "components/DataError";
Expand All @@ -20,12 +21,14 @@ interface ISoftwareCardProps {
isCollectingInventory: boolean;
isSoftwareFetching: boolean;
isSoftwareEnabled?: boolean;
software: any;
software?: ISoftwareResponse;
teamId?: number;
pageIndex: number;
navTabIndex: any;
onTabChange: any;
onQueryChange: any;
navTabIndex: number;
onTabChange: (index: number, last: number, event: Event) => boolean | void;
onQueryChange?:
| ((queryData: ITableQueryData) => void)
| ((queryData: ITableQueryData) => number);
router: InjectedRouter;
}

Expand Down Expand Up @@ -53,8 +56,6 @@ const Software = ({
teamId,
router,
}: ISoftwareCardProps): JSX.Element => {
const { noSandboxHosts } = useContext(AppContext);

const tableHeaders = useMemo(() => generateTableHeaders(teamId), [teamId]);

const handleRowSelect = (row: IRowProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Row } from "react-table";

import PATHS from "router/paths";

import { AppContext } from "context/app";
import { GITHUB_NEW_ISSUE_LINK } from "utilities/constants";

import CustomLink from "components/CustomLink";
Expand Down Expand Up @@ -54,8 +53,6 @@ const SoftwareOSTable = ({
isLoading,
resetPageIndex,
}: ISoftwareOSTableProps) => {
const { isSandboxMode, noSandboxHosts } = useContext(AppContext);

const determineQueryParamChange = useCallback(
(newTableQuery: ITableQueryData) => {
const changedEntry = Object.entries(newTableQuery).find(([key, val]) => {
Expand Down Expand Up @@ -182,7 +179,11 @@ const SoftwareOSTable = ({
isLoading={isLoading}
resultsTitle="items"
emptyComponent={() => (
<EmptySoftwareTable isSoftwareDisabled={!isSoftwareEnabled} />
<EmptySoftwareTable
tableName="operating systems"
isSoftwareDisabled={!isSoftwareEnabled}
isNotDetectingSoftware // non-searchable table renders not detecting by default
/>
)}
defaultSortHeader={orderKey}
defaultSortDirection={orderDirection}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ const SoftwareTable = ({
// determines if a user be able to search in the table
const searchable =
isSoftwareEnabled &&
(!!tableData || query !== "" || softwareFilter === "vulnerableSoftware");
((tableData && tableData.length > 0) ||
query !== "" ||
softwareFilter !== "allSoftware");

const getItemsCountText = () => {
const count = data?.count;
Expand Down Expand Up @@ -290,6 +292,10 @@ const SoftwareTable = ({
};

const renderCustomFilters = () => {
// Hide filters if no software is detected with no filters present
if (query === "" && !showVersions && softwareFilter === "allSoftware")
return <></>;

const options = showVersions
? SOFTWARE_VERSIONS_DROPDOWN_OPTIONS
: SOFTWARE_TITLES_DROPDOWN_OPTIONS;
Expand Down Expand Up @@ -341,8 +347,7 @@ const SoftwareTable = ({
<EmptySoftwareTable
softwareFilter={softwareFilter}
isSoftwareDisabled={!isSoftwareEnabled}
isCollectingSoftware={false} // TODO: update with new API
isSearching={query !== ""}
isNotDetectingSoftware={query === ""}
/>
)}
defaultSortHeader={orderKey}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ describe("Software Vulnerabilities table", () => {
/>
);

expect(
screen.getByText("No software match the current search criteria")
).toBeInTheDocument();
expect(screen.getByText("No software detected")).toBeInTheDocument();
expect(screen.queryByText("Vulnerability")).toBeNull();
});

Expand Down Expand Up @@ -133,7 +131,7 @@ describe("Software Vulnerabilities table", () => {
);

expect(
screen.getByText("No software match the current search criteria")
screen.getByText("No items match the current search criteria")
).toBeInTheDocument();
expect(screen.queryByText("Vulnerability")).toBeNull();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ const SoftwareVulnerabilitiesTable = ({
isLoading,
resetPageIndex,
}: ISoftwareVulnerabilitiesTableProps) => {
const { isPremiumTier, isSandboxMode, noSandboxHosts } = useContext(
AppContext
);
const { isPremiumTier } = useContext(AppContext);

const determineQueryParamChange = useCallback(
(newTableQuery: ITableQueryData) => {
Expand Down Expand Up @@ -139,7 +137,6 @@ const SoftwareVulnerabilitiesTable = ({
if (!data) return [];
return generateTableConfig(
isPremiumTier,
isSandboxMode,
router,
{
includeName: true,
Expand Down Expand Up @@ -269,7 +266,11 @@ const SoftwareVulnerabilitiesTable = ({
isLoading={isLoading}
resultsTitle={"items"}
emptyComponent={() => (
<EmptySoftwareTable isSoftwareDisabled={!isSoftwareEnabled} />
<EmptySoftwareTable
tableName="vulnerabilities"
isSoftwareDisabled={!isSoftwareEnabled}
isNotDetectingSoftware={query === ""}
/>
)}
defaultSortHeader={orderKey}
defaultSortDirection={orderDirection}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
import ViewAllHostsLink from "components/ViewAllHostsLink";
import LinkCell from "components/TableContainer/DataTable/LinkCell";
import TooltipWrapper from "components/TooltipWrapper";
import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
import { HumanTimeDiffWithDateTip } from "components/HumanTimeDiffWithDateTip";

interface ICellProps {
Expand Down Expand Up @@ -57,7 +56,6 @@ interface IVulnerabilitiesTableConfigOptions {

const generateTableHeaders = (
isPremiumTier?: boolean,
isSandboxMode?: boolean,
router?: InjectedRouter,
configOptions?: IVulnerabilitiesTableConfigOptions,
teamId?: number
Expand Down Expand Up @@ -124,7 +122,6 @@ const generateTableHeaders = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down Expand Up @@ -157,7 +154,6 @@ const generateTableHeaders = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down Expand Up @@ -191,7 +187,6 @@ const generateTableHeaders = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down Expand Up @@ -225,7 +220,6 @@ const generateTableHeaders = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,46 @@ import { ISoftwareDropdownFilterVal } from "pages/SoftwarePage/SoftwareTitles/So

export interface IEmptySoftwareTableProps {
softwareFilter?: ISoftwareDropdownFilterVal;
/** tableName is displayed in the search empty state */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

tableName?: string;
isSoftwareDisabled?: boolean;
/** isNotDetectingSoftware renders empty states when no search string is present */
isNotDetectingSoftware?: boolean;
/** isCollectingSoftware is only used on the Dashboard page with a TODO to revisit */
isCollectingSoftware?: boolean;
isSearching?: boolean;
}

const generateTypeText = (softwareFilter?: ISoftwareDropdownFilterVal) => {
const generateTypeText = (
tableName: string,
softwareFilter?: ISoftwareDropdownFilterVal
) => {
if (softwareFilter === "installableSoftware") {
return "installable";
return "installable software";
}
return softwareFilter === "vulnerableSoftware" ? "vulnerable" : "";
if (softwareFilter === "vulnerableSoftware") {
return "vulnerable software";
}
return tableName;
};

const EmptySoftwareTable = ({
softwareFilter,
tableName = "software",
isSoftwareDisabled,
isNotDetectingSoftware,
isCollectingSoftware,
isSearching,
}: IEmptySoftwareTableProps): JSX.Element => {
const softwareTypeText = generateTypeText(softwareFilter);
const softwareTypeText = generateTypeText(tableName, softwareFilter);

const emptySoftware: IEmptyTableProps = {
header: `No ${softwareTypeText} software match the current search criteria`,
info:
"This report is updated every hour to protect the performance of your devices.",
header: "No items match the current search criteria",
info: `Expecting to see ${softwareTypeText}? Check back later.`,
};

if (isNotDetectingSoftware) {
emptySoftware.header = "No software detected";
}

if (isCollectingSoftware) {
emptySoftware.header = "No software detected";
emptySoftware.info =
Expand All @@ -56,11 +70,6 @@ const EmptySoftwareTable = ({
</>
);
}
if (softwareFilter === "vulnerableSoftware" && !isSearching) {
emptySoftware.header = "No vulnerable software detected";
emptySoftware.info =
"This report is updated every hour to protect the performance of your devices.";
}

return (
<EmptyTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { AppContext } from "context/app";
import { ISoftwareVulnerability } from "interfaces/software";
import { GITHUB_NEW_ISSUE_LINK } from "utilities/constants";
import { buildQueryStringFromParams } from "utilities/url";

import TableContainer from "components/TableContainer";
import EmptyTable from "components/EmptyTable";
import CustomLink from "components/CustomLink";
Expand Down Expand Up @@ -68,7 +67,7 @@ const SoftwareVulnerabilitiesTable = ({
router,
teamIdForApi,
}: ISoftwareVulnerabilitiesTableProps) => {
const { isPremiumTier, isSandboxMode } = useContext(AppContext);
const { isPremiumTier } = useContext(AppContext);

const classNames = classnames(baseClass, className);

Expand All @@ -88,14 +87,8 @@ const SoftwareVulnerabilitiesTable = ({
};

const tableHeaders = useMemo(
() =>
generateTableConfig(
Boolean(isPremiumTier),
Boolean(isSandboxMode),
router,
teamIdForApi
),
[isPremiumTier, isSandboxMode]
() => generateTableConfig(Boolean(isPremiumTier), router, teamIdForApi),
[isPremiumTier]
);
return (
<div className={classNames}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import HeaderCell from "components/TableContainer/DataTable/HeaderCell/HeaderCel
import TextCell from "components/TableContainer/DataTable/TextCell";
import TooltipWrapper from "components/TooltipWrapper";
import { HumanTimeDiffWithDateTip } from "components/HumanTimeDiffWithDateTip";
import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
import ProbabilityOfExploit from "components/ProbabilityOfExploit/ProbabilityOfExploit";
import ViewAllHostsLink from "components/ViewAllHostsLink";
import LinkCell from "components/TableContainer/DataTable/LinkCell";
Expand Down Expand Up @@ -49,7 +48,6 @@ interface IDataColumn {

const generateTableConfig = (
isPremiumTier: boolean,
isSandboxMode: boolean,
router: InjectedRouter,
teamId?: number
): IDataColumn[] => {
Expand Down Expand Up @@ -108,7 +106,6 @@ const generateTableConfig = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down Expand Up @@ -141,7 +138,6 @@ const generateTableConfig = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down Expand Up @@ -175,7 +171,6 @@ const generateTableConfig = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down Expand Up @@ -209,7 +204,6 @@ const generateTableConfig = (
value={titleWithTooltip}
isSortedDesc={headerProps.column.isSortedDesc}
/>
{isSandboxMode && <PremiumFeatureIconWithTooltip />}
</>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,19 @@ const HostSoftwareTable = ({
[determineQueryParamChange, pagePath, generateNewQueryParams, router]
);

const count = data?.count || data?.software.length || 0;
const isSoftwareNotDetected = count === 0 && searchQuery === "";

const memoizedSoftwareCount = useCallback(() => {
const count = data?.count || data?.software.length || 0;
if (isSoftwareNotDetected) {
return null;
}

return <SoftwareCount count={count} />;
}, [data?.count, data?.software.length]);

const memoizedEmptyComponent = useCallback(() => {
return <EmptySoftwareTable isSearching={searchQuery !== ""} />;
return <EmptySoftwareTable isNotDetectingSoftware={searchQuery === ""} />;
}, [searchQuery]);

return (
Expand All @@ -134,7 +140,7 @@ const HostSoftwareTable = ({
emptyComponent={memoizedEmptyComponent}
showMarkAllPages={false}
isAllPagesSelected={false}
searchable
searchable={!isSoftwareNotDetected}
manualSortBy
/>
</div>
Expand Down
Loading