瀏覽代碼

Popover: add test

qingwei.li 8 年之前
父節點
當前提交
d2a998726d

+ 1 - 1
Makefile

@@ -1,4 +1,4 @@
-.PHONY: dist
+.PHONY: dist test
 default: help
 
 # build all theme

+ 6 - 0
packages/popover/index.js

@@ -1,8 +1,14 @@
 const Popover = require('./src/main');
+const directive = require('./src/directive').default;
+const Vue = require('vue');
+
+Vue.directive('popover', directive);
 
 /* istanbul ignore next */
 Popover.install = function(Vue) {
+  Vue.directive('popover', directive);
   Vue.component(Popover.name, Popover);
 };
+Popover.directive = directive;
 
 module.exports = Popover;

+ 5 - 0
packages/popover/src/directive.js

@@ -0,0 +1,5 @@
+export default {
+  bind(el, binding, vnode) {
+    vnode.context.$refs[binding.arg].$refs.reference = el;
+  }
+};

+ 51 - 50
packages/popover/src/main.vue

@@ -17,15 +17,8 @@
 
 <script>
 import Popper from 'element-ui/src/utils/vue-popper';
-import Vue from 'vue';
 import { on, off } from 'wind-dom/src/event';
 
-Vue.directive('popover', {
-  bind(el, binding, vnode) {
-    vnode.context.$refs[binding.arg].$refs.reference = el;
-  }
-});
-
 export default {
   name: 'el-popover',
 
@@ -52,55 +45,63 @@ export default {
   },
 
   mounted() {
-    let _timer;
-    const reference = this.reference || this.$refs.reference || this.$slots.reference[0].elm;
+    let reference = this.reference || this.$refs.reference;
     const popper = this.popper || this.$refs.popper;
 
-    this.$nextTick(() => {
-      if (this.trigger === 'click') {
-        on(reference, 'click', () => { this.showPopper = !this.showPopper; });
-        on(document, 'click', (e) => {
-          if (!this.$el ||
-              !reference ||
-              this.$el.contains(e.target) ||
-              reference.contains(e.target) ||
-              !popper ||
-              popper.contains(e.target)) return;
-          this.showPopper = false;
-        });
-      } else if (this.trigger === 'hover') {
-        on(reference, 'mouseenter', () => {
-          this.showPopper = true;
-          clearTimeout(_timer);
-        });
-        on(reference, 'mouseleave', () => {
-          _timer = setTimeout(() => {
-            this.showPopper = false;
-          }, 200);
-        });
-      } else {
-        if ([].slice.call(reference.children).length) {
-          const children = reference.childNodes;
+    if (!reference && this.$slots.reference && this.$slots.reference[0]) {
+      reference = this.$slots.reference[0].elm;
+    }
+    if (this.trigger === 'click') {
+      on(reference, 'click', () => { this.showPopper = !this.showPopper; });
+      on(document, 'click', (e) => {
+        if (!this.$el ||
+            !reference ||
+            this.$el.contains(e.target) ||
+            reference.contains(e.target) ||
+            !popper ||
+            popper.contains(e.target)) return;
+        this.showPopper = false;
+      });
+    } else if (this.trigger === 'hover') {
+      on(reference, 'mouseenter', this.handleMouseEnter);
+      on(popper, 'mouseenter', this.handleMouseEnter);
+      on(reference, 'mouseleave', this.handleMouseLeave);
+      on(popper, 'mouseleave', this.handleMouseLeave);
+    } else {
+      if ([].slice.call(reference.children).length) {
+        const children = reference.childNodes;
 
-          for (let i = 0; i < children.length; i++) {
-            if (children[i].nodeName === 'INPUT') {
-              on(children[i], 'focus', () => { this.showPopper = true; });
-              on(children[i], 'blur', () => { this.showPopper = false; });
-              break;
-            }
+        for (let i = 0; i < children.length; i++) {
+          if (children[i].nodeName === 'INPUT' ||
+              children[i].nodeName === 'TEXTAREA') {
+            on(children[i], 'focus', () => { this.showPopper = true; });
+            on(children[i], 'blur', () => { this.showPopper = false; });
+            break;
           }
-        } else if (
-            reference.nodeName === 'INPUT' ||
-            reference.nodeName === 'TEXTAREA'
-        ) {
-          on(reference, 'focus', () => { this.showPopper = true; });
-          on(reference, 'blur', () => { this.showPopper = false; });
-        } else {
-          on(reference, 'mousedown', () => { this.showPopper = true; });
-          on(reference, 'mouseup', () => { this.showPopper = false; });
         }
+      } else if (
+          reference.nodeName === 'INPUT' ||
+          reference.nodeName === 'TEXTAREA'
+      ) {
+        on(reference, 'focus', () => { this.showPopper = true; });
+        on(reference, 'blur', () => { this.showPopper = false; });
+      } else {
+        on(reference, 'mousedown', () => { this.showPopper = true; });
+        on(reference, 'mouseup', () => { this.showPopper = false; });
       }
-    });
+    }
+  },
+
+  methods: {
+    handleMouseEnter() {
+      this.showPopper = true;
+      clearTimeout(this._timer);
+    },
+    handleMouseLeave() {
+      this._timer = setTimeout(() => {
+        this.showPopper = false;
+      }, 200);
+    }
   },
 
   destroyed() {

+ 2 - 3
src/utils/vue-popper.js

@@ -19,8 +19,8 @@ export default {
       type: Number,
       default: 5
     },
-    reference: Object,
-    popper: Object,
+    reference: {},
+    popper: {},
     offset: {
       default: 0
     },
@@ -73,7 +73,6 @@ export default {
       const reference = this.referenceElm = this.referenceElm || this.reference || this.$refs.reference || 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')) {

+ 196 - 0
test/unit/specs/popover.spec.js

@@ -0,0 +1,196 @@
+import { createVue, triggerEvent, createTest } from '../util';
+import Popover, { directive } from 'packages/popover';
+
+describe('Popover', () => {
+  describe('trigger', () => {
+    const createVM = (trigger) => {
+      return createVue(`
+        <div>
+          <el-popover
+            ref="popover"
+            trigger="${trigger}"
+            content="content">
+            <button slot="reference">trigger ${trigger}</button>
+          </el-popover>
+        </div>
+      `, true);
+    };
+
+    it('click', () => {
+      const vm = createVM('click');
+      const compo = vm.$refs.popover;
+
+      vm.$el.querySelector('button').click();
+      expect(compo.showPopper).to.true;
+      document.body.click();
+      expect(compo.showPopper).to.false;
+    });
+
+    it('hover', done => {
+      const vm = createVM('hover');
+      const compo = vm.$refs.popover;
+      const button = vm.$el.querySelector('button');
+
+      triggerEvent(button, 'mouseenter');
+      expect(compo.showPopper).to.true;
+      triggerEvent(button, 'mouseleave');
+      setTimeout(_ => {
+        expect(compo.showPopper).to.false;
+        done();
+      }, 250); // 代码里是 200ms
+    });
+
+    it('focus input in children node', () => {
+      const vm = createVue(`
+        <div>
+          <el-popover
+            ref="popover"
+            trigger="focus"
+            content="content">
+            <div slot="reference">
+              <input type="text" value="trigger focus" />
+            </div>
+          </el-popover>
+        </div>
+      `, true);
+      const compo = vm.$refs.popover;
+      const input = vm.$el.querySelector('input');
+
+      input.focus();
+      expect(compo.showPopper).to.true;
+      input.blur();
+      expect(compo.showPopper).to.false;
+    });
+
+    it('focus textarea in children node', () => {
+      const vm = createVue(`
+        <div>
+          <el-popover
+            ref="popover"
+            trigger="focus"
+            content="content">
+            <div slot="reference">
+              <textarea></textarea>
+            </div>
+          </el-popover>
+        </div>
+      `, true);
+      const compo = vm.$refs.popover;
+      const textarea = vm.$el.querySelector('textarea');
+
+      textarea.focus();
+      expect(compo.showPopper).to.true;
+      textarea.blur();
+      expect(compo.showPopper).to.false;
+    });
+
+    it('focus input', () => {
+      const vm = createVue(`
+        <div>
+          <el-popover
+            ref="popover"
+            trigger="focus"
+            content="content">
+            <input type="text" slot="reference" value="trigger focus" />
+          </el-popover>
+        </div>
+      `, true);
+      const compo = vm.$refs.popover;
+      const input = vm.$el.querySelector('input');
+
+      input.focus();
+      expect(compo.showPopper).to.true;
+      input.blur();
+      expect(compo.showPopper).to.false;
+    });
+
+    it('focus button', () => {
+      const vm = createVM('focus');
+      const compo = vm.$refs.popover;
+      const button = vm.$el.querySelector('button');
+
+      triggerEvent(button, 'mousedown');
+      expect(compo.showPopper).to.true;
+      triggerEvent(button, 'mouseup');
+      expect(compo.showPopper).to.false;
+    });
+  });
+
+  describe('create by directive', () => {
+    const vm = createVue({
+      template: `
+        <div>
+          <el-popover
+            ref="popover1"
+            trigger="click"
+            content="content">
+          </el-popover>
+          <button v-popover:popover1>create by directive</button>
+        </div>
+      `,
+
+      directives: {
+        Popover: directive
+      }
+    }, true);
+    const compo = vm.$refs.popover1;
+
+    it('render', () => {
+      expect(vm.$el.querySelector('.el-popover')).to.have.deep.property('textContent').include('content');
+    });
+
+    it('triggering click', done => {
+      vm.$el.querySelector('button').click();
+      expect(compo.popperElm).to.not.exist;
+      vm.$nextTick(_ => {
+        expect(compo).to.have.deep.property('popperElm.style.display').not.equal('none');
+        done();
+      });
+    });
+
+    it('click outside', () => {
+      document.body.click();
+      expect(compo.showPopper).to.false;
+    });
+  });
+
+  describe('create by slot', () => {
+    const vm = createVue(`
+      <div>
+        <el-popover
+          ref="popover"
+          trigger="click"
+          content="content">
+          <button slot="reference">create by slot</button>
+        </el-popover>
+      </div>
+    `, true);
+    const compo = vm.$refs.popover;
+
+    it('render', () => {
+      expect(vm.$el.querySelector('.el-popover')).to.have.deep.property('textContent').include('content');
+    });
+
+    it('triggering click', done => {
+      vm.$el.querySelector('button').click();
+      expect(compo.popperElm).to.not.exist;
+      vm.$nextTick(_ => {
+        expect(compo).to.have.deep.property('popperElm.style.display').not.equal('none');
+        done();
+      });
+    });
+
+    it('click outside', () => {
+      document.body.click();
+      expect(compo.showPopper).to.false;
+    });
+  });
+
+  it('destroy event', () => {
+    const vm = createTest(Popover, {
+      reference: document.createElement('div'),
+      popper: document.createElement('div')
+    });
+    expect(() => vm.$destroy(true)).not.throw();
+  });
+});

+ 3 - 5
test/unit/specs/tooltip.spec.js

@@ -7,8 +7,7 @@ describe('Tooltip', () => {
         <button>click</button>
       </el-tooltip>`);
 
-    expect(vm.$el.querySelector('.el-tooltip__popper')).to.exist;
-    expect(vm.$el.querySelector('.el-tooltip__popper').textContent).to.equal('提示文字');
+    expect(vm.$el.querySelector('.el-tooltip__popper')).to.have.property('textContent', '提示文字');
   });
 
   it('hover', done => {
@@ -24,14 +23,13 @@ describe('Tooltip', () => {
 
     expect(tooltip.popperElm).to.not.exist;
     setTimeout(_ => {
-      expect(tooltip.popperElm).to.exist;
-      expect(tooltip.popperElm.style.display).to.not.equal('none');
+      expect(tooltip).to.have.deep.property('popperElm.style.display').not.equal('none');
 
       // trigger mouseleave
       tooltip.handleClosePopper();
 
       setTimeout(_ => {
-        expect(tooltip.popperElm.style.display).to.equal('none');
+        expect(tooltip).to.have.deep.property('popperElm.style.display', 'none');
         done();
       }, 500);
     }, 150);

+ 16 - 0
test/unit/util.js

@@ -46,3 +46,19 @@ exports.createTest = function(Compo, propsData = {}, mounted = false) {
   const Ctor = Vue.extend(Compo);
   return new Ctor({ propsData }).$mount(mounted === false ? null : elm);
 };
+
+/**
+ * 触发一个事件
+ * mouseenter, mouseleave, mouseover 等
+ * @param  {Element} elm
+ * @param  {EventName} name
+ * @param  {options} opts
+ */
+exports.triggerEvent = function(elm, name, opts) {
+  const evt = document.createEvent('MouseEvents');
+
+  evt.initEvent(name, ...opts);
+  elm.dispatchEvent
+    ? elm.dispatchEvent(evt)
+    : elm.fireEvent('on' + name, evt);
+};