Skip to content

Commit 6052607

Browse files
authored
feat: add user roles to menu (#1862)
* view user roles in menu resolves #1524 * fix stories * PR feedback
1 parent 8d7499f commit 6052607

File tree

4 files changed

+101
-29
lines changed

4 files changed

+101
-29
lines changed

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Box from "@material-ui/core/Box"
22
import { Story } from "@storybook/react"
33
import React from "react"
4+
import { MockUser } from "../../testHelpers/entities"
45
import { UserDropdown, UserDropdownProps } from "./UsersDropdown"
56

67
export default {
@@ -17,9 +18,9 @@ const Template: Story<UserDropdownProps> = (args: UserDropdownProps) => (
1718
</Box>
1819
)
1920

20-
export const Example = Template.bind({})
21-
Example.args = {
22-
user: { id: "1", username: "CathyCoder", email: "cathy@coder.com", created_at: "dawn" },
21+
export const ExampleNoRoles = Template.bind({})
22+
ExampleNoRoles.args = {
23+
user: MockUser,
2324
onSignOut: () => {
2425
return Promise.resolve()
2526
},

site/src/components/UserDropdown/UserDropdown.test.tsx

+31-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { screen } from "@testing-library/react"
22
import React from "react"
3-
import { MockUser } from "../../testHelpers/entities"
3+
import { MockAdminRole, MockMemberRole, MockUser } from "../../testHelpers/entities"
44
import { render } from "../../testHelpers/renderHelpers"
55
import { Language, UserDropdown, UserDropdownProps } from "./UsersDropdown"
66

@@ -33,6 +33,36 @@ describe("UserDropdown", () => {
3333
})
3434

3535
describe("when the menu is open", () => {
36+
it("displays the user's roles", async () => {
37+
await renderAndClick()
38+
39+
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
40+
expect(screen.getByText(MockMemberRole.display_name)).toBeDefined()
41+
})
42+
43+
it("has the correct link for the documentation item", async () => {
44+
process.env.CODER_VERSION = "v0.5.4"
45+
await renderAndClick()
46+
47+
const link = screen.getByText(Language.docsLabel).closest("a")
48+
if (!link) {
49+
throw new Error("Anchor tag not found for the documentation menu item")
50+
}
51+
52+
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
53+
})
54+
55+
it("has the correct link for the account item", async () => {
56+
await renderAndClick()
57+
58+
const link = screen.getByText(Language.accountLabel).closest("a")
59+
if (!link) {
60+
throw new Error("Anchor tag not found for the account menu item")
61+
}
62+
63+
expect(link.getAttribute("href")).toBe("/settings/account")
64+
})
65+
3666
describe("and sign out is clicked", () => {
3767
it("calls the onSignOut function", async () => {
3868
const onSignOut = jest.fn()
@@ -42,27 +72,4 @@ describe("UserDropdown", () => {
4272
})
4373
})
4474
})
45-
46-
it("has the correct link for the documentation item", async () => {
47-
process.env.CODER_VERSION = "v0.5.4"
48-
await renderAndClick()
49-
50-
const link = screen.getByText(Language.docsLabel).closest("a")
51-
if (!link) {
52-
throw new Error("Anchor tag not found for the documentation menu item")
53-
}
54-
55-
expect(link.getAttribute("href")).toBe(`https://github.com/coder/coder/tree/${process.env.CODER_VERSION}/docs`)
56-
})
57-
58-
it("has the correct link for the account item", async () => {
59-
await renderAndClick()
60-
61-
const link = screen.getByText(Language.accountLabel).closest("a")
62-
if (!link) {
63-
throw new Error("Anchor tag not found for the account menu item")
64-
}
65-
66-
expect(link.getAttribute("href")).toBe("/settings/account")
67-
})
6875
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Story } from "@storybook/react"
2+
import React from "react"
3+
import { MockUser } from "../../testHelpers/entities"
4+
import { UserProfileCard, UserProfileCardProps } from "./UserProfileCard"
5+
6+
export default {
7+
title: "components/UserDropdown",
8+
component: UserProfileCard,
9+
argTypes: {
10+
onSignOut: { action: "Sign Out" },
11+
},
12+
}
13+
14+
const Template: Story<UserProfileCardProps> = (args: UserProfileCardProps) => <UserProfileCard {...args} />
15+
16+
export const ExampleNoRoles = Template.bind({})
17+
ExampleNoRoles.args = {
18+
user: {
19+
...MockUser,
20+
roles: [],
21+
},
22+
}
23+
24+
export const ExampleOneRole = Template.bind({})
25+
ExampleOneRole.args = {
26+
user: {
27+
...MockUser,
28+
roles: [{ name: "member", display_name: "Member" }],
29+
},
30+
}
31+
32+
export const ExampleThreeRoles = Template.bind({})
33+
ExampleThreeRoles.args = {
34+
user: {
35+
...MockUser,
36+
roles: [
37+
{ name: "admin", display_name: "Admin" },
38+
{ name: "member", display_name: "Member" },
39+
{ name: "auditor", display_name: "Auditor" },
40+
],
41+
},
42+
}

site/src/components/UserProfileCard/UserProfileCard.tsx

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import Chip from "@material-ui/core/Chip"
12
import { makeStyles } from "@material-ui/core/styles"
23
import Typography from "@material-ui/core/Typography"
34
import React from "react"
45
import * as TypesGen from "../../api/typesGenerated"
6+
import { Role } from "../../api/typesGenerated"
57
import { UserAvatar } from "../UserAvatar/UserAvatar"
68

7-
interface UserProfileCardProps {
9+
export interface UserProfileCardProps {
810
user: TypesGen.User
911
}
1012

@@ -18,6 +20,13 @@ export const UserProfileCard: React.FC<UserProfileCardProps> = ({ user }) => {
1820
</div>
1921
<Typography className={styles.userName}>{user.username}</Typography>
2022
<Typography className={styles.userEmail}>{user.email}</Typography>
23+
<ul className={styles.chipContainer}>
24+
{user.roles.map((role: Role) => (
25+
<li key={role.name} className={styles.chipStyles}>
26+
<Chip classes={{ root: styles.chipRoot }} label={role.display_name} />
27+
</li>
28+
))}
29+
</ul>
2130
</div>
2231
)
2332
}
@@ -52,6 +61,19 @@ const useStyles = makeStyles((theme) => ({
5261
fontSize: 14,
5362
letterSpacing: 0.2,
5463
color: theme.palette.text.secondary,
55-
marginBottom: theme.spacing(1.5),
64+
},
65+
chipContainer: {
66+
display: "flex",
67+
justifyContent: "center",
68+
flexWrap: "wrap",
69+
listStyle: "none",
70+
margin: "0",
71+
padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`,
72+
},
73+
chipStyles: {
74+
margin: theme.spacing(0.5),
75+
},
76+
chipRoot: {
77+
backgroundColor: "#7057FF",
5678
},
5779
}))

0 commit comments

Comments
 (0)