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 &&
}