diff --git a/.changeset/eight-insects-tan.md b/.changeset/eight-insects-tan.md index 0223b0d275..6f4d65275f 100644 --- a/.changeset/eight-insects-tan.md +++ b/.changeset/eight-insects-tan.md @@ -2,4 +2,4 @@ '@backstage/core-components': patch --- -Add SidebarPageContent component and useContenRef hook to have a reference to content wrapper element +Add `LayoutProvider` component and `useLayoutContent` hook to have a reference to current main content element diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 296c1ad011..11eefcf8b4 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -571,6 +571,11 @@ enum LabelPosition { RIGHT = 'r', } +// Warning: (ae-missing-release-tag) "LayoutProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function LayoutProvider(props: PropsWithChildren<{}>): JSX.Element; + // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "Lifecycle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1237,24 +1242,6 @@ export function SidebarPage(props: SidebarPageProps): JSX.Element; // @public (undocumented) export type SidebarPageClassKey = 'root'; -// @public -export type SidebarPageProps = { - children?: React_2.ReactNode; -}; - -// @public -// Warning: (ae-missing-release-tag) "SidebarPageContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export function SidebarPageContent(props: PropsWithChildren<{}>): JSX.Element; - -// Warning: (ae-missing-release-tag) "SidebarPageContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type SidebarPageContextType = { - contentRef?: React_2.MutableRefObject; -}; - // Warning: (ae-missing-release-tag) "SidebarPinStateContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2419,12 +2406,13 @@ export function TrendLine( }, ): JSX.Element | null; -// Warning: (ae-missing-release-tag) "useContentRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "useLayoutContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function useContentRef(): - | React_2.MutableRefObject - | undefined; +export function useLayoutContent(): { + focusContent: () => void; + contentRef: React_2.MutableRefObject | undefined; +}; // Warning: (ae-forgotten-export) The symbol "SetQueryParams" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "useQueryParamState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 2243cfc10c..477aad9fc9 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -144,7 +144,6 @@ const DesktopSidebar = (props: SidebarProps) => { if (isPinned || disableExpandOnHover) { return; } - focusContent(); if (hoverTimerRef.current) { clearTimeout(hoverTimerRef.current); hoverTimerRef.current = undefined; diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 3dc31a5d52..665468231b 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -22,7 +22,6 @@ import React, { useEffect, useState, } from 'react'; - import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; diff --git a/packages/core-components/src/layout/Sidebar/SidebarPage.stories.tsx b/packages/core-components/src/layout/Sidebar/SidebarPage.stories.tsx deleted file mode 100644 index ca769e63cd..0000000000 --- a/packages/core-components/src/layout/Sidebar/SidebarPage.stories.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2020 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 AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; -import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; -import React, { useCallback } from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { - Sidebar, - SidebarDivider, - SidebarIntro, - SidebarItem, - SidebarSearchField, - SidebarSpace, - SidebarPage, -} from '.'; -import { useLayoutContent } from '../LayoutProvider'; -import { PluginWithTable } from '../Page/Page.stories'; - -export default { - title: 'Layout/SidebarPage', - component: SidebarPage, - decorators: [ - (storyFn: () => JSX.Element) => ( - {storyFn()} - ), - ], -}; - -export const SampleSidebarPage = () => { - return ( - - - - - ); -}; - -function CustomSidebar() { - const { focusContent } = useLayoutContent(); - const handleSearch = (input: string) => { - focusContent(); - // eslint-disable-next-line no-console - console.log(input); - }; - - return ( - - - - - - - - - - - ); -} diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 99ce966d0a..2ee84199de 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -26,7 +26,6 @@ export type { SidebarSubmenuItemProps, SidebarSubmenuItemDropdownItem, } from './SidebarSubmenuItem'; - export type { SidebarClassKey, SidebarProps } from './Bar'; export { SidebarPage, diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index c7ca33005d..841e7e0fe8 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -24,6 +24,7 @@ import { Grid, List, Paper, + useTheme, } from '@material-ui/core'; import LaunchIcon from '@material-ui/icons/Launch'; import { makeStyles } from '@material-ui/core/styles'; @@ -33,7 +34,7 @@ import { SearchResult } from '../SearchResult'; import { SearchContextProvider, useSearch } from '../SearchContext'; import { SearchResultPager } from '../SearchResultPager'; import { useRouteRef } from '@backstage/core-plugin-api'; -import { Link } from '@backstage/core-components'; +import { Link, useLayoutContent } from '@backstage/core-components'; import { rootRouteRef } from '../../plugin'; export interface SearchModalProps { @@ -60,10 +61,27 @@ export const Modal = ({ open = true, toggleModal }: SearchModalProps) => { const getSearchLink = useRouteRef(rootRouteRef); const classes = useStyles(); - const { term } = useSearch(); + const { focusContent } = useLayoutContent(); + const { transitions } = useTheme(); + const { term, setTerm } = useSearch(); + const [value, setValue] = useState(term); + + useEffect(() => { + setValue(prevValue => (prevValue !== term ? term : prevValue)); + }, [term]); + + useDebounce(() => setTerm(value), 500, [value]); + + const handleQuery = (newValue: string) => { + setValue(newValue); + }; + + const handleClear = () => setValue(''); const handleResultClick = () => { toggleModal(); + handleClear(); + setTimeout(focusContent, transitions.duration.leavingScreen); }; const handleKeyPress = () => { @@ -94,7 +112,13 @@ export const Modal = ({ open = true, toggleModal }: SearchModalProps) => { alignItems="center" > - + { + toggleModal(); + setTimeout(focusContent, transitions.duration.leavingScreen); + }} + to={`${getSearchLink()}?query=${value}`} + > View Full Results diff --git a/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx b/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx index 26b9583660..16fd6deb36 100644 --- a/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx +++ b/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx @@ -19,7 +19,10 @@ import { useNavigate } from 'react-router-dom'; import { rootRouteRef } from '../../plugin'; import { useRouteRef, IconComponent } from '@backstage/core-plugin-api'; -import { SidebarSearchField, useContentRef } from '@backstage/core-components'; +import { + SidebarSearchField, + useLayoutContent, +} from '@backstage/core-components'; export type SidebarSearchProps = { icon?: IconComponent; @@ -27,15 +30,15 @@ export type SidebarSearchProps = { export const SidebarSearch = (props: SidebarSearchProps) => { const searchRoute = useRouteRef(rootRouteRef); - const contentRef = useContentRef(); + const { focusContent } = useLayoutContent(); const navigate = useNavigate(); const handleSearch = useCallback( (query: string): void => { const queryString = qs.stringify({ query }, { addQueryPrefix: true }); - contentRef?.current?.focus(); + focusContent(); navigate(`${searchRoute()}${queryString}`); }, - [navigate, searchRoute, contentRef], + [focusContent, navigate, searchRoute], ); return (