Skip to content

Commit 64c1c70

Browse files
committed
extract UserDropdownContent into a single component
1 parent 6b2b57a commit 64c1c70

File tree

7 files changed

+220
-212
lines changed

7 files changed

+220
-212
lines changed

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

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

20-
export const Closed = Template.bind({})
21-
Closed.args = {
22-
user: MockUser,
23-
onSignOut: () => {
24-
return Promise.resolve()
25-
},
26-
}
27-
28-
export const Open = Template.bind({})
29-
Open.args = {
30-
isOpen: true,
20+
export const Example = Template.bind({})
21+
Example.args = {
3122
user: MockUser,
3223
onSignOut: () => {
3324
return Promise.resolve()
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { screen } from "@testing-library/react"
2-
import { MockAdminRole, MockMemberRole, 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,45 +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-
expect(screen.getByText(MockMemberRole.display_name)).toBeDefined()
40-
})
41-
42-
it("has the correct link for the documentation item", async () => {
43-
process.env.CODER_VERSION = "v0.5.4"
44-
await renderAndClick()
45-
46-
const link = screen.getByText(Language.docsLabel).closest("a")
47-
if (!link) {
48-
throw new Error("Anchor tag not found for the documentation menu item")
49-
}
50-
51-
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
52-
})
53-
54-
it("has the correct link for the account item", async () => {
55-
await renderAndClick()
56-
57-
const link = screen.getByText(Language.accountLabel).closest("a")
58-
if (!link) {
59-
throw new Error("Anchor tag not found for the account menu item")
60-
}
61-
62-
expect(link.getAttribute("href")).toBe("/settings/account")
63-
})
64-
65-
describe("and sign out is clicked", () => {
66-
it("calls the onSignOut function", async () => {
67-
const onSignOut = jest.fn()
68-
await renderAndClick({ onSignOut })
69-
screen.getByText(Language.signOutLabel).click()
70-
expect(onSignOut).toBeCalledTimes(1)
71-
})
72-
})
73-
})
7422
})
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
/**
@@ -68,43 +56,7 @@ export const UserDropdown: React.FC<UserDropdownProps> = ({ isOpen = false, user
6856
variant="user-dropdown"
6957
onClose={onPopoverClose}
7058
>
71-
<div className={styles.userInfo}>
72-
<UserProfileCard user={user} />
73-
74-
<Divider />
75-
76-
<Link to="/settings/account" className={styles.link}>
77-
<MenuItem className={styles.menuItem} onClick={onPopoverClose}>
78-
<ListItemIcon className={styles.icon}>
79-
<AccountIcon />
80-
</ListItemIcon>
81-
<ListItemText primary={Language.accountLabel} />
82-
</MenuItem>
83-
</Link>
84-
85-
<Divider />
86-
87-
<a
88-
href={`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`}
89-
target="_blank"
90-
rel="noreferrer"
91-
className={styles.link}
92-
>
93-
<MenuItem className={styles.menuItem} onClick={onPopoverClose}>
94-
<ListItemIcon className={styles.icon}>
95-
<DocsIcon />
96-
</ListItemIcon>
97-
<ListItemText primary={Language.docsLabel} />
98-
</MenuItem>
99-
</a>
100-
101-
<MenuItem className={styles.menuItem} onClick={onSignOut}>
102-
<ListItemIcon className={styles.icon}>
103-
<LogoutIcon />
104-
</ListItemIcon>
105-
<ListItemText primary={Language.signOutLabel} />
106-
</MenuItem>
107-
</div>
59+
<UserDropdownContent user={user} onPopoverClose={onPopoverClose} onSignOut={onSignOut} />
10860
</BorderedMenu>
10961
</>
11062
)
@@ -123,10 +75,6 @@ export const useStyles = makeStyles((theme) => ({
12375
maxWidth: 300,
12476
},
12577

126-
userInfo: {
127-
marginBottom: theme.spacing(1),
128-
},
129-
13078
menuItem: {
13179
height: navHeight,
13280
padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`,
@@ -136,13 +84,4 @@ export const useStyles = makeStyles((theme) => ({
13684
transition: "background-color 0.3s ease",
13785
},
13886
},
139-
140-
link: {
141-
textDecoration: "none",
142-
color: "inherit",
143-
},
144-
145-
icon: {
146-
color: theme.palette.text.secondary,
147-
},
14887
}))

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
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/UserProfileCard",
8-
component: UserProfileCard,
7+
title: "components/UserDropdownContent",
8+
component: UserDropdownContent,
99
argTypes: {},
1010
}
1111

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

1414
export const ExampleNoRoles = Template.bind({})
1515
ExampleNoRoles.args = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { screen } from "@testing-library/react"
2+
import { MockAdminRole, MockMemberRole, MockUser } from "../../testHelpers/entities"
3+
import { render } from "../../testHelpers/renderHelpers"
4+
import { Language, UserDropdownContent } from "./UserDropdownContent"
5+
6+
describe("UserProfileCard", () => {
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", async () => {
27+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
28+
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
29+
expect(screen.getByText(MockMemberRole.display_name)).toBeDefined()
30+
})
31+
32+
it("has the correct link for the documentation item", async () => {
33+
process.env.CODER_VERSION = "v0.5.4"
34+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
35+
const link = screen.getByText(Language.docsLabel).closest("a")
36+
if (!link) {
37+
throw new Error("Anchor tag not found for the documentation menu item")
38+
}
39+
40+
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
41+
})
42+
43+
it("has the correct link for the account item", async () => {
44+
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
45+
const link = screen.getByText(Language.accountLabel).closest("a")
46+
if (!link) {
47+
throw new Error("Anchor tag not found for the account menu item")
48+
}
49+
50+
expect(link.getAttribute("href")).toBe("/settings/account")
51+
})
52+
53+
it("calls the onSignOut function", async () => {
54+
const onSignOut = jest.fn()
55+
render(<UserDropdownContent user={MockUser} onSignOut={onSignOut} onPopoverClose={jest.fn()} />)
56+
screen.getByText(Language.signOutLabel).click()
57+
expect(onSignOut).toBeCalledTimes(1)
58+
})
59+
})

0 commit comments

Comments
 (0)