diff --git a/package.json b/package.json
index 67ea73e5db..db691519c7 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"build-storybook": "storybook build"
},
"dependencies": {
- "@canonical/maas-react-components": "1.14.0",
+ "@canonical/maas-react-components": "1.15.0",
"@canonical/macaroon-bakery": "1.3.2",
"@canonical/react-components": "0.46.0",
"@reduxjs/toolkit": "1.9.3",
diff --git a/src/app/devices/views/DeviceList/DeviceList.test.tsx b/src/app/devices/views/DeviceList/DeviceList.test.tsx
index eef5558355..dc42d67bb7 100644
--- a/src/app/devices/views/DeviceList/DeviceList.test.tsx
+++ b/src/app/devices/views/DeviceList/DeviceList.test.tsx
@@ -23,9 +23,7 @@ describe("DeviceList", () => {
route: "/devices?q=test+search",
state,
});
- expect(screen.getByRole("searchbox", { name: "Search" })).toHaveValue(
- "test search"
- );
+ expect(screen.getByRole("searchbox")).toHaveValue("test search");
});
it("changes the URL when the search text changes", async () => {
@@ -42,11 +40,8 @@ describe("DeviceList", () => {
>,
{ route: "/machines?q=test+search", state }
);
- await userEvent.clear(screen.getByRole("searchbox", { name: "Search" }));
- await userEvent.type(
- screen.getByRole("searchbox", { name: "Search" }),
- "hostname:foo"
- );
+ await userEvent.clear(screen.getByRole("searchbox"));
+ await userEvent.type(screen.getByRole("searchbox"), "hostname:foo");
await waitFor(() => {
expect(search).toBe("?hostname=foo");
diff --git a/src/app/devices/views/DeviceList/DeviceList.tsx b/src/app/devices/views/DeviceList/DeviceList.tsx
index 88f4478212..feedb4ed9c 100644
--- a/src/app/devices/views/DeviceList/DeviceList.tsx
+++ b/src/app/devices/views/DeviceList/DeviceList.tsx
@@ -4,7 +4,6 @@ import { useDispatch, useSelector } from "react-redux";
import { useLocation } from "react-router";
import { useNavigate } from "react-router-dom-v5-compat";
-import DeviceListControls from "./DeviceListControls";
import DeviceListHeader from "./DeviceListHeader";
import DeviceListTable from "./DeviceListTable";
@@ -53,6 +52,7 @@ const DeviceList = (): JSX.Element => {
@@ -68,7 +68,6 @@ const DeviceList = (): JSX.Element => {
}
sidePanelTitle={getSidePanelTitle("Devices", sidePanelContent)}
>
-
{
- let state: RootState = rootStateFactory();
-
- it("changes the search text when the filters change", () => {
- const { rerender } = renderWithBrowserRouter(
- ,
- { route: "/machines?q=test+search", state }
- );
- expect(screen.getByRole("searchbox")).toHaveValue("");
-
- rerender();
-
- expect(screen.getByRole("searchbox")).toHaveValue("free-text");
- });
-});
diff --git a/src/app/devices/views/DeviceList/DeviceListControls/DeviceListControls.tsx b/src/app/devices/views/DeviceList/DeviceListControls/DeviceListControls.tsx
deleted file mode 100644
index a6215f3ba0..0000000000
--- a/src/app/devices/views/DeviceList/DeviceListControls/DeviceListControls.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { useEffect, useState } from "react";
-
-import { Col, Row } from "@canonical/react-components";
-
-import DeviceFilterAccordion from "./DeviceFilterAccordion";
-
-import DebounceSearchBox from "app/base/components/DebounceSearchBox";
-import type { SetSearchFilter } from "app/base/types";
-
-type Props = {
- filter: string;
- setFilter: SetSearchFilter;
-};
-
-const DeviceListControls = ({ filter, setFilter }: Props): JSX.Element => {
- const [searchText, setSearchText] = useState(filter);
-
- useEffect(() => {
- // If the filters change then update the search input text.
- setSearchText(filter);
- }, [filter]);
-
- return (
-
-
-
-
-
- setFilter(debouncedText)}
- searchText={searchText}
- setSearchText={setSearchText}
- />
-
-
- );
-};
-
-export default DeviceListControls;
diff --git a/src/app/devices/views/DeviceList/DeviceListControls/index.ts b/src/app/devices/views/DeviceList/DeviceListControls/index.ts
deleted file mode 100644
index bc273122ff..0000000000
--- a/src/app/devices/views/DeviceList/DeviceListControls/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from "./DeviceListControls";
diff --git a/src/app/devices/views/DeviceList/DeviceListControls/DeviceFilterAccordion/DeviceFilterAccordion.test.tsx b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceFilterAccordion/DeviceFilterAccordion.test.tsx
similarity index 100%
rename from src/app/devices/views/DeviceList/DeviceListControls/DeviceFilterAccordion/DeviceFilterAccordion.test.tsx
rename to src/app/devices/views/DeviceList/DeviceListHeader/DeviceFilterAccordion/DeviceFilterAccordion.test.tsx
diff --git a/src/app/devices/views/DeviceList/DeviceListControls/DeviceFilterAccordion/DeviceFilterAccordion.tsx b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceFilterAccordion/DeviceFilterAccordion.tsx
similarity index 100%
rename from src/app/devices/views/DeviceList/DeviceListControls/DeviceFilterAccordion/DeviceFilterAccordion.tsx
rename to src/app/devices/views/DeviceList/DeviceListHeader/DeviceFilterAccordion/DeviceFilterAccordion.tsx
diff --git a/src/app/devices/views/DeviceList/DeviceListControls/DeviceFilterAccordion/index.ts b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceFilterAccordion/index.ts
similarity index 100%
rename from src/app/devices/views/DeviceList/DeviceListControls/DeviceFilterAccordion/index.ts
rename to src/app/devices/views/DeviceList/DeviceListHeader/DeviceFilterAccordion/index.ts
diff --git a/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.test.tsx b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.test.tsx
index a007f15809..b228b9b505 100644
--- a/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.test.tsx
+++ b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.test.tsx
@@ -30,6 +30,7 @@ describe("DeviceListHeader", () => {
state.device.loaded = false;
renderWithBrowserRouter(
,
@@ -42,26 +43,26 @@ describe("DeviceListHeader", () => {
state.device.loaded = true;
renderWithBrowserRouter(
,
{ state }
);
- expect(screen.getByTestId("section-header-subtitle")).toHaveTextContent(
- "2 devices available"
- );
+ expect(screen.getByText("2 devices available")).toBeInTheDocument();
});
it("disables the add device button if any devices are selected", () => {
state.device.selected = ["abc123"];
renderWithBrowserRouter(
,
{ state }
);
- expect(screen.getByTestId("add-device-button")).toBeDisabled();
+ expect(screen.getByRole("button", { name: "Add device" })).toBeDisabled();
});
it("can open the add device form", async () => {
@@ -69,15 +70,39 @@ describe("DeviceListHeader", () => {
renderWithBrowserRouter(
,
{ state }
);
- await userEvent.click(screen.getByTestId("add-device-button"));
+ await userEvent.click(screen.getByRole("button", { name: "Add device" }));
expect(setSidePanelContent).toHaveBeenCalledWith({
view: DeviceSidePanelViews.ADD_DEVICE,
});
});
+
+ it("changes the search text when the filters change", () => {
+ const { rerender } = renderWithBrowserRouter(
+ ,
+ { state }
+ );
+
+ expect(screen.getByRole("searchbox")).toHaveValue("");
+
+ rerender(
+
+ );
+
+ expect(screen.getByRole("searchbox")).toHaveValue("free-text");
+ });
});
diff --git a/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.tsx b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.tsx
index c63ab8560f..0f62cf0b2d 100644
--- a/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.tsx
+++ b/src/app/devices/views/DeviceList/DeviceListHeader/DeviceListHeader.tsx
@@ -1,30 +1,67 @@
-import { Button } from "@canonical/react-components";
+import { useEffect, useState } from "react";
+
+import { MainToolbar } from "@canonical/maas-react-components";
+import { Button, Col, Spinner } from "@canonical/react-components";
import { useSelector } from "react-redux";
+import DeviceFilterAccordion from "./DeviceFilterAccordion";
+
+import DebounceSearchBox from "app/base/components/DebounceSearchBox";
import ModelListSubtitle from "app/base/components/ModelListSubtitle";
import NodeActionMenu from "app/base/components/NodeActionMenu";
-import SectionHeader from "app/base/components/SectionHeader";
import type { SetSearchFilter } from "app/base/types";
import { DeviceSidePanelViews } from "app/devices/constants";
import type { DeviceSetSidePanelContent } from "app/devices/types";
import deviceSelectors from "app/store/device/selectors";
type Props = {
+ searchFilter: string;
setSidePanelContent: DeviceSetSidePanelContent;
setSearchFilter: SetSearchFilter;
};
const DeviceListHeader = ({
+ searchFilter,
setSidePanelContent,
setSearchFilter,
}: Props): JSX.Element => {
const devices = useSelector(deviceSelectors.all);
const devicesLoaded = useSelector(deviceSelectors.loaded);
const selectedDevices = useSelector(deviceSelectors.selected);
+ const [searchText, setSearchText] = useState(searchFilter);
+
+ useEffect(() => {
+ // If the filters change then update the search input text.
+ setSearchText(searchFilter);
+ }, [searchFilter]);
return (
-
+
+ Devices
+
+ {devicesLoaded ? (
+ setSearchFilter("in:(Selected)")}
+ modelName="device"
+ selected={selectedDevices.length}
+ />
+ ) : (
+
+ )}
+
+
+
+
+ setSearchFilter(debouncedText)}
+ searchText={searchText}
+ setSearchText={setSearchText}
+ />
,
+
0}
@@ -48,19 +85,9 @@ const DeviceListHeader = ({
}
}}
showCount
- />,
- ]}
- subtitle={
- setSearchFilter("in:(Selected)")}
- modelName="device"
- selected={selectedDevices.length}
/>
- }
- subtitleLoading={!devicesLoaded}
- title="Devices"
- />
+
+
);
};
diff --git a/src/scss/_vanilla-overrides.scss b/src/scss/_vanilla-overrides.scss
index 2cfe67151d..09d5af2231 100644
--- a/src/scss/_vanilla-overrides.scss
+++ b/src/scss/_vanilla-overrides.scss
@@ -35,3 +35,11 @@
.p-tooltip__message {
z-index: $side-navigation-z-index + 1;
}
+
+// 05-01-2024 Nick: Using MainToolbar without ContentSection means no padding at top of page.
+// Will be fixed once all main pages use MainToolbar and ContentSection is implemented
+// https://warthogs.atlassian.net/browse/MAASENG-2440
+// https://github.com/canonical/maas-ui/pull/5244
+.main-toolbar {
+ padding-top: $spv--medium;
+}
diff --git a/yarn.lock b/yarn.lock
index 6220f89da2..092b17734b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2907,10 +2907,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
-"@canonical/maas-react-components@1.14.0":
- version "1.14.0"
- resolved "https://registry.yarnpkg.com/@canonical/maas-react-components/-/maas-react-components-1.14.0.tgz#3347a28cc968ed8a13a5e501ec6ebde11a0274c5"
- integrity sha512-ApSpDchiCn8EJ597Er/yMeAzc4+iuphU0eXCmmZVcMFCSl2iQqJ6NncA1eCRPgBN7QQs++wqHJ5ry0VPHup0vA==
+"@canonical/maas-react-components@1.15.0":
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/@canonical/maas-react-components/-/maas-react-components-1.15.0.tgz#c14ec75eeb2bfe2757e45fb3bebf67689865e4df"
+ integrity sha512-0KQ0CXi6D2PURVUerRR/0CAitp3Qf7olUIJwXZnrePlLyas9zmnZ2HyVydlS/z5YptaHExqh/EQT9AGln/3T+A==
"@canonical/macaroon-bakery@1.3.2":
version "1.3.2"