Skip to content

Commit

Permalink
feat: tag management panel MAASENG-1427
Browse files Browse the repository at this point in the history
- remove .l-aside.is-collapsed workaround
- extract ResponsiveNodeActionMenu component
  • Loading branch information
petermakowski committed Jul 28, 2023
1 parent c0f9232 commit e5bb0da
Show file tree
Hide file tree
Showing 28 changed files with 766 additions and 507 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ module.exports = {
"no-only-tests/no-only-tests": "error",
// vanilla framework often hides default inputs and displays styled ones instead
// because of this we need to use use force option to allow interacting with hidden fields
"cypress/no-force": "warn",
"cypress/no-force": "off",
"prettier/prettier": "error",
},
},
Expand Down
32 changes: 29 additions & 3 deletions cypress/e2e/with-users/machines/actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const MACHINE_ACTIONS_GROUPS = [
const selectFirstMachine = () =>
cy.findByRole("grid", { name: /Machines/i }).within(() => {
cy.findAllByRole("gridcell", { name: /FQDN/i })
.first() // eslint-disable-next-line cypress/no-force
.first()
.within(() => cy.findByRole("checkbox").click({ force: true }));
});

Expand Down Expand Up @@ -117,13 +117,11 @@ context("Machine listing - actions", () => {
const machineName = generateName("machine");
cy.addMachine(machineName);
cy.findByRole("searchbox", { name: "Search" }).type(machineName);
// eslint-disable-next-line cypress/no-force
cy.findByRole("checkbox", { name: `${machineName}.maas` }).click({
force: true,
});
openMachineActionForm("Categorise", "Set pool");
cy.findByRole("complementary", { name: /Set pool/i }).should("exist");
// eslint-disable-next-line cypress/no-force
cy.findByLabelText(/Create pool/i).click({ force: true });
cy.findByLabelText(/Name/i).type(poolName);
cy.findByRole("button", { name: /Set pool for machine/i }).click();
Expand All @@ -134,4 +132,32 @@ context("Machine listing - actions", () => {
cy.deleteMachine(machineName);
cy.deletePool(poolName);
});

it("can create and add a tag to a machine", () => {
const tagName = generateName("tag");
const machineName = generateName("machine");
cy.addMachine(machineName);
cy.findByRole("searchbox", { name: "Search" }).type(machineName);
cy.waitForTableToLoad({ name: /Machines/i });
cy.findByRole("checkbox", { name: `${machineName}.maas` }).click({
force: true,
});
openMachineActionForm("Categorise", "Tag");
cy.findByRole("complementary", { name: /Tag/i }).should("exist");
cy.findByRole("textbox", {
name: "Search existing or add new tags",
}).type(tagName);
cy.findByRole("button", { name: `Create tag "${tagName}"` }).click();
cy.findByRole("form", { name: /Create tag/i }).should("be.visible");
cy.findByRole("button", { name: /Create and add to tag changes/i }).click();
cy.findByRole("table", { name: /Tag changes/i }).within(() => {
cy.findByRole("cell", { name: /To be added/ }).should("exist");
cy.findByRole("cell", { name: new RegExp(tagName, "i") }).should("exist");
});
cy.findByRole("button", { name: /Save/i }).click();
cy.findByRole("grid", { name: /Machines/i })
.within(() => cy.findByText(tagName))
.should("exist");
cy.deleteMachine(machineName);
});
});
4 changes: 0 additions & 4 deletions cypress/e2e/with-users/machines/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ context("Machine listing", () => {
cy.findByRole("searchbox").type(searchFilter);
cy.findByText(/Showing 2 out of 2 machines/).should("exist");
cy.findByRole("grid", { name: /Machines/ }).within(() =>
// eslint-disable-next-line cypress/no-force
cy
.findByRole("checkbox", { name: /Commissioning/i })
.click({ force: true })
Expand Down Expand Up @@ -113,7 +112,6 @@ context("Machine listing", () => {

cy.findAllByRole("button", { name: "Columns" }).click();
cy.findByLabelText("columns menu").within(() =>
// eslint-disable-next-line cypress/no-force
cy.findByRole("checkbox", { name: "Status" }).click({ force: true })
);

Expand All @@ -134,11 +132,9 @@ context("Machine listing", () => {
cy.findByRole("combobox", { name: "Group by" }).select("No grouping");
cy.findByRole("searchbox", { name: "Search" }).type(name);
cy.findByText(/Showing 3 out of 3 machines/).should("exist");
// eslint-disable-next-line cypress/no-force
cy.findByRole("checkbox", { name: `${newMachines[0]}.maas` }).click({
force: true,
});
// eslint-disable-next-line cypress/no-force
cy.findByRole("checkbox", { name: `${newMachines[2]}.maas` }).click({
shiftKey: true,
force: true,
Expand Down
3 changes: 1 addition & 2 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ Cypress.Commands.add("deleteMachine", (hostname: string) => {
cy.findByRole("combobox", { name: "Group by" }).select("No grouping");
cy.findByRole("searchbox").type(hostname);
cy.findByText(/Showing 1 out of 1 machines/).should("exist");
cy.findByRole("grid", { name: "Machines" }).within(() =>
// eslint-disable-next-line cypress/no-force
cy.findByRole("grid", { name: /Machines/ }).within(() =>
cy
.findByRole("checkbox", { name: new RegExp(hostname) })
.click({ force: true })
Expand Down
6 changes: 3 additions & 3 deletions src/app/base/components/AppSidePanel/AppSidePanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ it("displays side panel as a child of #maas-ui DOM node", async () => {
});

it("adds a correct className for a wide panel", async () => {
renderWithBrowserRouter(
<AppSidePanel size="wide" title="side panel title" />
);
renderWithBrowserRouter(<AppSidePanel title="side panel title" />, {
sidePanelSize: "wide",
});
expect(
screen
.getByRole("complementary", { name: "side panel title" })
Expand Down
64 changes: 54 additions & 10 deletions src/app/base/components/AppSidePanel/AppSidePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,65 @@
import type { ReactNode } from "react";
import { useEffect, type ReactNode } from "react";

import { Col, Row, useOnEscapePressed } from "@canonical/react-components";
import {
Col,
Row,
useOnEscapePressed,
usePrevious,
} from "@canonical/react-components";
import classNames from "classnames";
import { useLocation } from "react-router-dom-v5-compat";

import type { SidePanelSize } from "app/base/side-panel-context";
import { useSidePanel } from "app/base/side-panel-context";

type Props = {
title?: string | null;
size?: "wide" | "default" | "narrow";
export type AppSidePanelProps = {
title: string | null;
content?: ReactNode;
size: SidePanelSize;
};

const AppSidePanel = ({
const useCloseSidePanelOnRouteChange = (): void => {
const { setSidePanelContent } = useSidePanel();
const { pathname } = useLocation();
const previousPathname = usePrevious(pathname);

// close side panel on route change
useEffect(() => {
if (pathname !== previousPathname) {
setSidePanelContent(null);
}
}, [pathname, previousPathname, setSidePanelContent]);
};

const useResetSidePanelOnUnmount = (): void => {
const { setSidePanelSize } = useSidePanel();

// reset side panel size to default on unmount
useEffect(() => {
return () => {
setSidePanelSize("regular");
};
}, [setSidePanelSize]);
};

const useCloseSidePanelOnEscPressed = (): void => {
const { setSidePanelContent } = useSidePanel();
useOnEscapePressed(() => setSidePanelContent(null));
};

const AppSidePanelContent = ({
title,
size,
content,
}: Props & { title: string | null }): JSX.Element => {
const { setSidePanelContent } = useSidePanel();
useOnEscapePressed(() => setSidePanelContent(null));
}: AppSidePanelProps): JSX.Element => {
return (
<aside
aria-label={title ?? undefined}
className={classNames("l-aside", {
"is-collapsed": !content,
"is-wide": size === "wide",
"is-narrow": size === "narrow",
"is-large": size === "large",
"is-wide": size === "wide",
})}
data-testid="section-header-content"
id="aside-panel"
Expand All @@ -48,4 +83,13 @@ const AppSidePanel = ({
);
};

const AppSidePanel = (props: Omit<AppSidePanelProps, "size">): JSX.Element => {
useCloseSidePanelOnEscPressed();
useCloseSidePanelOnRouteChange();
useResetSidePanelOnUnmount();
const { sidePanelSize } = useSidePanel();

return <AppSidePanelContent {...props} size={sidePanelSize} />;
};

export default AppSidePanel;
2 changes: 1 addition & 1 deletion src/app/base/components/AppSidePanel/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default } from "./AppSidePanel";
export { default, type AppSidePanelProps } from "./AppSidePanel";
5 changes: 4 additions & 1 deletion src/app/base/components/NodeActionMenu/NodeActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ReactNode } from "react";
import type {
ButtonAppearance,
ButtonProps,
ContextualMenuDropdownProps,
ValueOf,
} from "@canonical/react-components";
import { ContextualMenu, Tooltip } from "@canonical/react-components";
Expand Down Expand Up @@ -39,7 +40,7 @@ type Props = {
toggleAppearance?: ValueOf<typeof ButtonAppearance>;
toggleClassName?: string | null;
toggleLabel?: string;
};
} & Pick<ContextualMenuDropdownProps, "constrainPanelWidth">;

const actionGroups: ActionGroup[] = [
{
Expand Down Expand Up @@ -166,6 +167,7 @@ export const NodeActionMenu = ({
toggleAppearance = "positive",
toggleClassName,
toggleLabel = Label.TakeAction,
constrainPanelWidth,
}: Props): JSX.Element => {
return (
<Tooltip
Expand All @@ -178,6 +180,7 @@ export const NodeActionMenu = ({
>
<ContextualMenu
className={className}
constrainPanelWidth={constrainPanelWidth}
data-testid="take-action-dropdown"
hasToggleIcon
links={getTakeActionLinks(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ const generateActionMenus = (
dropdownProps={{ "aria-label": `${group.title} submenu` }}
hasToggleIcon
links={groupLinks}
position="center"
position="left"
toggleLabel={
!group.icon ? (
group.title
Expand Down
14 changes: 5 additions & 9 deletions src/app/base/components/PageContent/PageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Footer from "../Footer";
import MainContentSection from "../MainContentSection";
import SecondaryNavigation from "../SecondaryNavigation";

import type { AppSidePanelProps } from "app/base/components/AppSidePanel";
import { useThemeContext } from "app/base/theme-context";
import { preferencesNavItems } from "app/preferences/constants";
import { settingsNavItems } from "app/settings/constants";
Expand All @@ -20,9 +21,9 @@ export type Props = {
header?: ReactNode;
sidebar?: ReactNode;
isNotificationListHidden?: boolean;
sidePanelContent: React.ReactNode;
sidePanelSize?: "wide";
sidePanelTitle: string | null;
sidePanelContent: AppSidePanelProps["content"];
sidePanelSize?: AppSidePanelProps["size"];
sidePanelTitle: AppSidePanelProps["title"];
} & HTMLProps<HTMLDivElement>;

const PageContent = ({
Expand All @@ -31,7 +32,6 @@ const PageContent = ({
sidebar,
isNotificationListHidden = false,
sidePanelContent,
sidePanelSize,
sidePanelTitle,
...props
}: Props): JSX.Element => {
Expand Down Expand Up @@ -82,11 +82,7 @@ const PageContent = ({
<Footer />
</div>
</main>
<AppSidePanel
content={sidePanelContent}
size={sidePanelSize}
title={sidePanelTitle}
/>
<AppSidePanel content={sidePanelContent} title={sidePanelTitle} />
</>
);
};
Expand Down
24 changes: 24 additions & 0 deletions src/app/base/side-panel-context.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import SidePanelContextProvider, { useSidePanel } from "./side-panel-context";

import { renderHook, act } from "testing/utils";

it("resets side panel size on close", () => {
const wrapper = ({ children }: { children: React.ReactNode }) => (
<SidePanelContextProvider>{children}</SidePanelContextProvider>
);

const { result } = renderHook(() => useSidePanel(), { wrapper });

act(() => {
result.current.setSidePanelSize("large");
});

expect(result.current.sidePanelSize).toBe("large");

act(() => {
result.current.setSidePanelContent(null);
});

expect(result.current.sidePanelSize).toBe("regular");
expect(result.current.sidePanelContent).toBeNull();
});
Loading

0 comments on commit e5bb0da

Please sign in to comment.