diff --git a/packages/casl-react/README.md b/packages/casl-react/README.md index 977935e50..0646a978c 100644 --- a/packages/casl-react/README.md +++ b/packages/casl-react/README.md @@ -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 ... @@ -227,6 +227,14 @@ There are several other property aliases which allow to construct a readable que ... ``` +* use `passThrough` if you want to customize behavior of `` component, for example disable button instead of hiding it: + +```jsx + + {can => } + +``` + ## Want to help? diff --git a/packages/casl-react/spec/Can.spec.js b/packages/casl-react/spec/Can.spec.js index d65927246..63ac340bc 100644 --- a/packages/casl-react/spec/Can.spec.js +++ b/packages/casl-react/spec/Can.spec.js @@ -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', () => { @@ -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) { diff --git a/packages/casl-react/spec/factory.spec.js b/packages/casl-react/spec/factory.spec.js index fb3ed88fc..6814d6f72 100644 --- a/packages/casl-react/spec/factory.spec.js +++ b/packages/casl-react/spec/factory.spec.js @@ -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', () => { @@ -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) }) }) }) diff --git a/packages/casl-react/src/Can.js b/packages/casl-react/src/Can.js index d4890cc32..c98731abd 100644 --- a/packages/casl-react/src/Can.js +++ b/packages/casl-react/src/Can.js @@ -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 }; @@ -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); diff --git a/packages/casl-react/src/factory.js b/packages/casl-react/src/factory.js index 8c2fda317..747a8fcb9 100644 --- a/packages/casl-react/src/factory.js +++ b/packages/casl-react/src/factory.js @@ -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, })); }; }