Skip to content

Commit

Permalink
feat(vue): adds passThrough prop to Can
Browse files Browse the repository at this point in the history
Fixes #105
  • Loading branch information
stalniy committed Nov 25, 2018
1 parent 2a29d26 commit 28ca883
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 20 deletions.
15 changes: 15 additions & 0 deletions packages/casl-vue/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/casl-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@
"npm": "^6.0.0"
},
"peerDependencies": {
"@casl/ability": "^2.0.0-alpha.1",
"@casl/ability": "^2.0.0",
"vue": "^2.0.0"
},
"devDependencies": {
"@casl/ability": "^2.0.0",
"@vue/test-utils": "^1.0.0-beta.12",
"@vue/test-utils": "^1.0.0-beta.25",
"vue": "^2.5.13",
"vue-template-compiler": "^2.5.13"
}
Expand Down
64 changes: 49 additions & 15 deletions packages/casl-vue/spec/can.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,38 +57,72 @@ describe('`Can` component', () => {
expect(wrapper.contains('h1')).to.be.false
})

xdescribe('props validation', () => {
// beforeAll(() => {
// spy.on(Vue.config, 'errorHandler', (error, vm) => vm.error = error)
// })
describe('`passThrough` property', () => {
let scopedSlot

beforeEach(() => {
scopedSlot = spy()
render((h) => h('div', [
h(Can, {
props: { I: 'delete', a: 'Plugin', passThrough: true },
scopedSlots: { default: scopedSlot }
})
]))
})

it('allows to always render scoped slot', () => {
expect(scopedSlot).to.have.been.called()
})

it('passes `allowed` and `ability` vars into scoped slot', () => {
expect(scopedSlot).to.have.been.called.with({ ability, allowed: false })
})
})

// afterAll(() => {
// spy.restore(Vue.config, 'errorHandler')
// })
describe('props validation', () => {
beforeAll(() => {
spy.on(console, 'error', () => {})
})

afterAll(() => {
spy.restore(console, 'error')
})

it('throws error if action (i.e., `I` or `do`) is not specified', () => {
const wrapper = render(`
expect(() => render(`
<Can a="Plugin">
<h1></h1>
</Can>
`)

expect(wrapper.vm.error).to.match(/`I` nor `do` property exist/)
`)).to.throw(/`I` nor `do` property exist/)
})

it('throws error if subject (i.e., `a`, `of`, `this` or `on`) is not specified', () => {
const wrapper = render(`
expect(() => render(`
<Can I="read">
<h1></h1>
</Can>
`)
`)).to.throw(/`of` nor `a` nor `this` nor `on` property exist/)
})

expect(wrapper.vm.error).to.match(/`of` nor `a` nor `this` nor `on` property exist/)
it('throws error if `passThrough` is passed without scoped slot', () => {
expect(() => render(`
<Can I="read" a="Post" passThrough>
<h1></h1>
</Can>
`)).to.throw(/`passThrough` expects default scoped slot/)
})
})

function render(template) {
return mount({ template: `<div>${template.trim()}</div>` }, {
const defs = {}

if (typeof template === 'function') {
defs.render = template
} else {
defs.template = `<div>${template.trim()}</div>`
}

return mount(defs, {
localVue: LocalVue
})
}
Expand Down
20 changes: 17 additions & 3 deletions packages/casl-vue/src/component/can.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export default {
of: [String, Function, Object],
this: [String, Function, Object],
on: [String, Function, Object],
not: { type: Boolean, default: false }
not: Boolean,
passThrough: Boolean,
},
render(h, { props, children, parent }) {
render(h, { props, children, parent, data }) {
const [action, field] = (props.I || props.do || '').split(' ');
const subject = props.of || props.a || props.this || props.on;

Expand All @@ -22,6 +23,19 @@ export default {
throw new Error('[Vue Can]: neither `of` nor `a` nor `this` nor `on` property exist');
}

return props.not ^ parent.$can(action, subject, field) ? children : null;
const allowed = !!(props.not ^ parent.$can(action, subject, field));

if (!props.passThrough) {
return allowed ? children : null;
}

if (!data.scopedSlots || !data.scopedSlots.default) {
throw new Error('[Vue Can]: `passThrough` expects default scoped slot to be specified')
}

return data.scopedSlots.default({
allowed,
ability: parent.$ability,
});
}
};

0 comments on commit 28ca883

Please sign in to comment.