diff --git a/build/config.js b/build/config.js
index d32d2eebc..710405a45 100644
--- a/build/config.js
+++ b/build/config.js
@@ -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;
});
diff --git a/packages/popover/src/main.vue b/packages/popover/src/main.vue
index 2159f3cab..4488febe0 100644
--- a/packages/popover/src/main.vue
+++ b/packages/popover/src/main.vue
@@ -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; });
diff --git a/src/utils/clickoutside.js b/src/utils/clickoutside.js
index 63a45992d..2b3ef0744 100644
--- a/src/utils/clickoutside.js
+++ b/src/utils/clickoutside.js
@@ -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 => {
*
* ```
*/
-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
diff --git a/src/utils/resize-event.js b/src/utils/resize-event.js
index a02a9ec10..a5aa48f2a 100644
--- a/src/utils/resize-event.js
+++ b/src/utils/resize-event.js
@@ -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);
diff --git a/src/utils/vue-popper.js b/src/utils/vue-popper.js
index 70a3cf92b..c0a4e6400 100644
--- a/src/utils/vue-popper.js
+++ b/src/utils/vue-popper.js
@@ -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) {
diff --git a/test/unit/specs/util.clickoutside.spec.js b/test/unit/specs/util.clickoutside.spec.js
new file mode 100644
index 000000000..2c0c2d514
--- /dev/null
+++ b/test/unit/specs/util.clickoutside.spec.js
@@ -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;
+ });
+});
diff --git a/test/unit/specs/util.vue-popper.spec.js b/test/unit/specs/util.vue-popper.spec.js
index fedf7ad0c..a3275f2d4 100644
--- a/test/unit/specs/util.vue-popper.spec.js
+++ b/test/unit/specs/util.vue-popper.spec.js
@@ -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);