fix: Role assign ui fixes (#3521)

Co-authored-by: Kira Pilot <kira@coder.com>
This commit is contained in:
Steven Masley
2022-08-16 10:39:42 -05:00
committed by GitHub
co-authored by Kira Pilot
parent 4b6a82f92a
commit 4be61d9250
17 changed files with 211 additions and 96 deletions
+2 -2
View File
@@ -311,8 +311,8 @@ export const updateUserPassword = async (
updatePassword: TypesGen.UpdateUserPasswordRequest,
): Promise<undefined> => axios.put(`/api/v2/users/${userId}/password`, updatePassword)
export const getSiteRoles = async (): Promise<Array<TypesGen.Role>> => {
const response = await axios.get<Array<TypesGen.Role>>(`/api/v2/users/roles`)
export const getSiteRoles = async (): Promise<Array<TypesGen.AssignableRoles>> => {
const response = await axios.get<Array<TypesGen.AssignableRoles>>(`/api/v2/users/roles`)
return response.data
}
+5
View File
@@ -24,6 +24,11 @@ export interface AgentGitSSHKey {
readonly private_key: string
}
// From codersdk/roles.go
export interface AssignableRoles extends Role {
readonly assignable: boolean
}
// From codersdk/users.go
export interface AuthMethods {
readonly password: boolean
@@ -1,5 +1,12 @@
import { ComponentMeta, Story } from "@storybook/react"
import { MockAdminRole, MockMemberRole, MockSiteRoles } from "../../testHelpers/renderHelpers"
import {
assignableRole,
MockAuditorRole,
MockMemberRole,
MockOwnerRole,
MockTemplateAdminRole,
MockUserAdminRole,
} from "../../testHelpers/renderHelpers"
import { RoleSelect, RoleSelectProps } from "./RoleSelect"
export default {
@@ -9,15 +16,30 @@ export default {
const Template: Story<RoleSelectProps> = (args) => <RoleSelect {...args} />
// Include 4 roles:
// - owner (disabled, not checked)
// - template admin (disabled, checked)
// - auditor (enabled, not checked)
// - user admin (enabled, checked)
export const Close = Template.bind({})
Close.args = {
roles: MockSiteRoles,
selectedRoles: [MockAdminRole, MockMemberRole],
roles: [
assignableRole(MockOwnerRole, false),
assignableRole(MockTemplateAdminRole, false),
assignableRole(MockAuditorRole, true),
assignableRole(MockUserAdminRole, true),
],
selectedRoles: [MockUserAdminRole, MockTemplateAdminRole, MockMemberRole],
}
export const Open = Template.bind({})
Open.args = {
open: true,
roles: MockSiteRoles,
selectedRoles: [MockAdminRole, MockMemberRole],
roles: [
assignableRole(MockOwnerRole, false),
assignableRole(MockTemplateAdminRole, false),
assignableRole(MockAuditorRole, true),
assignableRole(MockUserAdminRole, true),
],
selectedRoles: [MockUserAdminRole, MockTemplateAdminRole, MockMemberRole],
}
@@ -0,0 +1,44 @@
import { screen } from "@testing-library/react"
import {
assignableRole,
MockAuditorRole,
MockMemberRole,
MockOwnerRole,
MockTemplateAdminRole,
MockUserAdminRole,
render,
} from "testHelpers/renderHelpers"
import { RoleSelect } from "./RoleSelect"
describe("UserRoleSelect", () => {
it("renders content", async () => {
// When
render(
<RoleSelect
roles={[
assignableRole(MockOwnerRole, false),
assignableRole(MockTemplateAdminRole, false),
assignableRole(MockAuditorRole, true),
assignableRole(MockUserAdminRole, true),
]}
selectedRoles={[MockUserAdminRole, MockTemplateAdminRole, MockMemberRole]}
loading={false}
onChange={jest.fn()}
open
/>,
)
// Then
const owner = await screen.findByText(MockOwnerRole.display_name)
const templateAdmin = await screen.findByText(MockTemplateAdminRole.display_name)
const auditor = await screen.findByText(MockAuditorRole.display_name)
const userAdmin = await screen.findByText(MockUserAdminRole.display_name)
// The attributes are "strings", not boolean types.
expect(owner.getAttribute("aria-disabled")).toBe("true")
expect(templateAdmin.getAttribute("aria-disabled")).toBe("true")
expect(userAdmin.getAttribute("aria-disabled")).toBe("false")
expect(auditor.getAttribute("aria-disabled")).toBe("false")
})
})
@@ -3,13 +3,13 @@ import MenuItem from "@material-ui/core/MenuItem"
import Select from "@material-ui/core/Select"
import { makeStyles, Theme } from "@material-ui/core/styles"
import { FC } from "react"
import { Role } from "../../api/typesGenerated"
import { AssignableRoles, Role } from "../../api/typesGenerated"
export const Language = {
label: "Roles",
}
export interface RoleSelectProps {
roles: Role[]
roles: AssignableRoles[]
selectedRoles: Role[]
onChange: (roles: Role["name"][]) => void
loading?: boolean
@@ -46,7 +46,7 @@ export const RoleSelect: FC<RoleSelectProps> = ({
const isChecked = selectedRoles.some((selectedRole) => selectedRole.name === r.name)
return (
<MenuItem key={r.name} value={r.name} disabled={loading}>
<MenuItem key={r.name} value={r.name} disabled={loading || !r.assignable}>
<Checkbox size="small" color="primary" checked={isChecked} /> {r.display_name}
</MenuItem>
)
@@ -1,5 +1,5 @@
import { screen } from "@testing-library/react"
import { MockAdminRole, MockUser } from "../../testHelpers/entities"
import { MockOwnerRole, MockUser } from "../../testHelpers/entities"
import { render } from "../../testHelpers/renderHelpers"
import { Language, UserDropdownContent } from "./UserDropdownContent"
@@ -26,7 +26,7 @@ describe("UserDropdownContent", () => {
it("displays the user's roles", () => {
render(<UserDropdownContent user={MockUser} onSignOut={jest.fn()} onPopoverClose={jest.fn()} />)
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
expect(screen.getByText(MockOwnerRole.display_name)).toBeDefined()
})
it("has the correct link for the account item", () => {
@@ -18,7 +18,7 @@ export const Language = {
export interface UsersTableProps {
users?: TypesGen.User[]
roles?: TypesGen.Role[]
roles?: TypesGen.AssignableRoles[]
isUpdatingUserRoles?: boolean
canEditUsers?: boolean
isLoading?: boolean
@@ -20,7 +20,7 @@ export const Language = {
interface UsersTableBodyProps {
users?: TypesGen.User[]
roles?: TypesGen.Role[]
roles?: TypesGen.AssignableRoles[]
isUpdatingUserRoles?: boolean
canEditUsers?: boolean
isLoading?: boolean
+1 -1
View File
@@ -311,7 +311,7 @@ describe("Users Page", () => {
}, MockAuditorRole)
// Check if the select text was updated with the Auditor role
await waitFor(() => expect(rolesMenuTrigger).toHaveTextContent("Admin, Auditor"))
await waitFor(() => expect(rolesMenuTrigger).toHaveTextContent("Owner, Auditor"))
// Check if the API was called correctly
const currentRoles = MockUser.roles.map((r) => r.name)
+1 -1
View File
@@ -17,7 +17,7 @@ export const Language = {
export interface UsersPageViewProps {
users?: TypesGen.User[]
roles?: TypesGen.Role[]
roles?: TypesGen.AssignableRoles[]
filter?: string
error?: unknown
isUpdatingUserRoles?: boolean
+24 -5
View File
@@ -15,9 +15,19 @@ export const MockBuildInfo: TypesGen.BuildInfoResponse = {
version: "v99.999.9999+c9cdf14",
}
export const MockAdminRole: TypesGen.Role = {
name: "admin",
display_name: "Admin",
export const MockOwnerRole: TypesGen.Role = {
name: "owner",
display_name: "Owner",
}
export const MockUserAdminRole: TypesGen.Role = {
name: "user_admin",
display_name: "User Admin",
}
export const MockTemplateAdminRole: TypesGen.Role = {
name: "template_admin",
display_name: "Template Admin",
}
export const MockMemberRole: TypesGen.Role = {
@@ -30,7 +40,16 @@ export const MockAuditorRole: TypesGen.Role = {
display_name: "Auditor",
}
export const MockSiteRoles = [MockAdminRole, MockAuditorRole]
export const MockSiteRoles = [MockUserAdminRole, MockAuditorRole]
// assignableRole takes a role and a boolean. The boolean implies if the
// actor can assign (add/remove) the role from other users.
export function assignableRole(role: TypesGen.Role, assignable: boolean): TypesGen.AssignableRoles {
return {
...role,
assignable: assignable,
}
}
export const MockUser: TypesGen.User = {
id: "test-user",
@@ -39,7 +58,7 @@ export const MockUser: TypesGen.User = {
created_at: "",
status: "active",
organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"],
roles: [MockAdminRole],
roles: [MockOwnerRole],
}
export const MockUser2: TypesGen.User = {
@@ -8,7 +8,7 @@ export const Language = {
}
type SiteRolesContext = {
roles?: TypesGen.Role[]
roles?: TypesGen.AssignableRoles[]
getRolesError: Error | unknown
}
@@ -25,7 +25,7 @@ export const siteRolesMachine = createMachine(
events: {} as SiteRolesEvent,
services: {
getRoles: {
data: {} as TypesGen.Role[],
data: {} as TypesGen.AssignableRoles[],
},
},
},