Skip to content

Commit 29e9a1d

Browse files
lucasconstantinopedronauck
authored andcommitted
Add mapper functions. (#3)
feat: add support to pass a function as mapper value
1 parent 28d6c59 commit 29e9a1d

File tree

2 files changed

+62
-17
lines changed

2 files changed

+62
-17
lines changed

src/index.test.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,52 @@ test('rendering children component', () => {
5454
expect(result.html()).toBe('<div>foobar</div>')
5555
})
5656

57+
test('should allow a function as mapper', () => {
58+
const Foo = ({ children }) => children('foo')
59+
const foo = jest.fn(({ renderProp }) => <Foo children={renderProp} />)
60+
const children = jest.fn(() => null)
61+
const Composed = adopt({ foo })
62+
63+
mount(<Composed>{children}</Composed>)
64+
65+
expect(foo).toHaveBeenCalled()
66+
expect(children).toHaveBeenCalledWith({ foo: 'foo' })
67+
})
68+
69+
test('should provide a function mapper with all previous render prop results', () => {
70+
const Foo = ({ children }) => children('foo')
71+
const Bar = ({ children }) => children('bar')
72+
const bar = jest.fn(({ renderProp }) => <Bar children={renderProp} />)
73+
const children = jest.fn(() => null)
74+
75+
interface RenderProps {
76+
foo: 'foo'
77+
bar: 'bar'
78+
}
79+
80+
const Composed = adopt<RenderProps>({
81+
foo: <Foo />,
82+
bar,
83+
})
84+
85+
mount(<Composed>{children}</Composed>)
86+
87+
expect(bar.mock.calls[0][0]).toHaveProperty('foo', 'foo')
88+
expect(children).toHaveBeenCalledWith({ foo: 'foo', bar: 'bar' })
89+
})
90+
91+
test('should provide mapper functions with Composed component props', () => {
92+
const Foo = ({ children }) => children('foo')
93+
const foo = jest.fn(({ renderProp }) => <Foo children={renderProp} />)
94+
const children = jest.fn(() => null)
95+
const Composed = adopt({ foo })
96+
97+
mount(<Composed bar="bar">{children}</Composed>)
98+
99+
expect(foo.mock.calls[0][0]).toHaveProperty('bar', 'bar')
100+
expect(children).toHaveBeenCalledWith({ foo: 'foo' })
101+
})
102+
57103
test('throw with a wrong value on mapper', () => {
58104
expect(() => {
59105
const Composed = adopt({ foo: 'helo' })

src/index.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import * as React from 'react'
22
import { ComponentType, ReactNode, ReactElement } from 'react'
33

4-
const isAllElementValid = (values: ReactNode[]): boolean =>
5-
!values.some(value => !React.isValidElement(value))
6-
74
export type ChildrenFn<P> = (props: P) => ReactNode
85

96
export type RPC<Props> = ComponentType<{
@@ -12,31 +9,33 @@ export type RPC<Props> = ComponentType<{
129

1310
export type Mapper<R> = Record<keyof R, ReactElement<any> | any>
1411

12+
const isValidRenderProp = (prop: ReactNode | ChildrenFn): boolean =>
13+
React.isValidElement(prop) || typeof prop === 'function'
14+
15+
const Children = ({ children }: any) => children()
16+
1517
export function adopt<RP extends Record<string, any>>(
1618
mapper: Mapper<RP>
1719
): RPC<RP> {
18-
if (!isAllElementValid(Object.values(mapper))) {
20+
if (!Object.values(mapper).some(isValidRenderProp)) {
1921
throw new Error(
2022
'The render props object mapper just accept valid elements as value'
2123
)
2224
}
2325

24-
const Initial = ({ children }: any) =>
25-
children && typeof children === 'function' && children()
26-
2726
return Object.keys(mapper).reduce(
28-
(Component: RPC<RP>, key: keyof RP): RPC<RP> => ({ children }) => (
27+
(Component: RPC<RP>, key: keyof RP): RPC<RP> => ({ children, ...rest }) => (
2928
<Component>
30-
{props =>
31-
React.cloneElement(mapper[key], {
32-
children: (childProps: any) =>
33-
children &&
34-
typeof children === 'function' &&
35-
children(Object.assign({}, props, { [key]: childProps })),
36-
})
37-
}
29+
{props => {
30+
const renderProp = (childProps: any) =>
31+
children(Object.assign({}, props, { [key]: childProps }))
32+
33+
return typeof mapper[key] === 'function'
34+
? mapper[key](Object.assign({}, rest, props, { renderProp }))
35+
: React.cloneElement(mapper[key], { children: renderProp })
36+
}}
3837
</Component>
3938
),
40-
Initial
39+
Children
4140
)
4241
}

0 commit comments

Comments
 (0)