diff --git a/.changeset/search-countries-exercise.md b/.changeset/search-countries-exercise.md new file mode 100644 index 0000000000..390813444f --- /dev/null +++ b/.changeset/search-countries-exercise.md @@ -0,0 +1,283 @@ +--- +'@backstage/plugin-search-react': minor +--- + +The `` component now accepts a optional `query` prop to request results from the search api: + +> Note: If a query prop is not defined, the results will by default be consumed from the context. + +Example: + +```jsx +import React, { useState, useCallback } from 'react'; + +import { Grid, List, Paper } from '@material-ui/core'; + +import { Page, Header, Content, Lifecycle } from '@backstage/core-components'; +import { + DefaultResultListItem, + SearchBarBase, + SearchResult, +} from '@backstage/plugin-search-react'; + +const SearchPage = () => { + const [query, setQuery] = useState({ + term: '', + types: [], + filters: {}, + }); + + const handleChange = useCallback( + (term: string) => { + setQuery(prevQuery => ({ ...prevQuery, term })); + }, + [setQuery], + ); + + return ( + +
} /> + + + + + + + + + + {({ results }) => ( + + {results.map(({ document }) => ( + + ))} + + )} + + + + + + ); +}; +``` + +Additionally, a search page can also be composed using these two new results layout components: + +```jsx +// Example rendering results as list + + {({ results }) => ( + { + switch (type) { + case 'custom-result-item': + return ( + + ); + default: + return ( + + ); + } + }} + /> + )} + +``` + +```jsx +// Example rendering results as groups + + {({ results }) => ( + <> + } + title="Custom" + link="See all custom results" + resultItems={results.filter( + ({ type }) => type === 'custom-result-item', + )} + renderResultItem={({ document }) => ( + + )} + /> + } + title="Default" + resultItems={results.filter( + ({ type }) => type !== 'custom-result-item', + )} + renderResultItem={({ document }) => ( + + )} + /> + + )} + +``` + +A `SearchResultList` and `SearchResultGroup` components were also created for users who have search pages with multiple queries, both are specializations of `SearchResult` and also accept a `query` as a prop as well: + +```jsx +// Example using the +const SearchPage = () => { + const query = { + term: 'example', + }; + + return ( + { + switch (type) { + case 'custom': + return ( + } + result={document} + highlight={highlight} + rank={rank} + /> + ); + default: + return ( + + ); + } + }} + /> + ); +}; +``` + +```jsx +// Example using the for creating a component that search and group software catalog results +import React, { useState, useCallback } from 'react'; + +import { MenuItem } from '@material-ui/core'; + +import { JsonValue } from '@backstage/types'; +import { CatalogIcon } from '@backstage/core-components'; +import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; +import { + SearchResultGroup, + SearchResultGroupTextFilterField, + SearchResultGroupSelectFilterField, +} from @backstage/plugin-search-react; +import { SearchQuery } from '@backstage/plugin-search-common'; + +const CatalogResultsGroup = () => { + const [query, setQuery] = useState>({ + types: ['software-catalog'], + }); + + const filterOptions = [ + { + label: 'Lifecycle', + value: 'lifecycle', + }, + { + label: 'Owner', + value: 'owner', + }, + ]; + + const handleFilterAdd = useCallback( + (key: string) => () => { + setQuery(prevQuery => { + const { filters: prevFilters, ...rest } = prevQuery; + const newFilters = { ...prevFilters, [key]: undefined }; + return { ...rest, filters: newFilters }; + }); + }, + [], + ); + + const handleFilterChange = useCallback( + (key: string) => (value: JsonValue) => { + setQuery(prevQuery => { + const { filters: prevFilters, ...rest } = prevQuery; + const newFilters = { ...prevFilters, [key]: value }; + return { ...rest, filters: newFilters }; + }); + }, + [], + ); + + const handleFilterDelete = useCallback( + (key: string) => () => { + setQuery(prevQuery => { + const { filters: prevFilters, ...rest } = prevQuery; + const newFilters = { ...prevFilters }; + delete newFilters[key]; + return { ...rest, filters: newFilters }; + }); + }, + [], + ); + + return ( + } + title="Software Catalog" + link="See all software catalog results" + filterOptions={filterOptions} + renderFilterOption={({ label, value }) => ( + + {label} + + )} + renderFilterField={(key: string) => { + switch (key) { + case 'lifecycle': + return ( + + Production + Experimental + + ); + case 'owner': + return ( + + ); + default: + return null; + } + } + renderResultItem={({ document, highlight, rank }) => ( + + )} + /> + ); +}; +```