From 672d94de00c0841df2249514ae02ce7a724f3d8b Mon Sep 17 00:00:00 2001 From: Abdullah Asaad Date: Sun, 15 Jun 2025 12:16:11 -0700 Subject: [PATCH 1/4] feat(ui): update import flow actions to throw an error --- ui/src/components/canvas/FlowCanvas.tsx | 7 ++++++- ui/src/singletons/store/appActions.test.ts | 3 +-- ui/src/singletons/store/appActions.ts | 4 +--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/src/components/canvas/FlowCanvas.tsx b/ui/src/components/canvas/FlowCanvas.tsx index eaec61a6..18a5dc97 100644 --- a/ui/src/components/canvas/FlowCanvas.tsx +++ b/ui/src/components/canvas/FlowCanvas.tsx @@ -67,7 +67,12 @@ const ErrorHandler = ({ message, callback }: ErrorHandlerProps) => { const acceptDroppedFile = (file: File, importFlow: (json: string) => void) => { const reader = new FileReader() reader.onload = (e) => { - e.target && importFlow(e.target.result as string) + try { + e.target && importFlow(e.target.result as string) + } catch (e) { + // TODO: Display an error pop-up on failed import + console.error((e as Error).message) + } } reader.readAsText(file) } diff --git a/ui/src/singletons/store/appActions.test.ts b/ui/src/singletons/store/appActions.test.ts index 39e277ce..bed41624 100644 --- a/ui/src/singletons/store/appActions.test.ts +++ b/ui/src/singletons/store/appActions.test.ts @@ -603,8 +603,7 @@ describe("import flow from an exported JSON file", () => { const flow = JSON.parse(validExportedFlow) as Record delete flow[key] - act(() => importFlowFromJson(JSON.stringify(flow))) - + expect(() => importFlowFromJson(JSON.stringify(flow))).toThrowError() expect(getNodesView()).toEqual(initialNodes) expect(getEdgesView()).toEqual(initialEdges) } diff --git a/ui/src/singletons/store/appActions.ts b/ui/src/singletons/store/appActions.ts index 33f4c318..9e27e197 100644 --- a/ui/src/singletons/store/appActions.ts +++ b/ui/src/singletons/store/appActions.ts @@ -198,12 +198,10 @@ export const importFlowFromJson = (json: string) => { importFlowFromObject(flow) } -// TODO: Should a failed import throw an error on failure instead (for an error pop-up)? export const importFlowFromObject = (flow: SerializedFlow) => { useAppStore.setState(() => { if (!isStoreType(flow)) { - console.error("Failed to import an EIP flow JSON. Malformed input") - return {} + throw new Error("Failed to import an EIP flow JSON. Malformed input") } // Maintain backwards compatibility with older exported formats From 8cee95c056d2883a541e8b22256bbc51b5882ff3 Mon Sep 17 00:00:00 2001 From: Abdullah Asaad Date: Sun, 15 Jun 2025 12:16:53 -0700 Subject: [PATCH 2/4] feat(ui): add a modal for importing flow JSON strings --- .../components/options-menu/OptionsMenu.tsx | 44 ++++++--- .../options-menu/modals/ImportFlowModal.tsx | 91 +++++++++++++++++++ ui/src/styles.scss | 22 +++++ 3 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 ui/src/components/options-menu/modals/ImportFlowModal.tsx diff --git a/ui/src/components/options-menu/OptionsMenu.tsx b/ui/src/components/options-menu/OptionsMenu.tsx index 2593889d..a60e144d 100644 --- a/ui/src/components/options-menu/OptionsMenu.tsx +++ b/ui/src/components/options-menu/OptionsMenu.tsx @@ -1,23 +1,39 @@ -import { OverflowMenu } from "@carbon/react" +import { OverflowMenu, OverflowMenuItem } from "@carbon/react" import { Menu } from "@carbon/react/icons" +import { useState } from "react" import ExportPng from "./ExportPng" import SaveDiagram from "./SaveDiagram" +import { ImportFlowModal } from "./modals/ImportFlowModal" const OptionsMenu = () => { + const [importFlowModalOpen, setImportFlowModalOpen] = useState(false) + return ( - } - size="lg" - flipped - > - {/* OverflowMenu does not play nice when OverflowMenuItems are not direct children. Custom components will need to use forwardRef to avoid errors. */} - - - + <> + } + size="lg" + flipped + > + {/* OverflowMenu does not play nice when OverflowMenuItems are not direct children. Custom components will need to use forwardRef to avoid errors. */} + + + setImportFlowModalOpen(true)} + /> + + + {/* Modals */} + + ) } diff --git a/ui/src/components/options-menu/modals/ImportFlowModal.tsx b/ui/src/components/options-menu/modals/ImportFlowModal.tsx new file mode 100644 index 00000000..c209bf03 --- /dev/null +++ b/ui/src/components/options-menu/modals/ImportFlowModal.tsx @@ -0,0 +1,91 @@ +import { Modal } from "@carbon/react" +import { InlineLoadingStatus } from "carbon-components-react" +import hljs from "highlight.js/lib/core" +import json from "highlight.js/lib/languages/json" +import { useState } from "react" +import { createPortal } from "react-dom" +import Editor from "react-simple-code-editor" +import { importFlowFromJson } from "../../../singletons/store/appActions" + +hljs.registerLanguage("json", json) + +interface ImportFlowModalProps { + open: boolean + setOpen: (open: boolean) => void +} + +interface JsonEditorProps { + content: string + setContent: (content: string) => void +} + +const getLoadingDescription = (status: InlineLoadingStatus) => { + switch (status) { + case "active": + return "importing JSON" + case "error": + return "Invalid Flow JSON" + case "inactive": + case "finished": + return "" + } +} + +const FlowJsonEditor = ({ content, setContent }: JsonEditorProps) => { + return ( +
+ setContent(code)} + highlight={(code) => hljs.highlight(code, { language: "json" }).value} + padding={16} + textareaClassName="options-modal__editor-textarea" + /> +
+ ) +} + +export const ImportFlowModal = ({ open, setOpen }: ImportFlowModalProps) => { + const [loadingStatus, setLoadingStatus] = + useState("inactive") + const [content, setContent] = useState("") + + const resetAndCloseModal = () => { + setOpen(false) + setLoadingStatus("inactive") + setContent("") + } + + const doImport = () => { + setLoadingStatus("active") + try { + importFlowFromJson(content) + resetAndCloseModal() + } catch { + setLoadingStatus("error") + return + } + } + + const updateContent = (content: string) => { + loadingStatus !== "inactive" && setLoadingStatus("inactive") + setContent(content) + } + + return createPortal( + + + , + document.body + ) +} diff --git a/ui/src/styles.scss b/ui/src/styles.scss index 01ba4dc9..400a4489 100644 --- a/ui/src/styles.scss +++ b/ui/src/styles.scss @@ -48,6 +48,28 @@ } } +$options-modal-editor-height: 30vh; + +.options-modal__editor { + background-color: themes.$layer-02; + overflow-y: auto; + height: $options-modal-editor-height; + + @include type.type-style("code-02"); +} + +.options-modal__editor > div { + min-height: $options-modal-editor-height; +} + +.options-modal__editor:focus-within { + outline: 2px solid themes.$border-interactive; +} + +.options-modal__editor-textarea:focus { + outline: none; +} + .canvas { width: 100%; height: 100%; From 72d936a78aaae806d8bfd7f8b9aeee2e8776aa5b Mon Sep 17 00:00:00 2001 From: Abdullah Asaad Date: Sun, 15 Jun 2025 17:56:26 -0700 Subject: [PATCH 3/4] docs: update contributing docs --- docs/CONTRIBUTING.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index cdcaac42..73489e0c 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -34,17 +34,18 @@ Enhancements or new features can also be proposed by opening an issue. Please de git checkout -b feature/your-feature-name ``` 1. Make your changes. -1. Commit your changes, including a descriptive commit message: +1. Commit your changes, including a descriptive commit message following the [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) style. Example: ```shell - git commit -m "Short description of the changes (Issue # if present)" + git commit -m "feat(ui): description of a new ui feature (Issue # if present)" ``` + See the project's commit log for more examples. + 1. Push to your fork and submit a pull request: ```shell git push origin feature/your-feature-name ``` -1. Ensure the pull request description clearly describes the problem and solution. Include the issue number if - applicable. +1. Ensure the pull request description clearly describes the problem and solution. Include the issue number if applicable. From e763212168f85a97e73a07004a65fde6418511ac Mon Sep 17 00:00:00 2001 From: Abdullah Asaad Date: Mon, 16 Jun 2025 16:28:44 -0700 Subject: [PATCH 4/4] Add issue link to error pop-up TODO comment --- ui/src/components/canvas/FlowCanvas.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/components/canvas/FlowCanvas.tsx b/ui/src/components/canvas/FlowCanvas.tsx index 18a5dc97..2a6ab85a 100644 --- a/ui/src/components/canvas/FlowCanvas.tsx +++ b/ui/src/components/canvas/FlowCanvas.tsx @@ -71,6 +71,7 @@ const acceptDroppedFile = (file: File, importFlow: (json: string) => void) => { e.target && importFlow(e.target.result as string) } catch (e) { // TODO: Display an error pop-up on failed import + // https://github.com/OctoConsulting/keip-canvas/issues/7 console.error((e as Error).message) } }