Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI V2: header #696

Merged
merged 21 commits into from
Dec 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 25 additions & 36 deletions frontend/packages/core/src/AppLayout/header.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,33 @@
import React from "react";
import { Link } from "react-router-dom";
import {
AppBar as MuiAppBar,
Box,
Divider as MuiDivider,
IconButton,
Toolbar,
Typography,
} from "@material-ui/core";
import styled from "@emotion/styled";
import { AppBar as MuiAppBar, Box, Grid, IconButton, Toolbar, Typography } from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import styled from "styled-components";

import Drawer from "./drawer";
import Logo from "./logo";
import Notifications from "./notifications";
import SearchField from "./search";
import { UserInformation } from "./user";

const AppBar = styled(MuiAppBar)`
${({ theme }) => `
min-width: fit-content;
background-color: ${theme.palette.secondary.main};
color: ${theme.palette.primary.main};
min-width: fit-content;
`}
`;
// TODO (sperry): make header responsive for small devices
const AppBar = styled(MuiAppBar)({
minWidth: "fit-content",
dschaller marked this conversation as resolved.
Show resolved Hide resolved
background: "linear-gradient(90deg, #38106b 4.58%, #131c5f 89.31%)",
});

const MenuButton = styled(IconButton)`
padding: 12px;
margin-left: -12px;
`;
const MenuButton = styled(IconButton)({
padding: "12px",
marginLeft: "-12px",
});

const Title = styled(Typography)`
margin-right: 25px;
font-weight: bolder;
`;

const Divider = styled(MuiDivider)`
${({ theme }) => `
background-color: ${theme.palette.primary.main};
margin: 16px 8px;
`}
`;
// TODO (sperry): remove marginRight in new search bar design
const Title = styled(Typography)({
margin: "12px 25px 12px 8px",
fontWeight: "bold",
fontSize: "20px",
color: "rgba(255, 255, 255, 0.87)",
});

