Switch to using creation mode in XState

still problems in tests
This commit is contained in:
Presley
2022-04-27 18:18:07 +00:00
parent 30b8799bc2
commit cf8442fa4b
5 changed files with 54 additions and 40 deletions

View File

@ -1,6 +1,6 @@
import { useActor } from "@xstate/react"
import React, { useContext } from "react"
import { useNavigate } from "react-router"
import { Navigate } from "react-router"
import { CreateUserRequest } from "../../../api/typesGenerated"
import { CreateUserForm } from "../../../components/CreateUserForm/CreateUserForm"
import { XServiceContext } from "../../../xServices/StateContext"
@ -13,17 +13,19 @@ export const CreateUserPage = () => {
const xServices = useContext(XServiceContext)
const [usersState, usersSend] = useActor(xServices.usersXService)
const { createUserError, createUserFormErrors } = usersState.context
const navigate = useNavigate()
// There is no field for organization id in Community Edition, so handle its field error like a generic error
const genericError = (createUserError || createUserFormErrors?.organization_id) ? Language.unknownError : undefined
const genericError = createUserError || createUserFormErrors?.organization_id ? Language.unknownError : undefined
return (
<CreateUserForm
if (usersState.matches("creationMode")){
return <CreateUserForm
formErrors={createUserFormErrors}
onSubmit={(user: CreateUserRequest) => usersSend({ type: "CREATE", user })}
onCancel={() => navigate("/users")}
onCancel={() => {usersSend("EXIT_CREATION_MODE")}}
isLoading={usersState.hasTag("loading")}
error={genericError}
/>
)
} else {
// on cancel or success, redirect
return <Navigate to="/users"/>
}
}

View File

@ -1,6 +1,6 @@
import { useActor } from "@xstate/react"
import React, { useContext, useEffect } from "react"
import { useNavigate } from "react-router"
import { Navigate } from "react-router"
import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary"
import { XServiceContext } from "../../xServices/StateContext"
import { UsersPageView } from "./UsersPageView"
@ -9,7 +9,6 @@ export const UsersPage: React.FC = () => {
const xServices = useContext(XServiceContext)
const [usersState, usersSend] = useActor(xServices.usersXService)
const { users, pager, getUsersError } = usersState.context
const navigate = useNavigate()
/**
* Fetch users on component mount
@ -20,13 +19,16 @@ export const UsersPage: React.FC = () => {
if (usersState.matches("error")) {
return <ErrorSummary error={getUsersError} />
} else if (usersState.matches("creationMode")) {
console.log("loop")
return <Navigate to="/users/create" />
} else {
return (
<UsersPageView
users={users}
pager={pager}
openUserCreationDialog={() => {
navigate("/users/create")
usersSend("ENTER_CREATION_MODE")
}}
/>
)

View File

@ -3,6 +3,7 @@ import { render as wrappedRender, RenderResult } from "@testing-library/react"
import { createMemoryHistory } from "history"
import React from "react"
import { MemoryRouter, Route, Routes, unstable_HistoryRouter as HistoryRouter } from "react-router-dom"
import { AppRouter } from "../AppRouter"
import { RequireAuth } from "../components/RequireAuth/RequireAuth"
import { dark } from "../theme"
import { XServiceProvider } from "../xServices/StateContext"

View File

@ -1,6 +1,5 @@
import { useInterpret } from "@xstate/react"
import React, { createContext } from "react"
import { useNavigate } from "react-router"
import { ActorRefFrom } from "xstate"
import { authMachine } from "./auth/authXService"
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
@ -23,13 +22,12 @@ interface XServiceContextType {
export const XServiceContext = createContext({} as XServiceContextType)
export const XServiceProvider: React.FC = ({ children }) => {
const navigate = useNavigate()
return (
<XServiceContext.Provider
value={{
authXService: useInterpret(authMachine),
buildInfoXService: useInterpret(buildInfoMachine),
usersXService: useInterpret(usersMachine.withContext({ users: [], navigate })),
usersXService: useInterpret(usersMachine)
}}
>
{children}

View File

@ -1,4 +1,3 @@
import { NavigateFunction } from "react-router"
import { assign, createMachine } from "xstate"
import * as API from "../../api"
import { ApiError, FieldErrors, isApiError, mapApiErrorToFieldErrors } from "../../api/errors"
@ -16,10 +15,13 @@ export interface UsersContext {
getUsersError?: Error | unknown
createUserError?: Error | unknown
createUserFormErrors?: FieldErrors
navigate?: NavigateFunction
}
export type UsersEvent = { type: "GET_USERS" } | { type: "CREATE"; user: TypesGen.CreateUserRequest }
export type UsersEvent =
| { type: "GET_USERS" }
| { type: "ENTER_CREATION_MODE" }
| { type: "EXIT_CREATION_MODE" }
| { type: "CREATE"; user: TypesGen.CreateUserRequest }
export const usersMachine = createMachine(
{
@ -45,7 +47,7 @@ export const usersMachine = createMachine(
idle: {
on: {
GET_USERS: "gettingUsers",
CREATE: "creatingUser",
ENTER_CREATION_MODE: "creationMode",
},
},
gettingUsers: {
@ -67,28 +69,40 @@ export const usersMachine = createMachine(
},
tags: "loading",
},
creatingUser: {
invoke: {
src: "createUser",
id: "createUser",
onDone: {
target: "idle",
actions: ["displayCreateUserSuccess", "redirectToUsersPage", "clearCreateUserError"],
creationMode: {
initial: "idle",
states: {
idle: {
on: {
CREATE: "creatingUser",
EXIT_CREATION_MODE: "#usersState.idle"
},
},
onError: [
{
target: "idle",
cond: "isFormError",
actions: ["assignCreateUserFormErrors"],
creatingUser: {
invoke: {
src: "createUser",
id: "createUser",
onDone: {
target: "#usersState.idle",
actions: ["displayCreateUserSuccess", "clearCreateUserError"],
},
onError: [
{
target: "idle",
cond: "isFormError",
actions: ["assignCreateUserFormErrors"],
},
{
target: "idle",
actions: ["assignCreateUserError"],
},
],
},
{
target: "idle",
actions: ["assignCreateUserError"],
},
],
tags: "loading",
},
},
tags: "loading",
},
error: {
on: {
GET_USERS: "gettingUsers",
@ -102,7 +116,7 @@ export const usersMachine = createMachine(
createUser: (_, event) => API.createUser(event.user),
},
guards: {
isFormError: (_, event) => isApiError(event.data)
isFormError: (_, event) => isApiError(event.data),
},
actions: {
assignUsers: assign({
@ -121,7 +135,7 @@ export const usersMachine = createMachine(
}),
assignCreateUserFormErrors: assign({
// the guard ensures it is ApiError
createUserFormErrors: (_, event) => mapApiErrorToFieldErrors((event.data as ApiError).response.data)
createUserFormErrors: (_, event) => mapApiErrorToFieldErrors((event.data as ApiError).response.data),
}),
clearCreateUserError: assign((context: UsersContext) => ({
...context,
@ -130,9 +144,6 @@ export const usersMachine = createMachine(
displayCreateUserSuccess: () => {
displaySuccess(Language.createUserSuccess)
},
redirectToUsersPage: (context) => {
context.navigate && context.navigate("/users")
},
},
},
)