-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Stop workspace filter from scrolling and reordering on selection #90121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5caf8f0
126aaae
eaa1451
e51d77d
783f5e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import {useEffect, useState} from 'react'; | ||
|
|
||
| /** | ||
| * Returns an initially focused key that is cleared after the first render cycle. | ||
| * This prevents FlashList from auto-scrolling when data changes cause the key | ||
| * to transition from "not found" to "found" (e.g., clearing a search). | ||
| * | ||
| * Note: We use setTimeout instead of requestAnimationFrame because FlashList has a bug | ||
| * where clearing the focused key via requestAnimationFrame causes the list to scroll | ||
| * to the end unexpectedly. | ||
| */ | ||
| function useInitiallyFocusedKey(computeKey: () => string | undefined): string | undefined { | ||
| const [initiallyFocusedKey, setInitiallyFocusedKey] = useState(computeKey); | ||
|
|
||
| useEffect(() => { | ||
| const id = setTimeout(() => { | ||
| setInitiallyFocusedKey(undefined); | ||
| }); | ||
| return () => clearTimeout(id); | ||
| }, []); | ||
|
|
||
| return initiallyFocusedKey; | ||
| } | ||
|
|
||
| export default useInitiallyFocusedKey; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,13 @@ | ||
| import {emailSelector} from '@selectors/Session'; | ||
| import React, {useCallback, useMemo, useRef, useState} from 'react'; | ||
| import React, {useCallback, useMemo, useState} from 'react'; | ||
| import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; | ||
| import HeaderWithBackButton from '@components/HeaderWithBackButton'; | ||
| import ScreenWrapper from '@components/ScreenWrapper'; | ||
| import SearchFilterPageFooterButtons from '@components/Search/SearchFilterPageFooterButtons'; | ||
| import SelectionList from '@components/SelectionList'; | ||
| import UserListItem from '@components/SelectionList/ListItem/UserListItem'; | ||
| import type {SelectionListHandle} from '@components/SelectionList/types'; | ||
| import useDebouncedState from '@hooks/useDebouncedState'; | ||
| import useInitiallyFocusedKey from '@hooks/useInitiallyFocusedKey'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useNetwork from '@hooks/useNetwork'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
|
|
@@ -38,8 +38,6 @@ function SearchFiltersWorkspacePage() { | |
| const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); | ||
| const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); | ||
| const shouldShowLoadingIndicator = isLoadingApp && !isOffline; | ||
| const selectionListRef = useRef<SelectionListHandle<WorkspaceListItem>>(null); | ||
|
|
||
| const [selectedOptions, setSelectedOptions] = useState<string[]>(() => (searchAdvancedFiltersForm?.policyID ? Array.from(searchAdvancedFiltersForm?.policyID) : [])); | ||
|
|
||
| const {data, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({ | ||
|
|
@@ -49,8 +47,11 @@ function SearchFiltersWorkspacePage() { | |
| selectedPolicyIDs: selectedOptions, | ||
| searchTerm: debouncedSearchTerm, | ||
| localeCompare, | ||
| shouldSortSelectedToTop: false, | ||
| }); | ||
|
|
||
| const initiallyFocusedKey = useInitiallyFocusedKey(() => data.find((item) => item.isSelected)?.keyForList); | ||
|
|
||
| const selectWorkspace = useCallback( | ||
| (option: WorkspaceListItem) => { | ||
| const optionIndex = selectedOptions.findIndex((selectedOption: string) => { | ||
|
|
@@ -60,10 +61,6 @@ function SearchFiltersWorkspacePage() { | |
|
|
||
| if (optionIndex === -1 && option?.policyID) { | ||
| setSelectedOptions([...selectedOptions, option.policyID]); | ||
|
|
||
| requestAnimationFrame(() => { | ||
| selectionListRef.current?.scrollAndHighlightItem([option.keyForList]); | ||
| }); | ||
| } else { | ||
| const newSelectedOptions = [...selectedOptions.slice(0, optionIndex), ...selectedOptions.slice(optionIndex + 1)]; | ||
| setSelectedOptions(newSelectedOptions); | ||
|
|
@@ -114,12 +111,13 @@ function SearchFiltersWorkspacePage() { | |
| /> | ||
| ) : ( | ||
| <SelectionList<WorkspaceListItem> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MelvinBot
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch — added shouldUpdateFocusedIndex so the focus index updates correctly when toggling workspace selections (consistent with SearchMultipleSelectionPicker). |
||
| ref={selectionListRef} | ||
| data={data} | ||
| ListItem={UserListItem} | ||
| initiallyFocusedItemKey={initiallyFocusedKey} | ||
| onSelectRow={selectWorkspace} | ||
| textInputOptions={textInputOptions} | ||
| canSelectMultiple | ||
| shouldUpdateFocusedIndex | ||
| shouldShowLoadingPlaceholder={isLoadingOnyxValue(policiesResult) || !didScreenTransitionEnd} | ||
| disableMaintainingScrollPosition | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MelvinBot add
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — added |
||
| footerContent={ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MelvinBot is this safe to remove? Find PR that first introduced this