ソースを参照

Merge remote-tracking branch origin/next into theme

richard1015 3 年 前
コミット
19c663d528

+ 1 - 1
package.json

@@ -82,7 +82,6 @@
     "@vue/eslint-config-typescript": "^7.0.0",
     "@vue/test-utils": "^2.0.0-rc.18",
     "autoprefixer": "^10.3.4",
-    "remark-codesandbox": "^0.10.1",
     "axios": "^0.21.0",
     "eslint": "^7.23.2",
     "eslint-plugin-prettier": "^3.3.1",
@@ -98,6 +97,7 @@
     "markdown-it-container": "^3.0.0",
     "naive-ui": "^2.24.7",
     "prettier": "^2.0.0",
+    "remark-codesandbox": "^0.10.1",
     "standard-version": "^9.3.0",
     "swiper": "6.5.1",
     "transliteration": "^2.2.0",

+ 25 - 0
src/packages/__VUE/actionsheet/__test__/__snapshots__/index.spec.ts.snap

@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render sure actionsheet when use custom props 1`] = `
+"<view class=\\"nut-actionsheet\\">
+  <view>
+    <transition-stub class=\\"\\">
+      <view class=\\"nut-overlay\\" style=\\"animation-duration: 0.3s; z-index: 2000;\\"></view>
+    </transition-stub>
+    <transition-stub>
+      <view class=\\"nut-popup round popup-bottom popclass\\" style=\\"z-index: 2000; animation-duration: 0.3s;\\">
+        <view class=\\"nut-actionsheet-panel\\">
+          <!--v-if-->
+          <!--v-if-->
+          <view class=\\"nut-actionsheet-menu\\">
+            <view class=\\"nut-actionsheet-item\\" style=\\"color: rgb(26, 26, 26);\\">选项一<view class=\\"subdesc\\">描述信息</view>
+            </view>
+          </view>
+          <!--v-if-->
+        </view>
+        <!--v-if-->
+      </view>
+    </transition-stub>
+  </view>
+</view>"
+`;

+ 123 - 3
src/packages/__VUE/actionsheet/__test__/index.spec.ts

@@ -1,7 +1,8 @@
 import { config, mount } from '@vue/test-utils';
 import ActionSheet from '../index.vue';
 import NutIcon from '../../icon/index.vue';
-import NutPopup from '../../icon/index.vue';
+import NutPopup from '../../popup/index.vue';
+import { nextTick } from 'vue';
 
 beforeAll(() => {
   config.global.components = {
@@ -14,10 +15,129 @@ afterAll(() => {
   config.global.components = {};
 });
 
-test('should render shortpassword when visible is true', async () => {
+test('should render ActionSheet when visible is true', async () => {
   const wrapper = mount(ActionSheet, {
     props: {
-      visible: true
+      visible: true,
+      isWrapTeleport: false,
+      menuItems: [
+        {
+          name: '选项一'
+        },
+        {
+          name: '选项二'
+        },
+        {
+          name: '选项三'
+        }
+      ]
     }
   });
+  const menuItem = wrapper.findAll('.nut-actionsheet-item');
+  expect(menuItem.length).toBe(3);
+});
+test('should emit select event after clicking option', async () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      menuItems: [{ name: '选项一' }]
+    }
+  });
+  const menuItem = wrapper.find('.nut-actionsheet-item');
+  menuItem.trigger('click');
+  await nextTick();
+  expect(wrapper.emitted('choose')).toHaveLength(1);
+  expect(wrapper.emitted('choose')![0]).toEqual([
+    {
+      name: '选项一'
+    },
+    0
+  ]);
+});
+
+test('should render sure actionsheet when use custom props', () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      optionTag: 'names',
+      optionSubTag: 'subname',
+      menuItems: [{ names: '选项一', subname: '描述信息' }]
+    }
+  });
+
+  expect(wrapper.html()).toMatchSnapshot();
+  let subdesc = wrapper.find('.subdesc');
+  expect(subdesc.html()).toContain('描述信息');
+});
+
+test('should render sure choose when use choose-tag-value', async () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      chooseTagValue: '选项一',
+      menuItems: [{ name: '选项一' }, { name: '选项二' }]
+    }
+  });
+  let item: any = wrapper.findAll('.nut-actionsheet-item');
+  expect(item[0].element.style.color).toContain('238, 10, 36');
+});
+
+test('should render sure color when use color', async () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      chooseTagValue: '选项一',
+      color: 'green',
+      menuItems: [{ name: '选项一' }, { name: '选项二' }]
+    }
+  });
+  let item: any = wrapper.findAll('.nut-actionsheet-item');
+  expect(item[0].element.style.color).toContain('green');
+});
+
+test('should not emit select event after clicking disabled option', async () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      menuItems: [{ name: '选项一', disabled: true }]
+    }
+  });
+
+  wrapper.find('.nut-actionsheet-item').trigger('click');
+  await nextTick();
+  expect(wrapper.emitted('select')).toBeFalsy();
+});
+
+test('should render description when use description', async () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      description: '这是一段描述信息',
+      title: '主标题',
+      menuItems: [{ name: '选项一', disabled: true }]
+    }
+  });
+  let desc = wrapper.find('.desc');
+  let title = wrapper.find('.nut-actionsheet-title');
+  expect(desc.exists()).toBeTruthy();
+  expect(title.exists()).toBeTruthy();
+});
+
+test('should emit cancel event after clicking cancel ', () => {
+  const wrapper = mount(ActionSheet, {
+    props: {
+      visible: true,
+      isWrapTeleport: false,
+      menuItems: [{ name: '选项一' }],
+      cancelTxt: '取消'
+    }
+  });
+  wrapper.find('.nut-actionsheet-cancel').trigger('click');
+  expect(wrapper.emitted('cancel')).toHaveLength(1);
 });

+ 7 - 6
src/packages/__VUE/actionsheet/index.scss

@@ -37,22 +37,23 @@
     display: block;
     //   height: 24px;
     padding: 10px;
-    line-height: 24px;
-    font-size: $font-size-base;
-    color: $title-color;
+    line-height: $actionsheet-item-line-height;
+    font-size: $actionsheet-item-font-size;
+    color: $actionsheet-item-font-color;
     text-align: center;
     background-color: #fff;
+    border-bottom: $actionsheet-item-border-bottom;
     cursor: pointer;
   }
 
   .desc {
-    font-size: $font-size-2;
+    font-size: $actionsheet-item-font-size;
     color: #999;
   }
 
   .subdesc {
     display: block;
-    font-size: $font-size-small;
+    font-size: $actionsheet-item-subdesc-font-size;
     color: #999;
   }
 
@@ -63,6 +64,6 @@
 
   .nut-actionsheet-cancel {
     margin-top: 5px;
-    border-top: 1px solid $actionsheet-light-color;
+    border-top: $actionsheet-item-cancel-border-top;
   }
 }

+ 12 - 1
src/packages/__VUE/actionsheet/index.vue

@@ -1,6 +1,13 @@
 <template>
   <view :class="classes">
-    <nut-popup pop-class="popclass" :visible="visible" position="bottom" round @click-overlay="close">
+    <nut-popup
+      pop-class="popclass"
+      :visible="visible"
+      :isWrapTeleport="isWrapTeleport"
+      position="bottom"
+      round
+      @click-overlay="close"
+    >
       <view class="nut-actionsheet-panel">
         <view v-if="title" class="nut-actionsheet-title">{{ title }}</view>
         <view class="nut-actionsheet-item desc" v-if="description">{{ description }}</view>
@@ -61,6 +68,10 @@ export default create({
     menuItems: {
       type: Array,
       default: () => []
+    },
+    isWrapTeleport: {
+      type: Boolean,
+      default: true
     }
   },
   emits: ['cancel', 'close', 'choose', 'update:visible'],

+ 3 - 3
src/packages/__VUE/cell/index.scss

@@ -2,8 +2,8 @@
   position: relative;
   display: flex;
   width: 100%;
-  line-height: 20px;
-  padding: 13px 16px;
+  line-height: $cell-line-height;
+  padding: $cell-padding;
   background: $white;
   border-radius: $cell-border-radius;
   box-shadow: 0px 1px 7px 0px rgba(237, 238, 241, 1);
@@ -23,7 +23,7 @@
     box-sizing: border-box;
     content: ' ';
     pointer-events: none;
-    right: 16px;
+    right: $cell-after-right;
     bottom: 0;
     left: 16px;
     transform: scaleY(0.5);

+ 1 - 1
src/packages/__VUE/cellgroup/index.scss

@@ -32,7 +32,7 @@
   }
   .nut-cell {
     &::after {
-      border-bottom: 2px solid #f5f6f7;
+      border-bottom: $cell-after-border-bottom;
     }
   }
 }

+ 251 - 0
src/packages/__VUE/collapse/__tests__/collapse.spec.ts

@@ -0,0 +1,251 @@
+import { config, mount } from '@vue/test-utils';
+import NutIcon from '../../icon/index.vue';
+import Collapse from '../index.vue';
+import CollapseItem from '../../collapseitem/index.vue';
+import { nextTick, reactive, ref, toRefs } from 'vue';
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+// 所有的测试用例之前执行一次
+beforeAll(() => {
+  config.global.components = {
+    NutCollapse: Collapse,
+    NutCollapseItem: CollapseItem,
+    NutIcon
+  };
+});
+// 所有的测试用例之后执行一次
+afterAll(() => {
+  config.global.components = {};
+});
+test('should props active disabled', async () => {
+  const component = {
+    template: `<nut-collapse v-model:active="active" icon="down-arrow">
+      <nut-collapse-item :name="1">
+        <template v-slot:mTitle>
+          {{ title1 }}
+        </template>
+        NutUI是一套拥有京东风格的轻量级的 Vue 组件库
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2">
+        在产品的功能、体验、易用性和灵活性等各个方面做了全面的升级!
+      </nut-collapse-item>
+    </nut-collapse>`,
+    data() {
+      return {
+        active: [1, 2],
+        title1: '标题1',
+        title2: '标题2'
+      };
+    }
+  };
+  const wrapper = mount(component);
+  expect(wrapper.findAll('.nut-collapse-item')).toHaveLength(3);
+  const collapseWrapper = wrapper.findAll('.collapse-item');
+  await nextTick();
+  expect(collapseWrapper[0].classes()).toContain('item-expanded');
+  expect(collapseWrapper[1].classes()).toContain('item-expanded');
+});
+
+test('should props accordion', async () => {
+  const component = {
+    template: `<nut-collapse v-model:active="active" icon="down-arrow" accordion>
+      <nut-collapse-item title="标题1" :name="1">
+        NutUI是一套拥有京东风格的轻量级的 Vue 组件库
+      </nut-collapse-item>
+      <nut-collapse-item title="标题2" :name="2">
+        在产品的功能、体验、易用性和灵活性等各个方面做了全面的升级!
+      </nut-collapse-item>
+    </nut-collapse>`,
+    data() {
+      return {
+        active: 1
+      };
+    }
+  };
+  const wrapper = mount(component);
+  await nextTick();
+  expect(wrapper.findAll('.item-expanded')).toHaveLength(1);
+  const collapseWrapper = wrapper.findAll('.collapse-item');
+  collapseWrapper[0].trigger('click');
+  await nextTick();
+  expect(wrapper.findAll('.item-expanded')).toHaveLength(0);
+});
+
+test('should title props ', async () => {
+  const wrapper = mount({
+    template: `
+    <nut-collapse
+      v-model:active="activeName"
+      :title-icon="titleIcon"
+      :title-icon-color="titleIconColor"
+      :title-icon-size="titleIconSize"
+      :title-icon-position="titleIconPos"
+      icon="down-arrow"
+      :accordion="true"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        组件 emits 事件单独提取,增强代码可读性
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" sub-title="文本内容">
+        使用 Teleport 新特性重构挂载类组件
+      </nut-collapse-item>
+    </nut-collapse>
+    `,
+    setup() {
+      const activeName = ref(1);
+      const title = reactive({
+        title1: '标题1',
+        title2: '标题2',
+        titleIcon: 'issue',
+        titleIconSize: '20px',
+        titleIconColor: 'red',
+        titleIconPos: 'left'
+      });
+      return {
+        activeName,
+        ...toRefs(title)
+      };
+    }
+  });
+  await nextTick();
+  const collapseIcon = wrapper.findAll('.nut-icon');
+  expect(collapseIcon[0].classes()).toContain('nut-icon-issue');
+
+  const collapseIconEle = collapseIcon[0].element as HTMLElement;
+  expect(collapseIconEle.style.color).toEqual(wrapper.vm.titleIconColor);
+  expect(collapseIconEle.style.fontSize).toEqual(wrapper.vm.titleIconSize);
+  expect(collapseIcon[0].classes()).toContain('titleIconLeft');
+});
+
+test('should icon props ', async () => {
+  const wrapper = mount({
+    template: `
+    <nut-collapse
+      v-model:active="activeName"
+      :accordion="true"
+      :icon="icon"
+      :icon-size="iconSize"
+      :icon-color="iconColor"
+      :rotate="rotate"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        组件 emits 事件单独提取,增强代码可读性
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" sub-title="文本内容">
+        使用 Teleport 新特性重构挂载类组件
+      </nut-collapse-item>
+    </nut-collapse>
+    `,
+    setup() {
+      const activeName = ref(1);
+      const title = reactive({
+        title1: '标题1',
+        title2: '标题2',
+        icon: 'down-arrow',
+        iconSize: '16px',
+        iconColor: 'red',
+        rotate: '90'
+      });
+      return {
+        activeName,
+        ...toRefs(title)
+      };
+    }
+  });
+  await nextTick();
+  const collapseIcon = wrapper.findAll('.nut-icon');
+  expect(collapseIcon[0].classes()).toContain('nut-icon-' + wrapper.vm.icon);
+
+  const collapseIconEle = collapseIcon[0].element as HTMLElement;
+  expect(collapseIconEle.style.fontSize).toEqual(wrapper.vm.iconSize);
+  expect(collapseIconEle.style.color).toEqual(wrapper.vm.iconColor);
+  expect(collapseIconEle.style.transform).toEqual(`rotate(${wrapper.vm.rotate}deg)`);
+});
+
+test('should nut-collapse-item props ', async () => {
+  const wrapper = mount({
+    template: `
+    <nut-collapse
+      v-model:active="activeName"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        组件 emits 事件单独提取,增强代码可读性
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" :sub-title="subTitle">
+        使用 Teleport 新特性重构挂载类组件
+      </nut-collapse-item>
+      <nut-collapse-item :title="title3" :name="3" :disabled="disabled"> </nut-collapse-item>
+    </nut-collapse>
+    `,
+    setup() {
+      const activeName = ref(1);
+      const title = reactive({
+        title1: '标题1',
+        title2: '标题2',
+        title3: '标题3',
+        subTitle: '文本内容',
+        disabled: true
+      });
+      return {
+        activeName,
+        ...toRefs(title)
+      };
+    }
+  });
+  await nextTick();
+  const collapseItemTitle = wrapper.findAll('.collapse-title-value');
+  const collapseIconEle1 = collapseItemTitle[0].element as HTMLElement;
+  expect(collapseIconEle1.textContent).toEqual(wrapper.vm.title1);
+  const collapseIconEle2 = collapseItemTitle[1].element as HTMLElement;
+  expect(collapseIconEle2.textContent).toEqual(wrapper.vm.title2);
+
+  const collapseItemSubTitle = wrapper.findAll('.subTitle')[1].element as HTMLElement;
+  expect(collapseItemSubTitle.textContent).toEqual(wrapper.vm.subTitle);
+
+  const collapseWrapper = wrapper.findAll('.collapse-item');
+  expect(collapseWrapper[2].classes()).toContain('nut-collapse-item-disabled');
+});
+
+test('should event change', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-collapse': Collapse,
+      'nut-collapse-item': CollapseItem
+    },
+    template: `
+      <nut-collapse v-model:active="active" :accordion="true" @change="change">
+        <nut-collapse-item :title="title1" :name="1">
+          引入Vue3新特性 Composition API、Teleport、Emits 等
+        </nut-collapse-item>
+        <nut-collapse-item :title="title2" :name="2"> 全面使用 TypeScipt </nut-collapse-item>
+      </nut-collapse>
+    `,
+    setup() {
+      const state = reactive({
+        activeName: 1
+      });
+      const title = reactive({
+        active: 1,
+        title1: '标题1',
+        title2: '标题2'
+      });
+      const change = (name: any) => {
+        state.activeName = name;
+      };
+      return {
+        ...toRefs(title),
+        ...toRefs(state),
+        change
+      };
+    }
+  });
+
+  await nextTick();
+  const collapseWrapper = wrapper.findAll('.collapse-item');
+  collapseWrapper[1].trigger('click');
+  await sleep(1000);
+  expect(wrapper.vm.activeName).toBe(2);
+});

+ 1 - 1
src/packages/__VUE/collapse/demo.vue

@@ -14,7 +14,7 @@
       <nut-collapse-item :title="title3" :name="3" disabled> </nut-collapse-item>
     </nut-collapse>
     <h2>无icon样式</h2>
-    <nut-collapse v-model:active="active4" :accordion="true">
+    <nut-collapse v-model:active="active4" :accordion="true" @change="change">
       <nut-collapse-item :title="title1" :name="1">
         引入Vue3新特性 Composition API、Teleport、Emits 等
       </nut-collapse-item>

+ 3 - 1
src/packages/__VUE/collapseitem/index.vue

@@ -127,7 +127,9 @@ export default create({
     // 清除 willChange 减少性能浪费
     const onTransitionEnd = () => {
       const wrapperRefEle: any = document.getElementsByClassName('collapse-wrapper')[0];
-      wrapperRefEle.style.willChange = 'auto';
+      if (wrapperRefEle) {
+        wrapperRefEle.style.willChange = 'auto';
+      }
 
       // const query = wx.createSelectorQuery();
       // query.select('#productServe').boundingClientRect();

+ 1 - 2
src/packages/__VUE/toast/doc.md

@@ -285,6 +285,5 @@ toast.hide();
 | loadingRotate       | loading图标是否旋转,仅对loading类型生效                                      | Boolean       | true                          |
 | on-close             | 关闭时触发的事件                                                              | function      | null                          |
 | close-on-click-overlay | 是否在点击遮罩层后关闭提示                                                    | Boolean       | false                         |
-| toast-style          | 提示框style                                                        | object       | {}                         |
-| toast-class          | 提示框class                                                        | String       | ""                         |
+| custom-class          | 提示框class                                                        | String       | ""                         |
 

+ 10 - 0
src/packages/styles/variables.scss

@@ -97,6 +97,10 @@ $cell-title-desc-font: $font-size-1 !default;
 $cell-desc-font: $font-size-2 !default;
 $cell-desc-color: $disable-color !default;
 $cell-border-radius: 6px !default;
+$cell-padding: 13px 16px !default;
+$cell-line-height: 20px !default;
+$cell-after-right: 16px !default;
+$cell-after-border-bottom: 2px solid #f5f6f7 !default;
 
 // cell-group
 
@@ -150,6 +154,12 @@ $inputnumber-icon-size: 20px !default;
 
 // actionsheet
 $actionsheet-light-color: #f6f6f6 !default;
+$actionsheet-item-border-bottom: none !default;
+$actionsheet-item-font-size: $font-size-2 !default;
+$actionsheet-item-subdesc-font-size: $font-size-1 !default;
+$actionsheet-item-cancel-border-top: 1px solid $actionsheet-light-color !default;
+$actionsheet-item-line-height: 24px !default;
+$actionsheet-item-font-color: $title-color !default;
 
 //shortpassword
 $shortpassword-background-color: rgba(245, 245, 245, 1) !default;