Partiall upgrade packages/client
This commit is contained in:
parent
fea568b58a
commit
30d1f1fcd4
@ -29,6 +29,11 @@ rules:
|
||||
delimiter: none
|
||||
singleline:
|
||||
delimiter: comma
|
||||
'@typescript-eslint/no-unused-vars':
|
||||
- warn
|
||||
- vars: all
|
||||
args: none
|
||||
ignoreRestSiblings: true
|
||||
'@typescript-eslint/explicit-function-return-type': off
|
||||
'@typescript-eslint/no-non-null-assertion': off
|
||||
'@typescript-eslint/no-use-before-define': off
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import { TStateSelector } from '@rondo.dev/redux'
|
||||
import { Connector } from '../redux'
|
||||
import { Crumb } from './Crumb'
|
||||
import { CrumbsActions } from './CrumbsActions'
|
||||
import { ICrumbsState } from './CrumbsReducer'
|
||||
|
||||
export class CrumbsConnector extends Connector<ICrumbsState> {
|
||||
protected readonly breadcrumbsActions = new CrumbsActions()
|
||||
|
||||
connect<State>(getLocalState: TStateSelector<State, ICrumbsState>) {
|
||||
return this.wrap(
|
||||
getLocalState,
|
||||
state => state,
|
||||
dispatch => ({}),
|
||||
Crumb,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import {TestUtils} from '../test-utils'
|
||||
|
||||
const t = new TestUtils()
|
||||
|
||||
describe('CrumbsConnector', () => {
|
||||
describe('configrueCrumbs', () => {
|
||||
|
||||
const createTestCase = () => t.withProvider({
|
||||
reducers: {Crumbs: Feature.Crumbs},
|
||||
@ -23,7 +23,7 @@ describe('CrumbsConnector', () => {
|
||||
current: 'Three',
|
||||
},
|
||||
})
|
||||
.withComponent(select => new Feature.CrumbsConnector().connect(select))
|
||||
.withComponent(select => Feature.configureCrumbs(select))
|
||||
.withJSX(Component => <MemoryRouter><Component /></MemoryRouter>)
|
||||
|
||||
describe('render', () => {
|
||||
14
packages/client/src/crumbs/configureCrumbs.tsx
Normal file
14
packages/client/src/crumbs/configureCrumbs.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { pack, TStateSelector } from '@rondo.dev/redux'
|
||||
import { Crumb } from './Crumb'
|
||||
import { ICrumbsState } from './CrumbsReducer'
|
||||
|
||||
export function configureCrumbs<State>(
|
||||
getLocalState: TStateSelector<State, ICrumbsState>,
|
||||
) {
|
||||
return pack(
|
||||
getLocalState,
|
||||
state => state,
|
||||
dispatch => ({}),
|
||||
Crumb,
|
||||
)
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
export * from './configureCrumbs'
|
||||
export * from './Crumb'
|
||||
export * from './CrumbsActions'
|
||||
export * from './CrumbsConnector'
|
||||
export * from './CrumbsReducer'
|
||||
export * from './HistoryCrumbs'
|
||||
export * from './ICrumbLink'
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { IAPIDef } from '@rondo.dev/common'
|
||||
import { APIDef } from '@rondo.dev/common'
|
||||
import { HTTPClientMock } from '@rondo.dev/http-client'
|
||||
import { getError } from '@rondo.dev/test-utils'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import T from 'react-dom/test-utils'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { TestUtils } from '../test-utils'
|
||||
import { TestUtils, TestContainer } from '../test-utils'
|
||||
import * as Feature from './'
|
||||
import { configureRegister } from './configureRegister'
|
||||
|
||||
@ -13,7 +12,7 @@ const test = new TestUtils()
|
||||
|
||||
describe('configureRegister', () => {
|
||||
|
||||
const http = new HTTPClientMock<IAPIDef>()
|
||||
const http = new HTTPClientMock<APIDef>()
|
||||
const loginActions = new Feature.LoginActions(http)
|
||||
|
||||
const createTestProvider = () => test.withProvider({
|
||||
@ -45,7 +44,7 @@ describe('configureRegister', () => {
|
||||
}
|
||||
const onSuccess = jest.fn()
|
||||
let node: Element
|
||||
let component: React.Component
|
||||
let component: TestContainer
|
||||
beforeEach(() => {
|
||||
http.mockAdd({
|
||||
method: 'post',
|
||||
@ -85,8 +84,7 @@ describe('configureRegister', () => {
|
||||
.value,
|
||||
)
|
||||
.toEqual('')
|
||||
node = ReactDOM.findDOMNode(component) as Element
|
||||
expect(node.innerHTML).toMatch(/<a href="\/">/)
|
||||
expect(component.ref.current!.innerHTML).toMatch(/<a href="\/">/)
|
||||
})
|
||||
|
||||
it('sets the error message on failure', async () => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import {IPendingAction} from '@rondo.dev/redux'
|
||||
import {PendingAction} from '@rondo.dev/redux'
|
||||
|
||||
export interface IComponentProps<Data> {
|
||||
export interface ComponentProps<Data> {
|
||||
onSubmit: () => void
|
||||
onChange: (name: string, value: string) => void
|
||||
// TODO clear data on successful submission. This is important to prevent
|
||||
@ -9,29 +9,29 @@ export interface IComponentProps<Data> {
|
||||
data: Data
|
||||
}
|
||||
|
||||
export interface IFormHOCProps<Data> {
|
||||
onSubmit: (props: Data) => IPendingAction<any, any>
|
||||
export interface FormHOCProps<Data> {
|
||||
onSubmit: (props: Data) => PendingAction<unknown, string>
|
||||
// TODO figure out what would happen if the underlying child component
|
||||
// would have the same required property as the HOC, like onSuccess?
|
||||
onSuccess?: () => void
|
||||
clearOnSuccess?: boolean
|
||||
}
|
||||
|
||||
export interface IFormHOCState<Data> {
|
||||
export interface FormHOCState<Data> {
|
||||
error: string
|
||||
data: Data
|
||||
}
|
||||
|
||||
export function withForm<Data, Props extends IComponentProps<Data>>(
|
||||
export function withForm<Data, Props extends ComponentProps<Data>>(
|
||||
Component: React.ComponentType<Props>,
|
||||
initialState: Data,
|
||||
) {
|
||||
|
||||
type OtherProps = Pick<Props,
|
||||
Exclude<keyof Props, keyof IComponentProps<Data>>>
|
||||
type T = IFormHOCProps<Data> & OtherProps
|
||||
Exclude<keyof Props, keyof ComponentProps<Data>>>
|
||||
type T = FormHOCProps<Data> & OtherProps
|
||||
|
||||
return class FormHOC extends React.PureComponent<T, IFormHOCState<Data>> {
|
||||
return class FormHOC extends React.PureComponent<T, FormHOCState<Data>> {
|
||||
constructor(props: T) {
|
||||
super(props)
|
||||
this.state = {
|
||||
@ -78,7 +78,9 @@ export function withForm<Data, Props extends IComponentProps<Data>>(
|
||||
// https://github.com/Microsoft/TypeScript/issues/28938
|
||||
return (
|
||||
<Component
|
||||
{...otherProps as any}
|
||||
{
|
||||
...otherProps as any // eslint-disable-line
|
||||
}
|
||||
data={this.state}
|
||||
onSubmit={this.handleSubmit}
|
||||
onChange={this.handleChange}
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
import {TStateSelector} from '@rondo.dev/redux'
|
||||
import {connect, Omit} from 'react-redux'
|
||||
import {Dispatch} from 'redux'
|
||||
import {ComponentType} from 'react'
|
||||
|
||||
/**
|
||||
* Helps isolate the component along with actions and reducer from the global
|
||||
* application state.
|
||||
*
|
||||
* The connect() function requires a state selector, which can be used to
|
||||
* select a slice of the current state to the component, which will be passed
|
||||
* on to `mapStateToProps()`.
|
||||
*
|
||||
* Classes that extend Connector can provide dependencies in the constructor
|
||||
* and then bind them to dispatch in mapDispatchToProps. This is useful to
|
||||
* build components which do not depend on a "global" singletons. For example,
|
||||
* the Actions class might depend on the HTTPClient class, and then it becomes
|
||||
* easy to mock it during tests, or swap out different dependencies for
|
||||
* different applications.
|
||||
*/
|
||||
export abstract class Connector<LocalState> {
|
||||
|
||||
/**
|
||||
* Connects a component using redux. The `selectState` method is used to
|
||||
* select a subset of state to map to the component.
|
||||
*
|
||||
* It returns a component with `any` props. Ideally this could be changed to
|
||||
* required props.
|
||||
*
|
||||
* https://stackoverflow.com/questions/54277411
|
||||
*/
|
||||
abstract connect<State>(
|
||||
selectState: TStateSelector<State, LocalState>,
|
||||
): ComponentType<any>
|
||||
|
||||
protected wrap<
|
||||
State,
|
||||
Props,
|
||||
StateProps extends Partial<Props>,
|
||||
DispatchProps extends Partial<Props>,
|
||||
>(
|
||||
getLocalState: TStateSelector<State, LocalState>,
|
||||
mapStateToProps: (state: LocalState) => StateProps,
|
||||
mapDispatchToProps: (dispatch: Dispatch) => DispatchProps,
|
||||
Component: React.ComponentType<Props>,
|
||||
): ComponentType<
|
||||
Omit<Props, keyof Props & (keyof StateProps | keyof DispatchProps)>
|
||||
> {
|
||||
|
||||
return connect(
|
||||
(state: State) => {
|
||||
const l = getLocalState(state)
|
||||
return mapStateToProps(l)
|
||||
},
|
||||
mapDispatchToProps,
|
||||
)(Component as any) as any
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from './Connector'
|
||||
@ -1,4 +1,4 @@
|
||||
export interface IClientConfig {
|
||||
export interface ClientConfig {
|
||||
readonly appName: string
|
||||
readonly baseUrl: string
|
||||
readonly csrfToken: string
|
||||
@ -1,30 +1,27 @@
|
||||
import { createBrowserHistory } from 'history'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import {Action} from 'redux'
|
||||
import {IAPIDef} from '@rondo.dev/common'
|
||||
import {IClientConfig} from './IClientConfig'
|
||||
import {IHTTPClient, HTTPClient} from '@rondo.dev/http-client'
|
||||
import {IRenderer} from './IRenderer'
|
||||
import {Provider} from 'react-redux'
|
||||
import {Router} from 'react-router-dom'
|
||||
import {Store} from 'redux'
|
||||
import {createBrowserHistory} from 'history'
|
||||
import { Provider } from 'react-redux'
|
||||
import { Router } from 'react-router-dom'
|
||||
import { Store } from 'redux'
|
||||
import { ClientConfig } from './ClientConfig'
|
||||
import { Renderer } from './Renderer'
|
||||
|
||||
export interface IClientRendererParams<Props> {
|
||||
export interface ClientRendererParams<Props> {
|
||||
readonly RootComponent: React.ComponentType<Props>
|
||||
readonly target?: HTMLElement
|
||||
readonly hydrate: boolean // TODO make this better
|
||||
}
|
||||
|
||||
export class ClientRenderer<Props>
|
||||
implements IRenderer<Props> {
|
||||
constructor(readonly params: IClientRendererParams<Props>) {}
|
||||
implements Renderer<Props> {
|
||||
constructor(readonly params: ClientRendererParams<Props>) {}
|
||||
|
||||
render<State>(
|
||||
url: string,
|
||||
store: Store<State>,
|
||||
props: Props,
|
||||
config: IClientConfig,
|
||||
config: ClientConfig,
|
||||
) {
|
||||
const {
|
||||
RootComponent,
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
import {IAPIDef} from '@rondo.dev/common'
|
||||
import {IClientConfig} from './IClientConfig'
|
||||
import {Store} from 'redux'
|
||||
|
||||
export interface IRenderer<Props> {
|
||||
render<State>(
|
||||
url: string,
|
||||
store: Store<State>,
|
||||
props: Props,
|
||||
config: IClientConfig,
|
||||
): any
|
||||
}
|
||||
11
packages/client/src/renderer/Renderer.ts
Normal file
11
packages/client/src/renderer/Renderer.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Store } from 'redux'
|
||||
import { ClientConfig } from './ClientConfig'
|
||||
|
||||
export interface Renderer<Props> {
|
||||
render<State>(
|
||||
url: string,
|
||||
store: Store<State>,
|
||||
props: Props,
|
||||
config: ClientConfig,
|
||||
): unknown
|
||||
}
|
||||
@ -8,6 +8,10 @@ import { Store } from 'redux'
|
||||
import { IClientConfig } from './IClientConfig'
|
||||
import { IRenderer } from './IRenderer'
|
||||
|
||||
interface ComponentWithFetchData {
|
||||
fetchData(): Promise<unknown>
|
||||
}
|
||||
|
||||
export class ServerRenderer<Props> implements IRenderer<Props> {
|
||||
constructor(
|
||||
readonly RootComponent: React.ComponentType<Props>,
|
||||
@ -17,7 +21,7 @@ export class ServerRenderer<Props> implements IRenderer<Props> {
|
||||
store: Store<State>,
|
||||
props: Props,
|
||||
config: IClientConfig,
|
||||
host: string = '',
|
||||
host = '',
|
||||
headers: Record<string, string> = {},
|
||||
) {
|
||||
const {RootComponent} = this
|
||||
@ -39,7 +43,7 @@ export class ServerRenderer<Props> implements IRenderer<Props> {
|
||||
|
||||
await ssrPrepass(element, async (el, component) => {
|
||||
if (component && 'fetchData' in component) {
|
||||
await (component as any).fetchData()
|
||||
await (component as ComponentWithFetchData).fetchData()
|
||||
}
|
||||
})
|
||||
const stream = renderToNodeStream(element)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export * from './ClientRenderer'
|
||||
export * from './IClientConfig'
|
||||
export * from './IRenderer'
|
||||
export * from './ClientConfig'
|
||||
export * from './Renderer'
|
||||
export * from './isClientSide'
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
interface MockServerSide {
|
||||
__MOCK_SERVER_SIDE__?: boolean
|
||||
}
|
||||
|
||||
export function isClientSide() {
|
||||
return typeof window !== 'undefined' &&
|
||||
typeof window.document !== 'undefined' &&
|
||||
typeof window.document.createElement === 'function' &&
|
||||
typeof (window as any).__MOCK_SERVER_SIDE__ === 'undefined'
|
||||
typeof (window as MockServerSide).__MOCK_SERVER_SIDE__ === 'undefined'
|
||||
}
|
||||
|
||||
@ -3,23 +3,27 @@ import React from 'react'
|
||||
import { FaCheck, FaEdit, FaPlusSquare } from 'react-icons/fa'
|
||||
import { TeamActions, Team } from '@rondo.dev/common'
|
||||
|
||||
export type TTeamEditorProps = {
|
||||
interface AddTeamProps {
|
||||
type: 'add'
|
||||
onAddTeam: TeamActions['create']
|
||||
} | {
|
||||
}
|
||||
|
||||
interface UpdateTeamProps {
|
||||
type: 'update'
|
||||
onUpdateTeam: TeamActions['update']
|
||||
team: Team
|
||||
}
|
||||
|
||||
export interface ITeamEditorState {
|
||||
export type TTeamEditorProps = AddTeamProps | UpdateTeamProps
|
||||
|
||||
export interface TeamEditorState {
|
||||
// TODO use redux state for errors!
|
||||
error: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export class TeamEditor
|
||||
extends React.PureComponent<TTeamEditorProps, ITeamEditorState> {
|
||||
extends React.PureComponent<TTeamEditorProps, TeamEditorState> {
|
||||
constructor(props: TTeamEditorProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
@ -33,7 +37,7 @@ extends React.PureComponent<TTeamEditorProps, ITeamEditorState> {
|
||||
componentWillReceiveProps(nextProps: TTeamEditorProps) {
|
||||
if (nextProps.type === 'update') {
|
||||
const {team} = nextProps
|
||||
if (team !== (this.props as any).team) {
|
||||
if (team !== (this.props as UpdateTeamProps).team) {
|
||||
this.setState({
|
||||
name: this.getName(team),
|
||||
})
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
import { TReadonlyRecord, Team, TeamActions } from '@rondo.dev/common'
|
||||
import { ReadonlyRecord, Team, TeamActions } from '@rondo.dev/common'
|
||||
import { Button, Panel, PanelBlock, PanelHeading } from 'bloomer'
|
||||
import React from 'react'
|
||||
import { FaEdit, FaPlus, FaTimes } from 'react-icons/fa'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
export interface ITeamListProps {
|
||||
export interface TeamListProps {
|
||||
ListButtons?: React.ComponentType<{team: Team}>
|
||||
teamsById: TReadonlyRecord<number, Team>
|
||||
teamsById: ReadonlyRecord<number, Team>
|
||||
teamIds: ReadonlyArray<number>
|
||||
onAddTeam: TeamActions['create']
|
||||
onRemoveTeam: TeamActions['remove']
|
||||
}
|
||||
|
||||
export interface ITeamProps {
|
||||
export interface TeamProps {
|
||||
ListButtons?: React.ComponentType<{team: Team}>
|
||||
team: Team
|
||||
onRemoveTeam: TeamActions['remove']
|
||||
}
|
||||
|
||||
export class TeamRow extends React.PureComponent<ITeamProps> {
|
||||
export class TeamRow extends React.PureComponent<TeamProps> {
|
||||
handleRemove = async () => {
|
||||
const {onRemoveTeam, team: {id}} = this.props
|
||||
await onRemoveTeam({id}).payload
|
||||
@ -53,7 +53,7 @@ export class TeamRow extends React.PureComponent<ITeamProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export class TeamList extends React.PureComponent<ITeamListProps> {
|
||||
export class TeamList extends React.PureComponent<TeamListProps> {
|
||||
render() {
|
||||
const {teamIds, teamsById} = this.props
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IUserInTeam, TReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
|
||||
import { UserInTeam, ReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
|
||||
import { Panel, PanelBlock, PanelHeading } from 'bloomer'
|
||||
import { History, Location } from 'history'
|
||||
import React from 'react'
|
||||
@ -8,24 +8,24 @@ import { TeamEditor } from './TeamEditor'
|
||||
import { TeamList } from './TeamList'
|
||||
import { TeamUserList } from './TeamUserList'
|
||||
|
||||
export interface ITeamManagerProps {
|
||||
export interface TeamManagerProps {
|
||||
history: History
|
||||
location: Location
|
||||
match: Match<any>
|
||||
match: Match<any> // eslint-disable-line
|
||||
|
||||
ListButtons?: React.ComponentType<{team: Team}>
|
||||
|
||||
teamActions: TeamActions
|
||||
findUserByEmail: UserActions['findUserByEmail']
|
||||
|
||||
teamsById: TReadonlyRecord<number, Team>
|
||||
teamsById: ReadonlyRecord<number, Team>
|
||||
teamIds: ReadonlyArray<number>
|
||||
|
||||
userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
|
||||
usersByKey: TReadonlyRecord<string, IUserInTeam>
|
||||
userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>>
|
||||
usersByKey: ReadonlyRecord<string, UserInTeam>
|
||||
}
|
||||
|
||||
export class TeamManager extends React.PureComponent<ITeamManagerProps> {
|
||||
export class TeamManager extends React.PureComponent<TeamManagerProps> {
|
||||
async componentDidMount() {
|
||||
await this.props.teamActions.find()
|
||||
}
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { indexBy, Team as _Team, IUserInTeam, TReadonlyRecord, without, TeamActions } from '@rondo.dev/common'
|
||||
import { indexBy, Team as _Team, UserInTeam, ReadonlyRecord, without, TeamActions } from '@rondo.dev/common'
|
||||
import { createReducer } from '@rondo.dev/jsonrpc'
|
||||
|
||||
export interface ITeamState {
|
||||
export interface TeamState {
|
||||
readonly loading: number
|
||||
readonly error: string
|
||||
|
||||
readonly teamIds: ReadonlyArray<number>
|
||||
readonly teamsById: TReadonlyRecord<number, _Team>
|
||||
readonly teamsById: ReadonlyRecord<number, _Team>
|
||||
|
||||
readonly userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
|
||||
readonly usersByKey: TReadonlyRecord<string, IUserInTeam>
|
||||
readonly userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>>
|
||||
readonly usersByKey: ReadonlyRecord<string, UserInTeam>
|
||||
}
|
||||
|
||||
const defaultState: ITeamState = {
|
||||
const defaultState: TeamState = {
|
||||
loading: 0,
|
||||
error: '',
|
||||
|
||||
@ -104,7 +104,7 @@ export const Team = createReducer('teamService', defaultState)
|
||||
.reduce((obj, userInTeam) => {
|
||||
obj[getUserKey(userInTeam)] = userInTeam
|
||||
return obj
|
||||
}, {} as Record<string, IUserInTeam>)
|
||||
}, {} as Record<string, UserInTeam>)
|
||||
|
||||
return {
|
||||
userKeysByTeamId: {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { IUser, IUserInTeam, TReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
|
||||
import { User, UserInTeam, ReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
|
||||
import { Button, Control, Heading, Help, Input, Panel, PanelBlock, PanelHeading } from 'bloomer'
|
||||
import React from 'react'
|
||||
import { FaCheck, FaTimes, FaUser } from 'react-icons/fa'
|
||||
|
||||
const EMPTY_ARRAY: ReadonlyArray<string> = []
|
||||
|
||||
export interface ITeamUsersProps {
|
||||
export interface TeamUsersProps {
|
||||
// fetchMyTeams: () => void,
|
||||
fetchUsersInTeam: TeamActions['findUsers']
|
||||
findUserByEmail: UserActions['findUserByEmail']
|
||||
@ -14,29 +14,29 @@ export interface ITeamUsersProps {
|
||||
onRemoveUser: TeamActions['removeUser']
|
||||
|
||||
team: Team
|
||||
userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
|
||||
usersByKey: TReadonlyRecord<string, IUserInTeam>
|
||||
userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>>
|
||||
usersByKey: ReadonlyRecord<string, UserInTeam>
|
||||
}
|
||||
|
||||
export interface ITeamUserProps {
|
||||
export interface TeamUserProps {
|
||||
onRemoveUser: (
|
||||
params: {userId: number, teamId: number, roleId: number}) => void
|
||||
user: IUserInTeam
|
||||
user: UserInTeam
|
||||
}
|
||||
|
||||
export interface IAddUserProps {
|
||||
export interface AddUserProps {
|
||||
onAddUser: TeamActions['addUser']
|
||||
onSearchUser: UserActions['findUserByEmail']
|
||||
teamId: number
|
||||
}
|
||||
|
||||
export interface IAddUserState {
|
||||
export interface AddUserState {
|
||||
error: string
|
||||
email: string
|
||||
user?: IUser
|
||||
user?: User
|
||||
}
|
||||
|
||||
export class TeamUser extends React.PureComponent<ITeamUserProps> {
|
||||
export class TeamUser extends React.PureComponent<TeamUserProps> {
|
||||
handleRemoveUser = async () => {
|
||||
const {onRemoveUser, user} = this.props
|
||||
await onRemoveUser({...user, roleId: 1})
|
||||
@ -65,8 +65,8 @@ export class TeamUser extends React.PureComponent<ITeamUserProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export class AddUser extends React.PureComponent<IAddUserProps, IAddUserState> {
|
||||
constructor(props: IAddUserProps) {
|
||||
export class AddUser extends React.PureComponent<AddUserProps, AddUserState> {
|
||||
constructor(props: AddUserProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
error: '',
|
||||
@ -131,10 +131,10 @@ export class AddUser extends React.PureComponent<IAddUserProps, IAddUserState> {
|
||||
}
|
||||
}
|
||||
|
||||
export class TeamUserList extends React.PureComponent<ITeamUsersProps> {
|
||||
export class TeamUserList extends React.PureComponent<TeamUsersProps> {
|
||||
async componentDidMount() {
|
||||
await this.fetchUsersInTeam(this.props.team.id)
|
||||
} async componentWillReceiveProps(nextProps: ITeamUsersProps) {
|
||||
} async componentWillReceiveProps(nextProps: TeamUsersProps) {
|
||||
const {team} = nextProps
|
||||
if (team.id !== this.props.team.id) {
|
||||
this.fetchUsersInTeam(team.id)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ITeamService, ITeamUsers, IUserService, Team, TeamActions, TeamServiceMethods, UserActions, UserServiceMethods } from '@rondo.dev/common'
|
||||
import { TeamService, TeamUsers, UserService, Team, TeamActions, TeamServiceMethods, UserActions, UserServiceMethods } from '@rondo.dev/common'
|
||||
import { createActions } from '@rondo.dev/jsonrpc'
|
||||
import createClientMock from '@rondo.dev/jsonrpc/lib/createClientMock'
|
||||
import { getError } from '@rondo.dev/test-utils'
|
||||
@ -17,9 +17,9 @@ describe('TeamConnector', () => {
|
||||
}
|
||||
|
||||
const [teamClient, teamClientMock] =
|
||||
createClientMock<ITeamService>(TeamServiceMethods)
|
||||
const [userClient, userClientMock] =
|
||||
createClientMock<IUserService>(UserServiceMethods)
|
||||
createClientMock<TeamService>(TeamServiceMethods)
|
||||
const [userClient] =
|
||||
createClientMock<UserService>(UserServiceMethods)
|
||||
let teamActions!: TeamActions
|
||||
let userActions!: UserActions
|
||||
beforeEach(() => {
|
||||
@ -57,7 +57,7 @@ describe('TeamConnector', () => {
|
||||
userTeams: [],
|
||||
}]
|
||||
|
||||
const users: ITeamUsers = {
|
||||
const users: TeamUsers = {
|
||||
teamId: 123,
|
||||
usersInTeam: [{
|
||||
teamId: 123,
|
||||
@ -111,7 +111,6 @@ describe('TeamConnector', () => {
|
||||
})
|
||||
|
||||
it('displays an error', async () => {
|
||||
const error = {error: 'An error'}
|
||||
teamClientMock.create.mockRejectedValue(new Error('Test Error'))
|
||||
const {render} = createTestProvider()
|
||||
const {node, waitForActions} = render({
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import T from 'react-dom/test-utils'
|
||||
import {createStore, TStateSelector, WaitMiddleware} from '@rondo.dev/redux'
|
||||
import {Provider} from 'react-redux'
|
||||
import {
|
||||
@ -10,20 +10,18 @@ import {
|
||||
Reducer,
|
||||
ReducersMapObject,
|
||||
combineReducers,
|
||||
Store as ReduxStore,
|
||||
Unsubscribe,
|
||||
} from 'redux'
|
||||
import { format } from 'util'
|
||||
|
||||
interface IRenderParams<State, LocalState> {
|
||||
interface RenderParams<State, LocalState> {
|
||||
reducers: ReducersMapObject<State, any>
|
||||
select: TStateSelector<State, LocalState>
|
||||
// getComponent: (
|
||||
// select: TStateSelector<State, LocalState>) => React.ComponentType<Props>,
|
||||
// customJSX?: (
|
||||
// Component: React.ComponentType<Props>,
|
||||
// props: Props,
|
||||
// ) => JSX.Element
|
||||
}
|
||||
|
||||
export class TestContainer extends React.Component<{}> {
|
||||
ref = React.createRef<HTMLDivElement>()
|
||||
render() {
|
||||
return <div ref={this.ref}>{this.props.children}</div>
|
||||
}
|
||||
}
|
||||
|
||||
export class TestUtils {
|
||||
@ -34,9 +32,10 @@ export class TestUtils {
|
||||
|
||||
render(jsx: JSX.Element) {
|
||||
const $div = document.createElement('div')
|
||||
ReactDOM.render(<TestContainer>{jsx}</TestContainer>, $div)
|
||||
const component = ReactDOM.render(
|
||||
<div>{jsx}</div>, $div) as unknown as React.Component<any>
|
||||
const node = (ReactDOM.findDOMNode(component) as Element).children[0]
|
||||
<div>{jsx}</div>, $div) as unknown as TestContainer
|
||||
const node = component.ref.current!
|
||||
return {
|
||||
component,
|
||||
node,
|
||||
@ -55,7 +54,7 @@ export class TestUtils {
|
||||
* method to render the connected component with a `Provider`.
|
||||
*/
|
||||
withProvider<State, LocalState, A extends Action<any> = AnyAction>(
|
||||
params: IRenderParams<State, LocalState>,
|
||||
params: RenderParams<State, LocalState>,
|
||||
) {
|
||||
const {reducers, select} = params
|
||||
|
||||
@ -102,7 +101,7 @@ export class TestUtils {
|
||||
return {
|
||||
...result,
|
||||
async waitForActions(timeout = 2000) {
|
||||
await waitMiddleware.waitForRecorded(recorder)
|
||||
await waitMiddleware.waitForRecorded(recorder, timeout)
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -112,7 +111,7 @@ export class TestUtils {
|
||||
return self
|
||||
}
|
||||
|
||||
const self: ISelf<
|
||||
const self: Self<
|
||||
Props, typeof store, typeof Component, CreateJSX
|
||||
> = {
|
||||
render,
|
||||
@ -128,14 +127,14 @@ export class TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
interface ISelf<Props, Store, Component, CreateJSX> {
|
||||
interface Self<Props, Store, Component, CreateJSX> {
|
||||
render: (props: Props) => {
|
||||
component: React.Component<any>
|
||||
component: TestContainer
|
||||
node: Element
|
||||
waitForActions(timeout?: number): Promise<void>
|
||||
}
|
||||
store: Store
|
||||
Component: Component
|
||||
withJSX: (localCreateJSX: CreateJSX)
|
||||
=> ISelf<Props, Store, Component, CreateJSX>
|
||||
=> Self<Props, Store, Component, CreateJSX>
|
||||
}
|
||||
|
||||
2
packages/client/types/react-ssr-prepass.d.ts
vendored
2
packages/client/types/react-ssr-prepass.d.ts
vendored
@ -4,7 +4,7 @@ declare module 'react-ssr-prepass' {
|
||||
export type Visitor = (
|
||||
element: ReactElement,
|
||||
instance?: Component,
|
||||
) => void | Promise<any>
|
||||
) => void | Promise<unknown>
|
||||
|
||||
function ssrPrepass(node: ReactElement, visitor?: Visitor): Promise<void>
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
export * from './HTTPClient'
|
||||
export * from './HTTPClientMock'
|
||||
export * from './Headers'
|
||||
export * from './HTTPClient'
|
||||
export * from './HTTPClientMock'
|
||||
export * from './Request'
|
||||
export * from './RequestParams'
|
||||
export * from './RequestQuery'
|
||||
export * from './Response'
|
||||
export * from './SimpleHTTPClient'
|
||||
export * from './TypedRequestParams'
|
||||
export * from './URLFormatter'
|
||||
|
||||
@ -2,7 +2,7 @@ import { Logger } from '@rondo.dev/logger'
|
||||
import express, { ErrorRequestHandler, Request, Response, Router } from 'express'
|
||||
import { createError, ErrorResponse, isRPCError } from './error'
|
||||
import { IDEMPOTENT_METHOD_REGEX } from './idempotent'
|
||||
import { createRpcService, ERROR_METHOD_NOT_FOUND, ERROR_SERVER, IRequest, SuccessResponse } from './jsonrpc'
|
||||
import { createRpcService, ERROR_METHOD_NOT_FOUND, ERROR_SERVER, Request as RPCRequest, SuccessResponse } from './jsonrpc'
|
||||
import { FunctionPropertyNames } from './types'
|
||||
|
||||
export type TGetContext<Context> = (req: Request) => Promise<Context> | Context
|
||||
@ -16,13 +16,13 @@ export interface RPCReturnType {
|
||||
router(): Router
|
||||
}
|
||||
|
||||
export interface InvocationDetails<A extends IRequest, Context> {
|
||||
export interface InvocationDetails<A extends RPCRequest, Context> {
|
||||
context: Context
|
||||
path: string
|
||||
request: A
|
||||
}
|
||||
|
||||
async function defaultHook<A extends IRequest, R, Context>(
|
||||
async function defaultHook<A extends RPCRequest, R, Context>(
|
||||
details: InvocationDetails<A, Context>,
|
||||
invoke: () => Promise<R>,
|
||||
): Promise<R> {
|
||||
@ -33,7 +33,7 @@ async function defaultHook<A extends IRequest, R, Context>(
|
||||
export function jsonrpc<Context>(
|
||||
getContext: TGetContext<Context>,
|
||||
logger: Logger,
|
||||
hook: <A extends IRequest, R>(
|
||||
hook: <A extends RPCRequest, R>(
|
||||
details: InvocationDetails<A, Context>,
|
||||
invoke: (request?: A) => Promise<R>) => Promise<R> = defaultHook,
|
||||
idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user