simplify TeamsDropdown component and update ManageHostPage to keep po… (#7606)
* simplify TeamsDropdown component and update ManageHostPage to keep policy filter across team change * fix TeamDropdown for users not on global team
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- keep policy filter on Manage Hosts Page changes across a team changes
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { noop } from "lodash";
|
||||
import { renderWithAppContext } from "test/test-utils";
|
||||
|
||||
import TeamsDropdown from "./TeamsDropdown";
|
||||
|
||||
describe("TeamsDropdown - component", () => {
|
||||
const USER_TEAMS = [
|
||||
{ id: 1, name: "Team 1" },
|
||||
{ id: 2, name: "Team 2" },
|
||||
];
|
||||
|
||||
it("renders the given selected team from selectedTeamId", () => {
|
||||
render(
|
||||
<TeamsDropdown
|
||||
currentUserTeams={USER_TEAMS}
|
||||
selectedTeamId={1}
|
||||
onChange={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
const selectedTeam = screen.getByText("Team 1");
|
||||
expect(selectedTeam).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the first team option when includeAll is false and when no selectedTeamId is given", () => {
|
||||
render(
|
||||
<TeamsDropdown
|
||||
currentUserTeams={USER_TEAMS}
|
||||
includeAll={false}
|
||||
onChange={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
const selectedTeam = screen.getByText("Team 1");
|
||||
expect(selectedTeam).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("user is on the global team", () => {
|
||||
const contextValue = {
|
||||
isOnGlobalTeam: true,
|
||||
};
|
||||
|
||||
it("renders 'All teams' when no selectedTeamId is given", () => {
|
||||
renderWithAppContext(
|
||||
<TeamsDropdown currentUserTeams={USER_TEAMS} onChange={noop} />,
|
||||
{ contextValue }
|
||||
);
|
||||
|
||||
const selectedTeam = screen.getByText("All teams");
|
||||
expect(selectedTeam).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the first team option when includeAll is false and when no selectedTeamId is given", () => {
|
||||
renderWithAppContext(
|
||||
<TeamsDropdown
|
||||
currentUserTeams={USER_TEAMS}
|
||||
includeAll={false}
|
||||
onChange={noop}
|
||||
/>,
|
||||
{ contextValue }
|
||||
);
|
||||
|
||||
const selectedTeam = screen.getByText("Team 1");
|
||||
expect(selectedTeam).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("user is not on the global team", () => {
|
||||
const contextValue = { isOnGlobalTeam: false };
|
||||
|
||||
it("renders the first team when no selectedTeamId is given", () => {
|
||||
renderWithAppContext(
|
||||
<TeamsDropdown currentUserTeams={USER_TEAMS} onChange={noop} />,
|
||||
{ contextValue }
|
||||
);
|
||||
|
||||
expect(screen.getByText("Team 1")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useContext, useMemo } from "react";
|
||||
import classnames from "classnames";
|
||||
import { AppContext } from "context/app";
|
||||
import { ITeamSummary } from "interfaces/team";
|
||||
|
||||
// @ts-ignore
|
||||
import Dropdown from "components/forms/fields/Dropdown";
|
||||
import { AppContext } from "context/app";
|
||||
|
||||
const generateDropdownOptions = (
|
||||
teams: ITeamSummary[] | undefined,
|
||||
includeAll: boolean | undefined
|
||||
includeAll: boolean
|
||||
) => {
|
||||
if (!teams) {
|
||||
return [];
|
||||
@@ -30,12 +30,10 @@ const generateDropdownOptions = (
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
interface ITeamsDropdownProps {
|
||||
currentUserTeams: ITeamSummary[];
|
||||
selectedTeamId: number;
|
||||
includeAll?: boolean; // Include "All Teams" option for all users
|
||||
disableAll?: boolean; // Disable "All Teams" option for global users
|
||||
selectedTeamId?: number;
|
||||
includeAll?: boolean; // Include the "All Teams" option;
|
||||
isDisabled?: boolean;
|
||||
onChange: (newSelectedValue: number) => void;
|
||||
onOpen?: () => void;
|
||||
@@ -47,22 +45,18 @@ const baseClass = "component__team-dropdown";
|
||||
const TeamsDropdown = ({
|
||||
currentUserTeams,
|
||||
selectedTeamId,
|
||||
includeAll = false,
|
||||
disableAll = false,
|
||||
includeAll = true,
|
||||
isDisabled,
|
||||
onChange,
|
||||
onOpen,
|
||||
onClose,
|
||||
}: ITeamsDropdownProps): JSX.Element => {
|
||||
const { isOnGlobalTeam } = useContext(AppContext);
|
||||
const { isOnGlobalTeam = false } = useContext(AppContext);
|
||||
|
||||
const teamOptions = useMemo(
|
||||
() =>
|
||||
generateDropdownOptions(
|
||||
currentUserTeams,
|
||||
(isOnGlobalTeam && !disableAll) || includeAll
|
||||
),
|
||||
[currentUserTeams, isOnGlobalTeam]
|
||||
generateDropdownOptions(currentUserTeams, includeAll && isOnGlobalTeam),
|
||||
[currentUserTeams, includeAll, isOnGlobalTeam]
|
||||
);
|
||||
|
||||
const selectedValue = teamOptions.find(
|
||||
|
||||
@@ -24,7 +24,7 @@ interface IAutoSizeInputFieldProps {
|
||||
|
||||
const baseClass = "component__auto-size-input-field";
|
||||
|
||||
const TeamsDropdown = ({
|
||||
const AutoSizeInputField = ({
|
||||
name,
|
||||
placeholder,
|
||||
value,
|
||||
@@ -102,4 +102,4 @@ const TeamsDropdown = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamsDropdown;
|
||||
export default AutoSizeInputField;
|
||||
|
||||
+21
-21
@@ -57,32 +57,32 @@ type Props = {
|
||||
};
|
||||
|
||||
type InitialStateType = {
|
||||
availableTeams: ITeamSummary[] | undefined;
|
||||
availableTeams?: ITeamSummary[];
|
||||
config: IConfig | null;
|
||||
currentUser: IUser | null;
|
||||
currentTeam: ITeamSummary | undefined;
|
||||
currentTeam?: ITeamSummary;
|
||||
enrollSecret: IEnrollSecret[] | null;
|
||||
isPreviewMode: boolean | undefined;
|
||||
isSandboxMode: boolean | undefined;
|
||||
isFreeTier: boolean | undefined;
|
||||
isPremiumTier: boolean | undefined;
|
||||
isGlobalAdmin: boolean | undefined;
|
||||
isGlobalMaintainer: boolean | undefined;
|
||||
isGlobalObserver: boolean | undefined;
|
||||
isOnGlobalTeam: boolean | undefined;
|
||||
isAnyTeamMaintainer: boolean | undefined;
|
||||
isAnyTeamMaintainerOrTeamAdmin: boolean | undefined;
|
||||
isTeamObserver: boolean | undefined;
|
||||
isTeamMaintainer: boolean | undefined;
|
||||
isTeamMaintainerOrTeamAdmin: boolean | undefined;
|
||||
isAnyTeamAdmin: boolean | undefined;
|
||||
isTeamAdmin: boolean | undefined;
|
||||
isOnlyObserver: boolean | undefined;
|
||||
isNoAccess: boolean | undefined;
|
||||
isPreviewMode?: boolean;
|
||||
isSandboxMode?: boolean;
|
||||
isFreeTier?: boolean;
|
||||
isPremiumTier?: boolean;
|
||||
isGlobalAdmin?: boolean;
|
||||
isGlobalMaintainer?: boolean;
|
||||
isGlobalObserver?: boolean;
|
||||
isOnGlobalTeam?: boolean;
|
||||
isAnyTeamMaintainer?: boolean;
|
||||
isAnyTeamMaintainerOrTeamAdmin?: boolean;
|
||||
isTeamObserver?: boolean;
|
||||
isTeamMaintainer?: boolean;
|
||||
isTeamMaintainerOrTeamAdmin?: boolean;
|
||||
isAnyTeamAdmin?: boolean;
|
||||
isTeamAdmin?: boolean;
|
||||
isOnlyObserver?: boolean;
|
||||
isNoAccess?: boolean;
|
||||
sandboxExpiry?: string;
|
||||
setAvailableTeams: (availableTeams: ITeamSummary[]) => void;
|
||||
setCurrentUser: (user: IUser) => void;
|
||||
setCurrentTeam: (team: ITeamSummary | undefined) => void;
|
||||
setCurrentTeam: (team?: ITeamSummary) => void;
|
||||
setConfig: (config: IConfig) => void;
|
||||
setEnrollSecret: (enrollSecret: IEnrollSecret[]) => void;
|
||||
setSandboxExpiry: (sandboxExpiry: string) => void;
|
||||
@@ -90,7 +90,7 @@ type InitialStateType = {
|
||||
|
||||
export type IAppContext = InitialStateType;
|
||||
|
||||
const initialState = {
|
||||
export const initialState = {
|
||||
availableTeams: undefined,
|
||||
config: null,
|
||||
currentUser: null,
|
||||
|
||||
@@ -414,7 +414,7 @@ const TeamDetailsWrapper = ({
|
||||
selectedTeamId={toNumber(routeParams.team_id)}
|
||||
currentUserTeams={adminTeams || []}
|
||||
isDisabled={isLoadingTeams}
|
||||
disableAll
|
||||
includeAll={false}
|
||||
onChange={(newSelectedValue: number) =>
|
||||
handleTeamSelect(newSelectedValue)
|
||||
}
|
||||
|
||||
@@ -338,13 +338,7 @@ const ManageHostsPage = ({
|
||||
|
||||
useQuery<IPolicyAPIResponse, Error>(
|
||||
["policy"],
|
||||
() => {
|
||||
const teamId = parseInt(queryParams?.team_id, 10) || 0;
|
||||
const request = teamId
|
||||
? teamPoliciesAPI.load(teamId, policyId)
|
||||
: globalPoliciesAPI.load(policyId);
|
||||
return request;
|
||||
},
|
||||
() => globalPoliciesAPI.load(policyId),
|
||||
{
|
||||
enabled: !!policyId,
|
||||
onSuccess: ({ policy: policyAPIResponse }) => {
|
||||
@@ -408,10 +402,6 @@ const ManageHostsPage = ({
|
||||
}
|
||||
};
|
||||
|
||||
const getLabelSelected = () => {
|
||||
return selectedFilters.find((f) => f.includes(LABEL_SLUG_PREFIX));
|
||||
};
|
||||
|
||||
const getStatusSelected = () => {
|
||||
return selectedFilters.find((f) => !f.includes(LABEL_SLUG_PREFIX));
|
||||
};
|
||||
@@ -714,22 +704,19 @@ const ManageHostsPage = ({
|
||||
|
||||
const handleTeamSelect = (teamId: number) => {
|
||||
const { MANAGE_HOSTS } = PATHS;
|
||||
|
||||
const teamIdParam = getValidatedTeamId(
|
||||
availableTeams || [],
|
||||
teamId,
|
||||
currentUser,
|
||||
isOnGlobalTeam as boolean
|
||||
isOnGlobalTeam ?? false
|
||||
);
|
||||
|
||||
const slimmerParams = omit(queryParams, [
|
||||
"policy_id",
|
||||
"policy_response",
|
||||
"team_id",
|
||||
]);
|
||||
const slimmerParams = omit(queryParams, ["team_id"]);
|
||||
|
||||
const newQueryParams = !teamIdParam
|
||||
? slimmerParams
|
||||
: Object.assign({}, slimmerParams, { team_id: teamIdParam });
|
||||
: Object.assign(slimmerParams, { team_id: teamIdParam });
|
||||
|
||||
const nextLocation = getNextLocationPath({
|
||||
pathPrefix: MANAGE_HOSTS,
|
||||
@@ -1168,9 +1155,7 @@ const ManageHostsPage = ({
|
||||
const renderTeamsFilterDropdown = () => (
|
||||
<TeamsDropdown
|
||||
currentUserTeams={availableTeams || []}
|
||||
selectedTeamId={
|
||||
(policyId && policy?.team_id) || (currentTeam?.id as number)
|
||||
}
|
||||
selectedTeamId={currentTeam?.id}
|
||||
isDisabled={isHostsLoading || isHostCountLoading}
|
||||
onChange={(newSelectedValue: number) =>
|
||||
handleTeamSelect(newSelectedValue)
|
||||
@@ -1257,7 +1242,7 @@ const ManageHostsPage = ({
|
||||
/>
|
||||
<FilterPill
|
||||
icon={PolicyIcon}
|
||||
label={policy?.name ?? ""}
|
||||
label={policy?.name ?? "..."}
|
||||
onClear={handleClearPoliciesFilter}
|
||||
className={`${baseClass}__policies-filter-pill`}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import { render, RenderOptions } from "@testing-library/react";
|
||||
|
||||
import { AppContext, IAppContext, initialState } from "context/app";
|
||||
|
||||
type RenderOptionsWithProviderProps = RenderOptions & {
|
||||
contextValue: Partial<IAppContext>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A custom render method that provides a configurable App context when testing components
|
||||
*/
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const renderWithAppContext = (
|
||||
component: React.ReactNode,
|
||||
{ contextValue, ...renderOptions }: RenderOptionsWithProviderProps
|
||||
) => {
|
||||
const value: IAppContext = { ...initialState, ...contextValue };
|
||||
return render(
|
||||
<AppContext.Provider value={value}>{component}</AppContext.Provider>,
|
||||
renderOptions
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user