Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit e903dd5

Browse files
authored
feat(list): Add List Group and List Group Subheader (#386)
1 parent f6da361 commit e903dd5

File tree

10 files changed

+240
-20
lines changed

10 files changed

+240
-20
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@
3131
"card",
3232
"checkbox",
3333
"chips",
34+
"drawer",
3435
"fab",
3536
"floating-label",
3637
"icon-button",
3738
"layout-grid",
3839
"line-ripple",
40+
"list",
3941
"menu-surface",
4042
"ripple",
4143
"select",

packages/list/.npmignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
index.js
2-
ListDivider.js
2+
ListGroup.js
3+
ListGroupSubheader.js
34
ListItem.js
5+
ListItemGraphic.js
6+
ListItemMeta.js
7+
ListItemText.js

packages/list/ListGroup.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2018 Google, Inc.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
// THE SOFTWARE.
22+
23+
import React from 'react';
24+
import PropTypes from 'prop-types';
25+
import classnames from 'classnames';
26+
27+
const ListGroup = (props) => {
28+
const {
29+
tag: Tag,
30+
className,
31+
children,
32+
...otherProps
33+
} = props;
34+
35+
return (
36+
<Tag className={classnames('mdc-list-group', className)} {...otherProps}>
37+
{children}
38+
</Tag>
39+
);
40+
};
41+
42+
ListGroup.propTypes = {
43+
className: PropTypes.string,
44+
children: PropTypes.element,
45+
tag: PropTypes.string,
46+
};
47+
48+
ListGroup.defaultProps = {
49+
className: '',
50+
children: null,
51+
tag: 'div',
52+
};
53+
54+
export default ListGroup;

packages/list/ListGroupSubheader.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2018 Google, Inc.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
// THE SOFTWARE.
22+
23+
import React from 'react';
24+
import PropTypes from 'prop-types';
25+
import classnames from 'classnames';
26+
27+
const ListGroupSubheader = (props) => {
28+
const {
29+
tag: Tag,
30+
className,
31+
children,
32+
...otherProps
33+
} = props;
34+
35+
return (
36+
<Tag className={classnames('mdc-list-group__subheader', className)} {...otherProps}>
37+
{children}
38+
</Tag>
39+
);
40+
};
41+
42+
ListGroupSubheader.propTypes = {
43+
className: PropTypes.string,
44+
children: PropTypes.oneOfType([
45+
PropTypes.string,
46+
PropTypes.element,
47+
]),
48+
tag: PropTypes.string,
49+
};
50+
51+
ListGroupSubheader.defaultProps = {
52+
className: '',
53+
children: '',
54+
tag: 'h3',
55+
};
56+
57+
export default ListGroupSubheader;

packages/list/README.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import "@material/react-list/dist/list.css";
2828

2929
```js
3030
import React, {Component} from 'react';
31-
import List, {ListItem} from '@material/react-list';
31+
import List, {ListItem, ListItemText} from '@material/react-list';
3232

3333
class MyApp extends Component {
3434
render() {
@@ -56,6 +56,9 @@ class MyApp extends Component {
5656
You can use the `twoLine` Boolean prop for `List` combined with the `secondaryText` prop for `ListItem` to style a list as a double line list.
5757

5858
```js
59+
import React, {Component} from 'react';
60+
import List, {ListItem, ListItemText} from '@material/react-list';
61+
5962
class MyApp extends React.Component {
6063
render() {
6164
return (
@@ -87,7 +90,7 @@ You may add a leading visuals or trailing metadata to a list item using `ListIte
8790

8891
```js
8992
import React, {Component} from 'react';
90-
import List, {ListItem} from '@material/react-list';
93+
import List, {ListItem, ListItemGraphic, ListItemText, ListItemMeta} from '@material/react-list';
9194

9295
class MyApp extends Component {
9396
render() {
@@ -105,6 +108,36 @@ class MyApp extends Component {
105108
}
106109
```
107110

111+
### List groups
112+
113+
Multiple related lists can be grouped together using the `ListGroup` component. Optional subheaders can be added using `ListGroupSubheader`.
114+
115+
> _NOTE_: You can override the element that the `ListGroup` or `ListGroupSubheader` renders by passing in a `tag` prop. By default, `ListGroup` renders a `div` and `ListGroupSubheader` renders an `h3`.
116+
117+
```js
118+
import React, {Component} from 'react';
119+
import List, {ListItem, ListItemText, ListGroup, ListGroupSubheader} from '@material/react-list';
120+
121+
class MyApp extends Component {
122+
render() {
123+
return (
124+
<ListGroup>
125+
<ListGroupSubheader tag='h2'>Folders</ListGroupSubheader>
126+
<List>
127+
<ListItem><ListItemText primaryText='Photos' /></ListItem>
128+
...
129+
</List>
130+
<ListGroupSubheader tag='h2'>Recent Files</ListGroupSubheader>
131+
<List>
132+
<ListItem><ListItemText primaryText='Vacation' /></ListItem>
133+
...
134+
</List>
135+
</ListGroup>
136+
);
137+
}
138+
}
139+
```
140+
108141
### Single Selection
109142

110143
You can use the `singleSelection` Boolean prop for `List` to allow for selection of list items. You can also set the `selectedIndex` of the list programmatically.

packages/list/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import ListItem from './ListItem';
3030
import ListItemGraphic from './ListItemGraphic';
3131
import ListItemText from './ListItemText';
3232
import ListItemMeta from './ListItemMeta';
33+
import ListGroup from './ListGroup';
34+
import ListGroupSubheader from './ListGroupSubheader';
3335

3436
const ARIA_ORIENTATION = 'aria-orientation';
3537
const VERTICAL = 'vertical';
@@ -334,4 +336,4 @@ List.defaultProps = {
334336

335337
/* eslint-enable quote-props */
336338

337-
export {ListItem, ListItemGraphic, ListItemText, ListItemMeta};
339+
export {ListItem, ListItemGraphic, ListItemText, ListItemMeta, ListGroup, ListGroupSubheader};

test/screenshot/golden.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"icon-button": "5ffb1f7fbd06d2c0533f6ba8d4d9ea170cec1a248a61de1cc1bb626cb58ebcd2",
99
"layout-grid": "fe40f7a34853bc2a1d9a836e812599d4d47b5b26b839a8eaed7f98ea91946790",
1010
"line-ripple": "56b136db2dc7e09260849447e6bde9b55a837af332a05d9f52506ab1c95e2e57",
11-
"list": "2d98c951d42a2e724d57083159aebdc34848bbd3fa2c264c8595ea0982cc1a99",
11+
"list": "3a7f7334d1a3f883122e4b410ea1b23b7601754aa44ff46ee24d31aeb808632d",
1212
"material-icon": "442b39fb22d2c7a74efb23ca098429b471501ce21df8662327bbf9871fe0bcb0",
1313
"menu-surface": "f5face1a24fe166e86e8a3dc35ea85b2d4431469a3d06bf6fc1a30fbdc175aff",
1414
"notched-outline": "7770dd381c27608a1f43b6f83da92507fe53963f5e4409bd73184b86275538fe",

test/screenshot/list/index.js

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import './index.scss';
33
import '../../../packages/list/index.scss';
44

55
import MaterialIcon from '../../../packages/material-icon';
6-
import List from '../../../packages/list/index';
7-
import {ListItem} from '../../../packages/list/index';
8-
import ListItemGraphic from '../../../packages/list/ListItemGraphic';
9-
import ListItemText from '../../../packages/list/ListItemText';
10-
import ListItemMeta from '../../../packages/list/ListItemMeta';
6+
import List, {
7+
ListItem,
8+
ListItemGraphic,
9+
ListItemText,
10+
ListItemMeta,
11+
ListGroup,
12+
ListGroupSubheader,
13+
} from '../../../packages/list/index';
1114

1215
class SelectionListTest extends React.Component {
1316
state = {
@@ -41,21 +44,34 @@ const renderListItem = (primaryText, secondaryText) => {
4144
const ListScreenshotTest = () => {
4245
return (
4346
<div>
44-
<h2>Two-line Selection List</h2>
47+
<h2>Two-line selection list</h2>
4548
<SelectionListTest twoLine>
46-
{renderListItem('hello', 'world')}
47-
{renderListItem('hello', 'world')}
48-
{renderListItem('hello', 'world')}
49-
{renderListItem('hello', 'world')}
49+
{renderListItem('List item', 'Secondary text')}
50+
{renderListItem('List item', 'Secondary text')}
51+
{renderListItem('List item', 'Secondary text')}
5052
</SelectionListTest>
5153

52-
<h2>One-line List</h2>
54+
<h2>One-line list</h2>
5355
<List>
54-
{renderListItem('hello')}
55-
{renderListItem('hello')}
56-
{renderListItem('hello')}
57-
{renderListItem('hello')}
56+
{renderListItem('List item')}
57+
{renderListItem('List item')}
58+
{renderListItem('List item')}
5859
</List>
60+
61+
<h2>List group</h2>
62+
<ListGroup>
63+
<ListGroupSubheader>Folders</ListGroupSubheader>
64+
<List>
65+
{renderListItem('Photos')}
66+
{renderListItem('Recipes')}
67+
{renderListItem('Work')}
68+
</List>
69+
<ListGroupSubheader>Recent Files</ListGroupSubheader>
70+
<List>
71+
{renderListItem('Vacation itinerary')}
72+
{renderListItem('Kitchen remodel')}
73+
</List>
74+
</ListGroup>
5975
</div>
6076
);
6177
};

test/unit/list/ListGroup.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import {assert} from 'chai';
3+
import {shallow} from 'enzyme';
4+
import {ListGroup} from '../../../packages/list';
5+
6+
suite('ListGroup');
7+
8+
test('className adds classes', () => {
9+
const wrapper = shallow(<ListGroup className='test-class-name' />);
10+
assert.isTrue(wrapper.hasClass('test-class-name'));
11+
});
12+
13+
test('has mdc-list-group class', () => {
14+
const wrapper = shallow(<ListGroup />);
15+
assert.isTrue(wrapper.hasClass('mdc-list-group'));
16+
});
17+
18+
test('renders children', () => {
19+
const wrapper = shallow(<ListGroup><div className='child-list'/></ListGroup>);
20+
assert.exists(wrapper.find('.child-list'));
21+
});
22+
23+
test('renders with given tag', () => {
24+
const wrapper = shallow(<ListGroup tag='span' />);
25+
assert.exists(wrapper.find('span'));
26+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import {assert} from 'chai';
3+
import {shallow} from 'enzyme';
4+
import {ListGroupSubheader} from '../../../packages/list';
5+
6+
suite('ListGroupSubheader');
7+
8+
test('className adds classes', () => {
9+
const wrapper = shallow(<ListGroupSubheader className='test-class-name' />);
10+
assert.isTrue(wrapper.hasClass('test-class-name'));
11+
});
12+
13+
test('has mdc-list-group__subheader class', () => {
14+
const wrapper = shallow(<ListGroupSubheader />);
15+
assert.isTrue(wrapper.hasClass('mdc-list-group__subheader'));
16+
});
17+
18+
test('renders children', () => {
19+
const wrapper = shallow(<ListGroupSubheader><div className='child-list'/></ListGroupSubheader>);
20+
assert.exists(wrapper.find('.child-list'));
21+
});
22+
23+
test('renders with given tag', () => {
24+
const wrapper = shallow(<ListGroupSubheader tag='span' />);
25+
assert.exists(wrapper.find('span'));
26+
});

0 commit comments

Comments
 (0)