Skip to content

Commit cd25279

Browse files
boaz0jschuler
authored andcommitted
feat(DataList): Add simple data list (#927)
Signed-off-by: Boaz Shuster <boaz.shuster.github@gmail.com>
1 parent 2070327 commit cd25279

25 files changed

+830
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SFC, HTMLProps } from 'react';
2+
import { Omit } from '../../typeUtils';
3+
4+
export interface DataListProps extends Omit<HTMLProps<HTMLUListElement>, 'aria-label'> {
5+
'aria-label': string;
6+
}
7+
8+
declare const DataList: SFC<DataListProps>;
9+
10+
export default DataList;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {
2+
DataList,
3+
DataListItem,
4+
DataListCell,
5+
DataListCheck,
6+
DataListAction,
7+
DataListContent,
8+
DataListToggle
9+
} from '@patternfly/react-core';
10+
import Simple from './examples/SimpleDataList';
11+
import CheckboxAction from './examples/CheckboxActionDataList';
12+
import Expandable from './examples/ExpandableDataList';
13+
import Modifiers from './examples/ModifiersDataList';
14+
15+
export default {
16+
title: 'DataList',
17+
components: {
18+
DataList,
19+
DataListItem,
20+
DataListCell,
21+
DataListCheck,
22+
DataListAction,
23+
DataListContent,
24+
DataListToggle
25+
},
26+
examples: [
27+
{
28+
component: Simple,
29+
title: 'Data List Simple'
30+
},
31+
{
32+
component: CheckboxAction,
33+
title: 'Data List Checkboxes, Actions and Additional Cells'
34+
},
35+
{
36+
component: Expandable,
37+
title: 'Data List Expandable'
38+
},
39+
{
40+
component: Modifiers,
41+
title: 'Data List Width Modifiers'
42+
}
43+
]
44+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
import { css } from '@patternfly/react-styles';
3+
import PropTypes from 'prop-types';
4+
import styles from '@patternfly/patternfly-next/components/DataList/styles.css';
5+
import boxShStyles from '@patternfly/patternfly-next//utilities/BoxShadow/box-shadow.css';
6+
7+
const DataList = ({ children, className, 'aria-label': ariaLabel, ...props }) => {
8+
return (
9+
<ul className={css(styles.dataList, boxShStyles.boxShadowMd, className)} role="list" aria-label={ariaLabel} {...props}>
10+
{children}
11+
</ul>
12+
);
13+
};
14+
15+
DataList.propTypes = {
16+
/** Content rendered inside the DataList list */
17+
children: PropTypes.node,
18+
/** Additional classes added to the DataList list */
19+
className: PropTypes.string,
20+
/** Adds accessible text to the DataList list */
21+
'aria-label': PropTypes.string.isRequired
22+
};
23+
24+
DataList.defaultProps = {
25+
children: null,
26+
className: '',
27+
};
28+
29+
export default DataList;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
import DataList from './DataList';
4+
import DataListItem from './DataListItem';
5+
import DataListCell from './DataListCell';
6+
import DataListToggle from './DataListToggle';
7+
import { Button } from '../Button';
8+
9+
describe('DataList', () => {
10+
test('List default', () => {
11+
const view = shallow(<DataList aria-label="this is a simple list" />);
12+
expect(view).toMatchSnapshot();
13+
});
14+
15+
test('List', () => {
16+
const view = shallow(<DataList key="list-id-1" className="data-list-custom" aria-label="this is a simple list" />);
17+
expect(view).toMatchSnapshot();
18+
});
19+
20+
test('Item default', () => {
21+
const view = shallow(<DataListItem key="item-id-1" aria-labelledby="item-1" />);
22+
expect(view).toMatchSnapshot();
23+
});
24+
25+
test('Item expanded', () => {
26+
const view = shallow(<DataListItem aria-labelledby="item-1" isExpanded />);
27+
expect(view.props().className).toBe('pf-c-data-list__item pf-m-expanded');
28+
});
29+
30+
test('Item', () => {
31+
const view = shallow(<DataListItem className="data-list-item-custom" aria-labelledby="item-1" />);
32+
expect(view).toMatchSnapshot();
33+
});
34+
35+
test('Cell default', () => {
36+
const view = shallow(<DataListCell>Secondary</DataListCell>);
37+
expect(view).toMatchSnapshot();
38+
});
39+
40+
test('Cell', () => {
41+
const view = shallow(
42+
<DataListCell key="list-id-1" id="primary-item" className="data-list-custom">
43+
Primary Id
44+
</DataListCell>
45+
);
46+
expect(view).toMatchSnapshot();
47+
});
48+
49+
test('Cell with width modifier', () => {
50+
[
51+
{ width: 1, class: '' },
52+
{ width: 2, class: 'pf-m-flex-2' },
53+
{ width: 3, class: 'pf-m-flex-3' },
54+
{ width: 4, class: 'pf-m-flex-4' },
55+
{ width: 5, class: 'pf-m-flex-5' }
56+
].forEach(testCase => {
57+
const view = shallow(
58+
<DataListCell width={testCase.width} key="list-id-1" id="primary-item">
59+
Primary Id
60+
</DataListCell>
61+
);
62+
testCase.class === ''
63+
? expect(view.props().className).toBe('pf-c-data-list__cell')
64+
: expect(view.props().className).toBe(`pf-c-data-list__cell ${testCase.class}`);
65+
});
66+
});
67+
68+
test('Toggle default', () => {
69+
const view = shallow(
70+
<DataListToggle aria-label="Toggle details for" aria-labelledby="ex-toggle2 ex-item2" id="ex-toggle2" />
71+
);
72+
73+
expect(view.find(Button).props()['aria-label']).toBe('Toggle details for');
74+
expect(view.find(Button).props()['aria-labelledby']).toBe('ex-toggle2 ex-item2');
75+
expect(view.find(Button).props()['aria-expanded']).toBe('false');
76+
expect(view.find(Button).props().id).toBe('ex-toggle2');
77+
expect(view.find(Button).props().id).toBe('ex-toggle2');
78+
});
79+
80+
test('Toggle expanded', () => {
81+
const view = shallow(
82+
<DataListToggle
83+
aria-label="Toggle details for"
84+
aria-labelledby="ex-toggle2 ex-item2"
85+
id="ex-toggle2"
86+
isExpanded
87+
/>
88+
);
89+
expect(view.find(Button).props()['aria-expanded']).toBe('true');
90+
});
91+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { SFC, HTMLProps } from 'react';
2+
3+
export interface DataListActionProps extends HTMLProps<HTMLDivElement> {
4+
'aria-labelledby': string;
5+
'aria-label': string;
6+
id: string;
7+
}
8+
9+
declare const DataListAction: SFC<DataListActionProps>;
10+
11+
export default DataListAction;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import { css } from '@patternfly/react-styles';
3+
import PropTypes from 'prop-types';
4+
import styles from '@patternfly/patternfly-next/components/DataList/styles.css';
5+
import { EllipsisVIcon } from '@patternfly/react-icons';
6+
import { Button } from '../Button';
7+
8+
const DataListAction = ({ className, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }) => (
9+
<div className={css(styles.dataListAction, className)} {...props}>
10+
<Button variant="plain" id={id} aria-labelledby={ariaLabelledBy} aria-label={ariaLabel}>
11+
<EllipsisVIcon />
12+
</Button>
13+
</div>
14+
);
15+
16+
DataListAction.propTypes = {
17+
/** Content rendered inside the DataList list */
18+
children: PropTypes.node,
19+
/** Additional classes added to the DataList list */
20+
className: PropTypes.string,
21+
/** Identify the DataList toggle number */
22+
id: PropTypes.string.isRequired,
23+
/** Adds accessible text to the DataList item */
24+
'aria-labelledby': PropTypes.string.isRequired,
25+
/** Adds accessible text to the DataList item */
26+
'aria-label': PropTypes.string.isRequired
27+
};
28+
29+
DataListAction.defaultProps = {
30+
children: null,
31+
className: ''
32+
};
33+
34+
export default DataListAction;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { SFC, HTMLProps } from 'react';
2+
3+
export interface DataListCellProps extends HTMLProps<HTMLDivElement> {
4+
width: 1 | 2 | 3 | 4 | 5;
5+
}
6+
7+
declare const DataListCell: SFC<DataListCellProps>;
8+
9+
export default DataListCell;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { css, getModifier } from '@patternfly/react-styles';
3+
import PropTypes from 'prop-types';
4+
import styles from '@patternfly/patternfly-next/components/DataList/styles.css';
5+
6+
const DataListCell = ({ children, className, width, ...props }) => (
7+
<div
8+
className={css(styles.dataListCell, width > 1 && getModifier(styles, `flex_${width}`, ''), className)}
9+
{...props}
10+
>
11+
{children}
12+
</div>
13+
);
14+
15+
DataListCell.propTypes = {
16+
/** Content rendered inside the DataList cell */
17+
children: PropTypes.node,
18+
/** Additional classes added to the DataList cell */
19+
className: PropTypes.string,
20+
/** Width (from 1-5) to the DataList cell */
21+
width: PropTypes.oneOf([1, 2, 3, 4, 5])
22+
};
23+
24+
DataListCell.defaultProps = {
25+
children: null,
26+
className: '',
27+
width: 1
28+
};
29+
30+
export default DataListCell;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { HTMLProps, FormEvent, ReactNode } from 'react';
2+
import { Omit } from '../../typeUtils';
3+
4+
export interface DataListCheckProps
5+
extends Omit<HTMLProps<HTMLInputElement>, 'type' | 'onChange' | 'disabled' | 'aria-labelledby'> {
6+
isDisabled?: boolean;
7+
isValid?: boolean;
8+
isChecked?: boolean;
9+
onChange?(checked: boolean, event: FormEvent<HTMLInputElement>): void;
10+
'aria-labelledby': string;
11+
}
12+
13+
declare const DataListCheck: React.SFC<DataListCheckProps>;
14+
15+
export default DataListCheck;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import { css } from '@patternfly/react-styles';
3+
import PropTypes from 'prop-types';
4+
import styles from '@patternfly/patternfly-next/components/DataList/styles.css';
5+
import checkboxStyles from '@patternfly/patternfly-next/components/Check/check.css';
6+
7+
const DataListCheck = ({ className, onChange, isValid, isDisabled, isChecked, checked, ...props }) => (
8+
<div className={css(styles.dataListCheck, className)}>
9+
<input
10+
{...props}
11+
className={css(checkboxStyles.checkInput)}
12+
type="checkbox"
13+
onChange={event => onChange(event.currentTarget.checked, event)}
14+
aria-invalid={!isValid}
15+
disabled={isDisabled}
16+
checked={isChecked || checked}
17+
/>
18+
</div>
19+
);
20+
21+
DataListCheck.propTypes = {
22+
/** Additional classes added to the DataList item checkbox */
23+
className: PropTypes.string,
24+
/** Flag to show if the DataList checkbox selection is valid or invalid */
25+
isValid: PropTypes.bool,
26+
/** Flag to show if the DataList checkbox is disabled */
27+
isDisabled: PropTypes.bool,
28+
/** Flag to show if the DataList checkbox is checked */
29+
isChecked: PropTypes.bool,
30+
/** A callback for when the DataList checkbox selection changes */
31+
onChange: PropTypes.func,
32+
/** Aria-labelledby of the DataList checkbox */
33+
'aria-labelledby': PropTypes.string.isRequired
34+
};
35+
36+
DataListCheck.defaultProps = {
37+
className: '',
38+
isValid: true,
39+
isDisabled: false,
40+
isChecked: null,
41+
onChange: () => undefined
42+
};
43+
44+
export default DataListCheck;

0 commit comments

Comments
 (0)