Skip to content

Commit

Permalink
update to latest kcdshop
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed May 2, 2023
1 parent 45adc40 commit 45ff3eb
Show file tree
Hide file tree
Showing 73 changed files with 2,164 additions and 623 deletions.
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions exercises/03.compound-components/02.problem.validation/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Toggle, ToggleOn, ToggleOff, ToggleButton } from './toggle'

export function App() {
return (
<div>
<Toggle>
<ToggleOn>The button is on</ToggleOn>
<ToggleOff>The button is off</ToggleOff>
<div>
<ToggleButton />
</div>
</Toggle>
</div>
)
}
34 changes: 34 additions & 0 deletions exercises/03.compound-components/02.problem.validation/toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react'
import { Switch } from '~/shared/switch'

type ToggleValue = { on: boolean; toggle: () => void }
const ToggleContext = React.createContext<ToggleValue | undefined>(undefined)
ToggleContext.displayName = 'ToggleContext'

export function Toggle({ children }: { children: React.ReactNode }) {
const [on, setOn] = React.useState(false)
const toggle = () => setOn(!on)

return (
<ToggleContext.Provider value={{ on, toggle }}>
{children}
</ToggleContext.Provider>
)
}

export function ToggleOn({ children }: { children: React.ReactNode }) {
const { on } = React.useContext(ToggleContext)!
return <>{on ? children : null}</>
}

export function ToggleOff({ children }: { children: React.ReactNode }) {
const { on } = React.useContext(ToggleContext)!
return <>{on ? null : children}</>
}

export function ToggleButton({
...props
}: Omit<React.ComponentProps<typeof Switch>, 'on' | 'onClick'>) {
const { on, toggle } = React.useContext(ToggleContext)!
return <Switch {...props} on={on} onClick={toggle} />
}
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions exercises/05.state-initializer/02.problem.initial/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Switch } from '~/shared/switch'
import { useToggle } from './toggle'

export function App() {
const { on, getTogglerProps, getResetterProps } = useToggle({
initialOn: true,
})

return (
<div>
<Switch {...getTogglerProps({ on: on })} />
<button {...getResetterProps({})}>Reset</button>
</div>
)
}
61 changes: 61 additions & 0 deletions exercises/05.state-initializer/02.problem.initial/toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react'

function callAll<Args extends Array<unknown>>(
...fns: Array<((...args: Args) => unknown) | undefined>
) {
return (...args: Args) => fns.forEach(fn => fn?.(...args))
}

type ToggleState = { on: boolean }
type ToggleAction =
| { type: 'toggle' }
| { type: 'reset'; initialState: ToggleState }

function toggleReducer(state: ToggleState, action: ToggleAction) {
switch (action.type) {
case 'toggle': {
return { on: !state.on }
}
case 'reset': {
return action.initialState
}
}
}

export function useToggle({ initialOn = false } = {}) {
const initialState = { on: initialOn }
const [state, dispatch] = React.useReducer(toggleReducer, initialState)
const { on } = state

const toggle = () => dispatch({ type: 'toggle' })
const reset = () => dispatch({ type: 'reset', initialState })

function getTogglerProps<Props>({
onClick,
...props
}: { onClick?: React.DOMAttributes<HTMLButtonElement>['onClick'] } & Props) {
return {
'aria-checked': on,
onClick: callAll(onClick, toggle),
...props,
}
}

function getResetterProps<Props>({
onClick,
...props
}: { onClick?: React.DOMAttributes<HTMLButtonElement>['onClick'] } & Props) {
return {
onClick: callAll(onClick, reset),
...props,
}
}

return {
on,
reset,
toggle,
getTogglerProps,
getResetterProps,
}
}
File renamed without changes.
1 change: 1 addition & 0 deletions exercises/06.state-reducer/01.problem/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '/switch.styles.css';
6 changes: 6 additions & 0 deletions exercises/06.state-reducer/01.problem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as ReactDOM from 'react-dom/client'
import { App } from './app'

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
46 changes: 46 additions & 0 deletions exercises/06.state-reducer/02.problem.default/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react'
import { Switch } from '~/shared/switch'
import { useToggle } from './toggle'

