diff --git a/docs/src/app/components/pages/components/List/ExampleNested.js b/docs/src/app/components/pages/components/List/ExampleNested.js index 0ee790c33c01d9..539aef945c8f9d 100644 --- a/docs/src/app/components/pages/components/List/ExampleNested.js +++ b/docs/src/app/components/pages/components/List/ExampleNested.js @@ -6,37 +6,76 @@ import ContentInbox from 'material-ui/svg-icons/content/inbox'; import ContentDrafts from 'material-ui/svg-icons/content/drafts'; import ContentSend from 'material-ui/svg-icons/content/send'; import Subheader from 'material-ui/Subheader'; +import Toggle from 'material-ui/Toggle'; -const ListExampleNested = () => ( - - - Nested List Items - } /> - } /> - } - initiallyOpen={true} - primaryTogglesNestedList={true} - nestedItems={[ - } - />, - } - disabled={true} - nestedItems={[ - } />, - ]} - />, - ]} - /> - - -); +export default class ListExampleNested extends React.Component { -export default ListExampleNested; + state = { + open: false, + }; + + handleToggle = () => { + this.setState({ + open: !this.state.open, + }); + }; + + handleNestedListToggle = (item) => { + this.setState({ + open: item.state.open, + }); + }; + + render() { + return ( +
+ +
+ + + Nested List Items + } /> + } /> + } + initiallyOpen={true} + primaryTogglesNestedList={true} + nestedItems={[ + } + />, + } + disabled={true} + nestedItems={[ + } />, + ]} + />, + } + open={this.state.open} + onNestedListToggle={this.handleNestedListToggle} + nestedItems={[ + } />, + ]} + />, + ]} + /> + + +
+ ); + } +} diff --git a/src/List/ListItem.js b/src/List/ListItem.js index 7840aefd4f931a..8d849f8441605e 100644 --- a/src/List/ListItem.js +++ b/src/List/ListItem.js @@ -223,6 +223,10 @@ class ListItem extends Component { onTouchStart: PropTypes.func, /** @ignore */ onTouchTap: PropTypes.func, + /** + * Control toggle state of nested list. + */ + open: PropTypes.bool, /** * This is the block element that contains the primary text. * If a string is passed in, a div tag will be rendered. @@ -282,6 +286,7 @@ class ListItem extends Component { onMouseLeave: () => {}, onNestedListToggle: () => {}, onTouchStart: () => {}, + open: null, primaryTogglesNestedList: false, secondaryTextLines: 1, }; @@ -300,9 +305,15 @@ class ListItem extends Component { }; componentWillMount() { - if (this.props.initiallyOpen) { - this.setState({open: true}); - } + this.setState({ + open: this.props.open === null ? this.props.initiallyOpen === true : this.props.open, + }); + } + + componentWillReceiveProps(nextProps) { + // update the state when the component is controlled. + if (nextProps.open !== null) + this.setState({open: nextProps.open}); } shouldComponentUpdate(nextProps, nextState, nextContext) { diff --git a/src/List/ListItem.spec.js b/src/List/ListItem.spec.js index 0d79ccfcbe6e04..938011e1d55f4e 100644 --- a/src/List/ListItem.spec.js +++ b/src/List/ListItem.spec.js @@ -73,4 +73,43 @@ describe('', () => { assert.ok(wrapper.find('.test-checkbox').length); assert.strictEqual(wrapper.find(`.${testClass}`).length, 1, 'should have a div with the test class'); }); + + it('should initially open nested list', () => { + const testClass = 'test-class'; + const wrapper = shallowWithContext( + , + ]} + /> + ); + + assert.ok(wrapper.find('NestedList').length); + assert.ok(wrapper.find('.test-class').parent().children().nodes[1].props.open === true); + }); + + it('should toggle nested list', () => { + const testClass = 'test-class'; + const wrapper = shallowWithContext( + , + ]} + /> + ); + + assert.ok(wrapper.find('.test-class').parent().children().nodes[1].props.open === false); + wrapper.setProps({ + open: true, + }); + assert.ok(wrapper.find('.test-class').parent().children().nodes[1].props.open === true); + }); });