From b7c1a5a040bb2151aea39b68e9815d67bcb46e7f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 11 Aug 2021 20:11:22 +0200 Subject: [PATCH 01/45] Add SidebarGroup & BottomNavigation components Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 7 +++ .../src/layout/Sidebar/Bar.tsx | 22 ++++---- .../src/layout/Sidebar/BottomNavigation.tsx | 52 +++++++++++++++++++ .../src/layout/Sidebar/Group.tsx | 28 ++++++++++ .../src/layout/Sidebar/index.ts | 3 +- 5 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 packages/core-components/src/layout/Sidebar/BottomNavigation.tsx create mode 100644 packages/core-components/src/layout/Sidebar/Group.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 7a666a020a..a8ce1451da 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -40,6 +40,8 @@ import { SidebarDivider, SidebarSpace, SidebarScrollWrapper, + BottomNavigation, + SidebarGroup, } from '@backstage/core-components'; const useSidebarLogoStyles = makeStyles({ @@ -101,6 +103,11 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + + + {children} ); diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index e6fa3820ad..9ee63c1c3d 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -133,20 +133,17 @@ export function Sidebar(props: PropsWithChildren) { const isOpen = (state === State.Open && !isSmallScreen) || isPinned; return ( -
-
) { {children}
-
); } diff --git a/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx b/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx new file mode 100644 index 0000000000..2356cab249 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx @@ -0,0 +1,52 @@ +/* + * 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 { + BottomNavigation as MUIBottomNavigation, + makeStyles, +} from '@material-ui/core'; +import React from 'react'; + +const useStyles = makeStyles({ + root: { + position: 'fixed', + bottom: 0, + left: 0, + right: 0, + }, +}); + +/** + * Filters for sidebar groups and reorders them to create a custom BottomNavigation + */ +export const BottomNavigation = ({ children }: React.PropsWithChildren<{}>) => { + const classes = useStyles(); + const [value, setValue] = React.useState('recents'); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { + setValue(newValue); + }; + + return ( + + {children} + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Group.tsx b/packages/core-components/src/layout/Sidebar/Group.tsx new file mode 100644 index 0000000000..f346daa9a1 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/Group.tsx @@ -0,0 +1,28 @@ +/* + * 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 { BottomNavigationAction } from '@material-ui/core'; +import MenuIcon from '@material-ui/icons/Menu'; +import React from 'react'; + +/** + * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment + * Links to page, which will be displayed + * - If 'to' Prop is not defined it will render a Menu page out of the children (if children given) + */ +export const SidebarGroup = ({ children }: React.PropsWithChildren<{}>) => { + return } />; +}; diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 067a9bf2eb..cc1d127787 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,7 +15,8 @@ */ export { Sidebar } from './Bar'; -export type { SidebarClassKey } from './Bar'; +export { BottomNavigation } from './BottomNavigation'; +export { SidebarGroup } from './Group'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; export { From 772995ac5e3199901fcd9625f6e8f5afcbb68b7d Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 16 Aug 2021 11:23:56 +0200 Subject: [PATCH 02/45] Implement a grouping for mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 140 ++++++++++-------- .../src/layout/Sidebar/BottomNavigation.tsx | 52 ------- .../src/layout/Sidebar/Group.tsx | 43 +++++- .../src/layout/Sidebar/MobileSidebar.tsx | 96 ++++++++++++ .../src/layout/Sidebar/Page.tsx | 18 +-- .../src/layout/Sidebar/config.ts | 2 +- .../src/layout/Sidebar/index.ts | 2 +- 7 files changed, 224 insertions(+), 129 deletions(-) delete mode 100644 packages/core-components/src/layout/Sidebar/BottomNavigation.tsx create mode 100644 packages/core-components/src/layout/Sidebar/MobileSidebar.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index a8ce1451da..865e5e7e61 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -14,35 +14,44 @@ * limitations under the License. */ -import React, { useContext, PropsWithChildren } from 'react'; -import { Link, makeStyles } from '@material-ui/core'; -import HomeIcon from '@material-ui/icons/Home'; -import ExtensionIcon from '@material-ui/icons/Extension'; -import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; -import MapIcon from '@material-ui/icons/MyLocation'; -import LayersIcon from '@material-ui/icons/Layers'; -import LibraryBooks from '@material-ui/icons/LibraryBooks'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import MoneyIcon from '@material-ui/icons/MonetizationOn'; -import LogoFull from './LogoFull'; -import LogoIcon from './LogoIcon'; -import { NavLink } from 'react-router-dom'; +import { + MobileSidebar, + sidebarConfig, + SidebarContext, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, +} from '@backstage/core-components'; import { GraphiQLIcon } from '@backstage/plugin-graphiql'; -import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; import { SidebarSearch } from '@backstage/plugin-search'; import { Shortcuts } from '@backstage/plugin-shortcuts'; import { - Sidebar, - SidebarPage, - sidebarConfig, - SidebarContext, - SidebarItem, - SidebarDivider, - SidebarSpace, - SidebarScrollWrapper, + Settings as SidebarSettings, + UserSettingsSignInAvatar, +} from '@backstage/plugin-user-settings'; +import { BottomNavigation, - SidebarGroup, -} from '@backstage/core-components'; + BottomNavigationAction, + Link, + makeStyles, +} from '@material-ui/core'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import HomeIcon from '@material-ui/icons/Home'; +import LayersIcon from '@material-ui/icons/Layers'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import MenuIcon from '@material-ui/icons/Menu'; +import MoneyIcon from '@material-ui/icons/MonetizationOn'; +import MapIcon from '@material-ui/icons/MyLocation'; +import SearchIcon from '@material-ui/icons/Search'; +import React, { PropsWithChildren, useContext, useState } from 'react'; +import { NavLink } from 'react-router-dom'; +import LogoFull from './LogoFull'; +import LogoIcon from './LogoIcon'; const useSidebarLogoStyles = makeStyles({ root: { @@ -77,37 +86,52 @@ const SidebarLogo = () => { ); }; -export const Root = ({ children }: PropsWithChildren<{}>) => ( - - - - - - {/* Global nav, not org-specific */} - - - - - - {/* End global nav */} - - - - - - - - - - - - - - - - - - - {children} - -); +export const Root = ({ children }: PropsWithChildren<{}>) => { + return ( + + + + } to="/search"> + + + + {/* Global nav, not org-specific */} + }> + + + + + + {/* End global nav */} + + + + + + + + + + + + + } + to="/settings" + > + + + + {children} + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx b/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx deleted file mode 100644 index 2356cab249..0000000000 --- a/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx +++ /dev/null @@ -1,52 +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 { - BottomNavigation as MUIBottomNavigation, - makeStyles, -} from '@material-ui/core'; -import React from 'react'; - -const useStyles = makeStyles({ - root: { - position: 'fixed', - bottom: 0, - left: 0, - right: 0, - }, -}); - -/** - * Filters for sidebar groups and reorders them to create a custom BottomNavigation - */ -export const BottomNavigation = ({ children }: React.PropsWithChildren<{}>) => { - const classes = useStyles(); - const [value, setValue] = React.useState('recents'); - - const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { - setValue(newValue); - }; - - return ( - - {children} - - ); -}; diff --git a/packages/core-components/src/layout/Sidebar/Group.tsx b/packages/core-components/src/layout/Sidebar/Group.tsx index f346daa9a1..2aabea69b8 100644 --- a/packages/core-components/src/layout/Sidebar/Group.tsx +++ b/packages/core-components/src/layout/Sidebar/Group.tsx @@ -14,15 +14,50 @@ * limitations under the License. */ -import { BottomNavigationAction } from '@material-ui/core'; -import MenuIcon from '@material-ui/icons/Menu'; +import { + BottomNavigationAction, + BottomNavigationActionProps, + makeStyles, +} from '@material-ui/core'; import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; + +interface SidebarGroupProps extends BottomNavigationActionProps { + to?: string; + injectedSelected?: boolean; +} + +const useStyles = makeStyles({ + label: { + display: 'none', + }, +}); /** * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment * Links to page, which will be displayed * - If 'to' Prop is not defined it will render a Menu page out of the children (if children given) */ -export const SidebarGroup = ({ children }: React.PropsWithChildren<{}>) => { - return } />; +export const SidebarGroup = ({ + to, + label, + icon, + onClick, + injectedSelected, +}: React.PropsWithChildren) => { + const classes = useStyles(); + const location = useLocation(); + + return ( + + ); }; diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx new file mode 100644 index 0000000000..4d846e3580 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -0,0 +1,96 @@ +/* + * 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 { BottomNavigation, Box, makeStyles } from '@material-ui/core'; +import React, { useState, useEffect } from 'react'; +import { useLocation } from 'react-router'; +import { SidebarGroup } from './Group'; + +const useStyles = makeStyles({ + root: { + position: 'fixed', + bottom: 0, + left: 0, + right: 0, + zIndex: 1000, + }, +}); + +const OverlayMenu = ({ children }: React.PropsWithChildren<{}>) => ( + + {children} + +); + +let currentLocation: string; + +/** + * Filters for sidebar groups and reorders them to create a custom BottomNavigation + */ +export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { + const classes = useStyles(); + const [value, setValue] = useState(); + const [menu, setMenu] = useState(); + const location = useLocation(); + + useEffect(() => { + if (menu && currentLocation !== location.pathname) { + setMenu(undefined); + setValue(undefined); + } + currentLocation = location.pathname; + }, [location, menu]); + + return ( + + {value && menu && } + + {React.Children.map(children, (child, index) => { + if ( + React.isValidElement(child) && + (child as React.ReactElement).type === SidebarGroup + ) { + if (index === value && !menu && !child.props.to) { + setMenu(child.props.children); + } + return React.cloneElement(child, { + injectedSelected: value + ? index === value + : location.pathname === child.props.to, + onClick: () => { + if (index === value && menu) { + setValue(undefined); + setMenu(undefined); + } else { + setValue(index); + } + }, + }); + } + return null; + })} + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index cc50c5af6b..d33564b5d4 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -25,19 +25,11 @@ import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; -export type SidebarPageClassKey = 'root'; - -const useStyles = makeStyles( - { - root: { - width: '100%', - minHeight: '100%', - transition: 'padding-left 0.1s ease-out', - paddingLeft: ({ isPinned }) => - isPinned - ? sidebarConfig.drawerWidthOpen - : sidebarConfig.drawerWidthClosed, - }, +const useStyles = makeStyles({ + root: { + width: '100%', + minHeight: '100%', + transition: 'padding-left 0.1s ease-out', }, { name: 'BackstageSidebarPage' }, ); diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index d2a690e38f..bfff039492 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -45,5 +45,5 @@ export type SidebarContextType = { }; export const SidebarContext = createContext({ - isOpen: false, + isOpen: true, }); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index cc1d127787..320625dc11 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,7 +15,7 @@ */ export { Sidebar } from './Bar'; -export { BottomNavigation } from './BottomNavigation'; +export { MobileSidebar } from './MobileSidebar'; export { SidebarGroup } from './Group'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; From cbebaa841bd6bce99011cd17469205af58815a90 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 18 Aug 2021 10:38:55 +0200 Subject: [PATCH 03/45] Refactored select behaviour to be better readable - Remove header from Search & Settings on mobile Co-authored-by: Tejas Kumar Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 9 +- .../app/src/components/search/SearchPage.tsx | 65 +++++--- .../src/layout/Sidebar/MobileSidebar.tsx | 155 +++++++++++------- .../Sidebar/{Group.tsx => SidebarGroup.tsx} | 47 ++++-- .../src/layout/Sidebar/config.ts | 1 + .../src/layout/Sidebar/index.ts | 2 +- .../General/UserSettingsGeneral.tsx | 4 +- .../src/components/SettingsPage.tsx | 11 +- 8 files changed, 192 insertions(+), 102 deletions(-) rename packages/core-components/src/layout/Sidebar/{Group.tsx => SidebarGroup.tsx} (54%) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 865e5e7e61..fa7fc892b2 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -32,12 +32,7 @@ import { Settings as SidebarSettings, UserSettingsSignInAvatar, } from '@backstage/plugin-user-settings'; -import { - BottomNavigation, - BottomNavigationAction, - Link, - makeStyles, -} from '@material-ui/core'; +import { Link, makeStyles } from '@material-ui/core'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; import ExtensionIcon from '@material-ui/icons/Extension'; @@ -48,7 +43,7 @@ import MenuIcon from '@material-ui/icons/Menu'; import MoneyIcon from '@material-ui/icons/MonetizationOn'; import MapIcon from '@material-ui/icons/MyLocation'; import SearchIcon from '@material-ui/icons/Search'; -import React, { PropsWithChildren, useContext, useState } from 'react'; +import React, { PropsWithChildren, useContext } from 'react'; import { NavLink } from 'react-router-dom'; import LogoFull from './LogoFull'; import LogoIcon from './LogoIcon'; diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 54f59d1a6e..7829fa66aa 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -25,8 +25,18 @@ import { SearchType, } from '@backstage/plugin-search'; import { DocsResultListItem } from '@backstage/plugin-techdocs'; -import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core'; -import React from 'react'; +import { SearchResultSet } from '@backstage/search-common'; +import { BackstageTheme } from '@backstage/theme'; +import { + Grid, + List, + makeStyles, + Paper, + Theme, + useMediaQuery, +} from '@material-ui/core'; +import Pagination from '@material-ui/lab/Pagination'; +import React, { useState } from 'react'; const useStyles = makeStyles((theme: Theme) => ({ bar: { @@ -44,9 +54,16 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); + // Think about abstraction to utils here + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.up('xs'), + ); + return ( -
} /> + {!isMobileScreen && ( +
} /> + )} @@ -54,26 +71,28 @@ const SearchPage = () => { - - - - - - - - + {!isMobileScreen && ( + + + + + + + + )} + {({ results }) => ( diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 4d846e3580..d3cd9e65ae 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -14,83 +14,128 @@ * limitations under the License. */ -import { BottomNavigation, Box, makeStyles } from '@material-ui/core'; -import React, { useState, useEffect } from 'react'; +import { BackstageTheme } from '@backstage/theme'; +import { + BottomNavigation, + Box, + IconButton, + makeStyles, + Typography, +} from '@material-ui/core'; +import React, { createContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router'; -import { SidebarGroup } from './Group'; +import { sidebarConfig } from './config'; +import { SidebarGroup } from './SidebarGroup'; +import CloseIcon from '@material-ui/icons/Close'; -const useStyles = makeStyles({ +type MobileSidebarContextType = { + selectedMenuItemIndex: number; + setSelectedMenuItemIndex: React.Dispatch>; +}; + +const useStyles = makeStyles(theme => ({ root: { position: 'fixed', + backgroundColor: theme.palette.navigation.background, + color: theme.palette.navigation.color, bottom: 0, left: 0, right: 0, zIndex: 1000, + borderTop: '1px solid #383838', }, + + overlay: { + background: theme.palette.navigation.background, + width: '100%', + flex: '0 1 auto', + height: `calc(100% - ${sidebarConfig.mobileSidebarHeight}px)`, + overflow: 'auto', + position: 'fixed', + zIndex: 500, + }, + + overlayHeader: { + display: 'flex', + color: theme.palette.bursts.fontColor, + alignItems: 'center', + justifyContent: 'space-between', + padding: `${theme.spacing(2)}px ${theme.spacing(3)}px`, + }, + + overlayHeaderClose: { + color: theme.palette.bursts.fontColor, + }, +})); + +const OverlayMenu = ({ + children, + label, + onClose, +}: React.PropsWithChildren<{ label: string; onClose: () => void }>) => { + const classes = useStyles(); + + return ( + + + {label} + + + + + {children} + + ); +}; + +export const MobileSidebarContext = createContext({ + selectedMenuItemIndex: -1, + setSelectedMenuItemIndex: () => {}, }); -const OverlayMenu = ({ children }: React.PropsWithChildren<{}>) => ( - - {children} - -); - -let currentLocation: string; - /** * Filters for sidebar groups and reorders them to create a custom BottomNavigation */ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { const classes = useStyles(); - const [value, setValue] = useState(); - const [menu, setMenu] = useState(); const location = useLocation(); + const [selectedMenuItemIndex, setSelectedMenuItemIndex] = + useState(-1); useEffect(() => { - if (menu && currentLocation !== location.pathname) { - setMenu(undefined); - setValue(undefined); - } - currentLocation = location.pathname; - }, [location, menu]); + // This is getting triggered to often - fix me! + setSelectedMenuItemIndex(-1); + }, [location.pathname]); + + const sidebarGroups = React.Children.map(children, child => + React.isValidElement(child) && child.type === SidebarGroup ? child : null, + ); + + if (!sidebarGroups) { + // error api + return null; // think about the exception state + } + + const shouldShowGroupChildren = + selectedMenuItemIndex >= 0 && + !sidebarGroups[selectedMenuItemIndex].props.to; return ( - - {value && menu && } + + {shouldShowGroupChildren && ( + setSelectedMenuItemIndex(-1)} + /> + )} - {React.Children.map(children, (child, index) => { - if ( - React.isValidElement(child) && - (child as React.ReactElement).type === SidebarGroup - ) { - if (index === value && !menu && !child.props.to) { - setMenu(child.props.children); - } - return React.cloneElement(child, { - injectedSelected: value - ? index === value - : location.pathname === child.props.to, - onClick: () => { - if (index === value && menu) { - setValue(undefined); - setMenu(undefined); - } else { - setValue(index); - } - }, - }); - } - return null; - })} + {sidebarGroups} - + ); }; diff --git a/packages/core-components/src/layout/Sidebar/Group.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx similarity index 54% rename from packages/core-components/src/layout/Sidebar/Group.tsx rename to packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 2aabea69b8..db3c39e227 100644 --- a/packages/core-components/src/layout/Sidebar/Group.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-shadow */ /* * Copyright 2020 The Backstage Authors * @@ -14,24 +15,34 @@ * limitations under the License. */ +import { BackstageTheme } from '@backstage/theme'; import { BottomNavigationAction, BottomNavigationActionProps, makeStyles, } from '@material-ui/core'; -import React from 'react'; -import { NavLink, useLocation } from 'react-router-dom'; +import React, { useContext } from 'react'; +import { useLocation } from 'react-router-dom'; +import { Link } from '../../components'; +import { MobileSidebarContext } from './MobileSidebar'; interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; - injectedSelected?: boolean; } -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ + root: { + color: theme.palette.navigation.color, + }, + + selected: { + color: `${theme.palette.navigation.selectedColor}!important`, + }, + label: { display: 'none', }, -}); +})); /** * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment @@ -42,21 +53,35 @@ export const SidebarGroup = ({ to, label, icon, - onClick, - injectedSelected, + value, }: React.PropsWithChildren) => { const classes = useStyles(); const location = useLocation(); + const { selectedMenuItemIndex, setSelectedMenuItemIndex } = + useContext(MobileSidebarContext); + + const onChange = (_, value) => { + if (value === selectedMenuItemIndex && to !== location.pathname) { + setSelectedMenuItemIndex(-1); + } else if (value !== selectedMenuItemIndex) { + setSelectedMenuItemIndex(value); + } + }; + + const selected = + value === selectedMenuItemIndex || + (selectedMenuItemIndex >= 0 && to === location.pathname); return ( + // @ts-ignore Material UI issue: https://github.com/mui-org/material-ui/issues/27820 ); diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index bfff039492..e213385f73 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -35,6 +35,7 @@ export const sidebarConfig = { selectedIndicatorWidth: 3, userBadgePadding, userBadgeDiameter: drawerWidthClosed - userBadgePadding * 2, + mobileSidebarHeight: 56, }; export const SIDEBAR_INTRO_LOCAL_STORAGE = diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 320625dc11..26a4c0e8a5 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -16,7 +16,7 @@ export { Sidebar } from './Bar'; export { MobileSidebar } from './MobileSidebar'; -export { SidebarGroup } from './Group'; +export { SidebarGroup } from './SidebarGroup'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; export { diff --git a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx index bd08fef565..6705997d1c 100644 --- a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx @@ -21,10 +21,10 @@ import { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; export const UserSettingsGeneral = () => { return ( - + - + diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index eebf6ff865..20ea6820f4 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -14,21 +14,26 @@ * limitations under the License. */ +import { Header, Page, TabbedLayout } from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; +import { useMediaQuery } from '@material-ui/core'; import React from 'react'; import { UserSettingsAuthProviders } from './AuthProviders'; import { UserSettingsFeatureFlags } from './FeatureFlags'; import { UserSettingsGeneral } from './General'; -import { Header, Page, TabbedLayout } from '@backstage/core-components'; type Props = { providerSettings?: JSX.Element; }; export const SettingsPage = ({ providerSettings }: Props) => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.up('xs'), + ); + return ( -
- + {!isMobileScreen &&
} From d5f08f4b03505702bdc291742a7026c437f05821 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 19 Aug 2021 12:33:34 +0200 Subject: [PATCH 04/45] Adjust display style & selected behaviour Signed-off-by: Philipp Hugenroth --- .../core-components/src/layout/Page/Page.tsx | 31 +++-- .../src/layout/Sidebar/Items.tsx | 106 +++++++++++++----- .../src/layout/Sidebar/SidebarGroup.tsx | 17 ++- .../CatalogResultListItem.tsx | 1 + .../General/UserSettingsAppearanceCard.tsx | 2 +- 5 files changed, 104 insertions(+), 53 deletions(-) diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 57653d23b5..ed59fc530d 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -16,24 +16,21 @@ import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; +import { makeStyles, ThemeProvider } from '@material-ui/core'; +import { sidebarConfig } from '../Sidebar'; -export type PageClassKey = 'root'; - -const useStyles = makeStyles( - () => ({ - root: { - display: 'grid', - gridTemplateAreas: - "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", - gridTemplateRows: 'max-content auto 1fr', - gridTemplateColumns: 'auto 1fr auto', - height: '100vh', - overflowY: 'auto', - }, - }), - { name: 'BackstagePage' }, -); +const useStyles = makeStyles({ + root: { + display: 'grid', + gridTemplateAreas: + "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", + gridTemplateRows: 'max-content auto 1fr', + gridTemplateColumns: 'auto 1fr auto', + // TODO: Only mobile + height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, + overflowY: 'auto', + }, +}); type Props = { themeId: string; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index ed089ad6a1..13602b83d0 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -38,36 +38,82 @@ import { } from 'react-router-dom'; import { sidebarConfig, SidebarContext } from './config'; -export type SidebarItemClassKey = - | 'root' - | 'buttonItem' - | 'closed' - | 'open' - | 'label' - | 'iconContainer' - | 'searchRoot' - | 'searchField' - | 'searchFieldHTMLInput' - | 'searchContainer' - | 'secondaryAction' - | 'selected'; -const useStyles = makeStyles( - theme => { - const { - selectedIndicatorWidth, - drawerWidthClosed, - drawerWidthOpen, - iconContainerWidth, - } = sidebarConfig; - return { - root: { - color: theme.palette.navigation.color, - display: 'flex', - flexFlow: 'row nowrap', - alignItems: 'center', - height: 48, - cursor: 'pointer', +const useStyles = makeStyles(theme => { + const { + selectedIndicatorWidth, + drawerWidthClosed, + drawerWidthOpen, + iconContainerWidth, + } = sidebarConfig; + return { + root: { + color: theme.palette.navigation.color, + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + height: 48, + cursor: 'pointer', + }, + buttonItem: { + background: 'none', + border: 'none', + width: 'auto', + margin: 0, + padding: 0, + textAlign: 'inherit', + font: 'inherit', + }, + closed: { + width: drawerWidthClosed, + justifyContent: 'center', + }, + open: { + // Does not apply on mobile + // width: drawerWidthOpen, + }, + label: { + // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs + fontWeight: 'bold', + whiteSpace: 'nowrap', + lineHeight: 'auto', + flex: '3 1 auto', + width: '110px', + overflow: 'hidden', + 'text-overflow': 'ellipsis', + }, + iconContainer: { + boxSizing: 'border-box', + height: '100%', + width: iconContainerWidth, + marginRight: -theme.spacing(2), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + searchRoot: { + marginBottom: 12, + }, + searchField: { + color: '#b5b5b5', + fontWeight: 'bold', + fontSize: theme.typography.fontSize, + }, + searchFieldHTMLInput: { + padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + }, + searchContainer: { + width: drawerWidthOpen - iconContainerWidth, + }, + secondaryAction: { + width: theme.spacing(6), + textAlign: 'center', + marginRight: theme.spacing(1), + }, + selected: { + '&$root': { + borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, + color: theme.palette.navigation.selectedColor, }, buttonItem: { background: 'none', @@ -136,7 +182,7 @@ const useStyles = makeStyles( }, }, }; - }, + }}, { name: 'BackstageSidebarItem' }, ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index db3c39e227..02267931dc 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -24,6 +24,7 @@ import { import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; import { Link } from '../../components'; +import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; interface SidebarGroupProps extends BottomNavigationActionProps { @@ -32,11 +33,15 @@ interface SidebarGroupProps extends BottomNavigationActionProps { const useStyles = makeStyles(theme => ({ root: { + flexGrow: 0, + margin: `0 ${theme.spacing(2)}px`, color: theme.palette.navigation.color, }, selected: { color: `${theme.palette.navigation.selectedColor}!important`, + borderTop: `solid ${sidebarConfig.selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, + marginTop: '-1px', }, label: { @@ -60,17 +65,19 @@ export const SidebarGroup = ({ const { selectedMenuItemIndex, setSelectedMenuItemIndex } = useContext(MobileSidebarContext); - const onChange = (_, value) => { - if (value === selectedMenuItemIndex && to !== location.pathname) { + const onChange = (_: React.ChangeEvent<{}>, value: number) => { + if (value === selectedMenuItemIndex) { setSelectedMenuItemIndex(-1); - } else if (value !== selectedMenuItemIndex) { + } else { setSelectedMenuItemIndex(value); } }; const selected = - value === selectedMenuItemIndex || - (selectedMenuItemIndex >= 0 && to === location.pathname); + (value === selectedMenuItemIndex && selectedMenuItemIndex >= 0) || + (!(value === selectedMenuItemIndex) && + !(selectedMenuItemIndex >= 0) && + to === location.pathname); return ( // @ts-ignore Material UI issue: https://github.com/mui-org/material-ui/issues/27820 diff --git a/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx b/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx index 1aaa50a7bf..f0a32ec0e2 100644 --- a/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx +++ b/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx @@ -31,6 +31,7 @@ const useStyles = makeStyles({ }, itemText: { width: '100%', + wordBreak: 'break-all', marginBottom: '1rem', }, }); diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 8f6e60e289..cb198c8d01 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -23,7 +23,7 @@ export const UserSettingsAppearanceCard = () => ( - + {/* */} ); From 746af79164987d94d7ac891580fd974f5b379859 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 19 Aug 2021 13:56:04 +0200 Subject: [PATCH 05/45] Align to current screen size Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 6 +-- .../app/src/components/search/SearchPage.tsx | 3 +- .../core-components/src/layout/Page/Page.tsx | 12 ++++-- .../src/layout/Sidebar/Bar.tsx | 42 ++++++++++++------- .../src/layout/Sidebar/Items.tsx | 5 ++- .../src/layout/Sidebar/Page.tsx | 22 ++++++---- .../src/layout/Sidebar/SidebarGroup.tsx | 12 +++++- plugins/shortcuts/src/AddShortcut.tsx | 2 +- .../General/UserSettingsAppearanceCard.tsx | 27 +++++++----- .../src/components/SettingsPage.tsx | 2 +- 10 files changed, 86 insertions(+), 47 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index fa7fc892b2..b9b0384afb 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -15,7 +15,7 @@ */ import { - MobileSidebar, + Sidebar, sidebarConfig, SidebarContext, SidebarDivider, @@ -84,7 +84,7 @@ const SidebarLogo = () => { export const Root = ({ children }: PropsWithChildren<{}>) => { return ( - + } to="/search"> @@ -125,7 +125,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => { > - + {children} ); diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 7829fa66aa..fefed84f58 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -54,9 +54,8 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - // Think about abstraction to utils here const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.up('xs'), + theme.breakpoints.down('xs'), ); return ( diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index ed59fc530d..9f49174977 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -19,18 +19,22 @@ import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core'; import { sidebarConfig } from '../Sidebar'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ root: { display: 'grid', gridTemplateAreas: "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", gridTemplateRows: 'max-content auto 1fr', gridTemplateColumns: 'auto 1fr auto', - // TODO: Only mobile - height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, + [theme.breakpoints.up('sm')]: { + height: '100vh', + }, + [theme.breakpoints.down('xs')]: { + height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, + }, overflowY: 'auto', }, -}); +})); type Props = { themeId: string; diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 9ee63c1c3d..ead77d4cae 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -21,6 +21,7 @@ import React, { useRef, useState, useContext, PropsWithChildren } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; +import { MobileSidebar } from './MobileSidebar'; export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; @@ -80,12 +81,11 @@ type Props = { closeDelayMs?: number; }; -export function Sidebar(props: PropsWithChildren) { - const { - openDelayMs = sidebarConfig.defaultOpenDelayMs, - closeDelayMs = sidebarConfig.defaultCloseDelayMs, - children, - } = props; +const DesktopSidebar = ({ + openDelayMs = sidebarConfig.defaultOpenDelayMs, + closeDelayMs = sidebarConfig.defaultCloseDelayMs, + children, +}: PropsWithChildren) => { const classes = useStyles(); const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('md'), @@ -138,18 +138,30 @@ export function Sidebar(props: PropsWithChildren) { isOpen, }} > -
- {children} -
- + className={clsx(classes.drawer, { + [classes.drawerOpen]: isOpen, + })} + > + {children} + + ); -} +}; + +export const Sidebar = ({ children }: React.PropsWithChildren<{}>) => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + return isMobileScreen ? ( + {children} + ) : ( + {children} + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 13602b83d0..19fd8c0c0a 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -69,8 +69,9 @@ const useStyles = makeStyles(theme => { justifyContent: 'center', }, open: { - // Does not apply on mobile - // width: drawerWidthOpen, + [theme.breakpoints.up('sm')]: { + width: drawerWidthOpen, + }, }, label: { // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index d33564b5d4..cecc985a28 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core'; import React, { createContext, PropsWithChildren, @@ -25,12 +25,20 @@ import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; -const useStyles = makeStyles({ - root: { - width: '100%', - minHeight: '100%', - transition: 'padding-left 0.1s ease-out', - }, +const useStyles = makeStyles( + theme => ({ + root: { + width: '100%', + minHeight: '100%', + transition: 'padding-left 0.1s ease-out', + [theme.breakpoints.up('sm')]: { + paddingLeft: ({ isPinned }) => + isPinned + ? sidebarConfig.drawerWidthOpen + : sidebarConfig.drawerWidthClosed, + }, + }, + }), { name: 'BackstageSidebarPage' }, ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 02267931dc..75dbb9ae91 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -20,6 +20,7 @@ import { BottomNavigationAction, BottomNavigationActionProps, makeStyles, + useMediaQuery, } from '@material-ui/core'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; @@ -59,11 +60,15 @@ export const SidebarGroup = ({ label, icon, value, + children, }: React.PropsWithChildren) => { const classes = useStyles(); const location = useLocation(); const { selectedMenuItemIndex, setSelectedMenuItemIndex } = useContext(MobileSidebarContext); + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); const onChange = (_: React.ChangeEvent<{}>, value: number) => { if (value === selectedMenuItemIndex) { @@ -79,8 +84,9 @@ export const SidebarGroup = ({ !(selectedMenuItemIndex >= 0) && to === location.pathname); - return ( - // @ts-ignore Material UI issue: https://github.com/mui-org/material-ui/issues/27820 + return isMobileScreen ? ( + // Material UI issue: https://github.com/mui-org/material-ui/issues/27820 + // @ts-ignore + ) : ( + <>{children} ); }; diff --git a/plugins/shortcuts/src/AddShortcut.tsx b/plugins/shortcuts/src/AddShortcut.tsx index 87c2750206..7c1c5ca118 100644 --- a/plugins/shortcuts/src/AddShortcut.tsx +++ b/plugins/shortcuts/src/AddShortcut.tsx @@ -31,7 +31,7 @@ import { alertApiRef, useApi } from '@backstage/core-plugin-api'; const useStyles = makeStyles(theme => ({ card: { - width: 400, + maxWidth: 400, }, header: { marginBottom: theme.spacing(1), diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index cb198c8d01..83937f9300 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,17 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { List } from '@material-ui/core'; import { InfoCard } from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; +import { List, useMediaQuery } from '@material-ui/core'; +import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; -export const UserSettingsAppearanceCard = () => ( - - - - {/* */} - - -); +export const UserSettingsAppearanceCard = () => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + return ( + + + + {!isMobileScreen && } + + + ); +}; diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index 20ea6820f4..d5c7171148 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -28,7 +28,7 @@ type Props = { export const SettingsPage = ({ providerSettings }: Props) => { const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.up('xs'), + theme.breakpoints.down('xs'), ); return ( From 65e1a35c0fa05c295f84bed6292d22f2e9103365 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 20 Aug 2021 11:00:21 +0200 Subject: [PATCH 06/45] Update documentation & add changeset Signed-off-by: Philipp Hugenroth --- .changeset/fuzzy-olives-impress.md | 8 ++ .../configure-app-with-plugins.md | 17 ++++ packages/app/src/components/Root/Root.tsx | 88 ++++++++----------- .../src/layout/Sidebar/MobileSidebar.tsx | 11 ++- .../src/layout/Sidebar/Sidebar.stories.tsx | 20 +++-- plugins/cost-insights/README.md | 24 +++-- 6 files changed, 100 insertions(+), 68 deletions(-) create mode 100644 .changeset/fuzzy-olives-impress.md diff --git a/.changeset/fuzzy-olives-impress.md b/.changeset/fuzzy-olives-impress.md new file mode 100644 index 0000000000..9f9c8f755b --- /dev/null +++ b/.changeset/fuzzy-olives-impress.md @@ -0,0 +1,8 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-user-settings': patch +--- + +Add `MobileSidebar` component & use it for smaller screens to improve the UX on mobile devices by switching to a navigation at the bottom of the screen. For customizing the experience you can group `SidebarItems` in a `SidebarGroup` or create a `SidebarGroup` with a link. For an example take a look at the `Root.tsx` or the updated ["Adding a plugin page to the Sidebar"](https://backstage.io/docs/getting-started/configure-app-with-plugins) documentation. diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 746b4bd940..11e2f4d75a 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -90,3 +90,20 @@ extension to `.icon.svg`. For example: ```ts import InternalToolIcon from './internal-tool.icon.svg'; ``` + +On mobile devices the `Sidebar` is displayed at the bottom of the screen. For +customizing the experience you can group `SidebarItems` in a `SidebarGroup` or +create a `SidebarGroup` with a link. All `SidebarGroups` are displayed in the +bottom navigation with an icon. + +```ts +// ... inside the AppSidebar component +} label="Menu"> + ... + + ... + +``` + +If no `SidebarGroup`is provided a default menu will display the `Sidebar` +content. diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index b9b0384afb..5995346a6f 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -81,52 +81,42 @@ const SidebarLogo = () => { ); }; -export const Root = ({ children }: PropsWithChildren<{}>) => { - return ( - - - - } to="/search"> - - - - {/* Global nav, not org-specific */} - }> - - - - - - {/* End global nav */} - - - - - - - - - - - - - } - to="/settings" - > - - - - {children} - - ); -}; +export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + + {/* } to="/search"> */} + + {/* */} + + {/* Global nav, not org-specific */} + {/* }> */} + + + + + + {/* End global nav */} + + + + + + + + + + {/* */} + + + {/* } + to="/settings" + > */} + + {/* */} + + {children} + +); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index d3cd9e65ae..633f366501 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -22,11 +22,12 @@ import { makeStyles, Typography, } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import MenuIcon from '@material-ui/icons/Menu'; import React, { createContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router'; import { sidebarConfig } from './config'; import { SidebarGroup } from './SidebarGroup'; -import CloseIcon from '@material-ui/icons/Close'; type MobileSidebarContextType = { selectedMenuItemIndex: number; @@ -115,8 +116,14 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { ); if (!sidebarGroups) { - // error api return null; // think about the exception state + } else if (!sidebarGroups.length) { + // Render default SidebarGroup if no + sidebarGroups.push( + }> + {children} + , + ); } const shouldShowGroupChildren = diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index aee5c70b43..5aa279d846 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -16,11 +16,13 @@ import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; +import MenuIcon from '@material-ui/icons/Menu'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { Sidebar, SidebarDivider, + SidebarGroup, SidebarIntro, SidebarItem, SidebarSearchField, @@ -44,13 +46,15 @@ const handleSearch = (input: string) => { export const SampleSidebar = () => ( - - - - - - - - + + + + + + + + + + ); diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index 187ab78b73..91f717dfaa 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -79,21 +79,27 @@ To expose the plugin to your users, you can integrate the `cost-insights` route export const AppSidebar = () => ( - + } to="/search"> + + {/* Global nav, not org-specific */} - - - - - - -+ + }> + + + + + + + + + {/* End global nav */} - + } to="/settings"> + + ); ``` From 958e0b46479aa28f58497450a983868852637897 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 20 Aug 2021 11:28:47 +0200 Subject: [PATCH 07/45] Adjust create-app & api-report Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 54 ++++++++++--------- packages/core-components/api-report.md | 33 ++++++++++-- .../src/layout/Sidebar/Bar.tsx | 7 ++- .../packages/app/src/components/Root/Root.tsx | 53 ++++++++++-------- 4 files changed, 94 insertions(+), 53 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 5995346a6f..1851145a31 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -85,37 +85,41 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - {/* } to="/search"> */} - - {/* */} + } to="/search"> + + - {/* Global nav, not org-specific */} - {/* }> */} - - - - - - {/* End global nav */} - - - - - - - - - - {/* */} + }> + {/* Global nav, not org-specific */} + + + + + + {/* End global nav */} + + + + + + + + + + - {/* } to="/settings" - > */} - - {/* */} + > + + {children} diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index cc884c9e8f..583a29faa7 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -7,9 +7,11 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; -import { BackstageTheme } from '@backstage/theme'; -import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; -import { CardHeaderProps } from '@material-ui/core/CardHeader'; +import { BottomNavigationActionProps } from '@material-ui/core'; +import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; +import { ButtonProps } from '@material-ui/core'; +import { ButtonTypeMap } from '@material-ui/core'; +import { CardHeaderProps } from '@material-ui/core'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; @@ -632,6 +634,13 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element; // @public (undocumented) export type MissingAnnotationEmptyStateClassKey = 'code'; +// Warning: (ae-missing-release-tag) "MobileSidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const MobileSidebar: ({ + children, +}: React_2.PropsWithChildren<{}>) => JSX.Element | null; + // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -771,7 +780,10 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Sidebar(props: PropsWithChildren): JSX.Element; +export const Sidebar: ({ + children, + ...props +}: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -800,6 +812,7 @@ export const sidebarConfig: { selectedIndicatorWidth: number; userBadgePadding: number; userBadgeDiameter: number; + mobileSidebarHeight: number; }; // Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1083,6 +1096,18 @@ export const SidebarDivider: React_2.ComponentType< } >; +// Warning: (ae-forgotten-export) The symbol "SidebarGroupProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const SidebarGroup: ({ + to, + label, + icon, + value, + children, +}: React_2.PropsWithChildren) => JSX.Element; + // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index ead77d4cae..5bcd8ec789 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -154,13 +154,16 @@ const DesktopSidebar = ({ ); }; -export const Sidebar = ({ children }: React.PropsWithChildren<{}>) => { +export const Sidebar = ({ + children, + ...props +}: React.PropsWithChildren) => { const isMobileScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); return isMobileScreen ? ( - {children} + {children} ) : ( {children} ); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 198e7ec2b1..d07f8abae7 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -14,28 +14,27 @@ * limitations under the License. */ -import React, { useContext, PropsWithChildren } from 'react'; -import { Link, makeStyles } from '@material-ui/core'; -import HomeIcon from '@material-ui/icons/Home'; -import ExtensionIcon from '@material-ui/icons/Extension'; -import MapIcon from '@material-ui/icons/MyLocation'; -import LibraryBooks from '@material-ui/icons/LibraryBooks'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import LogoFull from './LogoFull'; -import LogoIcon from './LogoIcon'; -import { NavLink } from 'react-router-dom'; -import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; +import { + Sidebar, sidebarConfig, + SidebarContext, SidebarDivider, SidebarGroup, SidebarItem, SidebarPage, SidebarScrollWrapper, SidebarSpace +} from '@backstage/core-components'; import { SidebarSearch } from '@backstage/plugin-search'; import { - Sidebar, - SidebarPage, - sidebarConfig, - SidebarContext, - SidebarItem, - SidebarDivider, - SidebarSpace, - SidebarScrollWrapper, -} from '@backstage/core-components'; + Settings as SidebarSettings, + UserSettingsSignInAvatar +} from '@backstage/plugin-user-settings'; +import { Link, makeStyles } from '@material-ui/core'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import HomeIcon from '@material-ui/icons/Home'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import MenuIcon from '@material-ui/icons/Menu'; +import MapIcon from '@material-ui/icons/MyLocation'; +import SearchIcon from '@material-ui/icons/Search'; +import React, { PropsWithChildren, useContext } from 'react'; +import { NavLink } from 'react-router-dom'; +import LogoFull from './LogoFull'; +import LogoIcon from './LogoIcon'; const useSidebarLogoStyles = makeStyles({ root: { @@ -74,8 +73,11 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - + } to="/search"> + + + }> {/* Global nav, not org-specific */} @@ -86,9 +88,16 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + - + } + to="/settings" + > + + {children} From 4313e9167af56f875e7791c83bd0ed3fc569b541 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 23 Aug 2021 10:00:17 +0200 Subject: [PATCH 08/45] Prettier fix create-app templates Signed-off-by: Philipp Hugenroth --- .../default-app/packages/app/src/components/Root/Root.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index d07f8abae7..22d8fdd705 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -57,6 +57,7 @@ const SidebarLogo = () => { return (
+ Date: Mon, 23 Aug 2021 10:14:06 +0200 Subject: [PATCH 09/45] Prettier Signed-off-by: Philipp Hugenroth --- .../packages/app/src/components/Root/Root.tsx | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 22d8fdd705..4330ab9bcd 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -15,13 +15,20 @@ */ import { - Sidebar, sidebarConfig, - SidebarContext, SidebarDivider, SidebarGroup, SidebarItem, SidebarPage, SidebarScrollWrapper, SidebarSpace + Sidebar, + sidebarConfig, + SidebarContext, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, } from '@backstage/core-components'; import { SidebarSearch } from '@backstage/plugin-search'; import { Settings as SidebarSettings, - UserSettingsSignInAvatar + UserSettingsSignInAvatar, } from '@backstage/plugin-user-settings'; import { Link, makeStyles } from '@material-ui/core'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; @@ -57,7 +64,6 @@ const SidebarLogo = () => { return (
- ) => ( }> - {/* Global nav, not org-specific */} - - - - - {/* End global nav */} - - - - + {/* Global nav, not org-specific */} + + + + + {/* End global nav */} + + + + From aa0d73424a86408a5aa236a4fe3cab49b5360c1e Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 23 Aug 2021 13:18:52 +0200 Subject: [PATCH 10/45] Add priority prop to add possibility for rearranging SidebarGroups for the mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 2 +- .../src/layout/Sidebar/MobileSidebar.tsx | 43 +++++++++++++++---- .../src/layout/Sidebar/SidebarGroup.tsx | 6 +-- .../packages/app/src/components/Root/Root.tsx | 2 +- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 1851145a31..d4a791173b 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -89,7 +89,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 633f366501..93fc4d58c5 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -69,6 +69,22 @@ const useStyles = makeStyles(theme => ({ }, })); +const sortSidebarGroupsForPriority = ( + childA: React.ReactElement, + childB: React.ReactElement, +) => { + const priorityADefined = childA.props.priority !== undefined; + const priorityBDefined = childB.props.priority !== undefined; + if (priorityADefined && !priorityBDefined) { + return -1; + } else if (priorityBDefined && !priorityADefined) { + return 1; + } else if (priorityADefined && priorityBDefined) { + return childA.props.priority - childB.props.priority; + } + return 0; +}; + const OverlayMenu = ({ children, label, @@ -97,33 +113,42 @@ export const MobileSidebarContext = createContext({ setSelectedMenuItemIndex: () => {}, }); -/** - * Filters for sidebar groups and reorders them to create a custom BottomNavigation - */ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { const classes = useStyles(); const location = useLocation(); const [selectedMenuItemIndex, setSelectedMenuItemIndex] = useState(-1); + let shouldSortSidebarGroups = false; useEffect(() => { - // This is getting triggered to often - fix me! setSelectedMenuItemIndex(-1); }, [location.pathname]); - const sidebarGroups = React.Children.map(children, child => - React.isValidElement(child) && child.type === SidebarGroup ? child : null, - ); + // Filter children for SidebarGroups & set `shouldSortSidebarGroups` if priorities are set for one or more SidebarGroups + let sidebarGroups = React.Children.map(children, child => { + if (React.isValidElement(child) && child.type === SidebarGroup) { + if (child.props.priority !== undefined) { + shouldSortSidebarGroups = true; + } + return child; + } + return null; + }); if (!sidebarGroups) { - return null; // think about the exception state + // If Sidebar has no children the MobileSidebar won't be rendered + return null; } else if (!sidebarGroups.length) { - // Render default SidebarGroup if no + // If Sidebar has no SidebarGroup as a children a default + // SidebarGroup with the complete Sidebar content will be created sidebarGroups.push( }> {children} , ); + } else if (shouldSortSidebarGroups) { + // If a SidebarGroup has a given priority the SidebarGroups are sorted for prioirty + sidebarGroups = sidebarGroups.sort(sortSidebarGroupsForPriority); } const shouldShowGroupChildren = diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 75dbb9ae91..c7dfe0fc21 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -30,6 +30,7 @@ import { MobileSidebarContext } from './MobileSidebar'; interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; + priority?: number; } const useStyles = makeStyles(theme => ({ @@ -50,11 +51,6 @@ const useStyles = makeStyles(theme => ({ }, })); -/** - * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment - * Links to page, which will be displayed - * - If 'to' Prop is not defined it will render a Menu page out of the children (if children given) - */ export const SidebarGroup = ({ to, label, diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 4330ab9bcd..434400511a 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -84,7 +84,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} From 29715dd22bd7778f63b45074cbd00973493d799f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 23 Aug 2021 14:04:10 +0200 Subject: [PATCH 11/45] Generate api-report Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 583a29faa7..2ab91c9711 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -636,7 +636,7 @@ export type MissingAnnotationEmptyStateClassKey = 'code'; // Warning: (ae-missing-release-tag) "MobileSidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public +// @public (undocumented) export const MobileSidebar: ({ children, }: React_2.PropsWithChildren<{}>) => JSX.Element | null; @@ -1099,7 +1099,7 @@ export const SidebarDivider: React_2.ComponentType< // Warning: (ae-forgotten-export) The symbol "SidebarGroupProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public +// @public (undocumented) export const SidebarGroup: ({ to, label, From 8a08a9e7af3b5b908a0bb9d4fc22b0cd6c855187 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 25 Aug 2021 00:26:48 +0200 Subject: [PATCH 12/45] Add tests for MobileSidebar & SidebarGroup Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/MobileSidebar.test.tsx | 96 +++++++++++++++++++ .../src/layout/Sidebar/MobileSidebar.tsx | 6 +- .../src/layout/Sidebar/SidebarGroup.test.tsx | 66 +++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx create mode 100644 packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx new file mode 100644 index 0000000000..6f6463a3dc --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx @@ -0,0 +1,96 @@ +/* + * 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 { mockBreakpoint, renderInTestApp } from '@backstage/test-utils'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import HomeIcon from '@material-ui/icons/Home'; +import LayersIcon from '@material-ui/icons/Layers'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import { fireEvent } from '@testing-library/react'; +import React from 'react'; +import { Sidebar } from './Bar'; +import { SidebarItem } from './Items'; +import { MobileSidebar } from './MobileSidebar'; +import { SidebarGroup } from './SidebarGroup'; + +const MobileSidebarWithGroups = () => ( + +

Header

+ } label="Menu"> + + + + +
Content
+
More Content
+ } label="Create" to="#" /> +
Footer
+
+); + +const MobileSidebarWithoutGroups = () => ( + + + + + +); + +describe('', () => { + beforeEach(() => { + mockBreakpoint({ matches: true }); + }); + + it('should render MobileSidebar on smaller screens', async () => { + const { getByTestId } = await renderInTestApp( + + + , + ); + expect(getByTestId('mobile-sidebar-root')).toBeVisible(); + }); + + it('should render only SidebarGroups inside MobileSidebar', async () => { + const { findAllByRole, getByTestId, findByText } = await renderInTestApp( + , + ); + expect(getByTestId('mobile-sidebar-root').children.length).toBe(2); + expect((await findAllByRole('button')).length).toBe(2); + expect(await findByText('Menu')).toBeValid(); + expect(await findByText('Create')).toBeValid(); + }); + + it('should render default MobileSidebar when there are no SidebarGroups', async () => { + const { findAllByRole, getByTestId } = await renderInTestApp( + , + ); + expect(getByTestId('mobile-sidebar-root').children.length).toBe(1); + const defaultSidebarGroup = await findAllByRole('button'); + expect(defaultSidebarGroup.length).toBe(1); + }); + + it('should render OverlayMenu displaying SidebarItems', async () => { + const { findByText, getByRole } = await renderInTestApp( + , + ); + const menuButton = await findByText('Menu'); + fireEvent.click(menuButton); + expect(getByRole('heading', { name: 'Menu' })).toBeVisible(); + expect(getByRole('link', { name: 'Home' })).toBeVisible(); + expect(getByRole('link', { name: 'Explore' })).toBeVisible(); + expect(getByRole('link', { name: 'Docs' })).toBeVisible(); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 93fc4d58c5..881a933efd 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -43,6 +43,7 @@ const useStyles = makeStyles(theme => ({ left: 0, right: 0, zIndex: 1000, + // SidebarDivider color borderTop: '1px solid #383838', }, @@ -165,7 +166,10 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { onClose={() => setSelectedMenuItemIndex(-1)} /> )} - + {sidebarGroups} diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx new file mode 100644 index 0000000000..664495067c --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -0,0 +1,66 @@ +/* + * 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 { mockBreakpoint, renderInTestApp } from '@backstage/test-utils'; +import HomeIcon from '@material-ui/icons/Home'; +import LayersIcon from '@material-ui/icons/Layers'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import { fireEvent } from '@testing-library/react'; +import React from 'react'; +import { SidebarItem } from './Items'; +import { MobileSidebarContext } from './MobileSidebar'; +import { SidebarGroup } from './SidebarGroup'; + +const SidebarGroupWithItems = () => ( + } label="Menu"> + + + + +); + +describe('', () => { + it('should render Items in BottomNavigationAciton on small screens', async () => { + mockBreakpoint({ matches: true }); + const { findByRole, container } = await renderInTestApp( + , + ); + expect(container.childNodes.length).toBe(1); + expect(await findByRole('button')).toBeVisible(); + }); + + it('should render Items without wrapper on bigger screens', async () => { + mockBreakpoint({ matches: false }); + const { container } = await renderInTestApp(); + expect(container.childNodes.length).toBe(3); + }); + + it('should trigger update of MobileSidebarContext', async () => { + mockBreakpoint({ matches: true }); + const value = { + selectedMenuItemIndex: -1, + setSelectedMenuItemIndex: jest.fn(), + }; + const { findByRole } = await renderInTestApp( + + + , + ); + const group = await findByRole('button'); + fireEvent.click(group); + expect(value.setSelectedMenuItemIndex).toHaveBeenCalled(); + }); +}); From 19c9fdc2584a9e24d9501fe55a99a91c740676cc Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 25 Aug 2021 10:07:49 +0200 Subject: [PATCH 13/45] Cleanup before review Signed-off-by: Philipp Hugenroth --- .../core-components/src/layout/Sidebar/Sidebar.stories.tsx | 7 +------ packages/core-components/src/layout/Sidebar/config.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 5aa279d846..5ef4bd8915 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -39,15 +39,10 @@ export default { ], }; -const handleSearch = (input: string) => { - // eslint-disable-next-line no-console - console.log(input); -}; - export const SampleSidebar = () => ( - + {}} to="/search" /> diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index e213385f73..a90fac9672 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -46,5 +46,5 @@ export type SidebarContextType = { }; export const SidebarContext = createContext({ - isOpen: true, + isOpen: false, }); From 5707087ba7d2f6985577cdbae870a7ad35a09315 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 25 Aug 2021 14:23:59 +0200 Subject: [PATCH 14/45] Improve Sidebar Storybook Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/Sidebar.stories.tsx | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 5ef4bd8915..d99b96aebf 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -14,11 +14,18 @@ * limitations under the License. */ +// We don't want to export RoutingProvider from core-app-api, but it's way easier to +// use here. This hack only works in storybook stories. +// TODO: Export a nicer to user routing provider, perhaps from test-utils +// eslint-disable-next-line monorepo/no-internal-import +import { RoutingProvider } from '@backstage/core-app-api/src/routing/RoutingProvider'; +import { createRouteRef } from '@backstage/core-plugin-api'; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; +import ExtensionIcon from '@material-ui/icons/Extension'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import MenuIcon from '@material-ui/icons/Menu'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; +import React, { ComponentType } from 'react'; +import { MemoryRouter, useLocation } from 'react-router-dom'; import { Sidebar, SidebarDivider, @@ -28,28 +35,50 @@ import { SidebarSearchField, SidebarSpace, } from '.'; +import { SidebarPage } from './Page'; + +const routeRef = createRouteRef({ + id: 'storybook.test-route', +}); + +const Location = () => { + const location = useLocation(); + return
Current location: {location.pathname}
; +}; export default { title: 'Layout/Sidebar', component: Sidebar, decorators: [ - (storyFn: () => JSX.Element) => ( - {storyFn()} + (Story: ComponentType<{}>) => ( + + + + + ), ], }; export const SampleSidebar = () => ( - - - {}} to="/search" /> - - - - - - - - - + + + + {}} /> + + + + + + + + + + + ); From c260b564351848d6b792460172a165b1fe468513 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 26 Aug 2021 16:06:35 +0200 Subject: [PATCH 15/45] Fix hidden text for mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/core-components/src/layout/Sidebar/Bar.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 5bcd8ec789..1770289c08 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -163,7 +163,13 @@ export const Sidebar = ({ ); return isMobileScreen ? ( - {children} + + {children} + ) : ( {children} ); From 5885ac0f7d0dd9fc47d0efb7fec76e093c0ca032 Mon Sep 17 00:00:00 2001 From: tudi2d Date: Mon, 6 Sep 2021 12:34:00 +0200 Subject: [PATCH 16/45] Fix SearchPage imports Signed-off-by: tudi2d --- packages/app/src/components/search/SearchPage.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index fefed84f58..bb97993676 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -25,8 +25,6 @@ import { SearchType, } from '@backstage/plugin-search'; import { DocsResultListItem } from '@backstage/plugin-techdocs'; -import { SearchResultSet } from '@backstage/search-common'; -import { BackstageTheme } from '@backstage/theme'; import { Grid, List, @@ -35,8 +33,8 @@ import { Theme, useMediaQuery, } from '@material-ui/core'; -import Pagination from '@material-ui/lab/Pagination'; -import React, { useState } from 'react'; +import React from 'react'; +import { BackstageTheme } from '../../../../theme/src'; const useStyles = makeStyles((theme: Theme) => ({ bar: { From 84865c1e1c474cb43650d0dc21329f190c569c92 Mon Sep 17 00:00:00 2001 From: tudi2d Date: Thu, 23 Sep 2021 23:45:41 +0200 Subject: [PATCH 17/45] Fix linting error Signed-off-by: tudi2d --- packages/app/src/components/search/SearchPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index bb97993676..2a21ad0e86 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -34,7 +34,7 @@ import { useMediaQuery, } from '@material-ui/core'; import React from 'react'; -import { BackstageTheme } from '../../../../theme/src'; +import { BackstageTheme } from '@backstage/theme'; const useStyles = makeStyles((theme: Theme) => ({ bar: { From e6fd475243037ab474630e41f8808a3894c926b1 Mon Sep 17 00:00:00 2001 From: tudi2d Date: Wed, 29 Sep 2021 13:49:36 +0200 Subject: [PATCH 18/45] Fix api-report... Signed-off-by: tudi2d --- packages/core-components/api-report.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 2ab91c9711..978b65d43e 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -7,6 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; +import { BackstageTheme } from '@backstage/theme'; import { BottomNavigationActionProps } from '@material-ui/core'; import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; import { ButtonProps } from '@material-ui/core'; @@ -780,10 +781,10 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Sidebar: ({ +export function Sidebar({ children, ...props -}: React_2.PropsWithChildren) => JSX.Element; +}: React_2.PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // From 80cfeaec664482aaa05c076e8832dde0f4f1f05b Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 9 Nov 2021 15:45:38 +0100 Subject: [PATCH 19/45] Update imports & api-reports Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 10 ++++------ .../src/layout/Sidebar/MobileSidebar.tsx | 12 +++++------- .../src/layout/Sidebar/SidebarGroup.tsx | 9 ++++----- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 978b65d43e..b39a0509f5 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -8,11 +8,9 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { BottomNavigationActionProps } from '@material-ui/core'; -import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; -import { ButtonProps } from '@material-ui/core'; -import { ButtonTypeMap } from '@material-ui/core'; -import { CardHeaderProps } from '@material-ui/core'; +import { BottomNavigationActionProps } from '@material-ui/core/BottomNavigationAction'; +import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; +import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; @@ -784,7 +782,7 @@ export type SelectInputBaseClassKey = 'root' | 'input'; export function Sidebar({ children, ...props -}: React_2.PropsWithChildren): JSX.Element; +}: React_2.PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" 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/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 881a933efd..25202c12b0 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -15,13 +15,11 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { - BottomNavigation, - Box, - IconButton, - makeStyles, - Typography, -} from '@material-ui/core'; +import BottomNavigation from '@material-ui/core/BottomNavigation'; +import Box from '@material-ui/core/Box'; +import IconButton from '@material-ui/core/IconButton'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import CloseIcon from '@material-ui/icons/Close'; import MenuIcon from '@material-ui/icons/Menu'; import React, { createContext, useEffect, useState } from 'react'; diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index c7dfe0fc21..2c971c7941 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -16,12 +16,11 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { - BottomNavigationAction, +import BottomNavigationAction, { BottomNavigationActionProps, - makeStyles, - useMediaQuery, -} from '@material-ui/core'; +} from '@material-ui/core/BottomNavigationAction'; +import { makeStyles } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; import { Link } from '../../components'; From 14856682398b4214864b30aec366c6a4c2a1b260 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 10 Nov 2021 16:04:32 +0100 Subject: [PATCH 20/45] Cleanup after rebase & adjust components to use class keys Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 9 +- .../core-components/src/layout/Page/Page.tsx | 4 +- .../src/layout/Sidebar/Items.tsx | 179 ++++++++++-------- .../src/layout/Sidebar/Page.tsx | 4 +- .../src/overridableComponents.ts | 8 - 5 files changed, 105 insertions(+), 99 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index b39a0509f5..16cdda0f81 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -779,10 +779,10 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Sidebar({ +export const Sidebar: ({ children, ...props -}: React_2.PropsWithChildren): JSX.Element; +}: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -790,11 +790,6 @@ export function Sidebar({ export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; -// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; - // Warning: (ae-missing-release-tag) "sidebarConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 9f49174977..1810ef8177 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -16,9 +16,11 @@ import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, ThemeProvider } from '@material-ui/core'; +import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; import { sidebarConfig } from '../Sidebar'; +export type PageClassKey = 'root'; + const useStyles = makeStyles(theme => ({ root: { display: 'grid', diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 19fd8c0c0a..121f164fe2 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -38,83 +38,36 @@ import { } from 'react-router-dom'; import { sidebarConfig, SidebarContext } from './config'; +export type SidebarItemClassKey = + | 'root' + | 'buttonItem' + | 'closed' + | 'open' + | 'label' + | 'iconContainer' + | 'searchRoot' + | 'searchField' + | 'searchFieldHTMLInput' + | 'searchContainer' + | 'secondaryAction' + | 'selected'; -const useStyles = makeStyles(theme => { - const { - selectedIndicatorWidth, - drawerWidthClosed, - drawerWidthOpen, - iconContainerWidth, - } = sidebarConfig; - return { - root: { - color: theme.palette.navigation.color, - display: 'flex', - flexFlow: 'row nowrap', - alignItems: 'center', - height: 48, - cursor: 'pointer', - }, - buttonItem: { - background: 'none', - border: 'none', - width: 'auto', - margin: 0, - padding: 0, - textAlign: 'inherit', - font: 'inherit', - }, - closed: { - width: drawerWidthClosed, - justifyContent: 'center', - }, - open: { - [theme.breakpoints.up('sm')]: { - width: drawerWidthOpen, - }, - }, - label: { - // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs - fontWeight: 'bold', - whiteSpace: 'nowrap', - lineHeight: 'auto', - flex: '3 1 auto', - width: '110px', - overflow: 'hidden', - 'text-overflow': 'ellipsis', - }, - iconContainer: { - boxSizing: 'border-box', - height: '100%', - width: iconContainerWidth, - marginRight: -theme.spacing(2), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - searchRoot: { - marginBottom: 12, - }, - searchField: { - color: '#b5b5b5', - fontWeight: 'bold', - fontSize: theme.typography.fontSize, - }, - searchFieldHTMLInput: { - padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, - }, - searchContainer: { - width: drawerWidthOpen - iconContainerWidth, - }, - secondaryAction: { - width: theme.spacing(6), - textAlign: 'center', - marginRight: theme.spacing(1), - }, - selected: { - '&$root': { - borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, - color: theme.palette.navigation.selectedColor, +const useStyles = makeStyles( + theme => { + const { + selectedIndicatorWidth, + drawerWidthClosed, + drawerWidthOpen, + iconContainerWidth, + } = sidebarConfig; + return { + root: { + color: theme.palette.navigation.color, + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + height: 48, + cursor: 'pointer', }, buttonItem: { background: 'none', @@ -130,7 +83,9 @@ const useStyles = makeStyles(theme => { justifyContent: 'center', }, open: { - width: drawerWidthOpen, + [theme.breakpoints.up('sm')]: { + width: drawerWidthOpen, + }, }, label: { // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs @@ -175,15 +130,75 @@ const useStyles = makeStyles(theme => { borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, color: theme.palette.navigation.selectedColor, }, - '&$closed': { - width: drawerWidthClosed - selectedIndicatorWidth, + buttonItem: { + background: 'none', + border: 'none', + width: 'auto', + margin: 0, + padding: 0, + textAlign: 'inherit', + font: 'inherit', }, - '& $iconContainer': { - marginLeft: -selectedIndicatorWidth, + closed: { + width: drawerWidthClosed, + justifyContent: 'center', + }, + open: { + width: drawerWidthOpen, + }, + label: { + // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs + fontWeight: 'bold', + whiteSpace: 'nowrap', + lineHeight: 'auto', + flex: '3 1 auto', + width: '110px', + overflow: 'hidden', + 'text-overflow': 'ellipsis', + }, + iconContainer: { + boxSizing: 'border-box', + height: '100%', + width: iconContainerWidth, + marginRight: -theme.spacing(2), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + searchRoot: { + marginBottom: 12, + }, + searchField: { + color: '#b5b5b5', + fontWeight: 'bold', + fontSize: theme.typography.fontSize, + }, + searchFieldHTMLInput: { + padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + }, + searchContainer: { + width: drawerWidthOpen - iconContainerWidth, + }, + secondaryAction: { + width: theme.spacing(6), + textAlign: 'center', + marginRight: theme.spacing(1), + }, + selected: { + '&$root': { + borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, + color: theme.palette.navigation.selectedColor, + }, + '&$closed': { + width: drawerWidthClosed - selectedIndicatorWidth, + }, + '& $iconContainer': { + marginLeft: -selectedIndicatorWidth, + }, }, }, }; - }}, + }, { name: 'BackstageSidebarItem' }, ); diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index cecc985a28..7c7a38d363 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import React, { createContext, PropsWithChildren, @@ -25,6 +25,8 @@ import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; +export type SidebarPageClassKey = 'root'; + const useStyles = makeStyles( theme => ({ root: { diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index af35445e24..3dc208eb70 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -80,11 +80,7 @@ import { CardActionsTopRightClassKey, ItemCardGridClassKey, ItemCardHeaderClassKey, - PageClassKey, - SidebarClassKey, SidebarIntroClassKey, - SidebarItemClassKey, - SidebarPageClassKey, CustomProviderClassKey, SignInPageClassKey, TabbedCardClassKey, @@ -153,11 +149,7 @@ type BackstageComponentsNameToClassKey = { BackstageInfoCardCardActionsTopRight: CardActionsTopRightClassKey; BackstageItemCardGrid: ItemCardGridClassKey; BackstageItemCardHeader: ItemCardHeaderClassKey; - BackstagePage: PageClassKey; - BackstageSidebar: SidebarClassKey; BackstageSidebarIntro: SidebarIntroClassKey; - BackstageSidebarItem: SidebarItemClassKey; - BackstageSidebarPage: SidebarPageClassKey; BackstageCustomProvider: CustomProviderClassKey; BackstageSignInPage: SignInPageClassKey; BackstageTabbedCard: TabbedCardClassKey; From 39f1c063258c9e1ba124711a2f99cbf94211b15c Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 14:35:39 +0100 Subject: [PATCH 21/45] Adjust mobile-sidebar to updated code base Remove to deep changes Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 16 +++-- .../src/layout/Header/Header.tsx | 2 +- .../core-components/src/layout/Page/Page.tsx | 12 +--- .../src/layout/Sidebar/MobileSidebar.tsx | 56 +++++++----------- .../src/layout/Sidebar/Sidebar.stories.tsx | 59 ++++++++----------- .../src/layout/Sidebar/SidebarGroup.tsx | 28 ++++----- .../src/layout/Sidebar/index.ts | 1 + 7 files changed, 75 insertions(+), 99 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 16cdda0f81..701a608ece 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -1090,18 +1090,24 @@ export const SidebarDivider: React_2.ComponentType< } >; -// Warning: (ae-forgotten-export) The symbol "SidebarGroupProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const SidebarGroup: ({ - to, - label, - icon, - value, children, + ...props }: React_2.PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "SidebarGroupProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SidebarGroupProps extends BottomNavigationActionProps { + // (undocumented) + priority?: number; + // (undocumented) + to?: string; +} + // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 40495ca0e0..92f28c1d07 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -64,7 +64,7 @@ const useStyles = makeStyles( }, title: { color: theme.palette.bursts.fontColor, - wordBreak: 'break-all', + // ? fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', marginBottom: 0, }, diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 1810ef8177..ba3c8bfff7 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -14,26 +14,20 @@ * limitations under the License. */ -import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; -import { sidebarConfig } from '../Sidebar'; +import React, { PropsWithChildren } from 'react'; export type PageClassKey = 'root'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles(() => ({ root: { display: 'grid', gridTemplateAreas: "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", gridTemplateRows: 'max-content auto 1fr', gridTemplateColumns: 'auto 1fr auto', - [theme.breakpoints.up('sm')]: { - height: '100vh', - }, - [theme.breakpoints.down('xs')]: { - height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, - }, + height: '100vh', overflowY: 'auto', }, })); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 25202c12b0..247c191dee 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import { useElementFilter } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import BottomNavigation from '@material-ui/core/BottomNavigation'; import Box from '@material-ui/core/Box'; @@ -22,6 +23,7 @@ import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import CloseIcon from '@material-ui/icons/Close'; import MenuIcon from '@material-ui/icons/Menu'; +import { orderBy } from 'lodash'; import React, { createContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router'; import { sidebarConfig } from './config'; @@ -68,27 +70,18 @@ const useStyles = makeStyles(theme => ({ }, })); -const sortSidebarGroupsForPriority = ( - childA: React.ReactElement, - childB: React.ReactElement, -) => { - const priorityADefined = childA.props.priority !== undefined; - const priorityBDefined = childB.props.priority !== undefined; - if (priorityADefined && !priorityBDefined) { - return -1; - } else if (priorityBDefined && !priorityADefined) { - return 1; - } else if (priorityADefined && priorityBDefined) { - return childA.props.priority - childB.props.priority; - } - return 0; -}; +const sortSidebarGroupsForPriority = (children: React.ReactElement[]) => + orderBy( + children, + ({ props: { priority } }) => (priority ? priority : -1), + 'desc', + ); const OverlayMenu = ({ children, - label, + label = 'Menu', onClose, -}: React.PropsWithChildren<{ label: string; onClose: () => void }>) => { +}: React.PropsWithChildren<{ label?: string; onClose: () => void }>) => { const classes = useStyles(); return ( @@ -117,22 +110,19 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { const location = useLocation(); const [selectedMenuItemIndex, setSelectedMenuItemIndex] = useState(-1); - let shouldSortSidebarGroups = false; useEffect(() => { setSelectedMenuItemIndex(-1); }, [location.pathname]); - // Filter children for SidebarGroups & set `shouldSortSidebarGroups` if priorities are set for one or more SidebarGroups - let sidebarGroups = React.Children.map(children, child => { - if (React.isValidElement(child) && child.type === SidebarGroup) { - if (child.props.priority !== undefined) { - shouldSortSidebarGroups = true; - } - return child; - } - return null; - }); + // Filter children for SidebarGroups + let sidebarGroups = useElementFilter(children, elements => + elements + .getElements() + .filter( + child => React.isValidElement(child) && child.type === SidebarGroup, + ), + ); if (!sidebarGroups) { // If Sidebar has no children the MobileSidebar won't be rendered @@ -141,13 +131,11 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { // If Sidebar has no SidebarGroup as a children a default // SidebarGroup with the complete Sidebar content will be created sidebarGroups.push( - }> - {children} - , + }>{children}, ); - } else if (shouldSortSidebarGroups) { - // If a SidebarGroup has a given priority the SidebarGroups are sorted for prioirty - sidebarGroups = sidebarGroups.sort(sortSidebarGroupsForPriority); + } else { + // Sort SidebarGroups for the given Priority + sidebarGroups = sortSidebarGroupsForPriority(sidebarGroups); } const shouldShowGroupChildren = diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index d99b96aebf..e487f9758b 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -13,19 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// We don't want to export RoutingProvider from core-app-api, but it's way easier to -// use here. This hack only works in storybook stories. -// TODO: Export a nicer to user routing provider, perhaps from test-utils -// eslint-disable-next-line monorepo/no-internal-import -import { RoutingProvider } from '@backstage/core-app-api/src/routing/RoutingProvider'; import { createRouteRef } from '@backstage/core-plugin-api'; +import { wrapInTestApp } from '@backstage/test-utils'; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import ExtensionIcon from '@material-ui/icons/Extension'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import MenuIcon from '@material-ui/icons/Menu'; import React, { ComponentType } from 'react'; -import { MemoryRouter, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { Sidebar, SidebarDivider, @@ -50,35 +45,27 @@ export default { title: 'Layout/Sidebar', component: Sidebar, decorators: [ - (Story: ComponentType<{}>) => ( - - - - - - ), + (Story: ComponentType<{}>) => + wrapInTestApp(, { mountedRoutes: { '/': routeRef } }), ], }; -export const SampleSidebar = () => ( - - - - {}} /> - - - - - - - - - - - -); +export const SampleSidebar = () => { + return ( + + + + {}} /> + + + + + + + + + + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 2c971c7941..d71657ac5a 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -27,7 +27,7 @@ import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; -interface SidebarGroupProps extends BottomNavigationActionProps { +export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; priority?: number; } @@ -50,20 +50,11 @@ const useStyles = makeStyles(theme => ({ }, })); -export const SidebarGroup = ({ - to, - label, - icon, - value, - children, -}: React.PropsWithChildren) => { +const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { const classes = useStyles(); const location = useLocation(); const { selectedMenuItemIndex, setSelectedMenuItemIndex } = useContext(MobileSidebarContext); - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); const onChange = (_: React.ChangeEvent<{}>, value: number) => { if (value === selectedMenuItemIndex) { @@ -79,7 +70,7 @@ export const SidebarGroup = ({ !(selectedMenuItemIndex >= 0) && to === location.pathname); - return isMobileScreen ? ( + return ( // Material UI issue: https://github.com/mui-org/material-ui/issues/27820 // @ts-ignore - ) : ( - <>{children} ); }; + +export const SidebarGroup = ({ + children, + ...props +}: React.PropsWithChildren) => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + return isMobileScreen ? : <>{children}; +}; diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 26a4c0e8a5..14d8d35b48 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -17,6 +17,7 @@ export { Sidebar } from './Bar'; export { MobileSidebar } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; +export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; export { From aa2e6117070ec5ba4539849709a5ddd308d1ab42 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 16:03:50 +0100 Subject: [PATCH 22/45] Update api report after merge Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index d3c41a3652..a03e8c1447 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -802,7 +802,7 @@ export type SelectInputBaseClassKey = 'root' | 'input'; export const Sidebar: ({ children, ...props -}: React_2.PropsWithChildren) => JSX.Element; +}: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // From e3ca78b5a601dc51f2b2cc086e284d34d1fba9c3 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 16:48:03 +0100 Subject: [PATCH 23/45] Run prettier on default-app Signed-off-by: Philipp Hugenroth --- .../packages/app/src/components/Root/Root.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 54ca803773..23670dcb63 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -23,10 +23,13 @@ import { SidebarItem, SidebarPage, SidebarScrollWrapper, - SidebarSpace + SidebarSpace, } from '@backstage/core-components'; import { SidebarSearchModal } from '@backstage/plugin-search'; -import { Settings as SidebarSettings, UserSettingsSignInAvatar } from '@backstage/plugin-user-settings'; +import { + Settings as SidebarSettings, + UserSettingsSignInAvatar, +} from '@backstage/plugin-user-settings'; import { Link, makeStyles } from '@material-ui/core'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import ExtensionIcon from '@material-ui/icons/Extension'; @@ -78,7 +81,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( } to="/search"> - + }> From 882adc942efb62dcd6298704c10027809c3e6843 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 18:33:08 +0100 Subject: [PATCH 24/45] Add padding to SidebarPage to avoid MobileSidebar overlapping content Signed-off-by: Philipp Hugenroth --- packages/core-components/src/layout/Page/Page.tsx | 2 +- packages/core-components/src/layout/Sidebar/Page.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index ba3c8bfff7..a0c84bb56a 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -27,7 +27,7 @@ const useStyles = makeStyles(() => ({ "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", gridTemplateRows: 'max-content auto 1fr', gridTemplateColumns: 'auto 1fr auto', - height: '100vh', + height: '100%', overflowY: 'auto', }, })); diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 7c7a38d363..20faec2f30 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -31,7 +31,6 @@ const useStyles = makeStyles( theme => ({ root: { width: '100%', - minHeight: '100%', transition: 'padding-left 0.1s ease-out', [theme.breakpoints.up('sm')]: { paddingLeft: ({ isPinned }) => @@ -39,6 +38,9 @@ const useStyles = makeStyles( ? sidebarConfig.drawerWidthOpen : sidebarConfig.drawerWidthClosed, }, + [theme.breakpoints.down('xs')]: { + paddingBottom: sidebarConfig.mobileSidebarHeight, + }, }, }), { name: 'BackstageSidebarPage' }, From 4ead01ef5ab6a05015dd329ae58172ebd8647159 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 19:02:09 +0100 Subject: [PATCH 25/45] Fix overlapping content for expandable sidebar Signed-off-by: Philipp Hugenroth --- packages/core-components/src/layout/Sidebar/Bar.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 1770289c08..056a5ae38e 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -23,16 +23,10 @@ import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; import { MobileSidebar } from './MobileSidebar'; -export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; +export type SidebarClassKey = 'drawer' | 'drawerOpen'; const useStyles = makeStyles( theme => ({ - root: { - zIndex: 1000, - position: 'relative', - overflow: 'visible', - width: theme.spacing(7) + 1, - }, drawer: { display: 'flex', flexFlow: 'column nowrap', @@ -41,7 +35,7 @@ const useStyles = makeStyles( left: 0, top: 0, bottom: 0, - padding: 0, + zIndex: 1000, background: theme.palette.navigation.background, overflowX: 'hidden', msOverflowStyle: 'none', From 7ba416be786ea708c01e8a8d302def41e3f79c01 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 12 Nov 2021 18:05:55 +0100 Subject: [PATCH 26/45] Split & improve changesets Fix & cleanup changes related to review Signed-off-by: Philipp Hugenroth --- .changeset/fluffy-countries-beam.md | 10 ++++++++ .changeset/fuzzy-olives-impress.md | 8 ------ .changeset/great-bulldogs-provide.md | 11 ++++++++ .changeset/stale-brooms-fry.md | 5 ++++ packages/core-components/api-report.md | 6 +++-- .../src/layout/Header/Header.tsx | 1 - .../core-components/src/layout/Page/Page.tsx | 25 +++++++++++-------- .../src/layout/Sidebar/Bar.tsx | 7 ++---- .../src/layout/Sidebar/MobileSidebar.tsx | 2 +- .../src/layout/Sidebar/SidebarGroup.tsx | 11 ++++++-- 10 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 .changeset/fluffy-countries-beam.md delete mode 100644 .changeset/fuzzy-olives-impress.md create mode 100644 .changeset/great-bulldogs-provide.md create mode 100644 .changeset/stale-brooms-fry.md diff --git a/.changeset/fluffy-countries-beam.md b/.changeset/fluffy-countries-beam.md new file mode 100644 index 0000000000..145428267b --- /dev/null +++ b/.changeset/fluffy-countries-beam.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-catalog': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-user-settings': patch +--- + +**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore the "Pin Sidebar" setting is removed on mobile screens as the mobile sidebar is always pinned to the bottom. + +**Other plugins:** Smaller style adjustments across plugins to improve the UI on mobile devices. diff --git a/.changeset/fuzzy-olives-impress.md b/.changeset/fuzzy-olives-impress.md deleted file mode 100644 index 9f9c8f755b..0000000000 --- a/.changeset/fuzzy-olives-impress.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-user-settings': patch ---- - -Add `MobileSidebar` component & use it for smaller screens to improve the UX on mobile devices by switching to a navigation at the bottom of the screen. For customizing the experience you can group `SidebarItems` in a `SidebarGroup` or create a `SidebarGroup` with a link. For an example take a look at the `Root.tsx` or the updated ["Adding a plugin page to the Sidebar"](https://backstage.io/docs/getting-started/configure-app-with-plugins) documentation. diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md new file mode 100644 index 0000000000..636f9f3380 --- /dev/null +++ b/.changeset/great-bulldogs-provide.md @@ -0,0 +1,11 @@ +--- +'@backstage/core-components': patch +--- + +The `Bar` component will now render a `MobileSidebar` instead of the current sidebar on smaller screens. The state of the `MobileSidebar` will be treated as always open. + +--- + +**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally a `MobileSidebarContext`, which wraps the component, will safe the selected menu item. + +**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens this won't have any effect at the moment. On smaller screens it will render a given icon into the `MobileSidebar`. If a route is provided clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md new file mode 100644 index 0000000000..a3804f13eb --- /dev/null +++ b/.changeset/stale-brooms-fry.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +// TODO diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index a03e8c1447..2fa3315fe1 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -801,7 +801,6 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // @public (undocumented) export const Sidebar: ({ children, - ...props }: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1115,7 +1114,10 @@ export const SidebarDivider: React_2.ComponentType< // @public (undocumented) export const SidebarGroup: ({ children, - ...props + to, + label, + icon, + value, }: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SidebarGroupProps" 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/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 92f28c1d07..954575a95a 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -64,7 +64,6 @@ const useStyles = makeStyles( }, title: { color: theme.palette.bursts.fontColor, - // ? fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', marginBottom: 0, }, diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index a0c84bb56a..a1efe4dda9 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -20,17 +20,20 @@ import React, { PropsWithChildren } from 'react'; export type PageClassKey = 'root'; -const useStyles = makeStyles(() => ({ - root: { - display: 'grid', - gridTemplateAreas: - "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", - gridTemplateRows: 'max-content auto 1fr', - gridTemplateColumns: 'auto 1fr auto', - height: '100%', - overflowY: 'auto', - }, -})); +const useStyles = makeStyles( + () => ({ + root: { + display: 'grid', + gridTemplateAreas: + "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", + gridTemplateRows: 'max-content auto 1fr', + gridTemplateColumns: 'auto 1fr auto', + height: '100%', + overflowY: 'auto', + }, + }), + { name: 'BackstagePage' }, +); type Props = { themeId: string; diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 056a5ae38e..db85ca77d7 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -148,10 +148,7 @@ const DesktopSidebar = ({ ); }; -export const Sidebar = ({ - children, - ...props -}: React.PropsWithChildren) => { +export const Sidebar = ({ children }: React.PropsWithChildren) => { const isMobileScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); @@ -162,7 +159,7 @@ export const Sidebar = ({ isOpen: true, }} > - {children} + {children} ) : ( {children} diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 247c191dee..33505987bc 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -124,7 +124,7 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { ), ); - if (!sidebarGroups) { + if (!children) { // If Sidebar has no children the MobileSidebar won't be rendered return null; } else if (!sidebarGroups.length) { diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index d71657ac5a..51ae35c2db 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -88,11 +88,18 @@ const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { export const SidebarGroup = ({ children, - ...props + to, + label, + icon, + value, }: React.PropsWithChildren) => { const isMobileScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); - return isMobileScreen ? : <>{children}; + return isMobileScreen ? ( + + ) : ( + <>{children} + ); }; From 4b8cefcf05d5bf125cf6607eb016502df7d6757a Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 15 Nov 2021 11:55:17 +0100 Subject: [PATCH 27/45] Update changeset & documentation Signed-off-by: Philipp Hugenroth --- .changeset/stale-brooms-fry.md | 48 ++++++++++++++++++- app-config.yaml | 4 +- .../configure-app-with-plugins.md | 19 ++++++-- packages/app/src/components/Root/Root.tsx | 2 +- .../src/layout/Sidebar/MobileSidebar.tsx | 2 +- .../packages/app/src/components/Root/Root.tsx | 2 +- 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md index a3804f13eb..ddd7ba09a6 100644 --- a/.changeset/stale-brooms-fry.md +++ b/.changeset/stale-brooms-fry.md @@ -2,4 +2,50 @@ '@backstage/create-app': patch --- -// TODO +You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaler screens will look like. A `SidebarGroup` wil be displayed with the given icon in the `MobileSidebar`. + +A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full screen overlay menu (e.g. `Menu`). + +```diff + + ++ } to="/search"> + ++ + ++ }> + + + + + + ++ + + ++ } ++ to="/settings" ++ > + ++ + +``` + +Additionally you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in a descending order from left to right. + +```diff +} + to="/settings" ++ priority={1} +> + + +``` + +If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full screen overlay menu displaying all of the content of the current `Sidebar`. + +More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`. diff --git a/app-config.yaml b/app-config.yaml index 8aa3bc569c..2915a67265 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -23,9 +23,9 @@ app: title: '#backstage' backend: - baseUrl: http://localhost:7000 + baseUrl: http://localhost:7001 listen: - port: 7000 + port: 7001 database: client: sqlite3 connection: ':memory:' diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 11e2f4d75a..7f522437da 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -92,12 +92,12 @@ import InternalToolIcon from './internal-tool.icon.svg'; ``` On mobile devices the `Sidebar` is displayed at the bottom of the screen. For -customizing the experience you can group `SidebarItems` in a `SidebarGroup` or -create a `SidebarGroup` with a link. All `SidebarGroups` are displayed in the -bottom navigation with an icon. +customizing the experience you can group `SidebarItems` in a `SidebarGroup` +(Example 1) or create a `SidebarGroup` with a link (Example 2). All +`SidebarGroups` are displayed in the bottom navigation with an icon. ```ts -// ... inside the AppSidebar component +// Example 1 } label="Menu"> ... @@ -105,5 +105,14 @@ bottom navigation with an icon. ``` -If no `SidebarGroup`is provided a default menu will display the `Sidebar` +```ts +// Example 2 +} to="/search"> + ... + + ... + +``` + +If no `SidebarGroup` is provided a default menu will display the `Sidebar` content. diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 5412412126..87fa8c8470 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -89,7 +89,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 33505987bc..d7817f3037 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -73,7 +73,7 @@ const useStyles = makeStyles(theme => ({ const sortSidebarGroupsForPriority = (children: React.ReactElement[]) => orderBy( children, - ({ props: { priority } }) => (priority ? priority : -1), + ({ props: { priority } }) => (Number.isInteger(priority) ? priority : -1), 'desc', ); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 23670dcb63..c540ce7f59 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -84,7 +84,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} From 066805f9995a5a85a131ee12b7302b3f48c48ad2 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 15 Nov 2021 13:12:39 +0100 Subject: [PATCH 28/45] Spellchecking for changesets Signed-off-by: Philipp Hugenroth --- .changeset/fluffy-countries-beam.md | 2 +- .changeset/great-bulldogs-provide.md | 4 ++-- .changeset/stale-brooms-fry.md | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.changeset/fluffy-countries-beam.md b/.changeset/fluffy-countries-beam.md index 145428267b..f542cfd33b 100644 --- a/.changeset/fluffy-countries-beam.md +++ b/.changeset/fluffy-countries-beam.md @@ -5,6 +5,6 @@ '@backstage/plugin-user-settings': patch --- -**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore the "Pin Sidebar" setting is removed on mobile screens as the mobile sidebar is always pinned to the bottom. +**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore, the "Pin Sidebar" setting is removed on mobile screens, as the mobile sidebar is always pinned to the bottom. **Other plugins:** Smaller style adjustments across plugins to improve the UI on mobile devices. diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md index 636f9f3380..f076dfa545 100644 --- a/.changeset/great-bulldogs-provide.md +++ b/.changeset/great-bulldogs-provide.md @@ -6,6 +6,6 @@ The `Bar` component will now render a `MobileSidebar` instead of the current sid --- -**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally a `MobileSidebarContext`, which wraps the component, will safe the selected menu item. +**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item. -**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens this won't have any effect at the moment. On smaller screens it will render a given icon into the `MobileSidebar`. If a route is provided clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. +**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens, this won't have any effect at the moment. On smaller screens, it will render a given icon into the `MobileSidebar`. If a route is provided, clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided, it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md index ddd7ba09a6..c98a427e80 100644 --- a/.changeset/stale-brooms-fry.md +++ b/.changeset/stale-brooms-fry.md @@ -2,9 +2,9 @@ '@backstage/create-app': patch --- -You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaler screens will look like. A `SidebarGroup` wil be displayed with the given icon in the `MobileSidebar`. +You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`. -A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full screen overlay menu (e.g. `Menu`). +A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full-screen overlay menu (e.g. `Menu`). ```diff @@ -33,7 +33,7 @@ A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settin ``` -Additionally you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in a descending order from left to right. +Additionally, you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in descending order from left to right. ```diff ``` -If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full screen overlay menu displaying all of the content of the current `Sidebar`. +If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`. More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`. From 9a0e4ffe88c1b42a5aae37d0e44d39248e19293c Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 15 Nov 2021 13:28:35 +0100 Subject: [PATCH 29/45] Update app-config.yaml Signed-off-by: Philipp Hugenroth --- app-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 2915a67265..8aa3bc569c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -23,9 +23,9 @@ app: title: '#backstage' backend: - baseUrl: http://localhost:7001 + baseUrl: http://localhost:7000 listen: - port: 7001 + port: 7000 database: client: sqlite3 connection: ':memory:' From 7d9913fc43b24eb5afb9c0383fb1b80ad9af26d9 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 7 Dec 2021 16:49:24 +0100 Subject: [PATCH 30/45] Add basic support for submenus to mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 13 +++++--- .../src/layout/Sidebar/Bar.tsx | 20 +++-------- .../src/layout/Sidebar/Items.tsx | 31 +++-------------- .../src/layout/Sidebar/MobileSidebar.tsx | 33 ++++++++++--------- .../src/layout/Sidebar/SidebarSubmenu.tsx | 8 +++++ .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 4 +++ .../src/layout/Sidebar/config.ts | 3 +- 7 files changed, 50 insertions(+), 62 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 63cd694d28..a5f2010758 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -809,6 +809,11 @@ export const Sidebar: ({ export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; +// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SidebarClassKey = 'drawer' | 'drawerOpen'; + // Warning: (ae-missing-release-tag) "sidebarConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -838,7 +843,7 @@ export const SidebarContext: Context; // @public (undocumented) export type SidebarContextType = { isOpen: boolean; - setOpen: (open: boolean) => void; + setOpen?: (open: boolean) => void; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1110,6 +1115,9 @@ export const SidebarDivider: React_2.ComponentType< } >; +// @public +export const SidebarExpandButton: () => JSX.Element | null; + // Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1131,9 +1139,6 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; } -// @public -export const SidebarExpandButton: () => JSX.Element | null; - // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index f1d1b73be4..647495a6f1 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -178,18 +178,8 @@ export const Sidebar = ({ children }: React.PropsWithChildren) => { theme.breakpoints.down('xs'), ); - // TODO: Generalize - const setOpen = () => {}; - return isMobileScreen ? ( - - {children} - + {children} ) : ( {children} ); @@ -209,14 +199,14 @@ export const SidebarExpandButton = () => { theme.breakpoints.down('md'), ); + if (isPinned || isSmallScreen || !setOpen) { + return null; + } + const handleClick = () => { setOpen(!isOpen); }; - if (isPinned || isSmallScreen) { - return null; - } - return (
); - const closedContent = itemIcon; return ( -
+
- {isOpen ? openContent : closedContent} + {isOpen ? openContent : itemIcon} {!isHoveredOn && ( )} @@ -488,28 +483,12 @@ export const SidebarItem = forwardRef((props, ref) => { ), }; - let hasSubmenu = false; - let submenu: ReactNode; - const componentType = ( - - <> - - ).type; // Filter children for SidebarSubmenu components - const submenus = useElementFilter(children, elements => - elements.getElements().filter(child => child.type === componentType), + const [submenu] = useElementFilter(children, elements => + elements.getElements().filter(child => child.type === SidebarSubmenu), ); - // Error thrown if more than one SidebarSubmenu in a SidebarItem - if (submenus.length > 1) { - throw new Error( - 'Cannot render more than one SidebarSubmenu inside a SidebarItem', - ); - } else if (submenus.length === 1) { - hasSubmenu = true; - submenu = submenus[0]; - } - if (hasSubmenu) { + if (submenu) { return ( ) => { !sidebarGroups[selectedMenuItemIndex].props.to; return ( - - {shouldShowGroupChildren && ( - setSelectedMenuItemIndex(-1)} - /> - )} - + - {sidebarGroups} - - + {shouldShowGroupChildren && ( + setSelectedMenuItemIndex(-1)} + /> + )} + + {sidebarGroups} + + + ); }; diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx index 51324980fe..7c4a554e4d 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx @@ -58,12 +58,20 @@ const useStyles = (props: { left: number }) => }, drawerOpen: { width: submenuConfig.drawerWidthOpen, + [theme.breakpoints.down('sm')]: { + width: '100%', + position: 'relative', + paddingLeft: theme.spacing(3), + left: 0, + top: 0, + }, }, title: { fontSize: 24, fontWeight: 500, color: '#FFF', padding: 20, + display: 'none', }, })); diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 2bb8a13592..3d676ff83a 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -76,6 +76,10 @@ const useStyles = makeStyles(theme => ({ color: theme.palette.navigation.color, display: 'flex', justifyContent: 'center', + [theme.breakpoints.down('xs')]: { + display: 'block', + paddingLeft: theme.spacing(4), + }, fontSize: '14px', }, })); diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index 9aa2a25d05..a26a3dbc0d 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -60,12 +60,11 @@ export const SIDEBAR_INTRO_LOCAL_STORAGE = export type SidebarContextType = { isOpen: boolean; - setOpen: (open: boolean) => void; + setOpen?: (open: boolean) => void; }; export const SidebarContext = createContext({ isOpen: false, - setOpen: _open => {}, }); export type SidebarItemWithSubmenuContextType = { From 510a5953979db37e13461d145d34913b33dcfa20 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 9 Dec 2021 10:53:17 +0100 Subject: [PATCH 31/45] Improve mobile submenu experience Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/Items.tsx | 34 ++++++++++++++++--- .../src/layout/Sidebar/SidebarSubmenu.tsx | 12 +++++-- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 9 +++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 8c0e175c6c..d7bea9876d 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -17,6 +17,7 @@ import { IconComponent, useElementFilter } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, styled, Theme } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import Badge from '@material-ui/core/Badge'; import TextField from '@material-ui/core/TextField'; import Typography from '@material-ui/core/Typography'; @@ -46,6 +47,8 @@ import { SidebarItemWithSubmenuContext, } from './config'; import { SidebarSubmenu } from './SidebarSubmenu'; +import ArrowDropUp from '@material-ui/icons/ArrowDropUp'; +import ArrowDropDown from '@material-ui/icons/ArrowDropDown'; export type SidebarItemClassKey = | 'root' @@ -151,6 +154,9 @@ const useStyles = makeStyles( submenuArrow: { position: 'absolute', right: 0, + [theme.breakpoints.down('xs')]: { + right: theme.spacing(2), + }, }, selected: { '&$root': { @@ -274,6 +280,9 @@ const SidebarItemWithSubmenu = ({ const [isHoveredOn, setIsHoveredOn] = useState(false); const { pathname: locationPathname } = useLocation(); const isActive = isSidebarItemWithSubmenuActive(children, locationPathname); + const isSmallScreen = useMediaQuery((theme: BackstageTheme) => + theme.breakpoints.down('sm'), + ); const handleMouseEnter = () => { setIsHoveredOn(true); @@ -308,6 +317,21 @@ const SidebarItemWithSubmenu = ({ ); + const arrowIcon = () => { + if (isSmallScreen) { + return isHoveredOn ? ( + + ) : ( + + ); + } + return ( + !isHoveredOn && ( + + ) + ); + }; + return ( -
+
{isOpen ? openContent : itemIcon} - {!isHoveredOn && ( - - )} + {arrowIcon()}
{isHoveredOn && children}
diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx index 7c4a554e4d..dfa895df71 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx @@ -38,7 +38,13 @@ const useStyles = (props: { left: number }) => flexFlow: 'column nowrap', alignItems: 'flex-start', position: 'fixed', - left: props.left, + [theme.breakpoints.up('sm')]: { + marginLeft: props.left, + transition: theme.transitions.create('margin-left', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.shortest, + }), + }, top: 0, bottom: 0, padding: 0, @@ -58,7 +64,7 @@ const useStyles = (props: { left: number }) => }, drawerOpen: { width: submenuConfig.drawerWidthOpen, - [theme.breakpoints.down('sm')]: { + [theme.breakpoints.down('xs')]: { width: '100%', position: 'relative', paddingLeft: theme.spacing(3), @@ -99,7 +105,7 @@ export const SidebarSubmenu = ({ const left = isOpen ? sidebarConfig.drawerWidthOpen : sidebarConfig.drawerWidthClosed; - const props = { left: left }; + const props = { left }; const classes = useStyles(props)(); const { isHoveredOn } = useContext(SidebarItemWithSubmenuContext); diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 3d676ff83a..60dc591d92 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -29,6 +29,7 @@ import { BackstageTheme } from '@backstage/theme'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; +import { SidebarContext } from '../..'; const useStyles = makeStyles(theme => ({ item: { @@ -123,8 +124,13 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { pathname: locationPathname } = useLocation(); const { pathname: toPathname } = useResolvedPath(to); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); + const { setOpen } = useContext(SidebarContext); + const closeSubmenu = () => { setIsHoveredOn(false); + if (setOpen) { + setOpen(false); + } }; let isActive = locationPathname === toPathname; @@ -142,6 +148,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
- ); -}; diff --git a/packages/core-components/src/layout/Sidebar/Items.test.tsx b/packages/core-components/src/layout/Sidebar/Items.test.tsx index f4d699d09c..b2cabd03b8 100644 --- a/packages/core-components/src/layout/Sidebar/Items.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.test.tsx @@ -20,8 +20,8 @@ import { createEvent, fireEvent, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import HomeIcon from '@material-ui/icons/Home'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import { Sidebar, SidebarExpandButton } from './Bar'; -import { SidebarItem, SidebarSearchField } from './Items'; +import { Sidebar } from './Bar'; +import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items'; import { renderHook } from '@testing-library/react-hooks'; import { hexToRgb, makeStyles } from '@material-ui/core/styles'; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 0aed75460e..0b1d15d4b8 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -50,8 +50,8 @@ import { SidebarSubmenuProps, SidebarSubmenu, } from '.'; -import { isLocationMatch } from './utils'; -import { Location } from 'history'; +import DoubleArrowLeft from './icons/DoubleArrowLeft'; +import DoubleArrowRight from './icons/DoubleArrowRight'; export type SidebarItemClassKey = | 'root' @@ -157,6 +157,19 @@ const useStyles = makeStyles( submenuArrow: { display: 'flex', }, + expandButton: { + background: 'none', + border: 'none', + color: theme.palette.navigation.color, + width: '100%', + cursor: 'pointer', + position: 'relative', + height: 48, + }, + arrows: { + position: 'absolute', + right: 10, + }, selected: { '&$root': { borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, @@ -706,3 +719,39 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => { '&:hover': scrollbarStyles, }; }); + +/** + * A button which allows you to expand the sidebar when clicked. + * Use optionally to replace sidebar's expand-on-hover feature with expand-on-click. + * + * @public + */ +export const SidebarExpandButton = () => { + const classes = useStyles(); + const { isOpen, setOpen } = useContext(SidebarContext); + const isSmallScreen = useMediaQuery( + theme => theme.breakpoints.down('md'), + { noSsr: true }, + ); + + if (isSmallScreen || !setOpen) { + return null; + } + + const handleClick = () => { + setOpen(!isOpen); + }; + + return ( + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index e94d245266..47aba5e18e 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -29,7 +29,6 @@ import { BackstageTheme } from '@backstage/theme'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; -import { SidebarContext } from '../..'; import { isLocationMatch } from './utils'; const useStyles = makeStyles(theme => ({ @@ -123,13 +122,9 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { title, to, icon: Icon, dropdownItems } = props; const classes = useStyles(); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); - const { setOpen } = useContext(SidebarContext); const closeSubmenu = () => { setIsHoveredOn(false); - if (setOpen) { - setOpen(false); - } }; const toLocation = useResolvedPath(to); const currentLocation = useLocation(); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index b0b8ca70a5..2b5ff77538 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export { Sidebar, SidebarExpandButton } from './Bar'; +export { Sidebar } from './Bar'; export { MobileSidebar } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; @@ -35,6 +35,7 @@ export { SidebarSpace, SidebarSpacer, SidebarScrollWrapper, + SidebarExpandButton, } from './Items'; export type { SidebarItemClassKey, From 545a26a0b675e48647c3218f3638f6dbd33a9c41 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 28 Dec 2021 18:06:23 +0100 Subject: [PATCH 37/45] Move 'isMobile' in Context Optimize generated docs by labeling components with scope Signed-off-by: Philipp Hugenroth --- .../app/src/components/search/SearchPage.tsx | 30 +++--- packages/core-components/api-report.md | 66 ++++++------- .../src/layout/Sidebar/Bar.test.tsx | 12 ++- .../src/layout/Sidebar/Bar.tsx | 28 +++--- .../src/layout/Sidebar/Items.tsx | 93 +++---------------- .../src/layout/Sidebar/MobileSidebar.tsx | 49 +++++++--- .../src/layout/Sidebar/Page.tsx | 27 ++++-- .../src/layout/Sidebar/Sidebar.stories.tsx | 1 - .../src/layout/Sidebar/SidebarGroup.tsx | 26 +++--- .../src/layout/Sidebar/index.ts | 12 ++- .../General/UserSettingsAppearanceCard.tsx | 13 +-- .../General/UserSettingsPinToggle.test.tsx | 12 ++- .../General/UserSettingsPinToggle.tsx | 6 +- .../src/components/SettingsPage.tsx | 17 ++-- 14 files changed, 166 insertions(+), 226 deletions(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 2a21ad0e86..a734b72142 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -14,7 +14,13 @@ * limitations under the License. */ -import { Content, Header, Lifecycle, Page } from '@backstage/core-components'; +import { + Content, + Header, + Lifecycle, + Page, + SidebarStateContext, +} from '@backstage/core-components'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; import { DefaultResultListItem, @@ -25,16 +31,8 @@ import { SearchType, } from '@backstage/plugin-search'; import { DocsResultListItem } from '@backstage/plugin-techdocs'; -import { - Grid, - List, - makeStyles, - Paper, - Theme, - useMediaQuery, -} from '@material-ui/core'; -import React from 'react'; -import { BackstageTheme } from '@backstage/theme'; +import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core'; +import React, { useContext } from 'react'; const useStyles = makeStyles((theme: Theme) => ({ bar: { @@ -52,15 +50,11 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); + const { isMobile } = useContext(SidebarStateContext); return ( - {!isMobileScreen && ( -
} /> - )} + {!isMobile &&
} />} @@ -68,7 +62,7 @@ const SearchPage = () => { - {!isMobileScreen && ( + {!isMobile && ( ) => JSX.Element | null; +export const MobileSidebar: ( + props: React_2.PropsWithChildren<{}>, +) => JSX.Element | null; // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -843,16 +841,10 @@ export type SelectClassKey = // @public (undocumented) export type SelectInputBaseClassKey = 'root' | 'input'; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) -export const Sidebar: ({ - children, - openDelayMs, - closeDelayMs, - disableExpandOnHover, -}: React_2.PropsWithChildren) => JSX.Element; +export const Sidebar: ( + props: React_2.PropsWithChildren, +) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -860,8 +852,6 @@ export const Sidebar: ({ export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; -// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type SidebarClassKey = 'drawer' | 'drawerOpen'; @@ -1174,19 +1164,11 @@ export type SidebarDividerClassKey = 'root'; // @public export const SidebarExpandButton: () => JSX.Element | null; -// Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) -export const SidebarGroup: ({ - children, - to, - label, - icon, - value, -}: React_2.PropsWithChildren) => JSX.Element; +export const SidebarGroup: ( + props: React_2.PropsWithChildren, +) => JSX.Element; -// Warning: (ae-missing-release-tag) "SidebarGroupProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface SidebarGroupProps extends BottomNavigationActionProps { // (undocumented) @@ -1243,17 +1225,11 @@ export function SidebarPage(props: PropsWithChildren<{}>): JSX.Element; // @public (undocumented) export type SidebarPageClassKey = 'root'; -// 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) -export const SidebarPinStateContext: React_2.Context; - -// Warning: (ae-missing-release-tag) "SidebarPinStateContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type SidebarPinStateContextType = { - isPinned: boolean; - toggleSidebarPinState: () => any; +export type SidebarProps = { + openDelayMs?: number; + closeDelayMs?: number; + disableExpandOnHover?: boolean; }; // Warning: (ae-missing-release-tag) "SidebarScrollWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2079,6 +2055,16 @@ export const SidebarSpacer: React_2.ComponentType< // @public (undocumented) export type SidebarSpacerClassKey = 'root'; +// @public (undocumented) +export const SidebarStateContext: React_2.Context; + +// @public (undocumented) +export type SidebarStateContextType = { + isPinned: boolean; + toggleSidebarPinState: () => any; + isMobile: boolean; +}; + // @public export const SidebarSubmenu: (props: SidebarSubmenuProps) => JSX.Element; @@ -2111,7 +2097,7 @@ export type SidebarSubmenuProps = { // Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function SignInPage(props: Props_18): JSX.Element; +export function SignInPage(props: Props_17): JSX.Element; // Warning: (ae-missing-release-tag) "SignInPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2282,7 +2268,7 @@ export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root'; // Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function TabbedCard(props: PropsWithChildren): JSX.Element; +export function TabbedCard(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "TabbedCardClassKey" 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.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index 11b4088dc0..acfd949ac5 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -26,12 +26,16 @@ import { Sidebar } from './Bar'; import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items'; import { SidebarSubmenuItem } from './SidebarSubmenuItem'; import { SidebarSubmenu } from './SidebarSubmenu'; -import { SidebarPinStateContext } from '.'; +import { SidebarStateContext } from '.'; async function renderScalableSidebar() { await renderInTestApp( - {} }} + {}, + }} > {}} to="/search" /> @@ -58,7 +62,7 @@ async function renderScalableSidebar() { - , + , ); } diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 96f7335acd..51d4268d05 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -20,9 +20,10 @@ import classnames from 'classnames'; import React, { useState, useContext, PropsWithChildren, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarPinStateContext } from './Page'; +import { SidebarStateContext } from './Page'; import { MobileSidebar } from './MobileSidebar'; +/** @public */ export type SidebarClassKey = 'drawer' | 'drawerOpen'; const useStyles = makeStyles( @@ -69,7 +70,8 @@ enum State { Open, } -type Props = { +/** @public */ +export type SidebarProps = { openDelayMs?: number; closeDelayMs?: number; disableExpandOnHover?: boolean; @@ -80,7 +82,7 @@ const DesktopSidebar = ({ closeDelayMs = sidebarConfig.defaultCloseDelayMs, disableExpandOnHover, children, -}: PropsWithChildren) => { +}: PropsWithChildren) => { const classes = useStyles(); const isSmallScreen = useMediaQuery( theme => theme.breakpoints.down('md'), @@ -88,9 +90,7 @@ const DesktopSidebar = ({ ); const [state, setState] = useState(State.Closed); const hoverTimerRef = useRef(); - const { isPinned, toggleSidebarPinState } = useContext( - SidebarPinStateContext, - ); + const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); const handleOpen = () => { if (isPinned || disableExpandOnHover) { @@ -163,18 +163,12 @@ const DesktopSidebar = ({ ); }; -export const Sidebar = ({ - children, - openDelayMs, - closeDelayMs, - disableExpandOnHover, -}: React.PropsWithChildren) => { - const isMobileScreen = useMediaQuery( - theme => theme.breakpoints.down('xs'), - { noSsr: true }, - ); +/** @public */ +export const Sidebar = (props: React.PropsWithChildren) => { + const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; + const { isMobile } = useContext(SidebarStateContext); - return isMobileScreen ? ( + return isMobile ? ( {children} ) : ( ( fontSize: theme.typography.fontSize, }, searchFieldHTMLInput: { - padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + padding: theme.spacing(2, 0, 2), }, searchContainer: { width: drawerWidthOpen - iconContainerWidth, @@ -219,7 +222,7 @@ const useStyles = makeStyles( fontSize: theme.typography.fontSize, }, searchFieldHTMLInput: { - padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + padding: theme.spacing(2, 0, 2), }, searchContainer: { width: drawerWidthOpen - iconContainerWidth, @@ -237,7 +240,7 @@ const useStyles = makeStyles( const useLocationMatch = ( submenu: React.ReactElement, - locationPathname: string, + location: Location, ): boolean => // Evaluates the routes of the SubmenuItems & nested DropdownItems. // The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization @@ -257,93 +260,21 @@ const useLocationMatch = ( if (dropdownItems?.length) { dropdownItems.forEach( ({ to: _to }) => - (active = active || locationPathname.includes(_to)), + (active = + active || isLocationMatch(location, resolvePath(_to))), ); return; } if (to) { - active = locationPathname.includes(to); + active = isLocationMatch(location, resolvePath(to)); } } }, ); return active; }, - [locationPathname], + [location.pathname], ); -/* -function isSidebarItemWithSubmenuActive( - submenu: ReactNode, - currentLocation: Location, -) { - // Item is active if any of submenu items have active paths - const toPathnames: string[] = []; - let isActive = false; - let submenuItems: ReactNode; - Children.forEach(submenu, element => { - if (!React.isValidElement(element)) return; - submenuItems = element.props.children; - }); - Children.forEach(submenuItems, element => { - if (!React.isValidElement(element)) return; - if (element.props.dropdownItems) { - element.props.dropdownItems.map((item: { to: string }) => - toPathnames.push(item.to), - ); - } else if (element.props.to) { - toPathnames.push(element.props.to); - } - }); - isActive = toPathnames.some(to => { - const toLocation = resolvePath(to); - return isLocationMatch(currentLocation, toLocation); - }); - return isActive; -} - -const SidebarItemWithSubmenu = ({ - text, - hasNotifications = false, - icon: Icon, - children, -}: PropsWithChildren) => { - const classes = useStyles(); - const [isHoveredOn, setIsHoveredOn] = useState(false); - const currentLocation = useLocation(); - const isActive = isSidebarItemWithSubmenuActive(children, currentLocation); - - const handleMouseEnter = () => { - setIsHoveredOn(true); - }; - const handleMouseLeave = () => { - setIsHoveredOn(false); - }; - - const { isOpen } = useContext(SidebarContext); - const itemIcon = ( - - - - ); - const openContent = ( - <> -
- {itemIcon} -
- {text && ( - - {text} - - )} -
{}
- - );*/ type SidebarItemBaseProps = { icon: IconComponent; @@ -522,8 +453,8 @@ const SidebarItemWithSubmenu = ({ }) => { const classes = useStyles(); const [isHoveredOn, setIsHoveredOn] = useState(false); - const { pathname: locationPathname } = useLocation(); - const isActive = useLocationMatch(children, locationPathname); + const location = useLocation(); + const isActive = useLocationMatch(children, location); const isSmallScreen = useMediaQuery((theme: BackstageTheme) => theme.breakpoints.down('sm'), ); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 6ee2451755..158b20e6be 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -20,6 +20,7 @@ import BottomNavigation from '@material-ui/core/BottomNavigation'; import Box from '@material-ui/core/Box'; import IconButton from '@material-ui/core/IconButton'; import { makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; import Typography from '@material-ui/core/Typography'; import CloseIcon from '@material-ui/icons/Close'; import MenuIcon from '@material-ui/icons/Menu'; @@ -43,7 +44,7 @@ const useStyles = makeStyles(theme => ({ bottom: 0, left: 0, right: 0, - zIndex: 1000, + zIndex: theme.zIndex.snackbar, // SidebarDivider color borderTop: '1px solid #383838', }, @@ -51,11 +52,10 @@ const useStyles = makeStyles(theme => ({ overlay: { background: theme.palette.navigation.background, width: '100%', - flex: '0 1 auto', + bottom: `${sidebarConfig.mobileSidebarHeight}px`, height: `calc(100% - ${sidebarConfig.mobileSidebarHeight}px)`, + flex: '0 1 auto', overflow: 'auto', - position: 'fixed', - zIndex: 500, }, overlayHeader: { @@ -63,7 +63,7 @@ const useStyles = makeStyles(theme => ({ color: theme.palette.bursts.fontColor, alignItems: 'center', justifyContent: 'space-between', - padding: `${theme.spacing(2)}px ${theme.spacing(3)}px`, + padding: theme.spacing(2, 3), }, overlayHeaderClose: { @@ -81,12 +81,22 @@ const sortSidebarGroupsForPriority = (children: React.ReactElement[]) => const OverlayMenu = ({ children, label = 'Menu', + open, onClose, -}: React.PropsWithChildren<{ label?: string; onClose: () => void }>) => { +}: React.PropsWithChildren<{ + label?: string; + onClose: () => void; + open: boolean; +}>) => { const classes = useStyles(); return ( - + {label} {children} - + ); }; @@ -106,7 +116,9 @@ export const MobileSidebarContext = createContext({ setSelectedMenuItemIndex: () => {}, }); -export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { +/** @public */ +export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { + const { children } = props; const classes = useStyles(); const location = useLocation(); const [selectedMenuItemIndex, setSelectedMenuItemIndex] = @@ -146,12 +158,19 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { - {shouldShowGroupChildren && ( - setSelectedMenuItemIndex(-1)} - /> - )} + setSelectedMenuItemIndex(-1)} + > + {sidebarGroups[selectedMenuItemIndex] && + (sidebarGroups[selectedMenuItemIndex].props + .children as React.ReactChildren)} + ( { name: 'BackstageSidebarPage' }, ); -export type SidebarPinStateContextType = { +/** @public */ +export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; + isMobile: boolean; }; -export const SidebarPinStateContext = createContext( - { - isPinned: true, - toggleSidebarPinState: () => {}, - }, -); +/** @public */ +export const SidebarStateContext = createContext({ + isPinned: true, + toggleSidebarPinState: () => {}, + isMobile: false, +}); export function SidebarPage(props: PropsWithChildren<{}>) { const [isPinned, setIsPinned] = useState(() => @@ -67,17 +70,23 @@ export function SidebarPage(props: PropsWithChildren<{}>) { LocalStorage.setSidebarPinState(isPinned); }, [isPinned]); + const isMobile = useMediaQuery( + theme => theme.breakpoints.down('xs'), + { noSsr: true }, + ); + const toggleSidebarPinState = () => setIsPinned(!isPinned); const classes = useStyles({ isPinned }); return ( -
{props.children}
-
+ ); } diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 0636cac134..9b5d76a32c 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -108,6 +108,5 @@ export const SampleScalableSidebar = () => ( - Test ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 51ae35c2db..1a43f703bf 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -20,13 +20,14 @@ import BottomNavigationAction, { BottomNavigationActionProps, } from '@material-ui/core/BottomNavigationAction'; import { makeStyles } from '@material-ui/core/styles'; -import useMediaQuery from '@material-ui/core/useMediaQuery'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; +import { SidebarStateContext } from '.'; import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; +/** @public */ export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; priority?: number; @@ -35,7 +36,7 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { const useStyles = makeStyles(theme => ({ root: { flexGrow: 0, - margin: `0 ${theme.spacing(2)}px`, + margin: theme.spacing(0, 2), color: theme.palette.navigation.color, }, @@ -50,7 +51,8 @@ const useStyles = makeStyles(theme => ({ }, })); -const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { +const MobileSidebarGroup = (props: SidebarGroupProps) => { + const { to, label, icon, value } = props; const classes = useStyles(); const location = useLocation(); const { selectedMenuItemIndex, setSelectedMenuItemIndex } = @@ -86,18 +88,14 @@ const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { ); }; -export const SidebarGroup = ({ - children, - to, - label, - icon, - value, -}: React.PropsWithChildren) => { - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); +/** @public */ +export const SidebarGroup = ( + props: React.PropsWithChildren, +) => { + const { children, to, label, icon, value } = props; + const { isMobile } = useContext(SidebarStateContext); - return isMobileScreen ? ( + return isMobile ? ( ) : ( <>{children} diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 2b5ff77538..4a1e1c0474 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -25,9 +25,15 @@ export type { SidebarSubmenuItemProps, SidebarSubmenuItemDropdownItem, } from './SidebarSubmenuItem'; -export type { SidebarClassKey } from './Bar'; -export { SidebarPage, SidebarPinStateContext } from './Page'; -export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; +export type { SidebarClassKey, SidebarProps } from './Bar'; +export { + SidebarPage, + SidebarStateContext as SidebarStateContext, +} from './Page'; +export type { + SidebarStateContextType as SidebarStateContextType, + SidebarPageClassKey, +} from './Page'; export { SidebarDivider, SidebarItem, diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 83937f9300..915edc94cb 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,23 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InfoCard } from '@backstage/core-components'; -import { BackstageTheme } from '@backstage/theme'; -import { List, useMediaQuery } from '@material-ui/core'; -import React from 'react'; +import { InfoCard, SidebarStateContext } from '@backstage/core-components'; +import { List } from '@material-ui/core'; +import React, { useContext } from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); + const isMobile = useContext(SidebarStateContext); return ( - {!isMobileScreen && } + {!isMobile && } ); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx index 39387df907..e2c9f23b91 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx @@ -18,18 +18,22 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarStateContext } from '@backstage/core-components'; describe('', () => { it('toggles the pin sidebar button', async () => { const mockToggleFn = jest.fn(); const rendered = await renderWithEffects( wrapInTestApp( - - , + , ), ); expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx index 4d71df8113..1cfd8b06dd 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx @@ -22,12 +22,10 @@ import { Switch, Tooltip, } from '@material-ui/core'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarStateContext } from '@backstage/core-components'; export const UserSettingsPinToggle = () => { - const { isPinned, toggleSidebarPinState } = useContext( - SidebarPinStateContext, - ); + const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); return ( diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index d5c7171148..b1bfdb5fc9 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -14,10 +14,13 @@ * limitations under the License. */ -import { Header, Page, TabbedLayout } from '@backstage/core-components'; -import { BackstageTheme } from '@backstage/theme'; -import { useMediaQuery } from '@material-ui/core'; -import React from 'react'; +import { + Header, + Page, + SidebarStateContext, + TabbedLayout, +} from '@backstage/core-components'; +import React, { useContext } from 'react'; import { UserSettingsAuthProviders } from './AuthProviders'; import { UserSettingsFeatureFlags } from './FeatureFlags'; import { UserSettingsGeneral } from './General'; @@ -27,13 +30,11 @@ type Props = { }; export const SettingsPage = ({ providerSettings }: Props) => { - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); + const { isMobile } = useContext(SidebarStateContext); return ( - {!isMobileScreen &&
} + {!isMobile &&
} From 02338a65157fe16d6e9c8710cd5e91aafeae1a4f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 29 Dec 2021 18:05:17 +0100 Subject: [PATCH 38/45] Improve documentation - Fix tests Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 37 +++++++----- .../core-components/src/layout/Page/Page.tsx | 2 +- .../src/layout/Sidebar/Bar.test.tsx | 26 +++++---- .../src/layout/Sidebar/Bar.tsx | 34 ++++++++--- .../src/layout/Sidebar/Items.tsx | 29 +++++++++- .../src/layout/Sidebar/MobileSidebar.test.tsx | 57 +++++++++++-------- .../src/layout/Sidebar/MobileSidebar.tsx | 27 +++++++-- .../src/layout/Sidebar/Page.tsx | 12 +++- .../src/layout/Sidebar/SidebarGroup.test.tsx | 38 ++++++++----- .../src/layout/Sidebar/SidebarGroup.tsx | 29 +++++++++- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 1 - .../src/layout/Sidebar/config.ts | 6 ++ .../src/layout/Sidebar/index.ts | 3 +- .../src/overridableComponents.ts | 4 ++ .../General/UserSettingsAppearanceCard.tsx | 2 +- 15 files changed, 221 insertions(+), 86 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 04ca94bdcb..2f4a1fb107 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -700,11 +700,20 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element; // @public (undocumented) export type MissingAnnotationEmptyStateClassKey = 'code'; -// @public (undocumented) +// @public export const MobileSidebar: ( props: React_2.PropsWithChildren<{}>, ) => JSX.Element | null; +// @public +export const MobileSidebarContext: React_2.Context; + +// @public +export type MobileSidebarContextType = { + selectedMenuItemIndex: number; + setSelectedMenuItemIndex: React_2.Dispatch>; +}; + // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -844,7 +853,7 @@ export type SelectItem = { value: string | number; }; -// @public (undocumented) +// @public export const Sidebar: ( props: React_2.PropsWithChildren, ) => JSX.Element; @@ -879,12 +888,12 @@ export const sidebarConfig: { // Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export const SidebarContext: Context; // Warning: (ae-missing-release-tag) "SidebarContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export type SidebarContextType = { isOpen: boolean; setOpen?: (open: boolean) => void; @@ -1167,16 +1176,14 @@ export type SidebarDividerClassKey = 'root'; // @public export const SidebarExpandButton: () => JSX.Element | null; -// @public (undocumented) +// @public export const SidebarGroup: ( props: React_2.PropsWithChildren, ) => JSX.Element; -// @public (undocumented) +// @public export interface SidebarGroupProps extends BottomNavigationActionProps { - // (undocumented) priority?: number; - // (undocumented) to?: string; } @@ -1196,19 +1203,19 @@ export type SidebarIntroClassKey = // Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export const SidebarItem: React_2.ForwardRefExoticComponent< SidebarItemProps & React_2.RefAttributes >; -// Warning: (ae-missing-release-tag) "SidebarItemClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type SidebarItemClassKey = | 'root' | 'buttonItem' | 'closed' | 'open' + | 'highlightable' + | 'highlighted' | 'label' | 'iconContainer' | 'searchRoot' @@ -1216,6 +1223,10 @@ export type SidebarItemClassKey = | 'searchFieldHTMLInput' | 'searchContainer' | 'secondaryAction' + | 'closedItemIcon' + | 'submenuArrow' + | 'expandButton' + | 'arrows' | 'selected'; // Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2058,10 +2069,10 @@ export const SidebarSpacer: React_2.ComponentType< // @public (undocumented) export type SidebarSpacerClassKey = 'root'; -// @public (undocumented) +// @public export const SidebarStateContext: React_2.Context; -// @public (undocumented) +// @public export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index a1efe4dda9..b013affee3 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -14,9 +14,9 @@ * limitations under the License. */ +import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; -import React, { PropsWithChildren } from 'react'; export type PageClassKey = 'root'; diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index acfd949ac5..0e0a7d5aa2 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -14,19 +14,23 @@ * limitations under the License. */ -import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; +import AcUnitIcon from '@material-ui/icons/AcUnit'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import BuildRoundedIcon from '@material-ui/icons/BuildRounded'; +import MenuBookIcon from '@material-ui/icons/MenuBook'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import BuildRoundedIcon from '@material-ui/icons/BuildRounded'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import MenuBookIcon from '@material-ui/icons/MenuBook'; -import AcUnitIcon from '@material-ui/icons/AcUnit'; -import { Sidebar } from './Bar'; -import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items'; -import { SidebarSubmenuItem } from './SidebarSubmenuItem'; -import { SidebarSubmenu } from './SidebarSubmenu'; -import { SidebarStateContext } from '.'; +import React from 'react'; +import { + Sidebar, + SidebarExpandButton, + SidebarItem, + SidebarSearchField, + SidebarStateContext, + SidebarSubmenu, + SidebarSubmenuItem, +} from '.'; async function renderScalableSidebar() { await renderInTestApp( @@ -37,7 +41,7 @@ async function renderScalableSidebar() { toggleSidebarPinState: () => {}, }} > - + {}} to="/search" /> {}} text="Catalog"> diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 51d4268d05..c3eddfe218 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -36,7 +36,7 @@ const useStyles = makeStyles( left: 0, top: 0, bottom: 0, - zIndex: 1000, + zIndex: theme.zIndex.appBar, background: theme.palette.navigation.background, overflowX: 'hidden', msOverflowStyle: 'none', @@ -77,12 +77,23 @@ export type SidebarProps = { disableExpandOnHover?: boolean; }; -const DesktopSidebar = ({ - openDelayMs = sidebarConfig.defaultOpenDelayMs, - closeDelayMs = sidebarConfig.defaultCloseDelayMs, - disableExpandOnHover, - children, -}: PropsWithChildren) => { +/** + * Places the Sidebar & wraps the children providing context weather the `Sidebar` is open or not. + * + * Handles & delays hover events for expanding the `Sidebar` + * + * @param props `disableExpandOnHover` disables the default hover behaviour; + * `openDelayMs` & `closeDelayMs` set delay until sidebar will open/close on hover + * @returns + * @internal + */ +const DesktopSidebar = (props: PropsWithChildren) => { + const { + openDelayMs = sidebarConfig.defaultOpenDelayMs, + closeDelayMs = sidebarConfig.defaultCloseDelayMs, + disableExpandOnHover, + children, + } = props; const classes = useStyles(); const isSmallScreen = useMediaQuery( theme => theme.breakpoints.down('md'), @@ -130,6 +141,9 @@ const DesktopSidebar = ({ const isOpen = (state === State.Open && !isSmallScreen) || isPinned; + /** + * Close/Open Sidebar directily without delays. Also toggles `SidebarPinState` to avoid hidden content behind Sidebar. + */ const setOpen = (open: boolean) => { if (open) { setState(State.Open); @@ -163,7 +177,11 @@ const DesktopSidebar = ({ ); }; -/** @public */ +/** + * Passing children into the desktop or mobile sidebar depending on the context + * + * @public + */ export const Sidebar = (props: React.PropsWithChildren) => { const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; const { isMobile } = useContext(SidebarStateContext); diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 7e95ecb92b..f1b517cbce 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -37,9 +37,9 @@ import React, { import { Link, NavLinkProps, + resolvePath, useLocation, useResolvedPath, - resolvePath, } from 'react-router-dom'; import { sidebarConfig, @@ -56,11 +56,14 @@ import DoubleArrowRight from './icons/DoubleArrowRight'; import { isLocationMatch } from './utils'; import { Location } from 'history'; +/** @public */ export type SidebarItemClassKey = | 'root' | 'buttonItem' | 'closed' | 'open' + | 'highlightable' + | 'highlighted' | 'label' | 'iconContainer' | 'searchRoot' @@ -68,6 +71,10 @@ export type SidebarItemClassKey = | 'searchFieldHTMLInput' | 'searchContainer' | 'secondaryAction' + | 'closedItemIcon' + | 'submenuArrow' + | 'expandButton' + | 'arrows' | 'selected'; const useStyles = makeStyles( @@ -238,12 +245,18 @@ const useStyles = makeStyles( { name: 'BackstageSidebarItem' }, ); +/** + * Evaluates the routes of the SubmenuItems & nested DropdownItems. + * The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization. + * + * @param submenu SidebarSubmenu component + * @param location Location + * @returns boolean + */ const useLocationMatch = ( submenu: React.ReactElement, location: Location, ): boolean => - // Evaluates the routes of the SubmenuItems & nested DropdownItems. - // The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization useElementFilter( submenu.props.children, elements => { @@ -367,6 +380,9 @@ export const WorkaroundNavLink = React.forwardRef< ); }); +/** + * Common component used by SidebarItem & SidebarItemWithSubmenu + */ const SidebarItemBase = forwardRef((props, ref) => { const { icon: Icon, @@ -507,6 +523,11 @@ const SidebarItemWithSubmenu = ({ ); }; +/** + * Creates a `SidebarItem` + * + * If children contain a `SidebarSubmenu` component the `SidebarItem` will have a expandable submenu + */ export const SidebarItem = forwardRef((props, ref) => { // Filter children for SidebarSubmenu components const [submenu] = useElementFilter(props.children, elements => @@ -655,6 +676,8 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => { * A button which allows you to expand the sidebar when clicked. * Use optionally to replace sidebar's expand-on-hover feature with expand-on-click. * + * If you are using this you might want to set the `disableExpandOnHover` of the `Sidebar` to `true`. + * * @public */ export const SidebarExpandButton = () => { diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx index 6f6463a3dc..8dd06b6e2b 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx @@ -21,32 +21,39 @@ import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { Sidebar } from './Bar'; -import { SidebarItem } from './Items'; -import { MobileSidebar } from './MobileSidebar'; -import { SidebarGroup } from './SidebarGroup'; +import { + MobileSidebar, + Sidebar, + SidebarGroup, + SidebarItem, + SidebarPage, +} from '.'; const MobileSidebarWithGroups = () => ( - -

Header

- } label="Menu"> - - - - -
Content
-
More Content
- } label="Create" to="#" /> -
Footer
-
+ + +

Header

+ } label="Menu"> + + + + +
Content
+
More Content
+ } label="Create" to="#" /> +
Footer
+
+
); const MobileSidebarWithoutGroups = () => ( - - - - - + + + + + + + ); describe('', () => { @@ -56,9 +63,11 @@ describe('', () => { it('should render MobileSidebar on smaller screens', async () => { const { getByTestId } = await renderInTestApp( - - - , + + + + + , ); expect(getByTestId('mobile-sidebar-root')).toBeVisible(); }); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 158b20e6be..af66dcc322 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -31,7 +31,12 @@ import { SidebarContext } from '.'; import { sidebarConfig } from './config'; import { SidebarGroup } from './SidebarGroup'; -type MobileSidebarContextType = { +/** + * Type of `MobileSidebarContext` + * + * @public + */ +export type MobileSidebarContextType = { selectedMenuItemIndex: number; setSelectedMenuItemIndex: React.Dispatch>; }; @@ -111,12 +116,25 @@ const OverlayMenu = ({ ); }; +/** + * Context on which `SidebarGroup` is currently selected + * + * @public + */ export const MobileSidebarContext = createContext({ selectedMenuItemIndex: -1, setSelectedMenuItemIndex: () => {}, }); -/** @public */ +/** + * A navigation component for mobile screens, which sticks to the bottom. + * + * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroups` + * either rendering them as a link or an overlay menu. + * If no `SidebarGroups` are provided the sidebar content is wrapped in an default overlay menu. + * + * @public + */ export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { const { children } = props; const classes = useStyles(); @@ -160,9 +178,8 @@ export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { > setSelectedMenuItemIndex(-1)} diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 26b3426eb0..2bc4ed195b 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -47,14 +47,22 @@ const useStyles = makeStyles( { name: 'BackstageSidebarPage' }, ); -/** @public */ +/** + * Type of `SidebarStateContext` + * + * @public + */ export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; isMobile: boolean; }; -/** @public */ +/** + * Contains the state on how the `Sidebar` is rendered + * + * @public + */ export const SidebarStateContext = createContext({ isPinned: true, toggleSidebarPinState: () => {}, diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx index 664495067c..e4d7e21c50 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -20,32 +20,42 @@ import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { SidebarItem } from './Items'; -import { MobileSidebarContext } from './MobileSidebar'; -import { SidebarGroup } from './SidebarGroup'; +import { + MobileSidebarContext, + SidebarGroup, + SidebarItem, + SidebarPage, +} from '.'; const SidebarGroupWithItems = () => ( - } label="Menu"> - - - - + + } label="Menu"> + + + + + ); describe('', () => { - it('should render Items in BottomNavigationAciton on small screens', async () => { + it('should render Items in BottomNavigationAction on small screens', async () => { mockBreakpoint({ matches: true }); - const { findByRole, container } = await renderInTestApp( + const { getByRole, getAllByRole } = await renderInTestApp( , ); - expect(container.childNodes.length).toBe(1); - expect(await findByRole('button')).toBeVisible(); + expect(getAllByRole('button').length).toEqual(1); + expect(getByRole('button')).toBeVisible(); }); it('should render Items without wrapper on bigger screens', async () => { mockBreakpoint({ matches: false }); - const { container } = await renderInTestApp(); - expect(container.childNodes.length).toBe(3); + const { getByRole, queryByRole } = await renderInTestApp( + , + ); + expect(queryByRole('button')).not.toBeInTheDocument(); + expect(getByRole('link', { name: 'Home' })).toBeVisible(); + expect(getByRole('link', { name: 'Explore' })).toBeVisible(); + expect(getByRole('link', { name: 'Docs' })).toBeVisible(); }); it('should trigger update of MobileSidebarContext', async () => { diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 1a43f703bf..f80af385c8 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -27,9 +27,20 @@ import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; -/** @public */ +/** + * Props for the `SidebarGroup` + * + * @public + */ export interface SidebarGroupProps extends BottomNavigationActionProps { + /** + * If the `SidebarGroup` should be a `Link`, `to` should be a pathname to that location + */ to?: string; + /** + * If the `SidebarGroups` should be in a different order than in the normal `Sidebar`, you can provide + * each `SidebarGroup` it's own priority to reorder them. + */ priority?: number; } @@ -51,6 +62,13 @@ const useStyles = makeStyles(theme => ({ }, })); +/** + * Returns a MUI `BottomNavigationAction`, which is aware of the current location & the selected item in the `BottomNavigation`, + * such that it will highlight a `MobileSidebarGroup` either on location change or if the selected item changes. + * + * @param props `to`: pathname of link; `value`: index of the selected item + * @internal + */ const MobileSidebarGroup = (props: SidebarGroupProps) => { const { to, label, icon, value } = props; const classes = useStyles(); @@ -88,7 +106,14 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { ); }; -/** @public */ +/** + * Groups items of the `Sidebar` together. + * + * On bigger screens, this won't have any effect at the moment. + * On small screens, it will add an action to the bottom navigation - either triggering an overlay menu or acting as a link + * + * @public + */ export const SidebarGroup = ( props: React.PropsWithChildren, ) => { diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 47aba5e18e..1ede5f887e 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -122,7 +122,6 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { title, to, icon: Icon, dropdownItems } = props; const classes = useStyles(); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); - const closeSubmenu = () => { setIsHoveredOn(false); }; diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index a26a3dbc0d..0ef5282997 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -58,11 +58,17 @@ export const submenuConfig = { export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; +/** + * Types for the `SidebarContext` + */ export type SidebarContextType = { isOpen: boolean; setOpen?: (open: boolean) => void; }; +/** + * Context wether the `Sidebar` is open + */ export const SidebarContext = createContext({ isOpen: false, }); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 4a1e1c0474..a9d31f465b 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,7 +15,8 @@ */ export { Sidebar } from './Bar'; -export { MobileSidebar } from './MobileSidebar'; +export { MobileSidebar, MobileSidebarContext } from './MobileSidebar'; +export type { MobileSidebarContextType } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarSubmenuItem } from './SidebarSubmenuItem'; diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index 8044da4c81..c53c47da1b 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -87,6 +87,8 @@ import { SidebarSpacerClassKey, SidebarDividerClassKey, SidebarIntroClassKey, + SidebarItemClassKey, + SidebarPageClassKey, CustomProviderClassKey, SignInPageClassKey, TabbedCardClassKey, @@ -162,6 +164,8 @@ type BackstageComponentsNameToClassKey = { BackstageSidebarSpacer: SidebarSpacerClassKey; BackstageSidebarDivider: SidebarDividerClassKey; BackstageSidebarIntro: SidebarIntroClassKey; + BackstageSidebarItem: SidebarItemClassKey; + BackstageSidebarPage: SidebarPageClassKey; BackstageCustomProvider: CustomProviderClassKey; BackstageSignInPage: SignInPageClassKey; BackstageTabbedCard: TabbedCardClassKey; diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 915edc94cb..946d22a9f0 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -20,7 +20,7 @@ import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const isMobile = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarStateContext); return ( From 8c833df12a6730edd927ba7120a32545615165e7 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 29 Dec 2021 18:34:18 +0100 Subject: [PATCH 39/45] Fix dublicated css from merge Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/Items.tsx | 55 ++----------------- 1 file changed, 5 insertions(+), 50 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index f1b517cbce..ed090690f0 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -185,59 +185,14 @@ const useStyles = makeStyles( borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, color: theme.palette.navigation.selectedColor, }, - buttonItem: { - background: 'none', - border: 'none', - width: 'auto', - margin: 0, - padding: 0, - textAlign: 'inherit', - font: 'inherit', - }, - closed: { + '&$closed': { width: drawerWidthClosed, - justifyContent: 'center', }, - open: { - width: drawerWidthOpen, + '& $closedItemIcon': { + paddingRight: selectedIndicatorWidth, }, - label: { - // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs - fontWeight: 'bold', - whiteSpace: 'nowrap', - lineHeight: 'auto', - flex: '3 1 auto', - width: '110px', - overflow: 'hidden', - 'text-overflow': 'ellipsis', - }, - iconContainer: { - boxSizing: 'border-box', - height: '100%', - width: iconContainerWidth, - marginRight: -theme.spacing(2), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - searchRoot: { - marginBottom: 12, - }, - searchField: { - color: '#b5b5b5', - fontWeight: 'bold', - fontSize: theme.typography.fontSize, - }, - searchFieldHTMLInput: { - padding: theme.spacing(2, 0, 2), - }, - searchContainer: { - width: drawerWidthOpen - iconContainerWidth, - }, - secondaryAction: { - width: theme.spacing(6), - textAlign: 'center', - marginRight: theme.spacing(1), + '& $iconContainer': { + marginLeft: -selectedIndicatorWidth, }, }, }; From a0142b7b04881eac754a21ec2a8898ea6738d434 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 3 Jan 2022 10:44:39 +0100 Subject: [PATCH 40/45] chore: reset the adopters.md file Signed-off-by: blam --- ADOPTERS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 95f7812636..e0e6a818c6 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -77,5 +77,5 @@ | [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon), [Gauthier Roebroeck](https://github.com/gauthier-roebroeck-mox) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 | | [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 | | [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. | -| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding | -| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. | +| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding | +| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. | From 7c28c825377a809b62f6501d3e671a76595c8f7f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 5 Jan 2022 16:02:39 +0100 Subject: [PATCH 41/45] Improve typing by making isMobile optional & removing PropsWithChildren Improve consistency in documentation Signed-off-by: Philipp Hugenroth --- .changeset/great-bulldogs-provide.md | 2 +- .changeset/stale-brooms-fry.md | 4 +-- .../configure-app-with-plugins.md | 2 +- packages/core-components/api-report.md | 28 ++++++++++------- .../src/layout/Sidebar/Bar.tsx | 7 +++-- .../src/layout/Sidebar/MobileSidebar.tsx | 31 ++++++++++++++----- .../src/layout/Sidebar/Page.tsx | 20 +++++++----- .../src/layout/Sidebar/SidebarGroup.tsx | 10 +++--- .../src/layout/Sidebar/index.ts | 6 +++- 9 files changed, 71 insertions(+), 39 deletions(-) diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md index f076dfa545..3b51ea9bdf 100644 --- a/.changeset/great-bulldogs-provide.md +++ b/.changeset/great-bulldogs-provide.md @@ -6,6 +6,6 @@ The `Bar` component will now render a `MobileSidebar` instead of the current sid --- -**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item. +**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroup`s as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroup`s are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item. **Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens, this won't have any effect at the moment. On smaller screens, it will render a given icon into the `MobileSidebar`. If a route is provided, clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided, it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md index c98a427e80..18c847973e 100644 --- a/.changeset/stale-brooms-fry.md +++ b/.changeset/stale-brooms-fry.md @@ -2,7 +2,7 @@ '@backstage/create-app': patch --- -You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`. +You can now add `SidebarGroup`s to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`. A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full-screen overlay menu (e.g. `Menu`). @@ -46,6 +46,6 @@ Additionally, you can order the groups differently in the `MobileSidebar` than i ``` -If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`. +If you decide against adding `SidebarGroup`s to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`. More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`. diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 7f522437da..1bfabc335c 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -94,7 +94,7 @@ import InternalToolIcon from './internal-tool.icon.svg'; On mobile devices the `Sidebar` is displayed at the bottom of the screen. For customizing the experience you can group `SidebarItems` in a `SidebarGroup` (Example 1) or create a `SidebarGroup` with a link (Example 2). All -`SidebarGroups` are displayed in the bottom navigation with an icon. +`SidebarGroup`s are displayed in the bottom navigation with an icon. ```ts // Example 1 diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 2f4a1fb107..c2244e686d 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -701,9 +701,7 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element; export type MissingAnnotationEmptyStateClassKey = 'code'; // @public -export const MobileSidebar: ( - props: React_2.PropsWithChildren<{}>, -) => JSX.Element | null; +export const MobileSidebar: (props: MobileSidebarProps) => JSX.Element | null; // @public export const MobileSidebarContext: React_2.Context; @@ -714,6 +712,11 @@ export type MobileSidebarContextType = { setSelectedMenuItemIndex: React_2.Dispatch>; }; +// @public +export type MobileSidebarProps = { + children?: React_2.ReactNode; +}; + // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -854,9 +857,7 @@ export type SelectItem = { }; // @public -export const Sidebar: ( - props: React_2.PropsWithChildren, -) => JSX.Element; +export const Sidebar: (props: SidebarProps) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1177,12 +1178,11 @@ export type SidebarDividerClassKey = 'root'; export const SidebarExpandButton: () => JSX.Element | null; // @public -export const SidebarGroup: ( - props: React_2.PropsWithChildren, -) => JSX.Element; +export const SidebarGroup: (props: SidebarGroupProps) => JSX.Element; // @public export interface SidebarGroupProps extends BottomNavigationActionProps { + children?: React_2.ReactNode; priority?: number; to?: string; } @@ -1232,18 +1232,24 @@ export type SidebarItemClassKey = // Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function SidebarPage(props: PropsWithChildren<{}>): JSX.Element; +export function SidebarPage(props: SidebarPageProps): JSX.Element; // Warning: (ae-missing-release-tag) "SidebarPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export type SidebarPageClassKey = 'root'; +// @public +export type SidebarPageProps = { + children?: React_2.ReactNode; +}; + // @public (undocumented) export type SidebarProps = { openDelayMs?: number; closeDelayMs?: number; disableExpandOnHover?: boolean; + children?: React_2.ReactNode; }; // Warning: (ae-missing-release-tag) "SidebarScrollWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2076,7 +2082,7 @@ export const SidebarStateContext: React_2.Context; export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; - isMobile: boolean; + isMobile?: boolean; }; // @public diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index c3eddfe218..fe3964b745 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -17,7 +17,7 @@ import { makeStyles } from '@material-ui/core/styles'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import classnames from 'classnames'; -import React, { useState, useContext, PropsWithChildren, useRef } from 'react'; +import React, { useState, useContext, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarStateContext } from './Page'; @@ -75,6 +75,7 @@ export type SidebarProps = { openDelayMs?: number; closeDelayMs?: number; disableExpandOnHover?: boolean; + children?: React.ReactNode; }; /** @@ -87,7 +88,7 @@ export type SidebarProps = { * @returns * @internal */ -const DesktopSidebar = (props: PropsWithChildren) => { +const DesktopSidebar = (props: SidebarProps) => { const { openDelayMs = sidebarConfig.defaultOpenDelayMs, closeDelayMs = sidebarConfig.defaultCloseDelayMs, @@ -182,7 +183,7 @@ const DesktopSidebar = (props: PropsWithChildren) => { * * @public */ -export const Sidebar = (props: React.PropsWithChildren) => { +export const Sidebar = (props: SidebarProps) => { const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; const { isMobile } = useContext(SidebarStateContext); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index af66dcc322..0761199c76 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -41,6 +41,25 @@ export type MobileSidebarContextType = { setSelectedMenuItemIndex: React.Dispatch>; }; +/** + * Props of MobileSidebar + * + * @public + */ +export type MobileSidebarProps = { + children?: React.ReactNode; +}; + +/** + * @internal + */ +type OverlayMenuProps = { + label?: string; + onClose: () => void; + open: boolean; + children?: React.ReactNode; +}; + const useStyles = makeStyles(theme => ({ root: { position: 'fixed', @@ -88,11 +107,7 @@ const OverlayMenu = ({ label = 'Menu', open, onClose, -}: React.PropsWithChildren<{ - label?: string; - onClose: () => void; - open: boolean; -}>) => { +}: OverlayMenuProps) => { const classes = useStyles(); return ( @@ -129,13 +144,13 @@ export const MobileSidebarContext = createContext({ /** * A navigation component for mobile screens, which sticks to the bottom. * - * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroups` + * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroup`s * either rendering them as a link or an overlay menu. - * If no `SidebarGroups` are provided the sidebar content is wrapped in an default overlay menu. + * If no `SidebarGroup`s are provided the sidebar content is wrapped in an default overlay menu. * * @public */ -export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { +export const MobileSidebar = (props: MobileSidebarProps) => { const { children } = props; const classes = useStyles(); const location = useLocation(); diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 2bc4ed195b..9e5d897de6 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -15,12 +15,7 @@ */ import { makeStyles } from '@material-ui/core/styles'; -import React, { - createContext, - PropsWithChildren, - useEffect, - useState, -} from 'react'; +import React, { createContext, useEffect, useState } from 'react'; import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; @@ -55,7 +50,16 @@ const useStyles = makeStyles( export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; - isMobile: boolean; + isMobile?: boolean; +}; + +/** + * Props for SidebarPage + * + * @public + */ +export type SidebarPageProps = { + children?: React.ReactNode; }; /** @@ -69,7 +73,7 @@ export const SidebarStateContext = createContext({ isMobile: false, }); -export function SidebarPage(props: PropsWithChildren<{}>) { +export function SidebarPage(props: SidebarPageProps) { const [isPinned, setIsPinned] = useState(() => LocalStorage.getSidebarPinState(), ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index f80af385c8..fc451cd779 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -38,10 +38,14 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { */ to?: string; /** - * If the `SidebarGroups` should be in a different order than in the normal `Sidebar`, you can provide + * If the `SidebarGroup`s should be in a different order than in the normal `Sidebar`, you can provide * each `SidebarGroup` it's own priority to reorder them. */ priority?: number; + /** + * React children + */ + children?: React.ReactNode; } const useStyles = makeStyles(theme => ({ @@ -114,9 +118,7 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { * * @public */ -export const SidebarGroup = ( - props: React.PropsWithChildren, -) => { +export const SidebarGroup = (props: SidebarGroupProps) => { const { children, to, label, icon, value } = props; const { isMobile } = useContext(SidebarStateContext); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index a9d31f465b..61fbcd25ab 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -16,7 +16,10 @@ export { Sidebar } from './Bar'; export { MobileSidebar, MobileSidebarContext } from './MobileSidebar'; -export type { MobileSidebarContextType } from './MobileSidebar'; +export type { + MobileSidebarContextType, + MobileSidebarProps, +} from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarSubmenuItem } from './SidebarSubmenuItem'; @@ -34,6 +37,7 @@ export { export type { SidebarStateContextType as SidebarStateContextType, SidebarPageClassKey, + SidebarPageProps, } from './Page'; export { SidebarDivider, From 4ae04a2c2fd9964344b48290100ce185a7580791 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 10 Jan 2022 10:32:56 +0100 Subject: [PATCH 42/45] Fix naming of SidebarStateContext import Signed-off-by: Philipp Hugenroth --- plugins/techdocs/src/reader/components/Reader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 57d2786af5..424697f59d 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -31,7 +31,7 @@ import { EntityName } from '@backstage/catalog-model'; import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarStateContext } from '@backstage/core-components'; import { techdocsStorageApiRef } from '../../api'; @@ -146,7 +146,7 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => { const [dom, setDom] = useState(null); // sidebar pinned status to be used in computing CSS style injections - const { isPinned } = useContext(SidebarPinStateContext); + const { isPinned } = useContext(SidebarStateContext); const updateSidebarPosition = useCallback(() => { if (!dom || !sidebars) return; From 2989c8282588d135888d7f5014184bc85687ebc3 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 13 Jan 2022 10:35:33 +0100 Subject: [PATCH 43/45] Avoid breaking changes Signed-off-by: Philipp Hugenroth --- .../app/src/components/search/SearchPage.tsx | 4 ++-- packages/core-components/api-report.md | 22 +++++++++---------- .../src/layout/Sidebar/Bar.test.tsx | 6 ++--- .../src/layout/Sidebar/Bar.tsx | 8 ++++--- .../src/layout/Sidebar/Items.tsx | 2 +- .../src/layout/Sidebar/MobileSidebar.tsx | 2 +- .../src/layout/Sidebar/Page.tsx | 20 +++++++++-------- .../src/layout/Sidebar/SidebarGroup.tsx | 4 ++-- .../src/layout/Sidebar/config.ts | 3 ++- .../src/layout/Sidebar/index.ts | 4 ++-- .../techdocs/src/reader/components/Reader.tsx | 4 ++-- .../General/UserSettingsAppearanceCard.tsx | 4 ++-- .../General/UserSettingsPinToggle.test.tsx | 6 ++--- .../General/UserSettingsPinToggle.tsx | 6 +++-- .../src/components/SettingsPage.tsx | 4 ++-- 15 files changed, 53 insertions(+), 46 deletions(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 8cd1b85d2a..9a8c208103 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -21,7 +21,7 @@ import { Header, Lifecycle, Page, - SidebarStateContext, + SidebarPinStateContext, } from '@backstage/core-components'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; import { @@ -53,7 +53,7 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return ( diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index c2244e686d..f10d7102c9 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -897,7 +897,7 @@ export const SidebarContext: Context; // @public export type SidebarContextType = { isOpen: boolean; - setOpen?: (open: boolean) => void; + setOpen: (open: boolean) => void; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1244,6 +1244,16 @@ export type SidebarPageProps = { children?: React_2.ReactNode; }; +// @public +export const SidebarPinStateContext: React_2.Context; + +// @public +export type SidebarPinStateContextType = { + isPinned: boolean; + toggleSidebarPinState: () => any; + isMobile?: boolean; +}; + // @public (undocumented) export type SidebarProps = { openDelayMs?: number; @@ -2075,16 +2085,6 @@ export const SidebarSpacer: React_2.ComponentType< // @public (undocumented) export type SidebarSpacerClassKey = 'root'; -// @public -export const SidebarStateContext: React_2.Context; - -// @public -export type SidebarStateContextType = { - isPinned: boolean; - toggleSidebarPinState: () => any; - isMobile?: boolean; -}; - // @public export const SidebarSubmenu: (props: SidebarSubmenuProps) => JSX.Element; diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index 0e0a7d5aa2..89133997f9 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -27,14 +27,14 @@ import { SidebarExpandButton, SidebarItem, SidebarSearchField, - SidebarStateContext, + SidebarPinStateContext, SidebarSubmenu, SidebarSubmenuItem, } from '.'; async function renderScalableSidebar() { await renderInTestApp( -
- , + , ); } diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index fe3964b745..ff907cdda3 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -20,7 +20,7 @@ import classnames from 'classnames'; import React, { useState, useContext, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarStateContext } from './Page'; +import { SidebarPinStateContext } from './Page'; import { MobileSidebar } from './MobileSidebar'; /** @public */ @@ -102,7 +102,9 @@ const DesktopSidebar = (props: SidebarProps) => { ); const [state, setState] = useState(State.Closed); const hoverTimerRef = useRef(); - const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); + const { isPinned, toggleSidebarPinState } = useContext( + SidebarPinStateContext, + ); const handleOpen = () => { if (isPinned || disableExpandOnHover) { @@ -185,7 +187,7 @@ const DesktopSidebar = (props: SidebarProps) => { */ export const Sidebar = (props: SidebarProps) => { const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return isMobile ? ( {children} diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index ed090690f0..b16c9ad302 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -643,7 +643,7 @@ export const SidebarExpandButton = () => { { noSsr: true }, ); - if (isSmallScreen || !setOpen) { + if (isSmallScreen) { return null; } diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 0761199c76..298c53fb8f 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -187,7 +187,7 @@ export const MobileSidebar = (props: MobileSidebarProps) => { !sidebarGroups[selectedMenuItemIndex].props.to; return ( - + {} }}> diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 9e5d897de6..f23a6fa3b6 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -43,11 +43,11 @@ const useStyles = makeStyles( ); /** - * Type of `SidebarStateContext` + * Type of `SidebarPinStateContext` * * @public */ -export type SidebarStateContextType = { +export type SidebarPinStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; isMobile?: boolean; @@ -67,11 +67,13 @@ export type SidebarPageProps = { * * @public */ -export const SidebarStateContext = createContext({ - isPinned: true, - toggleSidebarPinState: () => {}, - isMobile: false, -}); +export const SidebarPinStateContext = createContext( + { + isPinned: true, + toggleSidebarPinState: () => {}, + isMobile: false, + }, +); export function SidebarPage(props: SidebarPageProps) { const [isPinned, setIsPinned] = useState(() => @@ -91,7 +93,7 @@ export function SidebarPage(props: SidebarPageProps) { const classes = useStyles({ isPinned }); return ( -
{props.children}
-
+ ); } diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index fc451cd779..bc7688153a 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -22,7 +22,7 @@ import BottomNavigationAction, { import { makeStyles } from '@material-ui/core/styles'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; -import { SidebarStateContext } from '.'; +import { SidebarPinStateContext } from '.'; import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; @@ -120,7 +120,7 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { */ export const SidebarGroup = (props: SidebarGroupProps) => { const { children, to, label, icon, value } = props; - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return isMobile ? ( diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index 0ef5282997..c41db1f537 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -63,7 +63,7 @@ export const SIDEBAR_INTRO_LOCAL_STORAGE = */ export type SidebarContextType = { isOpen: boolean; - setOpen?: (open: boolean) => void; + setOpen: (open: boolean) => void; }; /** @@ -71,6 +71,7 @@ export type SidebarContextType = { */ export const SidebarContext = createContext({ isOpen: false, + setOpen: () => {}, }); export type SidebarItemWithSubmenuContextType = { diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 61fbcd25ab..82becb667f 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -32,10 +32,10 @@ export type { export type { SidebarClassKey, SidebarProps } from './Bar'; export { SidebarPage, - SidebarStateContext as SidebarStateContext, + SidebarPinStateContext as SidebarPinStateContext, } from './Page'; export type { - SidebarStateContextType as SidebarStateContextType, + SidebarPinStateContextType as SidebarPinStateContextType, SidebarPageClassKey, SidebarPageProps, } from './Page'; diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 424697f59d..57d2786af5 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -31,7 +31,7 @@ import { EntityName } from '@backstage/catalog-model'; import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarStateContext } from '@backstage/core-components'; +import { SidebarPinStateContext } from '@backstage/core-components'; import { techdocsStorageApiRef } from '../../api'; @@ -146,7 +146,7 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => { const [dom, setDom] = useState(null); // sidebar pinned status to be used in computing CSS style injections - const { isPinned } = useContext(SidebarStateContext); + const { isPinned } = useContext(SidebarPinStateContext); const updateSidebarPosition = useCallback(() => { if (!dom || !sidebars) return; diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 946d22a9f0..15ab93c6d0 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InfoCard, SidebarStateContext } from '@backstage/core-components'; +import { InfoCard, SidebarPinStateContext } from '@backstage/core-components'; import { List } from '@material-ui/core'; import React, { useContext } from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return ( diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx index e2c9f23b91..b63ac85b10 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx @@ -18,14 +18,14 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; -import { SidebarStateContext } from '@backstage/core-components'; +import { SidebarPinStateContext } from '@backstage/core-components'; describe('', () => { it('toggles the pin sidebar button', async () => { const mockToggleFn = jest.fn(); const rendered = await renderWithEffects( wrapInTestApp( - ', () => { }} > - , + , ), ); expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx index 1cfd8b06dd..4d71df8113 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx @@ -22,10 +22,12 @@ import { Switch, Tooltip, } from '@material-ui/core'; -import { SidebarStateContext } from '@backstage/core-components'; +import { SidebarPinStateContext } from '@backstage/core-components'; export const UserSettingsPinToggle = () => { - const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); + const { isPinned, toggleSidebarPinState } = useContext( + SidebarPinStateContext, + ); return ( diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index b1bfdb5fc9..88b46fc372 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -17,7 +17,7 @@ import { Header, Page, - SidebarStateContext, + SidebarPinStateContext, TabbedLayout, } from '@backstage/core-components'; import React, { useContext } from 'react'; @@ -30,7 +30,7 @@ type Props = { }; export const SettingsPage = ({ providerSettings }: Props) => { - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return ( From 1fdf97f15043e309438b28e775af27b187c29edf Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 14 Jan 2022 16:09:50 +0100 Subject: [PATCH 44/45] Avoid breaking api changes Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 9 --------- .../core-components/src/layout/Sidebar/MobileSidebar.tsx | 4 ++-- packages/core-components/src/layout/Sidebar/index.ts | 7 ++----- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index f10d7102c9..b3f502e9ae 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -703,15 +703,6 @@ export type MissingAnnotationEmptyStateClassKey = 'code'; // @public export const MobileSidebar: (props: MobileSidebarProps) => JSX.Element | null; -// @public -export const MobileSidebarContext: React_2.Context; - -// @public -export type MobileSidebarContextType = { - selectedMenuItemIndex: number; - setSelectedMenuItemIndex: React_2.Dispatch>; -}; - // @public export type MobileSidebarProps = { children?: React_2.ReactNode; diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 298c53fb8f..3819f21a02 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -34,7 +34,7 @@ import { SidebarGroup } from './SidebarGroup'; /** * Type of `MobileSidebarContext` * - * @public + * @internal */ export type MobileSidebarContextType = { selectedMenuItemIndex: number; @@ -134,7 +134,7 @@ const OverlayMenu = ({ /** * Context on which `SidebarGroup` is currently selected * - * @public + * @internal */ export const MobileSidebarContext = createContext({ selectedMenuItemIndex: -1, diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 82becb667f..d175b364da 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,11 +15,8 @@ */ export { Sidebar } from './Bar'; -export { MobileSidebar, MobileSidebarContext } from './MobileSidebar'; -export type { - MobileSidebarContextType, - MobileSidebarProps, -} from './MobileSidebar'; +export { MobileSidebar } from './MobileSidebar'; +export type { MobileSidebarProps } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarSubmenuItem } from './SidebarSubmenuItem'; From 374550ead0bb797bc8900738b50046c013f84ed1 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 14 Jan 2022 16:18:22 +0100 Subject: [PATCH 45/45] Fix wrong import path in test Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/SidebarGroup.test.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx index e4d7e21c50..b8f1eb5ebc 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -20,12 +20,8 @@ import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { - MobileSidebarContext, - SidebarGroup, - SidebarItem, - SidebarPage, -} from '.'; +import { MobileSidebarContext } from './MobileSidebar'; +import { SidebarGroup, SidebarItem, SidebarPage } from '.'; const SidebarGroupWithItems = () => (