@@ -25,6 +25,7 @@ class CheckboxTree extends React.Component {
2525 noCascade : PropTypes . bool ,
2626 onlyLeafCheckboxes : PropTypes . bool ,
2727 optimisticToggle : PropTypes . bool ,
28+ showExpandAll : PropTypes . bool ,
2829 showNodeIcon : PropTypes . bool ,
2930 showNodeTitle : PropTypes . bool ,
3031 onCheck : PropTypes . func ,
@@ -44,6 +45,8 @@ class CheckboxTree extends React.Component {
4445 halfCheck : < span className = "rct-icon rct-icon-half-check" /> ,
4546 expandClose : < span className = "rct-icon rct-icon-expand-close" /> ,
4647 expandOpen : < span className = "rct-icon rct-icon-expand-open" /> ,
48+ expandAll : < span className = "rct-icon rct-icon-expand-all" /> ,
49+ collapseAll : < span className = "rct-icon rct-icon-collapse-all" /> ,
4750 parentClose : < span className = "rct-icon rct-icon-parent-close" /> ,
4851 parentOpen : < span className = "rct-icon rct-icon-parent-open" /> ,
4952 leaf : < span className = "rct-icon rct-icon-leaf" /> ,
@@ -54,6 +57,7 @@ class CheckboxTree extends React.Component {
5457 noCascade : false ,
5558 onlyLeafCheckboxes : false ,
5659 optimisticToggle : true ,
60+ showExpandAll : false ,
5761 showNodeIcon : true ,
5862 showNodeTitle : false ,
5963 onCheck : ( ) => { } ,
@@ -75,6 +79,8 @@ class CheckboxTree extends React.Component {
7579
7680 this . onCheck = this . onCheck . bind ( this ) ;
7781 this . onExpand = this . onExpand . bind ( this ) ;
82+ this . onExpandAll = this . onExpandAll . bind ( this ) ;
83+ this . onCollapseAll = this . onCollapseAll . bind ( this ) ;
7884 }
7985
8086 componentWillReceiveProps ( { nodes, checked, expanded } ) {
@@ -99,6 +105,14 @@ class CheckboxTree extends React.Component {
99105 onExpand ( this . serializeList ( 'expanded' ) , node ) ;
100106 }
101107
108+ onExpandAll ( ) {
109+ this . expandAllNodes ( ) ;
110+ }
111+
112+ onCollapseAll ( ) {
113+ this . expandAllNodes ( false ) ;
114+ }
115+
102116 getFormattedNodes ( nodes ) {
103117 return nodes . map ( ( node ) => {
104118 const formatted = { ...node } ;
@@ -107,7 +121,7 @@ class CheckboxTree extends React.Component {
107121 formatted . expanded = this . nodes [ node . value ] . expanded ;
108122 formatted . showCheckbox = node . showCheckbox !== undefined ? node . showCheckbox : true ;
109123
110- if ( Array . isArray ( node . children ) && node . children . length > 0 ) {
124+ if ( this . nodes [ node . value ] . isParent ) {
111125 formatted . children = this . getFormattedNodes ( formatted . children ) ;
112126 } else {
113127 formatted . children = null ;
@@ -145,6 +159,22 @@ class CheckboxTree extends React.Component {
145159 return Boolean ( node . disabled ) ;
146160 }
147161
162+ nodeHasChildren ( node ) {
163+ return Array . isArray ( node . children ) && node . children . length > 0 ;
164+ }
165+
166+ expandAllNodes ( expand = true ) {
167+ const { onExpand } = this . props ;
168+
169+ Object . keys ( this . nodes ) . forEach ( ( value ) => {
170+ if ( this . nodes [ value ] . isParent ) {
171+ this . nodes [ value ] . expanded = expand ;
172+ }
173+ } ) ;
174+
175+ onExpand ( this . serializeList ( 'expanded' , null ) ) ;
176+ }
177+
148178 toggleChecked ( node , isChecked , noCascade ) {
149179 if ( node . children === null || noCascade ) {
150180 // Set the check status of a leaf node or an uncoupled parent
@@ -167,7 +197,12 @@ class CheckboxTree extends React.Component {
167197 }
168198
169199 nodes . forEach ( ( node ) => {
170- this . nodes [ node . value ] = { } ;
200+ const isParent = this . nodeHasChildren ( node ) ;
201+
202+ this . nodes [ node . value ] = {
203+ isParent,
204+ isLeaf : ! isParent ,
205+ } ;
171206 this . flattenNodes ( node . children ) ;
172207 } ) ;
173208 }
@@ -288,12 +323,45 @@ class CheckboxTree extends React.Component {
288323 return null ;
289324 }
290325
326+ renderExpandAll ( ) {
327+ const { icons : { expandAll, collapseAll } , showExpandAll } = this . props ;
328+
329+ if ( ! showExpandAll ) {
330+ return null ;
331+ }
332+
333+ return (
334+ < div className = "rct-options" >
335+ < button
336+ aria-label = "Expand all"
337+ className = "rct-option rct-option-expand-all"
338+ title = "Expand all"
339+ type = "button"
340+ onClick = { this . onExpandAll }
341+ >
342+ { expandAll }
343+ </ button >
344+ < button
345+ aria-label = "Expand all"
346+ className = "rct-option rct-option-collapse-all"
347+ title = "Collapse all"
348+ type = "button"
349+ onClick = { this . onCollapseAll }
350+ >
351+ { collapseAll }
352+ </ button >
353+ </ div >
354+ ) ;
355+ }
356+
291357 renderHiddenInput ( ) {
292- if ( this . props . name === undefined ) {
358+ const { name, nameAsArray } = this . props ;
359+
360+ if ( name === undefined ) {
293361 return null ;
294362 }
295363
296- if ( this . props . nameAsArray ) {
364+ if ( nameAsArray ) {
297365 return this . renderArrayHiddenInput ( ) ;
298366 }
299367
@@ -325,6 +393,7 @@ class CheckboxTree extends React.Component {
325393
326394 return (
327395 < div className = { className } >
396+ { this . renderExpandAll ( ) }
328397 { this . renderHiddenInput ( ) }
329398 { treeNodes }
330399 </ div >
0 commit comments