const Header: React.FC = () => {
const [drawerOpen, setDrawerOpen] = React.useState(false);
Expand Down Expand Up @@ -68,20 +55,22 @@ const Header: React.FC = () => {

return (
<>
<AppBar position="static" color="secondary">
<AppBar position="static" elevation={0}>
<Toolbar>
<MenuButton onClick={openDrawer} edge="start" color="primary" data-qa="menuBtn">
<MenuIcon />
</MenuButton>
scarlettperry marked this conversation as resolved.
Show resolved Hide resolved
<Link to="/">
<Logo />
</Link>
<Divider orientation="vertical" flexItem />
<Title variant="h5">clutch</Title>
<Title>clutch</Title>
<Box />
<SearchField />
<Box />
<UserInformation />
<Grid container alignItems="center" justify="flex-end">
scarlettperry marked this conversation as resolved.
Show resolved Hide resolved
<Notifications />
<UserInformation />
scarlettperry marked this conversation as resolved.
Show resolved Hide resolved
</Grid>
</Toolbar>
</AppBar>
<Drawer open={drawerOpen} onClose={onDrawerClose} />
Expand Down
19 changes: 11 additions & 8 deletions frontend/packages/core/src/AppLayout/logo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import styled, { keyframes } from "styled-components";
import { keyframes } from "@emotion/react";
import styled from "@emotion/styled";

const rotate = keyframes`
from {
Expand All @@ -10,13 +11,15 @@ const rotate = keyframes`
}
`;

const StyledSvg = styled.svg`
height: 40px;
width: 40px;
&:hover {
animation: ${rotate} 5s linear;
}
`;
const StyledSvg = styled.svg({
height: "48px",
width: "48px",
padding: "8px",
"&:hover": {
animation: `${rotate} 5s linear`,
},
verticalAlign: "middle",
});

const Logo: React.FC = () => (
<StyledSvg id="logo" viewBox="0 0 250 250">
Expand Down
86 changes: 86 additions & 0 deletions frontend/packages/core/src/AppLayout/notifications.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from "react";
import styled from "@emotion/styled";
import {
Box,
ClickAwayListener,
Grow as MuiGrow,
IconButton,
MenuList,
Paper,
Popper,
} from "@material-ui/core";
import NotificationsIcon from "@material-ui/icons/Notifications";

const StyledNotificationsIcon = styled(IconButton)({
color: "#ffffff",
margin: "8px",
padding: "12px",
"&:hover": {
background: "#2d3db4",
},
"&:active": {
background: "#2938a5",
},
});

const Grow = styled(MuiGrow)((props: { placement: string }) => ({
transformOrigin: props.placement,
}));

// TODO (sperry): add interface to render menu items
const Notifications: React.FC = () => {
dschaller marked this conversation as resolved.
Show resolved Hide resolved
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);

const handleToggle = () => {
setOpen(!open);
};

const handleClose = event => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};

function handleListKeyDown(event) {
if (event.key === "Tab") {
event.preventDefault();
setOpen(false);
}
}

return (
<Box>
<StyledNotificationsIcon
ref={anchorRef}
edge="end"
aria-controls={open ? "notification-options" : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<NotificationsIcon />
</StyledNotificationsIcon>
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
placement={placement === "bottom" ? "center top" : "center bottom"}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList
autoFocusItem={open}
id="notification-options"
onKeyDown={handleListKeyDown}
/>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</Box>
);
};

export default Notifications;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be useful to have a notifications story so we can test the whole component (e.g. the popper and such) in isolation from the header.

Copy link
Contributor Author

@scarlettperry scarlettperry Dec 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed up a notifications story. We won't see the popper b/c we don't have menu items but we can test the hover/pressed states in isolation.

RE: #696 (comment), do you mean a simple interface like text: string? was thinking we'd implement the menu in the future. but if we're planning to use the avatar menu design, we can conditionally render a menu if there are items to show. lmk what you'd prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! I was thinking that we could have an interface for the component where it takes in the notifications that we want to render. I know we don't have designs for this yet so feel free to punt but ideally we pass in notifications that would allow us to test the component. Not blocking at this point but something to keep in mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay got it! will leave a to-do, I'll follow up to add an interface. I think the metadata table interface would be a good one to pattern

27 changes: 27 additions & 0 deletions frontend/packages/core/src/AppLayout/stories/header.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from "react";
import { MemoryRouter } from "react-router";
import type { Meta } from "@storybook/react";

import { ApplicationContext } from "../../Contexts/app-context";
import Header from "../header";

export default {
title: "Core/AppLayout/Header",
component: Header,
decorators: [
scarlettperry marked this conversation as resolved.
Show resolved Hide resolved
() => (
<MemoryRouter>
<Header />
</MemoryRouter>
),
StoryFn => {
return (
<ApplicationContext.Provider value={{ workflows: [] }}>
<StoryFn />
</ApplicationContext.Provider>
);
},
],
} as Meta;

export const Primary: React.FC<{}> = () => <Header />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from "react";
import type { Meta } from "@storybook/react";

import Notifications from "../notifications";

export default {
title: "Core/AppLayout/Notifications",
component: Notifications,
parameters: {
backgrounds: {
default: "header blue",
values: [{ name: "header blue", value: "#131C5F" }],
},
},
} as Meta;

export const Primary: React.FC<{}> = () => <Notifications />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from "react";
import { Grid } from "@material-ui/core";
import type { Meta } from "@storybook/react";

import { UserInformation } from "../user";

export default {
title: "Core/AppLayout/User Information",
component: UserInformation,
parameters: {
backgrounds: {
default: "header blue",
values: [{ name: "header blue", value: "#131C5F" }],
},
},
} as Meta;

const Template = () => (
<Grid container alignItems="center" justify="flex-end">
<UserInformation />
</Grid>
);
export const Primary = Template.bind({});
Loading