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
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Clear sidebar search field once a search is executed
|
||||
@@ -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.
|
||||
@@ -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 }) => (
|
||||
<SidebarPage>
|
||||
<Sidebar>
|
||||
<SidebarLogo />
|
||||
<SidebarSearchField onSearch={handleSearch} />
|
||||
<SidebarSearch />
|
||||
<SidebarDivider />
|
||||
{/* Global nav, not org-specific */}
|
||||
<SidebarItem icon={HomeIcon} to="/catalog" text="Home" />
|
||||
|
||||
@@ -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<T>(
|
||||
extractState(location.search, stateName),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const newState = extractState(location.search, stateName);
|
||||
|
||||
setQueryParamState(oldState =>
|
||||
isEqual(newState, oldState) ? oldState : newState,
|
||||
);
|
||||
}, [location, stateName]);
|
||||
|
||||
useDebounce(
|
||||
() => {
|
||||
const queryString = joinQueryString(
|
||||
|
||||
@@ -221,6 +221,7 @@ export const SidebarSearchField: FC<SidebarSearchFieldProps> = props => {
|
||||
const handleEnter: KeyboardEventHandler = ev => {
|
||||
if (ev.key === 'Enter') {
|
||||
props.onSearch(input);
|
||||
setInput('');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -233,6 +234,7 @@ export const SidebarSearchField: FC<SidebarSearchFieldProps> = props => {
|
||||
<SidebarItem icon={SearchIcon}>
|
||||
<TextField
|
||||
placeholder="Search"
|
||||
value={input}
|
||||
onChange={handleInput}
|
||||
onKeyDown={handleEnter}
|
||||
className={classes.searchContainer}
|
||||
|
||||
@@ -21,14 +21,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.3.1",
|
||||
"@backstage/plugin-catalog": "^0.2.2",
|
||||
"@backstage/theme": "^0.2.1",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"@backstage/plugin-catalog": "^0.2.2",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"qs": "^6.9.4",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -40,8 +41,8 @@
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"msw": "^0.21.2",
|
||||
"cross-fetch": "^3.0.6"
|
||||
"cross-fetch": "^3.0.6",
|
||||
"msw": "^0.21.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -13,22 +13,32 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Header, Content, Page } from '@backstage/core';
|
||||
import { Content, Header, Page, useQueryParamState } from '@backstage/core';
|
||||
import { Grid } from '@material-ui/core';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useDebounce } from 'react-use';
|
||||
import { SearchBar } from '../SearchBar';
|
||||
import { SearchResult } from '../SearchResult';
|
||||
|
||||
export const SearchPage = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [queryString, setQueryString] = useQueryParamState<string>('query');
|
||||
const [searchQuery, setSearchQuery] = useState(queryString ?? '');
|
||||
|
||||
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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 = () => {
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SearchResult searchQuery={searchQuery.toLowerCase()} />
|
||||
<SearchResult searchQuery={(queryString ?? '').toLowerCase()} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
|
||||
@@ -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 <SidebarSearchField onSearch={handleSearch} />;
|
||||
};
|
||||
@@ -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';
|
||||
@@ -18,3 +18,4 @@ export * from './Filters';
|
||||
export * from './SearchBar';
|
||||
export * from './SearchPage';
|
||||
export * from './SearchResult';
|
||||
export * from './SidebarSearch';
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export * from './components';
|
||||
|
||||
Reference in New Issue
Block a user