diff --git a/src/components/QuickScreens/Display.tsx b/src/components/QuickScreens/Display.tsx index ed30126..79e6379 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,7 +60,14 @@ export default function QuickScreenDisplay() { const fileContext = useContext(FileContext); const location = useLocation(); const quickScreen = location.state?.pageState?.quickScreen; - const bobQuickScreen = location.state?.pageState?.bobQuickScreen; + const bobQuickScreen = fileContext.pageState.bobQuickScreen; + const [bobScreenUrlId, setBobScreenUrlId] = useState( + location.state?.pageState?.bobScreenUrlId + ); + + const bobBreadcrumbs = bobScreenUrlId + ? extractAncestorScreens(bobScreenUrlId) + : []; const hasQuickScreen = !!quickScreen; const hasBobQuickScreen = !!bobQuickScreen; @@ -85,7 +99,9 @@ export default function QuickScreenDisplay() { bobDisplayUuid, setBobDisplayUuid, browsingMode, - setBrowsingMode + setBrowsingMode, + bobScreenUrlId, + setBobScreenUrlId }} > @@ -140,6 +156,24 @@ export default function QuickScreenDisplay() { }} /> + + + Quick Screen{" "} + {quickScreen.path !== "/new.bob" + ? `: ${quickScreen?.path}` + : ""} + + setPendingCloseLocation(null)} @@ -188,6 +222,33 @@ export default function QuickScreenDisplay() { showCloseButton={true} targetDisplayType="displayGridLayout" /> + + } + 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..197f95d 100644 --- a/src/components/QuickScreens/FileBrowser.tsx +++ b/src/components/QuickScreens/FileBrowser.tsx @@ -20,6 +20,7 @@ import { LOAD_SCREENS } from "../../store"; import { parseScreenTree, ScreenTreeViewBaseItem } from "../../utils/parser"; import { useDispatch } from "react-redux"; import { executeOpenPageActionWithFileGuid } from "../../utils/csWebLibActions"; +import { StorageContext } from "./Display"; /** * Custom Tree Item that lets us change icon @@ -57,6 +58,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 +75,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, 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(); + }); });