Add mobile drawer menu

This commit is contained in:
dominik-zeglen 2020-09-11 15:58:15 +02:00
parent 989f66f7de
commit bf752d1ee7
7 changed files with 290 additions and 9 deletions

View file

@ -16,6 +16,7 @@ import ErrorPage from "../ErrorPage";
import Navigator from "../Navigator";
import NavigatorButton from "../NavigatorButton/NavigatorButton";
import SideBar from "../SideBar";
import SideBarDrawer from "../SideBarDrawer/SideBarDrawer";
import UserChip from "../UserChip";
import AppActionContext from "./AppActionContext";
import AppHeaderContext from "./AppHeaderContext";
@ -162,7 +163,15 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
<Container>
<div className={classes.header}>
{isMdUp && <div ref={appHeaderAnchor} />}
{!isMdUp && <>=</>}
{!isMdUp && (
<SideBarDrawer
menuItems={menuStructure}
location={location.pathname}
user={user}
renderConfigure={renderConfigure}
onMenuItemClick={navigate}
/>
)}
<div className={classes.spacer} />
<div className={classes.userBar}>
<ThemeSwitch

View file

@ -9,7 +9,7 @@ import { sectionNames } from "@saleor/intl";
import classNames from "classnames";
import React from "react";
import SVG from "react-inlinesvg";
import { useIntl } from "react-intl";
import { IntlShape, useIntl } from "react-intl";
import { IMenuItem } from "../AppLayout/menuStructure";
import ExpandButton from "./ExpandButton";
@ -55,6 +55,14 @@ export interface IActiveSubMenu {
label: string | null;
}
export const getConfigureMenuItem = (intl: IntlShape): IMenuItem => ({
ariaLabel: "configure",
icon: configurationIcon,
label: intl.formatMessage(sectionNames.configuration),
testingContextId: "configure",
url: configurationMenuUrl
});
const SideBar: React.FC<SideBarProps> = ({
location,
menuItems,
@ -65,6 +73,7 @@ const SideBar: React.FC<SideBarProps> = ({
const classes = useStyles({});
const [isShrunk, setShrink] = useLocalStorage("isMenuSmall", false);
const intl = useIntl();
const configureMenuItem = getConfigureMenuItem(intl);
return (
<div
@ -107,13 +116,7 @@ const SideBar: React.FC<SideBarProps> = ({
)
}
isMenuShrunk={isShrunk}
menuItem={{
ariaLabel: "configure",
icon: configurationIcon,
label: intl.formatMessage(sectionNames.configuration),
testingContextId: "configure",
url: configurationMenuUrl
}}
menuItem={configureMenuItem}
onClick={onMenuItemClick}
/>
)}

View file

@ -0,0 +1,32 @@
import Typography from "@material-ui/core/Typography";
import { UseNavigatorResult } from "@saleor/hooks/useNavigator";
import React from "react";
import SVG from "react-inlinesvg";
import { IMenuItem } from "../AppLayout/menuStructure";
import useStyles from "./styles";
export interface MenuItemBtnProps {
menuItem: IMenuItem;
onClick: UseNavigatorResult;
}
const MenuItemBtn: React.FC<MenuItemBtnProps> = ({ menuItem, onClick }) => {
const classes = useStyles({});
return (
<button
className={classes.menuItemBtn}
data-test="menu-item-label"
data-test-id={menuItem.testingContextId}
onClick={() => onClick(menuItem.url)}
>
{menuItem.icon && <SVG className={classes.icon} src={menuItem.icon} />}
<Typography aria-label={menuItem.ariaLabel} className={classes.label}>
{menuItem.label}
</Typography>
</button>
);
};
MenuItemBtn.displayName = "MenuItemBtn";
export default MenuItemBtn;

View file

@ -0,0 +1,37 @@
import { staffMember } from "@saleor/staff/fixtures";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import { config } from "@test/intl";
import React from "react";
import { createIntl } from "react-intl";
import createMenuStructure from "../AppLayout/menuStructure";
import SideBarDrawer from "./SideBarDrawer";
const intl = createIntl(config);
const user = {
__typename: staffMember.__typename,
avatar: {
__typename: staffMember.avatar.__typename,
url: staffMember.avatar.url
},
email: staffMember.email,
firstName: "Adam Evan",
id: staffMember.id,
isStaff: true,
lastName: "Newton",
note: null,
userPermissions: staffMember.userPermissions
};
storiesOf("Generics / Mobile Side Menu", module)
.addDecorator(Decorator)
.add("default", () => (
<SideBarDrawer
location="/"
menuItems={createMenuStructure(intl)}
onMenuItemClick={() => undefined}
renderConfigure={true}
user={user}
/>
));

View file

@ -0,0 +1,132 @@
import logoLight from "@assets/images/logo-sidebar-light.svg";
import { Typography } from "@material-ui/core";
import Drawer from "@material-ui/core/Drawer";
import ArrowLeftIcon from "@material-ui/icons/ArrowLeft";
import MenuIcon from "@material-ui/icons/Menu";
import classNames from "classnames";
import React from "react";
import SVG from "react-inlinesvg";
import { useIntl } from "react-intl";
import { IMenuItem } from "../AppLayout/menuStructure";
import { getConfigureMenuItem, SideBarProps } from "../SideBar/SideBar";
import SquareButton from "../SquareButton";
import MenuItemBtn from "./MenuItemBtn";
import useStyles from "./styles";
export type SideBarDrawerProps = SideBarProps;
const SideBarDrawer: React.FC<SideBarDrawerProps> = ({
menuItems,
onMenuItemClick,
renderConfigure,
user
}) => {
const [isOpened, setOpened] = React.useState(false);
const classes = useStyles({});
const intl = useIntl();
const [activeMenu, setActiveMenu] = React.useState<IMenuItem>(null);
const [showSubmenu, setShowSubmenu] = React.useState(false);
const container = React.useRef<HTMLDivElement>(null);
const configureMenuItem = getConfigureMenuItem(intl);
const handleMenuItemClick = (url: string) => {
setOpened(false);
setShowSubmenu(false);
onMenuItemClick(url);
};
const handleMenuItemWithChildrenClick = (menuItem: IMenuItem) => {
setActiveMenu(menuItem);
setShowSubmenu(true);
container.current.scrollTo({
top: 0
});
};
return (
<>
<SquareButton onClick={() => setOpened(true)}>
<MenuIcon />
</SquareButton>
<Drawer
classes={{
paper: classes.root
}}
open={isOpened}
onClose={() => setOpened(false)}
>
<div
className={classNames(classes.container, {
[classes.containerSubMenu]: showSubmenu
})}
ref={container}
>
<div
className={classNames(classes.innerContainer, {
[classes.secondaryContentActive]: showSubmenu
})}
>
<div className={classes.content}>
<SVG className={classes.logo} src={logoLight} />
{menuItems.map(menuItem => {
if (
menuItem.permission &&
!user.userPermissions
.map(perm => perm.code)
.includes(menuItem.permission)
) {
return null;
}
return (
<MenuItemBtn
menuItem={menuItem}
onClick={
menuItem.children
? () => handleMenuItemWithChildrenClick(menuItem)
: handleMenuItemClick
}
key={menuItem.ariaLabel}
/>
);
})}
{renderConfigure && (
<MenuItemBtn
menuItem={configureMenuItem}
onClick={handleMenuItemClick}
/>
)}
</div>
{activeMenu && (
<div className={classes.content}>
<div className={classes.subMenuTopBar}>
<div className={classes.activeMenuLabel}>
<SVG className={classes.icon} src={activeMenu.icon} />
<Typography className={classes.label}>
{activeMenu.label}
</Typography>
</div>
<SquareButton onClick={() => setShowSubmenu(false)}>
<ArrowLeftIcon />
</SquareButton>
</div>
{activeMenu.children.map(subMenuItem => (
<MenuItemBtn
menuItem={subMenuItem}
onClick={handleMenuItemClick}
key={subMenuItem.ariaLabel}
/>
))}
</div>
)}
</div>
</div>
</Drawer>
</>
);
};
SideBarDrawer.displayName = "SideBarDrawer";
export default SideBarDrawer;

View file

View file

@ -0,0 +1,68 @@
import makeStyles from "@material-ui/core/styles/makeStyles";
const useStyles = makeStyles(
theme => ({
activeMenuLabel: {
display: "flex"
},
container: {
overflowX: "hidden",
width: "100%"
},
containerSubMenu: {
"&$container": {
overflow: "hidden"
}
},
content: {
width: "50%"
},
icon: {
marginRight: theme.spacing(2)
},
innerContainer: {
display: "flex",
position: "relative",
right: 0,
transition: theme.transitions.duration.short + "ms",
width: "200%"
},
label: {
fontWeight: "bold"
},
logo: {
display: "block",
marginBottom: theme.spacing(4)
},
menuItemBtn: {
alignItems: "center",
background: "none",
border: "none",
color: theme.palette.text.secondary,
display: "flex",
marginBottom: theme.spacing(3),
padding: 0
},
root: {
background: theme.palette.background.default,
borderBottomRightRadius: 32,
borderTopRightRadius: 32,
padding: theme.spacing(3),
width: 260
},
secondaryContentActive: {
right: "100%"
},
subMenuTopBar: {
alignItems: "center",
display: "flex",
justifyContent: "space-between",
marginBottom: theme.spacing(3)
}
}),
{
name: "SideBarDrawer"
}
);
export default useStyles;