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"> + + ); ```