From 0ededd07a9c1b258138fa2088884a88a78a75b49 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Fri, 17 Jul 2026 16:03:28 +0200 Subject: [PATCH] Fix HeaderWithBackButton header buttons remounts --- src/components/HeaderWithBackButton/index.tsx | 56 +++++++++++- src/components/HeaderWithBackButton/types.ts | 12 +++ src/components/MoneyReportHeader.tsx | 17 ++-- src/components/MoneyRequestHeader.tsx | 13 +-- src/pages/domain/Admins/DomainAdminsPage.tsx | 7 +- src/pages/domain/BaseDomainMembersPage.tsx | 7 +- src/pages/domain/Groups/DomainGroupsPage.tsx | 7 +- src/pages/settings/Rules/ExpenseRulesPage.tsx | 7 +- src/pages/workspace/WorkspaceMembersPage.tsx | 15 ++-- src/pages/workspace/WorkspaceOverviewPage.tsx | 4 +- .../workspace/WorkspacePageWithSections.tsx | 22 ++++- .../categories/WorkspaceCategoriesPage.tsx | 7 +- .../distanceRates/PolicyDistanceRatesPage.tsx | 7 +- .../WorkspaceExpensifyCardListPage.tsx | 7 +- .../perDiem/WorkspacePerDiemPage.tsx | 7 +- .../reports/ReportFieldsListValuesPage.tsx | 7 +- src/pages/workspace/rules/PolicyRulesPage.tsx | 1 + .../workspace/rules/PolicyRulesPageRevamp.tsx | 41 ++++----- .../tags/DynamicWorkspaceViewTagsPage.tsx | 7 +- .../workspace/tags/WorkspaceTagsPage.tsx | 7 +- .../workspace/taxes/WorkspaceTaxesPage.tsx | 7 +- src/styles/index.ts | 12 +++ tests/ui/WorkspaceMembersTest.tsx | 85 ++++++++++++++----- .../components/ButtonWithDropdownMenuTest.tsx | 73 +++++++++++++++- 24 files changed, 308 insertions(+), 127 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index fe343876f8bc..891b51dd4483 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -82,6 +82,10 @@ function HeaderWithBackButton({ shouldDisplaySearchRouter = false, progressBarPercentage, style, + shouldDisplayResponsiveChildrenInSeparateLine = false, + responsiveChildren, + responsiveChildrenContainerStyle, + bottomContent, subTitleLink = '', shouldMinimizeMenuButton = false, openParentReportInCurrentTab = false, @@ -232,22 +236,55 @@ function HeaderWithBackButton({ shouldSetModalVisibility, ]); + const shouldUseMultiLineLayout = shouldDisplayResponsiveChildrenInSeparateLine || !!bottomContent; + const movableChildrenContent = !!responsiveChildren && ( + + {responsiveChildren} + + ); + return ( Keyboard.dismiss() : undefined} > - + {shouldShowBackButton && ( )} {middleContent} - + + {!shouldDisplayResponsiveChildrenInSeparateLine && movableChildrenContent} + + {children} {shouldShowDownloadButton && @@ -375,6 +418,15 @@ function HeaderWithBackButton({ {shouldDisplaySearchRouter && } {shouldDisplayHelpButton && } + {!!bottomContent && ( + + {bottomContent} + + )} + {shouldDisplayResponsiveChildrenInSeparateLine && movableChildrenContent} ); } diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 8d3cfc6a9917..929edafeefc5 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -166,6 +166,18 @@ type HeaderWithBackButtonProps = Partial & { /** Additional styles to add to the component */ style?: StyleProp; + /** Whether responsive children should be displayed in a full-width line below the main header row */ + shouldDisplayResponsiveChildrenInSeparateLine?: boolean; + + /** Header actions that can move to a separate line. Regular children always stay in the main row. */ + responsiveChildren?: ReactNode; + + /** Additional styles for the responsive children container */ + responsiveChildrenContainerStyle?: StyleProp; + + /** Full-width content displayed below the main header row and above responsive children */ + bottomContent?: ReactNode; + /** The URL link associated with the attachment's subtitle, if available */ subTitleLink?: string; diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 1fba8db9c14a..9bab0e38b60b 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -131,24 +131,17 @@ function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButt shouldShowBorderBottom={false} shouldEnableDetailPageNavigation openParentReportInCurrentTab - > - {shouldShowHeaderButtonsInHeaderRow && ( + shouldDisplayResponsiveChildrenInSeparateLine={!shouldShowHeaderButtonsInHeaderRow} + responsiveChildrenContainerStyle={!shouldShowHeaderButtonsInHeaderRow && styles.ph0} + responsiveChildren={ - )} - - {!shouldShowHeaderButtonsInHeaderRow && ( - - )} + } + /> diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index def715741ef2..e05cec14fd15 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -178,13 +178,14 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe onBackButtonPress={() => onBackButtonPress(isFromReviewDuplicates)} shouldEnableDetailPageNavigation openParentReportInCurrentTab={shouldOpenParentReportInCurrentTab} - > - {!shouldDisplayButtonsInSeparateLine && ( + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={ - )} + } + > {shouldDisplayTransactionNavigation && !!transaction && ( )} - {shouldDisplayButtonsInSeparateLine && ( - - )} {!!statusBarProps && ( - {!shouldDisplayButtonsInSeparateLine && headerContent} - - {shouldDisplayButtonsInSeparateLine && !!headerContent && {headerContent}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={headerContent} + /> - {!shouldDisplayButtonsInSeparateLine && !!headerContent && {headerContent}} - - {shouldDisplayButtonsInSeparateLine && !!headerContent && {headerContent}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={!!headerContent && {headerContent}} + /> - {!shouldDisplayButtonsInSeparateLine && {createGroupHeaderButton}} - - {shouldDisplayButtonsInSeparateLine && {createGroupHeaderButton}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={createGroupHeaderButton} + /> - {!shouldDisplayButtonsInSeparateLine && hasRules && headerButton} - - {shouldDisplayButtonsInSeparateLine && hasRules && {headerButton}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={hasRules && headerButton} + /> {translate('expenseRulesPage.subtitle')} diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index 6ee5d9799be5..2b561f1dbe8f 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -702,11 +702,10 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers const shouldDisplayButtonsInSeparateLine = useShouldDisplayButtonsInSeparateLine(); - const getHeaderButtons = () => { - if (!canWriteMembers) { - return null; - } - return (shouldUseNarrowLayout ? canSelectMultiple : selectedEmployees.length > 0) ? ( + const shouldShowBulkActions = shouldUseNarrowLayout ? canSelectMultiple : selectedEmployees.length > 0; + let headerButtons = null; + if (canWriteMembers) { + headerButtons = shouldShowBulkActions ? ( variant={CONST.BUTTON_VARIANT.SUCCESS} shouldAlwaysShowDropdownMenu @@ -742,7 +741,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers /> ); - }; + } const selectionModeHeader = isMobileSelectionModeEnabled && shouldUseNarrowLayout; @@ -751,7 +750,8 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers headerText={selectionModeHeader ? translate('common.selectMultiple') : translate('workspace.common.members')} route={route} icon={!selectionModeHeader ? illustrations.ReceiptWrangler : undefined} - headerContent={!shouldDisplayButtonsInSeparateLine && getHeaderButtons()} + headerContent={headerButtons} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} testID="WorkspaceMembersPage" shouldShowLoading={false} shouldUseHeadlineHeader={!selectionModeHeader} @@ -769,7 +769,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers > {() => ( <> - {shouldDisplayButtonsInSeparateLine && {getHeaderButtons()}} - {shouldDisplayButtonsInSeparateLine && {headerButtons}}
& { + Pick< + HeaderWithBackButtonProps, + | 'shouldShowThreeDotsButton' + | 'threeDotsMenuItems' + | 'shouldShowBackButton' + | 'onBackButtonPress' + | 'shouldDisplayResponsiveChildrenInSeparateLine' + | 'responsiveChildrenContainerStyle' + | 'bottomContent' + > & { shouldSkipVBBACall?: boolean; /** The text to display in the header */ @@ -146,6 +155,9 @@ function WorkspacePageWithSections({ shouldUseHeadlineHeader = true, addBottomSafeAreaPadding = false, modals, + shouldDisplayResponsiveChildrenInSeparateLine = false, + responsiveChildrenContainerStyle, + bottomContent, }: WorkspacePageWithSectionsProps) { const styles = useThemeStyles(); const policyID = route.params?.policyID; @@ -246,9 +258,11 @@ function WorkspacePageWithSections({ threeDotsMenuItems={threeDotsMenuItems} shouldUseHeadlineHeader={shouldUseHeadlineHeader} shouldDisplayHelpButton - > - {headerContent} - + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayResponsiveChildrenInSeparateLine} + responsiveChildrenContainerStyle={responsiveChildrenContainerStyle} + responsiveChildren={headerContent} + bottomContent={bottomContent} + /> {!isOffline && (isLoading || shouldShowInitialLoading) && shouldShowLoading && isFocused ? ( { if (isMobileSelectionModeEnabled) { clearTableSelection(); @@ -674,10 +675,8 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { Navigation.goBack(); }} - > - {!shouldDisplayButtonsInSeparateLine && getHeaderButtons()} - - {shouldDisplayButtonsInSeparateLine && !!getHeaderButtons() && {getHeaderButtons()}} + responsiveChildren={getHeaderButtons()} + /> {(!hasVisibleCategories || isLoading) && headerContent} diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index a09e16bfcdfa..68b77156ac90 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -470,10 +470,9 @@ function PolicyDistanceRatesPage({ } Navigation.goBack(); }} - > - {!shouldDisplayButtonsInSeparateLine && headerButtons} - - {shouldDisplayButtonsInSeparateLine && !!headerButtons && {headerButtons}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={headerButtons} + /> {isLoading && ( - {!shouldShowSelector && !shouldDisplayButtonsInSeparateLine && isBankAccountVerified && shouldShowHeaderButtons && getHeaderButtons()} - - {!shouldShowSelector && shouldDisplayButtonsInSeparateLine && isBankAccountVerified && shouldShowHeaderButtons && {getHeaderButtons()}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={!shouldShowSelector && isBankAccountVerified && shouldShowHeaderButtons && getHeaderButtons()} + /> {shouldShowSelector && ( - {!shouldDisplayButtonsInSeparateLine && headerButtons} - - {!!headerButtons && shouldDisplayButtonsInSeparateLine && {headerButtons}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={headerButtons} + /> {(!hasVisibleSubRates || isLoading) && subtitleContent} {isLoading && ( - {!shouldDisplayButtonsInSeparateLine && headerButtons} - - {shouldDisplayButtonsInSeparateLine && {headerButtons}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={headerButtons} + /> {translate('workspace.reportFields.listInputSubtitle')} diff --git a/src/pages/workspace/rules/PolicyRulesPage.tsx b/src/pages/workspace/rules/PolicyRulesPage.tsx index 3628e417bf5e..59d4c3265122 100644 --- a/src/pages/workspace/rules/PolicyRulesPage.tsx +++ b/src/pages/workspace/rules/PolicyRulesPage.tsx @@ -59,6 +59,7 @@ function PolicyRulesPage(props: PolicyRulesPageProps) { const {canWrite: canWriteRules, showReadOnlyModal, withReadOnlyFallback} = usePolicyFeatureWriteAccess(policy, CONST.POLICY.POLICY_FEATURE.RULES); const {isBetaEnabled} = usePermissions(); const isRulesRevampEnabled = isBetaEnabled(CONST.BETAS.RULES_REVAMP); + const isCustomAgentBetaEnabled = isBetaEnabled(CONST.BETAS.CUSTOM_AGENT); const [isAgentsRulesBannerDismissed = false] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {selector: agentsRulesBannerDismissedSelector}); diff --git a/src/pages/workspace/rules/PolicyRulesPageRevamp.tsx b/src/pages/workspace/rules/PolicyRulesPageRevamp.tsx index a82020acb847..e298965e23dd 100644 --- a/src/pages/workspace/rules/PolicyRulesPageRevamp.tsx +++ b/src/pages/workspace/rules/PolicyRulesPageRevamp.tsx @@ -315,28 +315,29 @@ function PolicyRulesPageRevamp({route}: PolicyRulesPageRevampProps) { shouldShowNotFoundPage={false} shouldShowLoading={false} addBottomSafeAreaPadding - headerContent={!shouldDisplayButtonsInSeparateLine && headerButtons} + headerContent={headerButtons} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildrenContainerStyle={shouldDisplayButtonsInSeparateLine && styles.pb5} + bottomContent={ + + + { + if (!isRulesTab(key)) { + return; + } + setSelectedRuleKeysByTab({}); + turnOffMobileSelectionMode(); + Tab.setSelectedTab(CONST.TAB.RULES_TAB_TYPE, key); + }} + /> + + + } > - - - - { - if (!isRulesTab(key)) { - return; - } - setSelectedRuleKeysByTab({}); - turnOffMobileSelectionMode(); - Tab.setSelectedTab(CONST.TAB.RULES_TAB_TYPE, key); - }} - /> - - - - {shouldDisplayButtonsInSeparateLine && !!headerButtons && {headerButtons}} {activeTab === RULES_TAB.GENERAL && ( - {!shouldDisplayButtonsInSeparateLine && headerButtons} - - {shouldDisplayButtonsInSeparateLine && !!headerButtons && {headerButtons}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={headerButtons} + /> {!hasDependentTags && ( - {!shouldDisplayButtonsInSeparateLine && getHeaderButtons()} - - {shouldDisplayButtonsInSeparateLine && !!getHeaderButtons() && {getHeaderButtons()}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={getHeaderButtons()} + /> {(!hasVisibleTags || isLoading) && headerContent} {isLoading && ( - {!shouldDisplayButtonsInSeparateLine && headerButtons} - - {shouldDisplayButtonsInSeparateLine && !!headerButtons && {headerButtons}} + shouldDisplayResponsiveChildrenInSeparateLine={shouldDisplayButtonsInSeparateLine} + responsiveChildren={headerButtons} + /> {(!hasVisibleTaxes || isLoading) && headerContent} {isLoading && ( width: '100%', }, + headerBarWithSeparateLine: { + height: 'auto', + flexWrap: 'wrap', + alignItems: 'flex-start', + paddingLeft: 0, + }, + + headerBarSeparateLineChildren: { + width: '100%', + paddingHorizontal: 20, + }, + reportSearchHeaderBar: { justifyContent: 'center', display: 'flex', diff --git a/tests/ui/WorkspaceMembersTest.tsx b/tests/ui/WorkspaceMembersTest.tsx index 0b542bc18c65..6735df60a009 100644 --- a/tests/ui/WorkspaceMembersTest.tsx +++ b/tests/ui/WorkspaceMembersTest.tsx @@ -8,6 +8,7 @@ import OnyxListItemProvider from '@components/OnyxListItemProvider'; import {CurrentReportIDContextProvider} from '@hooks/useCurrentReportID'; import * as useResponsiveLayoutModule from '@hooks/useResponsiveLayout'; import type ResponsiveLayoutResult from '@hooks/useResponsiveLayout/types'; +import * as useShouldDisplayButtonsInSeparateLineModule from '@hooks/useShouldDisplayButtonsInSeparateLine'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; @@ -34,25 +35,34 @@ TestHelper.setupGlobalFetchMock(); const Stack = createPlatformStackNavigator(); +const getPage = (initialRouteName: typeof SCREENS.WORKSPACE.MEMBERS, initialParams: WorkspaceSplitNavigatorParamList[typeof SCREENS.WORKSPACE.MEMBERS]) => ( + + + + + + + + + +); + const renderPage = (initialRouteName: typeof SCREENS.WORKSPACE.MEMBERS, initialParams: WorkspaceSplitNavigatorParamList[typeof SCREENS.WORKSPACE.MEMBERS]) => { - return render( - - - - - - - - - , - ); + return render(getPage(initialRouteName, initialParams)); }; -const selectCheckboxByMemberName = (memberName: string) => { +const defaultResponsiveLayout = { + isSmallScreenWidth: false, + shouldUseNarrowLayout: false, +} as ResponsiveLayoutResult; +let responsiveLayout = defaultResponsiveLayout; +let shouldDisplayButtonsInSeparateLine = false; + +const getRowByMemberName = (memberName: string) => { const memberEmailByName: Record = { Owner: 'owner@gmail.com', Admin: 'admin@example.com', @@ -61,7 +71,11 @@ const selectCheckboxByMemberName = (memberName: string) => { Self: 'test@example.com', }; const displayName = memberName === 'Owner' || memberName === 'Self' ? memberName : `${memberName} User`; - const row = screen.getByLabelText(new RegExp(`^${displayName}, ${memberEmailByName[memberName]}`)); + return screen.getByLabelText(new RegExp(`^${displayName}, ${memberEmailByName[memberName]}`)); +}; + +const selectCheckboxByMemberName = (memberName: string) => { + const row = getRowByMemberName(memberName); fireEvent.press(within(row).getByLabelText(TestHelper.translateLocal('common.select'))); }; @@ -114,10 +128,10 @@ describe('WorkspaceMembers', () => { }); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); }); - jest.spyOn(useResponsiveLayoutModule, 'default').mockReturnValue({ - isSmallScreenWidth: false, - shouldUseNarrowLayout: false, - } as ResponsiveLayoutResult); + responsiveLayout = defaultResponsiveLayout; + shouldDisplayButtonsInSeparateLine = false; + jest.spyOn(useResponsiveLayoutModule, 'default').mockImplementation(() => responsiveLayout); + jest.spyOn(useShouldDisplayButtonsInSeparateLineModule, 'default').mockImplementation(() => shouldDisplayButtonsInSeparateLine); }); afterEach(async () => { @@ -427,6 +441,35 @@ describe('WorkspaceMembers', () => { }); describe('Removing members who are approvers and non-approvers', () => { + it('keeps the bulk menu action working after an orientation change', async () => { + const routeParams = {policyID: policy.id}; + const {rerender, unmount} = renderPage(SCREENS.WORKSPACE.MEMBERS, routeParams); + await waitForBatchedUpdatesWithAct(); + await screen.findByText(ADMIN_OPTION); + + selectCheckboxByMemberName('Admin'); + fireEvent.press(await screen.findByTestId('WorkspaceMembersPage-header-dropdown-menu-button')); + await waitForBatchedUpdatesWithAct(); + + const removeText = TestHelper.translateLocal('workspace.people.removeMembersTitle', {count: 1}); + expect(screen.getByTestId(`PopoverMenuItem-${removeText}`)).toBeOnTheScreen(); + + shouldDisplayButtonsInSeparateLine = true; + rerender(getPage(SCREENS.WORKSPACE.MEMBERS, routeParams)); + + const removeMenuItem = await screen.findByTestId(`PopoverMenuItem-${removeText}`); + fireEvent.press(removeMenuItem, { + nativeEvent: {}, + type: 'press', + target: removeMenuItem, + currentTarget: removeMenuItem, + }); + await waitForBatchedUpdatesWithAct(); + + expect(await screen.findByLabelText(TestHelper.translateLocal('common.remove'))).toBeOnTheScreen(); + unmount(); + }); + it('should call workflow actions once when removing multiple members including an approver', async () => { const {unmount} = renderPage(SCREENS.WORKSPACE.MEMBERS, {policyID: policy.id}); await waitForBatchedUpdatesWithAct(); diff --git a/tests/ui/components/ButtonWithDropdownMenuTest.tsx b/tests/ui/components/ButtonWithDropdownMenuTest.tsx index 15d7611fc431..18e74179b820 100644 --- a/tests/ui/components/ButtonWithDropdownMenuTest.tsx +++ b/tests/ui/components/ButtonWithDropdownMenuTest.tsx @@ -1,8 +1,11 @@ -import {fireEvent, render, renderHook, screen} from '@testing-library/react-native'; +import {fireEvent, render, renderHook, screen, waitFor} from '@testing-library/react-native'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import * as usePopoverPositionModule from '@hooks/usePopoverPosition'; +import * as useWindowDimensionsModule from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; @@ -127,3 +130,71 @@ describe('ButtonWithDropdownMenu (dropdown arrow flip)', () => { expect(arrowIcon).not.toHaveStyle({transform: 'rotate(180deg)'}); }); }); + +describe('ButtonWithDropdownMenu in a responsive header', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('stays open and keeps its action when moved to a separate line', () => { + const onSelected = jest.fn(); + const options = [{value: 'action', text: 'Responsive action', onSelected, shouldCloseModalOnSelect: false}]; + const renderHeader = (shouldDisplayResponsiveChildrenInSeparateLine: boolean) => ( + {}} + customText="More" + shouldAlwaysShowDropdownMenu + isSplitButton={false} + /> + } + /> + ); + const {rerender} = render(renderHeader(false)); + + fireEvent.press(screen.getByText('More')); + expect(screen.getByText('Responsive action')).toBeOnTheScreen(); + + rerender(renderHeader(true)); + expect(screen.getByText('Responsive action')).toBeOnTheScreen(); + + const menuItem = screen.getByTestId('PopoverMenuItem-Responsive action'); + fireEvent.press(menuItem, { + nativeEvent: {}, + type: 'press', + target: menuItem, + currentTarget: menuItem, + }); + expect(onSelected).toHaveBeenCalledTimes(1); + }); + + it('remeasures an open menu when the window dimensions change', async () => { + const calculatePopoverPosition = jest.fn().mockResolvedValue({horizontal: 100, vertical: 100, width: 40, height: 40}); + let windowDimensions = {windowWidth: 800, windowHeight: 600}; + jest.spyOn(usePopoverPositionModule, 'default').mockReturnValue({calculatePopoverPosition}); + jest.spyOn(useWindowDimensionsModule, 'default').mockImplementation(() => windowDimensions); + const renderDropdown = () => ( + {}} + customText="More" + shouldAlwaysShowDropdownMenu + isSplitButton={false} + /> + ); + const {rerender} = render(renderDropdown()); + + fireEvent.press(screen.getByText('More')); + await waitFor(() => expect(calculatePopoverPosition).toHaveBeenCalledTimes(1)); + + windowDimensions = {windowWidth: 600, windowHeight: 800}; + rerender(renderDropdown()); + + await waitFor(() => expect(calculatePopoverPosition).toHaveBeenCalledTimes(2)); + }); +});