浏览代码

fix(toast): add test and add new props (#1057)

* fix: toast add test add new props
Drjingfubo 3 年之前
父节点
当前提交
2a60dcc0a1

+ 11 - 10
src/packages/__VUE/toast/doc.md

@@ -270,20 +270,21 @@ toast.hide();
 
 | 字段                | 说明                                                                          | 类型          | 默认值                        |
 | ------------------- | ----------------------------------------------------------------------------- | ------------- | ----------------------------- |
-| id                  | 标识符,相同者共用一个实例<br>loading类型默认使用一个实例,其他类型默认不共用 | String/Number | -                             |
+| id                  | 标识符,相同者共用一个实例<br>loading类型默认使用一个实例,其他类型默认不共用 | String/Number | -                       |
 | duration            | 展示时长(毫秒)<br>值为 0 时,toast 不会自动消失      | Number        | 2000                          |
-| title            | 标题     | String        | ''                          |
+| title            | 标题     | String        |           -             |
 | center              | 是否展示在页面中部(为false时展示在底部)                                     | Boolean       | true                          |
-| bottom              | 距页面底部的距离(像素或者百分比),option.center为false时生效                          | String       | "30px"                     |
+| bottom              | 距页面底部的距离(像素或者百分比),option.center为false时生效                          | String       | 30px                    |
 | textAlignCenter     | 多行文案是否居中                                                              | Boolean       | true                          |
-| bgColor             | 背景颜色(透明度)                                                            | String        | "rgba(0, 0, 0, 0.8)"       |
-| customClass         | 自定义类名                                                                    | String        | ""                            |
-| icon                | 自定义图标,**支持图片链接或base64格式**                                        | String        | ""                            |
-| size                | 文案尺寸,**small**/**base**/**large**三选一                                                  | String        | "base"                        |
+| bgColor             | 背景颜色(透明度)                                                            | String        | rgba(0, 0, 0, 0.8)      |
+| customClass         | 自定义类名                                                                    | String        |          -                   |
+| icon                | 自定义图标,**支持图片链接或base64格式**                                        | String        |         -                   |
+| iconSize                | 自定义图标尺寸                                      | String        | 20                           |
+| size                | 文案尺寸,**small**/**base**/**large**三选一                                                  | String        | base                      |
 | cover               | 是否显示遮罩层                                          | Boolean       | false |
-| cover-color          | 遮罩层颜色,默认透明                                                          | String        | "rgba(0,0,0,0)"               |
+| cover-color          | 遮罩层颜色,默认透明                                                          | String        | rgba(0,0,0,0)             |
 | loadingRotate       | loading图标是否旋转,仅对loading类型生效                                      | Boolean       | true                          |
-| on-close             | 关闭时触发的事件                                                              | function      | null                          |
+| close             | 关闭时触发的事件                                                              | function      | null                          |
 | close-on-click-overlay | 是否在点击遮罩层后关闭提示                                                    | Boolean       | false                         |
-| custom-class          | 提示框class                                                        | String       | ""                         |
+| custom-class          | 提示框class                                                        | String       |               -          |
 

+ 5 - 4
src/packages/__VUE/toast/index.ts

@@ -7,10 +7,11 @@ const defaultOptions = {
   center: true, // 未实现
   type: 'text',
   title: '',
-  customClass: '', // 未实现
-  bottom: '30px', // 未实现
-  size: 'base', // 未实现
-  icon: null, // 未实现
+  customClass: '',
+  bottom: '30px',
+  size: 'base',
+  iconSize: '20',
+  icon: null,
   textAlignCenter: true, // 未实现
   loadingRotate: true, // 未实现
   bgColor: 'rgba(0, 0, 0, .8)',

+ 10 - 4
src/packages/__VUE/toast/index.vue

@@ -17,7 +17,7 @@
         }"
       >
         <view v-if="hasIcon" class="nut-toast-icon-wrapper">
-          <nut-icon size="20" color="#ffffff" :name="icon"></nut-icon>
+          <nut-icon :size="iconSize" color="#ffffff" :name="icon"></nut-icon>
         </view>
         <div v-if="title" class="nut-toast-title">
           {{ title }}
@@ -27,7 +27,7 @@
     </view>
   </Transition>
 </template>
-<script>
+<script lang="ts">
 import { toRefs, toRef, reactive, computed, watch, onMounted } from 'vue';
 import { createComponent } from '../../utils/create';
 const { create } = createComponent('toast');
@@ -57,6 +57,10 @@ export default create({
       type: [String, Number],
       default: 'base'
     },
+    iconSize: {
+      type: String,
+      default: '20'
+    },
     icon: String,
     textAlignCenter: {
       type: Boolean,
@@ -89,8 +93,9 @@ export default create({
       default: false
     }
   },
-  setup(props) {
-    let timer;
+  emits: ['close'],
+  setup(props: any, { emit }) {
+    let timer: number | null | undefined;
     const state = reactive({
       mounted: false
     });
@@ -117,6 +122,7 @@ export default create({
     const clickCover = () => {
       if (props.closeOnClickOverlay) {
         hide();
+        emit('close');
       }
     };
 

+ 37 - 0
src/packages/__VUE/toast/test/function.spec.ts

@@ -0,0 +1,37 @@
+import { ToastFunction } from './toast';
+
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+
+describe('function toast', () => {
+  test('show loading toast', async () => {
+    ToastFunction.loading('加载中');
+    expect(document.querySelector('.nut-toast.nut-toast-loading')).toBeTruthy();
+    let text = document.querySelector('.nut-toast .nut-toast-text') as HTMLDivElement;
+    expect(text.innerHTML).toEqual('加载中');
+    await sleep(3000);
+    let loading = document.querySelector('.nut-toast.nut-toast-loading');
+    expect(loading).toBeNull();
+  });
+
+  test('show text toast', async () => {
+    ToastFunction.text('文案', {
+      duration: 2000,
+      title: '标题文字'
+    });
+    await sleep();
+    let textToast: any = document.querySelector('.nut-toast.nut-toast-center');
+    expect(textToast.innerHTML).toContain('标题文字');
+    await sleep(3000);
+    expect(textToast.style.display).toEqual('none');
+  });
+  test('show fail toast', async () => {
+    ToastFunction.fail('文案');
+    await sleep();
+    let failToast: any = document.querySelector('.nut-icon-failure');
+    expect(failToast.style.fontSize).toEqual('20px');
+  });
+});

+ 85 - 0
src/packages/__VUE/toast/test/index.spec.ts

@@ -0,0 +1,85 @@
+import { config, mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import Toast from '../index.vue';
+
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+
+describe('component toast', () => {
+  test('should render toast after using msg and id', async () => {
+    const wrapper = mount(Toast, {
+      props: {
+        id: '12313',
+        msg: 'taost'
+      }
+    });
+    await sleep(1000);
+    let toast = wrapper.find('.nut-toast');
+    expect(toast.exists()).toBe(true);
+  });
+
+  test('should be displayed long enough when use duration ', async () => {
+    const wrapper = mount(Toast, {
+      props: {
+        msg: 'taost',
+        duration: 3000
+      }
+    });
+    await sleep(100);
+    let toast: any = wrapper.find('.nut-toast');
+    expect(toast.exists()).toBe(true);
+    await sleep(3100);
+    expect(toast.element.style.display).toEqual('none');
+  });
+
+  test('should change cover style after using cover-color prop', async () => {
+    const wrapper = mount(Toast, {
+      props: {
+        cover: true,
+        coverColor: 'black'
+      }
+    });
+    await sleep();
+    let toastCover: any = wrapper.find('.nut-toast-cover');
+    expect(toastCover.element.style.backgroundColor).toEqual('black');
+  });
+  test('should close Toast when using closeOnClickOverlay prop and clicked', async () => {
+    const wrapper = mount(Toast, {
+      props: {
+        cover: true,
+        coverColor: 'black',
+        closeOnClickOverlay: true
+      }
+    });
+    await sleep();
+    let toast: any = wrapper.find('.nut-toast');
+    toast.trigger('click');
+    await nextTick();
+    expect(toast.element.style.display).toEqual('none');
+  });
+  test('should render customClass when using customClass prop ', async () => {
+    const wrapper = mount(Toast, {
+      props: {
+        customClass: 'custom'
+      }
+    });
+    await sleep();
+    let toast: any = wrapper.find('.custom');
+    expect(toast.exists()).toBe(true);
+  });
+  test('should change icon size when using icon-size prop', async () => {
+    const wrapper = mount(Toast, {
+      props: {
+        icon: 'success',
+        iconSize: '30'
+      }
+    });
+
+    await sleep();
+    let icon: any = wrapper.find('.nut-icon');
+    expect(icon.element.style.fontSize).toEqual('30px');
+  });
+});

+ 128 - 0
src/packages/__VUE/toast/test/toast.ts

@@ -0,0 +1,128 @@
+import { createVNode, render } from 'vue';
+import Toast from '../index.vue';
+const defaultOptions = {
+  msg: '',
+  id: '',
+  duration: 2000, //显示时间(毫秒)
+  center: true, // 未实现
+  type: 'text',
+  title: '',
+  customClass: '', // 未实现
+  bottom: '30px', // 未实现
+  size: 'base', // 未实现
+  iconSize: '20',
+  icon: null, // 未实现
+  textAlignCenter: true, // 未实现
+  loadingRotate: true, // 未实现
+  bgColor: 'rgba(0, 0, 0, .8)',
+  onClose: null, // 未实现
+  unmount: null,
+  cover: false, //透明遮罩层 // 未实现
+  coverColor: 'rgba(0, 0, 0, 0)', // 未实现
+  closeOnClickOverlay: false // 未实现
+};
+
+let idsMap: string[] = [];
+let optsMap: any[] = [];
+const clearToast = (id?: string) => {
+  if (id) {
+    const container = document.getElementById(id);
+    optsMap = optsMap.filter((item) => item.id !== id);
+    idsMap = idsMap.filter((item) => item !== id);
+    if (container) {
+      document.body.removeChild(container);
+    }
+  } else {
+    idsMap.forEach((item) => {
+      const container = document.getElementById(item);
+      if (container) {
+        document.body.removeChild(container);
+      }
+    });
+    optsMap = [];
+    idsMap = [];
+  }
+};
+
+const updateToast = (opts: any) => {
+  const container = document.getElementById(opts.id);
+  if (container) {
+    const currentOpt = optsMap.find((item) => item.id === opts.id);
+    if (currentOpt) {
+      opts = { ...defaultOptions, ...currentOpt, ...opts };
+    } else {
+      opts = { ...defaultOptions, ...opts };
+    }
+    const instance: any = createVNode(Toast, opts);
+    render(instance, container);
+    return instance.component.ctx;
+  }
+};
+
+const mountToast = (opts: any) => {
+  opts.unmount = clearToast;
+  let _id;
+  // 如果是更新已有的toast
+  if (opts.id) {
+    _id = opts.id;
+    if (idsMap.find((item) => item === opts.id)) {
+      return updateToast(opts);
+    }
+  } else {
+    _id = new Date().getTime() + '';
+  }
+  opts = { ...defaultOptions, ...opts };
+  opts.id = _id;
+  idsMap.push(opts.id);
+  optsMap.push(opts);
+  const container = document.createElement('div');
+  container.id = opts.id;
+  const instance: any = createVNode(Toast, opts);
+  render(instance, container);
+  document.body.appendChild(container);
+  return instance.component.ctx;
+};
+
+const errorMsg = (msg: string) => {
+  if (!msg) {
+    console.warn('[NutUI Toast]: msg不能为空');
+    return;
+  }
+};
+
+export const ToastFunction = {
+  text(msg: string, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ ...opts, type: 'text', msg });
+  },
+  success(msg: string, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ icon: 'success', ...opts, msg, type: 'success' });
+  },
+  fail(msg: string, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ icon: 'failure', ...opts, msg, type: 'fail' });
+  },
+  warn(msg: string, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ icon: 'tips', ...opts, msg, type: 'warn' });
+  },
+  loading(msg: string, opts = {}) {
+    return mountToast({
+      icon: 'loading',
+      ...opts,
+      msg,
+      type: 'loading'
+    });
+  },
+  hide(id?: string) {
+    clearToast(id);
+  },
+  install(app: any): void {
+    app.use(Toast);
+    app.config.globalProperties.$toast = ToastFunction;
+  }
+};
+
+export { Toast };
+export default ToastFunction;