From 475fc0aaa33b044265cf03724676fdafab489be6 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 20 Nov 2020 15:14:18 +0100 Subject: [PATCH] Make sidebar search field work (#3362) * Make sidebar search field work Extend the search page to have the ability to react to query parameters. The search in the sidebar now navigates to the search page and passes the query parameter. The search box on the search page is now debounced. Closes #3341 * Fix sidebar search while the search page is already open --- .changeset/breezy-cobras-deny.md | 5 +++ .changeset/small-worms-check.md | 6 ++++ packages/app/src/components/Root/Root.tsx | 10 ++---- packages/core/src/hooks/useQueryParamState.ts | 11 +++++- packages/core/src/layout/Sidebar/Items.tsx | 2 ++ plugins/search/package.json | 9 ++--- .../src/components/SearchPage/SearchPage.tsx | 22 ++++++++---- .../SidebarSearch/SidebarSearch.tsx | 35 +++++++++++++++++++ .../src/components/SidebarSearch/index.ts | 16 +++++++++ plugins/search/src/components/index.tsx | 1 + plugins/search/src/index.ts | 1 + 11 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 .changeset/breezy-cobras-deny.md create mode 100644 .changeset/small-worms-check.md create mode 100644 plugins/search/src/components/SidebarSearch/SidebarSearch.tsx create mode 100644 plugins/search/src/components/SidebarSearch/index.ts diff --git a/.changeset/breezy-cobras-deny.md b/.changeset/breezy-cobras-deny.md new file mode 100644 index 0000000000..5f035c05ce --- /dev/null +++ b/.changeset/breezy-cobras-deny.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Clear sidebar search field once a search is executed diff --git a/.changeset/small-worms-check.md b/.changeset/small-worms-check.md new file mode 100644 index 0000000000..e39265f8f9 --- /dev/null +++ b/.changeset/small-worms-check.md @@ -0,0 +1,6 @@ +--- +'example-app': patch +'@backstage/plugin-search': patch +--- + +Using the search field in the sidebar now navigates to the search result page. diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index e947e5c91d..47e7d32d1a 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -33,12 +33,12 @@ import { SidebarContext, SidebarItem, SidebarDivider, - SidebarSearchField, SidebarSpace, } from '@backstage/core'; import { NavLink } from 'react-router-dom'; import { graphiQLRouteRef } from '@backstage/plugin-graphiql'; import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; +import { SidebarSearch } from '@backstage/plugin-search'; const useSidebarLogoStyles = makeStyles({ root: { @@ -73,17 +73,11 @@ const SidebarLogo: FC<{}> = () => { ); }; -const handleSearch = (query: string): void => { - // XXX (@koroeskohr): for testing purposes - // eslint-disable-next-line no-console - console.log(query); -}; - const Root: FC<{}> = ({ children }) => ( - + {/* Global nav, not org-specific */} diff --git a/packages/core/src/hooks/useQueryParamState.ts b/packages/core/src/hooks/useQueryParamState.ts index 9317dcd3e1..dc2279cb53 100644 --- a/packages/core/src/hooks/useQueryParamState.ts +++ b/packages/core/src/hooks/useQueryParamState.ts @@ -14,8 +14,9 @@ * limitations under the License. */ +import { isEqual } from 'lodash'; import qs from 'qs'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { useDebounce } from 'react-use'; @@ -64,6 +65,14 @@ export function useQueryParamState( extractState(location.search, stateName), ); + useEffect(() => { + const newState = extractState(location.search, stateName); + + setQueryParamState(oldState => + isEqual(newState, oldState) ? oldState : newState, + ); + }, [location, stateName]); + useDebounce( () => { const queryString = joinQueryString( diff --git a/packages/core/src/layout/Sidebar/Items.tsx b/packages/core/src/layout/Sidebar/Items.tsx index bb47701280..498946577d 100644 --- a/packages/core/src/layout/Sidebar/Items.tsx +++ b/packages/core/src/layout/Sidebar/Items.tsx @@ -221,6 +221,7 @@ export const SidebarSearchField: FC = props => { const handleEnter: KeyboardEventHandler = ev => { if (ev.key === 'Enter') { props.onSearch(input); + setInput(''); } }; @@ -233,6 +234,7 @@ export const SidebarSearchField: FC = props => { { - const [searchQuery, setSearchQuery] = useState(''); + const [queryString, setQueryString] = useQueryParamState('query'); + const [searchQuery, setSearchQuery] = useState(queryString ?? ''); const handleSearch = (event: React.ChangeEvent) => { event.preventDefault(); setSearchQuery(event.target.value); }; + useEffect(() => setSearchQuery(queryString ?? ''), [queryString]); + + useDebounce( + () => { + setQueryString(searchQuery); + }, + 200, + [searchQuery], + ); + const handleClearSearchBar = () => { setSearchQuery(''); }; @@ -46,7 +56,7 @@ export const SearchPage = () => { /> - + diff --git a/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx b/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx new file mode 100644 index 0000000000..6a279e0743 --- /dev/null +++ b/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useCallback } from 'react'; +import qs from 'qs'; +import { useNavigate } from 'react-router-dom'; +import { SidebarSearchField } from '@backstage/core'; + +export const SidebarSearch = () => { + const navigate = useNavigate(); + const handleSearch = useCallback( + (query: string): void => { + const queryString = qs.stringify({ query }, { addQueryPrefix: true }); + + // TODO: Here the url to the search plugin is hardcoded. We need a way to query the route in the future. + // Maybe an API that I can just call from other places? + navigate(`/search${queryString}`); + }, + [navigate], + ); + + return ; +}; diff --git a/plugins/search/src/components/SidebarSearch/index.ts b/plugins/search/src/components/SidebarSearch/index.ts new file mode 100644 index 0000000000..33869ffb77 --- /dev/null +++ b/plugins/search/src/components/SidebarSearch/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { SidebarSearch } from './SidebarSearch'; diff --git a/plugins/search/src/components/index.tsx b/plugins/search/src/components/index.tsx index f8e6a5a09e..ac47860dc2 100644 --- a/plugins/search/src/components/index.tsx +++ b/plugins/search/src/components/index.tsx @@ -18,3 +18,4 @@ export * from './Filters'; export * from './SearchBar'; export * from './SearchPage'; export * from './SearchResult'; +export * from './SidebarSearch'; diff --git a/plugins/search/src/index.ts b/plugins/search/src/index.ts index 224e293890..77ad7f9266 100644 --- a/plugins/search/src/index.ts +++ b/plugins/search/src/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export { plugin } from './plugin'; +export * from './components';