Skip to content

Instantly share code, notes, and snippets.

@esmevane
Created July 28, 2021 16:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save esmevane/628d87594abac733ad3cdaa2cd573534 to your computer and use it in GitHub Desktop.
Save esmevane/628d87594abac733ad3cdaa2cd573534 to your computer and use it in GitHub Desktop.
[ React / Typescript ]: Example of an architectural journey with a React Toggle component
/* eslint-disable @typescript-eslint/require-await */
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createContext, useContext, useReducer } from 'react';
type Events = { type: 'toggle' } | { type: 'reset' };
interface EventMap {
toggle: () => void;
reset: () => void;
}
interface State {
active: boolean;
dangerLevel: number;
}
const initial: State = { active: false, dangerLevel: 1 };
const update = (state: State, event: Events): State => {
switch (event.type) {
case 'toggle':
return { active: !state.active, dangerLevel: state.dangerLevel + 1 };
case 'reset':
return initial;
}
};
const ToggleStateContext = createContext<State>(initial);
const ToggleEventContext = createContext<EventMap>({
reset: () => undefined,
toggle: () => undefined,
});
function useToggle() {
const [state, dispatch] = useReducer(update, initial);
const events = {
toggle: () => dispatch({ type: 'toggle' }),
reset: () => dispatch({ type: 'reset' }),
};
return [state, events] as const;
}
function useToggleEvents() {
return useContext(ToggleEventContext);
}
function ToggleReset() {
const events = useToggleEvents();
return <button onClick={events.reset}>Reset</button>;
}
function Toggle(props: React.PropsWithChildren<unknown>) {
const [state, events] = useToggle();
return (
<ToggleStateContext.Provider value={state}>
<ToggleEventContext.Provider value={events}>
{state.dangerLevel > 5 ? (
<>
<span>Explosions!</span>
{props.children}
</>
) : (
<>
<button onClick={events.toggle}>Toggle</button>
{state.active ? 'On' : 'Off'}
</>
)}
</ToggleEventContext.Provider>
</ToggleStateContext.Provider>
);
}
test('toggles start off disabled', async () => {
const component = render(<Toggle />);
await component.findByText('Off');
});
test('toggles on when clicked', async () => {
const component = render(<Toggle />);
userEvent.click(await component.findByText('Toggle'));
await component.findByText('On');
});
test('toggles off again', async () => {
const component = render(<Toggle />);
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
await component.findByText('Off');
});
test('toggles can break', async () => {
const component = render(<Toggle />);
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
await component.findByText('Explosions!');
});
test('toggles can reset after breaking', async () => {
const component = render(
<Toggle>
<ToggleReset />
</Toggle>
);
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
userEvent.click(await component.findByText('Toggle'));
await component.findByText('Explosions!');
userEvent.click(await component.findByText('Reset'));
await component.findByText('Off');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment