From 09ea1afae8117a834e1e3561e56f42d6875ba21c Mon Sep 17 00:00:00 2001 From: heswell Date: Thu, 19 Oct 2023 00:41:16 +0100 Subject: [PATCH] add Layout Management Provider to sample apps (#917) * add Layout Management Provider to sample apps * fix test dependencies --- .../src/layout-persistence/index.ts | 7 +- .../useLayoutContextMenuItems.tsx | 80 +++++++ .../packages/vuu-layout/test/global-mocks.ts | 23 ++ .../LocalLayoutPersistenceManager.test.ts | 217 +++++++++++------- vuu-ui/packages/vuu-shell/src/shell.tsx | 11 +- .../instrument-search/InstrumentSearch.tsx | 2 +- .../app-vuu-basket-trader/index.tsx | 10 +- .../app-vuu-basket-trader/src/App.tsx | 85 ++++--- vuu-ui/sample-apps/app-vuu-example/index.tsx | 10 +- .../sample-apps/app-vuu-example/src/App.css | 7 + .../sample-apps/app-vuu-example/src/App.tsx | 35 +-- .../app-vuu-example/src/AppStack.tsx | 22 -- .../app-vuu-example/src/createPlaceholder.tsx | 13 ++ .../src/examples/Apps/NewTheme.examples.tsx | 89 +------ vuu-ui/tsconfig.json | 2 +- 15 files changed, 353 insertions(+), 260 deletions(-) create mode 100644 vuu-ui/packages/vuu-layout/src/layout-persistence/useLayoutContextMenuItems.tsx create mode 100644 vuu-ui/packages/vuu-layout/test/global-mocks.ts delete mode 100644 vuu-ui/sample-apps/app-vuu-example/src/AppStack.tsx create mode 100644 vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx diff --git a/vuu-ui/packages/vuu-layout/src/layout-persistence/index.ts b/vuu-ui/packages/vuu-layout/src/layout-persistence/index.ts index a047506db..10bbeed00 100644 --- a/vuu-ui/packages/vuu-layout/src/layout-persistence/index.ts +++ b/vuu-ui/packages/vuu-layout/src/layout-persistence/index.ts @@ -1,3 +1,4 @@ -export * from './LayoutPersistenceManager'; -export * from './LocalLayoutPersistenceManager'; -export * from './data'; \ No newline at end of file +export * from "./data"; +export * from "./LayoutPersistenceManager"; +export * from "./LocalLayoutPersistenceManager"; +export * from "./useLayoutContextMenuItems"; diff --git a/vuu-ui/packages/vuu-layout/src/layout-persistence/useLayoutContextMenuItems.tsx b/vuu-ui/packages/vuu-layout/src/layout-persistence/useLayoutContextMenuItems.tsx new file mode 100644 index 000000000..6d47585fd --- /dev/null +++ b/vuu-ui/packages/vuu-layout/src/layout-persistence/useLayoutContextMenuItems.tsx @@ -0,0 +1,80 @@ +import { + LayoutMetadata, + SaveLayoutPanel, + useLayoutManager, +} from "@finos/vuu-shell"; +import { + ContextMenuItemDescriptor, + MenuActionHandler, + MenuBuilder, +} from "@finos/vuu-data-types"; +import { ReactElement, useCallback, useMemo, useState } from "react"; +import { MenuActionClosePopup } from "@finos/vuu-popups"; + +export const useLayoutContextMenuItems = () => { + const [dialogContent, setDialogContent] = useState(); + + const { saveLayout } = useLayoutManager(); + + const handleCloseDialog = useCallback(() => { + setDialogContent(undefined); + }, []); + + const handleSave = useCallback( + (layoutMetadata: Omit) => { + saveLayout(layoutMetadata); + setDialogContent(undefined); + }, + [saveLayout] + ); + + const [buildMenuOptions, handleMenuAction] = useMemo< + [MenuBuilder, MenuActionHandler] + >(() => { + return [ + (location, options) => { + console.log({ options }); + const locations = location.split(" "); + const menuDescriptors: ContextMenuItemDescriptor[] = []; + if (locations.includes("main-tab")) { + menuDescriptors.push( + { + label: "Save Layout", + action: "save-layout", + options, + }, + { + label: "Layout Settings", + action: "layout-settings", + options, + } + ); + } + return menuDescriptors; + }, + (action: MenuActionClosePopup) => { + console.log("menu action", { + action, + }); + if (action.menuId === "save-layout") { + setDialogContent( + + ); + return true; + } + return false; + }, + ]; + }, [handleCloseDialog, handleSave]); + + return { + buildMenuOptions, + dialogContent, + handleCloseDialog, + handleMenuAction, + }; +}; diff --git a/vuu-ui/packages/vuu-layout/test/global-mocks.ts b/vuu-ui/packages/vuu-layout/test/global-mocks.ts new file mode 100644 index 000000000..705371d37 --- /dev/null +++ b/vuu-ui/packages/vuu-layout/test/global-mocks.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { vi } from "vitest"; + +const BlobMock = vi.fn(() => ({})); +const URLMock = { + createObjectURL: () => ({}), +}; +vi.stubGlobal("Blob", BlobMock); +vi.stubGlobal("URL", URLMock); +vi.stubGlobal("loggingSettings", { loggingLevel: "error" }); + +vi.mock("@finos/vuu-utils", async () => { + const actual = await vi.importActual("@finos/vuu-utils"); + return { + // @ts-ignore + ...actual, + uuid: () => "uuid-1", + }; +}); + +vi.mock("./inlined-worker", async () => ({ + workerSourceCode: "", +})); diff --git a/vuu-ui/packages/vuu-layout/test/layout-persistence/LocalLayoutPersistenceManager.test.ts b/vuu-ui/packages/vuu-layout/test/layout-persistence/LocalLayoutPersistenceManager.test.ts index 1b08d6072..a94ccc200 100644 --- a/vuu-ui/packages/vuu-layout/test/layout-persistence/LocalLayoutPersistenceManager.test.ts +++ b/vuu-ui/packages/vuu-layout/test/layout-persistence/LocalLayoutPersistenceManager.test.ts @@ -1,8 +1,12 @@ +import "../global-mocks"; import { Layout, LayoutMetadata } from "@finos/vuu-shell"; import { afterEach, describe, expect, it, vi } from "vitest"; import { LocalLayoutPersistenceManager } from "../../src/layout-persistence"; import { LayoutJSON } from "../../src/layout-reducer"; -import { getLocalEntity, saveLocalEntity } from "../../../vuu-filters/src/local-config"; +import { + getLocalEntity, + saveLocalEntity, +} from "../../../vuu-filters/src/local-config"; vi.mock("@finos/vuu-filters", async () => { return { @@ -18,7 +22,7 @@ vi.mock("@finos/vuu-filters", async () => { return undefined; } }, - } + }; }); const persistenceManager = new LocalLayoutPersistenceManager(); @@ -36,7 +40,7 @@ const existingMetadata: LayoutMetadata = { const existingLayout: Layout = { id: existingId, - json: { type: "t0" } + json: { type: "t0" }, }; const metadataToAdd: Omit = { @@ -56,24 +60,27 @@ const layoutsSaveLocation = "layouts/layouts"; afterEach(() => { localStorage.clear(); -}) +}); describe("createLayout", () => { - it("persists to local storage with a unique ID", async () => { - const returnedId = await persistenceManager.createLayout(metadataToAdd, layoutToAdd); + const returnedId = await persistenceManager.createLayout( + metadataToAdd, + layoutToAdd + ); - const persistedMetadata = getLocalEntity(metadataSaveLocation); + const persistedMetadata = + getLocalEntity(metadataSaveLocation); const persistedLayout = getLocalEntity(layoutsSaveLocation); const expectedMetadata: LayoutMetadata = { ...metadataToAdd, - id: returnedId + id: returnedId, }; const expectedLayout: Layout = { json: layoutToAdd, - id: returnedId + id: returnedId, }; expect(persistedMetadata).toEqual([expectedMetadata]); @@ -84,10 +91,14 @@ describe("createLayout", () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout]); - const returnedId = await persistenceManager.createLayout(metadataToAdd, layoutToAdd); + const returnedId = await persistenceManager.createLayout( + metadataToAdd, + layoutToAdd + ); expect(returnedId).not.toEqual(existingId); - const persistedMetadata = getLocalEntity(metadataSaveLocation); + const persistedMetadata = + getLocalEntity(metadataSaveLocation); const persistedLayout = getLocalEntity(layoutsSaveLocation); const expectedMetadata: LayoutMetadata = { @@ -106,14 +117,18 @@ describe("createLayout", () => { }); describe("updateLayout", () => { - it("updates an existing layout", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout]); - await persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd); + await persistenceManager.updateLayout( + existingId, + metadataToAdd, + layoutToAdd + ); - const persistedMetadata = getLocalEntity(metadataSaveLocation); + const persistedMetadata = + getLocalEntity(metadataSaveLocation); const persistedLayout = getLocalEntity(layoutsSaveLocation); const expectedMetadata: LayoutMetadata = { @@ -133,156 +148,183 @@ describe("updateLayout", () => { it("errors if there is no metadata in local storage with requested ID ", async () => { saveLocalEntity(layoutsSaveLocation, [existingLayout]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `No metadata with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `No metadata with ID ${existingId}` + ); }); it("errors if there is no layout in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `No layout with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `No layout with ID ${existingId}` + ); }); it("errors if there is no metadata or layout in local storage with requested ID ", async () => { const requestedId = "non_existent_id"; - expectError(() => - persistenceManager.updateLayout(requestedId, metadataToAdd, layoutToAdd), - `No metadata with ID ${requestedId}; No layout with ID ${requestedId}`); + expectError( + () => + persistenceManager.updateLayout( + requestedId, + metadataToAdd, + layoutToAdd + ), + `No metadata with ID ${requestedId}; No layout with ID ${requestedId}` + ); }); it("errors if there are multiple metadata entries in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `Non-unique metadata with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `Non-unique metadata with ID ${existingId}` + ); }); it("errors if there are multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `Non-unique layout with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `Non-unique layout with ID ${existingId}` + ); }); it("errors if there are multiple metadata entries and multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `Non-unique metadata with ID ${existingId}; Non-unique layout with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `Non-unique metadata with ID ${existingId}; Non-unique layout with ID ${existingId}` + ); }); it("errors if there are multiple metadata entries and no layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `Non-unique metadata with ID ${existingId}; No layout with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `Non-unique metadata with ID ${existingId}; No layout with ID ${existingId}` + ); }); it("errors if there are no metadata entries and multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), - `No metadata with ID ${existingId}; Non-unique layout with ID ${existingId}`); + expectError( + () => + persistenceManager.updateLayout(existingId, metadataToAdd, layoutToAdd), + `No metadata with ID ${existingId}; Non-unique layout with ID ${existingId}` + ); }); }); describe("deleteLayout", () => { - it("removes items from storage", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout]); await persistenceManager.deleteLayout(existingId); - const persistedMetadata = getLocalEntity(metadataSaveLocation); + const persistedMetadata = + getLocalEntity(metadataSaveLocation); const persistedLayouts = getLocalEntity(layoutsSaveLocation); expect(persistedMetadata).toEqual([]); expect(persistedLayouts).toEqual([]); - }) + }); it("errors if there is no metadata in local storage with requested ID ", async () => { saveLocalEntity(layoutsSaveLocation, [existingLayout]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `No metadata with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `No metadata with ID ${existingId}` + ); }); it("errors if there is no layout in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `No layout with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `No layout with ID ${existingId}` + ); }); it("errors if there is no metadata or layout in local storage with requested ID ", async () => { const requestedId = "non_existent_id"; - expectError(() => - persistenceManager.deleteLayout(requestedId), - `No metadata with ID ${requestedId}; No layout with ID ${requestedId}`); + expectError( + () => persistenceManager.deleteLayout(requestedId), + `No metadata with ID ${requestedId}; No layout with ID ${requestedId}` + ); }); it("errors if there are multiple metadata entries in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `Non-unique metadata with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `Non-unique metadata with ID ${existingId}` + ); }); it("errors if there are multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `Non-unique layout with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `Non-unique layout with ID ${existingId}` + ); }); it("errors if there are multiple metadata entries and multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `Non-unique metadata with ID ${existingId}; Non-unique layout with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `Non-unique metadata with ID ${existingId}; Non-unique layout with ID ${existingId}` + ); }); it("errors if there are multiple metadata entries and no layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `Non-unique metadata with ID ${existingId}; No layout with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `Non-unique metadata with ID ${existingId}; No layout with ID ${existingId}` + ); }); it("errors if there are no metadata entries and multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.deleteLayout(existingId), - `No metadata with ID ${existingId}; Non-unique layout with ID ${existingId}`); + expectError( + () => persistenceManager.deleteLayout(existingId), + `No metadata with ID ${existingId}; Non-unique layout with ID ${existingId}` + ); }); }); describe("loadLayout", () => { - it("retrieves a persisted layout", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout]); @@ -303,17 +345,19 @@ describe("loadLayout", () => { it("errors if there is no layout in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); - expectError(() => - persistenceManager.loadLayout(existingId), - `No layout with ID ${existingId}`); + expectError( + () => persistenceManager.loadLayout(existingId), + `No layout with ID ${existingId}` + ); }); it("errors if there is no metadata or layout in local storage with requested ID ", async () => { const requestedId = "non_existent_id"; - expectError(() => - persistenceManager.loadLayout(requestedId), - `No layout with ID ${requestedId}`); + expectError( + () => persistenceManager.loadLayout(requestedId), + `No layout with ID ${requestedId}` + ); }); it("retrieves layout if there are multiple metadata entries in local storage with requested ID ", async () => { @@ -329,39 +373,42 @@ describe("loadLayout", () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.loadLayout(existingId), - `Non-unique layout with ID ${existingId}`); + expectError( + () => persistenceManager.loadLayout(existingId), + `Non-unique layout with ID ${existingId}` + ); }); it("errors if there are multiple metadata entries and multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.loadLayout(existingId), - `Non-unique layout with ID ${existingId}`); + expectError( + () => persistenceManager.loadLayout(existingId), + `Non-unique layout with ID ${existingId}` + ); }); it("errors if there are multiple metadata entries and no layouts in local storage with requested ID ", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata, existingMetadata]); - expectError(() => - persistenceManager.loadLayout(existingId), - `No layout with ID ${existingId}`); + expectError( + () => persistenceManager.loadLayout(existingId), + `No layout with ID ${existingId}` + ); }); it("errors if there are no metadata entries and multiple layouts in local storage with requested ID ", async () => { saveLocalEntity(layoutsSaveLocation, [existingLayout, existingLayout]); - expectError(() => - persistenceManager.loadLayout(existingId), - `Non-unique layout with ID ${existingId}`); + expectError( + () => persistenceManager.loadLayout(existingId), + `Non-unique layout with ID ${existingId}` + ); }); }); describe("loadMetadata", () => { - it("retrieves array of persisted layout metadata", async () => { saveLocalEntity(metadataSaveLocation, [existingMetadata]); diff --git a/vuu-ui/packages/vuu-shell/src/shell.tsx b/vuu-ui/packages/vuu-shell/src/shell.tsx index 6fd9be378..4c2f03300 100644 --- a/vuu-ui/packages/vuu-shell/src/shell.tsx +++ b/vuu-ui/packages/vuu-shell/src/shell.tsx @@ -13,10 +13,7 @@ import { LayoutProvider, LayoutProviderProps, } from "@finos/vuu-layout"; -import { - LayoutChangeHandler, - LayoutJSON, -} from "@finos/vuu-layout/src/layout-reducer"; +import { LayoutChangeHandler } from "@finos/vuu-layout/src/layout-reducer"; import { AppHeader } from "./app-header"; import { ThemeMode, ThemeProvider, useThemeAttributes } from "./theme-provider"; import { logger } from "@finos/vuu-utils"; @@ -64,7 +61,8 @@ export const Shell = ({ }: ShellProps) => { const rootRef = useRef(null); const layoutId = useRef("latest"); - const { applicationLayout, saveApplicationLayout, loadLayoutById } = useLayoutManager(); + const { applicationLayout, saveApplicationLayout, loadLayoutById } = + useLayoutManager(); const handleLayoutChange = useCallback( (layout, layoutChangeReason) => { @@ -76,7 +74,7 @@ export const Shell = ({ error?.("Failed to save layout"); } }, - [applicationLayout] + [saveApplicationLayout] ); const handleSwitchTheme = useCallback((mode: ThemeMode) => { @@ -123,6 +121,7 @@ export const Shell = ({ return ( diff --git a/vuu-ui/packages/vuu-ui-controls/src/instrument-search/InstrumentSearch.tsx b/vuu-ui/packages/vuu-ui-controls/src/instrument-search/InstrumentSearch.tsx index c5eb364cf..34fc2e1a0 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/instrument-search/InstrumentSearch.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/instrument-search/InstrumentSearch.tsx @@ -92,4 +92,4 @@ export const InstrumentSearch = ({ ); }; -registerComponent("InstrumentSearch", InstrumentSearch, "view"); +registerComponent?.("InstrumentSearch", InstrumentSearch, "view"); diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx index 8d3a9bc4d..e2cd5be6f 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx +++ b/vuu-ui/sample-apps/app-vuu-basket-trader/index.tsx @@ -1,4 +1,8 @@ -import { getAuthDetailsFromCookies, redirectToLogin } from "@finos/vuu-shell"; +import { + getAuthDetailsFromCookies, + LayoutManagementProvider, + redirectToLogin, +} from "@finos/vuu-shell"; import React from "react"; import ReactDOM from "react-dom"; import { App } from "./src/App"; @@ -12,7 +16,9 @@ if (!username || !token) { redirectToLogin(); } else { ReactDOM.render( - , + + + , document.getElementById("root") ); } diff --git a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx b/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx index a445555ee..061618e8f 100644 --- a/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx +++ b/vuu-ui/sample-apps/app-vuu-basket-trader/src/App.tsx @@ -1,4 +1,4 @@ -import { Dialog } from "@finos/vuu-popups"; +import { ContextMenuProvider, Dialog } from "@finos/vuu-popups"; import { LeftNav, Shell, @@ -6,16 +6,18 @@ import { ShellProps, VuuUser, } from "@finos/vuu-shell"; -import { ReactElement, useRef, useState } from "react"; +import { ReactElement, useCallback, useRef, useState } from "react"; import { getDefaultColumnConfig } from "./columnMetaData"; import { createPlaceholder } from "./createPlaceholder"; -import { defaultLayout } from "./defaultLayout"; import { useFeatures } from "./useFeatures"; import { ColumnSettingsPanel, TableSettingsPanel, } from "@finos/vuu-table-extras"; -import { registerComponent } from "@finos/vuu-layout"; +import { + registerComponent, + useLayoutContextMenuItems, +} from "@finos/vuu-layout"; import "./App.css"; @@ -40,9 +42,6 @@ const { export const App = ({ user }: { user: VuuUser }) => { const dialogTitleRef = useRef(""); const [dialogContent, setDialogContent] = useState(); - const handleClose = () => { - setDialogContent(undefined); - }; const [features, tableFeatures] = useFeatures({ features: configuredFeatures, @@ -50,35 +49,53 @@ export const App = ({ user }: { user: VuuUser }) => { console.log({ features, tableFeatures }); + const { + buildMenuOptions, + dialogContent: saveLayoutDialog, + handleCloseDialog, + handleMenuAction, + } = useLayoutContextMenuItems(); + + const handleClose = useCallback(() => { + setDialogContent(undefined); + handleCloseDialog?.(); + }, [handleCloseDialog]); + // TODO get Context from Shell return ( - - - } - saveUrl="https://localhost:8443/api/vui" - serverUrl={serverUrl} - user={user} - > - + + + } + saveUrl="https://localhost:8443/api/vui" + serverUrl={serverUrl} + user={user} > - {dialogContent} - - - + + {dialogContent ?? saveLayoutDialog} + + + + ); }; diff --git a/vuu-ui/sample-apps/app-vuu-example/index.tsx b/vuu-ui/sample-apps/app-vuu-example/index.tsx index df995215e..770db3202 100644 --- a/vuu-ui/sample-apps/app-vuu-example/index.tsx +++ b/vuu-ui/sample-apps/app-vuu-example/index.tsx @@ -1,7 +1,11 @@ import React from "react"; import ReactDOM from "react-dom"; import { App } from "./src/App"; -import { getAuthDetailsFromCookies, redirectToLogin } from "@finos/vuu-shell"; +import { + getAuthDetailsFromCookies, + LayoutManagementProvider, + redirectToLogin, +} from "@finos/vuu-shell"; import "@salt-ds/theme/index.css"; import "@finos/vuu-icons/index.css"; @@ -12,7 +16,9 @@ if (!username || !token) { redirectToLogin(); } else { ReactDOM.render( - , + + + , document.getElementById("root") ); } diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.css b/vuu-ui/sample-apps/app-vuu-example/src/App.css index a02dbdbfa..048d3429c 100644 --- a/vuu-ui/sample-apps/app-vuu-example/src/App.css +++ b/vuu-ui/sample-apps/app-vuu-example/src/App.css @@ -64,3 +64,10 @@ body { .vuuToolbarProxy-vertical { flex-direction: column; } + +.vuuShell-mainTabs { + height: 100%; + position: relative; + width: 100%; +} + diff --git a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx index e90bf6e87..4a7e69fd5 100644 --- a/vuu-ui/sample-apps/app-vuu-example/src/App.tsx +++ b/vuu-ui/sample-apps/app-vuu-example/src/App.tsx @@ -7,14 +7,16 @@ import { SessionEditingForm, Shell, ShellContextProvider, + ShellProps, ThemeProvider, VuuUser, } from "@finos/vuu-shell"; import { ReactElement, useCallback, useRef, useState } from "react"; import { AppSidePanel } from "./app-sidepanel"; -import { Stack } from "./AppStack"; +// import { Stack } from "./AppStack"; import { getDefaultColumnConfig } from "./columnMetaData"; import { getFormConfig } from "./session-editing"; +import { createPlaceholder } from "./createPlaceholder"; import "./App.css"; // Because we do not render the AppSidePanel directly, the css will not be included in bundle. @@ -31,31 +33,12 @@ const { websocketUrl: serverUrl = defaultWebsocketUrl, features } = const vuuBlotterUrl = "./feature-vuu-table/index.js"; // const vuuBlotterUrl = "./feature-vuu-table/index.js"; -registerComponent("Stack", Stack, "container"); +// registerComponent("Stack", Stack, "container"); -const defaultLayout = { - type: "Stack", - props: { - style: { - width: "100%", - height: "100%", - }, - enableAddTab: true, - enableRemoveTab: true, - preserve: true, - active: 0, - TabstripProps: { - allowAddTab: true, - allowCloseTab: true, - allowRenameTab: true, - }, - }, - children: [ - { - type: "Placeholder", - title: "Page 1", - }, - ], +// createNewChild is used when we add a new Tab to Stack +const layoutProps: ShellProps["LayoutProps"] = { + createNewChild: createPlaceholder, + pathToDropTarget: "#main-tabs.ACTIVE_CHILD", }; const withTable = (action: unknown): action is { table: VuuTable } => @@ -118,8 +101,8 @@ export const App = ({ user }: { user: VuuUser }) => { } serverUrl={serverUrl} user={user} diff --git a/vuu-ui/sample-apps/app-vuu-example/src/AppStack.tsx b/vuu-ui/sample-apps/app-vuu-example/src/AppStack.tsx deleted file mode 100644 index da08ea4b0..000000000 --- a/vuu-ui/sample-apps/app-vuu-example/src/AppStack.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Placeholder, StackLayout, StackProps, View } from "@finos/vuu-layout"; - -const createPlaceholder = (index: number) => ( - // Note make this width 100% and height 100% and we get a weird error where view continually resizes - growing - - - -); - -export const Stack = (props: StackProps) => ( - -); -Stack.displayName = "Stack"; diff --git a/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx b/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx new file mode 100644 index 000000000..0bfc5cb22 --- /dev/null +++ b/vuu-ui/sample-apps/app-vuu-example/src/createPlaceholder.tsx @@ -0,0 +1,13 @@ +import { Placeholder, View } from "@finos/vuu-layout"; + +export const createPlaceholder = (index?: number) => ( + // Note make this width 100% and height 100% and we get a weird error where view continually resizes - growing + + + +); diff --git a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx b/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx index bc1910645..6829262b5 100644 --- a/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx +++ b/vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx @@ -1,36 +1,21 @@ import { byModule } from "@finos/vuu-data"; import { - ContextMenuItemDescriptor, - MenuActionHandler, - MenuBuilder, -} from "@finos/vuu-data-types"; -import { registerComponent } from "@finos/vuu-layout"; -import { - ContextMenuProvider, - Dialog, - MenuActionClosePopup, -} from "@finos/vuu-popups"; + registerComponent, + useLayoutContextMenuItems, +} from "@finos/vuu-layout"; +import { ContextMenuProvider, Dialog } from "@finos/vuu-popups"; import { FeatureConfig, FeatureProps, LayoutManagementProvider, - LayoutMetadata, LeftNav, - SaveLayoutPanel, Shell, - useLayoutManager, } from "@finos/vuu-shell"; import { ColumnSettingsPanel, TableSettingsPanel, } from "@finos/vuu-table-extras"; -import { - CSSProperties, - ReactElement, - useCallback, - useMemo, - useState, -} from "react"; +import { CSSProperties } from "react"; import { FilterTableFeatureProps } from "feature-vuu-filter-table"; import { schemas } from "../utils"; @@ -107,64 +92,12 @@ const tableFeatures: FeatureProps[] = Object.values( })); const ShellWithNewTheme = () => { - const [dialogContent, setDialogContent] = useState(); - - const handleCloseDialog = useCallback(() => { - setDialogContent(undefined); - }, []); - - const { saveLayout } = useLayoutManager(); - - const handleSave = useCallback( - (layoutMetadata: Omit) => { - saveLayout(layoutMetadata); - setDialogContent(undefined); - }, - [saveLayout] - ); - - const [buildMenuOptions, handleMenuAction] = useMemo< - [MenuBuilder, MenuActionHandler] - >(() => { - return [ - (location, options) => { - console.log({ options }); - const locations = location.split(" "); - const menuDescriptors: ContextMenuItemDescriptor[] = []; - if (locations.includes("main-tab")) { - menuDescriptors.push( - { - label: "Save Layout", - action: "save-layout", - options, - }, - { - label: "Layout Settings", - action: "layout-settings", - options, - } - ); - } - return menuDescriptors; - }, - (action: MenuActionClosePopup) => { - console.log("menu action", { - action, - }); - if (action.menuId === "save-layout") { - setDialogContent( - - ); - return true; - } - return false; - }, - ]; - }, [handleCloseDialog, handleSave]); + const { + buildMenuOptions, + dialogContent, + handleCloseDialog, + handleMenuAction, + } = useLayoutContextMenuItems(); return (