Created
June 2, 2020 19:59
-
-
Save esmevane/b9cfdd1551c77b285c0c7c74c2adb980 to your computer and use it in GitHub Desktop.
[ Typescript / React ]: Login kata 06-02-2020
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { render, fireEvent } from '@testing-library/react'; | |
import App, { AppContext } from './App'; | |
const SpiesOnApp = () => { | |
const context = React.useContext(AppContext); | |
return <>{JSON.stringify(context)}</>; | |
}; | |
test('tracks username', async () => { | |
const listener = jest.fn(); | |
const username = 'sir_farblegart'; | |
const password = 'nevergonnaguess'; | |
const testingLibraryAssertions = render( | |
<App onCreateUser={listener}> | |
<SpiesOnApp /> | |
</App> | |
); | |
fireEvent.input( | |
(await testingLibraryAssertions.findByPlaceholderText( | |
'username' | |
)) as HTMLInputElement, | |
{ | |
target: { | |
value: username, | |
}, | |
} | |
); | |
fireEvent.input( | |
(await testingLibraryAssertions.findByPlaceholderText( | |
'password' | |
)) as HTMLInputElement, | |
{ | |
target: { | |
value: password, | |
}, | |
} | |
); | |
fireEvent.click(await testingLibraryAssertions.findByRole('button')); | |
expect(listener).toHaveBeenCalledWith(username, password); | |
testingLibraryAssertions.debug(); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import './App.css'; | |
interface AppProps { | |
onCreateUser: (username: string, password: string) => void; | |
} | |
export const AppContext = React.createContext({} as any); | |
export const AppFields = React.createContext({} as any); | |
type Interface<State, Event, View, Effect> = ( | |
state: State, | |
event: Event | |
) => [View, Effect]; | |
const UpdatePassword: 'UPDATE_PASSWORD' = 'UPDATE_PASSWORD'; | |
const UpdateUsername: 'UPDATE_USERNAME' = 'UPDATE_USERNAME'; | |
interface AppState { | |
username: string; | |
password: string; | |
} | |
type AppEvent = | |
| { type: typeof UpdatePassword; password: string } | |
| { type: typeof UpdateUsername; username: string }; | |
const initialState: AppState = { username: '', password: '' }; | |
const reducer = (state: AppState, event: AppEvent) => { | |
switch (event.type) { | |
case UpdateUsername: | |
return { ...state, username: event.username }; | |
case UpdatePassword: | |
return { ...state, password: event.password }; | |
default: | |
return state; | |
} | |
}; | |
const useUserForm = () => { | |
const [state, dispatch] = React.useReducer(reducer, initialState); | |
const updatePassword = (password: string) => | |
dispatch({ type: 'UPDATE_PASSWORD', password }); | |
const updateUsername = (username: string) => | |
dispatch({ type: 'UPDATE_USERNAME', username }); | |
const events = { | |
updatePassword: (event: any) => updatePassword(event.target.value), | |
updateUsername: (event: any) => updateUsername(event.target.value), | |
}; | |
const fields = { | |
password: { | |
onChange: events.updatePassword, | |
name: 'password', | |
type: 'password', | |
value: state.password, | |
placeholder: 'password', | |
}, | |
username: { | |
onChange: events.updateUsername, | |
name: 'username', | |
type: 'text', | |
value: state.username, | |
placeholder: 'username', | |
}, | |
}; | |
return [state, fields] as [typeof state, typeof fields]; | |
}; | |
const Field: React.FC<{ type: 'username' | 'password' }> = ({ type }) => { | |
const fields = React.useContext(AppFields); | |
const fieldProps = fields[type]; | |
return ( | |
<> | |
<input {...fieldProps} /> | |
</> | |
); | |
}; | |
const App: React.FC<AppProps> = ({ children, onCreateUser }) => { | |
const [state, fields] = useUserForm(); | |
return ( | |
<AppContext.Provider value={state}> | |
<AppFields.Provider value={fields}> | |
<form | |
className="App" | |
onSubmit={(event) => { | |
event.preventDefault(); | |
onCreateUser(state.username, state.password); | |
}} | |
> | |
<Field type="username" /> | |
<Field type="password" /> | |
<button>Submit</button> | |
</form> | |
{children} | |
</AppFields.Provider> | |
</AppContext.Provider> | |
); | |
}; | |
export default App; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment