Skip to content

Filter disabled report field values out of the Recent section#94764

Draft
MelvinBot wants to merge 1 commit into
mainfrom
claude-disabledReportFieldInRecent
Draft

Filter disabled report field values out of the Recent section#94764
MelvinBot wants to merge 1 commit into
mainfrom
claude-disabledReportFieldInRecent

Conversation

@MelvinBot

Copy link
Copy Markdown
Contributor

Explanation of Change

The value selector for a list report field renders two lists: All (built from fieldOptions, which the caller already filters to exclude disabled values) and Recent (read straight from the per-user recentlyUsedReportFields Onyx NVP). The Recent source was never cross-checked against the enabled options, so a value that was used on a report and then disabled in workspace settings stayed in recentlyUsedReportFields and kept showing under Recent, while being correctly hidden from All.

This fixes it at the single convergence point — getReportFieldOptionsSection — by intersecting recentlyUsedOptions with the (already disabled-filtered) options set, so the Recent section can never contain a value that isn't in All:

const enabledOptionSet = new Set(options);
const filteredRecentlyUsedOptions = recentlyUsedOptions.filter((o) => !selectedKeySet.has(o) && enabledOptionSet.has(o));

Because options arriving here is already filtered to enabled values, this guarantees Recent ⊆ enabled options with no extra data plumbing. As a bonus, it also drops any stale recent value that was deleted from the field entirely. Filtering at render time (rather than pruning the NVP on disable) is the correct fix because recentlyUsedReportFields is a per-user NVP — an admin disabling a policy-level value cannot rewrite every member's recent list, so the stale value must be filtered on read.

Fixed Issues

$ #94372
PROPOSAL: #94372 (comment)

Tests

// TODO: The human co-author must fill out the tests you ran before marking this PR as "ready for review"
// Please describe what tests you performed that validates your changed worked.

  1. Create a workspace list report field with at least one value.
  2. Use that report field value on one or more reports (so it lands in Recent).
  3. Disable that report field value in workspace settings.
  4. Create a new report or edit an existing one, open the report field value selector and review the Recent section.
  5. Verify the disabled value no longer appears under Recent (and is still absent from All).
  • Verify that no errors appear in the JS console

Offline tests

N/A — render-time filtering only.

QA Steps

// TODO: The human co-author must fill out the QA tests you ran before marking this PR as "ready for review".
// Please describe what QA needs to do to validate your changes and what areas do they need to test for regressions.

  1. Create a workspace list report field with at least one value.
  2. Use that report field value on one or more reports.
  3. Disable that report field value in workspace settings.
  4. Create a new report or edit an existing one, open the report field value selector and review the Recent section.
  5. Verify the disabled value no longer appears anywhere in the selector, including Recent.
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I wrote testing steps that cover success & fail scenarios (if applicable)
  • I added steps for local testing in the Tests section
  • I added steps for the expected offline behavior in the Offline steps section
  • I added steps for Staging and/or Production testing in the QA steps section
  • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I verified there are no console errors related to changes in this PR
  • I followed proper code patterns (see Reviewing the code)
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes
  • I verified that all the inputs inside a form are aligned with each other.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

Co-authored-by: Sahil <thesahindia@users.noreply.github.com>
@MelvinBot MelvinBot requested a review from a team June 27, 2026 06:59
@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/libs/ReportFieldOptionsListUtils.ts 0.00% <0.00%> (ø)
... and 11 files with indirect coverage changes

@thesahindia

Copy link
Copy Markdown
Member
  1. On device A, go to workspace settings > Reports > List > List values
  2. On device B, create a report and open the report field value selector
  3. On device A disable one of the list values

@MelvinBot what should happen if I follow the steps listed above, should the list value disappear on device B?

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Yes — with both devices online, the value should disappear on device B, and after this PR it disappears from both sections of the open selector.

Why: The value list comes from the policy. fieldOptions is derived as policyField.values.filter((_v, i) => !policyField.disabledOptions.at(i)), where policyField is read via useOnyx on the policy. When device A disables the value, the backend updates that field's disabledOptions and pushes it to device B over Pusher/Onyx. Device B re-renders, fieldOptions recomputes without the disabled value, and it drops out of the All list live — no need to reopen the selector.

The Recent section was the gap: it was read straight from the per-user recentlyUsedReportFields NVP and never cross-checked against enabled options, so on device B the just-disabled value would keep showing under Recent even though it vanished from All. This PR intersects Recent with the enabled options set, so it disappears from Recent at render time too.

Caveat — offline: if device B is offline when the value is disabled, it won't get the Pusher update, so nothing changes until it reconnects and the policy syncs. That's expected offline-first behavior, not a regression.

@thesahindia

Copy link
Copy Markdown
Member

@MelvinBot, could you check why it doesn't disappear only at ios native app?

@MelvinBot

Copy link
Copy Markdown
Contributor Author

I couldn't reproduce an iOS-only cause from the code — every step of the pipeline that removes a disabled value is platform-agnostic, so this looks like a runtime live-update / re-render difference on iOS native, not a defect in this PR's Recent-filter logic.

What I verified is platform-agnostic (no iOS-only branch):

  • policy is read via useOnyx and fieldOptions is recomputed from disabledOptions on every render.
  • This PR's Recent-section filter is present and correct.
  • useFlattenedSections recomputes whenever sections changes — no stale internal snapshot, no platform branch.
  • The screen is a NewDot RHP modal (ModalStackNavigators), so iOS HybridApp does not route this to OldDot. The freeze wrappers only freeze blurred screens, so they can't hold a stale render of the focused selector.

So the break is most likely before the JS re-render — the disabledOptions policy update not being delivered/applied on the foregrounded iOS device while the RHP is open, or the re-render not firing there. I can't confirm that statically; it needs a device repro or logs.

Two quick checks that would pinpoint it:

  1. On iOS, after disabling on device A, close and reopen the selector. If the corrected list shows on reopen, it's a live-update/re-render delivery issue (not this PR's logic).
  2. Try the same steps on Android native. If Android updates live but iOS doesn't, it's an iOS-specific Pusher/websocket foreground-delivery behavior; if neither does, it's native-in-general.

If it turns out the fix needs changes in Mobile-Expensify/HybridApp delivery, that's outside the App repo and should be escalated to an internal engineer. Can you confirm the results of checks 1 and 2 so I can narrow it down?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants