Add basic support for submenus to mobile sidebar
Signed-off-by: Philipp Hugenroth <philipph@spotify.com>
This commit is contained in:
@@ -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<SidebarContextType>;
|
||||
// @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)
|
||||
|
||||
@@ -178,18 +178,8 @@ export const Sidebar = ({ children }: React.PropsWithChildren<Props>) => {
|
||||
theme.breakpoints.down('xs'),
|
||||
);
|
||||
|
||||
// TODO: Generalize
|
||||
const setOpen = () => {};
|
||||
|
||||
return isMobileScreen ? (
|
||||
<SidebarContext.Provider
|
||||
value={{
|
||||
isOpen: true,
|
||||
setOpen,
|
||||
}}
|
||||
>
|
||||
<MobileSidebar>{children}</MobileSidebar>
|
||||
</SidebarContext.Provider>
|
||||
<MobileSidebar>{children}</MobileSidebar>
|
||||
) : (
|
||||
<DesktopSidebar>{children}</DesktopSidebar>
|
||||
);
|
||||
@@ -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 (
|
||||
<button
|
||||
onClick={handleClick}
|
||||
|
||||
@@ -307,7 +307,6 @@ const SidebarItemWithSubmenu = ({
|
||||
<div className={classes.secondaryAction}>{}</div>
|
||||
</>
|
||||
);
|
||||
const closedContent = itemIcon;
|
||||
|
||||
return (
|
||||
<SidebarItemWithSubmenuContext.Provider
|
||||
@@ -316,12 +315,8 @@ const SidebarItemWithSubmenu = ({
|
||||
setIsHoveredOn,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onMouseLeave={handleMouseLeave}
|
||||
className={clsx(isHoveredOn && classes.highlighted)}
|
||||
>
|
||||
<div onMouseLeave={handleMouseLeave} onMouseEnter={handleMouseEnter}>
|
||||
<div
|
||||
onMouseEnter={handleMouseEnter}
|
||||
data-testid="item-with-submenu"
|
||||
className={clsx(
|
||||
classes.root,
|
||||
@@ -331,7 +326,7 @@ const SidebarItemWithSubmenu = ({
|
||||
isHoveredOn && classes.highlighted,
|
||||
)}
|
||||
>
|
||||
{isOpen ? openContent : closedContent}
|
||||
{isOpen ? openContent : itemIcon}
|
||||
{!isHoveredOn && (
|
||||
<ArrowRightIcon fontSize="small" className={classes.submenuArrow} />
|
||||
)}
|
||||
@@ -488,28 +483,12 @@ export const SidebarItem = forwardRef<any, SidebarItemProps>((props, ref) => {
|
||||
),
|
||||
};
|
||||
|
||||
let hasSubmenu = false;
|
||||
let submenu: ReactNode;
|
||||
const componentType = (
|
||||
<SidebarSubmenu>
|
||||
<></>
|
||||
</SidebarSubmenu>
|
||||
).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 (
|
||||
<SidebarItemWithSubmenu
|
||||
text={text}
|
||||
|
||||
@@ -26,6 +26,7 @@ import MenuIcon from '@material-ui/icons/Menu';
|
||||
import { orderBy } from 'lodash';
|
||||
import React, { createContext, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { SidebarContext } from '.';
|
||||
import { sidebarConfig } from './config';
|
||||
import { SidebarGroup } from './SidebarGroup';
|
||||
|
||||
@@ -143,21 +144,23 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => {
|
||||
!sidebarGroups[selectedMenuItemIndex].props.to;
|
||||
|
||||
return (
|
||||
<MobileSidebarContext.Provider
|
||||
value={{ selectedMenuItemIndex, setSelectedMenuItemIndex }}
|
||||
>
|
||||
{shouldShowGroupChildren && (
|
||||
<OverlayMenu
|
||||
{...sidebarGroups[selectedMenuItemIndex].props}
|
||||
onClose={() => setSelectedMenuItemIndex(-1)}
|
||||
/>
|
||||
)}
|
||||
<BottomNavigation
|
||||
className={classes.root}
|
||||
data-testid="mobile-sidebar-root"
|
||||
<SidebarContext.Provider value={{ isOpen: true }}>
|
||||
<MobileSidebarContext.Provider
|
||||
value={{ selectedMenuItemIndex, setSelectedMenuItemIndex }}
|
||||
>
|
||||
{sidebarGroups}
|
||||
</BottomNavigation>
|
||||
</MobileSidebarContext.Provider>
|
||||
{shouldShowGroupChildren && (
|
||||
<OverlayMenu
|
||||
{...sidebarGroups[selectedMenuItemIndex].props}
|
||||
onClose={() => setSelectedMenuItemIndex(-1)}
|
||||
/>
|
||||
)}
|
||||
<BottomNavigation
|
||||
className={classes.root}
|
||||
data-testid="mobile-sidebar-root"
|
||||
>
|
||||
{sidebarGroups}
|
||||
</BottomNavigation>
|
||||
</MobileSidebarContext.Provider>
|
||||
</SidebarContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -76,6 +76,10 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
color: theme.palette.navigation.color,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
display: 'block',
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
fontSize: '14px',
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -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<SidebarContextType>({
|
||||
isOpen: false,
|
||||
setOpen: _open => {},
|
||||
});
|
||||
|
||||
export type SidebarItemWithSubmenuContextType = {
|
||||
|
||||
Reference in New Issue
Block a user