Skip to content

Web/Safari - Account Settings - Confirmation buttons in RHP settings cut off in Safari #86276

Closed

Description

@nlemma

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.3.42-0
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from BrowserStack: Exploratory when retest issue https://github.com/Expensify/Expensify/issues/432325
Email or phone of affected tester (no customers): fischer9966+053122@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Critical User Impact
Device used: MacOS 26.3.1 / Safari
App Component: User Settings

Action Performed:

  1. Go to staging.new.expensify.com in Safari
  2. Login with any account
  3. Go to Account > Any Settings with confirmation buttons eq. Profile > Display name, Contact methods. Profile > Security > Any menu point, etc
  4. Verify that RHP with settings has confirmation buttons in the bottom

Expected Result:

RHP with settings has confirmation buttons in the bottom in Safari

Actual Result:

Bug 1:
Confirmation buttons in RHP settings cut off in Safari. Main window content is not displayed.
Bug 2:
A blank background gets displayed.

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari

Screenshots/Videos

Bug7111091_1774296679908.Safari-RHP-buttons-Cuts-off.mp4
Bug.2.mov

View all open jobs on GitHub

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    BugSomething is broken. Auto assigns a BugZero manager.
    on Mar 24, 2026
  2. melvin-bot commented on Mar 24, 2026

    @melvin-bot

    Triggered auto assignment to @youssef-lr (DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.

  3. melvin-bot commented on Mar 24, 2026

    @melvin-bot

    💬 A slack conversation has been started in #expensify-open-source

  4. github-actions commented on Mar 24, 2026

    @github-actions
    Contributor

    👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:

    1. Identify the pull request that introduced this issue and revert it.
    2. Find someone who can quickly fix the issue.
    3. Fix the issue yourself.
  5. OSBotify commented on Mar 24, 2026

    @OSBotify
    Contributor

    Investigation Summary

    Classification: Frontend bug
    Causing PR: #85887 - "Revert focus restoration mechanism" by @marcaaron (High confidence)
    Related Issues: This PR was a revert of #79834 to fix issues #85866, #85874, #85878, #85886, #85896, #85895

    Recommendation: REVERT

    PR #85887 should be reverted from staging. This was a CP (Cherry-Pick) to staging that is causing Safari-specific layout issues.

    Labels: No label changes needed - this is correctly labeled as DeployBlockerCash (App/frontend issue)

    Detailed Analysis

    Evidence

    PR #85887 was merged to staging on March 20, 2026 (label "CP Staging" confirms it is staging-only). The issue appeared after this date and is reproducible only on staging, not production, which perfectly matches the PR's deployment timeline.

    Verification

    The PR modified src/components/ScreenWrapper/ScreenWrapperContainer.tsx, adding Safari-specific viewport resize handling (lines 189-208 in current code):

    useEffect(() => {
        const handleViewportResize = () => {
            if (!isSafari()) {
                return;
            }
            setIsBlurred(false); // This is the problematic line
        };
        const removeResizeListener = addViewportResizeListener(handleViewportResize);
        return () => {
            removeResizeListener();
        };
    }, [setIsBlurred]);

    Root Cause

    When Safari resizes the viewport (which happens frequently in Safari), the handler sets isBlurred(false). This state controls whether maxHeight is applied to the KeyboardAvoidingView (line 230):

    style={[styles.w100, styles.h100, !isBlurred ? {maxHeight} : undefined, ...]}

    In Safari RHP settings pages, this maxHeight restriction is cutting off the bottom confirmation buttons because the content exceeds the calculated viewport height. The main window content becomes hidden, and buttons are not visible.

    This is a Safari-specific bug because only Safari triggers the viewport resize handler that forces isBlurred to false, thereby always applying the height restriction.

    Why This is a Frontend Bug

    The fix requires reverting PR #85887 from staging (or finding an alternative fix in the App codebase). No backend changes are involved.

  6. MelvinBot commented on Mar 24, 2026

    @MelvinBot
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    On Safari (MacOS), confirmation buttons (Save/Update) in RHP settings pages (Profile > Display Name, Contact Methods, Security, etc.) are cut off, main window content doesn't render properly, and a blank background appears. This is reproducible in staging (v9.3.42-0) but not in production, indicating a regression from a recently merged PR.

    What is the root cause of that problem?

    After analyzing all 42 PRs in the current staging release (comparing production to staging), the only PR that modified the shared form infrastructure used by the affected settings pages is PR #84516 ("fix: The displayed error messages are not announced once they appear").

    This PR modified FormProvider.tsx, FormWrapper.tsx, FormContext.tsx, FormAlertWrapper.tsx, and FormHelpMessage.tsx — the exact component chain used by every RHP settings page with confirmation buttons (DisplayNamePage uses ScreenWrapper → FormProvider → FormWrapper → FormAlertWithSubmitButton).

    Key changes from PR #84516:

    1. New hidden <Text> elements with style={styles.hiddenElementOutsideOfWindow} (position: absolute, top: -10000) are inserted inside FormWrapper's FormElement (between form children and the submit button) and inside FormHelpMessage
    2. New state and context values added to FormProvider (errorAnnouncementKey, getErrorAnnouncementKey, getFallbackAnnouncementMessage) causing additional re-render cycles
    3. FormAlertWrapper now always passes a non-empty announcementMessage to FormHelpMessage (previously passed empty string when no message existed)

    Safari has documented quirks with position: absolute elements inside flex/scroll containers, and the RHP settings layout already has Safari-specific handling (ScreenWrapperContainer.tsx:230 removes maxHeight when isBlurred is true on Safari, useRHPScreenOptions.ts:47-49 applies a custom card style interpolator with overflow: hidden for Safari). The combination of the new hidden elements and additional re-renders from PR #84516 likely triggers a Safari-specific layout calculation issue in the deeply nested flex hierarchy: position:fixed card → KeyboardAvoidingView(w100, h100, maxHeight) → PickerAvoidingView(flex:1) → ScrollView(flex:1, flexGrow:1) → FormElement → [children + hidden Text + SubmitButton].

    Important note: The core Safari layout infrastructure (ScreenWrapperContainer, browser detection, viewport handling, card styles) is completely unchanged between production and staging. The regression comes from how PR #84516's form changes interact with Safari's rendering engine in this pre-existing layout hierarchy.

    What changes do you think we should make in order to solve the problem?

    Option A (Recommended — targeted fix): In FormWrapper.tsx, move the hidden announcement <Text> elements outside of the FormElement/ScrollView container so they don't participate in the flex layout calculation. Specifically, render them as siblings of the ScrollView within the outer View(flex:1) wrapper, or use a React Portal to render them outside the form hierarchy entirely.

    Option B (Quick verification): Revert PR #84516 on staging to confirm it's the cause, then re-land with the fix from Option A applied.

    The relevant files to modify:

    • src/components/Form/FormWrapper.tsx — move hidden <Text> elements out of FormElement
    • src/components/FormHelpMessage.tsx — similarly move hidden <Text> out of the form content flow

    What alternative solutions did you explore? (Optional)


    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot to refine this analysis, or explain why you are rejecting Melvin's proposal.

  7. youssef-lr commented on Mar 24, 2026

    @youssef-lr
    Contributor

    Checking

  8. youssef-lr commented on Mar 25, 2026

    @youssef-lr
    Contributor

    @Krishna2323 can you take a look please? this seems related to your PR

  9. 1 remaining item

  10. Krishna2323 commented on Mar 25, 2026

    @Krishna2323
    Contributor

    @MelvinBot are you sure #84516 is the cause? I can’t reproduce this on staging or main. The PR also isn't deployed to staging yet. Am I missing something?

  11. MelvinBot commented on Mar 25, 2026

    @MelvinBot
    Contributor

    Thanks for the feedback! I double-checked, and PR #84516 is deployed to staging. Here's the verification:

    git log origin/staging --oneline --grep='84516'
    → 55c79172935 Merge pull request #84516
    
    git log origin/production --oneline --grep='84516'
    → (empty)
    

    The staging deploy checklist also lists it as checked. So the PR is in staging but not production, which matches the timing of this regression.

    Regarding not being able to reproduce: The hidden <Text> elements from your PR only render conditionally:

    • FormWrapper hidden Text: only when errorAnnouncementKey > 1 (after form submission with server error)
    • FormHelpMessage hidden Text: only when shouldReannounceOnSubmit && hasError && errorAnnouncementKey > 0

    If you're testing just by loading the page without triggering form errors, the hidden elements wouldn't render, which could explain why you can't reproduce it. The issue may require:

    1. Submitting a form with a validation error first (to trigger errorAnnouncementKey increment)
    2. Being on a specific Safari version (the reporter used macOS 26.3.1 / Safari)
    3. Interacting with the form inputs (to trigger the Safari isBlurred state in ScreenWrapperContainer.tsx:230)

    However, the FormAlertWrapper change (always passing a non-empty announcementMessage instead of empty string) and the additional FormContext values (getErrorAnnouncementKey, getFallbackAnnouncementMessage) are active on every render, not just after errors. These could cause additional re-render cycles that interact with Safari's layout engine in the constrained maxHeight=windowHeight RHP container.

    Suggested debugging steps:

    1. Test on Safari specifically (the reporter used macOS Safari, not Chrome)
    2. On the Display Name page, edit the name, trigger a validation error by clearing both fields and hitting Save, then try navigating
    3. Check Safari DevTools for the computed height of the KeyboardAvoidingView and FormElement containers — are they overflowing the maxHeight constraint?

    I still believe PR #84516 is the most likely cause since it's the only change between production and staging that touches the shared form infrastructure used by all affected pages. No other PR modified ScreenWrapper, ScreenWrapperContainer, navigation card styles, or the settings pages themselves.

  12. Krishna2323 commented on Mar 25, 2026

    @Krishna2323
    Contributor

    @MelvinBot I still can't repro the issue. BTW the same style on View and test is used in TextInputMeasurement component.

  13. MelvinBot commented on Mar 25, 2026

    @MelvinBot
    Contributor

    Thanks for the feedback — that's a valid point. The hiddenElementOutsideOfWindow style is indeed already used in TextInputMeasurement (which renders on these same pages) without causing Safari layout issues, so the hidden <Text> elements alone are unlikely to be the root cause.

    After broadening the investigation, here's an updated analysis:

    Key Observation: macOS 26.3.1 / Safari 26

    The reporter is on macOS 26.3.1 (Tahoe) which runs Safari 26 — a very new engine version. The codebase has extensive Safari-specific workarounds for position: fixed height calculation bugs (documented in issues #12005, #17824, #20709):

    • getNavigationModalCardStyles uses position: fixed instead of absolute specifically to work around Safari height issues (src/styles/utils/getNavigationModalCardStyles/index.ts:6-14)
    • ScreenWrapperContainer disables minHeight for Safari (src/components/ScreenWrapper/ScreenWrapperContainer.tsx:123)
    • RHP uses a custom cardStyleInterpolator on Safari with overflow: hidden (src/libs/Navigation/AppNavigator/useRHPScreenOptions.ts:47-49)

    If you're testing on a different macOS/Safari version (e.g., macOS 15 / Safari 18), that could explain why you can't reproduce it. The regression may be Safari 26-specific.

    Revised Candidate PRs

    1. PR Preserve deep-linked report route during onboarding guard redirect #85556 ("Preserve deep-linked report route during onboarding guard redirect") — Strong candidate for the "blank background" symptom. It changes RootStackRouter.ts:97-118 to preserve existing fullscreen routes during navigation guard redirects instead of resetting the stack. If a guard redirect fires while in Settings, the stack could end up in an unexpected state with Settings partially rendered underneath.

    2. PR fix: The displayed error messages are not announced once they appear. #84516 — Weakened but not fully ruled out. The hidden elements only render when errorAnnouncementKey > 0 (after form submission with errors). If the bug occurs on initial page load before any form interaction, this PR is not the cause.

    3. Safari 26 engine changes — The existing position: fixed + overflow: hidden + disabled minHeight workarounds may behave differently in Safari 26's rendering engine.

    Suggested Next Steps

  14. removed
    DeployBlockerCashThis issue or pull request should block deployment
    on Mar 25, 2026
  15. grgia commented on Mar 25, 2026

    @grgia
    Contributor

    I also cannot reproduce

  16. melvin-bot commented on Mar 30, 2026

    @melvin-bot

    @youssef-lr Huh... This is 4 days overdue. Who can take care of this?

  17. melvin-bot commented on Mar 30, 2026

    @melvin-bot

    @youssef-lr Huh... This is 4 days overdue. Who can take care of this?

  18. melvin-bot commented on Mar 30, 2026

    @melvin-bot

    @youssef-lr Eep! 4 days overdue now. Issues have feelings too...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2Engineering

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions