From 0d2580e76ef34b9d862bc2e67e5db8888ee30dcd Mon Sep 17 00:00:00 2001 From: Sebastian Frey Date: Mon, 2 Dec 2019 15:58:04 +0100 Subject: [PATCH 1/3] Added secondaryHidden prop --- README.md | 6 ++ .../components/TogglableSidebarLayout.jsx | 77 +++++++++++++------ src/components/Pane.jsx | 7 +- src/components/SplitterLayout.jsx | 16 +++- 4 files changed, 79 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 3131fd9..a73d7d5 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,12 @@ The `SplitterLayout` component supports the following props. If this prop is not defined, `SplitterLayout` tries to split the layout with equal sizes. (Note: equal size may not apply when there are nested layouts.) +* `secondaryHidden: PropTypes.bool` + + Determine whether the secondary pane is visible or not. The default value is `false`. + + Use this prop for hiding the secondary pane in favour of conditional rendering to avoid re-renders. + * `onDragStart: PropTypes.func` Called when dragging is started. diff --git a/example/javascripts/components/TogglableSidebarLayout.jsx b/example/javascripts/components/TogglableSidebarLayout.jsx index 1d12a23..65b9eb2 100644 --- a/example/javascripts/components/TogglableSidebarLayout.jsx +++ b/example/javascripts/components/TogglableSidebarLayout.jsx @@ -6,8 +6,10 @@ export default class TogglableSidebarLayout extends React.Component { constructor(props) { super(props); this.toggleSidebar = this.toggleSidebar.bind(this); + this.toggleUseSecondaryHidden = this.toggleUseSecondaryHidden.bind(this); this.state = { - sidebarVisible: true + sidebarVisible: true, + useSecondaryHidden: false }; } @@ -15,9 +17,17 @@ export default class TogglableSidebarLayout extends React.Component { this.setState(state => ({ sidebarVisible: !state.sidebarVisible })); } + toggleUseSecondaryHidden() { + this.setState(state => ({ useSecondaryHidden: !state.useSecondaryHidden })); + } + render() { return ( - +

1st Pane

This is the 1st pane, and this is the primary pane by default.

@@ -25,29 +35,50 @@ export default class TogglableSidebarLayout extends React.Component { {this.state.sidebarVisible && 'Hide Sidebar'} {!this.state.sidebarVisible && 'Show Sidebar'} -
-            <SplitterLayout primaryIndex={'{0}'}>{'\n'}
-              <div>1st</div>{'\n'}
-            {this.state.sidebarVisible && '  
2nd
\n'} - </SplitterLayout> -
+ + {!this.state.useSecondaryHidden && ( +
+              <SplitterLayout primaryIndex={'{0}'}>{'\n'}
+                <div>1st</div>{'\n'}
+              {this.state.sidebarVisible && '  
2nd
\n'} + </SplitterLayout> +
+ )} + {this.state.useSecondaryHidden && ( +
+              <SplitterLayout primaryIndex={'{0}'} secondaryHidden={`{${!this.state.sidebarVisible}}`}>{'\n'}
+                <div>1st</div>{'\n'}
+              {'  
2nd
\n'} + </SplitterLayout> +
+ )} +
- {this.state.sidebarVisible && - ( -
-

2nd Pane

-

This is the 2nd pane, considered as a sidebar.

-
-                <SplitterLayout primaryIndex={'{0}'}>{'\n'}
-                  <div>1st</div>{'\n'}
-                  <div>2nd</div>{'\n'}
-                </SplitterLayout>
-              
- -
- ) - } +
+

2nd Pane

+

This is the 2nd pane, considered as a sidebar.

+ {!this.state.useSecondaryHidden && ( +
+              <SplitterLayout primaryIndex={'{0}'}>{'\n'}
+                <div>1st</div>{'\n'}
+                <div>2nd</div>{'\n'}
+              </SplitterLayout>
+            
+ )} + {this.state.useSecondaryHidden && ( +
+              <SplitterLayout primaryIndex={'{0}'} secondaryHidden={`{${!this.state.sidebarVisible}}`}>{'\n'}
+                <div>1st</div>{'\n'}
+                <div>2nd</div>{'\n'}
+              </SplitterLayout>
+            
+ )} + +
); } diff --git a/src/components/Pane.jsx b/src/components/Pane.jsx index 57c6dc5..f226b80 100644 --- a/src/components/Pane.jsx +++ b/src/components/Pane.jsx @@ -4,10 +4,13 @@ import PropTypes from 'prop-types'; function Pane(props) { const size = props.size || 0; const unit = props.percentage ? '%' : 'px'; + const hidden = props.hidden || false; let classes = 'layout-pane'; const style = {}; if (!props.primary) { - if (props.vertical) { + if (hidden) { + style.display = 'none'; + } else if (props.vertical) { style.height = `${size}${unit}`; } else { style.width = `${size}${unit}`; @@ -24,6 +27,7 @@ Pane.propTypes = { vertical: PropTypes.bool, primary: PropTypes.bool, size: PropTypes.number, + hidden: PropTypes.bool, percentage: PropTypes.bool, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), @@ -35,6 +39,7 @@ Pane.defaultProps = { vertical: false, primary: false, size: 0, + hidden: false, percentage: false, children: [] }; diff --git a/src/components/SplitterLayout.jsx b/src/components/SplitterLayout.jsx index ef9185d..f28936d 100644 --- a/src/components/SplitterLayout.jsx +++ b/src/components/SplitterLayout.jsx @@ -190,12 +190,20 @@ class SplitterLayout extends React.Component { for (let i = 0; i < children.length; ++i) { let primary = true; let size = null; + let hidden = false; if (children.length > 1 && i !== primaryIndex) { primary = false; size = this.state.secondaryPaneSize; + hidden = this.props.secondaryHidden === true; } wrappedChildren.push( - + ); @@ -204,7 +212,7 @@ class SplitterLayout extends React.Component { return (
{ this.container = c; }}> {wrappedChildren[0]} - {wrappedChildren.length > 1 && + {!this.props.secondaryHidden && wrappedChildren.length > 1 && (
) } - {wrappedChildren.length > 1 && wrappedChildren[1]} + {wrappedChildren[1] || false}
); } @@ -229,6 +237,7 @@ SplitterLayout.propTypes = { primaryMinSize: PropTypes.number, secondaryInitialSize: PropTypes.number, secondaryMinSize: PropTypes.number, + secondaryHidden: PropTypes.bool, onDragStart: PropTypes.func, onDragEnd: PropTypes.func, onSecondaryPaneSizeChange: PropTypes.func, @@ -243,6 +252,7 @@ SplitterLayout.defaultProps = { primaryMinSize: 0, secondaryInitialSize: undefined, secondaryMinSize: 0, + secondaryHidden: false, onDragStart: null, onDragEnd: null, onSecondaryPaneSizeChange: null, From 8d4b59aa8b0af240c8bba19f9a77fb1a85f11cdd Mon Sep 17 00:00:00 2001 From: Sebastian Frey Date: Mon, 2 Dec 2019 16:52:06 +0100 Subject: [PATCH 2/3] Add tests --- test/SplitterLayout.spec.jsx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/SplitterLayout.spec.jsx b/test/SplitterLayout.spec.jsx index 158cf05..312345c 100644 --- a/test/SplitterLayout.spec.jsx +++ b/test/SplitterLayout.spec.jsx @@ -121,6 +121,24 @@ describe('SplitterLayout', () => { expect(output.props.children[2].props.primary).toBe(false); expect(output.props.children[2].props.percentage).toBe(false); }); + + it('should render 2 children when 2 children provided and secondaryHidden is true', () => { + const output = render(2, { secondaryHidden: true }); + expect(output.type).toBe('div'); + expect(output.props.className).toBe('splitter-layout'); + expect(output.props.children.length).toBe(3); + expect(output.props.children[0].type).toBe(Pane); + expect(output.props.children[0].props.vertical).toBe(false); + expect(output.props.children[0].props.primary).toBe(true); + expect(output.props.children[0].props.percentage).toBe(false); + expect(output.props.children[0].props.hidden).toBe(false); + expect(output.props.children[1]).toBe(false); + expect(output.props.children[2].type).toBe(Pane); + expect(output.props.children[2].props.vertical).toBe(false); + expect(output.props.children[2].props.primary).toBe(false); + expect(output.props.children[2].props.percentage).toBe(false); + expect(output.props.children[2].props.hidden).toBe(true); + }); }); describe('sizing', () => { @@ -550,5 +568,10 @@ describe('SplitterLayout', () => { const component = renderIntoDocument(2, { secondaryInitialSize: 20, vertical: true }); expect(component.state.secondaryPaneSize).toBe(20); }); + + it('should set secondary size to zero if secondary is hidden', () => { + const component = renderIntoDocument(2, { secondaryHidden: true }); + expect(component.state.secondaryPaneSize).toBe(-4); + }); }); }); From 6c37a836bc9237c97e9d0aa646c2cee3462333cb Mon Sep 17 00:00:00 2001 From: Sebastian Frey Date: Mon, 2 Dec 2019 16:52:16 +0100 Subject: [PATCH 3/3] Fix example --- .../components/TogglableSidebarLayout.jsx | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/example/javascripts/components/TogglableSidebarLayout.jsx b/example/javascripts/components/TogglableSidebarLayout.jsx index 65b9eb2..967f772 100644 --- a/example/javascripts/components/TogglableSidebarLayout.jsx +++ b/example/javascripts/components/TogglableSidebarLayout.jsx @@ -58,27 +58,29 @@ export default class TogglableSidebarLayout extends React.Component {
-
-

2nd Pane

-

This is the 2nd pane, considered as a sidebar.

- {!this.state.useSecondaryHidden && ( -
-              <SplitterLayout primaryIndex={'{0}'}>{'\n'}
-                <div>1st</div>{'\n'}
-                <div>2nd</div>{'\n'}
-              </SplitterLayout>
-            
- )} - {this.state.useSecondaryHidden && ( -
-              <SplitterLayout primaryIndex={'{0}'} secondaryHidden={`{${!this.state.sidebarVisible}}`}>{'\n'}
-                <div>1st</div>{'\n'}
-                <div>2nd</div>{'\n'}
-              </SplitterLayout>
-            
- )} - -
+ {(this.state.sidebarVisible || this.state.useSecondaryHidden) && ( +
+

2nd Pane

+

This is the 2nd pane, considered as a sidebar.

+ {!this.state.useSecondaryHidden && ( +
+                <SplitterLayout primaryIndex={'{0}'}>{'\n'}
+                  <div>1st</div>{'\n'}
+                  <div>2nd</div>{'\n'}
+                </SplitterLayout>
+              
+ )} + {this.state.useSecondaryHidden && ( +
+                <SplitterLayout primaryIndex={'{0}'} secondaryHidden={`{${!this.state.sidebarVisible}}`}>{'\n'}
+                  <div>1st</div>{'\n'}
+                  <div>2nd</div>{'\n'}
+                </SplitterLayout>
+              
+ )} + +
+ )}
); }