浏览代码

test: add cypress test to tabs, upload, navigation, etc (#912)

YannLynn 3 年之前
父节点
当前提交
a46adc8d42

+ 16 - 0
cypress/integration/navigation.spec.js

@@ -0,0 +1,16 @@
+describe('navigation', () => {
+    it('danamic change', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=navigation--items-change-demo&viewMode=story');
+
+        cy.get('span').contains('用户管理').should('exist');
+        cy.get('button').contains('change items').click();
+        cy.get('span').contains('用户管理').should('not.exist');
+    });
+
+    it('auto open', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=navigation--auto-open&viewMode=story');
+
+        cy.get('span').contains('人员管理').should('exist');
+    });
+
+});

+ 36 - 1
cypress/integration/table.spec.js

@@ -72,5 +72,40 @@ describe('table', () => {
         cy.visit('http://localhost:6006/iframe.html?id=table--fixed-on-header-row&args=&viewMode=story');
         cy.visit('http://localhost:6006/iframe.html?id=table--fixed-on-header-row&args=&viewMode=story');
         cy.contains('标题').click();
         cy.contains('标题').click();
         cy.contains('header click').should('be.visible');
         cy.contains('header click').should('be.visible');
-    })
+    });
+
+    it('infinite scroll', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=table--infinite-scroll-demo&args=&viewMode=story');
+        // fixed the viewport
+        cy.viewport(1000, 660);
+
+        // the virtualied table should not load all table item
+        cy.get('div[role="row"]').should('have.length.below', 30);
+
+        // test the scroll in virtualized table 
+        cy.get('.semi-table-body').scrollTo('bottom');
+        // Wait for the scroll result to take effect
+        cy.wait(500);
+        cy.get('div[role="gridcell"]').contains('Edward King 14');
+        cy.get('.semi-table-body').scrollTo('bottom');
+        cy.wait(500);
+        cy.get('div[role="row"]').contains('Edward King 34');
+        cy.get('.semi-table-body').scrollTo('top');
+        cy.wait(500);
+        cy.get('div[role="row"]').contains('Edward King 0');
+    });
+
+    it.skip('scrollToFirstRowOnChange', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=table--virtualized&args=&viewMode=story');
+        cy.get('.semi-table-body').scrollTo(0, 150);
+        cy.get('div[role="button"]').click();
+        cy.get('div[role="row"]').contains('Edward King 9983');
+    });
+
+    it('resize', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=table--dynamic-table&args=&viewMode=story');
+        cy.viewport(1000, 660);
+        cy.get('.semi-switch').eq(0).click();
+        cy.get('.semi-table-body').should('have.css', "max-height", "300px");
+    });
 });
 });

+ 46 - 0
cypress/integration/tabs.spec.js

@@ -36,4 +36,50 @@ describe('tabs', () => {
         cy.get('.semi-tabs-tab').contains('Tab-10').click({ force: true });
         cy.get('.semi-tabs-tab').contains('Tab-10').click({ force: true });
         cy.get('.semi-tabs-content').eq(0).contains('Content of card tab 10');
         cy.get('.semi-tabs-content').eq(0).contains('Content of card tab 10');
     });
     });
+
+    it('keyboard test when the tabs is horizontal', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tabs--disabled-tab&args=&viewMode=story');
+        cy.get('[id=semiTab2]').click();
+        cy.get('[id=semiTab2]').should('be.focused');
+
+        cy.get('[id=semiTab2]').type('{rightArrow}');
+        cy.get('[id=semiTab5]').should('be.focused');
+
+        cy.get('[id=semiTab5]').type('{home}');
+        cy.get('[id=semiTab1]').should('be.focused');
+
+        cy.get('[id=semiTab1]').type('{leftArrow}');
+        cy.get('[id=semiTab5]').should('be.focused');
+
+        cy.get('[id=semiTab5]').type('{rightArrow}');
+        cy.get('[id=semiTab1]').should('be.focused');
+
+        cy.get('[id=semiTab1]').type('{end}');
+        cy.get('[id=semiTab5]').should('be.focused');
+
+        cy.get('[id=semiTab5]').type('{enter}');
+        cy.get('[id=semiTab5]').tab();
+        cy.get("[id=semiTabPanel5]").should('be.focused');
+    });
+
+    it('keyboard test when the tabs is vertical', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tabs--vertical-tabs&args=&viewMode=story');
+        cy.get('[id=semiTab1]').click();
+        cy.get('[id=semiTab1]').should('be.focused');
+
+        cy.get('[id=semiTab1]').type('{downArrow}');
+        cy.get('[id=semiTab2]').should('be.focused');
+
+        cy.get('[id=semiTab2]').type('{upArrow}');
+        cy.get('[id=semiTab1]').should('be.focused');
+    });
+
+    it('keyboard test when the tabs is closable', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tabs--tab-closable&args=&viewMode=story');
+        cy.get('[id=semiTab1]').click();
+        cy.get('[id=semiTab1]').should('be.focused');
+
+        cy.get('[id=semiTab1]').type('{backspace}');
+        cy.get('[id=semiTab1]').should('not.exist');
+    });
 });
 });

