chore: remove admin dropdown (#1802)

resolves #1748
This commit is contained in:
Kira Pilot
2022-05-26 16:04:51 -04:00
committed by GitHub
parent 07ebd59e94
commit d4c26d534c
7 changed files with 8 additions and 238 deletions
@@ -1,17 +0,0 @@
import Box from "@material-ui/core/Box"
import { Story } from "@storybook/react"
import React from "react"
import { AdminDropdown } from "./AdminDropdown"
export default {
title: "components/AdminDropdown",
component: AdminDropdown,
}
const Template: Story = () => (
<Box style={{ backgroundColor: "#000", width: 100 }}>
<AdminDropdown />
</Box>
)
export const Example = Template.bind({})
@@ -1,28 +0,0 @@
import { screen } from "@testing-library/react"
import React from "react"
import { history, render } from "../../testHelpers/renderHelpers"
import { AdminDropdown, Language } from "./AdminDropdown"
const renderAndClick = async () => {
render(<AdminDropdown />)
const trigger = await screen.findByText(Language.menuTitle)
trigger.click()
}
describe("AdminDropdown", () => {
describe("when the trigger is clicked", () => {
it("opens the menu", async () => {
await renderAndClick()
expect(screen.getByText(Language.usersLabel)).toBeDefined()
})
})
it("links to the users page", async () => {
await renderAndClick()
const usersLink = screen.getByText(Language.usersLabel).closest("a")
usersLink?.click()
expect(history.location.pathname).toEqual("/users")
})
})
@@ -1,137 +0,0 @@
import ListItem from "@material-ui/core/ListItem"
import ListItemText from "@material-ui/core/ListItemText"
import { fade, makeStyles, Theme } from "@material-ui/core/styles"
import React, { useState } from "react"
import { navHeight } from "../../theme/constants"
import { BorderedMenu } from "../BorderedMenu/BorderedMenu"
import { BorderedMenuRow } from "../BorderedMenuRow/BorderedMenuRow"
import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows"
import { UsersOutlinedIcon } from "../Icons/UsersOutlinedIcon"
export const Language = {
menuTitle: "Admin",
usersLabel: "Users",
usersDescription: "Manage users, roles, and permissions.",
}
const entries = [
{
label: Language.usersLabel,
description: Language.usersDescription,
path: "/users",
Icon: UsersOutlinedIcon,
},
]
export const AdminDropdown: React.FC = () => {
const styles = useStyles()
const [anchorEl, setAnchorEl] = useState<HTMLElement>()
const onClose = () => setAnchorEl(undefined)
const onOpenAdminMenu = (ev: React.MouseEvent<HTMLDivElement>) => setAnchorEl(ev.currentTarget)
return (
<>
<div className={styles.link}>
<ListItem selected={Boolean(anchorEl)} button onClick={onOpenAdminMenu}>
<ListItemText className="no-brace" primary={Language.menuTitle} />
{anchorEl ? <CloseDropdown /> : <OpenDropdown />}
</ListItem>
</div>
<BorderedMenu
anchorEl={anchorEl}
getContentAnchorEl={null}
open={!!anchorEl}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
marginThreshold={0}
variant="admin-dropdown"
onClose={onClose}
>
{entries.map((entry) => (
<BorderedMenuRow
description={entry.description}
Icon={entry.Icon}
key={entry.label}
path={entry.path}
title={entry.label}
variant="narrow"
onClick={() => {
onClose()
}}
/>
))}
</BorderedMenu>
</>
)
}
const useStyles = makeStyles((theme: Theme) => ({
link: {
"&:focus": {
outline: "none",
"& .MuiListItem-button": {
background: fade(theme.palette.primary.light, 0.1),
},
},
"& .MuiListItemText-root": {
display: "flex",
flexDirection: "column",
alignItems: "center",
},
"& .feature-stage-chip": {
position: "absolute",
bottom: theme.spacing(1),
"& .MuiChip-labelSmall": {
fontSize: "10px",
},
},
whiteSpace: "nowrap",
"& .MuiListItem-button": {
height: navHeight,
color: "#A7A7A7",
padding: `0 ${theme.spacing(3)}px`,
"&.Mui-selected": {
background: "transparent",
"& .MuiListItemText-root": {
color: theme.palette.primary.contrastText,
"&:not(.no-brace) .MuiTypography-root": {
position: "relative",
"&::before": {
content: `"{"`,
left: -14,
position: "absolute",
},
"&::after": {
content: `"}"`,
position: "absolute",
right: -14,
},
},
},
},
"&.Mui-focusVisible, &:hover": {
background: "#333",
},
"& .MuiListItemText-primary": {
fontFamily: theme.typography.fontFamily,
fontSize: 16,
fontWeight: 500,
},
},
},
}))
@@ -1,40 +0,0 @@
import { screen, waitFor } from "@testing-library/react"
import React from "react"
import * as API from "../../api/api"
import { renderWithAuth } from "../../testHelpers/renderHelpers"
import { checks } from "../../xServices/auth/authXService"
import { Language as AdminDropdownLanguage } from "../AdminDropdown/AdminDropdown"
import { Navbar } from "./Navbar"
beforeEach(() => {
jest.resetAllMocks()
})
describe("Navbar", () => {
describe("when user has permission to update users", () => {
it("displays the admin menu", async () => {
const checkUserPermissionsSpy = jest.spyOn(API, "checkUserPermissions").mockResolvedValueOnce({
[checks.updateUsers]: true,
})
renderWithAuth(<Navbar />)
// Wait for the request is done
await waitFor(() => expect(checkUserPermissionsSpy).toBeCalledTimes(1))
await screen.findByRole("button", { name: AdminDropdownLanguage.menuTitle })
})
})
describe("when user has NO permission to update users", () => {
it("does not display the admin menu", async () => {
const checkUserPermissionsSpy = jest.spyOn(API, "checkUserPermissions").mockResolvedValueOnce({
[checks.updateUsers]: false,
})
renderWithAuth(<Navbar />)
// Wait for the request is done
await waitFor(() => expect(checkUserPermissionsSpy).toBeCalledTimes(1))
expect(screen.queryByRole("button", { name: AdminDropdownLanguage.menuTitle })).not.toBeInTheDocument()
})
})
})
+2 -7
View File
@@ -1,6 +1,5 @@
import { useActor, useSelector } from "@xstate/react"
import { useActor } from "@xstate/react"
import React, { useContext } from "react"
import { selectPermissions } from "../../xServices/auth/authSelectors"
import { XServiceContext } from "../../xServices/StateContext"
import { NavbarView } from "../NavbarView/NavbarView"
@@ -8,11 +7,7 @@ export const Navbar: React.FC = () => {
const xServices = useContext(XServiceContext)
const [authState, authSend] = useActor(xServices.authXService)
const { me } = authState.context
const permissions = useSelector(xServices.authXService, selectPermissions)
// When we have more options in the admin dropdown we may want to check this
// for more permissions
const displayAdminDropdown = !!permissions?.updateUsers
const onSignOut = () => authSend("SIGN_OUT")
return <NavbarView user={me} onSignOut={onSignOut} displayAdminDropdown={displayAdminDropdown} />
return <NavbarView user={me} onSignOut={onSignOut} />
}
@@ -10,26 +10,26 @@ describe("NavbarView", () => {
}
it("renders content", async () => {
// When
render(<NavbarView user={MockUser} onSignOut={noop} displayAdminDropdown />)
render(<NavbarView user={MockUser} onSignOut={noop} />)
// Then
await screen.findAllByText("Coder", { exact: false })
})
it("workspaces nav link has the correct href", async () => {
render(<NavbarView user={MockUser} onSignOut={noop} displayAdminDropdown />)
render(<NavbarView user={MockUser} onSignOut={noop} />)
const workspacesLink = await screen.findByText(navLanguage.workspaces)
expect((workspacesLink as HTMLAnchorElement).href).toContain("/workspaces")
})
it("templates nav link has the correct href", async () => {
render(<NavbarView user={MockUser} onSignOut={noop} displayAdminDropdown />)
render(<NavbarView user={MockUser} onSignOut={noop} />)
const templatesLink = await screen.findByText(navLanguage.templates)
expect((templatesLink as HTMLAnchorElement).href).toContain("/templates")
})
it("users nav link has the correct href", async () => {
render(<NavbarView user={MockUser} onSignOut={noop} displayAdminDropdown />)
render(<NavbarView user={MockUser} onSignOut={noop} />)
const userLink = await screen.findByText(navLanguage.users)
expect((userLink as HTMLAnchorElement).href).toContain("/users")
})
@@ -42,7 +42,7 @@ describe("NavbarView", () => {
}
// When
render(<NavbarView user={mockUser} onSignOut={noop} displayAdminDropdown />)
render(<NavbarView user={mockUser} onSignOut={noop} />)
// Then
// There should be a 'B' avatar!
@@ -5,14 +5,12 @@ import React from "react"
import { NavLink } from "react-router-dom"
import * as TypesGen from "../../api/typesGenerated"
import { navHeight } from "../../theme/constants"
import { AdminDropdown } from "../AdminDropdown/AdminDropdown"
import { Logo } from "../Icons/Logo"
import { UserDropdown } from "../UserDropdown/UsersDropdown"
export interface NavbarViewProps {
user?: TypesGen.User
onSignOut: () => void
displayAdminDropdown: boolean
}
export const Language = {
@@ -21,7 +19,7 @@ export const Language = {
users: "Users",
}
export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut, displayAdminDropdown }) => {
export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut }) => {
const styles = useStyles()
return (
<nav className={styles.root}>
@@ -48,7 +46,6 @@ export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut, display
</ListItem>
</List>
<div className={styles.fullWidth} />
{displayAdminDropdown && <AdminDropdown />}
<div className={styles.fixed}>{user && <UserDropdown user={user} onSignOut={onSignOut} />}</div>
</nav>
)