From b11facd9e438d4c862853e313cd66ee9eb0eddce Mon Sep 17 00:00:00 2001 From: Mikael Solstad Date: Thu, 7 Dec 2023 12:02:51 +0100 Subject: [PATCH] Refactor/1508 rewrite page navigation (#1682) * refactor: Add routes for pageLayouts. Show pages of a form with said routes. Navigation updated to use routes * refactor: Add taskId to url navigation * Add navigateToCurrentProcess button * refactor: get pageorder by using hook instead of redux * refactor: fix showing of confirmation, receipt, and feedback steps. Fix small bug with progress component * refactor: fix returnToView. Add state to context * Add returnToView and scrollPosition to PageNavigationContext * Add support for navigating users to the last visited page stored in local storage * Add UiConfigContext. Fix more tests after refactoring navigation * refactor: e2e-tests after refactoring to use router based page navigation * fix: bug with layouts becoming hidden with any expression * refactor: layoutsettings to custom context * refactor: adapt code after merging with new app-flow * refactor: add page routing for stateless apps * fix: remove keepScroll functionality until validation is in place * fix: issue with back to summary page being shown after navigating to next step * fix: bug with add items in sub group repeating groups * fix: bug with datamodel not being accessible during confirmation/receipt steps * fix: summary tests * fix: temporarily skip validation cypress tests while waiting for 1506 * fix: remove currentView from devtools * fix: remove use of redux based currentView in repGroupDeleteRow * fix: rewrite PresentationComponent to have prop stating whether to render navbar or not * fix: unit tests after refactor * fix: bug with mergeAndSort function * fix: issue with validation currentView in GenericComponent * refactor: remove unused sagas related to page navigation * fix: bug with navigating away from instantiation-selection * fix: bug with RepeatingGroupsLikertContainer tests * fix: issue with receipt redirecting * fix: redirect to page and processEnd. Use currentPage from url in ExprContext * Update process mutation data when navigating to next step * fix: bug with overwriting queryData in InstanceContext * fix: unit test for NavigationBar component * refactor: types on ProcessContext * refactor: use lang component for generic help page when navigating * refactor: remove uiconfig options that are available in UiConfigContext from redux * fix: redirect to start for stateless-apps on invalid pages * fix: add process task error messages for all types of process steps * fix: remove unnecessary routes in stateless apps * fix: use enums to represent fixed routes * fix: issue with useLayoutQuery * refactor: move LayoutProvider, UiConfigProvider and LayoutSettingsProvider to renderWithInstanceAndLayout * docs: add comment about useTaskType * refactor: add test for bug with validation and new page navigation * fix: remove unnecessary redirect to confirmation page from receipt page * refactor: remove duplicate import --------- Co-authored-by: Ole Martin Handeland --- src/App.tsx | 8 +- src/__mocks__/getAttachmentsMock.ts | 59 +++-- src/components/form/Form.test.tsx | 6 + src/components/form/Form.tsx | 34 ++- src/components/message/ErrorReport.tsx | 20 +- src/components/presentation/NavBar.test.tsx | 60 ++--- src/components/presentation/NavBar.tsx | 64 +++-- .../presentation/Presentation.test.tsx | 49 ++-- src/components/presentation/Presentation.tsx | 63 +---- .../presentation/ProcessNavigation.tsx | 2 +- src/components/presentation/Progress.test.tsx | 27 +++ src/components/presentation/Progress.tsx | 10 +- .../wrappers/ProcessWrapper.module.css | 6 + src/components/wrappers/ProcessWrapper.tsx | 173 ++++++++++++-- src/core/loading/Loader.tsx | 1 + .../attachments/utils/sorting.test.ts | 36 +++ src/features/attachments/utils/sorting.ts | 4 +- .../DevNavigationButtons.tsx | 37 +-- .../ExpressionPlayground.tsx | 7 +- .../PermissionsEditor/PermissionsEditor.tsx | 28 +-- .../devtools/utils/layoutSchemaValidation.ts | 2 +- src/features/entrypoint/Entrypoint.tsx | 15 +- src/features/form/FormContext.tsx | 25 +- src/features/form/layout/LayoutsContext.tsx | 104 ++++---- .../form/layout/PageNavigationContext.tsx | 71 ++++++ src/features/form/layout/UiConfigContext.tsx | 70 ++++++ src/features/form/layout/formLayoutSlice.ts | 87 +------ src/features/form/layout/formLayoutTypes.ts | 22 +- .../repGroups/initRepeatingGroupsSaga.ts | 1 + .../layout/repGroups/repGroupAddRowSaga.ts | 9 +- .../repGroups/repGroupDeleteRowSaga.test.ts | 1 + .../layout/repGroups/repGroupDeleteRowSaga.ts | 9 +- .../updateRepeatingGroupEditIndexSaga.ts | 6 +- .../update/updateFormLayoutSagas.test.ts | 180 -------------- .../layout/update/updateFormLayoutSagas.ts | 225 +----------------- .../layoutSettings/LayoutSettingsContext.tsx | 10 +- src/features/formData/FormDataContext.tsx | 13 +- .../formData/submit/submitFormDataSagas.ts | 11 +- src/features/instance/InstanceContext.tsx | 8 +- src/features/instance/ProcessContext.tsx | 73 +++++- .../instance/ProcessNavigationContext.tsx | 8 +- .../instantiate/InstantiationContext.tsx | 4 +- .../confirm/components/ConfirmButton.tsx | 0 .../confirm/containers/Confirm.test.tsx | 2 +- .../confirm/containers/Confirm.tsx | 2 +- .../confirm/containers/ConfirmPage.test.tsx | 2 +- .../confirm/containers/ConfirmPage.tsx | 2 +- .../returnConfirmSummaryObject.test.ts | 2 +- .../helpers/returnConfirmSummaryObject.ts | 0 .../{ => processEnd}/feedback/Feedback.tsx | 0 .../receipt/ReceiptContainer.test.tsx | 2 + src/features/receipt/ReceiptContainer.tsx | 6 + src/hooks/useNavigatePage.ts | 197 +++++++++++++++ src/index.tsx | 86 ++++--- src/language/texts/en.ts | 3 + src/language/texts/nb.ts | 3 + src/language/texts/nn.ts | 3 + src/layout/Button/ButtonComponent.tsx | 3 + src/layout/Button/GoToTaskButton.test.tsx | 7 +- src/layout/GenericComponent.tsx | 17 +- src/layout/Group/GroupContainer.test.tsx | 14 +- src/layout/Group/GroupContainer.tsx | 11 +- ...epeatingGroupsLikertContainerTestUtils.tsx | 1 + .../NavigationBarComponent.test.tsx | 111 +++------ .../NavigationBar/NavigationBarComponent.tsx | 34 +-- .../NavigationButtonsComponent.test.tsx | 38 +-- .../NavigationButtonsComponent.tsx | 70 +++--- .../Panel/PanelReferenceGroupContainer.tsx | 3 + src/layout/Summary/SummaryComponent.test.tsx | 23 +- src/layout/Summary/SummaryComponent.tsx | 16 +- src/queries/queries.ts | 2 +- src/selectors/getLayoutData.test.ts | 56 ----- src/selectors/getLayoutData.ts | 16 -- src/selectors/getLayoutOrder.test.ts | 23 -- src/selectors/getLayoutOrder.ts | 44 ---- src/test/mockWindow.ts | 36 +++ src/test/renderWithProviders.tsx | 77 +++--- src/test/routerUtils.tsx | 19 ++ src/types/index.ts | 14 +- src/types/shared.ts | 1 + src/utils/layout/ExprContext.tsx | 19 +- src/utils/layout/HierarchyGenerator.ts | 4 +- src/utils/layout/LayoutPages.ts | 1 - src/utils/layout/hierarchy.ts | 10 +- src/utils/urls/appUrlHelper.test.ts | 6 - src/utils/urls/appUrlHelper.ts | 7 +- .../anonymous-stateless-app/validation.ts | 7 +- .../frontend-test/auto-save-behavior.ts | 7 +- .../integration/frontend-test/components.ts | 8 +- .../e2e/integration/frontend-test/dynamics.ts | 7 +- test/e2e/integration/frontend-test/grid.ts | 7 +- test/e2e/integration/frontend-test/group.ts | 21 +- test/e2e/integration/frontend-test/summary.ts | 31 ++- .../integration/frontend-test/validation.ts | 57 ++++- test/e2e/support/custom.ts | 52 ++-- test/e2e/support/global.d.ts | 1 + 96 files changed, 1471 insertions(+), 1368 deletions(-) create mode 100644 src/components/presentation/Progress.test.tsx create mode 100644 src/features/attachments/utils/sorting.test.ts create mode 100644 src/features/form/layout/PageNavigationContext.tsx create mode 100644 src/features/form/layout/UiConfigContext.tsx delete mode 100644 src/features/form/layout/update/updateFormLayoutSagas.test.ts rename src/features/{ => processEnd}/confirm/components/ConfirmButton.tsx (100%) rename src/features/{ => processEnd}/confirm/containers/Confirm.test.tsx (89%) rename src/features/{ => processEnd}/confirm/containers/Confirm.tsx (93%) rename src/features/{ => processEnd}/confirm/containers/ConfirmPage.test.tsx (98%) rename src/features/{ => processEnd}/confirm/containers/ConfirmPage.tsx (95%) rename src/features/{ => processEnd}/confirm/helpers/returnConfirmSummaryObject.test.ts (95%) rename src/features/{ => processEnd}/confirm/helpers/returnConfirmSummaryObject.ts (100%) rename src/features/{ => processEnd}/feedback/Feedback.tsx (100%) create mode 100644 src/hooks/useNavigatePage.ts delete mode 100644 src/selectors/getLayoutData.test.ts delete mode 100644 src/selectors/getLayoutData.ts delete mode 100644 src/selectors/getLayoutOrder.test.ts delete mode 100644 src/selectors/getLayoutOrder.ts create mode 100644 src/test/mockWindow.ts create mode 100644 src/test/routerUtils.tsx diff --git a/src/App.tsx b/src/App.tsx index 65e7881f01..9b13cde9e6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; -import { ProcessWrapper } from 'src/components/wrappers/ProcessWrapper'; +import { ProcessWrapperWrapper } from 'src/components/wrappers/ProcessWrapper'; import { Entrypoint } from 'src/features/entrypoint/Entrypoint'; import { InstanceProvider } from 'src/features/instance/InstanceContext'; import { PartySelection } from 'src/features/instantiate/containers/PartySelection'; @@ -10,7 +10,7 @@ import { InstanceSelectionWrapper } from 'src/features/instantiate/selection/Ins export const App = () => ( } /> ( element={} /> - + } /> diff --git a/src/__mocks__/getAttachmentsMock.ts b/src/__mocks__/getAttachmentsMock.ts index 5852d67889..800f90089d 100644 --- a/src/__mocks__/getAttachmentsMock.ts +++ b/src/__mocks__/getAttachmentsMock.ts @@ -1,6 +1,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { UploadedAttachment } from 'src/features/attachments'; +import type { IData } from 'src/types/shared'; const getRandomFileSize = () => Math.floor(Math.random() * (2500 - 250 + 1)) + 250; @@ -13,29 +14,43 @@ export const getAttachmentsMock = ({ count = 3, fileSize }: IGetAttachmentsMock const out: UploadedAttachment[] = []; for (let i = 0; i < count; i++) { - out.push({ - error: undefined, - uploaded: true, - deleting: false, - updating: false, - data: { - id: uuidv4(), - dataType: 'file', - size: fileSize || getRandomFileSize(), - filename: `attachment-name-${i}`, - tags: [`attachment-tag-${i}`], - created: new Date().toISOString(), - createdBy: 'test', - lastChanged: new Date().toISOString(), - lastChangedBy: 'test', - blobStoragePath: 'test', - contentType: 'test', - locked: false, - instanceGuid: 'test', - refs: [], - }, - }); + out.push( + getAttachmentMock({ + data: getAttachmentDataMock({ + size: fileSize || getRandomFileSize(), + filename: `attachment-name-${i}`, + tags: [`attachment-tag-${i}`], + }), + }), + ); } return out; }; + +export const getAttachmentDataMock = (overrides: Partial = {}): IData => ({ + id: uuidv4(), + dataType: 'file', + size: getRandomFileSize(), + filename: 'attachment-name', + tags: ['attachment-tag-'], + created: new Date().toISOString(), + createdBy: 'test', + lastChanged: new Date().toISOString(), + lastChangedBy: 'test', + blobStoragePath: 'test', + contentType: 'test', + locked: false, + instanceGuid: 'test', + refs: [], + ...overrides, +}); + +export const getAttachmentMock = (overrides: Partial = {}): UploadedAttachment => ({ + error: undefined, + uploaded: true, + deleting: false, + updating: false, + data: getAttachmentDataMock(overrides.data), + ...overrides, +}); diff --git a/src/components/form/Form.test.tsx b/src/components/form/Form.test.tsx index 107fd93073..38ba3d2a71 100644 --- a/src/components/form/Form.test.tsx +++ b/src/components/form/Form.test.tsx @@ -6,6 +6,7 @@ import { getFormLayoutStateMock } from 'src/__mocks__/getFormLayoutStateMock'; import { getInitialStateMock } from 'src/__mocks__/initialStateMock'; import { Form } from 'src/components/form/Form'; import { renderWithInstanceAndLayout } from 'src/test/renderWithProviders'; +import { PageNavigationRouter } from 'src/test/routerUtils'; import type { CompExternal, ILayout } from 'src/layout/layout'; import type { CompSummaryExternal } from 'src/layout/Summary/config.generated'; import type { RootState } from 'src/redux/store'; @@ -219,6 +220,11 @@ describe('Form', () => { async function render(layout = mockComponents, customState: Partial = {}) { await renderWithInstanceAndLayout({ renderer: () =>
, + router: PageNavigationRouter('FormLayout'), + queries: { + fetchLayouts: () => Promise.resolve({}), + fetchLayoutSettings: () => Promise.resolve({ pages: { order: ['FormLayout', '2', '3'] } }), + }, reduxState: { ...getInitialStateMock(), ...customState, diff --git a/src/components/form/Form.tsx b/src/components/form/Form.tsx index 89293fa989..f3a8c349a1 100644 --- a/src/components/form/Form.tsx +++ b/src/components/form/Form.tsx @@ -8,6 +8,7 @@ import { ErrorReport } from 'src/components/message/ErrorReport'; import { ReadyForPrint } from 'src/components/ReadyForPrint'; import { useLanguage } from 'src/features/language/useLanguage'; import { useAppSelector } from 'src/hooks/useAppSelector'; +import { useNavigatePage } from 'src/hooks/useNavigatePage'; import { GenericComponent } from 'src/layout/GenericComponent'; import { getFieldName } from 'src/utils/formComponentUtils'; import { extractBottomButtons, hasRequiredFields } from 'src/utils/formLayout'; @@ -15,24 +16,18 @@ import { useExprContext } from 'src/utils/layout/ExprContext'; import { getFormHasErrors, missingFieldsInLayoutValidations } from 'src/utils/validation/validation'; export function Form() { - const nodes = useExprContext(); const langTools = useLanguage(); + const { currentPageId } = useNavigatePage(); const validations = useAppSelector((state) => state.formValidations.validations); - const hasErrors = useAppSelector((state) => getFormHasErrors(state.formValidations.validations)); - const page = nodes?.current(); - const pageKey = page?.top.myKey; + const nodes = useExprContext(); - const [mainNodes, errorReportNodes] = React.useMemo(() => { - if (!page) { - return [[], []]; - } - return hasErrors ? extractBottomButtons(page) : [page.children(), []]; - }, [page, hasErrors]); + const page = nodes?.all?.()?.[currentPageId]; + const hasErrors = useAppSelector((state) => getFormHasErrors(state.formValidations.validations)); const requiredFieldsMissing = React.useMemo(() => { - if (validations && pageKey && validations[pageKey]) { + if (validations && validations[currentPageId]) { const requiredValidationTextResources: string[] = []; - page.flat(true).forEach((node) => { + page?.flat(true).forEach((node) => { const trb = node.item.textResourceBindings; const fieldName = getFieldName(trb, langTools); if ('required' in node.item && node.item.required && trb && 'requiredValidation' in trb) { @@ -40,15 +35,18 @@ export function Form() { } }); - return missingFieldsInLayoutValidations(validations[pageKey], requiredValidationTextResources, langTools); + return missingFieldsInLayoutValidations(validations[currentPageId], requiredValidationTextResources, langTools); } return false; - }, [validations, pageKey, page, langTools]); + }, [validations, currentPageId, page, langTools]); - if (!page) { - return null; - } + const [mainNodes, errorReportNodes] = React.useMemo(() => { + if (!page) { + return [[], []]; + } + return hasErrors ? extractBottomButtons(page) : [page.children(), []]; + }, [page, hasErrors]); return ( <> @@ -63,7 +61,7 @@ export function Form() { spacing={3} alignItems='flex-start' > - {mainNodes.map((n) => ( + {mainNodes?.map((n) => ( { const dispatch = useAppDispatch(); - const currentView = useAppSelector((state) => state.formLayout.uiConfig.currentView); + const { currentPageId, navigateToPage } = useNavigatePage(); + const { setFocusId } = usePageNavigationContext(); const [errorsMapped, errorsUnmapped] = useAppSelector(selectMappedUnmappedErrors); const allNodes = useExprContext(); const hasErrors = errorsUnmapped.length > 0 || errorsMapped.length > 0; @@ -59,12 +62,8 @@ export const ErrorReport = ({ nodes }: IErrorReportProps) => { return; } - if (currentView !== error.layout) { - dispatch( - FormLayoutActions.updateCurrentView({ - newView: error.layout, - }), - ); + if (currentPageId !== error.layout) { + navigateToPage(error.layout); } const allParents = componentNode?.parents() || []; @@ -116,17 +115,14 @@ export const ErrorReport = ({ nodes }: IErrorReportProps) => { FormLayoutActions.updateRepeatingGroupsEditIndex({ group: parentNode.item.id, index: childNode.rowIndex, + currentPageId, }), ); } } // Set focus - dispatch( - FormLayoutActions.updateFocus({ - focusComponentId: error.componentId, - }), - ); + setFocusId(error.componentId); }; const errorMessage = (message: string) => diff --git a/src/components/presentation/NavBar.test.tsx b/src/components/presentation/NavBar.test.tsx index edc8f03743..f4ee4736d2 100644 --- a/src/components/presentation/NavBar.test.tsx +++ b/src/components/presentation/NavBar.test.tsx @@ -5,87 +5,76 @@ import { userEvent } from '@testing-library/user-event'; import mockAxios from 'jest-mock-axios'; import { getFormLayoutStateMock } from 'src/__mocks__/getFormLayoutStateMock'; -import { getUiConfigStateMock } from 'src/__mocks__/getUiConfigStateMock'; import { getInitialStateMock } from 'src/__mocks__/initialStateMock'; import { NavBar } from 'src/components/presentation/NavBar'; -import { renderWithoutInstanceAndLayout } from 'src/test/renderWithProviders'; +import { mockWindow } from 'src/test/mockWindow'; +import { renderWithInstanceAndLayout } from 'src/test/renderWithProviders'; +import { ProcessTaskType } from 'src/types'; import type { IRawTextResource } from 'src/features/language/textResources'; +import type { PresentationType } from 'src/types'; import type { IAppLanguage } from 'src/types/shared'; afterEach(() => mockAxios.reset()); interface RenderNavBarProps { - showBackArrow: boolean; + currentPageId?: string; hideCloseButton: boolean; - showLanguageSelector: boolean; languageResponse?: IAppLanguage[]; + showLanguageSelector: boolean; textResources?: IRawTextResource[]; + type?: ProcessTaskType | PresentationType; + initialPage?: string; } const render = async ({ hideCloseButton, - showBackArrow, showLanguageSelector, languageResponse, + type = ProcessTaskType.Data, + initialPage, textResources = [], }: RenderNavBarProps) => { - const mockClose = jest.fn(); - const mockBack = jest.fn(); - const mockAppLanguageChange = jest.fn(); - - await renderWithoutInstanceAndLayout({ - renderer: () => ( - - ), + await renderWithInstanceAndLayout({ + renderer: () => , reduxState: { ...getInitialStateMock(), - formLayout: getFormLayoutStateMock({ - uiConfig: getUiConfigStateMock({ - hideCloseButton, - showLanguageSelector, - }), - }), + formLayout: getFormLayoutStateMock({}), }, + initialPage, queries: { fetchAppLanguages: () => languageResponse ? Promise.resolve(languageResponse) : Promise.reject(new Error('No languages mocked')), fetchTextResources: () => Promise.resolve({ language: 'nb', resources: textResources }), + fetchLayoutSettings: () => + Promise.resolve({ pages: { hideCloseButton, showLanguageSelector, order: ['1', '2', '3'] } }), }, reduxGateKeeper: (action) => 'type' in action && action.type === 'deprecated/setCurrentLanguage', }); - - return { mockClose, mockBack, mockAppLanguageChange }; }; describe('NavBar', () => { + const { mockAssign } = mockWindow(); it('should render nav', async () => { await render({ hideCloseButton: true, - showBackArrow: false, showLanguageSelector: false, }); screen.getByRole('navigation', { name: /Appnavigasjon/i }); }); it('should render close button', async () => { - const { mockClose } = await render({ + await render({ hideCloseButton: false, - showBackArrow: false, showLanguageSelector: false, }); const closeButton = screen.getByRole('button', { name: /Lukk Skjema/i }); await userEvent.click(closeButton); - expect(mockClose).toHaveBeenCalled(); + expect(mockAssign).toHaveBeenCalled(); }); it('should hide close button and back button', async () => { await render({ hideCloseButton: true, - showBackArrow: false, showLanguageSelector: false, }); expect(screen.queryAllByRole('button')).toHaveLength(0); @@ -93,19 +82,17 @@ describe('NavBar', () => { }); it('should render back button', async () => { - const { mockBack } = await render({ + await render({ hideCloseButton: true, - showBackArrow: true, showLanguageSelector: false, + type: ProcessTaskType.Data, + initialPage: 'Task_1/2', }); - const backButton = screen.getByTestId('form-back-button'); - await userEvent.click(backButton); - expect(mockBack).toHaveBeenCalled(); + expect(screen.getByTestId('form-back-button')).toBeInTheDocument(); }); it('should render and change app language', async () => { await render({ hideCloseButton: false, - showBackArrow: true, showLanguageSelector: true, languageResponse: [{ language: 'en' }, { language: 'nb' }], }); @@ -122,7 +109,6 @@ describe('NavBar', () => { it('should render app language with custom labels', async () => { await render({ hideCloseButton: false, - showBackArrow: true, showLanguageSelector: true, textResources: [ { id: 'language.selector.label', value: 'Velg språk test' }, diff --git a/src/components/presentation/NavBar.tsx b/src/components/presentation/NavBar.tsx index 9de95eab36..c758af50b3 100644 --- a/src/components/presentation/NavBar.tsx +++ b/src/components/presentation/NavBar.tsx @@ -6,41 +6,69 @@ import cn from 'classnames'; import { LanguageSelector } from 'src/components/presentation/LanguageSelector'; import classes from 'src/components/presentation/NavBar.module.css'; -import { FormLayoutActions } from 'src/features/form/layout/formLayoutSlice'; +import { usePageNavigationContext } from 'src/features/form/layout/PageNavigationContext'; +import { useUiConfigContext } from 'src/features/form/layout/UiConfigContext'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useAppDispatch } from 'src/hooks/useAppDispatch'; -import { useAppSelector } from 'src/hooks/useAppSelector'; +import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useNavigatePage } from 'src/hooks/useNavigatePage'; +import { PresentationType, ProcessTaskType } from 'src/types'; +import { httpGet } from 'src/utils/network/networking'; +import { getRedirectUrl } from 'src/utils/urls/appUrlHelper'; +import { returnUrlFromQueryParameter, returnUrlToMessagebox } from 'src/utils/urls/urlHelper'; export interface INavBarProps { - handleClose: () => void; - handleBack: (e: any) => void; - showBackArrow?: boolean; + type: PresentationType | ProcessTaskType; } const expandIconStyle = { transform: 'rotate(45deg)' }; -export const NavBar = (props: INavBarProps) => { - const dispatch = useAppDispatch(); +export const NavBar = ({ type }: INavBarProps) => { const { langAsString } = useLanguage(); - const { hideCloseButton, showLanguageSelector, showExpandWidthButton, expandedWidth } = useAppSelector( - (state) => state.formLayout.uiConfig, - ); + const { navigateToPage, previous } = useNavigatePage(); + + const { returnToView } = usePageNavigationContext(); + const party = useCurrentParty(); + const { hideCloseButton, showLanguageSelector, showExpandWidthButton, expandedWidth, toggleExpandedWidth } = + useUiConfigContext(); - const handleExpand = () => { - dispatch(FormLayoutActions.toggleExpandedWidth()); + const handleBackArrowButton = () => { + if (returnToView) { + navigateToPage(returnToView); + } else if (previous !== undefined && (type === ProcessTaskType.Data || type === PresentationType.Stateless)) { + navigateToPage(previous); + } }; + const handleModalCloseButton = () => { + const queryParameterReturnUrl = returnUrlFromQueryParameter(); + const messageBoxUrl = returnUrlToMessagebox(window.location.origin, party?.partyId); + if (!queryParameterReturnUrl && messageBoxUrl) { + window.location.assign(messageBoxUrl); + return; + } + + if (queryParameterReturnUrl) { + httpGet(getRedirectUrl(queryParameterReturnUrl)) + .then((response) => response) + .catch(() => messageBoxUrl) + .then((returnUrl) => { + window.location.assign(returnUrl); + }); + } + }; + + const showBackArrow = !!previous && (type === ProcessTaskType.Data || type === PresentationType.Stateless); return (