Bug 11525: Fixed navigation issues on 'My Device' page (#12102)

Fixed navigation on DeviceUserPage Tab components.
This commit is contained in:
Juan Fernandez 2023-06-06 06:46:46 -04:00 committed by GitHub
parent 40d866a274
commit 90197d83ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 23 deletions

View File

@ -0,0 +1 @@
- Fixed bug with page navigation inside 'My Device' page.

View File

@ -8,6 +8,30 @@ import { createCustomRenderer } from "test/test-utils";
import { customDeviceHandler } from "test/handlers/device-handler";
import DeviceUserPage from "./DeviceUserPage";
const mockRouter = {
push: jest.fn(),
replace: jest.fn(),
goBack: jest.fn(),
goForward: jest.fn(),
go: jest.fn(),
setRouteLeaveHook: jest.fn(),
isActive: jest.fn(),
createHref: jest.fn(),
createPath: jest.fn(),
};
const mockLocation = {
pathname: "",
query: {
vulnerable: undefined,
page: undefined,
query: undefined,
order_key: undefined,
order_direction: undefined,
},
search: undefined,
};
describe("Device User Page", () => {
it("renders the software empty message if the device has no software", async () => {
const render = createCustomRenderer({
@ -16,7 +40,11 @@ describe("Device User Page", () => {
// TODO: fix return type from render
const { user } = render(
<DeviceUserPage params={{ device_auth_token: "testToken" }} />
<DeviceUserPage
router={mockRouter}
params={{ device_auth_token: "testToken" }}
location={mockLocation}
/>
);
// waiting for the device data to render
@ -37,7 +65,11 @@ describe("Device User Page", () => {
});
const { user } = await render(
<DeviceUserPage params={{ device_auth_token: "testToken" }} />
<DeviceUserPage
router={mockRouter}
params={{ device_auth_token: "testToken" }}
location={mockLocation}
/>
);
// waiting for the device data to render

View File

@ -1,9 +1,9 @@
import React, { useState, useContext, useCallback } from "react";
import { Params } from "react-router/lib/Router";
import { InjectedRouter, Params } from "react-router/lib/Router";
import { useQuery } from "react-query";
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
import { pick } from "lodash";
import { pick, findIndex } from "lodash";
import { NotificationContext } from "context/notification";
import deviceUserAPI from "services/entities/device_user";
@ -28,6 +28,7 @@ import {
wrapFleetHelper,
humanHostDiskEncryptionEnabled,
} from "utilities/helpers";
import PATHS from "router/paths";
import HostSummaryCard from "../cards/HostSummary";
import AboutCard from "../cards/About";
@ -47,6 +48,18 @@ import BootstrapPackageModal from "../HostDetailsPage/modals/BootstrapPackageMod
const baseClass = "device-user";
interface IDeviceUserPageProps {
location: {
pathname: string;
query: {
vulnerable?: string;
page?: string;
query?: string;
order_key?: string;
order_direction?: "asc" | "desc";
};
search?: string;
};
router: InjectedRouter;
params: Params;
}
@ -56,9 +69,12 @@ interface IHostDiskEncryptionProps {
}
const DeviceUserPage = ({
location,
router,
params: { device_auth_token },
}: IDeviceUserPageProps): JSX.Element => {
const deviceAuthToken = device_auth_token;
const queryParams = location.query;
const { renderFlash } = useContext(NotificationContext);
const [isPremiumTier, setIsPremiumTier] = useState(false);
@ -347,6 +363,15 @@ const DeviceUserPage = ({
host?.mdm.macos_settings?.disk_encryption === "action_required" &&
host?.mdm.macos_settings?.action_required === "rotate_key";
const tabPaths = [
PATHS.DEVICE_USER_DETAILS(deviceAuthToken),
PATHS.DEVICE_USER_DETAILS_SOFTWARE(deviceAuthToken),
PATHS.DEVICE_USER_DETAILS_POLICIES(deviceAuthToken),
];
const findSelectedTab = (pathname: string) =>
findIndex(tabPaths, (x) => x.startsWith(pathname.split("?")[0]));
return (
<div className="fleet-desktop-wrapper">
{isLoadingHost ? (
@ -394,7 +419,10 @@ const DeviceUserPage = ({
deviceUser
/>
<TabsWrapper>
<Tabs>
<Tabs
selectedIndex={findSelectedTab(location.pathname)}
onSelect={(i) => router.push(tabPaths[i])}
>
<TabList>
<Tab>Details</Tab>
<Tab>Software</Tab>
@ -419,11 +447,15 @@ const DeviceUserPage = ({
</TabPanel>
<TabPanel>
<SoftwareCard
router={router}
isLoading={isLoadingHost}
software={hostSoftware}
deviceUser
hostId={host?.id || 0}
pathname={location.pathname}
pathPrefix={PATHS.DEVICE_USER_DETAILS_SOFTWARE(
deviceAuthToken
)}
queryParams={queryParams}
/>
</TabPanel>
{isPremiumTier && (

View File

@ -746,8 +746,8 @@ const HostDetailsPage = ({
router={router}
queryParams={queryParams}
routeTemplate={routeTemplate}
hostId={host?.id || 0}
pathname={pathname}
pathPrefix={PATHS.HOST_SOFTWARE(host?.id || 0)}
/>
{host?.platform === "darwin" && macadmins && (
<MunkiIssuesCard

View File

@ -44,8 +44,8 @@ interface ISoftwareTableProps {
order_direction?: "asc" | "desc";
};
routeTemplate?: string;
hostId: number;
pathname: string;
pathPrefix: string;
}
interface IRowProps extends Row {
@ -67,7 +67,7 @@ const SoftwareTable = ({
router,
queryParams,
routeTemplate,
hostId,
pathPrefix,
pathname,
}: ISoftwareTableProps): JSX.Element => {
const { isSandboxMode, setFilteredSoftwarePath } = useContext(AppContext);
@ -118,8 +118,9 @@ const SoftwareTable = ({
) {
newQueryParams.page = 0;
}
const locationPath = getNextLocationPath({
pathPrefix: PATHS.HOST_SOFTWARE(hostId),
pathPrefix,
routeTemplate,
queryParams: newQueryParams,
});
@ -132,7 +133,7 @@ const SoftwareTable = ({
const onClientSidePaginationChange = useCallback(
(pageIndex: number) => {
const locationPath = getNextLocationPath({
pathPrefix: PATHS.HOST_SOFTWARE(hostId),
pathPrefix,
routeTemplate,
queryParams: {
...queryParams,
@ -163,17 +164,16 @@ const SoftwareTable = ({
);
const handleVulnFilterDropdownChange = (isFilterVulnerable: string) => {
router?.replace(
getNextLocationPath({
pathPrefix: PATHS.HOST_SOFTWARE(hostId),
routeTemplate,
queryParams: {
...queryParams,
page: 0,
vulnerable: isFilterVulnerable,
},
})
);
const nextPath = getNextLocationPath({
pathPrefix,
routeTemplate,
queryParams: {
...queryParams,
page: 0,
vulnerable: isFilterVulnerable,
},
});
router?.replace(nextPath);
};
const handleRowSelect = (row: IRowProps) => {

View File

@ -231,7 +231,16 @@ const routes = (
/>
</Route>
</Route>
<Route path="/device/:device_auth_token" component={DeviceUserPage} />
<Route path="device">
<IndexRedirect to=":device_auth_token" />
<Route component={DeviceUserPage}>
<Route path=":device_auth_token" component={DeviceUserPage}>
<Route path="software" component={DeviceUserPage} />
<Route path="policies" component={DeviceUserPage} />
</Route>
</Route>
</Route>
</Route>
<Route path="/apionlyuser" component={ApiOnlyUser} />
<Route path="/404" component={Fleet404} />

View File

@ -75,6 +75,12 @@ export default {
DEVICE_USER_DETAILS: (deviceAuthToken: any): string => {
return `${URL_PREFIX}/device/${deviceAuthToken}`;
},
DEVICE_USER_DETAILS_SOFTWARE: (deviceAuthToken: string): string => {
return `${URL_PREFIX}/device/${deviceAuthToken}/software`;
},
DEVICE_USER_DETAILS_POLICIES: (deviceAuthToken: string): string => {
return `${URL_PREFIX}/device/${deviceAuthToken}/policies`;
},
MANAGE_SOFTWARE: `${URL_PREFIX}/software/manage`,
SOFTWARE_DETAILS: (id: string): string => {
return `${URL_PREFIX}/software/${id}`;