From 0db5980f09c68eced317b717d998d91d9dfcedcd Mon Sep 17 00:00:00 2001 From: danfunk Date: Wed, 17 May 2023 11:58:17 -0400 Subject: [PATCH 1/2] Force all pages back to the top, rather than just a few of them. --- spiffworkflow-frontend/src/App.tsx | 3 +++ .../src/components/ErrorDisplay.tsx | 1 - .../src/components/ScrollToTop.tsx | 12 ++++++++++++ spiffworkflow-frontend/src/routes/TaskShow.tsx | 1 - 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 spiffworkflow-frontend/src/components/ScrollToTop.tsx diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index b7eaca800..17afca14d 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -15,6 +15,8 @@ import { AbilityContext } from './contexts/Can'; import UserService from './services/UserService'; import ErrorDisplay from './components/ErrorDisplay'; import APIErrorProvider from './contexts/APIErrorContext'; +import ScrollToTop from "./components/ScrollToTop"; +import React from "react"; export default function App() { if (!UserService.isLoggedIn()) { @@ -32,6 +34,7 @@ export default function App() { + diff --git a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx index 401169733..67e6e18dc 100644 --- a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx +++ b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx @@ -109,7 +109,6 @@ export default function ErrorDisplay() { if (errorObject) { const title = 'Error:'; - window.scrollTo(0, 0); // Scroll back to the top of the page errorTag = ( removeError()} type="error"> diff --git a/spiffworkflow-frontend/src/components/ScrollToTop.tsx b/spiffworkflow-frontend/src/components/ScrollToTop.tsx new file mode 100644 index 000000000..01d0b5806 --- /dev/null +++ b/spiffworkflow-frontend/src/components/ScrollToTop.tsx @@ -0,0 +1,12 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +export default function ScrollToTop() { + const { pathname } = useLocation(); + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return null; +} \ No newline at end of file diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index a74aa829b..aa507d036 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -112,7 +112,6 @@ export default function TaskShow() { if (!result.can_complete) { navigateToInterstitial(result); } - window.scrollTo(0, 0); // Scroll back to the top of the page /* Disable call to load previous tasks -- do not display menu. const url = `/v1.0/process-instances/for-me/${modifyProcessIdentifierForPathParam( From 505e6dc8b5657768ede365aab1909aeddde75db0 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 17 May 2023 12:14:44 -0400 Subject: [PATCH 2/2] do not run validations on save and close on a form w/ burnettk --- .../spiffworkflow_backend/config/__init__.py | 8 +++-- .../src/routes/TaskShow.tsx | 35 ++++++++++++++++--- .../src/services/HttpService.ts | 1 + 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py index 55c958977..224791108 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py @@ -40,7 +40,8 @@ def setup_database_configs(app: Flask) -> None: if pool_size is not None: pool_size = int(pool_size) else: - # this one doesn't come from app config and isn't documented in default.py because we don't want to give people the impression + # this one doesn't come from app config and isn't documented in default.py + # because we don't want to give people the impression # that setting it in flask python configs will work. on the contrary, it's used by a bash # script that starts the backend, so it can only be set in the environment. threads_per_worker_config = os.environ.get("SPIFFWORKFLOW_BACKEND_THREADS_PER_WORKER") @@ -50,8 +51,9 @@ def setup_database_configs(app: Flask) -> None: # this is a sqlalchemy default, if we don't have any better ideas pool_size = 5 - app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {} - app.config['SQLALCHEMY_ENGINE_OPTIONS']['pool_size'] = pool_size + app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {} + app.config["SQLALCHEMY_ENGINE_OPTIONS"]["pool_size"] = pool_size + def load_config_file(app: Flask, env_config_module: str) -> None: """Load_config_file.""" diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index a74aa829b..5a422e5fa 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -94,6 +94,9 @@ export default function TaskShow() { const params = useParams(); const navigate = useNavigate(); const [disabled, setDisabled] = useState(false); + const [noValidate, setNoValidate] = useState(false); + + const [taskData, setTaskData] = useState(null); const { addError, removeError } = useAPIError(); @@ -108,6 +111,7 @@ export default function TaskShow() { useEffect(() => { const processResult = (result: Task) => { setTask(result); + setTaskData(result.data); setDisabled(false); if (!result.can_complete) { navigateToInterstitial(result); @@ -160,22 +164,30 @@ export default function TaskShow() { } }; - const handleFormSubmit = (formObject: any, event: any) => { + const handleFormSubmit = (formObject: any, _event: any) => { if (disabled) { return; } + const dataToSubmit = formObject?.formData; if (!dataToSubmit) { navigate(`/tasks`); return; } let queryParams = ''; - if (event && event.submitter.id === 'close-button') { + + // if validations are turned off then save as draft + if (noValidate) { queryParams = '?save_as_draft=true'; } setDisabled(true); removeError(); delete dataToSubmit.isManualTask; + + // NOTE: rjsf sets blanks values to undefined and JSON.stringify removes keys with undefined values + // so there is no way to clear out a field that previously had a value. + // To resolve this, we could potentially go through the object that we are posting (either in here or in + // HttpService) and translate all undefined values to null. HttpService.makeCallToBackend({ path: `/tasks/${params.process_instance_id}/${params.task_id}${queryParams}`, successCallback: processSubmitResult, @@ -290,17 +302,27 @@ export default function TaskShow() { return errors; }; + // This turns off validations and then dispatches the click event after + // waiting a second to give the state time to update. + // This is to allow saving the form without validations causing issues. + const handleSaveAndCloseButton = () => { + setNoValidate(true); + setTimeout(() => { + (document.getElementById('our-very-own-form') as any).dispatchEvent( + new Event('submit', { cancelable: true, bubbles: true }) + ); + }, 1000); + }; + const formElement = () => { if (!task) { return null; } let formUiSchema; - let taskData = task.data; let jsonSchema = task.form_schema; let reactFragmentToHideSubmitButton = null; if (task.typename === 'ManualTask') { - taskData = {}; jsonSchema = { type: 'object', required: [], @@ -341,7 +363,7 @@ export default function TaskShow() { closeButton = (