Skip to content

Commit d2a7b94

Browse files
Merge pull request mui#4850 from davincho/list-item-prog-open
[ListItem] New property open to toggle nested list
2 parents 5883067 + 673f0e8 commit d2a7b94

3 files changed

Lines changed: 124 additions & 35 deletions

File tree

docs/src/app/components/pages/components/List/ExampleNested.js

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,76 @@ import ContentInbox from 'material-ui/svg-icons/content/inbox';
66
import ContentDrafts from 'material-ui/svg-icons/content/drafts';
77
import ContentSend from 'material-ui/svg-icons/content/send';
88
import Subheader from 'material-ui/Subheader';
9+
import Toggle from 'material-ui/Toggle';
910

10-
const ListExampleNested = () => (
11-
<MobileTearSheet>
12-
<List>
13-
<Subheader>Nested List Items</Subheader>
14-
<ListItem primaryText="Sent mail" leftIcon={<ContentSend />} />
15-
<ListItem primaryText="Drafts" leftIcon={<ContentDrafts />} />
16-
<ListItem
17-
primaryText="Inbox"
18-
leftIcon={<ContentInbox />}
19-
initiallyOpen={true}
20-
primaryTogglesNestedList={true}
21-
nestedItems={[
22-
<ListItem
23-
key={1}
24-
primaryText="Starred"
25-
leftIcon={<ActionGrade />}
26-
/>,
27-
<ListItem
28-
key={2}
29-
primaryText="Sent Mail"
30-
leftIcon={<ContentSend />}
31-
disabled={true}
32-
nestedItems={[
33-
<ListItem key={1} primaryText="Drafts" leftIcon={<ContentDrafts />} />,
34-
]}
35-
/>,
36-
]}
37-
/>
38-
</List>
39-
</MobileTearSheet>
40-
);
11+
export default class ListExampleNested extends React.Component {
4112

42-
export default ListExampleNested;
13+
state = {
14+
open: false,
15+
};
16+
17+
handleToggle = () => {
18+
this.setState({
19+
open: !this.state.open,
20+
});
21+
};
22+
23+
handleNestedListToggle = (item) => {
24+
this.setState({
25+
open: item.state.open,
26+
});
27+
};
28+
29+
render() {
30+
return (
31+
<div>
32+
<Toggle
33+
toggled={this.state.open}
34+
onToggle={this.handleToggle}
35+
labelPosition="right"
36+
label="This toggle controls the expanded state of the submenu item."
37+
/>
38+
<br />
39+
<MobileTearSheet>
40+
<List>
41+
<Subheader>Nested List Items</Subheader>
42+
<ListItem primaryText="Sent mail" leftIcon={<ContentSend />} />
43+
<ListItem primaryText="Drafts" leftIcon={<ContentDrafts />} />
44+
<ListItem
45+
primaryText="Inbox"
46+
leftIcon={<ContentInbox />}
47+
initiallyOpen={true}
48+
primaryTogglesNestedList={true}
49+
nestedItems={[
50+
<ListItem
51+
key={1}
52+
primaryText="Starred"
53+
leftIcon={<ActionGrade />}
54+
/>,
55+
<ListItem
56+
key={2}
57+
primaryText="Sent Mail"
58+
leftIcon={<ContentSend />}
59+
disabled={true}
60+
nestedItems={[
61+
<ListItem key={1} primaryText="Drafts" leftIcon={<ContentDrafts />} />,
62+
]}
63+
/>,
64+
<ListItem
65+
key={3}
66+
primaryText="Inbox"
67+
leftIcon={<ContentInbox />}
68+
open={this.state.open}
69+
onNestedListToggle={this.handleNestedListToggle}
70+
nestedItems={[
71+
<ListItem key={1} primaryText="Drafts" leftIcon={<ContentDrafts />} />,
72+
]}
73+
/>,
74+
]}
75+
/>
76+
</List>
77+
</MobileTearSheet>
78+
</div>
79+
);
80+
}
81+
}

src/List/ListItem.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ class ListItem extends Component {
223223
onTouchStart: PropTypes.func,
224224
/** @ignore */
225225
onTouchTap: PropTypes.func,
226+
/**
227+
* Control toggle state of nested list.
228+
*/
229+
open: PropTypes.bool,
226230
/**
227231
* This is the block element that contains the primary text.
228232
* If a string is passed in, a div tag will be rendered.
@@ -282,6 +286,7 @@ class ListItem extends Component {
282286
onMouseLeave: () => {},
283287
onNestedListToggle: () => {},
284288
onTouchStart: () => {},
289+
open: null,
285290
primaryTogglesNestedList: false,
286291
secondaryTextLines: 1,
287292
};
@@ -300,9 +305,15 @@ class ListItem extends Component {
300305
};
301306

302307
componentWillMount() {
303-
if (this.props.initiallyOpen) {
304-
this.setState({open: true});
305-
}
308+
this.setState({
309+
open: this.props.open === null ? this.props.initiallyOpen === true : this.props.open,
310+
});
311+
}
312+
313+
componentWillReceiveProps(nextProps) {
314+
// update the state when the component is controlled.
315+
if (nextProps.open !== null)
316+
this.setState({open: nextProps.open});
306317
}
307318

308319
shouldComponentUpdate(nextProps, nextState, nextContext) {

src/List/ListItem.spec.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,43 @@ describe('<ListItem />', () => {
7373
assert.ok(wrapper.find('.test-checkbox').length);
7474
assert.strictEqual(wrapper.find(`.${testClass}`).length, 1, 'should have a div with the test class');
7575
});
76+
77+
it('should initially open nested list', () => {
78+
const testClass = 'test-class';
79+
const wrapper = shallowWithContext(
80+
<ListItem
81+
className={testClass}
82+
initiallyOpen={true}
83+
nestedItems={[
84+
<ListItem
85+
key={1}
86+
/>,
87+
]}
88+
/>
89+
);
90+
91+
assert.ok(wrapper.find('NestedList').length);
92+
assert.ok(wrapper.find('.test-class').parent().children().nodes[1].props.open === true);
93+
});
94+
95+
it('should toggle nested list', () => {
96+
const testClass = 'test-class';
97+
const wrapper = shallowWithContext(
98+
<ListItem
99+
className={testClass}
100+
open={false}
101+
nestedItems={[
102+
<ListItem
103+
key={1}
104+
/>,
105+
]}
106+
/>
107+
);
108+
109+
assert.ok(wrapper.find('.test-class').parent().children().nodes[1].props.open === false);
110+
wrapper.setProps({
111+
open: true,
112+
});
113+
assert.ok(wrapper.find('.test-class').parent().children().nodes[1].props.open === true);
114+
});
76115
});

0 commit comments

Comments
 (0)