mirror of
https://gitee.com/ElemeFE/element.git
synced 2024-11-30 03:07:36 +08:00
Clickoutside: add test & improve vue-popper test (#577)
This commit is contained in:
parent
3317f1f7cd
commit
706d47b710
@ -8,7 +8,7 @@ Object.keys(Components).forEach(function(key) {
|
||||
externals[`element-ui/packages/${key}/style.css`] = `element-ui/lib/${key}/style.css`;
|
||||
});
|
||||
|
||||
Object.keys(dependencies).forEach(function (key) {
|
||||
Object.keys(dependencies).forEach(function(key) {
|
||||
externals[key] = key;
|
||||
});
|
||||
|
||||
|
@ -49,7 +49,7 @@ export default {
|
||||
const popper = this.popper || this.$refs.popper;
|
||||
|
||||
if (!reference && this.$slots.reference && this.$slots.reference[0]) {
|
||||
reference = this.$slots.reference[0].elm;
|
||||
reference = this.referenceElm = this.$slots.reference[0].elm;
|
||||
}
|
||||
if (this.trigger === 'click') {
|
||||
on(reference, 'click', () => { this.showPopper = !this.showPopper; });
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { on } from 'wind-dom/src/event';
|
||||
|
||||
const nodeList = [];
|
||||
const ctx = '@@clickoutsideContext';
|
||||
|
||||
on(document, 'click', e => {
|
||||
nodeList.forEach(node => node[clickoutsideContext].documentHandler(e));
|
||||
nodeList.forEach(node => node[ctx].documentHandler(e));
|
||||
});
|
||||
/**
|
||||
* v-clickoutside
|
||||
@ -12,23 +14,24 @@ on(document, 'click', e => {
|
||||
* <div v-element-clickoutside="handleClose">
|
||||
* ```
|
||||
*/
|
||||
const clickoutsideContext = '@@clickoutsideContext';
|
||||
|
||||
export default {
|
||||
bind(el, binding, vnode) {
|
||||
const id = nodeList.push(el) - 1;
|
||||
const documentHandler = function(e) {
|
||||
if (!vnode.context ||
|
||||
el.contains(e.target) ||
|
||||
!vnode.context.popperElm ||
|
||||
vnode.context.popperElm.contains(e.target)) return;
|
||||
(vnode.context.popperElm &&
|
||||
vnode.context.popperElm.contains(e.target))) return;
|
||||
|
||||
if (binding.expression) {
|
||||
vnode.context[el[clickoutsideContext].methodName]();
|
||||
el[ctx].methodName &&
|
||||
vnode.context[el[ctx].methodName] &&
|
||||
vnode.context[el[ctx].methodName]();
|
||||
} else {
|
||||
el[clickoutsideContext].bindingFn();
|
||||
el[ctx].bindingFn && el[ctx].bindingFn();
|
||||
}
|
||||
};
|
||||
el[clickoutsideContext] = {
|
||||
el[ctx] = {
|
||||
id,
|
||||
documentHandler,
|
||||
methodName: binding.expression,
|
||||
@ -37,15 +40,17 @@ export default {
|
||||
},
|
||||
|
||||
update(el, binding) {
|
||||
el[clickoutsideContext].methodName = binding.expression;
|
||||
el[clickoutsideContext].bindingFn = binding.value;
|
||||
el[ctx].methodName = binding.expression;
|
||||
el[ctx].bindingFn = binding.value;
|
||||
},
|
||||
|
||||
unbind(el) {
|
||||
nodeList.splice(el[clickoutsideContext].id, 1);
|
||||
nodeList.splice(el[ctx].id, 1);
|
||||
delete el[ctx];
|
||||
},
|
||||
|
||||
install(Vue) {
|
||||
/* istanbul ignore next */
|
||||
Vue.directive('clickoutside', {
|
||||
bind: this.bind,
|
||||
unbind: this.unbind
|
||||
|
@ -4,6 +4,7 @@
|
||||
* version: 0.5.3
|
||||
**/
|
||||
|
||||
/* istanbul ignore next */
|
||||
const requestFrame = (function() {
|
||||
const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
@ -14,6 +15,7 @@ const requestFrame = (function() {
|
||||
};
|
||||
})();
|
||||
|
||||
/* istanbul ignore next */
|
||||
const cancelFrame = (function() {
|
||||
const cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout;
|
||||
return function(id) {
|
||||
@ -21,6 +23,7 @@ const cancelFrame = (function() {
|
||||
};
|
||||
})();
|
||||
|
||||
/* istanbul ignore next */
|
||||
const resetTrigger = function(element) {
|
||||
const trigger = element.__resizeTrigger__;
|
||||
const expand = trigger.firstElementChild;
|
||||
@ -35,10 +38,12 @@ const resetTrigger = function(element) {
|
||||
expand.scrollTop = expand.scrollHeight;
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
const checkTriggers = function(element) {
|
||||
return element.offsetWidth !== element.__resizeLast__.width || element.offsetHeight !== element.__resizeLast__.height;
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
const scrollListener = function(event) {
|
||||
resetTrigger(this);
|
||||
if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
|
||||
@ -62,6 +67,7 @@ let animation = false;
|
||||
let keyFramePrefix = '';
|
||||
let animationStartEvent = 'animationstart';
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!attachEvent) {
|
||||
const testElement = document.createElement('fakeelement');
|
||||
if (testElement.style.animationName !== undefined) {
|
||||
@ -83,6 +89,7 @@ if (!attachEvent) {
|
||||
}
|
||||
|
||||
let stylesCreated = false;
|
||||
/* istanbul ignore next */
|
||||
const createStyles = function() {
|
||||
if (!stylesCreated) {
|
||||
const animationKeyframes = `@${keyFramePrefix}keyframes ${RESIZE_ANIMATION_NAME} { from { opacity: 0; } to { opacity: 0; } } `;
|
||||
@ -110,6 +117,7 @@ const createStyles = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
export const addResizeListener = function(element, fn) {
|
||||
if (attachEvent) {
|
||||
element.attachEvent('onresize', fn);
|
||||
@ -143,6 +151,7 @@ export const addResizeListener = function(element, fn) {
|
||||
}
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
export const removeResizeListener = function(element, fn) {
|
||||
if (attachEvent) {
|
||||
element.detachEvent('onresize', fn);
|
||||
|
@ -70,12 +70,17 @@ export default {
|
||||
|
||||
const options = this.options;
|
||||
const popper = this.popperElm = this.popperElm || this.popper || this.$refs.popper;
|
||||
const reference = this.referenceElm = this.referenceElm || this.reference || this.$refs.reference || this.$slots.reference[0].elm;
|
||||
let reference = this.referenceElm = this.referenceElm || this.reference || this.$refs.reference;
|
||||
|
||||
if (!reference &&
|
||||
this.$slots.reference &&
|
||||
this.$slots.reference[0]) {
|
||||
reference = this.referenceElm = this.$slots.reference[0].elm;
|
||||
}
|
||||
if (!popper || !reference) return;
|
||||
if (this.visibleArrow) this.appendArrow(popper);
|
||||
if (this.appendToBody) document.body.appendChild(this.popperElm);
|
||||
if (this.popperJS && this.popperJS.hasOwnProperty('destroy')) {
|
||||
if (this.popperJS && this.popperJS.destroy) {
|
||||
this.popperJS.destroy();
|
||||
}
|
||||
|
||||
@ -95,6 +100,7 @@ export default {
|
||||
},
|
||||
|
||||
doDestroy() {
|
||||
/* istanbul ignore if */
|
||||
if (this.showPopper || !this.popperJS) return;
|
||||
this.popperJS.destroy();
|
||||
this.popperJS = null;
|
||||
@ -110,7 +116,9 @@ export default {
|
||||
let placementMap = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
|
||||
let placement = this.popperJS._popper.getAttribute('x-placement').split('-')[0];
|
||||
let origin = placementMap[placement];
|
||||
this.popperJS._popper.style.transformOrigin = ['top', 'bottom'].indexOf(placement) > -1 ? `center ${ origin }` : `${ origin } center`;
|
||||
this.popperJS._popper.style.transformOrigin = ['top', 'bottom'].indexOf(placement) > -1
|
||||
? `center ${ origin }`
|
||||
: `${ origin } center`;
|
||||
},
|
||||
|
||||
appendArrow(element) {
|
||||
|
148
test/unit/specs/util.clickoutside.spec.js
Normal file
148
test/unit/specs/util.clickoutside.spec.js
Normal file
@ -0,0 +1,148 @@
|
||||
import Clickoutside from 'element-ui/src/utils/clickoutside';
|
||||
const ctx = '@@clickoutsideContext';
|
||||
|
||||
describe('Utils:Clickoutside', () => {
|
||||
it('create', () => {
|
||||
let count = 0;
|
||||
const el = document.createElement('div');
|
||||
const vnode = {
|
||||
context: {
|
||||
handleClick: () => ++count
|
||||
}
|
||||
};
|
||||
const binding = {
|
||||
expression: 'handleClick'
|
||||
};
|
||||
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
expect(el[ctx]).to.exist;
|
||||
});
|
||||
|
||||
it('cotext not exist', () => {
|
||||
const el = document.createElement('div');
|
||||
const vnode = {};
|
||||
const binding = {
|
||||
expression: 'handleClick'
|
||||
};
|
||||
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
expect(el[ctx]).to.exist;
|
||||
});
|
||||
|
||||
it('binding expression', () => {
|
||||
const el = document.createElement('div');
|
||||
let count = 0;
|
||||
const vnode = {
|
||||
context: {
|
||||
handleClick: () => ++count
|
||||
}
|
||||
};
|
||||
const binding = {
|
||||
expression: 'handleClick'
|
||||
};
|
||||
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
document.body.click();
|
||||
expect(count).to.equal(1);
|
||||
});
|
||||
|
||||
it('click inside', () => {
|
||||
const el = document.createElement('div');
|
||||
const insideElm = document.createElement('div');
|
||||
let count = 0;
|
||||
const vnode = {
|
||||
context: {
|
||||
handleClick: () => ++count
|
||||
}
|
||||
};
|
||||
const binding = {
|
||||
expression: 'handleClick'
|
||||
};
|
||||
|
||||
el.appendChild(insideElm);
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
insideElm.click();
|
||||
expect(count).to.equal(0);
|
||||
document.body.click();
|
||||
expect(count).to.equal(1);
|
||||
});
|
||||
|
||||
it('tigger event in popperElm', () => {
|
||||
const el = document.createElement('div');
|
||||
const insideElm = document.createElement('div');
|
||||
let count = 0;
|
||||
const vnode = {
|
||||
context: {
|
||||
handleClick: () => ++count,
|
||||
popperElm: document.createElement('div')
|
||||
}
|
||||
};
|
||||
const binding = {
|
||||
expression: 'handleClick'
|
||||
};
|
||||
|
||||
vnode.context.popperElm.appendChild(insideElm);
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
insideElm.click();
|
||||
expect(count).to.equal(0);
|
||||
document.body.click();
|
||||
expect(count).to.equal(1);
|
||||
});
|
||||
|
||||
it('binding value', () => {
|
||||
const el = document.createElement('div');
|
||||
let count = 0;
|
||||
const vnode = {
|
||||
context: {}
|
||||
};
|
||||
const binding = {
|
||||
value: () => ++count
|
||||
};
|
||||
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
expect(count).to.equal(0);
|
||||
document.body.click();
|
||||
expect(count).to.equal(1);
|
||||
});
|
||||
|
||||
it('update', () => {
|
||||
let count = 0;
|
||||
const el = document.createElement('div');
|
||||
const vnode = {
|
||||
context: {
|
||||
abc: () => ++count,
|
||||
ddd: () => ++count
|
||||
}
|
||||
};
|
||||
const binding = {
|
||||
expression: 'abc'
|
||||
};
|
||||
|
||||
const newBinding = {
|
||||
expression: 'ddd'
|
||||
};
|
||||
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
expect(el[ctx].methodName).to.equal('abc');
|
||||
Clickoutside.update(el, newBinding);
|
||||
expect(el[ctx].methodName).to.equal('ddd');
|
||||
});
|
||||
|
||||
it('unbind', () => {
|
||||
const el = document.createElement('div');
|
||||
let count = 0;
|
||||
const vnode = {
|
||||
context: {}
|
||||
};
|
||||
const binding = {
|
||||
value: () => ++count
|
||||
};
|
||||
|
||||
Clickoutside.bind(el, binding, vnode);
|
||||
document.body.click();
|
||||
Clickoutside.unbind(el);
|
||||
document.body.click();
|
||||
expect(count).to.equal(1);
|
||||
expect(el[ctx]).to.not.exist;
|
||||
});
|
||||
});
|
@ -12,13 +12,71 @@ const Popper = Object.assign({}, VuePopper, {
|
||||
}
|
||||
});
|
||||
|
||||
const CleanPopper = Object.assign({}, VuePopper, {
|
||||
render(h) {
|
||||
return h('div');
|
||||
}
|
||||
});
|
||||
|
||||
describe('Utils:VuePopper', () => {
|
||||
it('set popper not reference', () => {
|
||||
const vm = createTest(CleanPopper, {
|
||||
popper: document.createElement('div')
|
||||
});
|
||||
vm.createPopper();
|
||||
expect(vm.popperElm).to.exist;
|
||||
expect(vm.referenceElm).to.not.exist;
|
||||
expect(vm.popperJS).to.not.exist;
|
||||
});
|
||||
|
||||
it('set reference not popper', () => {
|
||||
const vm = createTest(CleanPopper, {
|
||||
reference: document.createElement('div')
|
||||
});
|
||||
vm.createPopper();
|
||||
expect(vm.referenceElm).to.exist;
|
||||
expect(vm.popperElm).to.not.exist;
|
||||
expect(vm.popperJS).to.not.exist;
|
||||
});
|
||||
|
||||
it('set reference by slot', () => {
|
||||
const vm = createTest(CleanPopper);
|
||||
vm.$slots['reference'] = [{
|
||||
elm: document.createElement('div')
|
||||
}];
|
||||
vm.createPopper();
|
||||
expect(vm.referenceElm).to.exist;
|
||||
expect(vm.popperElm).to.not.exist;
|
||||
expect(vm.popperJS).to.not.exist;
|
||||
});
|
||||
|
||||
it('createPopper', () => {
|
||||
const vm = createTest(Popper, { placement: 'top' });
|
||||
vm.createPopper();
|
||||
expect(vm.popperJS._popper.getAttribute('x-placement')).to.equal('top');
|
||||
});
|
||||
|
||||
it('destroy popper when calling createPopper twice', () => {
|
||||
const vm = createTest(Popper);
|
||||
vm.createPopper();
|
||||
const popperJS = vm.popperJS;
|
||||
|
||||
expect(vm.popperJS).to.exist;
|
||||
expect(vm.popperJS).to.equal(popperJS);
|
||||
vm.createPopper();
|
||||
expect(vm.popperJS).to.not.equal(popperJS);
|
||||
});
|
||||
|
||||
it('updatePopper', () => {
|
||||
const vm = createTest(Popper);
|
||||
vm.updatePopper();
|
||||
const popperJS = vm.popperJS;
|
||||
|
||||
expect(vm.popperJS).to.exist;
|
||||
vm.updatePopper();
|
||||
expect(vm.popperJS).to.equal(popperJS);
|
||||
});
|
||||
|
||||
it('doDestroy', () => {
|
||||
const vm = createTest(Popper, { placement: 'top' });
|
||||
vm.createPopper();
|
||||
@ -27,6 +85,15 @@ describe('Utils:VuePopper', () => {
|
||||
expect(vm.popperJS).to.not.exist;
|
||||
});
|
||||
|
||||
it('destroyPopper', () => {
|
||||
const vm = createTest(Popper);
|
||||
const vm2 = createTest(Popper);
|
||||
|
||||
vm.createPopper();
|
||||
expect(() => vm.destroyPopper()).to.not.throw();
|
||||
expect(() => vm2.destroyPopper()).to.not.throw();
|
||||
});
|
||||
|
||||
it('placement', () => {
|
||||
const vm = createTest(Popper, { placement: 'bottom-start' });
|
||||
const vm2 = createTest(Popper, { placement: 'bottom-abc' });
|
||||
@ -46,6 +113,61 @@ describe('Utils:VuePopper', () => {
|
||||
expect(vm.popperJS._popper.querySelector('div[x-arrow]')).to.exist;
|
||||
});
|
||||
|
||||
it('update showPopper', done => {
|
||||
const vm = createTest(Popper);
|
||||
expect(vm.popperJS).to.not.exist;
|
||||
vm.showPopper = true;
|
||||
setTimeout(_ => {
|
||||
expect(vm.popperJS).to.exist;
|
||||
vm.showPopper = false;
|
||||
setTimeout(_ => {
|
||||
expect(vm.popperJS).to.exist;
|
||||
}, 50);
|
||||
done();
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('resetTransformOrigin', () => {
|
||||
const vm = createTest(Popper, {
|
||||
placement: 'left'
|
||||
});
|
||||
vm.createPopper();
|
||||
expect(vm.popperJS._popper.style.transformOrigin).to.include('right center');
|
||||
});
|
||||
|
||||
it('appendArrow', () => {
|
||||
const vm = createTest(Popper, {
|
||||
visibleArrow: true
|
||||
});
|
||||
expect(vm.appended).to.empty;
|
||||
vm.createPopper();
|
||||
expect(vm.appended).to.true;
|
||||
vm.appendArrow();
|
||||
expect(vm.popperJS._popper.querySelector('[x-arrow]')).to.exist;
|
||||
expect(vm.appended).to.true;
|
||||
});
|
||||
|
||||
it('appendArrow: add scoped', () => {
|
||||
const popper = document.createElement('div');
|
||||
popper.setAttribute('_v-110', true);
|
||||
const vm = createTest(CleanPopper, {
|
||||
reference: document.createElement('div'),
|
||||
visibleArrow: true,
|
||||
popper
|
||||
});
|
||||
expect(vm.appended).to.empty;
|
||||
vm.createPopper();
|
||||
expect(vm.popperJS._popper.querySelector('[x-arrow][_v-110]')).to.exist;
|
||||
});
|
||||
|
||||
it('appendToBody set false', () => {
|
||||
const vm = createTest(Popper, {
|
||||
appendToBody: false
|
||||
});
|
||||
vm.createPopper();
|
||||
expect(document.body.contains(vm.popperElm)).to.false;
|
||||
});
|
||||
|
||||
it('destroy', () => {
|
||||
const vm = createTest(Popper, true);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user