diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json index a5ccc976a202..9dd2b2528cd8 100644 --- a/code/ui/manager/package.json +++ b/code/ui/manager/package.json @@ -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", diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx index 0df164a2d35b..38ac1b3c1f21 100644 --- a/code/ui/manager/src/components/sidebar/Search.tsx +++ b/code/ui/manager/src/components/sidebar/Search.tsx @@ -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((acc, [refId, { index, status }]) => { const groupStatus = getGroupStatus(index || {}, status); @@ -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, changes: StateChangeOptions) => { switch (changes.type) { @@ -242,13 +251,12 @@ 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: { @@ -256,38 +264,29 @@ export const Search = React.memo<{ // 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(); @@ -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, @@ -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({ @@ -359,7 +367,6 @@ export const Search = React.memo<{ - {/* @ts-expect-error (TODO) */} {!isMobile && enableShortcuts && !isOpen && ( diff --git a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx index 6e464e9122be..05b64669bbc2 100644 --- a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx +++ b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx @@ -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', diff --git a/code/ui/manager/src/components/sidebar/SearchResults.tsx b/code/ui/manager/src/components/sidebar/SearchResults.tsx index faead314e0e8..db6819a1c288 100644 --- a/code/ui/manager/src/components/sidebar/SearchResults.tsx +++ b/code/ui/manager/src/components/sidebar/SearchResults.tsx @@ -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, HTMLLIElement> +> = React.memo(function Result({ item, matches, onClick, ...props }) { + const click: MouseEventHandler = useCallback( (event) => { event.preventDefault(); - onClick(event); + onClick?.(event); }, [onClick] ); @@ -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; } diff --git a/code/yarn.lock b/code/yarn.lock index 665cf7ea2356..9f4a56d84842 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -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: @@ -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" @@ -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" @@ -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 @@ -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 @@ -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