Skip to content

[Awaiting Payment 26th Sept] [$250] Selecting a filter and using keyboard return on the filter confirmation screen ends up clearing filters. #48108

Closed

Description

@m-natarajan

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.0.25-7
Reproducible in staging?: Y
Reproducible in production?: Y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Expensify/Expensify Issue URL:
Issue reported by: @JmillsExpensify
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1724762642354929

Action Performed:

  1. Go to Search
  2. Click on Filters
  3. Click on a specific filter
  4. Enter your query, use return on your keyboard to confirm
  5. You’re taken back to the main filters page, click on another filter
  6. Enter your query, use return on your keyboard to confirm
  7. Now enter the return key again on the filter summary/confirmation screen

Expected Result:

No filters are cleared

Actual Result:

All filters are cleared, nothing happens.

Workaround:

Unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

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

Screenshots/Videos

CleanShot 2024-08-27 at 14 43 10

Recording.496.mp4

Add any screenshot/video evidence

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~01e1f39a3a6d7c0f4a
  • Upwork Job ID: 1828501186366247316
  • Last Price Increase: 2024-09-03
  • Automatic offers:
    • alitoshmatov | Reviewer | 103916367
    • suneox | Contributor | 103916369
Issue OwnerCurrent Issue Owner: @trjExpensify

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Aug 27, 2024
  2. melvin-bot commented on Aug 27, 2024

    @melvin-bot

    Triggered auto assignment to @trjExpensify (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

  3. self-assigned this
    on Aug 27, 2024
  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Aug 27, 2024
  5. melvin-bot commented on Aug 27, 2024

    @melvin-bot
  6. changed the title [-]Selecting a filter and using keyboard return on the filter confirmation screen ends up clearing filters.[/-] [+][$250] Selecting a filter and using keyboard return on the filter confirmation screen ends up clearing filters.[/+] on Aug 27, 2024
  7. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Aug 27, 2024
  8. melvin-bot commented on Aug 27, 2024

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @alitoshmatov (External)

  9. luacmartins commented on Aug 27, 2024

    @luacmartins
    Contributor

    This is related to focus trap. It seems like the top back button is selected when Enter is pressed, which navigates the user back. This also seems to happen in other flows, e.g. Submit expense

  10. abzokhattab commented on Aug 27, 2024

    @abzokhattab
    Contributor

    Edited by proposal-police: This proposal was edited at 2024-08-27 18:38:39 UTC.

    Proposal

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

    Focus is set on the back button when navigating to the filter page using keyboard

    What is the root cause of that problem?

    when the filter page is first rendered, the focus trap keeps the focus on the back button and doesn't move to the confirm button.

    we solved this issue in the submit report confirmation page by bluring the active element so the focus moves from the back header to the button after the timeout:

    const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
    useFocusEffect(
    useCallback(() => {
    focusTimeoutRef.current = setTimeout(() => {
    InteractionManager.runAfterInteractions(() => {
    blurActiveElement();
    });
    }, CONST.ANIMATED_TRANSITION);
    return () => focusTimeoutRef.current && clearTimeout(focusTimeoutRef.current);
    }, []),
    );

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

    we should do the same in the filter page to keep both flows consistent and don't introduce breaking changes to the focus trap logic.

    Optional

    this logic can be moved to a hook and used when needed to avoid code duplication

    result

    Screen.Recording.2024-08-27.at.8.47.38.PM.mov
  11. Krishna2323 commented on Aug 27, 2024

    @Krishna2323
    Contributor

    Proposal

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

    • Selecting a filter and using keyboard return on the filter confirmation screen ends up clearing filters.

    • This problem was faced in the submit expense flow, to solve that issue we manually moved removed the focus from the back button.

    const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
    useFocusEffect(
    useCallback(() => {
    focusTimeoutRef.current = setTimeout(() => {
    InteractionManager.runAfterInteractions(() => {
    blurActiveElement();
    });
    }, CONST.ANIMATED_TRANSITION);
    return () => focusTimeoutRef.current && clearTimeout(focusTimeoutRef.current);
    }, []),
    );

    What is the root cause of that problem?

    Due to the focus trap the focus is set to the first focus element instead of the submit button.

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

    • Instead of calling the blurActiveElement on every page. I suggest to add the function in the button component.
      function KeyboardShortcutComponent({isDisabled = false, isLoading = false, onPress = () => {}, pressOnEnter, allowBubble, enterKeyEventListenerPriority}: KeyboardShortcutComponentProps) {
      const isFocused = useIsFocused();
      const activeElementRole = useActiveElementRole();
      const shouldDisableEnterShortcut = useMemo(() => accessibilityRoles.includes(activeElementRole ?? '') && activeElementRole !== CONST.ROLE.PRESENTATION, [activeElementRole]);
      const keyboardShortcutCallback = useCallback(
      (event?: GestureResponderEvent | KeyboardEvent) => {
      if (!validateSubmitShortcut(isDisabled, isLoading, event)) {
      return;
      }
      onPress();
      },
      [isDisabled, isLoading, onPress],
      );
    • When subscribing to the keyboard short, we can check if pressOnEnter is true or not and based on that we will call blurActiveElement. After making this change we can remove all the instances which is used in a component to remove the focus from the back button.
        const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
    
        useFocusEffect(
            useCallback(() => {
                if (pressOnEnter) {
                    focusTimeoutRef.current = setTimeout(() => {
                        InteractionManager.runAfterInteractions(() => {
                            blurActiveElement();
                        });
                    }, CONST.ANIMATED_TRANSITION);
                }
                return () => {
                    if (!focusTimeoutRef.current) {
                        return;
                    }
                    clearTimeout(focusTimeoutRef.current);
                };
            }, [pressOnEnter]),
        );

    What alternative solutions did you explore? (Optional)



    Result

  12. tsa321 commented on Aug 28, 2024

    @tsa321
    Contributor

    Edited by proposal-police: This proposal was edited at 2024-08-28 08:54:02 UTC.

    Proposal

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

    When entering a search filter on the advanced search page, pressing Enter and then pressing Enter again does not proceed with the search request and ends up clearing the search filter.

    What is the root cause of the problem?

    When the screen is mounted, the focus trap sets the initialFocus to the first focusable element:

    initialFocus: (focusTrapContainers) => {
    if (!canFocusInputOnScreenFocus()) {
    return false;
    }
    const isFocusedElementInsideContainer = focusTrapContainers?.some((container) => container.contains(document.activeElement));
    if (isFocusedElementInsideContainer) {
    return false;
    }
    return undefined;

    Since the document's activeElement is outside of the screen (the view result button is not focused), the initialFocus will return undefined and will focus the first element (in this case, the header back button). When the user presses Enter, the back button is pressed instead of the View result button.

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

    The focus trap already listens for the focus event and checks whether the focused element is within the element to which it is attached (in this case, within the screen). It will automatically move the focus inside the screen if it is outside. For example, when the user presses the Tab key, the focus trap will focus the first element of the screen if the current active element is outside the screen. Therefore, we do not need the focus trap to auto-focus when the screen is mounted. The auto-focus behavior of the focus trap will also interfere with the auto-focus applied to elements inside the screen.

    We can disable the initialFocus. In the FocusTrapForScreen options:

    focusTrapOptions={{
    trapStack: sharedTrapStack,

    we could add an onActivate property with the function: () => document?.activeElement?.blur(). Then, in initialFocus:

    initialFocus: (focusTrapContainers) => {
    if (!canFocusInputOnScreenFocus()) {
    return false;
    }
    const isFocusedElementInsideContainer = focusTrapContainers?.some((container) => container.contains(document.activeElement));
    if (isFocusedElementInsideContainer) {
    return false;
    }
    return undefined;
    },

    we should always set it to false or return false.

    The focus trap already listens for the focus event and will check whether the focused element is inside the element to which it is attached (in this case, inside the screen) and will automatically move the focus inside the screen if it is outside.

    The code change (in legacy mode, to test set USE_REACT_STRICT_MODE to false) could be:

    focusTrapOptions={{
    +                onActivate: () => {
    +                    document?.activeElement?.blur();
    +               },
                    trapStack: sharedTrapStack,
                    allowOutsideClick: true,
                    fallbackFocus: document.body,
                    delayInitialFocus: CONST.ANIMATED_TRANSITION,
                    initialFocus: (focusTrapContainers) => {
    -                     if (!canFocusInputOnScreenFocus()) {
    -                        return false;
    -                     }
    
    -                     const isFocusedElementInsideContainer = focusTrapContainers?.some((container) => container.contains(document.activeElement));
    -                     if (isFocusedElementInsideContainer) {
    -                        return false;
    -                     }
    -                    return undefined;
    +                    return false;
                    },
                    setReturnFocus: (element) => {
    +                    return false;
    -                    if (document.activeElement && document.activeElement !== document.body) {
    -                        return false;
    -                    }
    -                    return element;
                    },
                    ...(focusTrapSettings?.focusTrapOptions ?? {}),
                }}

    What alternative solutions did you explore? (2)

    On other pages, we use useAutoFocusInput(); as shown in:

    const {inputCallbackRef} = useAutoFocusInput();

    const {inputCallbackRef} = useAutoFocusInput();

    const {inputCallbackRef} = useAutoFocusInput();

    etc..

    We could also use it for AdvancedSearchFilters. To do this, we can add:

    const { inputCallbackRef } = useAutoFocusInput();

    Then, in:

    <FormAlertWithSubmitButton
    buttonText={translate('search.viewResults')}

    we add buttonRef={inputCallbackRef}. Additionally, since the hook is general and not limited to text input, we can rename it if necessary.

  13. 30 remaining items

  14. melvin-bot commented on Sep 18, 2024

    @melvin-bot
  15. added
    Awaiting PaymentAuto-added when associated PR is deployed to production
    and removed
    ReviewingHas a PR in review
    on Sep 23, 2024
  16. changed the title [-][$250] Selecting a filter and using keyboard return on the filter confirmation screen ends up clearing filters.[/-] [+][Awaiting Payment 26th Sept] [$250] Selecting a filter and using keyboard return on the filter confirmation screen ends up clearing filters.[/+] on Sep 23, 2024
  17. trjExpensify commented on Sep 23, 2024

    @trjExpensify
    Contributor

    PR on prod, updated the title to reflect the payment hold.

  18. melvin-bot commented on Sep 24, 2024

    @melvin-bot

    @suneox @trjExpensify @luacmartins @alitoshmatov this issue is now 4 weeks old, please consider:

    • Finding a contributor to fix the bug
    • Closing the issue if BZ has been unable to add the issue to a VIP or Wave project
    • If you have any questions, don't hesitate to start a discussion in #expensify-open-source

    Thanks!

  19. trjExpensify commented on Sep 25, 2024

    @trjExpensify
    Contributor

    Go away, melvin.

  20. trjExpensify commented on Sep 26, 2024

    @trjExpensify
    Contributor

    Payment summary as follows:

    Paid, closing!

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

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething is broken. Auto assigns a BugZero manager.ExternalAdded to denote the issue can be worked on by a contributorWeeklyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions