From 33aa73c8f5c3e73da31f77197ea9f156411b1beb Mon Sep 17 00:00:00 2001 From: "Sode, Adedamola (DLSLtd,RAL,LSCI)" Date: Thu, 3 Sep 2026 09:37:10 +0100 Subject: [PATCH 1/4] Added labels to quickscreens --- src/components/QuickScreens/Display.tsx | 36 ++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/components/QuickScreens/Display.tsx b/src/components/QuickScreens/Display.tsx index ed30126..4449f44 100644 --- a/src/components/QuickScreens/Display.tsx +++ b/src/components/QuickScreens/Display.tsx @@ -54,7 +54,7 @@ export default function QuickScreenDisplay() { const location = useLocation(); const quickScreen = location.state?.pageState?.quickScreen; const bobQuickScreen = location.state?.pageState?.bobQuickScreen; - + console.log(bobQuickScreen, "bobQuickScreen"); const hasQuickScreen = !!quickScreen; const hasBobQuickScreen = !!bobQuickScreen; @@ -140,6 +140,23 @@ export default function QuickScreenDisplay() { }} /> + + + {quickScreen.path !== "/new.bob" + ? `Quick Screen: ${quickScreen?.path}` + : "Quick Screen"} + + setPendingCloseLocation(null)} @@ -188,6 +205,23 @@ export default function QuickScreenDisplay() { showCloseButton={true} targetDisplayType="displayGridLayout" /> + + + {bobQuickScreen?.macros?.label + ? `Bob Screen: ${bobQuickScreen?.macros?.label}` + : "Bob Screen"} + + )} From f02306966c575d7a3c9eda379a87e643d133a3d6 Mon Sep 17 00:00:00 2001 From: "Sode, Adedamola (DLSLtd,RAL,LSCI)" Date: Tue, 8 Sep 2026 14:16:17 +0100 Subject: [PATCH 2/4] Modified to use breadcrumbs --- src/components/QuickScreens/Display.tsx | 47 ++++++++++++++++----- src/components/QuickScreens/FileBrowser.tsx | 7 +++ 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/components/QuickScreens/Display.tsx b/src/components/QuickScreens/Display.tsx index 4449f44..cf50282 100644 --- a/src/components/QuickScreens/Display.tsx +++ b/src/components/QuickScreens/Display.tsx @@ -20,6 +20,9 @@ import { APP_BAR_HEIGHT, useWindowHeight } from "../../utils/helper"; +import { extractAncestorScreens } from "../../utils/screenUrlIdUtils"; +import { Breadcrumbs } from "@mui/material"; +import NavigateNextIcon from "@mui/icons-material/NavigateNext"; import { createContext, useContext, useState } from "react"; import QuickScreenSettings from "./Settings"; import { useLocation } from "react-router"; @@ -30,11 +33,15 @@ export const StorageContext = createContext<{ setBobDisplayUuid: any; browsingMode?: string; setBrowsingMode: any; + bobScreenUrlId?: string; + setBobScreenUrlId: React.Dispatch>; }>({ bobDisplayUuid: "", setBobDisplayUuid: () => null, browsingMode: "Load", - setBrowsingMode: () => null + setBrowsingMode: () => null, + bobScreenUrlId: undefined, + setBobScreenUrlId: () => null }); const Paper = styled(MuiPaper)(({ theme }) => ({ @@ -53,8 +60,13 @@ export default function QuickScreenDisplay() { const fileContext = useContext(FileContext); const location = useLocation(); const quickScreen = location.state?.pageState?.quickScreen; - const bobQuickScreen = location.state?.pageState?.bobQuickScreen; - console.log(bobQuickScreen, "bobQuickScreen"); + const bobQuickScreen = fileContext.pageState.bobQuickScreen; + const [bobScreenUrlId, setBobScreenUrlId] = useState(); + + const bobBreadcrumbs = bobScreenUrlId + ? extractAncestorScreens(bobScreenUrlId) + : []; + const hasQuickScreen = !!quickScreen; const hasBobQuickScreen = !!bobQuickScreen; @@ -85,7 +97,9 @@ export default function QuickScreenDisplay() { bobDisplayUuid, setBobDisplayUuid, browsingMode, - setBrowsingMode + setBrowsingMode, + bobScreenUrlId, + setBobScreenUrlId }} > @@ -152,9 +166,10 @@ export default function QuickScreenDisplay() { }} > + Quick Screen{" "} {quickScreen.path !== "/new.bob" - ? `Quick Screen: ${quickScreen?.path}` - : "Quick Screen"} + ? `: ${quickScreen?.path}` + : ""} - - {bobQuickScreen?.macros?.label - ? `Bob Screen: ${bobQuickScreen?.macros?.label}` - : "Bob Screen"} - + } + aria-label="Bob screen breadcrumb" + sx={{ color: "text.secondary", cursor: "default" }} + > + {bobBreadcrumbs.map(item => ( + + {item.displayName} + + ))} + )} diff --git a/src/components/QuickScreens/FileBrowser.tsx b/src/components/QuickScreens/FileBrowser.tsx index 181ee63..0c24f7b 100644 --- a/src/components/QuickScreens/FileBrowser.tsx +++ b/src/components/QuickScreens/FileBrowser.tsx @@ -20,6 +20,8 @@ import { LOAD_SCREENS } from "../../store"; import { parseScreenTree, ScreenTreeViewBaseItem } from "../../utils/parser"; import { useDispatch } from "react-redux"; import { executeOpenPageActionWithFileGuid } from "../../utils/csWebLibActions"; +import { useLocation, useNavigate } from "react-router"; +import { StorageContext } from "./Display"; /** * Custom Tree Item that lets us change icon @@ -57,6 +59,7 @@ export default function BobFileBrowser() { const dispatch = useDispatch(); const fileContext = useContext(FileContext); const { state } = useContext(BeamlineTreeStateContext); + const { setBobScreenUrlId } = useContext(StorageContext); const { showWarning } = useNotification(); const [bobFileTree, setBobFileTree] = useState([]); const [selectedItemId, setSelectedItemId] = useState(""); @@ -73,6 +76,10 @@ export default function BobFileBrowser() { if (!selectedBeamline) return; // Get current beamline const beamlineState = state.beamlines[selectedBeamline]; + const selectedFile = beamlineState.filePathIds[selectedItemId]; + + setBobScreenUrlId(selectedFile?.urlId); + executeOpenPageActionWithFileGuid( beamlineState, selectedItemId, From ca18f544a09e3ac890732e71d71e461f5b461fe7 Mon Sep 17 00:00:00 2001 From: "Sode, Adedamola (DLSLtd,RAL,LSCI)" Date: Tue, 8 Sep 2026 14:19:18 +0100 Subject: [PATCH 3/4] Linting changes --- src/components/QuickScreens/FileBrowser.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/QuickScreens/FileBrowser.tsx b/src/components/QuickScreens/FileBrowser.tsx index 0c24f7b..197f95d 100644 --- a/src/components/QuickScreens/FileBrowser.tsx +++ b/src/components/QuickScreens/FileBrowser.tsx @@ -20,7 +20,6 @@ import { LOAD_SCREENS } from "../../store"; import { parseScreenTree, ScreenTreeViewBaseItem } from "../../utils/parser"; import { useDispatch } from "react-redux"; import { executeOpenPageActionWithFileGuid } from "../../utils/csWebLibActions"; -import { useLocation, useNavigate } from "react-router"; import { StorageContext } from "./Display"; /** From 7394f553f92de9cc3e662f05b79dbc04f5e4fa4c Mon Sep 17 00:00:00 2001 From: "Sode, Adedamola (DLSLtd,RAL,LSCI)" Date: Fri, 11 Sep 2026 09:10:04 +0100 Subject: [PATCH 4/4] Added tests --- src/components/QuickScreens/Display.tsx | 4 +- .../components/QuickScreenDisplay.test.tsx | 65 +++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/components/QuickScreens/Display.tsx b/src/components/QuickScreens/Display.tsx index cf50282..79e6379 100644 --- a/src/components/QuickScreens/Display.tsx +++ b/src/components/QuickScreens/Display.tsx @@ -61,7 +61,9 @@ export default function QuickScreenDisplay() { const location = useLocation(); const quickScreen = location.state?.pageState?.quickScreen; const bobQuickScreen = fileContext.pageState.bobQuickScreen; - const [bobScreenUrlId, setBobScreenUrlId] = useState(); + const [bobScreenUrlId, setBobScreenUrlId] = useState( + location.state?.pageState?.bobScreenUrlId + ); const bobBreadcrumbs = bobScreenUrlId ? extractAncestorScreens(bobScreenUrlId) diff --git a/src/tests/components/QuickScreenDisplay.test.tsx b/src/tests/components/QuickScreenDisplay.test.tsx index bc7d5f5..bf8ab8e 100644 --- a/src/tests/components/QuickScreenDisplay.test.tsx +++ b/src/tests/components/QuickScreenDisplay.test.tsx @@ -9,6 +9,20 @@ const renderComponent = () => { const mockUseLocation = vi.fn(); +type MockBobQuickScreen = { + path: string; + macros: Record; + defaultProtocol: string; + saved?: boolean; +}; + +const mockFileContext = vi.hoisted(() => ({ + pageState: { + bobQuickScreen: undefined as MockBobQuickScreen | undefined + }, + removePage: vi.fn() +})); + const mockLocalStorage = { getItem: vi.fn(), setItem: vi.fn(), @@ -24,6 +38,7 @@ Object.defineProperty(window, "localStorage", { beforeEach(() => { vi.clearAllMocks(); mockLocalStorage.clear(); + mockFileContext.pageState.bobQuickScreen = undefined; }); vi.mock("react-router", async () => { @@ -37,8 +52,11 @@ vi.mock("react-router", async () => { vi.mock("@diamondlightsource/cs-web-lib", async () => { const actual = await vi.importActual("@diamondlightsource/cs-web-lib"); + const { createContext } = await import("react"); + return { ...actual, + FileContext: createContext(mockFileContext), DynamicPageWidget: (props: any) => { vi.fn(props); return
; @@ -75,7 +93,7 @@ describe("", () => { expect(queryByText("No Quick Screen loaded")).not.toBeInTheDocument(); }); - it("Displays a dialog box before closing on unsaved quick screen", () => { + it("displays a dialog box before closing on unsaved quick screen", () => { mockUseLocation.mockReturnValue({ state: { pageState: { @@ -97,15 +115,14 @@ describe("", () => { ).toBeInTheDocument(); }); - it("Doesn't display a dialog box when closing a saved quick screen", () => { + it("doesn't display a dialog box when closing a saved quick screen", () => { mockUseLocation.mockReturnValue({ state: { pageState: { quickScreen: { path: "wow.bob", macros: {}, - defaultProtocol: "ca", - saved: true + defaultProtocol: "ca" } } } @@ -123,4 +140,44 @@ describe("", () => { ) ).not.toBeInTheDocument(); }); + + it("shows name of quick screen when opened", () => { + mockUseLocation.mockReturnValue({ + state: { + pageState: { + quickScreen: { + path: "wow.bob", + macros: {}, + defaultProtocol: "ca" + } + } + } + }); + + const { getByText } = renderComponent(); + + expect(getByText("Quick Screen : wow.bob")).toBeInTheDocument(); + }); + + it("shows the breadcrumbs of the bobquickscreen when opened", () => { + mockFileContext.pageState.bobQuickScreen = { + path: "wow.bob", + macros: {}, + defaultProtocol: "ca" + }; + + mockUseLocation.mockReturnValue({ + state: { + pageState: { + bobScreenUrlId: "Page 1/Page 2/Motor X" + } + } + }); + + const { getByText } = renderComponent(); + + expect(getByText("Page 1")).toBeInTheDocument(); + expect(getByText("Page 2")).toBeInTheDocument(); + expect(getByText("Motor X")).toBeInTheDocument(); + }); });