+ 33 - 0
cypress/integration/tag.spec.js

@@ -0,0 +1,33 @@
+describe('tag', () => {
+    it('tag group with max count', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tag--tag-group&args=&viewMode=story');
+
+        // focus and esc
+        cy.get('.semi-tag-content').eq(3).click();
+        cy.get('.semi-portal');
+    });
+
+    it('keyboard test', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tag--tag-avatar&args=&viewMode=story', {
+            onBeforeLoad(win) {
+                cy.stub(win.console, 'log').as('consoleLog'); // 测试时用到控制台的前置步骤
+            },
+        });
+
+        // focus + esc + enter
+        cy.get('body').tab(); // 按下tab键
+        cy.get('.semi-tag').eq(0).should('be.focused'); // 第一个tag应该被focus
+
+        cy.get('.semi-tag').eq(0).type('{enter}'); // 在第一个tag上按enter
+        cy.get('@consoleLog').should('be.calledWith', '如果能重来,我要做李白'); // 控制台应该打印“如果能重来,我要做李白”
+        cy.get('.semi-tag').eq(0).type('{esc}'); // 在第一个tag上按ESC
+        cy.get('.semi-tag').eq(0).should('not.be.focused'); // 第一个tag应该没有被focus
+
+        // backspace
+        cy.get('.semi-tag').eq(0).tab();
+        cy.get('.semi-tag').eq(4).should('be.focused');
+        cy.get('.semi-tag').eq(4).type('{backspace}'); // 在第5个tag上按backspace
+        cy.get('.semi-tag').eq(4).get('.semi-tag-invisible'); // 第5个tag上应该被隐藏
+
+    });
+});

+ 49 - 0
cypress/integration/upload.spec.js

@@ -18,4 +18,53 @@ describe('upload', () => {
         cy.get('.semi-upload-file-card-info-main-text').contains('README.md');
         cy.get('.semi-upload-file-card-info-main-text').contains('README.md');
     });
     });
 
 
+    it('replace', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=upload--test-replace-func&args=&viewMode=story', {
+            onBeforeLoad(win) {
+                cy.stub(win.console, 'log').as('consoleLog'); // 测试时用到控制台的前置步骤
+            },
+        });
+
+        // not an acceptable type
+        cy.get('input[type=file]').eq(3).selectFile('README.md', { force: true });
+
+        // not an acceptable size
+        cy.get('input[type=file]').eq(5).selectFile('README.md', { force: true });
+        cy.get('div').not('.semi-upload-file-list');
+
+        cy.get('input[type=file]').eq(1).selectFile('README.md', { force: true });
+        
+        // assert
+        cy.get('.semi-upload').eq(1).get('div').not('.semi-upload-file-list');
+        cy.get('@consoleLog').should('be.calledWith', 'onSizeError');
+        cy.get('.semi-upload-file-card-info-main-text').contains('README.md');
+    });
+
+    it('ref method insert', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=upload--insert&args=&viewMode=story',  {
+            onBeforeLoad(win) {
+                cy.stub(win.console, 'log').as('consoleLog'); // 测试时用到控制台的前置步骤
+            },
+        });
+
+        cy.get('input[type=file]').eq(1).selectFile('README.md', { force: true });
+        cy.get('input[type=file]').eq(1).selectFile('README.md', { force: true });
+
+
+        // test file number limit
+        cy.get('span').contains('插入首项上传1').click();
+        cy.get('span').contains('插入首项上传1').click();
+        cy.get('.semi-upload-file-list').get('div[role="listitem"]').should('have.length', 1);
+
+        // test file number limit
+        cy.get('span').contains('插入首项上传2').click();
+        cy.get('span').contains('插入首项上传2').click();
+        cy.get('.semi-upload-file-list').get('div[role="listitem"]').should('have.length', 3);
+
+        // test size limit
+        cy.get('span').contains('插入首项上传3').click();
+        cy.get('@consoleLog').should('be.calledWith', 'onSizeError');
+        
+    });
+
 });
 });

+ 2 - 0
packages/semi-foundation/navigation/foundation.ts

@@ -53,6 +53,7 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
         super({ ...adapter });
         super({ ...adapter });
     }
     }
 
 
+    /* istanbul ignore next */
     static getZeroParentKeys(itemKeysMap = {}, ...itemKeys: (string | number)[]) {
     static getZeroParentKeys(itemKeysMap = {}, ...itemKeys: (string | number)[]) {
         const willAddKeys = [];
         const willAddKeys = [];
         if (itemKeys.length) {
         if (itemKeys.length) {
@@ -240,6 +241,7 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
         this._adapter.notifySelect(data);
         this._adapter.notifySelect(data);
     }
     }
 
 
+    /* istanbul ignore next */
     judgeIfOpen(openKeys: (string | number)[], items: NavItemType[]): boolean {
     judgeIfOpen(openKeys: (string | number)[], items: NavItemType[]): boolean {
         let shouldBeOpen = false;
         let shouldBeOpen = false;
 
 

+ 1 - 1
packages/semi-foundation/tabs/foundation.ts

@@ -80,7 +80,7 @@ class TabsFoundation<P = Record<string, any>, S = Record<string, any>> extends B
 
 
     handleKeyDown = (event: any, itemKey: string, closable: boolean) => {
     handleKeyDown = (event: any, itemKey: string, closable: boolean) => {
         const tabs = [...event.target.parentNode.childNodes].filter(item => {
         const tabs = [...event.target.parentNode.childNodes].filter(item => {
-            return get(item, 'attributes.data-tabkey.value', '').includes('semiTab') && item.ariaDisabled!=="true";
+            return get(item, 'attributes.data-tabkey.value', '').includes('semiTab') && get(item, 'attributes.aria-disabled.value', '') !== "true";
         });
         });
 
 
         switch (event.key) {
         switch (event.key) {

+ 2 - 0
packages/semi-foundation/upload/foundation.ts

@@ -401,6 +401,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
         });
         });
     }
     }
 
 
+    /* istanbul ignore next */
     manualUpload(): void {
     manualUpload(): void {
         // find the list of files that have not been uploaded
         // find the list of files that have not been uploaded
         const waitToUploadFileList = this.getState('fileList').filter((item: BaseFileItem) => item.status === FILE_STATUS_WAIT_UPLOAD);
         const waitToUploadFileList = this.getState('fileList').filter((item: BaseFileItem) => item.status === FILE_STATUS_WAIT_UPLOAD);
@@ -739,6 +740,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
         return /(webp|svg|png|gif|jpg|jpeg|bmp|dpg)$/i.test(file.type);
         return /(webp|svg|png|gif|jpg|jpeg|bmp|dpg)$/i.test(file.type);
     }
     }
 
 
+    /* istanbul ignore next */
     isMultiple(): boolean {
     isMultiple(): boolean {
         return Boolean(this.getProp('multiple'));
         return Boolean(this.getProp('multiple'));
     }
     }

+ 36 - 0
packages/semi-ui/tabs/_story/tabs.stories.js

@@ -867,3 +867,39 @@ export const TabListChange = () => <TabListChangeDemo />;
 TabListChange.story = {
 TabListChange.story = {
   name: 'tablist change',
   name: 'tablist change',
 };
 };
+
+
+class TabClosableDemo extends React.Component {
+  constructor(props){
+    super(props);
+      this.state = {
+          tabList: [
+              {tab: '文档', itemKey:'1', text:'文档', closable:true},
+              {tab: '快速起步', itemKey:'2', text:'快速起步', closable:true},
+              {tab: '帮助', itemKey:'3', text:'帮助'},
+          ]
+      }
+  }
+  close(key){
+    const newTabList = [...this.state.tabList];
+    const closeIndex = newTabList.findIndex(t=>t.itemKey===key);
+    newTabList.splice(closeIndex, 1);
+    this.setState({tabList:newTabList});
+  }
+
+  render() {
+    return (
+      <Tabs type="card" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
+          {
+            this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>{t.text}</TabPane>)
+          }
+      </Tabs>
+    );
+  }
+}
+
+export const TabClosable = () => <TabClosableDemo />;
+
+TabClosable.story = {
+  name: 'tab closable',
+};

+ 1 - 1
packages/semi-ui/tag/_story/tag.stories.js

@@ -155,7 +155,7 @@ export const TagAvatar = () => {
     'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg';
     'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg';
   return (
   return (
     <div style={{ margin: 10 }}>
     <div style={{ margin: 10 }}>
-      <Tag avatarSrc={avatarSrc} avatarShape={'square'}>
+      <Tag avatarSrc={avatarSrc} avatarShape={'square'} onClick={()=>{console.log('如果能重来,我要做李白')}}>
         李白
         李白
       </Tag>
       </Tag>
       <br />
       <br />

+ 152 - 0
packages/semi-ui/upload/_story/upload.stories.js

@@ -957,3 +957,155 @@ export const CustomListOperation = () => {
 CustomListOperation.story = {
 CustomListOperation.story = {
   name: 'custom list operation',
   name: 'custom list operation',
 }
 }
+
+
+export const TestReplaceFunc = () => (
+  <>
+    <Upload
+      {...commonProps}
+      action={action}
+      accept=".md,image/*,video/*"
+      maxSize={mb1}
+      minSize={0}
+      transformFile={(fileInstance)=>{return fileInstance;}}
+    >
+      <Button icon={<IconUpload />} theme="light">
+        点击上传(最小0kB,最大1MB)
+      </Button>
+    </Upload>
+    <Upload
+      {...commonProps}
+      action={action}
+      accept="image/*"
+      maxSize={mb1}
+      minSize={0}
+      transformFile={(fileInstance)=>{return fileInstance;}}
+    >
+      <Button icon={<IconUpload />} theme="light">
+        只接受image点击上传(最小0kB,最大1MB)
+      </Button>
+    </Upload>
+    <Upload
+      {...commonProps}
+      action={action}
+      accept=".md,image/*,video/*"
+      maxSize={mb1}
+      minSize={kb2}
+      transformFile={(fileInstance)=>{return fileInstance;}}
+    >
+      <Button icon={<IconUpload />} theme="light">
+        点击上传(最小200kB,最大1MB)
+      </Button>
+    </Upload>
+  </>
+);
+
+TestReplaceFunc.story = {
+  name: 'test replace func',
+};
+
+
+class InsertUpload extends React.Component {
+   constructor() {
+        super();
+        this.onFileChange = this.onFileChange.bind(this);
+        this.insert1 = this.insert1.bind(this);
+        this.insert2 = this.insert2.bind(this);
+        this.insert3 = this.insert3.bind(this);
+        this.uploadRef1 = React.createRef();
+        this.uploadRef2 = React.createRef();
+        this.uploadRef3 = React.createRef();
+        this.file = null;
+    }
+
+    onFileChange(file) {
+      delete file[0].uid;
+      this.file = file;
+    }
+
+    insert1() {
+      // test file number limit
+      this.uploadRef1.current.insert(this.file, 0);
+    }
+
+    insert2() {
+      this.uploadRef2.current.insert(this.file, 0);
+    }
+
+    insert3() {
+      // test size limit
+      this.uploadRef3.current.insert(this.file, 0);
+    }
+
+    render() {
+        let action = 'https://run.mocky.io/v3/d6ac5c9e-4d39-4309-a747-7ed3b5694859';
+        return (
+            <div>
+                <Upload
+                    action={action}
+                    ref={this.uploadRef1}
+                    accept=".md,image/*,video/*"
+                    onSuccess={(...v) => console.log(...v)}
+                    onError={(...v) => console.log(...v)}
+                    onFileChange={this.onFileChange}
+                    maxSize={mb1}
+                    minSize={0}
+                    limit={1}
+                    transformFile={(fileInstance)=>{return fileInstance;}}
+                >
+                    <Button icon={<IconPlus />} theme="light" style={{ marginRight: 8 }}>
+                        选择文件 limit 1
+                    </Button>
+                </Upload>
+                <Upload
+                    action={action}
+                    ref={this.uploadRef2}
+                    accept=".md,image/*,video/*"
+                    onSuccess={(...v) => console.log(...v)}
+                    onError={(...v) => console.log(...v)}
+                    onFileChange={this.onFileChange}
+                    maxSize={mb1}
+                    minSize={0}
+                    limit={2}
+                    transformFile={(fileInstance)=>{return fileInstance;}}
+                >
+                    <Button icon={<IconPlus />} theme="light" style={{ marginRight: 8 }}>
+                        选择文件 limit 2
+                    </Button>
+                </Upload>
+                <Upload
+                    {...commonProps}
+                    action={action}
+                    ref={this.uploadRef3}
+                    accept=".md,image/*,video/*"
+                    onSuccess={(...v) => console.log(...v)}
+                    onError={(...v) => console.log(...v)}
+                    onFileChange={this.onFileChange}
+                    maxSize={mb1}
+                    minSize={kb2}
+                    limit={1}
+                    transformFile={(fileInstance)=>{return fileInstance;}}
+                >
+                    <Button icon={<IconPlus />} theme="light" style={{ marginRight: 8 }}>
+                        选择文件 size 限制
+                    </Button>
+                </Upload>
+                <Button icon={<IconUpload />} theme="light" onClick={this.insert1}>
+                  插入首项上传1
+                </Button>
+                <Button icon={<IconUpload />} theme="light" onClick={this.insert2}>
+                  插入首项上传2
+                </Button>
+                <Button icon={<IconUpload />} theme="light" onClick={this.insert3}>
+                  插入首项上传3
+                </Button>
+            </div>
+        );
+    }
+}
+
+export const Insert = () => <InsertUpload></InsertUpload>;
+
+Insert.story = {
+  name: 'insert',
+};

+ 85 - 0
yarn.lock

@@ -1460,6 +1460,16 @@
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
   integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
   integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
 
 
+"@douyinfe/[email protected]":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.12.0.tgz#6674f30e22fab6559f573d822c441bc99753e2a1"
+  integrity sha512-/BqguKql4bjfo4uhy/lfR/0wojBG30N13g3S/hEwETC2iFVwQab3Io5/uuzJpa+ARUcrbKJYbGesPDwM0lyR/g==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.15.4"
+    "@douyinfe/semi-animation" "2.12.0"
+    "@douyinfe/semi-animation-styled" "2.12.0"
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.9.1.tgz#f2e4c6ef7899729ee6145edf0579598ba195bfdd"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.9.1.tgz#f2e4c6ef7899729ee6145edf0579598ba195bfdd"
@@ -1470,6 +1480,13 @@
     "@douyinfe/semi-animation-styled" "2.9.1"
     "@douyinfe/semi-animation-styled" "2.9.1"
     classnames "^2.2.6"
     classnames "^2.2.6"
 
 
+"@douyinfe/[email protected]":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.12.0.tgz#27df4be8f23553791f5954d7f9529f26a7d76211"
+  integrity sha512-VdMh6xKiqFBi9ATyQhdyDSXK5QLvJBBUjIqOuTjgxSVmx884GChnK2nzHSIhGrlFDpM8mJd1gQ2uDQ7jdAFJgg==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.15.4"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.9.1.tgz#0a4a3c521626118b209604b2d6447fbcaa4839a4"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.9.1.tgz#0a4a3c521626118b209604b2d6447fbcaa4839a4"
@@ -1493,6 +1510,21 @@
     "@babel/runtime-corejs3" "^7.15.4"
     "@babel/runtime-corejs3" "^7.15.4"
     bezier-easing "^2.1.0"
     bezier-easing "^2.1.0"
 
 
+"@douyinfe/[email protected]":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.12.0.tgz#76b00351b8c0438ae974c3a218c560766e1db2e4"
+  integrity sha512-HuH9Qafm+VzZATguaT+r3nMSpfw9vL57erXu7jcDr7YqQkz3VgDX7c6/qHwIkWdeCuiLAyjNb886K+AmFuY8hg==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.15.4"
+    "@douyinfe/semi-animation" "2.12.0"
+    async-validator "^3.5.0"
+    classnames "^2.2.6"
+    date-fns "^2.9.0"
+    date-fns-tz "^1.0.10"
+    lodash "^4.17.21"
+    memoize-one "^5.2.1"
+    scroll-into-view-if-needed "^2.2.24"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.9.1.tgz#1300bb97d6ceb92274ca4c9e6c66c5c16dc284ea"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.9.1.tgz#1300bb97d6ceb92274ca4c9e6c66c5c16dc284ea"
@@ -1508,6 +1540,14 @@
     memoize-one "^5.2.1"
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
     scroll-into-view-if-needed "^2.2.24"
 
 
+"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.12.0.tgz#9af44b9c9366a9034d6215364efca3649d9dc779"
+  integrity sha512-PQEFzOtTyft+mWiiXxRg/p5v5VtDfS0fXFmq7r+uZMdEP5PDR/CicMnRp6iNjBhtBveT92TkGCnK40YAptImww==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.15.4"
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.9.1.tgz#7a04e1a77070220b04f63e6f65aac30155ed8ddd"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.9.1.tgz#7a04e1a77070220b04f63e6f65aac30155ed8ddd"
@@ -1516,6 +1556,13 @@
     "@babel/runtime-corejs3" "^7.15.4"
     "@babel/runtime-corejs3" "^7.15.4"
     classnames "^2.2.6"
     classnames "^2.2.6"
 
 
+"@douyinfe/[email protected]":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.12.0.tgz#be55cdf71c74fd23496fec0cb03a01f2c7f6c445"
+  integrity sha512-JHTxEGp8LZ1bLyi2yosD0fPuJYy5y/yJUByNTmRVTXIwIPemqOGF0n9ryySbV4g2fLjan1hUuJ0mSgsppAq5pQ==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.15.4"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.9.1.tgz#1a448d1854ee1beeba57ea612da052b549ea105f"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.9.1.tgz#1a448d1854ee1beeba57ea612da052b549ea105f"
@@ -1579,6 +1626,13 @@
     monaco-themes "^0.3.3"
     monaco-themes "^0.3.3"
     react-live "^2.2.2"
     react-live "^2.2.2"
 
 
+"@douyinfe/[email protected]":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.12.0.tgz#15f8905f39e2dd016e025bb728d6e604dbd1b471"
+  integrity sha512-R80krfri+X9tlopaOthInAAJ73uGrRZsRBEmrpIkXd2+l26Xv3Y3J+MlyeaynbWSVlqKB1Ol/GHc7mcS6R3wdg==
+  dependencies:
+    glob "^7.1.6"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.9.1.tgz#734113e9783ca58b69afe1769005e7e57e5a4da7"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.9.1.tgz#734113e9783ca58b69afe1769005e7e57e5a4da7"
@@ -1586,6 +1640,32 @@
   dependencies:
   dependencies:
     glob "^7.1.6"
     glob "^7.1.6"
 
 
+"@douyinfe/semi-ui@^2.0.0":
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.12.0.tgz#94fd7f72c4804210f016955228f0d739e1c4adb5"
+  integrity sha512-3JmSgQQ41WSYcx0ow27BAEsF5jQ2t3IcCJeAs3gpe4P0ioUT1BMH9XzMkKxohcTuFbJ9Sh4+H1cZYCRjltNhqQ==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.15.4"
+    "@douyinfe/semi-animation" "2.12.0"
+    "@douyinfe/semi-animation-react" "2.12.0"
+    "@douyinfe/semi-foundation" "2.12.0"
+    "@douyinfe/semi-icons" "2.12.0"
+    "@douyinfe/semi-illustrations" "2.12.0"
+    "@douyinfe/semi-theme-default" "2.12.0"
+    "@types/react-window" "^1.8.2"
+    async-validator "^3.5.0"
+    classnames "^2.2.6"
+    copy-text-to-clipboard "^2.1.1"
+    date-fns "^2.9.0"
+    date-fns-tz "^1.0.10"
+    lodash "^4.17.21"
+    react-resizable "^1.8.0"
+    react-sortable-hoc "^2.0.0"
+    react-window "^1.8.2"
+    resize-observer-polyfill "^1.5.1"
+    scroll-into-view-if-needed "^2.2.24"
+    utility-types "^3.10.0"
+
 "@douyinfe/semi-ui@latest":
 "@douyinfe/semi-ui@latest":
   version "2.9.1"
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.9.1.tgz#505d4783ea1fa73d307b75f62091030f1fee9332"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.9.1.tgz#505d4783ea1fa73d307b75f62091030f1fee9332"
@@ -10935,6 +11015,11 @@ eslint-plugin-react@^7.20.6, eslint-plugin-react@^7.24.0:
     semver "^6.3.0"
     semver "^6.3.0"
     string.prototype.matchall "^4.0.6"
     string.prototype.matchall "^4.0.6"
 
 
+eslint-plugin-semi-design@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-semi-design/-/eslint-plugin-semi-design-0.0.1.tgz#e918a43e5b633a4352e173086c150e5e59b800e7"
+  integrity sha512-Gtd4rNrb/hPmn+/SimRTjOZ3d3NoR2SzW8t1uS6/uHjf7pcXOISUqY4709qOwF0EeU7FJHRIJyB4mFOUUs3/jw==
+
 eslint-rule-composer@^0.3.0:
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
   resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"