Skip to content

Commit

Permalink
Merge pull request #30 from jmeas/3.1.0
Browse files Browse the repository at this point in the history
3.1.0
  • Loading branch information
jamesplease authored Feb 9, 2018
2 parents 02d11f8 + a96302d commit f1233ab
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 6 deletions.
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# Changelog

### v3.0.1 (2018/1/3)
### v3.1.0 (2018/2/8)

**New Features**

* Within the `components` array, you may now specify a `function` that returns
a [React Element](https://reactjs.org/docs/glossary.html#elements).
The function will be called with the currently accumulated results.

### v3.0.1 (2018/2/3)

**Bug Fixes**

* Ensures the array passed to the render prop is a new object each time

### v3.0.0 (2018/1/2)
### v3.0.0 (2018/2/3)

React's new Context API has been finalized, and it uses functional `children` rather than a prop
named `render`. Accordingly, this library has been updated to use `children` as the default.
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,25 @@ follows:

##### `components`

The render prop components to compose.
The render prop components to compose. This is an array of [React elements](https://reactjs.org/docs/glossary.html#elements) and/or functions that return elements given the currently accumulated results.

```jsx
<Composer
components={[
// Simple elements may be passed where previous results are not required.
<Outer />,
// A function may be passed that will be invoked with the currently accumulated results.
// Functions provided must return a valid React element.
([outerResults]) => <Middle results={[outerResults]} />,
([outerResults, middleResults]) => (
<Inner results={[outerResults, middleResults]} />
)
]}>
{([outerResults, middleResults, innerResults]) => {
/* ... */
}}
</Composer>
```

> Note: You do not need to specify the render prop on the components. If you do specify the render prop, it will
> be ignored.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-composer",
"version": "3.0.1",
"version": "3.1.0",
"description": "Compose render prop components",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down
12 changes: 10 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ export default function Composer({
}

const componentIndex = childrenComponents.length - 1;
const component = components[componentIndex];

const component =
// Each props.components entry is either an element or function [element factory]
// When it is a function, produce an element by invoking it with currently accumulated results.
typeof components[componentIndex] === 'function'
? components[componentIndex](results)
: components[componentIndex];

// This is the index of where we should place the response within `results`.
// It's not the same as `componentIndex` because we reversed the components when
Expand Down Expand Up @@ -57,7 +63,9 @@ export default function Composer({

Composer.propTypes = {
children: PropTypes.func,
components: PropTypes.array,
components: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.element, PropTypes.func])
),
renderPropName: PropTypes.string,
mapResult: PropTypes.func
};
Expand Down
40 changes: 40 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import React from 'react';
import { shallow, mount } from 'enzyme';
import Composer from '../src';

// Does nothing other than render so that its props may be inspected
function MyComponent(props) {
return <div>Inspect my props!</div>;
}

function Echo({ value, children }) {
return children({ value });
}
Expand Down Expand Up @@ -92,6 +97,41 @@ describe('React Composer', () => {
});
});

describe('Render, three components; elements and functions', () => {
test('It supports both elements and functions in props.components', () => {
const wrapper = mount(
<Composer
components={[
// Simple elements may be passed where previous results are not required.
<Echo value="outer" />,

// A function [element factory] may be passed that is invoked with
// the currently accumulated results to produce an element.
([outerResult]) => {
expect(outerResult).toEqual({ value: 'outer' });
return <Echo value={`${outerResult.value} + middle`} />;
},

([outerResult, middleResult]) => {
// Assert within element factory to avoid insane error messages for failed tests :)
expect(outerResult).toEqual({ value: 'outer' });
expect(middleResult).toEqual({ value: 'outer + middle' });
return <Echo value={`${middleResult.value} + inner`} />;
}
]}
children={results => <MyComponent results={results} />}
/>
);

expect(wrapper.find(Echo).length).toEqual(3);
expect(wrapper.find(MyComponent).prop('results')).toEqual([
{ value: 'outer' },
{ value: 'outer + middle' },
{ value: 'outer + middle + inner' }
]);
});
});

describe('Render, one component; custom `renderPropName`', () => {
test('It should render the expected result', () => {
const mockChildren = jest.fn(([result]) => <div>{result.value}</div>);
Expand Down

0 comments on commit f1233ab

Please sign in to comment.