-
- {!isMdUp && (
-
- )}
-
-
-
+ className={classNames(classes.menuIcon, {
+ [classes.menuIconOpen]: isDrawerOpened,
+ [classes.menuIconDark]: isDark
+ })}
+ onClick={() => setDrawerState(!isDrawerOpened)}
+ >
+
+
+
+
+
+
+
+
+
+
setNavigatorVisibility(true)}
/>
-
- navigate(staffMemberDetailsUrl(user.id))
+
+
+
+ )
}
- user={user}
+ classes={{
+ avatar: classes.avatar
+ }}
+ className={classes.userChip}
+ label={
+ <>
+ {user.email}
+
+ >
+ }
+ onClick={() => setMenuState(!isMenuOpened)}
+ data-test="userMenu"
/>
+
+ {({ TransitionProps, placement }) => (
+
+
+ setMenuState(false)}
+ mouseEvent="onClick"
+ >
+
+
+
+
+ )}
+
diff --git a/src/components/AppLayout/MenuList.tsx b/src/components/AppLayout/MenuList.tsx
new file mode 100644
index 000000000..05f6fa971
--- /dev/null
+++ b/src/components/AppLayout/MenuList.tsx
@@ -0,0 +1,369 @@
+import configureIcon from "@assets/images/menu-configure-icon.svg";
+import { makeStyles } from "@material-ui/core/styles";
+import Typography from "@material-ui/core/Typography";
+import { User } from "@saleor/fragments/types/User";
+import useTheme from "@saleor/hooks/useTheme";
+import { sectionNames } from "@saleor/intl";
+import classNames from "classnames";
+import React from "react";
+import SVG from "react-inlinesvg";
+import { FormattedMessage, useIntl } from "react-intl";
+import { matchPath } from "react-router";
+
+import {
+ configurationMenuUrl,
+ createConfigurationMenu
+} from "../../configuration";
+import { createHref } from "../../misc";
+import { orderDraftListUrl, orderListUrl } from "../../orders/urls";
+import MenuNested from "./MenuNested";
+import { IMenuItem } from "./menuStructure";
+
+const useStyles = makeStyles(
+ theme => ({
+ menuIcon: {
+ "& svg": {
+ height: 32,
+ width: 32
+ },
+ display: "inline-block",
+ position: "relative",
+ top: 8
+ },
+ menuIconDark: {
+ "& path": {
+ fill: theme.palette.common.white
+ }
+ },
+ menuIconSmall: {
+ left: -5
+ },
+ menuIsActive: {
+ boxShadow: "0px 0px 12px 1px rgba(0,0,0,0.2)"
+ },
+ menuItemHover: {
+ "& p": {
+ fontSize: 14,
+ transition: "color 0.5s ease, opacity 0.3s ease-out"
+ },
+ "& path": {
+ transition: "fill 0.5s ease"
+ },
+ "&:hover": {
+ "& p": {
+ color: theme.palette.primary.main
+ },
+ "& path": {
+ fill: theme.palette.primary.main
+ },
+ "&:before": {
+ borderLeft: `solid 2px ${theme.palette.primary.main}`,
+ content: "''",
+ height: 33,
+ left: -20,
+ position: "absolute",
+ top: 8
+ },
+ color: theme.palette.primary.main
+ },
+ cursor: "pointer",
+ position: "relative"
+ },
+ menuList: {
+ display: "flex",
+ flexDirection: "column",
+ height: "100%",
+ marginLeft: theme.spacing(4),
+ marginTop: theme.spacing(2),
+ paddingBottom: theme.spacing(3)
+ },
+ menuListItem: {
+ alignItems: "center",
+ display: "block",
+ marginBottom: theme.spacing(5),
+ paddingLeft: 0,
+ textDecoration: "none",
+ transition: theme.transitions.duration.standard + "ms"
+ },
+ menuListItemActive: {
+ "& $menuListItemText": {
+ color: theme.palette.primary.main
+ },
+ "& path": {
+ color: theme.palette.primary.main,
+ fill: theme.palette.primary.main
+ }
+ },
+ menuListItemOpen: {
+ "&:after": {
+ borderBottom: `10px solid transparent`,
+ borderLeft: `10px solid ${theme.palette.background.paper}`,
+ borderTop: `10px solid transparent`,
+ content: "''",
+ height: 0,
+ position: "absolute",
+ right: -30,
+ top: 15,
+ width: 0
+ },
+ "&:before": {
+ borderLeft: `solid 2px ${theme.palette.primary.main}`,
+ content: "''",
+ height: 33,
+ left: -20,
+ position: "absolute",
+ top: 8
+ },
+ position: "relative"
+ },
+ menuListItemText: {
+ "&:hover": {
+ color: theme.palette.primary.main
+ },
+ bottom: 0,
+ cursor: "pointer",
+ fontSize: "1rem",
+ fontWeight: 500,
+ left: 30,
+ opacity: 1,
+ paddingLeft: 16,
+ position: "absolute",
+ textTransform: "uppercase",
+ transition: "opacity 0.5s ease"
+ },
+ menuListItemTextHide: {
+ bottom: 0,
+ left: 30,
+ opacity: 0,
+ position: "absolute"
+ },
+ subMenu: {
+ padding: "0 15px"
+ },
+ subMenuDrawer: {
+ background: "#000",
+ cursor: "pointer",
+ height: "100vh",
+ left: 0,
+ opacity: 0.2,
+ position: "absolute",
+ top: 0,
+ width: 0,
+ zIndex: -2
+ },
+ subMenuDrawerOpen: {
+ width: `100vw`
+ }
+ }),
+ { name: "MenuList" }
+);
+
+interface MenuListProps {
+ className?: string;
+ menuItems: IMenuItem[];
+ isMenuSmall: boolean;
+ location: string;
+ user: User;
+ renderConfigure: boolean;
+ onMenuItemClick: (url: string, event: React.MouseEvent
) => void;
+}
+
+export interface IActiveSubMenu {
+ isActive: boolean;
+ label: string | null;
+}
+
+const MenuList: React.FC = props => {
+ const {
+ className,
+ menuItems,
+ isMenuSmall,
+ location,
+ user,
+ renderConfigure,
+ onMenuItemClick
+ } = props;
+
+ const classes = useStyles(props);
+
+ const { isDark } = useTheme();
+ const [activeSubMenu, setActiveSubMenu] = React.useState({
+ isActive: false,
+ label: null
+ });
+ const intl = useIntl();
+
+ const configurationMenu = createConfigurationMenu(intl).map(menu => {
+ menu.menuItems.map(item =>
+ user.userPermissions.map(perm => perm.code).includes(item.permission)
+ );
+ });
+
+ const handleSubMenu = itemLabel => {
+ setActiveSubMenu({
+ isActive:
+ itemLabel === activeSubMenu.label ? !activeSubMenu.isActive : true,
+ label: itemLabel
+ });
+ };
+
+ const closeSubMenu = (menuItemUrl, event) => {
+ setActiveSubMenu({
+ isActive: false,
+ label: null
+ });
+ if (menuItemUrl && event) {
+ onMenuItemClick(menuItemUrl, event);
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ };
+
+ return (
+
+ {/* FIXME: this .split("?")[0] looks gross */}
+ {menuItems.map(menuItem => {
+ const isActive = (menuItem: IMenuItem) =>
+ location.split("?")[0] === orderDraftListUrl().split("?")[0] &&
+ menuItem.url.split("?")[0] === orderListUrl().split("?")[0]
+ ? false
+ : !!matchPath(location.split("?")[0], {
+ exact: menuItem.url.split("?")[0] === "/",
+ path: menuItem.url.split("?")[0]
+ });
+
+ if (
+ menuItem.permission &&
+ !user.userPermissions
+ .map(perm => perm.code)
+ .includes(menuItem.permission)
+ ) {
+ return null;
+ }
+
+ if (!menuItem.url) {
+ const isAnyChildActive = menuItem.children.reduce(
+ (acc, child) => acc || isActive(child),
+ false
+ );
+
+ return (
+
+ );
+};
+
+MenuList.displayName = "MenuList";
+export default MenuList;
diff --git a/src/components/AppLayout/MenuNested.tsx b/src/components/AppLayout/MenuNested.tsx
new file mode 100644
index 000000000..0a64b1892
--- /dev/null
+++ b/src/components/AppLayout/MenuNested.tsx
@@ -0,0 +1,194 @@
+import menuArrowIcon from "@assets/images/menu-arrow-icon.svg";
+import Hidden from "@material-ui/core/Hidden";
+import { makeStyles } from "@material-ui/core/styles";
+import Typography from "@material-ui/core/Typography";
+import useTheme from "@saleor/hooks/useTheme";
+import { createHref } from "@saleor/misc";
+import classNames from "classnames";
+import React from "react";
+import SVG from "react-inlinesvg";
+
+import { drawerNestedMenuWidth, drawerWidthExpandedMobile } from "./consts";
+import { IActiveSubMenu } from "./MenuList";
+import { IMenuItem } from "./menuStructure";
+
+const useStyles = makeStyles(
+ theme => ({
+ menuListNested: {
+ background: theme.palette.background.paper,
+ height: "100vh",
+ position: "absolute",
+ right: 0,
+ top: 0,
+ transition: `right ${theme.transitions.duration.shorter}ms ease`,
+ width: drawerNestedMenuWidth,
+ zIndex: -1
+ },
+ menuListNestedClose: {
+ "& svg": {
+ fill: theme.palette.primary.main,
+ left: 11,
+ position: "relative",
+ top: 1
+ },
+ border: `solid 1px #EAEAEA`,
+ borderRadius: "100%",
+ cursor: "pointer",
+ height: 32,
+ position: "absolute",
+ right: 32,
+ top: 35,
+ transform: "rotate(180deg)",
+ width: 32
+ },
+ menuListNestedCloseDark: {
+ border: `solid 1px #252728`
+ },
+ menuListNestedHide: {
+ opacity: 0
+ },
+ menuListNestedIcon: {
+ "& path": {
+ fill: "initial"
+ },
+ "& svg": { height: 32, position: "relative", top: 7, width: 32 }
+ },
+ menuListNestedIconDark: {
+ "& path": {
+ fill: theme.palette.common.white
+ }
+ },
+ menuListNestedItem: {
+ "&:hover": {
+ "& p": {
+ color: theme.palette.primary.main
+ }
+ },
+ display: "block",
+ marginBottom: theme.spacing(2),
+ padding: "0px 30px",
+ textDecoration: "none"
+ },
+ menuListNestedOpen: {
+ [theme.breakpoints.down("sm")]: {
+ right: 0,
+ width: drawerWidthExpandedMobile,
+ zIndex: 2
+ },
+ right: -drawerNestedMenuWidth,
+ width: drawerNestedMenuWidth,
+ zIndex: -1
+ },
+ subHeader: {
+ borderBottom: "solid 1px #EAEAEA",
+ margin: "30px",
+ marginBottom: 39,
+ paddingBottom: 22
+ },
+ subHeaderDark: {
+ borderBottom: "solid 1px #252728"
+ },
+ subHeaderTitle: {
+ [theme.breakpoints.up("md")]: {
+ paddingLeft: 0
+ },
+ display: "inline",
+ paddingLeft: 10
+ }
+ }),
+ { name: "MenuNested" }
+);
+
+export interface MenuNestedProps {
+ activeItem: IActiveSubMenu;
+ ariaLabel: string;
+ closeSubMenu: ({ isActive, label }: IActiveSubMenu) => void;
+ icon: string;
+ menuItem: IMenuItem;
+ title: string;
+ handleSubMenu: (itemLabel: string) => void;
+ onMenuItemClick: (url: string, event: React.MouseEvent
) => void;
+}
+
+const MenuNested: React.FC = props => {
+ const {
+ activeItem,
+ ariaLabel,
+
+ closeSubMenu,
+ icon,
+ menuItem,
+ onMenuItemClick,
+ title
+ } = props;
+ const classes = useStyles(props);
+
+ const menuItems = menuItem.children;
+ const { isDark } = useTheme();
+ const closeMenu = (menuItemUrl, event) => {
+ onMenuItemClick(menuItemUrl, event);
+ closeSubMenu({
+ isActive: false,
+ label: null
+ });
+ event.stopPropagation();
+ event.preventDefault();
+ };
+ return (
+ <>
+
+ >
+ );
+};
+
+MenuNested.displayName = "MenuNested";
+export default MenuNested;
diff --git a/src/components/AppLayout/ResponsiveDrawer.tsx b/src/components/AppLayout/ResponsiveDrawer.tsx
new file mode 100644
index 000000000..49baa1e7f
--- /dev/null
+++ b/src/components/AppLayout/ResponsiveDrawer.tsx
@@ -0,0 +1,74 @@
+import Drawer from "@material-ui/core/Drawer";
+import Hidden from "@material-ui/core/Hidden";
+import { makeStyles } from "@material-ui/core/styles";
+import React from "react";
+
+import {
+ drawerWidth,
+ drawerWidthExpanded,
+ drawerWidthExpandedMobile
+} from "./consts";
+
+const useStyles = makeStyles(
+ theme => ({
+ drawerDesktop: {
+ backgroundColor: theme.palette.background.paper,
+ border: "none",
+ height: "100vh",
+ overflow: "visible",
+ padding: 0,
+ position: "fixed" as "fixed",
+ transition: "width 0.3s ease",
+ width: drawerWidthExpanded
+ },
+ drawerDesktopSmall: {
+ overflow: "visible",
+ transition: "width 0.2s ease",
+ width: drawerWidth
+ },
+ drawerMobile: {
+ width: drawerWidthExpandedMobile
+ }
+ }),
+ { name: "ResponsiveDrawer" }
+);
+
+interface ResponsiveDrawerProps {
+ children?: React.ReactNode;
+ open: boolean;
+ small: boolean;
+ onClose?();
+}
+
+const ResponsiveDrawer: React.FC = props => {
+ const { children, onClose, open, small } = props;
+
+ const classes = useStyles(props);
+
+ return (
+ <>
+
+
+ {children}
+
+
+
+
+ {children}
+
+
+ >
+ );
+};
+export default ResponsiveDrawer;
diff --git a/src/components/Navigator/Navigator.tsx b/src/components/Navigator/Navigator.tsx
index 203c39b5e..569eede26 100644
--- a/src/components/Navigator/Navigator.tsx
+++ b/src/components/Navigator/Navigator.tsx
@@ -49,9 +49,6 @@ const useStyles = makeStyles(
overflow: "hidden"
},
root: {
- [theme.breakpoints.down("sm")]: {
- height: "auto"
- },
height: 500,
maxWidth: 600,
outline: 0,
diff --git a/src/components/NavigatorButton/NavigatorButton.tsx b/src/components/NavigatorButton/NavigatorButton.tsx
index 5e3c07341..ad3189280 100644
--- a/src/components/NavigatorButton/NavigatorButton.tsx
+++ b/src/components/NavigatorButton/NavigatorButton.tsx
@@ -66,10 +66,6 @@ const useStyles = makeStyles(
"&:not(:hover)": {
backgroundColor: theme.palette.background.paper
},
- [theme.breakpoints.down("sm")]: {
- border: "none",
- borderRadius: 16
- },
border: `1px solid ${theme.palette.divider}`,
height: 40,
marginRight: theme.spacing(2),
diff --git a/src/components/SaveButtonBar/SaveButtonBar.tsx b/src/components/SaveButtonBar/SaveButtonBar.tsx
index b7a5b6cf0..adf2bdcdb 100644
--- a/src/components/SaveButtonBar/SaveButtonBar.tsx
+++ b/src/components/SaveButtonBar/SaveButtonBar.tsx
@@ -5,7 +5,6 @@ import Portal from "@material-ui/core/Portal";
import { makeStyles } from "@material-ui/core/styles";
import useWindowScroll from "@saleor/hooks/useWindowScroll";
import { buttonMessages } from "@saleor/intl";
-import classNames from "classnames";
import React from "react";
import { useIntl } from "react-intl";
@@ -18,21 +17,12 @@ import Container from "../Container";
const useStyles = makeStyles(
theme => ({
- applyShadow: {
- "&$card": {
- boxShadow: "0px 6px 30px rgba(0, 0, 0, 0.16)"
- }
- },
button: {
marginRight: theme.spacing(1)
},
cancelButton: {
marginRight: theme.spacing(2)
},
- card: {
- boxShadow: "0px 0px 0px rgba(0, 0, 0, 0.16)",
- transition: theme.transitions.duration.shortest + "ms"
- },
content: {
"&:last-child": {
paddingBottom: theme.spacing(2)
@@ -89,7 +79,7 @@ export const SaveButtonBar: React.FC = props => {
const intl = useIntl();
const scrollPosition = useWindowScroll();
const scrolledToBottom =
- scrollPosition.y + window.innerHeight - document.body.scrollHeight >= -5;
+ scrollPosition.y + window.innerHeight >= document.body.scrollHeight;
return (
@@ -98,11 +88,7 @@ export const SaveButtonBar: React.FC = props => {
-
+
{!!onDelete && (
`;
-exports[`Storyshots Generics / Mobile Side Menu default 1`] = `
-
-`;
-
exports[`Storyshots Generics / Money formatting default 1`] = `
`;
-exports[`Storyshots Generics / Square Button default 1`] = `
-
-`;
-
exports[`Storyshots Generics / StatusLabel when error 1`] = `
|
|
| = props => {
onClick={warehouse ? onRowClick(warehouse.id) : undefined}
key={warehouse ? warehouse.id : "skeleton"}
data-test="warehouseEntry"
- data-test-id={warehouse?.name.toLowerCase().replace(" ", "")}
+ data-testid={warehouse?.name.toLowerCase().replace(" ", "")}
>
{maybe(() => warehouse.name, )}
|