• React-View-UI组件库封装—— Notification通知提醒框


    演示

    提醒框是一个很常用的交互组件,效果如下:

    在这里插入图片描述
    触发某事件后出现提示信息达到反馈效果,与前文Message效果类似。

    组件库文档

    组件库文档页面如下:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    文档中可以看到,组件封装中提供了不同位置、不同类型、自定义操作按钮、自定义样式的支持,让组件更加灵活,同时提供了如下的API能力:

    在这里插入图片描述

    调用方式 & 封装思路

    代码如下:

    Notification.info({
      title: 'Notification',
      content: 'this is a Notification!',
      duration: 3000,
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    因为是暴露给用户一个函数,因此组件的设计其实也是在监听到调用时,转入到组件内部自定义函数进行调用:

    Notification.info = (props: string | NotificationProps<string>) => {
      return addInstance('info', props);
    };
    Notification.success = (props: string | NotificationProps<string>) => {
      return addInstance('success', props);
    };
    Notification.error = (props: string | NotificationProps<string>) => {
      return addInstance('error', props);
    };
    Notification.normal = (props: string | NotificationProps<string>) => {
      return addInstance('normal', props);
    };
    Notification.warning = (props: string | NotificationProps<string>) => {
      return addInstance('warning', props);
    };
    Notification.loading = (props: string | NotificationProps<string>) => {
      return addInstance('loading', props);
    };
    
    export default Notification;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可以看到,其实就是暴露出去了6个函数,再看一下addInstance函数吧:

    //添加消息窗口
    function addInstance(
      type: 'info' | 'success' | 'warning' | 'error' | 'normal' | 'loading',
      props: string | NotificationProps<string>,
    ) {
      let style: CSSProperties = {},
        duration: number = 3000,
        title: string = '',
        content: string = '',
        position: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' = 'topRight',
        clearable: boolean = false,
        showFooter: boolean = false,
        footerBtnVal: footerBtnVal = {
          enter: 'OK',
          exit: 'Cancel',
        },
        doneCallback: Function | undefined;
      if (typeof props === 'object') {
        title = props.title;
        style = props.style || {};
        duration = props.duration || 3000;
        content = props.content as string;
        doneCallback = props.doneCallback;
        if (!props.position) {
          position = 'topRight';
        } else {
          position = props.position;
        }
        clearable = props.clearable ? props.clearable : false;
        showFooter = props.showFooter ? props.showFooter : false;
        if (props.footerBtnVal) {
          footerBtnVal = props.footerBtnVal as footerBtnVal;
        }
      } else if (typeof props === 'string') {
        content = props;
      }
      const div = document.createElement('div');
      const messageBoxId = String(Math.floor(Math.random() * 1000));
      div.setAttribute('class', `${position}-${messageBoxId}`);
      if (container) {
        container.appendChild(div);
      } else {
        container = document.createElement('div');
        container.setAttribute('class', 'notification-container');
        document.body.appendChild(container);
        container.appendChild(div);
      }
      setTimeout(() => {
        if (Array.prototype.slice.call(container?.childNodes).includes(div)) {
          changeHeight(Array.prototype.slice.call(container?.childNodes), position);
          container?.removeChild(div);
          if (position === 'topLeft') {
            topLeftMessageNum--;
          } else if (position === 'topRight') {
            topRightMessageNum--;
          } else if (position === 'bottomLeft') {
            bottomLeftMessageNum--;
          } else if (position === 'bottomRight') {
            bottomRightMessageNum--;
          }
        }
      }, duration + 200);
      //挂载组件
      ReactDOM.render(
        <Notification
          title={title}
          style={style}
          content={content}
          type={type}
          duration={duration}
          position={position}
          clearable={clearable}
          showFooter={showFooter}
          footerBtnVal={footerBtnVal}
          doneCallback={doneCallback}
          messageBoxId={messageBoxId}
        />,
        div,
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    函数功能分两步:

    1. 收集传入对象参数,作为props备用,处理初始化值;
    2. 创建一个全局dom,用来存放所有的通知提醒框,插入到这个容器中,并赋予一个独特的id,以便于后期删除;

    在最后,ReactDOM.render挂载了这个组件,做到这一步,其实点击后的交互就出来了。

    Notification组件代码如下:

    const Notification = (props: NotificationProps<string>) => {
      const {
        style,
        title,
        content,
        type,
        duration,
        position,
        clearable,
        showFooter,
        footerBtnVal,
        doneCallback,
        messageBoxId,
      } = props;
      const [opac, setOpac] = useState(1);
      const messageDom = useRef<any>(null);
    
      useEffect(() => {
        if (position === 'topLeft') {
          topLeftMessageNum++;
        } else if (position === 'topRight') {
          topRightMessageNum++;
        } else if (position === 'bottomLeft') {
          bottomLeftMessageNum++;
        } else if (position === 'bottomRight') {
          bottomRightMessageNum++;
        }
        setTimeout(() => {
          (messageDom.current as HTMLElement).style.transition = '0.2s linear';
          (messageDom.current as HTMLElement).style.animation = 'none';
        }, 500);
        setTimeout(() => {
          setOpac(0);
        }, duration);
      }, []);
    
      useEffect(() => {
        let transform;
        if (position?.startsWith('top')) {
          transform = 'top';
        } else {
          transform = 'bottom';
        }
        let defaultHeight = 0;
        let avaHeight;
        if (position === 'topLeft' && topLeftMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (topLeftMessageNum - 1);
          avaHeight = topLeftMessageNum;
        } else if (position === 'topRight' && topRightMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (topRightMessageNum - 1);
          avaHeight = topRightMessageNum;
        } else if (position === 'bottomLeft' && bottomLeftMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (bottomLeftMessageNum - 1);
          avaHeight = bottomLeftMessageNum;
        } else if (position === 'bottomRight' && bottomRightMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (bottomRightMessageNum - 1);
          avaHeight = bottomRightMessageNum;
        }
        (messageDom?.current as HTMLElement).style[transform as 'top' | 'bottom'] =
          (avaHeight as number) * 30 + defaultHeight + 'px';
      }, [topLeftMessageNum, topRightMessageNum, bottomLeftMessageNum, bottomRightMessageNum]);
    
      const messageIcon = useMemo(() => {
        if (type === 'info') {
          return <ExclamationCircleFilled style={{ color: '#1890ff', fontSize: '24px' }} />;
        } else if (type === 'error') {
          return <CloseCircleFilled style={{ color: '#f53f3f', fontSize: '24px' }} />;
        } else if (type === 'normal') {
          return <></>;
        } else if (type === 'success') {
          return <CheckCircleFilled style={{ color: '#19b42a', fontSize: '24px' }} />;
        } else if (type === 'warning') {
          return <ExclamationCircleFilled style={{ color: '#fa7d00', fontSize: '24px' }} />;
        } else if (type === 'loading') {
          return <LoadingOutlined style={{ color: '#1890ff', fontSize: '24px' }} />;
        }
      }, [type]);
    
      const messageXtransform = useMemo(() => {
        //提示框水平位置,居左/居右
        if (position?.includes('Left')) {
          return {
            left: '20px',
          };
        } else {
          return {
            right: '20px',
          };
        }
      }, [position]);
      const closeMessage = () => {
        //close按钮关闭
        remove(messageBoxId as string, position as string, () => {
          doneCallback && doneCallback(1);
        });
      };
      const enter = () => {
        //确认关闭
        remove(messageBoxId as string, position as string, () => {
          doneCallback && doneCallback(2);
        });
      };
      const exit = () => {
        //取消关闭
        remove(messageBoxId as string, position as string, () => {
          doneCallback && doneCallback(3);
        });
      };
    
      return (
        <div
          className="notifica-container"
          style={{ opacity: opac, ...messageXtransform, ...style }}
          ref={messageDom}
        >
          <div className="title">
            <div className="title-left">
              {messageIcon}
              <span className="title-content">{title}</span>
            </div>
            {clearable && <CloseOutlined className="close-icon" onClick={closeMessage} />}
          </div>
          <div className="notification-content">{content}</div>
          {showFooter && (
            <div className="notification-footer">
              <div></div>
              <div>
                <Button type="text" height={30} handleClick={enter}>
                  {(footerBtnVal as footerBtnVal).exit}
                </Button>
                <Button type="primary" height={30} style={{ marginLeft: '15px' }} handleClick={exit}>
                  {(footerBtnVal as footerBtnVal).enter}
                </Button>
              </div>
            </div>
          )}
        </div>
      );
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

    组件中的代码主要做了一些样式设计,可以看到组件中共有三个事件:

    • enter
    • exit
    • closeMessage

    在其中,都调用了move方法,而move方法并不在组件中,原因很简单,move其实像所有组件的父亲一样,用于全局控制组件销毁后页面上dom节点的删除,看一下move方法:

    //移除窗口
    function remove(id: string, position: string, callback: Function) {
      const container = document.querySelector('.notification-container');
      const children = Array.prototype.slice.call(container?.childNodes);
      for (let key in children) {
        if (children[key].getAttribute('class') === `${position}-${id}`) {
          const removeDom = children[key];
          console.log(removeDom.childNodes);
          removeDom.childNodes[0].style.opacity = 0;
          setTimeout(() => {
            container?.removeChild(removeDom);
          }, 50);
    
          if (position === 'topLeft') {
            topLeftMessageNum--;
          } else if (position === 'topRight') {
            topRightMessageNum--;
          } else if (position === 'bottomLeft') {
            bottomLeftMessageNum--;
          } else if (position === 'bottomRight') {
            bottomRightMessageNum--;
          }
          changeHeight(children.slice(Number(key)), position);
          callback();
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    根据我们最早挂载的id唯一标识,去查找这样标识的dom节点,删除,并把对应位置的计数减1之后调用changeHeight方法重排手动删除元素下的元素位置(让他们顶上去,不留空隙)

    changeHeight方法如下:

    //重排节点下窗口高度
    function changeHeight(children: Array<HTMLElement>, position: any) {
      const transform = position.startsWith('top') ? 'top' : 'bottom';
      for (let key in children) {
        const child = children[key].childNodes[0] as HTMLElement;
        if (children[key].getAttribute('class')?.startsWith(transform)) {
          const domHeight = document.querySelector('.notifica-container')?.clientHeight;
          child.style[transform] =
            Number(child.style[transform].split('p')[0]) - 30 - (domHeight as number) + 'px';
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    组件完整源码

    import React, { useState, useEffect, useMemo, useRef, CSSProperties } from 'react';
    import ReactDOM from 'react-dom';
    import { NotificationProps, footerBtnVal } from './interface';
    import Button from '../Button';
    import './index.module.less';
    import {
      ExclamationCircleFilled,
      CheckCircleFilled,
      CloseCircleFilled,
      LoadingOutlined,
      CloseOutlined,
    } from '@ant-design/icons';
    
    let container: HTMLDivElement | null;
    let topLeftMessageNum: number = 0;
    let topRightMessageNum: number = 0;
    let bottomLeftMessageNum: number = 0;
    let bottomRightMessageNum: number = 0;
    
    //添加消息窗口
    function addInstance(
      type: 'info' | 'success' | 'warning' | 'error' | 'normal' | 'loading',
      props: string | NotificationProps<string>,
    ) {
      let style: CSSProperties = {},
        duration: number = 3000,
        title: string = '',
        content: string = '',
        position: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' = 'topRight',
        clearable: boolean = false,
        showFooter: boolean = false,
        footerBtnVal: footerBtnVal = {
          enter: 'OK',
          exit: 'Cancel',
        },
        doneCallback: Function | undefined;
      if (typeof props === 'object') {
        title = props.title;
        style = props.style || {};
        duration = props.duration || 3000;
        content = props.content as string;
        doneCallback = props.doneCallback;
        if (!props.position) {
          position = 'topRight';
        } else {
          position = props.position;
        }
        clearable = props.clearable ? props.clearable : false;
        showFooter = props.showFooter ? props.showFooter : false;
        if (props.footerBtnVal) {
          footerBtnVal = props.footerBtnVal as footerBtnVal;
        }
      } else if (typeof props === 'string') {
        content = props;
      }
      const div = document.createElement('div');
      const messageBoxId = String(Math.floor(Math.random() * 1000));
      div.setAttribute('class', `${position}-${messageBoxId}`);
      if (container) {
        container.appendChild(div);
      } else {
        container = document.createElement('div');
        container.setAttribute('class', 'notification-container');
        document.body.appendChild(container);
        container.appendChild(div);
      }
      setTimeout(() => {
        if (Array.prototype.slice.call(container?.childNodes).includes(div)) {
          changeHeight(Array.prototype.slice.call(container?.childNodes), position);
          container?.removeChild(div);
          if (position === 'topLeft') {
            topLeftMessageNum--;
          } else if (position === 'topRight') {
            topRightMessageNum--;
          } else if (position === 'bottomLeft') {
            bottomLeftMessageNum--;
          } else if (position === 'bottomRight') {
            bottomRightMessageNum--;
          }
        }
      }, duration + 200);
      //挂载组件
      ReactDOM.render(
        <Notification
          title={title}
          style={style}
          content={content}
          type={type}
          duration={duration}
          position={position}
          clearable={clearable}
          showFooter={showFooter}
          footerBtnVal={footerBtnVal}
          doneCallback={doneCallback}
          messageBoxId={messageBoxId}
        />,
        div,
      );
    }
    //移除窗口
    function remove(id: string, position: string, callback: Function) {
      const container = document.querySelector('.notification-container');
      const children = Array.prototype.slice.call(container?.childNodes);
      for (let key in children) {
        if (children[key].getAttribute('class') === `${position}-${id}`) {
          const removeDom = children[key];
          console.log(removeDom.childNodes);
          removeDom.childNodes[0].style.opacity = 0;
          setTimeout(() => {
            container?.removeChild(removeDom);
          }, 50);
    
          if (position === 'topLeft') {
            topLeftMessageNum--;
          } else if (position === 'topRight') {
            topRightMessageNum--;
          } else if (position === 'bottomLeft') {
            bottomLeftMessageNum--;
          } else if (position === 'bottomRight') {
            bottomRightMessageNum--;
          }
          changeHeight(children.slice(Number(key)), position);
          callback();
        }
      }
    }
    //重排节点下窗口高度
    function changeHeight(children: Array<HTMLElement>, position: any) {
      const transform = position.startsWith('top') ? 'top' : 'bottom';
      for (let key in children) {
        const child = children[key].childNodes[0] as HTMLElement;
        if (children[key].getAttribute('class')?.startsWith(transform)) {
          const domHeight = document.querySelector('.notifica-container')?.clientHeight;
          child.style[transform] =
            Number(child.style[transform].split('p')[0]) - 30 - (domHeight as number) + 'px';
        }
      }
    }
    const Notification = (props: NotificationProps<string>) => {
      const {
        style,
        title,
        content,
        type,
        duration,
        position,
        clearable,
        showFooter,
        footerBtnVal,
        doneCallback,
        messageBoxId,
      } = props;
      const [opac, setOpac] = useState(1);
      const messageDom = useRef<any>(null);
    
      useEffect(() => {
        if (position === 'topLeft') {
          topLeftMessageNum++;
        } else if (position === 'topRight') {
          topRightMessageNum++;
        } else if (position === 'bottomLeft') {
          bottomLeftMessageNum++;
        } else if (position === 'bottomRight') {
          bottomRightMessageNum++;
        }
        setTimeout(() => {
          (messageDom.current as HTMLElement).style.transition = '0.2s linear';
          (messageDom.current as HTMLElement).style.animation = 'none';
        }, 500);
        setTimeout(() => {
          setOpac(0);
        }, duration);
      }, []);
    
      useEffect(() => {
        let transform;
        if (position?.startsWith('top')) {
          transform = 'top';
        } else {
          transform = 'bottom';
        }
        let defaultHeight = 0;
        let avaHeight;
        if (position === 'topLeft' && topLeftMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (topLeftMessageNum - 1);
          avaHeight = topLeftMessageNum;
        } else if (position === 'topRight' && topRightMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (topRightMessageNum - 1);
          avaHeight = topRightMessageNum;
        } else if (position === 'bottomLeft' && bottomLeftMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (bottomLeftMessageNum - 1);
          avaHeight = bottomLeftMessageNum;
        } else if (position === 'bottomRight' && bottomRightMessageNum >= 1) {
          defaultHeight = messageDom.current.clientHeight * (bottomRightMessageNum - 1);
          avaHeight = bottomRightMessageNum;
        }
        (messageDom?.current as HTMLElement).style[transform as 'top' | 'bottom'] =
          (avaHeight as number) * 30 + defaultHeight + 'px';
      }, [topLeftMessageNum, topRightMessageNum, bottomLeftMessageNum, bottomRightMessageNum]);
    
      const messageIcon = useMemo(() => {
        if (type === 'info') {
          return <ExclamationCircleFilled style={{ color: '#1890ff', fontSize: '24px' }} />;
        } else if (type === 'error') {
          return <CloseCircleFilled style={{ color: '#f53f3f', fontSize: '24px' }} />;
        } else if (type === 'normal') {
          return <></>;
        } else if (type === 'success') {
          return <CheckCircleFilled style={{ color: '#19b42a', fontSize: '24px' }} />;
        } else if (type === 'warning') {
          return <ExclamationCircleFilled style={{ color: '#fa7d00', fontSize: '24px' }} />;
        } else if (type === 'loading') {
          return <LoadingOutlined style={{ color: '#1890ff', fontSize: '24px' }} />;
        }
      }, [type]);
    
      const messageXtransform = useMemo(() => {
        //提示框水平位置,居左/居右
        if (position?.includes('Left')) {
          return {
            left: '20px',
          };
        } else {
          return {
            right: '20px',
          };
        }
      }, [position]);
      const closeMessage = () => {
        //close按钮关闭
        remove(messageBoxId as string, position as string, () => {
          doneCallback && doneCallback(1);
        });
      };
      const enter = () => {
        //确认关闭
        remove(messageBoxId as string, position as string, () => {
          doneCallback && doneCallback(2);
        });
      };
      const exit = () => {
        //取消关闭
        remove(messageBoxId as string, position as string, () => {
          doneCallback && doneCallback(3);
        });
      };
    
      return (
        <div
          className="notifica-container"
          style={{ opacity: opac, ...messageXtransform, ...style }}
          ref={messageDom}
        >
          <div className="title">
            <div className="title-left">
              {messageIcon}
              <span className="title-content">{title}</span>
            </div>
            {clearable && <CloseOutlined className="close-icon" onClick={closeMessage} />}
          </div>
          <div className="notification-content">{content}</div>
          {showFooter && (
            <div className="notification-footer">
              <div></div>
              <div>
                <Button type="text" height={30} handleClick={enter}>
                  {(footerBtnVal as footerBtnVal).exit}
                </Button>
                <Button type="primary" height={30} style={{ marginLeft: '15px' }} handleClick={exit}>
                  {(footerBtnVal as footerBtnVal).enter}
                </Button>
              </div>
            </div>
          )}
        </div>
      );
    };
    
    Notification.info = (props: string | NotificationProps<string>) => {
      return addInstance('info', props);
    };
    Notification.success = (props: string | NotificationProps<string>) => {
      return addInstance('success', props);
    };
    Notification.error = (props: string | NotificationProps<string>) => {
      return addInstance('error', props);
    };
    Notification.normal = (props: string | NotificationProps<string>) => {
      return addInstance('normal', props);
    };
    Notification.warning = (props: string | NotificationProps<string>) => {
      return addInstance('warning', props);
    };
    Notification.loading = (props: string | NotificationProps<string>) => {
      return addInstance('loading', props);
    };
    
    export default Notification;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299

    组件测试

    组件jest测试代码如下:

    import React from 'react';
    import Notification from '../../Notification/index';
    import Enzyme from '../setup';
    import mountTest from '../mountTest';
    
    const { mount } = Enzyme;
    
    describe('Notification', () => {
      beforeEach(() => {
        jest.useFakeTimers();
      });
    
      afterEach(() => {
        jest.runAllTimers();
      });
    
      it('test base Notification show correctly', () => {
        //基础测试
        Notification.info({
          title: 'Notification',
          content: 'test',
          duration: 3000,
        });
        expect(document.querySelectorAll('.notification-container')).toHaveLength(1);
        expect(document.querySelectorAll('.notifica-container .title-content')[0].innerHTML).toBe(
          'Notification',
        );
        expect(
          document.querySelectorAll('.notifica-container .notification-content')[0].innerHTML,
        ).toBe('test');
      });
    
      it('test click five nums Notification show num correctly', () => {
        //测试多次渲染
        for (let i = 0; i < 5; i++) {
          Notification.info({
            title: 'Notification',
            content: 'test',
            duration: 3000,
          });
        }
        expect(document.querySelectorAll('.notification-container')[0].childNodes.length).toBe(5);
      });
    
      it('test four transform Notification show correctly', async () => {
        //测试不同方向
        const transforms = ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'];
        for (let i = 0; i < transforms.length; i++) {
          const transform = transforms[i] as 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
          Notification.info({
            title: 'Notification',
            content: 'test',
            duration: 3000,
            position: transform,
          });
          setTimeout(() => {
            const dom = document.querySelectorAll('.notifica-container')[i];
            switch (i) {
              case 0:
                expect(
                  dom.getAttribute('style')?.includes('top') &&
                    dom.getAttribute('style')?.includes('left'),
                ).toBe(true);
                break;
              case 1:
                expect(
                  dom.getAttribute('style')?.includes('top') &&
                    dom.getAttribute('style')?.includes('right'),
                ).toBe(true);
                break;
              case 2:
                expect(
                  dom.getAttribute('style')?.includes('bottom') &&
                    dom.getAttribute('style')?.includes('left'),
                ).toBe(true);
                break;
              case 3:
                expect(
                  dom.getAttribute('style')?.includes('bottom') &&
                    dom.getAttribute('style')?.includes('right'),
                ).toBe(true);
                break;
            }
          }, 500);
        }
      });
    
      it('test footer Notification show correctly', () => {
        //测试自定义按钮
        const mockFn = jest.fn();
        Notification.info({
          title: 'Notification',
          content: 'test',
          duration: 10000,
          clearable: true,
          showFooter: true,
          footerBtnVal: {
            enter: '确认',
            exit: '取消',
          },
          doneCallback: mockFn,
        });
        expect(document.querySelector('.notifica-container .title')?.childNodes.length).toBe(2);
        expect(document.querySelector('.notifica-container .notification-footer')).toBeDefined();
        expect(
          document.querySelector('.notifica-container .notification-footer .text')?.innerHTML,
        ).toBe('取消');
        expect(
          document.querySelector('.notifica-container .notification-footer .primary')?.innerHTML,
        ).toBe('确认');
      });
    
      it('test setting style Notification correctly', () => {
        //测试自定义样式
        Notification.info({
          title: 'Notification',
          content: 'test',
          duration: 3000,
          style: { width: '500px', fontSize: '15px' },
        });
        expect(
          document
            .querySelector('.notifica-container')
            ?.getAttribute('style')
            ?.includes('width: 500px, font-size: 15px'),
        );
      });
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129

    主要测试了文档中所有的功能,文档地址在下面~~

    组件库地址

    React-View-UI组件库线上链接:http://react-view-ui.com:92/#
    github:https://github.com/fengxinhhh/React-View-UI-fs
    npm:https://www.npmjs.com/package/react-view-ui

    开源不易,欢迎学习和体验,喜欢请多多支持,有问题请留言。

  • 相关阅读:
    【python】python+numpy模块读、写raw图并使用opencv显示图片
    肖sir___面试就业课__非技术面试题
    【2023年中国研究生数学建模竞赛华为杯】E题 出血性脑卒中临床智能诊疗建模 问题分析、数学模型及代码实现
    TCP 协议的相关(部分)特性
    尚硅谷_vue
    如何体验最新GPT-4o模型?
    Linux下怎样使用core文件查看异常崩溃的程序问题
    OVN 流表基础 -- 基于 kubeOVN (一)
    ssh密钥登录服务器
    [C国演义] 第二十章
  • 原文地址:https://blog.csdn.net/m0_46995864/article/details/125611883