Improve mobile submenu experience

Signed-off-by: Philipp Hugenroth <philipph@spotify.com>
This commit is contained in:
Philipp Hugenroth
2021-12-09 10:53:17 +01:00
parent 7d9913fc43
commit 510a595397
3 changed files with 48 additions and 7 deletions
@@ -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<BackstageTheme>(
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<BackstageTheme>((theme: BackstageTheme) =>
theme.breakpoints.down('sm'),
);
const handleMouseEnter = () => {
setIsHoveredOn(true);
@@ -308,6 +317,21 @@ const SidebarItemWithSubmenu = ({
</>
);
const arrowIcon = () => {
if (isSmallScreen) {
return isHoveredOn ? (
<ArrowDropUp fontSize="small" className={classes.submenuArrow} />
) : (
<ArrowDropDown fontSize="small" className={classes.submenuArrow} />
);
}
return (
!isHoveredOn && (
<ArrowRightIcon fontSize="small" className={classes.submenuArrow} />
)
);
};
return (
<SidebarItemWithSubmenuContext.Provider
value={{
@@ -315,7 +339,11 @@ const SidebarItemWithSubmenu = ({
setIsHoveredOn,
}}
>
<div onMouseLeave={handleMouseLeave} onMouseEnter={handleMouseEnter}>
<div
onMouseLeave={handleMouseLeave}
onTouchStart={isHoveredOn ? handleMouseLeave : handleMouseEnter}
onMouseEnter={handleMouseEnter}
>
<div
data-testid="item-with-submenu"
className={clsx(
@@ -327,9 +355,7 @@ const SidebarItemWithSubmenu = ({
)}
>
{isOpen ? openContent : itemIcon}
{!isHoveredOn && (
<ArrowRightIcon fontSize="small" className={classes.submenuArrow} />
)}
{arrowIcon()}
</div>
{isHoveredOn && children}
</div>
@@ -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);
@@ -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<BackstageTheme>(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) => {
<div className={classes.itemContainer}>
<button
onClick={handleClickDropdown}
onTouchStart={e => e.stopPropagation()}
className={clsx(
classes.item,
isActive ? classes.selected : undefined,
@@ -166,6 +173,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
underline="none"
className={classes.dropdownItem}
onClick={closeSubmenu}
onTouchStart={e => e.stopPropagation()}
key={key}
>
<Typography className={classes.textContent}>
@@ -187,6 +195,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
underline="none"
className={clsx(classes.item, isActive ? classes.selected : undefined)}
onClick={closeSubmenu}
onTouchStart={e => e.stopPropagation()}
>
<Icon fontSize="small" />
<Typography variant="subtitle1" className={classes.label}>