diff --git a/CHANGELOG.md b/CHANGELOG.md index b23aec6e6..6b68a94ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable, unreleased changes to this project will be documented in this file. - create Apps - #599 by @AlicjaSzu - Refactor authorization - #624 by @dominik-zeglen - Fix minor bugs - #628 by @dominik-zeglen +- Add navigator button - #635 by @dominik-zeglen ## 2.10.1 diff --git a/assets/images/navigator.svg b/assets/images/navigator.svg new file mode 100644 index 000000000..dbbde3b2f --- /dev/null +++ b/assets/images/navigator.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 92fbc001a..204f492b7 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1512,6 +1512,9 @@ "src_dot_components_dot_MultiSelectField_dot_4205644805": { "string": "No results found" }, + "src_dot_components_dot_NavigatorButton_dot_154826674": { + "string": "Navigator" + }, "src_dot_components_dot_Navigator_dot_1116468870": { "context": "navigator placeholder", "string": "Order Number" diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index d521bb9b1..bbfbf4475 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -28,6 +28,8 @@ import useRouter from "use-react-router"; import Container from "../Container"; import ErrorPage from "../ErrorPage"; +import Navigator from "../Navigator"; +import NavigatorButton from "../NavigatorButton/NavigatorButton"; import AppActionContext from "./AppActionContext"; import AppHeaderContext from "./AppHeaderContext"; import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts"; @@ -310,6 +312,7 @@ const AppLayout: React.FC = ({ children }) => { const intl = useIntl(); const [appState, dispatchAppState] = useAppState(); const { location } = useRouter(); + const [isNavigatorVisible, setNavigatorVisibility] = React.useState(false); const menuStructure = createMenuStructure(intl); const configurationMenu = createConfigurationMenu(intl); @@ -356,170 +359,184 @@ const AppLayout: React.FC = ({ children }) => { }; return ( - - -
-
- setDrawerState(false)} - open={isDrawerOpened} - small={!isMenuSmall} - > -
+ + + +
+
+ setDrawerState(false)} + open={isDrawerOpened} + small={!isMenuSmall} > - -
-
- +
-
- - -
-
- {appState.loading ? ( - - ) : ( -
- )} -
-
- -
-
setDrawerState(!isDrawerOpened)} - > - - - - -
-
-
-
- -
- - ) - } - classes={{ - avatar: classes.avatar - }} - className={classes.userChip} - label={ - <> - {user.email} - - - } - onClick={() => setMenuState(!isMenuOpened)} - data-test="userMenu" + +
+ +
+
+ + +
+
+ {appState.loading ? ( + + ) : ( +
+ )} +
+
+ +
+
setDrawerState(!isDrawerOpened)} + > + + + + +
+
+
+
+ - - {({ TransitionProps, placement }) => ( - - - setMenuState(false)} - mouseEvent="onClick" - > - - - - - - - - - - - - )} - + setNavigatorVisibility(true)} + /> +
+ + ) + } + classes={{ + avatar: classes.avatar + }} + className={classes.userChip} + label={ + <> + {user.email} + + + } + onClick={() => setMenuState(!isMenuOpened)} + data-test="userMenu" + /> + + {({ TransitionProps, placement }) => ( + + + setMenuState(false)} + mouseEvent="onClick" + > + + + + + + + + + + + + )} + +
-
- + +
+
+ {appState.error + ? appState.error === "unhandled" && ( + + ) + : children} +
-
- {appState.error - ? appState.error === "unhandled" && ( - - ) - : children} -
+
-
-
- - + + + ); }; diff --git a/src/components/Navigator/Navigator.tsx b/src/components/Navigator/Navigator.tsx index 07fc647b8..569eede26 100644 --- a/src/components/Navigator/Navigator.tsx +++ b/src/components/Navigator/Navigator.tsx @@ -60,8 +60,12 @@ const useStyles = makeStyles( } ); -const Navigator: React.FC = () => { - const [visible, setVisible] = React.useState(false); +export interface NavigatorProps { + visible: boolean; + setVisibility: (state: boolean) => void; +} + +const Navigator: React.FC = ({ visible, setVisibility }) => { const input = React.useRef(null); const [query, mode, change, actions] = useQuickSearch(visible, input); const intl = useIntl(); @@ -76,7 +80,7 @@ const Navigator: React.FC = () => { React.useEffect(() => { hotkeys(navigatorHotkey, event => { event.preventDefault(); - setVisible(!visible); + setVisibility(!visible); }); if (cmp(APP_VERSION, "2.1.0") !== 1 && !notifiedAboutNavigator) { @@ -110,7 +114,7 @@ const Navigator: React.FC = () => { setVisible(false)} + onClose={() => setVisibility(false)} >
@@ -122,7 +126,7 @@ const Navigator: React.FC = () => { onSelect={(item: QuickSearchAction) => { const shouldRemainVisible = item.onClick(); if (!shouldRemainVisible) { - setVisible(false); + setVisibility(false); } }} onInputValueChange={value => diff --git a/src/components/NavigatorButton/NavigatorButton.stories.tsx b/src/components/NavigatorButton/NavigatorButton.stories.tsx new file mode 100644 index 000000000..95e7f0881 --- /dev/null +++ b/src/components/NavigatorButton/NavigatorButton.stories.tsx @@ -0,0 +1,12 @@ +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import NavigatorButton from "./NavigatorButton"; + +storiesOf("Generics / NavigatorButton", module) + .addDecorator(Decorator) + .addDecorator(CardDecorator) + .add("mac", () => ) + .add("other", () => ); diff --git a/src/components/NavigatorButton/NavigatorButton.tsx b/src/components/NavigatorButton/NavigatorButton.tsx new file mode 100644 index 000000000..d35b87668 --- /dev/null +++ b/src/components/NavigatorButton/NavigatorButton.tsx @@ -0,0 +1,153 @@ +import navigatorIcon from "@assets/images/navigator.svg"; +import Grow from "@material-ui/core/Grow"; +import IconButton, { IconButtonProps } from "@material-ui/core/IconButton"; +import Paper from "@material-ui/core/Paper"; +import Popper from "@material-ui/core/Popper"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import classNames from "classnames"; +import React from "react"; +import ReactSVG from "react-inlinesvg"; +import { FormattedMessage } from "react-intl"; + +const useStyles = makeStyles( + theme => { + const triangle = (color: string, width: number) => ({ + borderBottom: `${width}px solid ${color}`, + borderLeft: `${width}px solid transparent`, + borderRight: `${width}px solid transparent`, + height: 0, + width: 0 + }); + + return { + keyTile: { + "&:first-child": { + marginLeft: theme.spacing() + }, + alignItems: "center", + background: theme.palette.background.default, + borderRadius: 8, + display: "inline-flex", + height: 32, + justifyContent: "center", + marginLeft: theme.spacing(0.5), + padding: theme.spacing(), + width: 32 + }, + keyTileLabel: { + verticalAlign: "middle" + }, + paper: { + "&:after": { + ...triangle(theme.palette.background.paper, 7), + content: "''", + left: theme.spacing(2) + 2, + position: "absolute", + top: -theme.spacing() + 1 + }, + "&:before": { + ...triangle(theme.palette.divider, 8), + content: "''", + left: theme.spacing(2) + 1, + position: "absolute", + top: -theme.spacing() + }, + border: `1px solid ${theme.palette.divider}`, + borderRadius: 6, + marginTop: theme.spacing(2), + padding: theme.spacing(2), + position: "relative" + }, + + root: { + "& path": { + color: theme.palette.primary.main + }, + "&:not(:hover)": { + backgroundColor: theme.palette.background.paper + }, + border: `1px solid ${theme.palette.divider}`, + height: 40, + marginRight: theme.spacing(2), + width: 40 + } + }; + }, + { + name: "NavigatorButton" + } +); + +export interface NavigatorButtonProps extends IconButtonProps { + isMac: boolean; +} + +const NavigatorButton: React.FC = ({ + className, + isMac, + ...props +}) => { + const classes = useStyles({}); + const helperTimer = React.useRef(null); + const [helperVisibility, setHelperVisibility] = React.useState(false); + const anchor = React.useRef(); + + const setHelper = () => { + helperTimer.current = setTimeout(() => setHelperVisibility(true), 2 * 1000); + }; + + const clearHelper = () => { + if (helperTimer.current) { + clearTimeout(helperTimer.current); + helperTimer.current = null; + } + setHelperVisibility(false); + }; + + return ( + <> + + + + + {({ TransitionProps, placement }) => ( + + + +
+ + {isMac ? "⌘" : "Ctrl"} + +
+
+ K +
+
+
+ )} +
+ + ); +}; + +NavigatorButton.displayName = "NavigatorButton"; +export default NavigatorButton; diff --git a/src/index.tsx b/src/index.tsx index f684c326c..9317abeea 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,3 @@ -import Navigator from "@saleor/components/Navigator"; import useAppState from "@saleor/hooks/useAppState"; import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory"; import { ApolloClient } from "apollo-client"; @@ -139,7 +138,6 @@ const Routes: React.FC = () => { {isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? ( - dispatchAppState({ diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 93ef683e4..165558e69 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -4670,6 +4670,66 @@ exports[`Storyshots Generics / Multiple select with autocomplete no data 1`] = `
`; +exports[`Storyshots Generics / NavigatorButton mac 1`] = ` +
+
+
+ +
+
+
+`; + +exports[`Storyshots Generics / NavigatorButton other 1`] = ` +
+
+
+ +
+
+
+`; + exports[`Storyshots Generics / PageHeader with title 1`] = `