ソースを参照

fix: 日历组件调整 (#1090)

lkjh3214 3 年 前
コミット
7eab93c870

+ 10 - 0
src/packages/__VUE/button/__tests__/__snapshots__/button.spec.ts.snap

@@ -0,0 +1,10 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should change icon class prefix when using icon-class-prefix prop 1`] = `
+"<view class=\\"nut-button nut-button--default nut-button--normal nut-button--round\\">
+  <view class=\\"nut-button__warp\\">
+    <!--v-if--><i class=\\"nutui-iconfont nut-icon my-icon-star-fill\\" src=\\"\\"></i>
+    <!--v-if-->
+  </view>
+</view>"
+`;

+ 38 - 1
src/packages/__VUE/button/__tests__/button.spec.ts

@@ -1,6 +1,6 @@
 import { mount } from '@vue/test-utils';
 import Button from '../index.vue';
-
+import { nextTick } from 'vue';
 test('emit click event', () => {
   const wrapper = mount(Button);
 
@@ -16,3 +16,40 @@ test('slot test', async () => {
   });
   expect(wrapper.html()).toContain('按钮测试');
 });
+test('should emit click event', () => {
+  const wrapper = mount(Button);
+
+  wrapper.trigger('click');
+  expect(wrapper.emitted('click')).toHaveLength(1);
+});
+
+test('should not allow click when set disabled props', async () => {
+  const wrapper = mount(Button, {
+    props: {
+      disabled: true
+    }
+  });
+  wrapper.trigger('click');
+  await nextTick();
+  expect(wrapper.emitted('click')).toBeFalsy();
+});
+test('should not emit click event when loading', () => {
+  const wrapper = mount(Button, {
+    props: {
+      loading: true
+    }
+  });
+
+  wrapper.trigger('click');
+  expect(wrapper.emitted('click')).toBeFalsy();
+});
+test('should change icon class prefix when using icon-class-prefix prop', () => {
+  const wrapper = mount(Button, {
+    props: {
+      icon: 'star-fill',
+      iconClassPrefix: 'my-icon'
+    }
+  });
+
+  expect(wrapper.html()).toMatchSnapshot();
+});

+ 347 - 0
src/packages/__VUE/calendar/__tests__/index.spec.ts

@@ -0,0 +1,347 @@
+import { mount, shallowMount } from '@vue/test-utils';
+import Swiper from '../index.vue';
+import SwiperItem from './../../swiperitem/index.vue';
+import { nextTick, toRefs, reactive } from 'vue';
+
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+
+test('should render width and height', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :pagination-visible="true" pagination-color="#426543" auto-play="2000" :height="height">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 1,
+        height: '500',
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+
+  expect(swiperItem.style.height).toEqual(`${wrapper.vm.height}px`);
+  expect(swiperItem.style.width).toEqual(`${window.innerWidth * wrapper.vm.list.length}px`);
+});
+
+test('should render initpage', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :width="width">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 2,
+        width: 375,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+  await nextTick();
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+
+  expect(swiperItem.style.transform).toEqual(`translateX(-${wrapper.vm.width * wrapper.vm.page}px)`);
+});
+
+test('should render direction', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :height="height" :direction="direction">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 1,
+        height: 300,
+        direction: 'vertical',
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+  await nextTick();
+
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+
+  expect(swiperItem.style.transform).toEqual(`translateY(-${wrapper.vm.height * wrapper.vm.page}px)`);
+});
+
+test('should render pagination', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :pagination-visible="paginationVisible" :pagination-color="paginationColor">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 0,
+        paginationVisible: true,
+        paginationColor: 'red',
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+  await nextTick();
+
+  const swiperPagination = wrapper.find('.nut-swiper-pagination');
+  expect(swiperPagination.exists()).toBe(true);
+  expect(swiperPagination.findAll('i')[0].element.style.backgroundColor).toEqual('red');
+});
+
+test('should render loop and auto-play', async () => {
+  const onChange = jest.fn();
+  const wrapper = mount({
+    props: {
+      onChange
+    },
+    emits: ['change'],
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" :width="width" @change="change">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 0,
+        autoPlay: 100,
+        width: 375,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  await sleep(200);
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+  expect(wrapper.vm.page).toBe(1);
+  expect(swiperItem.style.transform).toEqual(`translateX(-${wrapper.vm.width * wrapper.vm.page}px)`);
+  await sleep(200);
+  expect(wrapper.vm.page).toBe(2);
+});
+
+test('should not allow to drag when touchable is false', async () => {
+  const onChange = jest.fn();
+  const wrapper = mount(Swiper, {
+    props: {
+      onChange,
+      touchable: false
+    }
+  });
+
+  const track = wrapper.find('.nut-swiper-inner');
+
+  track.trigger('drag');
+  expect(onChange).toHaveBeenCalledTimes(0);
+});
+
+test('should not allow to drag when loop is false', async () => {
+  const wrapper = mount({
+    emits: ['change'],
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" :width="width" :loop="loop" @change="change">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 3,
+        autoPlay: 100,
+        width: 375,
+        loop: false,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  await sleep(2000);
+  expect(wrapper.vm.page).toBe(3);
+});
+
+test('should swiper to prev swiper after calling prev method', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" @change="change" ref="swiper">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 3,
+        autoPlay: 0,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  const { swiper } = wrapper.vm.$refs as any;
+  swiper.prev();
+  await sleep(1000);
+  expect(wrapper.vm.page).toBe(2);
+});
+
+test('should swiper to swiper after calling to method', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" @change="change" ref="swiper">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 3,
+        autoPlay: 0,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  const { swiper } = wrapper.vm.$refs as any;
+  swiper.to(1);
+  await sleep(1000);
+  expect(wrapper.vm.page).toBe(1);
+});

+ 347 - 0
src/packages/__VUE/calendaritem/__tests__/index.spec.ts

@@ -0,0 +1,347 @@
+import { mount, shallowMount } from '@vue/test-utils';
+import Swiper from '../index.vue';
+import SwiperItem from './../../swiperitem/index.vue';
+import { nextTick, toRefs, reactive } from 'vue';
+
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+
+test('should render width and height', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :pagination-visible="true" pagination-color="#426543" auto-play="2000" :height="height">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 1,
+        height: '500',
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+
+  expect(swiperItem.style.height).toEqual(`${wrapper.vm.height}px`);
+  expect(swiperItem.style.width).toEqual(`${window.innerWidth * wrapper.vm.list.length}px`);
+});
+
+test('should render initpage', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :width="width">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 2,
+        width: 375,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+  await nextTick();
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+
+  expect(swiperItem.style.transform).toEqual(`translateX(-${wrapper.vm.width * wrapper.vm.page}px)`);
+});
+
+test('should render direction', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :height="height" :direction="direction">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 1,
+        height: 300,
+        direction: 'vertical',
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+  await nextTick();
+
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+
+  expect(swiperItem.style.transform).toEqual(`translateY(-${wrapper.vm.height * wrapper.vm.page}px)`);
+});
+
+test('should render pagination', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :pagination-visible="paginationVisible" :pagination-color="paginationColor">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 0,
+        paginationVisible: true,
+        paginationColor: 'red',
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      return { ...toRefs(state) };
+    }
+  });
+
+  await nextTick();
+  await nextTick();
+
+  const swiperPagination = wrapper.find('.nut-swiper-pagination');
+  expect(swiperPagination.exists()).toBe(true);
+  expect(swiperPagination.findAll('i')[0].element.style.backgroundColor).toEqual('red');
+});
+
+test('should render loop and auto-play', async () => {
+  const onChange = jest.fn();
+  const wrapper = mount({
+    props: {
+      onChange
+    },
+    emits: ['change'],
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" :width="width" @change="change">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 0,
+        autoPlay: 100,
+        width: 375,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  await sleep(200);
+  const swiperItem = wrapper.findAll('.nut-swiper-inner')[0].element as HTMLElement;
+  expect(wrapper.vm.page).toBe(1);
+  expect(swiperItem.style.transform).toEqual(`translateX(-${wrapper.vm.width * wrapper.vm.page}px)`);
+  await sleep(200);
+  expect(wrapper.vm.page).toBe(2);
+});
+
+test('should not allow to drag when touchable is false', async () => {
+  const onChange = jest.fn();
+  const wrapper = mount(Swiper, {
+    props: {
+      onChange,
+      touchable: false
+    }
+  });
+
+  const track = wrapper.find('.nut-swiper-inner');
+
+  track.trigger('drag');
+  expect(onChange).toHaveBeenCalledTimes(0);
+});
+
+test('should not allow to drag when loop is false', async () => {
+  const wrapper = mount({
+    emits: ['change'],
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" :width="width" :loop="loop" @change="change">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 3,
+        autoPlay: 100,
+        width: 375,
+        loop: false,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  await sleep(2000);
+  expect(wrapper.vm.page).toBe(3);
+});
+
+test('should swiper to prev swiper after calling prev method', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" @change="change" ref="swiper">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 3,
+        autoPlay: 0,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  const { swiper } = wrapper.vm.$refs as any;
+  swiper.prev();
+  await sleep(1000);
+  expect(wrapper.vm.page).toBe(2);
+});
+
+test('should swiper to swiper after calling to method', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-swiper': Swiper,
+      'nut-swiper-item': SwiperItem
+    },
+    template: `
+        <nut-swiper :init-page="page" :auto-play="autoPlay" @change="change" ref="swiper">
+          <nut-swiper-item v-for="item in list" :key="item">
+            <img :src="item" alt="" />
+          </nut-swiper-item>
+        </nut-swiper>
+    `,
+    setup() {
+      const state = reactive({
+        page: 3,
+        autoPlay: 0,
+        list: [
+          'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
+          'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
+          'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
+          'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
+        ]
+      });
+
+      const change = (index: number) => {
+        state.page = index;
+      };
+
+      return { ...toRefs(state), change };
+    }
+  });
+
+  await nextTick();
+
+  const { swiper } = wrapper.vm.$refs as any;
+  swiper.to(1);
+  await sleep(1000);
+  expect(wrapper.vm.page).toBe(1);
+});

+ 51 - 44
src/packages/__VUE/calendaritem/index.taro.vue

@@ -13,7 +13,7 @@
         <slot name="btn"> </slot>
       </view>
       <view class="calendar-curr-month" v-if="showSubTitle">{{ yearMonthTitle }}</view>
-      <view class="calendar-weeks" ref="weeksPanel">
+      <view class="calendar-weeks">
         <view class="calendar-week-item" v-for="(item, index) of weeks" :key="index">{{ item }}</view>
       </view>
     </view>
@@ -22,12 +22,12 @@
       :scroll-top="scrollTop"
       :scroll-y="true"
       class="nut-calendar-content"
-      ref="months"
       @scroll="mothsViewScroll"
+      ref="months"
     >
-      <view class="calendar-months-panel" ref="monthsPanel">
-        <view class="viewArea" ref="viewArea" :style="{ transform: `translateY(${translateY}px)` }">
-          <view class="calendar-month" v-for="(month, index) of compConthsDatas" :key="index" :id="month.title">
+      <view class="calendar-months-panel" style="{{heihgt:containerHeight}}">
+        <view class="viewArea" :style="{ transform: `translateY(${translateY}px)` }">
+          <view class="calendar-month" v-for="(month, index) of compConthsDatas" :key="index">
             <view class="calendar-month-title">{{ month.title }}</view>
             <view class="calendar-month-con">
               <view class="calendar-month-item" :class="type === 'range' ? 'month-item-range' : ''">
@@ -45,15 +45,9 @@
                     <view class="calendar-curr-tips calendar-curr-tips-bottom" v-if="bottomInfo">
                       <slot name="bottomInfo" :date="day.type == 'curr' ? day : ''"> </slot>
                     </view>
-                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(month, day.day)">
-                      今天
-                    </view>
-                    <view
-                      class="calendar-day-tip"
-                      :class="{ 'calendar-curr-tips-top': rangeTip(day, month) }"
-                      v-if="isStartTip(day, month)"
-                    >
-                      {{ startText }}
+                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(day)"> 今天 </view>
+                    <view :class="{ 'calendar-curr-tips-top': rangeTip(day, month), 'calendar-day-tip': true }">
+                      {{ isStartTip(day, month) ? startText : '' }}
                     </view>
                     <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText }}</view>
                   </view>
@@ -71,16 +65,18 @@
   </view>
 </template>
 <script lang="ts">
-import { PropType, reactive, ref, watch, toRefs, computed, onMounted } from 'vue';
+import { PropType, reactive, ref, watch, toRefs, computed, onMounted, nextTick } from 'vue';
 import { createComponent } from '../../utils/create';
 const { create } = createComponent('calendar-item');
 import Taro from '@tarojs/taro';
 import Utils from '../../utils/date';
 import requestAniFrame from '../../utils/raf';
+let TARO_ENV = process.env.TARO_ENV;
 
 type InputDate = string | string[];
 interface CalendarState {
   yearMonthTitle: string;
+  containerHeight: string;
   currDate: any;
   propStartDate: string;
   propEndDate: string;
@@ -177,12 +173,9 @@ export default create({
   setup(props, { emit, slots }) {
     const weeks = ref(['日', '一', '二', '三', '四', '五', '六']);
     // element refs
-    const months = ref<null | HTMLElement>(null);
-    const monthsPanel = ref<null | HTMLElement>(null);
-    const weeksPanel = ref<null | HTMLElement>(null);
-    const viewArea = ref<null | HTMLElement>(null);
     const scalePx = ref(2);
     const viewHeight = ref(0);
+    const months = ref<null | HTMLElement>(null);
 
     const compConthsData = computed(() => {
       return state.monthsData.slice(state.defaultRange[0], state.defaultRange[1]);
@@ -201,7 +194,7 @@ export default create({
       yearMonthTitle: '',
       defaultRange: [0, 1],
       compConthsDatas: [],
-
+      containerHeight: '',
       currDate: '',
       propStartDate: '',
       propEndDate: '',
@@ -407,9 +400,14 @@ export default create({
           ...(getDaysStatus(currMonthDays, 'curr', title) as Day[])
         ]
       };
-
-      let titleHeight = Math.floor(46 * scalePx.value) + Math.floor(16 * scalePx.value) * 2;
-      let itemHeight = Math.floor(128 * scalePx.value);
+      let titleHeight, itemHeight;
+      if (TARO_ENV === 'h5') {
+        titleHeight = 46 * scalePx.value + 16 * scalePx.value * 2;
+        itemHeight = 128 * scalePx.value;
+      } else {
+        titleHeight = Math.floor(46 * scalePx.value) + Math.floor(16 * scalePx.value) * 2;
+        itemHeight = Math.floor(128 * scalePx.value);
+      }
       monthInfo.cssHeight = titleHeight + (monthInfo.monthData.length > 35 ? itemHeight * 6 : itemHeight * 5);
 
       let cssScrollHeight = 0;
@@ -529,13 +527,24 @@ export default create({
 
       let lastItem = state.monthsData[state.monthsData.length - 1];
       let containerHeight = lastItem.cssHeight + lastItem.cssScrollHeight;
-      if (months?.value && monthsPanel?.value && viewArea?.value) {
-        viewHeight.value = months.value.clientHeight;
-        monthsPanel.value.style.height = `${containerHeight}px`;
 
-        state.scrollTop = state.monthsData[state.currentIndex].cssScrollHeight;
-      }
+      state.containerHeight = `${containerHeight}px`;
+      state.scrollTop = state.monthsData[state.currentIndex].cssScrollHeight;
       state.avgHeight = Math.floor(containerHeight / (monthsNum + 1));
+
+      if (months?.value) {
+        viewHeight.value = months.value.clientHeight;
+      }
+      if (TARO_ENV === 'h5') {
+        Taro.nextTick(() => {
+          Taro.createSelectorQuery()
+            .select('.nut-calendar-content')
+            .boundingClientRect((res) => {
+              viewHeight.value = res.height;
+            })
+            .exec();
+        });
+      }
     };
     const setDefaultRange = (monthsNum: number, current: number) => {
       let rangeArr: any[] = [];
@@ -565,11 +574,7 @@ export default create({
 
     // 是否有开始提示
     const isStartTip = (day: Day, month: MonthInfo) => {
-      if (isActive(day, month)) {
-        return isStart(getCurrDate(day, month));
-      } else {
-        return false;
-      }
+      return isActive(day, month) && isStart(getCurrDate(day, month));
     };
 
     // 是否有结束提示
@@ -586,13 +591,13 @@ export default create({
       }
     };
     // 是否有是当前日期
-    const isCurrDay = (month: any, day: string) => {
-      const date = `${month.curData[0]}-${month.curData[1]}-${day}`;
+    const isCurrDay = (dateInfo: any) => {
+      const date = `${dateInfo.year}-${dateInfo.month}-${dateInfo.day < 10 ? '0' + dateInfo.day : dateInfo.day}`;
       return Utils.isEqual(date, Utils.date2Str(new Date()));
     };
 
     const mothsViewScroll = (e: any) => {
-      var currentScrollTop = e.target.scrollTop;
+      const currentScrollTop = e.target.scrollTop;
       let current = Math.floor(currentScrollTop / state.avgHeight);
       if (current == 0) {
         if (currentScrollTop >= state.monthsData[current + 1].cssScrollHeight) {
@@ -606,16 +611,16 @@ export default create({
           current -= 1;
         }
       } else {
+        const viewPosition = Math.round(currentScrollTop + viewHeight.value);
         if (
-          currentScrollTop + viewHeight.value <
-          state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight
+          viewPosition < state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight &&
+          currentScrollTop < state.monthsData[current].cssScrollHeight
         ) {
           current -= 1;
         }
         if (
           current + 1 <= state.monthsNum &&
-          currentScrollTop + viewHeight.value >=
-            state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
+          viewPosition >= state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
         ) {
           current += 1;
         }
@@ -628,6 +633,7 @@ export default create({
         state.currentIndex = current;
         setDefaultRange(state.monthsNum, current);
       }
+
       state.yearMonthTitle = state.monthsData[current].title;
     };
     // 重新渲染
@@ -642,7 +648,11 @@ export default create({
         success(res) {
           let scale = 2;
           let screenWidth = res.screenWidth;
-          scale = Number((screenWidth / 750).toFixed(3));
+          let toFixed = 3;
+          if (TARO_ENV === 'h5') {
+            toFixed = 5;
+          }
+          scale = Number((screenWidth / 750).toFixed(toFixed));
           scalePx.value = scale;
           initData();
         }
@@ -674,10 +684,7 @@ export default create({
       chooseDay,
       isCurrDay,
       confirm,
-      monthsPanel,
       months,
-      weeksPanel,
-      viewArea,
       ...toRefs(state),
       ...toRefs(props)
     };

+ 10 - 16
src/packages/__VUE/calendaritem/index.vue

@@ -39,9 +39,7 @@
                     <view class="calendar-curr-tips calendar-curr-tips-bottom" v-if="bottomInfo">
                       <slot name="bottomInfo" :date="day.type == 'curr' ? day : ''"> </slot>
                     </view>
-                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(month, day.day)">
-                      今天
-                    </view>
+                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(day)"> 今天 </view>
                     <view
                       class="calendar-day-tip"
                       :class="{ 'calendar-curr-tips-top': rangeTip(day, month) }"
@@ -500,7 +498,6 @@ export default create({
       setDefaultRange(monthsNum, current);
       state.currentIndex = current;
       state.yearMonthTitle = state.monthsData[state.currentIndex].title;
-
       // 设置当前选中日期
       if (state.isRange) {
         chooseDay({ day: state.defaultData[2], type: 'curr' }, state.monthsData[state.currentIndex], true);
@@ -544,11 +541,7 @@ export default create({
 
     // 是否有开始提示
     const isStartTip = (day: Day, month: MonthInfo) => {
-      if (isActive(day, month)) {
-        return isStart(getCurrDate(day, month));
-      } else {
-        return false;
-      }
+      return isActive(day, month) && isStart(getCurrDate(day, month));
     };
 
     // 是否有结束提示
@@ -565,13 +558,13 @@ export default create({
       }
     };
     // 是否有 当前日期
-    const isCurrDay = (month: any, day: string) => {
-      const date = `${month.curData[0]}-${month.curData[1]}-${day}`;
+    const isCurrDay = (dateInfo: any) => {
+      const date = `${dateInfo.year}-${dateInfo.month}-${dateInfo.day < 10 ? '0' + dateInfo.day : dateInfo.day}`;
       return Utils.isEqual(date, Utils.date2Str(new Date()));
     };
     // 滚动处理事件
     const mothsViewScroll = (e: any) => {
-      var currentScrollTop = e.target.scrollTop;
+      const currentScrollTop = e.target.scrollTop;
       let current = Math.floor(currentScrollTop / state.avgHeight);
       if (current == 0) {
         if (currentScrollTop >= state.monthsData[current + 1].cssScrollHeight) {
@@ -585,16 +578,16 @@ export default create({
           current -= 1;
         }
       } else {
+        const viewPosition = Math.round(currentScrollTop + viewHeight.value);
         if (
-          currentScrollTop + viewHeight.value <
-          state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight
+          viewPosition < state.monthsData[current].cssScrollHeight + state.monthsData[current].cssHeight &&
+          currentScrollTop > state.monthsData[current - 1].cssScrollHeight
         ) {
           current -= 1;
         }
         if (
           current + 1 <= state.monthsNum &&
-          currentScrollTop + viewHeight.value >=
-            state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
+          viewPosition >= state.monthsData[current + 1].cssScrollHeight + state.monthsData[current + 1].cssHeight
         ) {
           current += 1;
         }
@@ -602,6 +595,7 @@ export default create({
           current -= 1;
         }
       }
+
       if (state.currentIndex !== current) {
         state.currentIndex = current;
         setDefaultRange(state.monthsNum, current);

+ 112 - 0
src/packages/__VUE/range/__test__/range.spec.ts

@@ -0,0 +1,112 @@
+import { mount } from '@vue/test-utils';
+import { nextTick, reactive, toRefs } from 'vue';
+import Range from '../index.vue';
+import { trigger, triggerDrag } from '../../../utils/test/event';
+
+test('should render range when used ', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 30
+    }
+  });
+  let inner = wrapper.find<HTMLElement>('.nut-range-button .number');
+  expect(inner.text()).toBe('30');
+});
+
+test('should render two buttons when use range props', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: [30, 70],
+      range: true
+    }
+  });
+  let left = wrapper.find<HTMLElement>('.nut-range-button-wrapper-left .number');
+  let right = wrapper.find<HTMLElement>('.nut-range-button-wrapper-right .number');
+  expect(left.text()).toBe('30');
+  expect(right.text()).toBe('70');
+});
+
+test('should set min and max when use min and max props', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 0,
+      max: 10,
+      min: -10
+    }
+  });
+  let min = wrapper.find<HTMLElement>('.min');
+  let max = wrapper.find<HTMLElement>('.max');
+  let btn = wrapper.find<HTMLElement>('.nut-range-button-wrapper-left .number');
+  expect(min.text()).toBe('-10');
+  expect(max.text()).toBe('10');
+  expect(btn.text()).toBe('0');
+});
+
+test('should change color template when use color props', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 10,
+      inactiveColor: 'rgb(163,184,255)',
+      buttonColor: 'rgb(52,96,250)',
+      activeColor: 'rgb(238, 238, 238)'
+    }
+  });
+  await nextTick();
+  let root = wrapper.find<HTMLElement>('.nut-range');
+  let bar = wrapper.find<HTMLElement>('.nut-range-bar');
+  let btn = wrapper.find<HTMLElement>('.nut-range-button');
+  expect(root.element.style.backgroundColor).toEqual('rgb(163, 184, 255)');
+  expect(bar.element.style.background).toEqual('rgb(238, 238, 238)');
+  expect(btn.element.style.borderColor).toEqual('rgb(52,96,250)');
+});
+
+test('should render slots template when use slots', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 10
+    },
+    slots: {
+      button: '<div class="test_div">btn</div>'
+    }
+  });
+  let btn = wrapper.find<HTMLElement>('.nut-range-button-wrapper');
+  expect(btn.html()).toContain('<div class="test_div">btn</div>');
+});
+
+test('should not render elements  when use hidden elements props', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 10,
+      hiddenRange: true,
+      hiddenTag: true
+    }
+  });
+  expect(wrapper.find<HTMLElement>('.nut-range-button').exists()).toBe(true);
+  expect(wrapper.find<HTMLElement>('.min').exists()).toBe(false);
+  expect(wrapper.find<HTMLElement>('.max').exists()).toBe(false);
+  expect(wrapper.find<HTMLElement>('.nut-range-button .number').exists()).toBe(false);
+});
+
+test('should not allow to click slider when disabled', async () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 10,
+      disabled: true
+    }
+  });
+  let range = wrapper.find('.nut-range');
+  trigger(range, 'click', 100, 0);
+  expect(wrapper.emitted('change')).toBeFalsy();
+});
+
+test('should emit "change" event after dragging button', () => {
+  const wrapper = mount(Range, {
+    props: {
+      modelValue: 10
+    }
+  });
+
+  const button = wrapper.find('.nut-range-button');
+  triggerDrag(button, 50, 0);
+  expect(wrapper.emitted('change')!.pop()).toEqual([100]);
+});