export function App() {
const [timesClicked, setTimesClicked] = React.useState(0)
const clickedTooMuch = timesClicked >= 4

const { on, getTogglerProps, getResetterProps } = useToggle({
reducer(state, action) {
switch (action.type) {
case 'toggle': {
if (clickedTooMuch) {
return state
}
return { on: !state.on }
}
case 'reset': {
return { on: false }
}
}
},
})

return (
<div>
<Switch
{...getTogglerProps({
on: on,
onClick: () => setTimesClicked(count => count + 1),
})}
/>
{clickedTooMuch ? (
<div data-testid="notice">
Whoa, you clicked too much!
<br />
</div>
) : timesClicked > 0 ? (
<div data-testid="click-count">Click count: {timesClicked}</div>
) : null}
<button {...getResetterProps({ onClick: () => setTimesClicked(0) })}>
Reset
</button>
</div>
)
}
1 change: 1 addition & 0 deletions exercises/06.state-reducer/02.problem.default/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '/switch.styles.css';
6 changes: 6 additions & 0 deletions exercises/06.state-reducer/02.problem.default/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as ReactDOM from 'react-dom/client'
import { App } from './app'

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
61 changes: 61 additions & 0 deletions exercises/06.state-reducer/02.problem.default/toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react'

function callAll<Args extends Array<unknown>>(
...fns: Array<((...args: Args) => unknown) | undefined>
) {
return (...args: Args) => fns.forEach(fn => fn?.(...args))
}

type ToggleState = { on: boolean }
type ToggleAction =
| { type: 'toggle' }
| { type: 'reset'; initialState: ToggleState }

function toggleReducer(state: ToggleState, action: ToggleAction) {
switch (action.type) {
case 'toggle': {
return { on: !state.on }
}
case 'reset': {
return action.initialState
}
}
}

export function useToggle({ initialOn = false, reducer = toggleReducer } = {}) {
const { current: initialState } = React.useRef<ToggleState>({ on: initialOn })
const [state, dispatch] = React.useReducer(reducer, initialState)
const { on } = state

const toggle = () => dispatch({ type: 'toggle' })
const reset = () => dispatch({ type: 'reset', initialState })

function getTogglerProps<Props>({
onClick,
...props
}: { onClick?: React.DOMAttributes<HTMLButtonElement>['onClick'] } & Props) {
return {
'aria-checked': on,
onClick: callAll(onClick, toggle),
...props,
}
}

function getResetterProps<Props>({
onClick,
...props
}: { onClick?: React.DOMAttributes<HTMLButtonElement>['onClick'] } & Props) {
return {
onClick: callAll(onClick, reset),
...props,
}
}

return {
on,
reset,
toggle,
getTogglerProps,
getResetterProps,
}
}
File renamed without changes.
1 change: 1 addition & 0 deletions exercises/07.control-props/01.problem/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '/switch.styles.css';
6 changes: 6 additions & 0 deletions exercises/07.control-props/01.problem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as ReactDOM from 'react-dom/client'
import { App } from './app'

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
47 changes: 47 additions & 0 deletions exercises/07.control-props/02.problem.warnings/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from 'react'
import { Toggle, type ToggleAction, type ToggleState } from './toggle'

export function App() {
const [bothOn, setBothOn] = React.useState(false)
const [timesClicked, setTimesClicked] = React.useState(0)

function handleToggleChange(state: ToggleState, action: ToggleAction) {
if (action.type === 'toggle' && timesClicked > 4) {
return
}
setBothOn(state.on)
setTimesClicked(c => c + 1)
}

function handleResetClick() {
setBothOn(false)
setTimesClicked(0)
}

return (
<div>
<div>
<Toggle on={bothOn} onChange={handleToggleChange} />
<Toggle on={bothOn} onChange={handleToggleChange} />
</div>
{timesClicked > 4 ? (
<div data-testid="notice">
Whoa, you clicked too much!
<br />
</div>
) : (
<div data-testid="click-count">Click count: {timesClicked}</div>
)}
<button onClick={handleResetClick}>Reset</button>
<hr />
<div>
<div>Uncontrolled Toggle:</div>
<Toggle
onChange={(...args) =>
console.info('Uncontrolled Toggle onChange', ...args)
}
/>
</div>
</div>
)
}
1 change: 1 addition & 0 deletions exercises/07.control-props/02.problem.warnings/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '/switch.styles.css';
6 changes: 6 additions & 0 deletions exercises/07.control-props/02.problem.warnings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as ReactDOM from 'react-dom/client'
import { App } from './app'

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
Loading

0 comments on commit 45ff3eb

Please sign in to comment.