Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: Add user menu#887

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 8 commits intomainfrombq/usermenu
Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes fromall 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
12 changes: 12 additions & 0 deletionssite/src/AppRouter.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import { NotFoundPage } from "./pages/404"
import { CliAuthenticationPage } from "./pages/cli-auth"
import { HealthzPage } from "./pages/healthz"
import { SignInPage } from "./pages/login"
import { PreferencesPage } from "./pages/preferences"
import { TemplatesPage } from "./pages/templates"
import { TemplatePage } from "./pages/templates/[organization]/[template]"
import { CreateWorkspacePage } from "./pages/templates/[organization]/[template]/create"
Expand DownExpand Up@@ -67,6 +68,17 @@ export const AppRouter: React.FC = () => (
/>
</Route>

<Route path="preferences">
<Route
index
element={
<AuthAndNav>
<PreferencesPage />
</AuthAndNav>
}
/>
</Route>

{/* Using path="*"" means "match anything", so this route
acts like a catch-all for URLs that we don't have explicit
routes for. */}
Expand Down
13 changes: 13 additions & 0 deletionssite/src/components/Icons/DocsIcon.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"
import React from "react"

export const DocsIcon = (props: SvgIconProps): JSX.Element => (
<SvgIcon {...props} viewBox="0 0 24 24">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.53846 3.75C4.67698 3.75 2.86058 4.50721 2.86058 4.50721L2.5 4.66947V16.4423H9.00841C9.20898 16.7871 9.57407 17.0192 10 17.0192C10.4259 17.0192 10.791 16.7871 10.9916 16.4423H17.5V4.66947L17.1394 4.50721C17.1394 4.50721 15.323 3.75 13.4615 3.75C11.7781 3.75 10.2997 4.31566 10 4.4351C9.70027 4.31566 8.22191 3.75 6.53846 3.75ZM6.53846 4.90385C7.654 4.90385 8.84615 5.26442 9.42308 5.46274V14.7656C8.7808 14.5538 7.72611 14.2608 6.53846 14.2608C5.32602 14.2608 4.33894 14.5403 3.65385 14.7656V5.46274C4.09781 5.30273 5.26968 4.90385 6.53846 4.90385ZM13.4615 4.90385C14.7303 4.90385 15.9022 5.30273 16.3462 5.46274V14.7656C15.6611 14.5403 14.674 14.2608 13.4615 14.2608C12.2739 14.2608 11.2192 14.5538 10.5769 14.7656V5.46274C11.1538 5.26442 12.346 4.90385 13.4615 4.90385Z"
fill="currentColor"
/>
</SvgIcon>
)
26 changes: 26 additions & 0 deletionssite/src/components/Navbar/UserDropdown.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
import Box from "@material-ui/core/Box"
import { Story } from "@storybook/react"
import React from "react"
import { UserDropdown, UserDropdownProps } from "./UserDropdown"

export default {
title: "Page/UserDropdown",
component: UserDropdown,
argTypes: {
onSignOut: { action: "Sign Out" },
},
}

const Template: Story<UserDropdownProps> = (args: UserDropdownProps) => (
<Box style={{ backgroundColor: "#000", width: 88 }}>
<UserDropdown {...args} />
</Box>
)

export const Example = Template.bind({})
Example.args = {
user: { id: "1", username: "CathyCoder", email: "cathy@coder.com", created_at: "dawn" },
onSignOut: () => {
return Promise.resolve()
},
}
55 changes: 55 additions & 0 deletionssite/src/components/Navbar/UserDropdown.test.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
import { screen } from "@testing-library/react"
import React from "react"
import { render } from "../../test_helpers"
import { MockUser } from "../../test_helpers/entities"
import { Language, UserDropdown, UserDropdownProps } from "./UserDropdown"

const renderAndClick = async (props: Partial<UserDropdownProps> = {}) => {
render(<UserDropdown user={props.user ?? MockUser} onSignOut={props.onSignOut ?? jest.fn()} />)
const trigger = await screen.findByTestId("user-dropdown-trigger")
trigger.click()
}

describe("UserDropdown", () => {
describe("when the trigger is clicked", () => {
it("opens the menu", async () => {
await renderAndClick()
expect(screen.getByText(Language.accountLabel)).toBeDefined()
expect(screen.getByText(Language.docsLabel)).toBeDefined()
expect(screen.getByText(Language.signOutLabel)).toBeDefined()
})
})

describe("when the menu is open", () => {
describe("and sign out is clicked", () => {
it("calls the onSignOut function", async () => {
const onSignOut = jest.fn()
await renderAndClick({ onSignOut })
screen.getByText(Language.signOutLabel).click()
expect(onSignOut).toBeCalledTimes(1)
})
})
})

it("has the correct link for the documentation item", async () => {
await renderAndClick()

const link = screen.getByText(Language.docsLabel).closest("a")
if (!link) {
throw new Error("Anchor tag not found for the documentation menu item")
}

expect(link.getAttribute("href")).toBe("https://coder.com/docs")
})

it("has the correct link for the account item", async () => {
await renderAndClick()

const link = screen.getByText(Language.accountLabel).closest("a")
if (!link) {
throw new Error("Anchor tag not found for the account menu item")
}

expect(link.getAttribute("href")).toBe("/preferences")
})
})
40 changes: 37 additions & 3 deletionssite/src/components/Navbar/UserDropdown.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,23 @@ import ListItemIcon from "@material-ui/core/ListItemIcon"
import ListItemText from "@material-ui/core/ListItemText"
import MenuItem from "@material-ui/core/MenuItem"
import { fade, makeStyles } from "@material-ui/core/styles"
import AccountIcon from "@material-ui/icons/AccountCircleOutlined"
import KeyboardArrowDown from "@material-ui/icons/KeyboardArrowDown"
import KeyboardArrowUp from "@material-ui/icons/KeyboardArrowUp"
import React, { useState } from "react"
import { Link } from "react-router-dom"
import { UserResponse } from "../../api/types"
import { LogoutIcon } from "../Icons"
import { DocsIcon } from "../Icons/DocsIcon"
import { UserAvatar } from "../User"
import { UserProfileCard } from "../User/UserProfileCard"
import { BorderedMenu } from "./BorderedMenu"

export const Language = {
accountLabel: "Account",
docsLabel: "Documentation",
signOutLabel: "Sign Out",
}
export interface UserDropdownProps {
user: UserResponse
onSignOut: () => void
Expand All@@ -32,7 +40,7 @@ export const UserDropdown: React.FC<UserDropdownProps> = ({ user, onSignOut }: U
return (
<>
<div>
<MenuItem onClick={handleDropdownClick}>
<MenuItem onClick={handleDropdownClick} data-testid="user-dropdown-trigger">
<div className={styles.inner}>
<Badge overlap="circle">
<UserAvatar username={user.username} />
Expand DownExpand Up@@ -65,13 +73,31 @@ export const UserDropdown: React.FC<UserDropdownProps> = ({ user, onSignOut }: U
<div className={styles.userInfo}>
<UserProfileCard user={user} />

<Divider className={styles.divider} />
<Divider />

<Link to="/preferences" className={styles.link}>
<MenuItem className={styles.menuItem} onClick={handleDropdownClick}>
<ListItemIcon className={styles.icon}>
<AccountIcon />
</ListItemIcon>
<ListItemText primary={Language.accountLabel} />
</MenuItem>
</Link>

<a href="https://coder.com/docs" target="_blank" rel="noreferrer" className={styles.link}>
<MenuItem className={styles.menuItem} onClick={handleDropdownClick}>
<ListItemIcon className={styles.icon}>
<DocsIcon />
</ListItemIcon>
<ListItemText primary={Language.docsLabel} />
</MenuItem>
</a>

<MenuItem className={styles.menuItem} onClick={onSignOut}>
<ListItemIcon className={styles.icon}>
<LogoutIcon />
</ListItemIcon>
<ListItemText primary="Sign Out" />
<ListItemText primary={Language.signOutLabel} />
</MenuItem>
</div>
</BorderedMenu>
Expand All@@ -84,6 +110,7 @@ export const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},

inner: {
display: "flex",
alignItems: "center",
Expand All@@ -94,12 +121,14 @@ export const useStyles = makeStyles((theme) => ({
userInfo: {
marginBottom: theme.spacing(1),
},

arrowIcon: {
color: fade(theme.palette.primary.contrastText, 0.7),
marginLeft: theme.spacing(1),
width: 16,
height: 16,
},

arrowIconUp: {
color: theme.palette.primary.contrastText,
},
Expand All@@ -114,6 +143,11 @@ export const useStyles = makeStyles((theme) => ({
},
},

link: {
textDecoration: "none",
color: "inherit",
},

icon: {
color: theme.palette.text.secondary,
},
Expand Down
15 changes: 15 additions & 0 deletionssite/src/pages/preferences/index.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
import Box from "@material-ui/core/Box"
import Paper from "@material-ui/core/Paper"
import React from "react"
import { Header } from "../../components/Header"
import { Footer } from "../../components/Page"

export const PreferencesPage: React.FC = () => {
return (
<Box display="flex" flexDirection="column">
<Header title="Preferences" />
<Paper style={{ maxWidth: "1380px", margin: "1em auto", width: "100%" }}>Preferences here!</Paper>
<Footer />
</Box>
)
}

[8]ページ先頭

©2009-2025 Movatter.jp