diff --git a/plugins/search/src/components/SearchBarNext/SearchBarNext.tsx b/plugins/search/src/components/SearchBarNext/SearchBarNext.tsx index 70868f5ce5..0e8c626b6f 100644 --- a/plugins/search/src/components/SearchBarNext/SearchBarNext.tsx +++ b/plugins/search/src/components/SearchBarNext/SearchBarNext.tsx @@ -14,9 +14,8 @@ * limitations under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import { useDebounce } from 'react-use'; -import { useQueryParamState } from '@backstage/core'; import { Paper, InputBase, IconButton, makeStyles } from '@material-ui/core'; import SearchIcon from '@material-ui/icons/Search'; import ClearButton from '@material-ui/icons/Clear'; @@ -32,23 +31,26 @@ const useStyles = makeStyles(() => ({ }, })); -export const SearchBarNext = () => { +type Props = { + debounceTime?: number; +}; + +export const SearchBarNext = ({ debounceTime = 200 }: Props) => { const classes = useStyles(); const { term, setTerm, setPageCursor } = useSearch(); - - const [, setQueryString] = useQueryParamState('query'); + const [value, setValue] = useState(term); useDebounce( () => { - setQueryString(term); + setTerm(value); }, - 200, - [term], + debounceTime, + [value], ); const handleSearch = (event: React.ChangeEvent | React.FormEvent) => { event.preventDefault(); - setTerm((event.target as HTMLInputElement).value as string); + setValue((event.target as HTMLInputElement).value as string); }; const handleClearSearchBar = () => { @@ -57,22 +59,18 @@ export const SearchBarNext = () => { }; return ( - handleSearch(e)} - className={classes.root} - > + handleSearch(e)} + value={value} + onChange={handleSearch} inputProps={{ 'aria-label': 'search backstage' }} /> - handleClearSearchBar()}> + diff --git a/plugins/search/src/components/SearchPageNext/SearchPageNext.tsx b/plugins/search/src/components/SearchPageNext/SearchPageNext.tsx index f3a4efc32c..bae5be6042 100644 --- a/plugins/search/src/components/SearchPageNext/SearchPageNext.tsx +++ b/plugins/search/src/components/SearchPageNext/SearchPageNext.tsx @@ -17,15 +17,38 @@ import React from 'react'; import qs from 'qs'; import { Outlet, useLocation } from 'react-router'; -import { useQueryParamState } from '@backstage/core'; -import { SearchContextProvider } from '../SearchContext'; +import { SearchContextProvider, useSearch } from '../SearchContext'; import { JsonObject } from '@backstage/config'; +export const UrlUpdater = () => { + const { term, types, pageCursor, filters } = useSearch(); + + const newParams = qs.stringify( + { + query: term, + types, + pageCursor, + filters, + }, + { arrayFormat: 'brackets' }, + ); + const newUrl = `${window.location.pathname}?${newParams}`; + + // We directly manipulate window history here in order to not re-render + // infinitely (state => location => state => etc). The intention of this + // code is just to ensure the right query/filters are loaded when a user + // clicks the "back" button after clicking a result. + window.history.replaceState(null, document.title, newUrl); + + return null; +}; + export const SearchPageNext = () => { const location = useLocation(); - const [queryString] = useQueryParamState('query'); - const filters = (qs.parse(location.search.substring(1), { arrayLimit: 0 }) - .filters || {}) as JsonObject; + const query = qs.parse(location.search.substring(1), { arrayLimit: 0 }) || {}; + const filters = (query.filters as JsonObject) || {}; + const queryString = (query.query as string) || ''; + const initialState = { term: queryString || '', types: [], @@ -35,6 +58,7 @@ export const SearchPageNext = () => { return ( + );