Browse Source

Merge branch 'v2-dev' of https://github.com/jdf2e/nutui into v2-dev

richard1015 5 years ago
parent
commit
3f5d1fa6f9

+ 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>

+ 1 - 0
src/packages/searchbar/searchbar.scss

@@ -8,6 +8,7 @@
     margin-right: 10px;
     background-color: $light-color;
     border-radius: 20px;
+    overflow: hidden;
     form {
       display: flex;
       align-items: center;

+ 9 - 0
src/packages/switch/demo.vue

@@ -56,6 +56,15 @@
         <span slot="title"><nut-switch :active="true" class="my-switch"></nut-switch></span>
       </nut-cell>
     </div>
+        <h4>内嵌文字标签</h4>
+    <div>
+      <nut-cell>
+        <span slot="title"><nut-switch :active="true" label="ON|OFF"></nut-switch></span>
+      </nut-cell>
+           <nut-cell>
+        <span slot="title"><nut-switch :active="false" label="通过|拒绝"></nut-switch></span>
+      </nut-cell>
+    </div>
   </div>
 </template>
 

+ 10 - 2
src/packages/switch/doc.md

@@ -80,7 +80,14 @@ export default {
 >
 </nut-switch>
 ```
-
+## 内嵌文字
+```html
+<nut-switch 
+  :active="true"  
+  :label="开|关"
+>
+</nut-switch>
+```
 
 ## Prop
 
@@ -88,4 +95,5 @@ export default {
 |----- | ----- | ----- | -----
 | active | 开关状态 | Boolean | false
 | size | 尺寸,可选值small/base/large | String | base
-| disabled | 是否禁用 | Boolean | false
+| disabled | 是否禁用 | Boolean | false
+| label | 内嵌文字标签,竖线分开,如ON\|OFF 或 开\|关 | String  | 

+ 38 - 9
src/packages/switch/switch.scss

@@ -1,12 +1,25 @@
 .nut-switch {
   position: relative;
-  display: inline-block;
+  width: auto;
+  display: inline-block !important;
   background: #fff;
   border-radius: 1000px;
   vertical-align: bottom;
   box-sizing: content-box;
   border: 2px $border-style-base $border-color-base;
   transition: all $transition-duration $animation-timing-fun;
+  .nut-switch-label {
+    position:relative;
+    width:auto;
+    left:0;
+    margin-left:22px;
+    display: inline-block !important;
+    padding:0 2px 0 2px  !important;
+    text-align:center !important;
+    color:#999 !important;
+    font-style:normal !important;
+    font-size:12px;
+  }
   .nut-switch-btn {
     position: absolute;
     left: 0;
@@ -15,9 +28,16 @@
     border-radius: 50%;
     box-sizing: border-box;
     transition: all $transition-duration $animation-timing-fun;
+
   }
   &.nut-switch-active {
     border-color: $border-color-active;
+   .nut-switch-label {
+    left:0;
+    margin-left:0px;
+    color:red !important;
+    margin-right:22px;
+   }
     .nut-switch-btn {
       background-color: $primary-color;
     }
@@ -29,42 +49,51 @@
 
 .nut-switch-small {
   height: 14px;
-  width: 32px;
+  min-width: 36px;
+  .nut-switch-label {
+    font-size: 10px;
+    top:-4px;
+    margin-left:-25px;
+  }
   .nut-switch-btn {
     height: 14px;
     width: 14px;
   }
   &.nut-switch-active {
     .nut-switch-btn {
-      left: 18px;
+      left: 100%;
+      margin-left:-15px;
     }
   }
 }
 
 .nut-switch-base {
   height: 20px;
-  width: 46px;
+  min-width: 46px;
   .nut-switch-btn {
     height: 20px;
-    width: 20px;
+    width: 22px;
   }
   &.nut-switch-active {
     .nut-switch-btn {
-      left: 26px;
+      left: 100%;
+      margin-left:-21px;
     }
   }
 }
 
 .nut-switch-large {
   height: 28px;
-  width: 58px;
+  min-width: 58px;
+  font-size: 14px;
   .nut-switch-btn {
     height: 28px;
-    width: 28px;
+    min-width: 28px;
   }
   &.nut-switch-active {
     .nut-switch-btn {
-      left: 30px;
+      left: 100%;
+      margin-left:-28px;
     }
   }
 }

+ 10 - 2
src/packages/switch/switch.vue

@@ -1,6 +1,9 @@
 <template>
   <div class="nut-switch" :class="[{ 'nut-switch-active': isActive }, 'nut-switch-' + size, { 'nut-switch-disabled': disabled }]" @click="toggle">
-    <div class="nut-switch-btn"></div>
+    <div class="nut-switch-btn">
+    </div>
+      <div class="nut-switch-label" v-if="isActive">{{arrLabel[0]}}</div>
+      <div class="nut-switch-label" v-else>{{arrLabel[1]}}</div>
   </div>
 </template>
 <script>
@@ -18,11 +21,16 @@ export default {
     disabled: {
       type: Boolean,
       default: false
+    },
+    label:{
+      type: String,
+      default: ""
     }
   },
   data() {
     return {
-      isActive: false
+      isActive: false,
+      arrLabel : ( this.label||'').split('|')
     };
   },
   created() {

+ 12 - 1
src/packages/tabbar/tabbar.vue

@@ -3,7 +3,7 @@
     <a
       class="tabbar-nav"
       v-for="(value, index) in tabList"
-      :class="[{ curr: index == currIndex }, type]"
+      :class="[{ 'curr': index == currIndex }, type]"
       :key="value.tabTitle"
       v-on:click="switchTabs(value, index)"
       :href="value.href"
@@ -44,15 +44,26 @@ export default {
       tabList: this.tabbarList
     };
   },
+  mounted(){
+    this.initBar();
+  },
   watch: {
     tabbarList: {
       handler(value) {
         this.tabList = value;
+        this.initBar();
       },
       deep: true
     }
   },
   methods: {
+    initBar(){
+      this.tabList.forEach((item,index)=>{
+        if(item.curr){
+            this.currIndex = index;
+        }
+      })
+    },
     switchTabs: function(value, index) {
       this.currIndex = index;
       this.$emit('tab-switch', value, index);