From 73df6454082c49b13949dbe1e0248b29e59340c6 Mon Sep 17 00:00:00 2001 From: jasquat <2487833+jasquat@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:01:26 -0500 Subject: [PATCH] Feature/upgrade react router dom (#1050) * updated react-router-dom to match react-router version w/ burnettk * disable save button on process model edit diagram page unless a change has been made w/ burnettk * remove web components from form data on extensions page to avoid potential errors w/ burnettk * updates based on coderabbit w/ burnettk * fixed cypress issues --------- Co-authored-by: jasquat --- .../cypress/e2e/process_instances.cy.js | 4 +- .../cypress/e2e/process_models.cy.js | 5 +- spiffworkflow-frontend/package-lock.json | 62 +++++-------------- spiffworkflow-frontend/package.json | 2 +- spiffworkflow-frontend/src/App.tsx | 14 ++--- .../src/ContainerForExtensions.tsx | 61 ++++++++++-------- .../src/components/MarkdownRenderer.tsx | 5 +- .../src/components/Notification.tsx | 12 +++- .../src/components/ReactDiagramEditor.tsx | 10 ++- .../src/extension_ui_schema_interfaces.ts | 6 ++ .../src/hooks/UsePrompt.tsx | 62 ------------------- spiffworkflow-frontend/src/interfaces.ts | 3 - .../src/routes/EditorRoutes.tsx | 4 +- .../src/routes/Extension.tsx | 32 +++++++--- .../src/routes/ProcessModelEditDiagram.tsx | 48 +++++++++++--- 15 files changed, 152 insertions(+), 178 deletions(-) delete mode 100644 spiffworkflow-frontend/src/hooks/UsePrompt.tsx diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index 629875a7f..a0140fd51 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -36,7 +36,7 @@ const updateDmnText = (oldText, newText, elementId = 'wonderful_process') => { // wait for a little bit for the xml to get set before saving // FIXME: gray out save button or add spinner while xml is loading cy.wait(500); - cy.contains('Save').click(); + cy.getBySel('process-model-file-save-button').click(); }; const updateBpmnPythonScript = (pythonScript, elementId = 'process_script') => { @@ -47,7 +47,7 @@ const updateBpmnPythonScript = (pythonScript, elementId = 'process_script') => { // wait for a little bit for the xml to get set before saving cy.wait(500); - cy.contains('Save').click(); + cy.getBySel('process-model-file-save-button').click(); }; // NOTE: anytime the status dropdown box is clicked on, click off of it diff --git a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js index 05c217dba..f17cd36d0 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js @@ -82,7 +82,8 @@ describe('process-models', () => { cy.get('#bio-properties-panel-name').clear(); cy.get('#bio-properties-panel-name').type('Start Event Name'); cy.wait(500); - cy.contains('Save').click(); + cy.getBySel('process-model-file-changed'); + cy.getBySel('process-model-file-save-button').click(); cy.contains('Start Event Name'); cy.get(fileNameInputSelector).type(bpmnFileName); cy.contains(saveChangesButtonText).click(); @@ -101,7 +102,7 @@ describe('process-models', () => { cy.get('#bio-properties-panel-id').clear(); cy.get('#bio-properties-panel-id').type(decisionAcceptanceTestId); cy.contains('General').click(); - cy.contains('Save').click(); + cy.getBySel('process-model-file-save-button').click(); cy.get(fileNameInputSelector).type(dmnFileName); cy.contains(saveChangesButtonText).click(); cy.contains(`Process Model File: ${dmnFileName}`); diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index f55a4c393..463763990 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -56,7 +56,7 @@ "react-icons": "^5.0.1", "react-jsonschema-form": "^1.8.1", "react-router": "^6.22.0", - "react-router-dom": "6.3.0", + "react-router-dom": "^6.22.0", "react-scripts": "^5.0.1", "serve": "^14.0.0", "timepicker": "^1.13.18", @@ -15604,14 +15604,6 @@ "he": "bin/he" } }, - "node_modules/history": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", - "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", - "dependencies": { - "@babel/runtime": "^7.7.6" - } - }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -26244,29 +26236,21 @@ } }, "node_modules/react-router-dom": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz", - "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==", + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", + "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", "dependencies": { - "history": "^5.2.0", - "react-router": "6.3.0" + "@remix-run/router": "1.15.0", + "react-router": "6.22.0" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, - "node_modules/react-router-dom/node_modules/react-router": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", - "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==", - "dependencies": { - "history": "^5.2.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -45705,14 +45689,6 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "history": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", - "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", - "requires": { - "@babel/runtime": "^7.7.6" - } - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -53463,22 +53439,12 @@ } }, "react-router-dom": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz", - "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==", + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", + "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", "requires": { - "history": "^5.2.0", - "react-router": "6.3.0" - }, - "dependencies": { - "react-router": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", - "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==", - "requires": { - "history": "^5.2.0" - } - } + "@remix-run/router": "1.15.0", + "react-router": "6.22.0" } }, "react-scripts": { diff --git a/spiffworkflow-frontend/package.json b/spiffworkflow-frontend/package.json index 70b5d58b2..149176e61 100644 --- a/spiffworkflow-frontend/package.json +++ b/spiffworkflow-frontend/package.json @@ -51,7 +51,7 @@ "react-icons": "^5.0.1", "react-jsonschema-form": "^1.8.1", "react-router": "^6.22.0", - "react-router-dom": "6.3.0", + "react-router-dom": "^6.22.0", "react-scripts": "^5.0.1", "serve": "^14.0.0", "timepicker": "^1.13.18", diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index 998a6605f..31adaf5c0 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -1,4 +1,3 @@ -import { BrowserRouter } from 'react-router-dom'; import { defineAbility } from '@casl/ability'; import React from 'react'; @@ -10,14 +9,11 @@ export default function App() { const ability = defineAbility(() => {}); return (
- {/* @ts-ignore */} - - - - - - - + + + + +
); } diff --git a/spiffworkflow-frontend/src/ContainerForExtensions.tsx b/spiffworkflow-frontend/src/ContainerForExtensions.tsx index ec62e7474..be65af25b 100644 --- a/spiffworkflow-frontend/src/ContainerForExtensions.tsx +++ b/spiffworkflow-frontend/src/ContainerForExtensions.tsx @@ -1,5 +1,5 @@ import { Content } from '@carbon/react'; -import { Routes, Route } from 'react-router-dom'; +import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom'; import React, { useEffect, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; @@ -100,42 +100,51 @@ export default function ContainerForExtensions() { ]); const routeComponents = () => { - return ( - - } - /> - } /> - } /> - } /> - - ); + return [ + { + path: '*', + element: , + }, + { path: 'editor/*', element: }, + { path: 'extensions/:page_identifier', element: }, + { path: 'login', element: }, + ]; }; const backendIsDownPage = () => { - return ; + return []; }; const innerComponents = () => { if (backendIsUp === null) { - return null; + return []; } if (backendIsUp) { - return routeComponents(); + return ; } return backendIsDownPage(); }; - return ( - <> - ; - - - - {innerComponents()} - - - - ); + const layout = () => { + return ( + <> + ; + + + + {innerComponents()} + + + + ); + }; + + const router = createBrowserRouter([ + { + path: '*', + Component: layout, + children: routeComponents(), + }, + ]); + return ; } diff --git a/spiffworkflow-frontend/src/components/MarkdownRenderer.tsx b/spiffworkflow-frontend/src/components/MarkdownRenderer.tsx index f4d52ad23..f7094bae4 100644 --- a/spiffworkflow-frontend/src/components/MarkdownRenderer.tsx +++ b/spiffworkflow-frontend/src/components/MarkdownRenderer.tsx @@ -1,6 +1,9 @@ import MDEditor from '@uiw/react-md-editor'; +import FormattingService from '../services/FormattingService'; export default function MarkdownRenderer(props: any) { + const { source } = props; + const newMarkdown = FormattingService.checkForSpiffFormats(source); let wrapperClassName = ''; const propsToUse = props; if ('wrapperClassName' in propsToUse) { @@ -10,7 +13,7 @@ export default function MarkdownRenderer(props: any) { return (
{/* eslint-disable-next-line react/jsx-props-no-spreading */} - +
); } diff --git a/spiffworkflow-frontend/src/components/Notification.tsx b/spiffworkflow-frontend/src/components/Notification.tsx index 3cfc66ab2..3c65b5645 100644 --- a/spiffworkflow-frontend/src/components/Notification.tsx +++ b/spiffworkflow-frontend/src/components/Notification.tsx @@ -7,6 +7,7 @@ import { } from '@carbon/icons-react'; // @ts-ignore import { Button } from '@carbon/react'; +import { ObjectWithStringKeysAndValues } from '../interfaces'; type OwnProps = { title: string; @@ -17,6 +18,7 @@ type OwnProps = { allowTogglingFullMessage?: boolean; timeout?: number; withBottomMargin?: boolean; + 'data-qa'?: string; }; export function Notification({ @@ -28,6 +30,7 @@ export function Notification({ allowTogglingFullMessage = false, timeout, withBottomMargin = true, + 'data-qa': dataQa, }: OwnProps) { const [showMessage, setShowMessage] = useState( !allowTogglingFullMessage @@ -48,8 +51,15 @@ export function Notification({ classes = `${classes} with-bottom-margin`; } + const additionalProps: ObjectWithStringKeysAndValues = {}; + if (dataQa) { + additionalProps['data-qa'] = dataQa; + } + return ( -
+ // we control the props added to the variable so we know it's fine + // eslint-disable-next-line react/jsx-props-no-spreading +
{iconComponent} diff --git a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx index b56eec234..25d4aca70 100644 --- a/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx +++ b/spiffworkflow-frontend/src/components/ReactDiagramEditor.tsx @@ -91,6 +91,7 @@ type OwnProps = { url?: string; callers?: ProcessReference[]; activeUserElement?: React.ReactElement; + disableSaveButton?: boolean; }; const FitViewport = 'fit-viewport'; @@ -121,6 +122,7 @@ export default function ReactDiagramEditor({ url, callers, activeUserElement, + disableSaveButton, }: OwnProps) { const [diagramXMLString, setDiagramXMLString] = useState(''); const [diagramModelerState, setDiagramModelerState] = useState(null); @@ -708,7 +710,13 @@ export default function ReactDiagramEditor({ a={targetUris.processModelFileShowPath} ability={ability} > - + ; +}; /** ************************************* */ diff --git a/spiffworkflow-frontend/src/hooks/UsePrompt.tsx b/spiffworkflow-frontend/src/hooks/UsePrompt.tsx deleted file mode 100644 index f2a8a05e2..000000000 --- a/spiffworkflow-frontend/src/hooks/UsePrompt.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/** - * These hooks re-implement the now removed useBlocker and usePrompt hooks in 'react-router-dom'. - * Thanks for the idea @piecyk https://github.com/remix-run/react-router/issues/8139#issuecomment-953816315 - * Source: https://github.com/remix-run/react-router/commit/256cad70d3fd4500b1abcfea66f3ee622fb90874#diff-b60f1a2d4276b2a605c05e19816634111de2e8a4186fe9dd7de8e344b65ed4d3L344-L381 - */ - -import { useCallback, useContext, useEffect } from 'react'; -import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'; - -/** - * Blocks all navigation attempts. This is useful for preventing the page from - * changing until some condition is met, like saving form data. - * - * @param blocker - * @param when - * @see https://reactrouter.com/api/useBlocker - */ -export function useBlocker(blocker: any, when: any = true) { - const { navigator } = useContext(NavigationContext); - - useEffect(() => { - if (!when) { - return undefined; - } - - const unblock = (navigator as any).block((tx: any) => { - const autoUnblockingTx = { - ...tx, - retry() { - // Automatically unblock the transition so it can play all the way - // through before retrying it. TODO: Figure out how to re-enable - // this block if the transition is cancelled for some reason. - unblock(); - tx.retry(); - }, - }; - - blocker(autoUnblockingTx); - }); - - return unblock; - }, [navigator, blocker, when]); -} -/** - * Prompts the user with an Alert before they leave the current screen. - * - * @param message - * @param when - */ -export function usePrompt(message: any, when: any = true) { - const blocker = useCallback( - (tx: any) => { - // eslint-disable-next-line no-alert - if (window.confirm(message)) { - tx.retry(); - } - }, - [message] - ); - - useBlocker(blocker, when); -} diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index dfa093a3a..d8dfb380c 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -148,9 +148,6 @@ export interface ProcessReference { } export type ObjectWithStringKeysAndValues = { [key: string]: string }; -export type ObjectWithStringKeysAndFunctionValues = { - [key: string]: Function; -}; export interface FilterOperator { id: string; diff --git a/spiffworkflow-frontend/src/routes/EditorRoutes.tsx b/spiffworkflow-frontend/src/routes/EditorRoutes.tsx index b35031ebf..0e3885766 100644 --- a/spiffworkflow-frontend/src/routes/EditorRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/EditorRoutes.tsx @@ -1,9 +1,9 @@ -import { Routes, Route, useLocation } from 'react-router-dom'; +import { useLocation, Routes, Route } from 'react-router-dom'; import React, { useEffect } from 'react'; -import ProcessModelEditDiagram from './ProcessModelEditDiagram'; import ErrorDisplay from '../components/ErrorDisplay'; import LoginHandler from '../components/LoginHandler'; +import ProcessModelEditDiagram from './ProcessModelEditDiagram'; export default function EditorRoutes() { const location = useLocation(); diff --git a/spiffworkflow-frontend/src/routes/Extension.tsx b/spiffworkflow-frontend/src/routes/Extension.tsx index 70639380e..245fdc3d0 100644 --- a/spiffworkflow-frontend/src/routes/Extension.tsx +++ b/spiffworkflow-frontend/src/routes/Extension.tsx @@ -1,12 +1,8 @@ -import { useCallback, useEffect, useState } from 'react'; +import { createElement, useCallback, useEffect, useState } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; import { Editor } from '@monaco-editor/react'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; -import { - ObjectWithStringKeysAndFunctionValues, - ProcessFile, - ProcessModel, -} from '../interfaces'; +import { ProcessFile, ProcessModel } from '../interfaces'; import HttpService from '../services/HttpService'; import useAPIError from '../hooks/UseApiError'; import { recursivelyChangeNullAndUndefined, makeid } from '../helpers'; @@ -16,6 +12,7 @@ import { ExtensionApiResponse, ExtensionPostBody, ExtensionUiSchema, + SupportedComponentList, UiSchemaPageComponent, UiSchemaPageDefinition, } from '../extension_ui_schema_interfaces'; @@ -65,7 +62,7 @@ export default function Extension({ const { addError, removeError } = useAPIError(); - const supportedComponents: ObjectWithStringKeysAndFunctionValues = { + const supportedComponents: SupportedComponentList = { CreateNewInstance, CustomForm, MarkdownRenderer, @@ -98,7 +95,6 @@ export default function Extension({ ); const processLoadResult = useCallback( (result: ExtensionApiResponse, pageDefinition: UiSchemaPageDefinition) => { - setFormData(result.task_data); if (pageDefinition.navigate_to_on_load) { const optionString = interpolateNavigationString( pageDefinition.navigate_to_on_load, @@ -114,6 +110,8 @@ export default function Extension({ ); setMarkdownToRenderOnLoad(newMarkdown); } + + const taskDataCopy = { ...result.task_data }; if ( pageDefinition.on_load && pageDefinition.on_load.ui_schema_page_components_variable @@ -123,7 +121,18 @@ export default function Extension({ pageDefinition.on_load.ui_schema_page_components_variable ] ); + + // we were getting any AJV8Validator error when we had this data in the task data + // when we attempted to submit a form using this task data. + // The error was: + // Uncaught RangeError: Maximum call stack size exceeded + // + // Removing the ui schema page components dictionary seems to resolve it. + delete taskDataCopy[ + pageDefinition.on_load.ui_schema_page_components_variable + ]; } + setFormData(taskDataCopy); setReadyForComponentsToDisplay(true); }, [interpolateNavigationString] @@ -303,7 +312,7 @@ export default function Extension({ // eslint-disable-next-line sonarjs/cognitive-complexity const renderComponentArguments = (component: UiSchemaPageComponent) => { const argumentsForComponent: any = component.arguments; - if (processModel) { + if (processModel && argumentsForComponent) { Object.keys(argumentsForComponent).forEach((argName: string) => { const argValue = argumentsForComponent[argName]; if ( @@ -368,7 +377,10 @@ export default function Extension({ if (supportedComponents[componentName]) { const argumentsForComponent = renderComponentArguments(component); componentsToDisplay.push( - supportedComponents[componentName](argumentsForComponent) + createElement( + supportedComponents[componentName], + argumentsForComponent + ) ); } else { console.error( diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 8238be25e..db007b5e0 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -47,7 +47,6 @@ import { } from '../interfaces'; import ProcessSearch from '../components/ProcessSearch'; import { Notification } from '../components/Notification'; -import { usePrompt } from '../hooks/UsePrompt'; import ActiveUsers from '../components/ActiveUsers'; import { useFocusedTabStatus } from '../hooks/useFocusedTabStatus'; @@ -137,8 +136,6 @@ export default function ProcessModelEditDiagram() { const [callers, setCallers] = useState([]); - usePrompt('Changes you made may not be saved.', diagramHasChanges); - const getProcessesCallback = useCallback((onProcessesFetched?: Function) => { const processResults = (result: any) => { const selectionArray = result.map((item: any) => { @@ -1054,7 +1051,7 @@ export default function ProcessModelEditDiagram() { path = generatePath( '/editor/process-models/:process_model_id/files/:file_name', { - process_model_id: params.process_model_id, + process_model_id: params.process_model_id || null, file_name: file.name, } ); @@ -1063,7 +1060,7 @@ export default function ProcessModelEditDiagram() { path = generatePath( '/editor/process-models/:process_model_id/files?file_type=dmn', { - process_model_id: params.process_model_id, + process_model_id: params.process_model_id || null, } ); } @@ -1121,6 +1118,7 @@ export default function ProcessModelEditDiagram() { onElementsChanged={onElementsChanged} callers={callers} activeUserElement={} + disableSaveButton={!diagramHasChanges} /> ); }; @@ -1131,6 +1129,8 @@ export default function ProcessModelEditDiagram() { setDisplaySaveFileMessage(false)} + hideCloseButton + timeout={3000} > Changes to the file were saved. @@ -1139,6 +1139,34 @@ export default function ProcessModelEditDiagram() { return null; }; + const unsavedChangesMessage = () => { + if (diagramHasChanges) { + return ( + + Please save to avoid losing your work. + + ); + } + return null; + }; + + const pageModals = () => { + return ( + <> + {newFileNameBox()} + {scriptEditorAndTests()} + {markdownEditor()} + {jsonSchemaEditor()} + {processModelSelector()} + + ); + }; + // if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) { const processModelFileName = processModelFile ? processModelFile.name : ''; @@ -1159,13 +1187,13 @@ export default function ProcessModelEditDiagram() { Process Model File{processModelFile ? ': ' : ''} {processModelFileName} + + {pageModals()} + + {unsavedChangesMessage()} {saveFileMessage()} + {appropriateEditor()} - {newFileNameBox()} - {scriptEditorAndTests()} - {markdownEditor()} - {jsonSchemaEditor()} - {processModelSelector()}
);