Skip to content

Commit

Permalink
feat(react:can): adds passThrough option
Browse files Browse the repository at this point in the history
Relates to #105
  • Loading branch information
stalniy committed Aug 31, 2018
1 parent 748ea64 commit 045318c
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 8 deletions.
10 changes: 9 additions & 1 deletion packages/casl-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ For example, the code below reads as `Can I create a Post`.

There are several other property aliases which allow to construct a readable question:

* use `a` alias when you check by Type
* use `a` (or `an`) alias when you check by Type

```jsx
<Can I="read" a="Post">...</Can>
Expand Down Expand Up @@ -227,6 +227,14 @@ There are several other property aliases which allow to construct a readable que
<Can not I="read" a="Post">...</Can>
```

* use `passThrough` if you want to customize behavior of `<Can>` component, for example disable button instead of hiding it:

```jsx
<Can I="read" a="Post" passThrough>
{can => <button disabled={!can}>Save</button>}
</Can>
```


## Want to help?

Expand Down
13 changes: 11 additions & 2 deletions packages/casl-react/spec/Can.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ describe('`Can` component', () => {
expect(() => validateProps(Can, props)).not.to.throw(Error)
})

it('passes ability instance as an argument to "children" function', () => {
it('passes ability check value and instance as arguments to "children" function', () => {
const component = renderer.create(e(Can, { I: 'read', a: 'Post', ability }, children))

expect(children).to.have.been.called.with.exactly(ability)
expect(children).to.have.been.called.with.exactly(ability.can('read', 'Post'), ability)
})

it('requires to pass "a" or "this" or "of" as string or object', () => {
Expand Down Expand Up @@ -154,6 +154,15 @@ describe('`Can` component', () => {

expect(component.toJSON()).to.deep.equal(renderedChildren)
})

it('always renders children if `passThrough` prop is `true`', () => {
const component = renderer.create(
e(Can, { I: 'delete', a: 'Post', passThrough: true, ability }, child)
)

expect(ability.can('delete', 'Post')).to.be.false
expect(component.toJSON().children).to.deep.equal([child.props.children])
})
})

function validateProps(Component, props, propName) {
Expand Down
4 changes: 2 additions & 2 deletions packages/casl-react/spec/factory.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('Factory methods which create `Can` component', () => {
const element = e(ContextualCan, { I: 'read', a: 'Post', ability }, child)
const component = renderer.create(element)

expect(child).to.have.been.called.with.exactly(ability)
expect(child).to.have.been.called.with(ability)
})

it('expects `Ability` instance to be provided by context Provider', () => {
Expand All @@ -63,7 +63,7 @@ describe('Factory methods which create `Can` component', () => {
)
const component = renderer.create(App)

expect(child).to.have.been.called.with.exactly(ability)
expect(child).to.have.been.called.with(ability)
})
})
})
6 changes: 4 additions & 2 deletions packages/casl-react/src/Can.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default class Can extends PureComponent {
do: alias('I', PropTypes.string.isRequired),
on: alias('this a of an', REQUIRED_OBJECT_OR_STRING),
not: PropTypes.bool,
passThrough: PropTypes.bool,
children: PropTypes.any.isRequired,
ability: PropTypes.instanceOf(Ability).isRequired
};
Expand Down Expand Up @@ -86,13 +87,14 @@ export default class Can extends PureComponent {
}

render() {
return this.state.allowed ? this.renderChildren() : null;
const canRender = this.props.passThrough || this.state.allowed;
return canRender ? this.renderChildren() : null;
}

renderChildren() {
const { children } = this.props;
const elements = typeof children === 'function'
? children(this.state.ability)
? children(this.state.allowed, this.state.ability)
: children;

return renderChildren(elements);
Expand Down
3 changes: 2 additions & 1 deletion packages/casl-react/src/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export function createContextualCan(Consumer) {
I: props.I || props.do,
a: props.on || props.a || props.an || props.of || props.this,
not: props.not,
children: props.children
children: props.children,
passThrough: props.passThrough,
}));
};
}

0 comments on commit 045318c

Please sign in to comment.