Skip to content
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

UI: Fix sidebar search hanging when selecting a story in touch mode #26807

Merged
merged 8 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/ui/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"@types/semver": "^7.3.4",
"browser-dtector": "^3.4.0",
"copy-to-clipboard": "^3.3.1",
"downshift": "^6.0.15",
"downshift": "^9.0.4",
"fs-extra": "^11.1.0",
"fuse.js": "^3.6.1",
"lodash": "^4.17.21",
Expand Down
63 changes: 35 additions & 28 deletions code/ui/manager/src/components/sidebar/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,17 +165,6 @@ export const Search = React.memo<{
const [allComponents, showAllComponents] = useState(false);
const searchShortcut = api ? shortcutToHumanString(api.getShortcutKeys().search) : '/';

const selectStory = useCallback(
(id: string, refId: string) => {
if (api) {
api.selectStory(id, undefined, { ref: refId !== DEFAULT_REF_ID && refId });
}
inputRef.current.blur();
showAllComponents(false);
},
[api, inputRef, showAllComponents, DEFAULT_REF_ID]
);

const makeFuse = useCallback(() => {
const list = dataset.entries.reduce<SearchItem[]>((acc, [refId, { index, status }]) => {
const groupStatus = getGroupStatus(index || {}, status);
Expand Down Expand Up @@ -232,6 +221,26 @@ export const Search = React.memo<{
[allComponents, makeFuse]
);

const onSelect = useCallback(
(selectedItem: DownshiftItem) => {
if (isSearchResult(selectedItem)) {
const { id, refId } = selectedItem.item;
api?.selectStory(id, undefined, { ref: refId !== DEFAULT_REF_ID && refId });
inputRef.current.blur();
showAllComponents(false);
return;
}
if (isExpandType(selectedItem)) {
selectedItem.showAll();
}
},
[api]
);

const onInputValueChange = useCallback((inputValue: string, stateAndHelpers: any) => {
showAllComponents(false);
}, []);

const stateReducer = useCallback(
(state: DownshiftState<DownshiftItem>, changes: StateChangeOptions<DownshiftItem>) => {
switch (changes.type) {
Expand All @@ -242,52 +251,42 @@ export const Search = React.memo<{
inputValue: state.inputValue,
// Return to the tree view after selecting an item
isOpen: state.inputValue && !state.selectedItem,
selectedItem: null,
};
}

case Downshift.stateChangeTypes.mouseUp: {
// Prevent clearing the input on refocus
return {};
return state;
}

case Downshift.stateChangeTypes.keyDownEscape: {
if (state.inputValue) {
// Clear the inputValue, but don't return to the tree view
return { ...changes, inputValue: '', isOpen: true, selectedItem: null };
}
// When pressing escape a second time, blur the input and return to the tree view
inputRef.current.blur();
// When pressing escape a second time return to the tree view
// The onKeyDown handler will also blur the input in this case
return { ...changes, isOpen: false, selectedItem: null };
}

case Downshift.stateChangeTypes.clickItem:
case Downshift.stateChangeTypes.keyDownEnter: {
if (isSearchResult(changes.selectedItem)) {
const { id, refId } = changes.selectedItem.item;
selectStory(id, refId);
// Return to the tree view, but keep the input value
return { ...changes, inputValue: state.inputValue, isOpen: false };
return { ...changes, inputValue: state.inputValue };
}
if (isExpandType(changes.selectedItem)) {
changes.selectedItem.showAll();
// Downshift should completely ignore this
return {};
return state;
}
return changes;
}

case Downshift.stateChangeTypes.changeInput: {
// Reset the "show more" state whenever the input changes
showAllComponents(false);
return changes;
}

default:
return changes;
}
},
[inputRef, selectStory, showAllComponents]
[]
);
const { isMobile } = useLayout();

Expand All @@ -298,6 +297,8 @@ export const Search = React.memo<{
// @ts-expect-error (Converted from ts-ignore)
itemToString={(result) => result?.item?.name || ''}
scrollIntoView={(e) => scrollIntoView(e)}
onSelect={onSelect}
onInputValueChange={onInputValueChange}
>
{({
isOpen,
Expand Down Expand Up @@ -343,6 +344,13 @@ export const Search = React.memo<{
setPlaceholder('Type to find...');
},
onBlur: () => setPlaceholder('Find components'),
onKeyDown: (e) => {
if (e.key === 'Escape' && inputValue.length === 0) {
// When pressing escape while the input is empty, blur the input
// The stateReducer will handle returning to the tree view
inputRef.current.blur();
}
},
});

const labelProps = getLabelProps({
Expand All @@ -359,7 +367,6 @@ export const Search = React.memo<{
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
{/* @ts-expect-error (TODO) */}
<Input {...inputProps} />
{!isMobile && enableShortcuts && !isOpen && (
<FocusKey>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const recents = stories
.map((story) => ({ item: story, matches: [], score: 0 }));

// We need this to prevent react key warnings
const passKey = (props: any = {}) => ({ key: props.key });
const passKey: any = (props = { key: '' }) => ({ key: props.key });

export const searching = {
query: 'query',
Expand Down
12 changes: 5 additions & 7 deletions code/ui/manager/src/components/sidebar/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,13 @@ const Path = styled.div(({ theme }) => ({

const Result: FC<
SearchResult & {
icon: string;
isHighlighted: boolean;
onClick: MouseEventHandler;
}
> = React.memo(function Result({ item, matches, icon, onClick, ...props }) {
const click: MouseEventHandler = useCallback(
} & React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>
> = React.memo(function Result({ item, matches, onClick, ...props }) {
const click: MouseEventHandler<HTMLLIElement> = useCallback(
(event) => {
event.preventDefault();
onClick(event);
onClick?.(event);
},
[onClick]
);
Expand Down Expand Up @@ -260,7 +258,7 @@ export const SearchResults: FC<{
return () => document.removeEventListener('keydown', handleEscape);
}, [closeMenu, enableShortcuts, isLoading]);

const mouseOverHandler = useCallback((event: MouseEvent) => {
const mouseOverHandler: MouseEventHandler = useCallback((event) => {
if (!api) {
return;
}
Expand Down
43 changes: 26 additions & 17 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1996,7 +1996,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/runtime@npm:7.24.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
"@babel/runtime@npm:7.24.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.24.0
resolution: "@babel/runtime@npm:7.24.0"
dependencies:
Expand All @@ -2014,6 +2014,15 @@ __metadata:
languageName: node
linkType: hard

"@babel/runtime@npm:^7.22.15":
version: 7.24.4
resolution: "@babel/runtime@npm:7.24.4"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10c0/785aff96a3aa8ff97f90958e1e8a7b1d47f793b204b47c6455eaadc3f694f48c97cd5c0a921fe3596d818e71f18106610a164fb0f1c71fd68c622a58269d537c
languageName: node
linkType: hard

"@babel/runtime@npm:~7.5.4":
version: 7.5.5
resolution: "@babel/runtime@npm:7.5.5"
Expand Down Expand Up @@ -6083,7 +6092,7 @@ __metadata:
"@types/semver": "npm:^7.3.4"
browser-dtector: "npm:^3.4.0"
copy-to-clipboard: "npm:^3.3.1"
downshift: "npm:^6.0.15"
downshift: "npm:^9.0.4"
fs-extra: "npm:^11.1.0"
fuse.js: "npm:^3.6.1"
lodash: "npm:^4.17.21"
Expand Down Expand Up @@ -11940,10 +11949,10 @@ __metadata:
languageName: node
linkType: hard

"compute-scroll-into-view@npm:^1.0.17":
version: 1.0.20
resolution: "compute-scroll-into-view@npm:1.0.20"
checksum: 10c0/19034322590bfce59cb6939b3603e7aaf6f0d4128b8627bbc136e71c8714905e2f8bf2ba0cb7f153c6e8cdb8ad907ffd6d0188ccc7625dc05790a59ae6a81f01
"compute-scroll-into-view@npm:^3.0.3":
version: 3.1.0
resolution: "compute-scroll-into-view@npm:3.1.0"
checksum: 10c0/bf305c4ece8e5c59ed3f7ed82b6dab5b7487ce26f56a693d903869964712870fccb08fe31d40edcbd600b03c99198f54d443acb315d674bd64fd344410c8672e
languageName: node
linkType: hard

Expand Down Expand Up @@ -13211,18 +13220,18 @@ __metadata:
languageName: node
linkType: hard

"downshift@npm:^6.0.15":
version: 6.1.12
resolution: "downshift@npm:6.1.12"
"downshift@npm:^9.0.4":
version: 9.0.4
resolution: "downshift@npm:9.0.4"
dependencies:
"@babel/runtime": "npm:^7.14.8"
compute-scroll-into-view: "npm:^1.0.17"
prop-types: "npm:^15.7.2"
react-is: "npm:^17.0.2"
tslib: "npm:^2.3.0"
"@babel/runtime": "npm:^7.22.15"
compute-scroll-into-view: "npm:^3.0.3"
prop-types: "npm:^15.8.1"
react-is: "npm:^18.2.0"
tslib: "npm:^2.6.2"
peerDependencies:
react: ">=16.12.0"
checksum: 10c0/fa29bfcb9db520c9e45735a49e00d85bb47fb3ad0d4b754dbf2e75ef5cb9264c0950be3186422f14a8acb0e753a7fc0564e4312906de641989c349ea70424558
checksum: 10c0/8474a42d7fdbe9e5fb748c79b0f824610375921a5300426312d8b8e13d72aa29a6a5d45dba42b599878855d127ba7fa4b26862a8a12d12fd576e8d955b6670c4
languageName: node
linkType: hard

Expand Down Expand Up @@ -23759,14 +23768,14 @@ __metadata:
languageName: node
linkType: hard

"react-is@npm:^17.0.1, react-is@npm:^17.0.2":
"react-is@npm:^17.0.1":
version: 17.0.2
resolution: "react-is@npm:17.0.2"
checksum: 10c0/2bdb6b93fbb1820b024b496042cce405c57e2f85e777c9aabd55f9b26d145408f9f74f5934676ffdc46f3dcff656d78413a6e43968e7b3f92eea35b3052e9053
languageName: node
linkType: hard

"react-is@npm:^18.0.0":
"react-is@npm:^18.0.0, react-is@npm:^18.2.0":
version: 18.2.0
resolution: "react-is@npm:18.2.0"
checksum: 10c0/6eb5e4b28028c23e2bfcf73371e72cd4162e4ac7ab445ddae2afe24e347a37d6dc22fae6e1748632cd43c6d4f9b8f86dcf26bf9275e1874f436d129952528ae0
Expand Down