Skip to content

Commit e980d97

Browse files
AbhineetJainkylecarbs
authored andcommitted
feat: add a divider after Account menu item (#1927)
* add a divider after Account menu item * test: improve Storybook tests * add closed and open userdropdown tests * add default isOpen * extract UserDropdownContent into a single component * remove the isOpen prop * address nit comments * update test name
1 parent ecda971 commit e980d97

File tree

7 files changed

+223
-203
lines changed

7 files changed

+223
-203
lines changed

site/src/components/UserDropdown/UserDropdown.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ const Template: Story<UserDropdownProps> = (args: UserDropdownProps) => (
1717
</Box>
1818
)
1919

20-
export const ExampleNoRoles = Template.bind({})
21-
ExampleNoRoles.args = {
20+
export const Example = Template.bind({})
21+
Example.args = {
2222
user: MockUser,
2323
onSignOut: () => {
2424
return Promise.resolve()
Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { screen } from "@testing-library/react"
2-
import { MockAdminRole, MockUser } from "../../testHelpers/entities"
2+
import { MockUser } from "../../testHelpers/entities"
33
import { render } from "../../testHelpers/renderHelpers"
4-
import { Language, UserDropdown, UserDropdownProps } from "./UsersDropdown"
4+
import { Language } from "../UserDropdownContent/UserDropdownContent"
5+
import { UserDropdown, UserDropdownProps } from "./UsersDropdown"
56

67
const renderAndClick = async (props: Partial<UserDropdownProps> = {}) => {
78
render(<UserDropdown user={props.user ?? MockUser} onSignOut={props.onSignOut ?? jest.fn()} />)
@@ -10,18 +11,6 @@ const renderAndClick = async (props: Partial<UserDropdownProps> = {}) => {
1011
}
1112

1213
describe("UserDropdown", () => {
13-
const env = process.env
14-
15-
// REMARK: copying process.env so we don't mutate that object or encounter conflicts between tests
16-
beforeEach(() => {
17-
process.env = { ...env }
18-
})
19-
20-
// REMARK: restoring process.env
21-
afterEach(() => {
22-
process.env = env
23-
})
24-
2514
describe("when the trigger is clicked", () => {
2615
it("opens the menu", async () => {
2716
await renderAndClick()
@@ -30,44 +19,4 @@ describe("UserDropdown", () => {
3019
expect(screen.getByText(Language.signOutLabel)).toBeDefined()
3120
})
3221
})
33-
34-
describe("when the menu is open", () => {
35-
it("displays the user's roles", async () => {
36-
await renderAndClick()
37-
38-
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
39-
})
40-
41-
it("has the correct link for the documentation item", async () => {
42-
process.env.CODER_VERSION = "v0.5.4"
43-
await renderAndClick()
44-
45-
const link = screen.getByText(Language.docsLabel).closest("a")
46-
if (!link) {
47-
throw new Error("Anchor tag not found for the documentation menu item")
48-
}
49-
50-
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
51-
})
52-
53-
it("has the correct link for the account item", async () => {
54-
await renderAndClick()
55-
56-
const link = screen.getByText(Language.accountLabel).closest("a")
57-
if (!link) {
58-
throw new Error("Anchor tag not found for the account menu item")
59-
}
60-
61-
expect(link.getAttribute("href")).toBe("/settings/account")
62-
})
63-
64-
describe("and sign out is clicked", () => {
65-
it("calls the onSignOut function", async () => {
66-
const onSignOut = jest.fn()
67-
await renderAndClick({ onSignOut })
68-
screen.getByText(Language.signOutLabel).click()
69-
expect(onSignOut).toBeCalledTimes(1)
70-
})
71-
})
72-
})
7322
})
Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
import Badge from "@material-ui/core/Badge"
2-
import Divider from "@material-ui/core/Divider"
3-
import ListItemIcon from "@material-ui/core/ListItemIcon"
4-
import ListItemText from "@material-ui/core/ListItemText"
52
import MenuItem from "@material-ui/core/MenuItem"
63
import { fade, makeStyles } from "@material-ui/core/styles"
7-
import AccountIcon from "@material-ui/icons/AccountCircleOutlined"
84
import React, { useState } from "react"
9-
import { Link } from "react-router-dom"
105
import * as TypesGen from "../../api/typesGenerated"
116
import { navHeight } from "../../theme/constants"
127
import { BorderedMenu } from "../BorderedMenu/BorderedMenu"
138
import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows"
14-
import { DocsIcon } from "../Icons/DocsIcon"
15-
import { LogoutIcon } from "../Icons/LogoutIcon"
169
import { UserAvatar } from "../UserAvatar/UserAvatar"
17-
import { UserProfileCard } from "../UserProfileCard/UserProfileCard"
10+
import { UserDropdownContent } from "../UserDropdownContent/UserDropdownContent"
1811

19-
export const Language = {
20-
accountLabel: "Account",
21-
docsLabel: "Documentation",
22-
signOutLabel: "Sign Out",
23-
}
2412
export interface UserDropdownProps {
2513
user: TypesGen.User
2614
onSignOut: () => void
@@ -64,41 +52,7 @@ export const UserDropdown: React.FC<UserDropdownProps> = ({ user, onSignOut }: U
6452
variant="user-dropdown"
6553
onClose={onPopoverClose}
6654
>
67-
<div className={styles.userInfo}>
68-
<UserProfileCard user={user} />
69-
70-
<Divider />
71-
72-
<Link to="/settings/account" className={styles.link}>
73-
<MenuItem className={styles.menuItem} onClick={onPopoverClose}>
74-
<ListItemIcon className={styles.icon}>
75-
<AccountIcon />
76-
</ListItemIcon>
77-
<ListItemText primary={Language.accountLabel} />
78-
</MenuItem>
79-
</Link>
80-
81-
<a
82-
href={`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`}
83-
target="_blank"
84-
rel="noreferrer"
85-
className={styles.link}
86-
>
87-
<MenuItem className={styles.menuItem} onClick={onPopoverClose}>
88-
<ListItemIcon className={styles.icon}>
89-
<DocsIcon />
90-
</ListItemIcon>
91-
<ListItemText primary={Language.docsLabel} />
92-
</MenuItem>
93-
</a>
94-
95-
<MenuItem className={styles.menuItem} onClick={onSignOut}>
96-
<ListItemIcon className={styles.icon}>
97-
<LogoutIcon />
98-
</ListItemIcon>
99-
<ListItemText primary={Language.signOutLabel} />
100-
</MenuItem>
101-
</div>
55+
<UserDropdownContent user={user} onPopoverClose={onPopoverClose} onSignOut={onSignOut} />
10256
</BorderedMenu>
10357
</>
10458
)
@@ -117,10 +71,6 @@ export const useStyles = makeStyles((theme) => ({
11771
maxWidth: 300,
11872
},
11973

120-
userInfo: {
121-
marginBottom: theme.spacing(1),
122-
},
123-
12474
menuItem: {
12575
height: navHeight,
12676
padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`,
@@ -130,13 +80,4 @@ export const useStyles = makeStyles((theme) => ({
13080
transition: "background-color 0.3s ease",
13181
},
13282
},
133-
134-
link: {
135-
textDecoration: "none",
136-
color: "inherit",
137-
},
138-
139-
icon: {
140-
color: theme.palette.text.secondary,
141-
},
14283
}))

site/src/components/UserProfileCard/UserProfileCard.stories.tsx renamed to site/src/components/UserDropdownContent/UserDropdownContent.stories.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import { Story } from "@storybook/react"
22
import React from "react"
33
import { MockUser } from "../../testHelpers/entities"
4-
import { UserProfileCard, UserProfileCardProps } from "./UserProfileCard"
4+
import { UserDropdownContent, UserDropdownContentProps } from "./UserDropdownContent"
55

66
export default {
7-
title: "components/UserDropdown",
8-
component: UserProfileCard,
9-
argTypes: {
10-
onSignOut: { action: "Sign Out" },
11-
},
7+
title: "components/UserDropdownContent",
8+
component: UserDropdownContent,
129
}
1310

14-
const Template: Story<UserProfileCardProps> = (args: UserProfileCardProps) => <UserProfileCard {...args} />
11+
const Template: Story<UserDropdownContentProps> = (args) => <UserDropdownContent {...args} />
1512

1613
export const ExampleNoRoles = Template.bind({})
1714
ExampleNoRoles.args = {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { screen } from "@testing-library/react"
2+
import { MockAdminRole, MockUser } from "../../testHelpers/entities"
3+
import { render } from "../../testHelpers/renderHelpers"
4+
import { Language, UserDropdownContent } from "./UserDropdownContent"
5+
6+
describe("UserDropdownContent", () => {
7+
const env = process.env
8+
9+
// REMARK: copying process.env so we don't mutate that object or encounter conflicts between tests
10+
beforeEach(() => {
11+
process.env = { ...env }
12+
})
13+
14+
// REMARK: restoring process.env
15+
afterEach(() => {
16+
process.env = env
17+
})
18+
19+
it("displays the menu items", () => {
20+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
21+
expect(screen.getByText(Language.accountLabel)).toBeDefined()
22+
expect(screen.getByText(Language.docsLabel)).toBeDefined()
23+
expect(screen.getByText(Language.signOutLabel)).toBeDefined()
24+
})
25+
26+
it("displays the user's roles", () => {
27+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
28+
29+
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
30+
})
31+
32+
it("has the correct link for the documentation item", () => {
33+
process.env.CODER_VERSION = "v0.5.4"
34+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
35+
36+
const link = screen.getByText(Language.docsLabel).closest("a")
37+
if (!link) {
38+
throw new Error("Anchor tag not found for the documentation menu item")
39+
}
40+
41+
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
42+
})
43+
44+
it("has the correct link for the account item", () => {
45+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
46+
47+
const link = screen.getByText(Language.accountLabel).closest("a")
48+
if (!link) {
49+
throw new Error("Anchor tag not found for the account menu item")
50+
}
51+
52+
expect(link.getAttribute("href")).toBe("/settings/account")
53+
})
54+
55+
it("calls the onSignOut function", () => {
56+
const onSignOut = jest.fn()
57+
render(<UserDropdownContent user={MockUser} onSignOut={onSignOut} onPopoverClose={jest.fn()} />)
58+
screen.getByText(Language.signOutLabel).click()
59+
expect(onSignOut).toBeCalledTimes(1)
60+
})
61+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy