Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 82275a8

Browse files
presleypgreyscaled
andauthored
feat(site): Read users into basic UsersTable (#981)
* Start users * Set up fake response * Update handler * Update types * Set up page * Start adding table * Add header * Add Header * Remove roles * Add UsersPageView * Add test * Lint * Storybook error summary * Strip Pager to just what's currently needed * Clean up ErrorSummary while I'm here * Storybook tweaks * Extract language * Lint * Add missing $ Co-authored-by: G r e y <[email protected]> * Lint * Lint * Fix syntax error * Lint Co-authored-by: G r e y <[email protected]>
1 parent f803e37 commit 82275a8

File tree

17 files changed

+287
-14
lines changed

17 files changed

+287
-14
lines changed

site/src/AppRouter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { SettingsPage } from "./pages/settings"
1616
import { TemplatesPage } from "./pages/templates"
1717
import { TemplatePage } from "./pages/templates/[organization]/[template]"
1818
import { CreateWorkspacePage } from "./pages/templates/[organization]/[template]/create"
19-
import { UsersPage } from "./pages/users"
19+
import { UsersPage } from "./pages/UsersPage/UsersPage"
2020
import { WorkspacePage } from "./pages/workspaces/[workspace]"
2121

2222
export const AppRouter: React.FC = () => (

site/src/api/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import axios, { AxiosRequestHeaders } from "axios"
22
import { mutate } from "swr"
3+
import { MockPager, MockUser, MockUser2 } from "../test_helpers"
34
import * as Types from "./types"
45

56
const CONTENT_TYPE_JSON: AxiosRequestHeaders = {
@@ -69,6 +70,15 @@ export const getApiKey = async (): Promise<Types.APIKeyResponse> => {
6970
return response.data
7071
}
7172

73+
export const getUsers = async (): Promise<Types.PagedUsers> => {
74+
// const response = await axios.get<Types.UserResponse[]>("/api/v2/users")
75+
// return response.data
76+
return Promise.resolve({
77+
page: [MockUser, MockUser2],
78+
pager: MockPager,
79+
})
80+
}
81+
7282
export const getBuildInfo = async (): Promise<Types.BuildInfoResponse> => {
7383
const response = await axios.get("/api/v2/buildinfo")
7484
return response.data

site/src/api/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ export interface UserAgent {
7979
readonly os: string
8080
}
8181

82+
export interface Pager {
83+
total: number
84+
}
85+
86+
export interface PagedUsers {
87+
page: UserResponse[]
88+
pager: Pager
89+
}
90+
8291
export interface WorkspaceAutostartRequest {
8392
schedule: string
8493
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ComponentMeta, Story } from "@storybook/react"
2+
import React from "react"
3+
import { ErrorSummary, ErrorSummaryProps } from "."
4+
5+
export default {
6+
title: "components/ErrorSummary",
7+
component: ErrorSummary,
8+
} as ComponentMeta<typeof ErrorSummary>
9+
10+
const Template: Story<ErrorSummaryProps> = (args) => <ErrorSummary {...args} />
11+
12+
export const WithError = Template.bind({})
13+
WithError.args = {
14+
error: new Error("Something went wrong!"),
15+
}
16+
17+
export const WithUndefined = Template.bind({})
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import React from "react"
22

3+
const Language = {
4+
unknownErrorMessage: "Unknown error",
5+
}
6+
37
export interface ErrorSummaryProps {
4-
error: Error | undefined
8+
error: Error | unknown
59
}
610

711
export const ErrorSummary: React.FC<ErrorSummaryProps> = ({ error }) => {
812
// TODO: More interesting error page
913

10-
if (typeof error === "undefined") {
11-
return <div>{"Unknown error"}</div>
14+
if (!(error instanceof Error)) {
15+
return <div>{Language.unknownErrorMessage}</div>
16+
} else {
17+
return <div>{error.toString()}</div>
1218
}
13-
14-
return <div>{error.toString()}</div>
1519
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ComponentMeta, Story } from "@storybook/react"
2+
import React from "react"
3+
import { MockUser, MockUser2 } from "../../test_helpers"
4+
import { UsersTable, UsersTableProps } from "./UsersTable"
5+
6+
export default {
7+
title: "Components/UsersTable",
8+
component: UsersTable,
9+
} as ComponentMeta<typeof UsersTable>
10+
11+
const Template: Story<UsersTableProps> = (args) => <UsersTable {...args} />
12+
13+
export const Example = Template.bind({})
14+
Example.args = {
15+
users: [MockUser, MockUser2],
16+
}
17+
18+
export const Empty = Template.bind({})
19+
Empty.args = {
20+
users: [],
21+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from "react"
2+
import { UserResponse } from "../../api/types"
3+
import { Column, Table } from "../../components/Table"
4+
import { EmptyState } from "../EmptyState"
5+
import { UserCell } from "../Table/Cells/UserCell"
6+
7+
const Language = {
8+
pageTitle: "Users",
9+
usersTitle: "All users",
10+
emptyMessage: "No users found",
11+
usernameLabel: "User",
12+
}
13+
14+
const emptyState = <EmptyState message={Language.emptyMessage} />
15+
16+
const columns: Column<UserResponse>[] = [
17+
{
18+
key: "username",
19+
name: Language.usernameLabel,
20+
renderer: (field, data) => {
21+
return <UserCell Avatar={{ username: data.username }} primaryText={data.username} caption={data.email} />
22+
},
23+
},
24+
]
25+
26+
export interface UsersTableProps {
27+
users: UserResponse[]
28+
}
29+
30+
export const UsersTable: React.FC<UsersTableProps> = ({ users }) => {
31+
return <Table columns={columns} data={users} title={Language.usersTitle} emptyState={emptyState} />
32+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { screen } from "@testing-library/react"
2+
import React from "react"
3+
import { MockPager, render } from "../../test_helpers"
4+
import { UsersPage } from "./UsersPage"
5+
import { Language } from "./UsersPageView"
6+
7+
describe("Users Page", () => {
8+
it("has a header with the total number of users", async () => {
9+
render(<UsersPage />)
10+
const total = await screen.findByText(/\d+ total/)
11+
expect(total.innerHTML).toEqual(Language.pageSubtitle(MockPager))
12+
})
13+
it("shows users", async () => {
14+
render(<UsersPage />)
15+
const users = await screen.findAllByText(/.*@coder.com/)
16+
expect(users.length).toEqual(2)
17+
})
18+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useActor } from "@xstate/react"
2+
import React, { useContext } from "react"
3+
import { ErrorSummary } from "../../components/ErrorSummary"
4+
import { XServiceContext } from "../../xServices/StateContext"
5+
import { UsersPageView } from "./UsersPageView"
6+
7+
export const UsersPage: React.FC = () => {
8+
const xServices = useContext(XServiceContext)
9+
const [usersState] = useActor(xServices.usersXService)
10+
const { users, pager, getUsersError } = usersState.context
11+
12+
if (usersState.matches("error")) {
13+
return <ErrorSummary error={getUsersError} />
14+
} else {
15+
return <UsersPageView users={users} pager={pager} />
16+
}
17+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ComponentMeta, Story } from "@storybook/react"
2+
import React from "react"
3+
import { MockPager, MockUser, MockUser2 } from "../../test_helpers"
4+
import { UsersPageView, UsersPageViewProps } from "./UsersPageView"
5+
6+
export default {
7+
title: "pages/UsersPageView",
8+
component: UsersPageView,
9+
} as ComponentMeta<typeof UsersPageView>
10+
11+
const Template: Story<UsersPageViewProps> = (args) => <UsersPageView {...args} />
12+
13+
export const Ready = Template.bind({})
14+
Ready.args = {
15+
users: [MockUser, MockUser2],
16+
pager: MockPager,
17+
}
18+
export const Empty = Template.bind({})
19+
Empty.args = {
20+
users: [],
21+
}

0 commit comments

Comments
 (0)