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 <jasquat@users.noreply.github.com>
This commit is contained in:
parent
9b8cb58e99
commit
73df645408
|
@ -36,7 +36,7 @@ const updateDmnText = (oldText, newText, elementId = 'wonderful_process') => {
|
||||||
// wait for a little bit for the xml to get set before saving
|
// 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
|
// FIXME: gray out save button or add spinner while xml is loading
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.contains('Save').click();
|
cy.getBySel('process-model-file-save-button').click();
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBpmnPythonScript = (pythonScript, elementId = 'process_script') => {
|
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
|
// wait for a little bit for the xml to get set before saving
|
||||||
cy.wait(500);
|
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
|
// NOTE: anytime the status dropdown box is clicked on, click off of it
|
||||||
|
|
|
@ -82,7 +82,8 @@ describe('process-models', () => {
|
||||||
cy.get('#bio-properties-panel-name').clear();
|
cy.get('#bio-properties-panel-name').clear();
|
||||||
cy.get('#bio-properties-panel-name').type('Start Event Name');
|
cy.get('#bio-properties-panel-name').type('Start Event Name');
|
||||||
cy.wait(500);
|
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.contains('Start Event Name');
|
||||||
cy.get(fileNameInputSelector).type(bpmnFileName);
|
cy.get(fileNameInputSelector).type(bpmnFileName);
|
||||||
cy.contains(saveChangesButtonText).click();
|
cy.contains(saveChangesButtonText).click();
|
||||||
|
@ -101,7 +102,7 @@ describe('process-models', () => {
|
||||||
cy.get('#bio-properties-panel-id').clear();
|
cy.get('#bio-properties-panel-id').clear();
|
||||||
cy.get('#bio-properties-panel-id').type(decisionAcceptanceTestId);
|
cy.get('#bio-properties-panel-id').type(decisionAcceptanceTestId);
|
||||||
cy.contains('General').click();
|
cy.contains('General').click();
|
||||||
cy.contains('Save').click();
|
cy.getBySel('process-model-file-save-button').click();
|
||||||
cy.get(fileNameInputSelector).type(dmnFileName);
|
cy.get(fileNameInputSelector).type(dmnFileName);
|
||||||
cy.contains(saveChangesButtonText).click();
|
cy.contains(saveChangesButtonText).click();
|
||||||
cy.contains(`Process Model File: ${dmnFileName}`);
|
cy.contains(`Process Model File: ${dmnFileName}`);
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-jsonschema-form": "^1.8.1",
|
"react-jsonschema-form": "^1.8.1",
|
||||||
"react-router": "^6.22.0",
|
"react-router": "^6.22.0",
|
||||||
"react-router-dom": "6.3.0",
|
"react-router-dom": "^6.22.0",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"serve": "^14.0.0",
|
"serve": "^14.0.0",
|
||||||
"timepicker": "^1.13.18",
|
"timepicker": "^1.13.18",
|
||||||
|
@ -15604,14 +15604,6 @@
|
||||||
"he": "bin/he"
|
"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": {
|
"node_modules/hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
|
@ -26244,29 +26236,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "6.3.0",
|
"version": "6.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz",
|
||||||
"integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==",
|
"integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"history": "^5.2.0",
|
"@remix-run/router": "1.15.0",
|
||||||
"react-router": "6.3.0"
|
"react-router": "6.22.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16.8",
|
"react": ">=16.8",
|
||||||
"react-dom": ">=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": {
|
"node_modules/react-scripts": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
"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": {
|
"hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
|
@ -53463,22 +53439,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-router-dom": {
|
"react-router-dom": {
|
||||||
"version": "6.3.0",
|
"version": "6.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz",
|
||||||
"integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==",
|
"integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"history": "^5.2.0",
|
"@remix-run/router": "1.15.0",
|
||||||
"react-router": "6.3.0"
|
"react-router": "6.22.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-scripts": {
|
"react-scripts": {
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-jsonschema-form": "^1.8.1",
|
"react-jsonschema-form": "^1.8.1",
|
||||||
"react-router": "^6.22.0",
|
"react-router": "^6.22.0",
|
||||||
"react-router-dom": "6.3.0",
|
"react-router-dom": "^6.22.0",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"serve": "^14.0.0",
|
"serve": "^14.0.0",
|
||||||
"timepicker": "^1.13.18",
|
"timepicker": "^1.13.18",
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
import { defineAbility } from '@casl/ability';
|
import { defineAbility } from '@casl/ability';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -10,14 +9,11 @@ export default function App() {
|
||||||
const ability = defineAbility(() => {});
|
const ability = defineAbility(() => {});
|
||||||
return (
|
return (
|
||||||
<div className="cds--white">
|
<div className="cds--white">
|
||||||
{/* @ts-ignore */}
|
<APIErrorProvider>
|
||||||
<AbilityContext.Provider value={ability}>
|
<AbilityContext.Provider value={ability}>
|
||||||
<APIErrorProvider>
|
<ContainerForExtensions />
|
||||||
<BrowserRouter>
|
</AbilityContext.Provider>
|
||||||
<ContainerForExtensions />
|
</APIErrorProvider>
|
||||||
</BrowserRouter>
|
|
||||||
</APIErrorProvider>
|
|
||||||
</AbilityContext.Provider>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Content } from '@carbon/react';
|
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 React, { useEffect, useState } from 'react';
|
||||||
import { ErrorBoundary } from 'react-error-boundary';
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
|
|
||||||
|
@ -100,42 +100,51 @@ export default function ContainerForExtensions() {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const routeComponents = () => {
|
const routeComponents = () => {
|
||||||
return (
|
return [
|
||||||
<Routes>
|
{
|
||||||
<Route
|
path: '*',
|
||||||
path="/*"
|
element: <BaseRoutes extensionUxElements={extensionUxElements} />,
|
||||||
element={<BaseRoutes extensionUxElements={extensionUxElements} />}
|
},
|
||||||
/>
|
{ path: 'editor/*', element: <EditorRoutes /> },
|
||||||
<Route path="/editor/*" element={<EditorRoutes />} />
|
{ path: 'extensions/:page_identifier', element: <Extension /> },
|
||||||
<Route path="/extensions/:page_identifier" element={<Extension />} />
|
{ path: 'login', element: <Login /> },
|
||||||
<Route path="/login" element={<Login />} />
|
];
|
||||||
</Routes>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const backendIsDownPage = () => {
|
const backendIsDownPage = () => {
|
||||||
return <BackendIsDown />;
|
return [<BackendIsDown />];
|
||||||
};
|
};
|
||||||
|
|
||||||
const innerComponents = () => {
|
const innerComponents = () => {
|
||||||
if (backendIsUp === null) {
|
if (backendIsUp === null) {
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
if (backendIsUp) {
|
if (backendIsUp) {
|
||||||
return routeComponents();
|
return <Outlet />;
|
||||||
}
|
}
|
||||||
return backendIsDownPage();
|
return backendIsDownPage();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const layout = () => {
|
||||||
<>
|
return (
|
||||||
<NavigationBar extensionUxElements={extensionUxElements} />;
|
<>
|
||||||
<Content className={contentClassName}>
|
<NavigationBar extensionUxElements={extensionUxElements} />;
|
||||||
<ScrollToTop />
|
<Content className={contentClassName}>
|
||||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
<ScrollToTop />
|
||||||
{innerComponents()}
|
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||||
</ErrorBoundary>
|
{innerComponents()}
|
||||||
</Content>
|
</ErrorBoundary>
|
||||||
</>
|
</Content>
|
||||||
);
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
Component: layout,
|
||||||
|
children: routeComponents(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return <RouterProvider router={router} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import MDEditor from '@uiw/react-md-editor';
|
import MDEditor from '@uiw/react-md-editor';
|
||||||
|
import FormattingService from '../services/FormattingService';
|
||||||
|
|
||||||
export default function MarkdownRenderer(props: any) {
|
export default function MarkdownRenderer(props: any) {
|
||||||
|
const { source } = props;
|
||||||
|
const newMarkdown = FormattingService.checkForSpiffFormats(source);
|
||||||
let wrapperClassName = '';
|
let wrapperClassName = '';
|
||||||
const propsToUse = props;
|
const propsToUse = props;
|
||||||
if ('wrapperClassName' in propsToUse) {
|
if ('wrapperClassName' in propsToUse) {
|
||||||
|
@ -10,7 +13,7 @@ export default function MarkdownRenderer(props: any) {
|
||||||
return (
|
return (
|
||||||
<div data-color-mode="light" className={wrapperClassName}>
|
<div data-color-mode="light" className={wrapperClassName}>
|
||||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
<MDEditor.Markdown {...propsToUse} />
|
<MDEditor.Markdown {...{ ...propsToUse, ...{ source: newMarkdown } }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
} from '@carbon/icons-react';
|
} from '@carbon/icons-react';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Button } from '@carbon/react';
|
import { Button } from '@carbon/react';
|
||||||
|
import { ObjectWithStringKeysAndValues } from '../interfaces';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -17,6 +18,7 @@ type OwnProps = {
|
||||||
allowTogglingFullMessage?: boolean;
|
allowTogglingFullMessage?: boolean;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
withBottomMargin?: boolean;
|
withBottomMargin?: boolean;
|
||||||
|
'data-qa'?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Notification({
|
export function Notification({
|
||||||
|
@ -28,6 +30,7 @@ export function Notification({
|
||||||
allowTogglingFullMessage = false,
|
allowTogglingFullMessage = false,
|
||||||
timeout,
|
timeout,
|
||||||
withBottomMargin = true,
|
withBottomMargin = true,
|
||||||
|
'data-qa': dataQa,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const [showMessage, setShowMessage] = useState<boolean>(
|
const [showMessage, setShowMessage] = useState<boolean>(
|
||||||
!allowTogglingFullMessage
|
!allowTogglingFullMessage
|
||||||
|
@ -48,8 +51,15 @@ export function Notification({
|
||||||
classes = `${classes} with-bottom-margin`;
|
classes = `${classes} with-bottom-margin`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const additionalProps: ObjectWithStringKeysAndValues = {};
|
||||||
|
if (dataQa) {
|
||||||
|
additionalProps['data-qa'] = dataQa;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div role="status" className={classes}>
|
// we control the props added to the variable so we know it's fine
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
<div role="status" className={classes} {...additionalProps}>
|
||||||
<div className="cds--inline-notification__details">
|
<div className="cds--inline-notification__details">
|
||||||
<div className="cds--inline-notification__text-wrapper">
|
<div className="cds--inline-notification__text-wrapper">
|
||||||
{iconComponent}
|
{iconComponent}
|
||||||
|
|
|
@ -91,6 +91,7 @@ type OwnProps = {
|
||||||
url?: string;
|
url?: string;
|
||||||
callers?: ProcessReference[];
|
callers?: ProcessReference[];
|
||||||
activeUserElement?: React.ReactElement;
|
activeUserElement?: React.ReactElement;
|
||||||
|
disableSaveButton?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FitViewport = 'fit-viewport';
|
const FitViewport = 'fit-viewport';
|
||||||
|
@ -121,6 +122,7 @@ export default function ReactDiagramEditor({
|
||||||
url,
|
url,
|
||||||
callers,
|
callers,
|
||||||
activeUserElement,
|
activeUserElement,
|
||||||
|
disableSaveButton,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const [diagramXMLString, setDiagramXMLString] = useState('');
|
const [diagramXMLString, setDiagramXMLString] = useState('');
|
||||||
const [diagramModelerState, setDiagramModelerState] = useState(null);
|
const [diagramModelerState, setDiagramModelerState] = useState(null);
|
||||||
|
@ -708,7 +710,13 @@ export default function ReactDiagramEditor({
|
||||||
a={targetUris.processModelFileShowPath}
|
a={targetUris.processModelFileShowPath}
|
||||||
ability={ability}
|
ability={ability}
|
||||||
>
|
>
|
||||||
<Button onClick={handleSave}>Save</Button>
|
<Button
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={disableSaveButton}
|
||||||
|
data-qa="process-model-file-save-button"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
</Can>
|
</Can>
|
||||||
<Can
|
<Can
|
||||||
I="DELETE"
|
I="DELETE"
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
* See below for more details.
|
* See below for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { FunctionComponent } from 'react';
|
||||||
|
|
||||||
// Current version of the extension uischema.
|
// Current version of the extension uischema.
|
||||||
export type ExtensionUiSchemaVersion = '0.1' | '0.2';
|
export type ExtensionUiSchemaVersion = '0.1' | '0.2';
|
||||||
|
|
||||||
|
@ -210,4 +212,8 @@ export interface ExtensionApiResponse {
|
||||||
// The markdown string rendered from the process model.
|
// The markdown string rendered from the process model.
|
||||||
rendered_results_markdown?: string;
|
rendered_results_markdown?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SupportedComponentList = {
|
||||||
|
[key in keyof typeof UiSchemaPageComponentList]: FunctionComponent<any>;
|
||||||
|
};
|
||||||
/** ************************************* */
|
/** ************************************* */
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -148,9 +148,6 @@ export interface ProcessReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ObjectWithStringKeysAndValues = { [key: string]: string };
|
export type ObjectWithStringKeysAndValues = { [key: string]: string };
|
||||||
export type ObjectWithStringKeysAndFunctionValues = {
|
|
||||||
[key: string]: Function;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface FilterOperator {
|
export interface FilterOperator {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -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 React, { useEffect } from 'react';
|
||||||
import ProcessModelEditDiagram from './ProcessModelEditDiagram';
|
|
||||||
import ErrorDisplay from '../components/ErrorDisplay';
|
import ErrorDisplay from '../components/ErrorDisplay';
|
||||||
import LoginHandler from '../components/LoginHandler';
|
import LoginHandler from '../components/LoginHandler';
|
||||||
|
import ProcessModelEditDiagram from './ProcessModelEditDiagram';
|
||||||
|
|
||||||
export default function EditorRoutes() {
|
export default function EditorRoutes() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
|
@ -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 { useParams, useSearchParams } from 'react-router-dom';
|
||||||
import { Editor } from '@monaco-editor/react';
|
import { Editor } from '@monaco-editor/react';
|
||||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
import {
|
import { ProcessFile, ProcessModel } from '../interfaces';
|
||||||
ObjectWithStringKeysAndFunctionValues,
|
|
||||||
ProcessFile,
|
|
||||||
ProcessModel,
|
|
||||||
} from '../interfaces';
|
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
import useAPIError from '../hooks/UseApiError';
|
import useAPIError from '../hooks/UseApiError';
|
||||||
import { recursivelyChangeNullAndUndefined, makeid } from '../helpers';
|
import { recursivelyChangeNullAndUndefined, makeid } from '../helpers';
|
||||||
|
@ -16,6 +12,7 @@ import {
|
||||||
ExtensionApiResponse,
|
ExtensionApiResponse,
|
||||||
ExtensionPostBody,
|
ExtensionPostBody,
|
||||||
ExtensionUiSchema,
|
ExtensionUiSchema,
|
||||||
|
SupportedComponentList,
|
||||||
UiSchemaPageComponent,
|
UiSchemaPageComponent,
|
||||||
UiSchemaPageDefinition,
|
UiSchemaPageDefinition,
|
||||||
} from '../extension_ui_schema_interfaces';
|
} from '../extension_ui_schema_interfaces';
|
||||||
|
@ -65,7 +62,7 @@ export default function Extension({
|
||||||
|
|
||||||
const { addError, removeError } = useAPIError();
|
const { addError, removeError } = useAPIError();
|
||||||
|
|
||||||
const supportedComponents: ObjectWithStringKeysAndFunctionValues = {
|
const supportedComponents: SupportedComponentList = {
|
||||||
CreateNewInstance,
|
CreateNewInstance,
|
||||||
CustomForm,
|
CustomForm,
|
||||||
MarkdownRenderer,
|
MarkdownRenderer,
|
||||||
|
@ -98,7 +95,6 @@ export default function Extension({
|
||||||
);
|
);
|
||||||
const processLoadResult = useCallback(
|
const processLoadResult = useCallback(
|
||||||
(result: ExtensionApiResponse, pageDefinition: UiSchemaPageDefinition) => {
|
(result: ExtensionApiResponse, pageDefinition: UiSchemaPageDefinition) => {
|
||||||
setFormData(result.task_data);
|
|
||||||
if (pageDefinition.navigate_to_on_load) {
|
if (pageDefinition.navigate_to_on_load) {
|
||||||
const optionString = interpolateNavigationString(
|
const optionString = interpolateNavigationString(
|
||||||
pageDefinition.navigate_to_on_load,
|
pageDefinition.navigate_to_on_load,
|
||||||
|
@ -114,6 +110,8 @@ export default function Extension({
|
||||||
);
|
);
|
||||||
setMarkdownToRenderOnLoad(newMarkdown);
|
setMarkdownToRenderOnLoad(newMarkdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const taskDataCopy = { ...result.task_data };
|
||||||
if (
|
if (
|
||||||
pageDefinition.on_load &&
|
pageDefinition.on_load &&
|
||||||
pageDefinition.on_load.ui_schema_page_components_variable
|
pageDefinition.on_load.ui_schema_page_components_variable
|
||||||
|
@ -123,7 +121,18 @@ export default function Extension({
|
||||||
pageDefinition.on_load.ui_schema_page_components_variable
|
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);
|
setReadyForComponentsToDisplay(true);
|
||||||
},
|
},
|
||||||
[interpolateNavigationString]
|
[interpolateNavigationString]
|
||||||
|
@ -303,7 +312,7 @@ export default function Extension({
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
const renderComponentArguments = (component: UiSchemaPageComponent) => {
|
const renderComponentArguments = (component: UiSchemaPageComponent) => {
|
||||||
const argumentsForComponent: any = component.arguments;
|
const argumentsForComponent: any = component.arguments;
|
||||||
if (processModel) {
|
if (processModel && argumentsForComponent) {
|
||||||
Object.keys(argumentsForComponent).forEach((argName: string) => {
|
Object.keys(argumentsForComponent).forEach((argName: string) => {
|
||||||
const argValue = argumentsForComponent[argName];
|
const argValue = argumentsForComponent[argName];
|
||||||
if (
|
if (
|
||||||
|
@ -368,7 +377,10 @@ export default function Extension({
|
||||||
if (supportedComponents[componentName]) {
|
if (supportedComponents[componentName]) {
|
||||||
const argumentsForComponent = renderComponentArguments(component);
|
const argumentsForComponent = renderComponentArguments(component);
|
||||||
componentsToDisplay.push(
|
componentsToDisplay.push(
|
||||||
supportedComponents[componentName](argumentsForComponent)
|
createElement(
|
||||||
|
supportedComponents[componentName],
|
||||||
|
argumentsForComponent
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(
|
||||||
|
|
|
@ -47,7 +47,6 @@ import {
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import ProcessSearch from '../components/ProcessSearch';
|
import ProcessSearch from '../components/ProcessSearch';
|
||||||
import { Notification } from '../components/Notification';
|
import { Notification } from '../components/Notification';
|
||||||
import { usePrompt } from '../hooks/UsePrompt';
|
|
||||||
import ActiveUsers from '../components/ActiveUsers';
|
import ActiveUsers from '../components/ActiveUsers';
|
||||||
import { useFocusedTabStatus } from '../hooks/useFocusedTabStatus';
|
import { useFocusedTabStatus } from '../hooks/useFocusedTabStatus';
|
||||||
|
|
||||||
|
@ -137,8 +136,6 @@ export default function ProcessModelEditDiagram() {
|
||||||
|
|
||||||
const [callers, setCallers] = useState<ProcessReference[]>([]);
|
const [callers, setCallers] = useState<ProcessReference[]>([]);
|
||||||
|
|
||||||
usePrompt('Changes you made may not be saved.', diagramHasChanges);
|
|
||||||
|
|
||||||
const getProcessesCallback = useCallback((onProcessesFetched?: Function) => {
|
const getProcessesCallback = useCallback((onProcessesFetched?: Function) => {
|
||||||
const processResults = (result: any) => {
|
const processResults = (result: any) => {
|
||||||
const selectionArray = result.map((item: any) => {
|
const selectionArray = result.map((item: any) => {
|
||||||
|
@ -1054,7 +1051,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
path = generatePath(
|
path = generatePath(
|
||||||
'/editor/process-models/:process_model_id/files/:file_name',
|
'/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,
|
file_name: file.name,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1063,7 +1060,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
path = generatePath(
|
path = generatePath(
|
||||||
'/editor/process-models/:process_model_id/files?file_type=dmn',
|
'/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}
|
onElementsChanged={onElementsChanged}
|
||||||
callers={callers}
|
callers={callers}
|
||||||
activeUserElement={<ActiveUsers />}
|
activeUserElement={<ActiveUsers />}
|
||||||
|
disableSaveButton={!diagramHasChanges}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1131,6 +1129,8 @@ export default function ProcessModelEditDiagram() {
|
||||||
<Notification
|
<Notification
|
||||||
title="File Saved: "
|
title="File Saved: "
|
||||||
onClose={() => setDisplaySaveFileMessage(false)}
|
onClose={() => setDisplaySaveFileMessage(false)}
|
||||||
|
hideCloseButton
|
||||||
|
timeout={3000}
|
||||||
>
|
>
|
||||||
Changes to the file were saved.
|
Changes to the file were saved.
|
||||||
</Notification>
|
</Notification>
|
||||||
|
@ -1139,6 +1139,34 @@ export default function ProcessModelEditDiagram() {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unsavedChangesMessage = () => {
|
||||||
|
if (diagramHasChanges) {
|
||||||
|
return (
|
||||||
|
<Notification
|
||||||
|
title="Unsaved changes."
|
||||||
|
type="error"
|
||||||
|
hideCloseButton
|
||||||
|
data-qa="process-model-file-changed"
|
||||||
|
>
|
||||||
|
Please save to avoid losing your work.
|
||||||
|
</Notification>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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 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) {
|
if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) {
|
||||||
const processModelFileName = processModelFile ? processModelFile.name : '';
|
const processModelFileName = processModelFile ? processModelFile.name : '';
|
||||||
|
@ -1159,13 +1187,13 @@ export default function ProcessModelEditDiagram() {
|
||||||
Process Model File{processModelFile ? ': ' : ''}
|
Process Model File{processModelFile ? ': ' : ''}
|
||||||
{processModelFileName}
|
{processModelFileName}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
{pageModals()}
|
||||||
|
|
||||||
|
{unsavedChangesMessage()}
|
||||||
{saveFileMessage()}
|
{saveFileMessage()}
|
||||||
|
|
||||||
{appropriateEditor()}
|
{appropriateEditor()}
|
||||||
{newFileNameBox()}
|
|
||||||
{scriptEditorAndTests()}
|
|
||||||
{markdownEditor()}
|
|
||||||
{jsonSchemaEditor()}
|
|
||||||
{processModelSelector()}
|
|
||||||
<div id="diagram-container" />
|
<div id="diagram-container" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue