Browse Source

Merge pull request #2104 from changlin2569/feat/add-lazyRender-prop-for-Collapsible

feat: add lazyRender prop for Collapsible
代强 1 year ago
parent
commit
00f5304dda

+ 1 - 0
content/show/collapsible/index-en-US.md

@@ -184,6 +184,7 @@ import { Collapsible, Button } from '@douyinfe/semi-ui';
 | duration | Time of animation execution                                                                                | number | 250 | - |
 | isOpen | Toggle whether to expand the content area                                                                  | boolean | `false` | - |
 | keepDOM | Whether to keep the hidden panel in DOM tree, destroyed by default                                         | boolean | `false` | 0.25.0 |
+| lazyRender | Used with keepDOM, when true, the component will not be rendered when mounting                                         | boolean | `true` | 2.24 |
 | motion | Toggle whether to turn on animation                                                                        | Motion | `true` | - |
 | onMotionEnd | Animation end callback                                                                                     | () => void | - | - |
 | reCalcKey | When reCalcKey changes, the height of children will be reset. Used for optimize dynamic content rendering. | number \| string | - | 1.5.0 |

+ 1 - 0
content/show/collapsible/index.md

@@ -216,6 +216,7 @@ import { Collapsible, Button } from '@douyinfe/semi-ui';
 | fade | 是否开启淡入淡出 | boolean | false | 2.21.0 |
 | isOpen | 是否展开内容区域 | boolean | `false` | - |
 | keepDOM | 是否保留隐藏的面板 DOM 树,默认销毁 | boolean | `false` | 0.25.0 |
+| lazyRender | 配合 keepDOM 使用,为 true 时挂载时不会渲染组件 | boolean | `true` | 2.24 |
 | motion | 是否开启动画 | boolean | `true` | - |
 | onMotionEnd | 动画结束的回调 | () => void | - | - |
 | reCalcKey | 当 reCalcKey 改变时,将重新计算子节点的高度,用于优化动态渲染时的计算 | number \| string | - | 1.5.0 |

+ 44 - 0
packages/semi-ui/collapsible/__test__/collapsible.test.js

@@ -0,0 +1,44 @@
+import { clear } from 'jest-date-mock';
+import { Collapsible } from '../../index';
+
+function getCollapsible(props, children) {
+    return (<Collapsible {...(props || {})}>
+        {children ? children : <div className='collapsible-test-content'>hello</div>}
+    </Collapsible>);
+}
+
+describe('collapsible', () => {
+    beforeEach(() => {
+        // Avoid `attachTo: document.body` Warning
+        const div = document.createElement('div');
+        div.setAttribute('id', 'container');
+        document.body.appendChild(div);
+        clear();
+    });
+
+    it('keepDOM = true', async () => {
+        let component = getCollapsible({ keepDOM: true, motion: false });
+        let collapsible = mount(component, { attachTo: document.getElementById('container') })
+        expect(collapsible.exists(`.collapsible-test-content`)).toEqual(false);
+        // set true
+        collapsible.setProps({ isOpen: true });
+        collapsible.update(); // 必须调用一次update
+        expect(collapsible.exists(`.collapsible-test-content`)).toEqual(true);
+        collapsible.setProps({ isOpen: false });
+        collapsible.update(); // 必须调用一次update
+        expect(collapsible.exists(`.collapsible-test-content`)).toEqual(false);
+    });
+
+    it('keepDOM + lazyRender = false', () => {
+        let component = getCollapsible({ keepDOM: true, lazyRender: false });
+        let collapsible = mount(component);
+        expect(collapsible.exists(`.collapsible-test-content`)).toEqual(true);
+        // set true
+        collapsible.setProps({ isOpen: true });
+        collapsible.update(); // 必须调用一次update
+        expect(collapsible.exists(`.collapsible-test-content`)).toEqual(true);
+        collapsible.setProps({ isOpen: false });
+        collapsible.update(); // 必须调用一次update
+        expect(collapsible.exists(`.collapsible-test-content`)).toEqual(true);
+    });
+});

+ 3 - 1
packages/semi-ui/collapsible/_story/collapsible.stories.jsx

@@ -81,7 +81,7 @@ class DemoDOM extends React.Component {
     return (
       <div>
         <Button onClick={() => this.toggle()}>显示更多</Button>
-        <Collapsible keepDOM isOpen={isOpen}>
+        <Collapsible keepDOM lazyRender={!!this.props.lazyRender} isOpen={isOpen}>
           {collapsed}
         </Collapsible>
       </div>
@@ -91,6 +91,8 @@ class DemoDOM extends React.Component {
 
 export const KeepDom = () => <DemoDOM />;
 
+export const LazyRender = () => <DemoDOM lazyRender={true} />;
+
 class DefaultOpen extends React.Component {
   state = {
     isOpen: true,

+ 7 - 3
packages/semi-ui/collapsible/index.tsx

@@ -19,6 +19,7 @@ export interface CollapsibleProps extends CollapsibleFoundationProps {
     isOpen?: boolean;
     duration?: number;
     keepDOM?: boolean;
+    lazyRender?: boolean;
     className?: string;
     style?: React.CSSProperties;
     collapseHeight?: number;
@@ -42,6 +43,7 @@ class Collapsible extends BaseComponent<CollapsibleProps, CollapsibleState> {
         duration: 250,
         motion: true,
         keepDOM: false,
+        lazyRender: true,
         collapseHeight: 0,
         fade: false
     }) 
@@ -157,9 +159,8 @@ class Collapsible extends BaseComponent<CollapsibleProps, CollapsibleState> {
     isChildrenInRenderTree = () => {
         if (this.domRef.current) {
             return this.domRef.current.offsetHeight > 0;
-        } else {
-            return false;
         }
+        return false;
     }
 
     render() {
@@ -173,6 +174,9 @@ class Collapsible extends BaseComponent<CollapsibleProps, CollapsibleState> {
         const wrapperCls = cls(`${cssClasses.PREFIX}-wrapper`, {
             [`${cssClasses.PREFIX}-transition`]: this.props.motion && this.state.isTransitioning
         }, this.props.className);
+
+        const shouldRender = (this.props.keepDOM && !this.props.lazyRender) || this.props.collapseHeight !== 0 || this.state.visible || this.props.isOpen;
+
         return (
             <div
                 className={wrapperCls}
@@ -193,7 +197,7 @@ class Collapsible extends BaseComponent<CollapsibleProps, CollapsibleState> {
                     id={this.props.id}
                 >
                     {
-                        (this.props.keepDOM || this.props.collapseHeight !== 0 || this.state.visible || this.props.isOpen) && this.props.children
+                        shouldRender && this.props.children
                     }
                 </div>
             </div>