@samitouri / QOS-React-2 / commits / 4c522c4c38

Selected search result auto-opens collapsed nodes when necessary.

Also fixed an unrelated bug about when we reset search index when text changes.

Brian Vaughn committed Apr 9, 2019 at 18:11 UTC 4c522c4c3845aac5442fd5f12957da6167b609b4
1 file changed +45 -5
src/devtools/views/Components/TreeContext.js
+45 -5
@@ -24,6 +24,7 @@ import React, {
24 useEffect,
25 useMemo,
26 useReducer,
27 + useRef,
28 } from 'react';
29 import { createRegExp } from '../utils';
30 import { BridgeContext, StoreContext } from '../context';
@@ -198,7 +199,7 @@ function reduceSearchState(store: Store, state: State, action: Action): State {
199 selectedElementIndex,
200 } = state;
201
201 - const prevSearchIndex = searchIndex;
202 + let prevSearchIndex = searchIndex;
203 const prevSearchText = searchText;
204 const numPrevSearchResults = searchResults.length;
205
@@ -306,10 +307,25 @@ function reduceSearchState(store: Store, state: State, action: Action): State {
307 if (prevSearchIndex === null) {
308 searchIndex = 0;
309 } else {
309 - searchIndex = Math.min(
310 - ((prevSearchIndex: any): number),
311 - searchResults.length - 1
312 - );
310 + // Changes in search index or typing should override the selected element.
311 + // The one exception is when a search is broadened (e.g. "Cat" -> "Ca").
312 + // In this case, it's probably desirable to maintain a stable selection.
313 + // Replacements of text( e.g. "cat" -> "dog") likely require an index change.
314 + const didRelaxSearchText =
315 + prevSearchText.length > searchText.length &&
316 + prevSearchText.startsWith(searchText);
317 + if (didRelaxSearchText) {
318 + searchIndex = prevSearchIndex;
319 + } else {
320 + searchIndex = Math.min(
321 + ((prevSearchIndex: any): number),
322 + searchResults.length - 1
323 + );
324 +
325 + // Force selected element ID to be re-evaluated below, even if the search index didn't change,
326 + // because new search text means the same index may now point to a new element.
327 + prevSearchIndex = null;
328 + }
329 }
330 }
331 }
@@ -686,6 +702,30 @@ function TreeContextController({ children, viewElementSource }: Props) {
702 return () => bridge.removeListener('selectFiber', handleSelectFiber);
703 }, [bridge, dispatch]);
704
705 + // If a newly-selected search result is inside of a collapsed subtree, auto expand it.
706 + // We also need to handle when the search text changed (selecting a new element) without changing the index.
707 + const prevSearchIndex = useRef<number | null>(null);
708 + const prevSearchText = useRef<string>('');
709 + useEffect(() => {
710 + if (
711 + state.searchIndex !== prevSearchIndex.current ||
712 + state.searchText !== prevSearchText.current
713 + ) {
714 + prevSearchIndex.current = state.searchIndex;
715 + prevSearchText.current = state.searchText;
716 +
717 + if (state.searchIndex !== null && state.selectedElementID !== null) {
718 + let element = store.getElementByID(state.selectedElementID);
719 + while (element !== null && element.parentID > 0) {
720 + element = ((store.getElementByID(element.parentID): any): Element);
721 + if (element.isCollapsed) {
722 + store.toggleIsCollapsed(element.id, false);
723 + }
724 + }
725 + }
726 + }
727 + }, [state.searchIndex, state.searchText, state.selectedElementID, store]);
728 +
729 // Mutations to the underlying tree may impact this context (e.g. search results, selection state).
730 useEffect(() => {
731 const handleStoreMutated = ([