Browse Source

fix: 完善 popup 代码逻辑

杨凯旋 5 years ago
parent
commit
f8be1fa44e

+ 104 - 47
src/packages/popup/__test__/popup.spec.js

@@ -1,52 +1,109 @@
-import { mount } from '@vue/test-utils'
-import popup from '../popup.vue'
+import { mount } from '@vue/test-utils';
+import Popup ,{popupProps} from '../popup.vue';
+import { overlayProps } from '../overlay/overlay.vue';
 import Vue from 'vue';
-// import overlay from "../overlay.vue";
-import Icon from '../../icon/icon.vue';
-describe('Menu.vue',() => {
-    const wrapper = mount(popup, {
-        
-    });
+let wrapper,allProps = {}; 
+Object.assign(allProps,overlayProps,popupProps)
+function getProps() {
+  let obj = {};
+
+  Object.keys(allProps).forEach(res => {
+    if (res !== 'value') {
+      obj[res] = this[res];
+    }
+  });
+  return obj;
+}
+describe('popup.vue', () => {
+  afterEach(function() {
+    wrapper.destroy();
+  });
+  const component = {
+    template: `<div>
+                <popup v-model='popupVal' v-bind="maskProps" ></popup>
+            </div>`,
+    components: {
+      Popup
+    },
+    data() {
+      return {
+        maskProps: getProps.apply(this),
+        popupVal: this.value
+      };
+    },
+    watch: {
+      value(v) {
+        this.popupVal = v;
+      }
+    },
+    props: { ...allProps, closeable: Boolean }
+  };
+
+    it('1.render popup && overlay', async function() {
+      wrapper = mount(component, { propsData: { value: true } });
+      wrapper.setProps({ value: true });
+      await Vue.nextTick();
+      expect(wrapper.find('.popup-box').isVisible()).toBe(true);
+      expect(wrapper.find('.nut-mask').isVisible()).toBe(true);
 
-    it('1.判断是否显示内容',() => {
-        wrapper.setData({
-            value:true
-        }); 
-        return Vue.nextTick().then(function() {
-            expect(wrapper.classes()).toContain('popup-box')
-        }) 
     });
-    it('2.判断弹出位置',() => {
-        wrapper.setData({ 
-            position:"top"
-        }); 
-        return Vue.nextTick().then(function() {
-            expect(wrapper.classes()).toContain('popup-top')
-        }) 
+
+    it('2.test props overlay', async function() {
+      wrapper = mount(component, { propsData: { value: true, overlay: false } });
+      await Vue.nextTick();
+      expect(wrapper.contains('.nut-mask')).toBe(false);
+
     });
-    it('3.判断是否有关闭图标',() => {
-        wrapper.setData({ 
-            closeable:true
-        });  Icon
-        let i = wrapper.find('.nutui-popup__close-icon')
-        return Vue.nextTick().then(function() {
-            expect(i.is(Icon)).toBe(true)
-        }) 
+
+    it('3.test props lockScroll', async function() {
+      const wrapper1 = mount(component, { propsData: { value: true } });
+      const wrapper2 = mount(component, { propsData: { value: true } });
+      expect(document.body.classList.contains('nut-overflow-hidden')).toBe(true);
+
+      wrapper1.destroy();
+      await Vue.nextTick();
+      expect(document.body.classList.contains('nut-overflow-hidden')).toBe(true);
+      wrapper2.destroy();
+      await Vue.nextTick();
+      expect(document.body.classList.contains('nut-overflow-hidden')).toBe(false);
+
     });
-    // it('4.判断点击关闭按钮',() => {
-    //     wrapper.setData({ 
-    //         closeable:true
-    //     }); 
-    //     let i = wrapper.find('.nutui-popup__close-icon');
-    //     i.trigger('click')
-    //     console.log()
-    //     return Vue.nextTick().then(function() {
-    //         setTimeout(()=>{
-    //             expect(wrapper.contains(popup)).toBe(false)
-    //         },wrapper.duration*1000)           
-    //     }) 
-    // });
-
- 
- 
-});
+
+  it('4.test closeOnClickOverlay', async function() {
+    wrapper = mount(component, { propsData: { value: true, closeOnClickOverlay: true } });
+    await Vue.nextTick();
+    wrapper.find('.nut-mask').trigger('click');
+    await Vue.nextTick();
+    const duration = wrapper.vm.duration * 1500; 
+    await new Promise(resolve => {
+      setTimeout(() => resolve(), duration);
+    }); 
+    
+    expect(wrapper.find('.nut-mask').isVisible() || wrapper.find('.popup-box').isVisible()).toBe(false); 
+  });
+  it('5.test closeOnClickOverlay', async function() {
+    wrapper = mount(component, { propsData: { value: true, closeable: true } });
+    await Vue.nextTick();
+    let i = wrapper.find('.nutui-popup__close-icon');
+    i.trigger('click');
+    await Vue.nextTick();
+    await new Promise(resolve => {
+      setTimeout(() => resolve(), wrapper.duration * 1000);
+    });  
+     
+    expect(wrapper.find('.popup-box').isVisible()).toBe(false);
+  });
+
+   
+  it('6.test top ', async function()  {
+      wrapper = mount(component, { propsData: { value: true, position:"top"}});
+      await Vue.nextTick();  
+      expect(wrapper.find('.popup-box').classes()).toContain('popup-top')
+  });
+  it('7. zindex ',async function()  {
+    wrapper = mount(component, { propsData: { value: true,zIndex:999}});
+      const  wrapper2 = mount(component, { propsData: { value: true}});
+      await Vue.nextTick();  
+      expect(wrapper.find('.popup-box').vm.zIndex).toBe(999) 
+  });
+});

+ 16 - 20
src/packages/popup/overlay/overlay-manager.js

@@ -3,9 +3,8 @@ import overlayComponent from "./overlay.vue";
 
 let modalStack = [];
 let _zIndex = 2000;
-
-const overlayManager = {
-  overlay: null,
+let overlay;
+const overlayManager = { 
 
   lockCount: 0,
 
@@ -17,27 +16,27 @@ const overlayManager = {
   },
 
   updateOverlay() {
-    const { overlay, clickHandle, topStack } = overlayManager;
+    const {  clickHandle, topStack } = overlayManager;
     if (!overlay) {
-      overlayManager.overlay = mount(overlayComponent, {
+      overlay = mount(overlayComponent, {
         nativeOn: {
           click: clickHandle,
         },
       });
-    }
-
+    } 
+ 
     if (topStack) {
       const { vm, config } = topStack;
       const el = vm.$el;
       el && el.parentNode && el.parentNode.nodeType !== 11
         ? el.parentNode.appendChild(overlay.$el)
         : document.body.appendChild(overlay.$el);
-
-      Object.assign(overlayManager.overlay, config, {
+      
+      Object.assign(overlay, config, {
         value: true,
-      });
-    } else {
-      overlayManager.overlay.value = false;
+      }); 
+    } else { 
+      overlay.value = false;
     }
   },
 
@@ -45,8 +44,6 @@ const overlayManager = {
   openModal(vm, config) {
     let { zIndex, duration, className, customStyle } = config;
 
-    overlayManager.updateOverlay();
-
     modalStack.push({
       vm,
       config: {
@@ -67,7 +64,6 @@ const overlayManager = {
     if (modalStack.length && topStack.vm.closeOnClickOverlay) { 
       topStack.vm.$emit("click-overlay");
       topStack.vm.close();
-  
     }
   },
 
@@ -118,16 +114,16 @@ const overlayProps = {
 };
 
 function mount(Component, data) {
-  const instance = new Vue({
-    el: document.createElement("div"),
+
+  const instance = new Vue({ 
     props: Component.props,
-    render(h) {
+    render(h) {    
       return h(Component, {
-        props: this.$props,
+        props:this.$props,
         ...data,
       });
     },
-  });
+  }).$mount();
   return instance;
 }
 

+ 84 - 110
src/packages/popup/popup.vue

@@ -1,9 +1,5 @@
 <template>
-  <transition
-    :name="transitionName"
-    @after-enter="$emit('opened')"
-    @after-leave="$emit('closed')"
-  >
+  <transition :name="transitionName" @after-enter="$emit('opened')" @after-leave="$emit('closed')">
     <div
       ref="popupBox"
       v-show="value"
@@ -26,93 +22,95 @@
   </transition>
 </template>
 <script>
-import Vue from "vue";
-import Icon from "../icon/icon.vue";
-import touchMixins from "../../mixins/touch.js";
-import {overlayManager ,overlayProps} from "./overlay/overlay-manager.js";
-import { on, off } from "../../utils/event"; 
-import "../icon/icon.scss";
-
+import Vue from 'vue';
+import Icon from '../icon/icon.vue';
+import touchMixins from '../../mixins/touch.js';
+import { overlayManager, overlayProps } from './overlay/overlay-manager.js';
+import { on, off } from '../../utils/event';
+import '../icon/icon.scss';
 
 const overflowScrollReg = /scroll|auto/i;
+const popupProps = {
+  position: {
+    type: String,
+    default: 'center'
+  },
+
+  transition: String,
+
+  closeable: {
+    type: Boolean,
+    default: false
+  },
+  closeIconPosition: {
+    type: String,
+    default: 'top-right'
+  },
+  closeIcon: {
+    type: String,
+    default: 'cross'
+  },
+
+  closeOnClickOverlay: {
+    type: Boolean,
+    default: true
+  },
+
+  destroyOnClose: {
+    type: Boolean,
+    default: false
+  },
+  getContainer: String,
+  round: {
+    type: Boolean,
+    default: false
+  }
+};
 export default {
-  name: "nut-popup",
+  name: 'nut-popup',
   mixins: [touchMixins],
   components: {
-    icon: Icon,
+    icon: Icon
   },
   props: {
     ...overlayProps,
-    position: {
-      type: String,
-      default: "center",
-    },
-
-    transition: String,
-
-    closeable: {
-      type: Boolean,
-      default: false,
-    },
-    closeIconPosition: {
-      type: String,
-      default: "top-right",
-    },
-    closeIcon: {
-      type: String,
-      default: "cross",
-    },
-
-    closeOnClickOverlay: {
-      type: Boolean,
-      default: true,
-    },
-
-    destroyOnClose: {
-      type: Boolean,
-      default: false,
-    },
-    getContainer: String,
-    round: {
-      type: Boolean,
-      default: false,
-    },
+    ...popupProps
   },
   created() {
-    this.transition
-      ? (this.transitionName = this.transition)
-      : (this.transitionName = `popup-slide-${this.position}`);
+    this.transition ? (this.transitionName = this.transition) : (this.transitionName = `popup-slide-${this.position}`);
   },
   mounted() {
     if (this.value) {
       this.open();
     }
   },
+  beforeDestroy() {
+    this.close();
+  },
   watch: {
     value(val) {
-      const type = val ? "open" : "close";
+      const type = val ? 'open' : 'close';
       this[type]();
     },
     position(val) {
-      val === "center"
-        ? (this.transitionName = "popup-fade")
-        : (this.transitionName = `popup-slide-${this.position}`);
+      val === 'center' ? (this.transitionName = 'popup-fade') : (this.transitionName = `popup-slide-${this.position}`);
     },
-    getContainer: "portal",
-    overlay: "renderOverlay",
+    getContainer: 'portal',
+    overlay: 'renderOverlay'
   },
   data() {
     return {
       showSlot: true,
-      transitionName: "popup-fade-center",
-      overlayInstant: null,
+      transitionName: 'popup-fade-center',
+      overlayInstant: null
     };
   },
   computed: {
     transitionDuration() {
-      return this.duration ? this.duration + "s" : "initial";
-    },
+      return this.duration ? this.duration + 's' : 'initial';
+    }
   },
+
   methods: {
     open() {
       if (this.opened) {
@@ -120,36 +118,30 @@ export default {
       }
 
       this.opened = true;
-      this.$emit("open");
-   
-      const {
-        duration,
-        overlayClass,
-        overlayStyle,
-        lockScroll,
-        closeOnClickOverlay,
-      } = this; 
+      this.$emit('open');
+
+      const { duration, overlayClass, overlayStyle, lockScroll, closeOnClickOverlay } = this;
       const config = {
-        zIndex:this.zIndex ? this.zIndex : overlayManager.zIndex,
+        zIndex: this.zIndex ? this.zIndex : overlayManager.zIndex,
         duration,
         overlayClass,
         overlayStyle,
         lockScroll,
-        closeOnClickOverlay,
+        closeOnClickOverlay
       };
 
       this.renderOverlay(config);
 
       if (this.lockScroll) {
-        on(document, "touchstart", this.touchStart);
-        on(document, "touchmove", this.onTouchMove);
+        on(document, 'touchstart', this.touchStart);
+        on(document, 'touchmove', this.onTouchMove);
 
         if (!overlayManager.lockCount) {
-          document.body.classList.add("nut-overflow-hidden");
+          document.body.classList.add('nut-overflow-hidden');
         }
         overlayManager.lockCount++;
       }
-      
+
       this.$el.style.zIndex = this.zIndex ? this.zIndex + 1 : overlayManager.zIndex;
     },
     renderOverlay(config) {
@@ -165,61 +157,42 @@ export default {
     },
     onTouchMove(event) {
       this.touchMove(event);
-      const el = this.getScroller(event.target, this.$el);
-      const { scrollHeight, offsetHeight, scrollTop } = el;
-
-      if (
-        (this.deltaY > 0 && scrollTop === 0) ||
-        (this.deltaY < 0 && scrollTop + offsetHeight >= scrollHeight)
-      ) {
-        event.preventDefault();
+      const el = this.getScroller(event.target);
+      const { scrollHeight, offsetHeight, scrollTop } = el ? el : this.$el;
+
+      if ((this.deltaY > 0 && scrollTop === 0) || (this.deltaY < 0 && scrollTop + offsetHeight >= scrollHeight)) {
+        //event.preventDefault();
       }
     },
-    getScroller(el, root) {
+    getScroller(el) {
       let node = el;
-      while (
-        node &&
-        node.tagName !== "HTML" &&
-        node.nodeType === 1 &&
-        node !== root
-      ) {
+      while (node && node.tagName !== 'HTML' && node.nodeType === 1) {
         const { overflowY } = window.getComputedStyle(node);
 
         if (overflowScrollReg.test(overflowY)) {
-          if (node.tagName !== "BODY") {
-            return node;
-          }
-          const { overflowY: htmlOverflowY } = window.getComputedStyle(
-            node.parentNode
-          );
-
-          if (overflowScrollReg.test(htmlOverflowY)) {
-            return node;
-          }
+          return node;
         }
 
         node = node.parentNode;
       }
-
-      return root;
     },
     close() {
       if (!this.opened) {
         return;
       }
-      this.$emit('close')
+      this.$emit('close');
       this.opened = false;
       if (this.lockScroll) {
         overlayManager.lockCount--;
-        off(document, "touchstart", this.touchStart);
-        off(document, "touchmove", this.onTouchMove);
+        off(document, 'touchstart', this.touchStart);
+        off(document, 'touchmove', this.onTouchMove);
         if (!overlayManager.lockCount) {
-          document.body.classList.remove("nut-overflow-hidden");
+          document.body.classList.remove('nut-overflow-hidden');
         }
       }
 
       overlayManager.closeOverlay(this);
-      this.$emit("input", false);
+      this.$emit('input', false);
     },
 
     getElement(selector) {
@@ -239,7 +212,8 @@ export default {
       if (container && container !== el.parentNode) {
         container.appendChild(el);
       }
-    },
-  },
+    }
+  }
 };
+export  {popupProps}
 </script>