From b1ff70f143d5b542af869d3e3e0baa6b6e87a3f0 Mon Sep 17 00:00:00 2001 From: Galen Gray Date: Thu, 28 Sep 2023 09:03:09 -0700 Subject: [PATCH 1/4] Improved search results, custom modal, custom techdocs search results --- packages/app/src/components/Root/Root.tsx | 61 ++++++- .../app/src/components/search/CustomModal.tsx | 68 ++++++++ .../app/src/components/search/SearchPage.tsx | 62 +------ .../search/SearchResultCustomList.tsx | 70 ++++++++ .../TechDocsSearchResultCustomListItem.tsx | 162 ++++++++++++++++++ packages/app/src/components/utils/icons.tsx | 13 ++ 6 files changed, 374 insertions(+), 62 deletions(-) create mode 100644 packages/app/src/components/search/CustomModal.tsx create mode 100644 packages/app/src/components/search/SearchResultCustomList.tsx create mode 100644 packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index cb95a00..2d90f70 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -1,5 +1,5 @@ -import React, { PropsWithChildren } from 'react'; -import { makeStyles } from '@material-ui/core'; +import React, { PropsWithChildren, useCallback, useEffect, useRef } from 'react'; +import { makeStyles, useTheme } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; // import CatalogIcon from '@material-ui/icons/LocalLibrary'; // import ExtensionIcon from '@material-ui/icons/Extension'; @@ -26,10 +26,13 @@ import { SidebarSpace, useSidebarOpenState, Link, + useContent, } from '@backstage/core-components'; // import { useApp } from '@backstage/core-plugin-api'; import MenuIcon from '@material-ui/icons/Menu'; import SearchIcon from '@material-ui/icons/Search'; +import CustomModal from '../search/CustomModal'; +import { useNavigate } from 'react-router-dom'; const storedTheme = localStorage.getItem('theme'); @@ -59,6 +62,30 @@ const useSidebarLogoStyles = makeStyles({ }, }); +const useModalStyles = makeStyles(theme => ({ + dialogTitle: { + gap: theme.spacing(1), + display: 'grid', + alignItems: 'center', + gridTemplateColumns: '1fr auto', + '&> button': { + marginTop: theme.spacing(1), + }, + }, + input: { + flex: 1, + }, + button: { + '&:hover': { + background: 'none', + }, + }, + // Reduces default height of the modal, keeping a gap of 128px between the top and bottom of the page. + paperFullWidth: { height: 'calc(100% - 128px)' }, + dialogActionsContainer: { padding: theme.spacing(1, 3) }, + viewResultsLink: { verticalAlign: '0.5em' }, +})); + const SidebarLogo = () => { const classes = useSidebarLogoStyles(); const { isOpen } = useSidebarOpenState(); @@ -72,12 +99,36 @@ const SidebarLogo = () => { ); }; -export const Root = ({ children }: PropsWithChildren<{}>) => ( +export const Root = ({ children }: PropsWithChildren<{}>) => { + const classes = useModalStyles(); + const navigate = useNavigate(); + const searchBarRef = useRef(null); + + const { transitions } = useTheme(); + const { focusContent } = useContent(); + + useEffect(() => { + searchBarRef?.current?.focus(); + }); + + const handleSearchResultClick = useCallback(() => { + setTimeout(focusContent, transitions.duration.leavingScreen); + }, [focusContent, transitions]); + + const handleSearchBarSubmit = useCallback(() => { + const query = searchBarRef.current?.value ?? ''; + navigate(`/search?query=${query}`); + handleSearchResultClick(); + }, [navigate, handleSearchResultClick]); + + return ( } to="/search"> - + + {({ toggleModal }) => CustomModal(toggleModal, classes, searchBarRef, handleSearchBarSubmit)} + }> @@ -144,4 +195,4 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( {children} -); +)}; diff --git a/packages/app/src/components/search/CustomModal.tsx b/packages/app/src/components/search/CustomModal.tsx new file mode 100644 index 0000000..acd875e --- /dev/null +++ b/packages/app/src/components/search/CustomModal.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { SearchBar, SearchResultPager } from "@backstage/plugin-search-react"; +import { Box, Button, DialogActions, DialogContent, DialogTitle, Divider, Grid, IconButton } from "@material-ui/core"; +import CloseIcon from '@material-ui/icons/Close'; +import ArrowForwardIcon from '@material-ui/icons/ArrowForward'; +import { searchResultCustomList } from './SearchResultCustomList'; + + +export const CustomModal = (toggleModal : any, classes : any, searchBarRef : any, handleSearchBarSubmit : any) => { + // const handleSearchBarSubmit = () => { + // const query = searchBarRef.current?.value ?? ''; + // // update to use route ref? + // // navigate(`${searchRootRoute}?query=${query}`); + // navigate(`/search?query=${query}`); + // handleSearchResultClick(); + // }; + + return ( + <> + + + + + + + + + + + + + + + + + + {searchResultCustomList} + + + + + + + + + + + + ); +}; + +export default CustomModal; \ No newline at end of file diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 3bc85fc..2602aab 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -1,21 +1,17 @@ import React from 'react'; -import { makeStyles, Theme, Grid, Paper, List } from '@material-ui/core'; +import { makeStyles, Theme, Grid, Paper } from '@material-ui/core'; -import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; import { catalogApiRef, CATALOG_FILTER_EXISTS, } from '@backstage/plugin-catalog-react'; -import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs'; import { SearchType } from '@backstage/plugin-search'; import { SearchBar, SearchFilter, - SearchResult, SearchPagination, - useSearch, - DefaultResultListItem + useSearch } from '@backstage/plugin-search-react'; import { CatalogIcon, @@ -25,7 +21,8 @@ import { Page, } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; -import { StackOverflowSearchResultListItem, StackOverflowIcon } from '@backstage/plugin-stack-overflow'; +import { StackOverflowIcon } from '@backstage/plugin-stack-overflow'; +import { searchResultCustomList } from './SearchResultCustomList'; const useStyles = makeStyles((theme: Theme) => ({ search: { @@ -119,56 +116,7 @@ const SearchPage = () => { - - {({ results }) => ( - - {results.map(({ type, document, highlight, rank }) => { - switch (type) { - case 'software-catalog': - return ( - } - /> - ); - case 'techdocs': - return ( - } - /> - ); - case 'stack-overflow': - return ( - } - /> - ); - default: - return ( - - ); - } - })} - - )} - {/* } /> - } /> - } /> */} - + {searchResultCustomList} diff --git a/packages/app/src/components/search/SearchResultCustomList.tsx b/packages/app/src/components/search/SearchResultCustomList.tsx new file mode 100644 index 0000000..f00b03b --- /dev/null +++ b/packages/app/src/components/search/SearchResultCustomList.tsx @@ -0,0 +1,70 @@ +import React from 'react'; + +import { List } from '@material-ui/core'; +import { SearchResult, DefaultResultListItem } from '@backstage/plugin-search-react'; + +import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; +import { StackOverflowSearchResultListItem, StackOverflowIcon } from '@backstage/plugin-stack-overflow'; +import { CatalogIcon, DocsIcon } from '@backstage/core-components'; +import { OpenShiftSvgIcon } from "../utils/icons"; +import { TechDocsSearchResultCustomListItem } from './TechDocsSearchResultCustomListItem'; + +const SearchResultCustomList = () => { + return ( + + {({ results }) => ( + + {results.map(({ type, document, highlight, rank }) => { + switch (type) { + case 'software-catalog': + return ( + } + /> + ); + case 'techdocs': + return ( + { + if (document.location.includes('platform-developer-docs')) + return + else + return + }} + /> + ); + case 'stack-overflow': + return ( + } + /> + ); + default: + return ( + + ); + } + })} + + )} + + ) +} + +export const searchResultCustomList = ; \ No newline at end of file diff --git a/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx b/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx new file mode 100644 index 0000000..ec0fee3 --- /dev/null +++ b/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx @@ -0,0 +1,162 @@ +import React, { PropsWithChildren, ReactNode } from 'react'; +import { Box, Chip, Divider, ListItem, ListItemIcon, ListItemText, makeStyles } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import { Link } from '@backstage/core-components'; +import { ResultHighlight } from '@backstage/plugin-search-common'; +import { HighlightedSearchResultText } from '@backstage/plugin-search-react'; + +const useStyles = makeStyles({ + flexContainer: { + flexWrap: 'wrap', + }, + itemText: { + width: '100%', + marginBottom: '1rem', + }, +}); + +/** + * Props for {@link TechDocsSearchResultCustomListItem}. + * + * @public + */ +export type TechDocsSearchResultListItemProps = { + icon?: ReactNode | ((result: any) => ReactNode); + result?: any; + highlight?: ResultHighlight; + rank?: number; + lineClamp?: number; + asListItem?: boolean; + asLink?: boolean; + title?: string; +}; + +/** + * Component which renders documentation and related metadata. + * + * @public + */ +export const TechDocsSearchResultCustomListItem = ( + props: TechDocsSearchResultListItemProps, +) => { + const { + result, + highlight, + lineClamp = 5, + asListItem = true, + asLink = true, + title, + icon, + } = props; + const classes = useStyles(); + + const LinkWrapper = ({ children }: PropsWithChildren<{}>) => + asLink ? ( + + {children} + + ) : ( + <>{children} + ); + + const TextItem = () => { + const resultTitle = highlight?.fields.title ? ( + + ) : ( + result.title + ); + + const entityTitle = highlight?.fields.entityTitle ? ( + + ) : ( + result.entityTitle + ); + + const resultName = highlight?.fields.name ? ( + + ) : ( + result.name + ); + + if (!result) return null; + + return ( + <> + + {icon && + {typeof icon === 'function' ? icon(result) : icon} + } + + + {title ? ( + title + ) : ( + <> + {resultTitle} | {entityTitle ?? resultName} docs + + )} + + } + secondary={ + + {highlight?.fields.text ? ( + + ) : ( + result.text + )} + + } + /> + + + + + + ); + }; + + const ListItemWrapper = ({ children }: PropsWithChildren<{}>) => + asListItem ? ( + <> +
{children}
+ + ) : ( + <>{children} + ); + + return ( + + + + ); +}; \ No newline at end of file diff --git a/packages/app/src/components/utils/icons.tsx b/packages/app/src/components/utils/icons.tsx index 3af2c35..9565547 100644 --- a/packages/app/src/components/utils/icons.tsx +++ b/packages/app/src/components/utils/icons.tsx @@ -33,3 +33,16 @@ export const StackOverFlowIcon = createSvgIcon( , "StackOverflow"); + +// @see https://icon-sets.iconify.design/logos/openshift/ +export const OpenShiftSvgIcon = createSvgIcon( + + + + + + + + + , "OpenShift"); \ No newline at end of file From 96355ff59bbe3abd85942b28f73315ce710f9e96 Mon Sep 17 00:00:00 2001 From: Galen Gray Date: Thu, 28 Sep 2023 09:09:58 -0700 Subject: [PATCH 2/4] removing some commented out code --- packages/app/src/components/search/CustomModal.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/app/src/components/search/CustomModal.tsx b/packages/app/src/components/search/CustomModal.tsx index acd875e..92957bd 100644 --- a/packages/app/src/components/search/CustomModal.tsx +++ b/packages/app/src/components/search/CustomModal.tsx @@ -7,14 +7,6 @@ import { searchResultCustomList } from './SearchResultCustomList'; export const CustomModal = (toggleModal : any, classes : any, searchBarRef : any, handleSearchBarSubmit : any) => { - // const handleSearchBarSubmit = () => { - // const query = searchBarRef.current?.value ?? ''; - // // update to use route ref? - // // navigate(`${searchRootRoute}?query=${query}`); - // navigate(`/search?query=${query}`); - // handleSearchResultClick(); - // }; - return ( <> From 23eb2cce1c0722228515ab4fb96d8ae46e383085 Mon Sep 17 00:00:00 2001 From: Galen Gray Date: Tue, 3 Oct 2023 10:55:55 -0700 Subject: [PATCH 3/4] Some code clean up and attributions --- packages/app/src/components/Root/Root.tsx | 70 ++----- .../app/src/components/search/CustomModal.tsx | 191 +++++++++++++----- .../search/SearchResultCustomList.tsx | 8 +- .../TechDocsSearchResultCustomListItem.tsx | 23 ++- 4 files changed, 184 insertions(+), 108 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 2d90f70..371cd69 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -1,5 +1,5 @@ -import React, { PropsWithChildren, useCallback, useEffect, useRef } from 'react'; -import { makeStyles, useTheme } from '@material-ui/core'; +import React, { PropsWithChildren } from 'react'; +import { makeStyles } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; // import CatalogIcon from '@material-ui/icons/LocalLibrary'; // import ExtensionIcon from '@material-ui/icons/Extension'; @@ -12,7 +12,7 @@ import { Settings as SidebarSettings, UserSettingsSignInAvatar, } from '@backstage/plugin-user-settings'; -import { SidebarSearchModal } from '@backstage/plugin-search'; +import { SearchModalProvider, useSearchModal } from '@backstage/plugin-search'; import { Sidebar, sidebarConfig, @@ -26,13 +26,11 @@ import { SidebarSpace, useSidebarOpenState, Link, - useContent, } from '@backstage/core-components'; // import { useApp } from '@backstage/core-plugin-api'; import MenuIcon from '@material-ui/icons/Menu'; import SearchIcon from '@material-ui/icons/Search'; -import CustomModal from '../search/CustomModal'; -import { useNavigate } from 'react-router-dom'; +import { CustomSearchModal } from '../search/CustomModal'; const storedTheme = localStorage.getItem('theme'); @@ -62,30 +60,6 @@ const useSidebarLogoStyles = makeStyles({ }, }); -const useModalStyles = makeStyles(theme => ({ - dialogTitle: { - gap: theme.spacing(1), - display: 'grid', - alignItems: 'center', - gridTemplateColumns: '1fr auto', - '&> button': { - marginTop: theme.spacing(1), - }, - }, - input: { - flex: 1, - }, - button: { - '&:hover': { - background: 'none', - }, - }, - // Reduces default height of the modal, keeping a gap of 128px between the top and bottom of the page. - paperFullWidth: { height: 'calc(100% - 128px)' }, - dialogActionsContainer: { padding: theme.spacing(1, 3) }, - viewResultsLink: { verticalAlign: '0.5em' }, -})); - const SidebarLogo = () => { const classes = useSidebarLogoStyles(); const { isOpen } = useSidebarOpenState(); @@ -100,35 +74,25 @@ const SidebarLogo = () => { }; export const Root = ({ children }: PropsWithChildren<{}>) => { - const classes = useModalStyles(); - const navigate = useNavigate(); - const searchBarRef = useRef(null); - - const { transitions } = useTheme(); - const { focusContent } = useContent(); - - useEffect(() => { - searchBarRef?.current?.focus(); - }); - - const handleSearchResultClick = useCallback(() => { - setTimeout(focusContent, transitions.duration.leavingScreen); - }, [focusContent, transitions]); - - const handleSearchBarSubmit = useCallback(() => { - const query = searchBarRef.current?.value ?? ''; - navigate(`/search?query=${query}`); - handleSearchResultClick(); - }, [navigate, handleSearchResultClick]); + const { state, toggleModal } = useSearchModal(); return ( } to="/search"> - - {({ toggleModal }) => CustomModal(toggleModal, classes, searchBarRef, handleSearchBarSubmit)} - + + + + }> diff --git a/packages/app/src/components/search/CustomModal.tsx b/packages/app/src/components/search/CustomModal.tsx index 92957bd..8978a5a 100644 --- a/packages/app/src/components/search/CustomModal.tsx +++ b/packages/app/src/components/search/CustomModal.tsx @@ -1,60 +1,157 @@ -import React from 'react'; -import { SearchBar, SearchResultPager } from "@backstage/plugin-search-react"; -import { Box, Button, DialogActions, DialogContent, DialogTitle, Divider, Grid, IconButton } from "@material-ui/core"; +/* + * CustomModal.tsx is a customized version of the original SearchModal.tsx + * The searchResultCustomList component is used rather than element + */ + +/* + * Copyright 2021 The Backstage Authors + * + * 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, useEffect, useRef } from 'react'; +import { SearchBar, SearchContextProvider, SearchResultPager } from "@backstage/plugin-search-react"; +import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Grid, IconButton, makeStyles, useTheme } from "@material-ui/core"; import CloseIcon from '@material-ui/icons/Close'; import ArrowForwardIcon from '@material-ui/icons/ArrowForward'; import { searchResultCustomList } from './SearchResultCustomList'; +import { SearchModalChildrenProps, SearchModalProps } from '@backstage/plugin-search'; +import { useNavigate } from 'react-router-dom'; +import { useContent } from '@backstage/core-components'; +const useStyles = makeStyles(theme => ({ + dialogTitle: { + gap: theme.spacing(1), + display: 'grid', + alignItems: 'center', + gridTemplateColumns: '1fr auto', + '&> button': { + marginTop: theme.spacing(1), + }, + }, + input: { + flex: 1, + }, + button: { + '&:hover': { + background: 'none', + }, + }, + // Reduces default height of the modal, keeping a gap of 128px between the top and bottom of the page. + paperFullWidth: { height: 'calc(100% - 128px)' }, + dialogActionsContainer: { padding: theme.spacing(1, 3) }, + viewResultsLink: { verticalAlign: '0.5em' }, + })); -export const CustomModal = (toggleModal : any, classes : any, searchBarRef : any, handleSearchBarSubmit : any) => { - return ( - <> +export const CustomModal = ({ toggleModal }: SearchModalChildrenProps) => { + const classes = useStyles(); + const navigate = useNavigate(); + const { transitions } = useTheme(); + const { focusContent } = useContent(); + const searchBarRef = useRef(null); + + useEffect(() => { + searchBarRef?.current?.focus(); + }); + + const handleSearchResultClick = useCallback(() => { + setTimeout(focusContent, transitions.duration.leavingScreen); + }, [focusContent, transitions]); + + // This handler is called when "enter" is pressed + const handleSearchBarSubmit = useCallback(() => { + // Using ref to get the current field value without waiting for a query debounce + const query = searchBarRef.current?.value ?? ''; + navigate(`/search?query=${query}`); + handleSearchResultClick(); + }, [navigate, handleSearchResultClick]); + + return ( + <> - - - - - - + + + + + + + - - - - - - - - - {searchResultCustomList} - - - + + + + + + + + + {searchResultCustomList} + + + - + - + - + - - ); -}; + + ); + }; + +export const CustomSearchModal = (props: SearchModalProps) => { + const { open = true, hidden, toggleModal, children } = props; -export default CustomModal; \ No newline at end of file + const classes = useStyles(); + + return ( + + ); +}; \ No newline at end of file diff --git a/packages/app/src/components/search/SearchResultCustomList.tsx b/packages/app/src/components/search/SearchResultCustomList.tsx index f00b03b..4cfbc5b 100644 --- a/packages/app/src/components/search/SearchResultCustomList.tsx +++ b/packages/app/src/components/search/SearchResultCustomList.tsx @@ -6,7 +6,6 @@ import { SearchResult, DefaultResultListItem } from '@backstage/plugin-search-re import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; import { StackOverflowSearchResultListItem, StackOverflowIcon } from '@backstage/plugin-stack-overflow'; import { CatalogIcon, DocsIcon } from '@backstage/core-components'; -import { OpenShiftSvgIcon } from "../utils/icons"; import { TechDocsSearchResultCustomListItem } from './TechDocsSearchResultCustomListItem'; const SearchResultCustomList = () => { @@ -34,12 +33,7 @@ const SearchResultCustomList = () => { highlight={highlight} rank={rank} asListItem={true} - icon={() => { - if (document.location.includes('platform-developer-docs')) - return - else - return - }} + icon={} /> ); case 'stack-overflow': diff --git a/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx b/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx index ec0fee3..e485998 100644 --- a/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx +++ b/packages/app/src/components/search/TechDocsSearchResultCustomListItem.tsx @@ -1,3 +1,24 @@ +/* + * TechDocsSearchResultCustomListItem.tsx is a customized version of the original TechDocsSearchResultListItem.tsx + * This version removes the entityTitle from the result title and places it as a tag instead + */ + +/* + * Copyright 2021 The Backstage Authors + * + * 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, { PropsWithChildren, ReactNode } from 'react'; import { Box, Chip, Divider, ListItem, ListItemIcon, ListItemText, makeStyles } from '@material-ui/core'; import Typography from '@material-ui/core/Typography'; @@ -108,7 +129,7 @@ export const TechDocsSearchResultCustomListItem = ( title ) : ( <> - {resultTitle} | {entityTitle ?? resultName} docs + {resultTitle} docs )} From 998cc9f84ab41c0cede7e8359f44828b7393715c Mon Sep 17 00:00:00 2001 From: Galen Gray Date: Wed, 4 Oct 2023 16:19:42 -0700 Subject: [PATCH 4/4] Adjusting contrast on search result tags --- packages/app/src/devex-theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/devex-theme.ts b/packages/app/src/devex-theme.ts index 956c7e0..01a8ad2 100644 --- a/packages/app/src/devex-theme.ts +++ b/packages/app/src/devex-theme.ts @@ -252,7 +252,7 @@ const createCustomThemeOverrides = ( MuiChip: { root: { borderRadius: 3, - backgroundColor: theme.palette.grey[50], + backgroundColor: 'rgba(0, 0, 0, .11)', color: theme.palette.primary.dark, margin: 4, },