diff --git a/src/components/DynamicForm/index.tsx b/src/components/DynamicForm/index.tsx
index 851ea83d86a2..380be536fda2 100644
--- a/src/components/DynamicForm/index.tsx
+++ b/src/components/DynamicForm/index.tsx
@@ -1,8 +1,9 @@
-import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
+import ActivityIndicator from '@components/ActivityIndicator';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useSubPage from '@hooks/useSubPage';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import {findLastPageIndex} from '@libs/SubPageUtils';
@@ -16,6 +17,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import {Str} from 'expensify-common';
import React, {useEffect, useRef, useState} from 'react';
+import {View} from 'react-native';
import type {DynamicFormFlowProps, DynamicFormSubPageProps, DynamicFormValues} from './types';
@@ -60,6 +62,7 @@ function DynamicFormFlow({
layout = 'auto',
hasConfirmation = true,
}: DynamicFormFlowProps) {
+ const styles = useThemeStyles();
const {translate} = useLocalize();
const [draft, draftMetadata] = useOnyx(`${formID}Draft`);
const [sensitiveAnswersByForm] = useOnyx(ONYXKEYS.RAM_ONLY_DYNAMIC_FORM_SENSITIVE_ANSWERS);
@@ -312,7 +315,9 @@ function DynamicFormFlow({
shouldShowStepIndicator={isStepIndicatorAsked && stepNames.length > 1}
>
{isDraftLoading || isRedirecting || isOnUnavailablePage || (!hasPageToShow && pages.length > 0) ? (
-
+
+
+
) : (
;
+ return ;
}
if (decision === 'notFound') {
diff --git a/src/pages/workspace/connections/ConnectionsMergePageBase.tsx b/src/pages/workspace/connections/ConnectionsMergePageBase.tsx
index fb8c65ec2a9e..6d9d785f3bb2 100644
--- a/src/pages/workspace/connections/ConnectionsMergePageBase.tsx
+++ b/src/pages/workspace/connections/ConnectionsMergePageBase.tsx
@@ -152,10 +152,7 @@ function ConnectionsMergePageBase({policyID, category, cards, shouldBeBlocked}:
fullPageNotFoundViewProps={{onBackButtonPress: () => Navigation.goBack(ROUTES.WORKSPACE_CONNECTIONS.getRoute(policyID))}}
>
{isFetchingConnections ? (
-
+
) : (
Navigation.goBack(ROUTES.WORKSPACE_CONNECTIONS.getRoute(policyID))}}
>
{isLoading ? (
-
+
) : (
({
}));
jest.mock('@libs/Navigation/Navigation', () => ({navigate: jest.fn(), goBack: jest.fn()}));
jest.mock('@components/ScreenWrapper', () => jest.fn(({children}: {children: React.ReactNode}) => children));
+jest.mock('@components/ActivityIndicator', () => jest.fn(() => null));
jest.mock('@components/FullscreenLoadingIndicator', () => jest.fn(() => null));
jest.mock('@components/InteractiveStepWrapper', () => jest.fn((props: {stepNames: string[]; startStepIndex: number}) => mockStepWrapper(props)));
jest.mock('@components/Header/composed/HeaderWithBackButtonAndTitle', () =>
@@ -429,6 +433,18 @@ describe('DynamicFormFlow', () => {
expect(subPageOptions.skipPages).toEqual([]);
});
+ it('shows a spinner below the header while it loads, so no second Go Back button covers the header', async () => {
+ // Given a flow that is redirecting to another page
+ // When the flow renders
+ await renderFlow({isRedirecting: true});
+
+ // Then the header is drawn with a plain spinner under it instead of a page
+ expect(HeaderWithBackButtonAndTitle).toHaveBeenCalled();
+ expect(ActivityIndicator).toHaveBeenCalled();
+ expect(FullscreenLoadingIndicator).not.toHaveBeenCalled();
+ expect(mockCurrentPage).not.toHaveBeenCalled();
+ });
+
it('sends the user back to the incomplete page without pushing a second copy of it', async () => {
// Given a form whose SSN was lost when the app restarted
const {subPageOptions, onSubmit} = await renderFlow({draft: {firstName: 'Jane', legalType: 'PRIVATE'}});