Skip to content

Commit

Permalink
Update policies page empty state (#25726)
Browse files Browse the repository at this point in the history
for #23312 

# Checklist for submitter

- [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/Committing-Changes.md#changes-files)
for more information.

This PR updates the verbiage on the Policies page when no policies are
present for the selected team (or All Teams). It also does a little bit
of code cleanup. Existing test was updated and a new test added. I've
also added VSCode test runners to easily run Jest tests from the IDE.

The [original request](#23073)
mentioned removing the button from the page if All Teams is selected,
but I don't think we should do that -- you can add All Teams policies
with it.

## Screenshots

Empty state for "All teams" (admin):
<img width="658" alt="image"
src="https://github.com/user-attachments/assets/3db674ef-b83e-4a4f-9ba9-adaf0ff17d3d"
/>

Empty state for a team (admin):
<img width="699" alt="image"
src="https://github.com/user-attachments/assets/49b966ff-f335-43c6-b1ed-b6f11b167c68"
/>

Empty state for "All teams" (non-admin):
<img width="663" alt="image"
src="https://github.com/user-attachments/assets/b9685b40-3b42-43f0-a0ff-09602b9d532a"
/>

Empty state for a team (non-admin):
<img width="643" alt="image"
src="https://github.com/user-attachments/assets/034566d2-7c1b-42c8-8655-99447193d099"
/>
  • Loading branch information
sgress454 authored Jan 29, 2025
1 parent 289c426 commit e247a3b
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 46 deletions.
25 changes: 25 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,31 @@
"path": "${workspaceFolder}/frontend"
}
]
},
{
"name": "Jest: test current file",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/.bin/jest",
"args": [
"--config",
"./frontend/test/jest.config.ts",
"${relativeFile}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Jest: run all tests",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/.bin/jest",
"args": [
"--config",
"./frontend/test/jest.config.ts"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
1 change: 1 addition & 0 deletions changes/23312-update-policies-empty-state
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Clarified text on the Policies page when no policies exist for the selected team (or All Teams)
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import createMockPolicy from "__mocks__/policyMock";
import PoliciesTable from "./PoliciesTable";

describe("Policies table", () => {
it("Renders the page-wide empty state when no policies are present", async () => {
it("Renders the page-wide empty state when no policies are present (all teams)", async () => {
const render = createCustomRenderer({
context: {
app: {
Expand All @@ -22,8 +22,7 @@ describe("Policies table", () => {
<PoliciesTable
policiesList={[]}
isLoading={false}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onDeletePolicyClick={() => {}}
onDeletePolicyClick={noop}
currentTeam={{ id: -1, name: "All teams" }}
isPremiumTier
searchQuery=""
Expand All @@ -34,7 +33,40 @@ describe("Policies table", () => {
/>
);

expect(screen.getByText("You don't have any policies")).toBeInTheDocument();
expect(
screen.getByText("You don't have any policies that apply to all teams")
).toBeInTheDocument();
expect(screen.queryByText("Name")).toBeNull();
});

it("Renders the page-wide empty state when no policies are present (specific team)", async () => {
const render = createCustomRenderer({
context: {
app: {
isGlobalAdmin: true,
currentUser: createMockUser(),
},
},
});

render(
<PoliciesTable
policiesList={[]}
isLoading={false}
onDeletePolicyClick={noop}
currentTeam={{ id: 1, name: "Some team" }}
isPremiumTier
searchQuery=""
page={0}
onQueryChange={noop}
renderPoliciesCount={() => null}
resetPageIndex={false}
/>
);

expect(
screen.getByText("You don't have any policies that apply to this team")
).toBeInTheDocument();
expect(screen.queryByText("Name")).toBeNull();
});

Expand All @@ -52,8 +84,7 @@ describe("Policies table", () => {
<PoliciesTable
policiesList={[]}
isLoading={false}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onDeletePolicyClick={() => {}}
onDeletePolicyClick={noop}
currentTeam={{ id: -1, name: "All teams" }}
isPremiumTier
searchQuery="shouldn't match anything"
Expand Down Expand Up @@ -84,8 +115,7 @@ describe("Policies table", () => {
<PoliciesTable
policiesList={[testCriticalPolicy]}
isLoading={false}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onDeletePolicyClick={() => {}}
onDeletePolicyClick={noop}
currentTeam={{ id: -1, name: "All teams" }}
isPremiumTier
searchQuery=""
Expand Down Expand Up @@ -123,8 +153,7 @@ describe("Policies table", () => {
<PoliciesTable
policiesList={[testInheritedPolicy]}
isLoading={false}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onDeletePolicyClick={() => {}}
onDeletePolicyClick={noop}
currentTeam={{ id: 2, name: "Team 2" }}
isPremiumTier
searchQuery=""
Expand Down Expand Up @@ -162,8 +191,7 @@ describe("Policies table", () => {
<PoliciesTable
policiesList={[testGlobalPolicy]}
isLoading={false}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onDeletePolicyClick={() => {}}
onDeletePolicyClick={noop}
currentTeam={{ id: -1, name: "All teams" }}
isPremiumTier
searchQuery=""
Expand Down Expand Up @@ -202,8 +230,7 @@ describe("Policies table", () => {
<PoliciesTable
policiesList={[...testInheritedPolicies, ...testTeamPolicies]}
isLoading={false}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onDeletePolicyClick={() => {}}
onDeletePolicyClick={noop}
currentTeam={{ id: 2, name: "Team 2" }}
isPremiumTier
searchQuery=""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useContext } from "react";
import { AppContext } from "context/app";

import { IPolicyStats } from "interfaces/policy";
import { ITeamSummary } from "interfaces/team";
import { ITeamSummary, APP_CONTEXT_ALL_TEAMS_ID } from "interfaces/team";
import { IEmptyTableProps } from "interfaces/empty_table";

import Button from "components/buttons/Button";
Expand Down Expand Up @@ -55,34 +55,43 @@ const PoliciesTable = ({
}: IPoliciesTableProps): JSX.Element => {
const { config } = useContext(AppContext);

const emptyState = () => {
const emptyPolicies: IEmptyTableProps = {
graphicName: "empty-policies",
header: "You don't have any policies",
info:
"Add policies to detect device health issues and trigger automations.",
};
if (canAddOrDeletePolicy) {
emptyPolicies.primaryButton = (
<Button
variant="brand"
className={`${baseClass}__select-policy-button`}
onClick={onAddPolicyClick}
>
Add policy
</Button>
);
}
if (searchQuery) {
delete emptyPolicies.graphicName;
delete emptyPolicies.primaryButton;
emptyPolicies.header = "No matching policies";
emptyPolicies.info = "No policies match the current filters.";
}

return emptyPolicies;
const emptyState: IEmptyTableProps = {
graphicName: "empty-policies",
header: "You don't have any policies",
info:
"Add policies to detect device health issues and trigger automations.",
};

if (
currentTeam?.id === null ||
currentTeam?.id === APP_CONTEXT_ALL_TEAMS_ID
) {
emptyState.header += " that apply to all teams";
} else {
emptyState.header += " that apply to this team";
}

if (canAddOrDeletePolicy) {
emptyState.primaryButton = (
<Button
variant="brand"
className={`${baseClass}__select-policy-button`}
onClick={onAddPolicyClick}
>
Add policy
</Button>
);
} else {
emptyState.info = "";
}

if (searchQuery) {
delete emptyState.graphicName;
delete emptyState.primaryButton;
emptyState.header = "No matching policies";
emptyState.info = "No policies match the current filters.";
}

const searchable = !(policiesList?.length === 0 && searchQuery === "");

const hasPermissionAndPoliciesToDelete =
Expand Down Expand Up @@ -120,11 +129,11 @@ const PoliciesTable = ({
}}
emptyComponent={() =>
EmptyTable({
graphicName: emptyState().graphicName,
header: emptyState().header,
info: emptyState().info,
additionalInfo: emptyState().additionalInfo,
primaryButton: emptyState().primaryButton,
graphicName: emptyState.graphicName,
header: emptyState.header,
info: emptyState.info,
additionalInfo: emptyState.additionalInfo,
primaryButton: emptyState.primaryButton,
})
}
renderCount={renderPoliciesCount}
Expand Down

0 comments on commit e247a3b

Please sign in to comment.