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

feat(vault): Display warning icon next to controller nav link if not all region controllers are configured with Vault #4504

Merged
merged 3 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/app/base/components/Header/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
authState as authStateFactory,
config as configFactory,
configState as configStateFactory,
controller as controllerFactory,
controllerState as controllerStateFactory,
rootState as rootStateFactory,
user as userFactory,
userState as userStateFactory,
Expand All @@ -39,6 +41,9 @@ beforeEach(() => {
],
loaded: true,
}),
controller: controllerStateFactory({
items: [controllerFactory()],
}),
user: userStateFactory({
auth: authStateFactory({
user: userFactory({
Expand Down Expand Up @@ -218,6 +223,35 @@ it("highlights sub-urls", () => {
expect(currentMenuItem).toHaveTextContent("Machines");
});

it("displays a warning icon next to controllers if vault is not fully configured", () => {
state.controller.items = [
controllerFactory({ vault_configured: true }),
controllerFactory({ vault_configured: false }),
];
renderWithBrowserRouter(<Header />, { route: "/", wrapperProps: { state } });

const controllerLink = screen.getByRole("link", {
name: "warning Controllers",
});
const warningIcon = within(controllerLink).getByTestId("warning-icon");
expect(warningIcon).toHaveClass(
"p-navigation--item-icon p-icon--security-warning-grey"
);
});

it("does not display a warning icon next to controllers if vault is fully configured", () => {
state.controller.items = [
controllerFactory({ vault_configured: true }),
controllerFactory({ vault_configured: true }),
];
renderWithBrowserRouter(<Header />, { route: "/", wrapperProps: { state } });

const controllerLink = screen.getByRole("link", { name: "Controllers" });
expect(
within(controllerLink).queryByTestId("warning-icon")
).not.toBeInTheDocument();
});

it("links from the logo to the dashboard for admins", () => {
state.user.auth.user = userFactory({ is_superuser: true });
renderWithBrowserRouter(<Header />, {
Expand Down
62 changes: 54 additions & 8 deletions src/app/base/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect, useContext } from "react";

import type { NavLink } from "@canonical/react-components";
import {
Icon,
isNavigationButton,
Theme,
Navigation,
Expand All @@ -26,6 +27,9 @@ import ThemePreviewContext from "app/base/theme-preview-context";
import urls from "app/base/urls";
import authSelectors from "app/store/auth/selectors";
import configSelectors from "app/store/config/selectors";
import { actions as controllerActions } from "app/store/controller";
import controllerSelectors from "app/store/controller/selectors";
import type { RootState } from "app/store/root/types";
import { actions as statusActions } from "app/store/status";

type NavItem = {
Expand Down Expand Up @@ -137,11 +141,17 @@ const isSelected = (path: string, link: NavItem) => {
);
};

const generateItems = (
links: NavItem[],
path: string,
forHardwareMenu: boolean
) => {
const generateItems = ({
links,
path,
forHardwareMenu,
vaultIncomplete,
}: {
links: NavItem[];
path: string;
forHardwareMenu: boolean;
vaultIncomplete: boolean;
}) => {
if (forHardwareMenu) {
// Only include the items for the hardware menu.
links = links.filter((link) => link.inHardwareMenu);
Expand All @@ -155,7 +165,21 @@ const generateItems = (
}),
isSelected: isSelected(path, link),
key: link.url,
label: link.label,
label:
link.label === "Controllers" && vaultIncomplete ? ( // check if vault is set up on all controllers
<>
<Icon
aria-label="warning"
className="p-navigation--item-icon"
data-testid="warning-icon"
name="security-warning-grey"
/>
{link.label}
{/** Display a warning icon if setup is incomplete */}
</>
) : (
link.label
),
url: link.url,
}));
};
Expand Down Expand Up @@ -203,6 +227,18 @@ export const Header = (): JSX.Element => {
setTheme(maasTheme ? maasTheme : "default");
}, [location, maasTheme, setTheme]);

useEffect(() => {
dispatch(controllerActions.fetch());
}, [dispatch]);

const { unconfiguredControllers, configuredControllers } = useSelector(
(state: RootState) =>
controllerSelectors.getVaultConfiguredControllers(state)
);

const vaultIncomplete =
unconfiguredControllers.length >= 1 && configuredControllers.length >= 1;

// Hide the navigation items when the user is not authenticated or hasn't been
// through the intro process.
const showLinks = isAuthenticated && completedIntro && completedUserIntro;
Expand Down Expand Up @@ -233,10 +269,20 @@ export const Header = (): JSX.Element => {
? [
{
className: "p-navigation__hardware-menu",
items: generateItems(links, path, true),
items: generateItems({
links: links,
path: path,
forHardwareMenu: true,
vaultIncomplete: vaultIncomplete,
}),
label: "Hardware",
},
...generateItems(links, path, false),
...generateItems({
links: links,
path: path,
forHardwareMenu: false,
vaultIncomplete: vaultIncomplete,
}),
]
: null
}
Expand Down
4 changes: 4 additions & 0 deletions src/app/base/components/Header/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@
.p-navigation--red {
background-color: #A71B33 !important;
}

.p-navigation--item-icon {
margin-right: $sph--small;
}
}