-
Notifications
You must be signed in to change notification settings - Fork 685
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add dependency: lodash.memoize Also, add a script for running jest in watch mode. * Add list components - Bring `List` and its dependencies from the theme repo - Add tests for `List`, `Items`, and `Item` - Improve them based on issues encountered while testing * Re-export List components * Add storybooks for List * Fix broken test after jest upgrade * Fix tests and docs * Update list docs * Remove lodash dependency Replace it with a small memoize utility for unary functions. * Add @storybook/addons
- Loading branch information
Showing
21 changed files
with
3,645 additions
and
1,501 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Item | ||
|
||
The `Item` component is a direct child of the `Items` fragment. | ||
|
||
## Usage | ||
|
||
See `List`. | ||
|
||
## Props | ||
|
||
Prop Name | Required? | Description | ||
--------- | :-------: | :---------- | ||
`classes` | ❌ | A classname object. | ||
`hasFocus` | ❌ | Whether the element currently has browser focus | ||
`isSelected` | ❌ | Whether the item is currently selected | ||
`item` | ✅ | A data object. If `item` is a string, it will be rendered as a child | ||
`render` | ✅ | A [render prop](https://reactjs.org/docs/render-props.html). Also accepts a tagname (e.g., `"div"`) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Items | ||
|
||
The `Items` component is a direct child of the `List` component. As a fragment, it returns its children directly, with no wrapping element. | ||
|
||
## Usage | ||
|
||
See `List`. | ||
|
||
## Props | ||
|
||
Prop Name | Required? | Description | ||
--------- | :-------: | :---------- | ||
`items` | ✅ | An iterable that yields `[key, item]` pairs, such as an ES2015 `Map` | ||
`renderItem` | ❌ | A [render prop](https://reactjs.org/docs/render-props.html). Also accepts a tagname (e.g., `"div"`) | ||
`selectionModel` | ❌ | A string specifying whether to use a `radio` or `checkbox` selection model |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# List | ||
|
||
The `List` component maps a collection of data objects into an array of elements. It also manages the selection and focus of those elements. | ||
|
||
## Usage | ||
|
||
```jsx | ||
import { List } from '@magento/peregrine'; | ||
|
||
const simpleData = new Map() | ||
.set('s', 'Small') | ||
.set('m', 'Medium') | ||
.set('l', 'Large') | ||
|
||
<List | ||
classes={{ root: 'foo' }} | ||
items={simpleData} | ||
render={'ul'} | ||
renderItem={'li'} | ||
/> | ||
|
||
const complexData = new Map() | ||
.set('s', { id: 's', value: 'Small' }) | ||
.set('m', { id: 'm', value: 'Medium' }) | ||
.set('l', { id: 'l', value: 'Large' }) | ||
|
||
<List | ||
classes={{ root: 'bar' }} | ||
items={complexData} | ||
render={props => (<ul>{props.children}</ul>)} | ||
renderItem={props => (<li>{props.item.value}</li>)} | ||
/> | ||
``` | ||
|
||
## Props | ||
|
||
Prop Name | Required? | Description | ||
--------- | :-------: | :---------- | ||
`classes` | ❌ | A classname hash | ||
`items` | ✅ | An iterable that yields `[key, item]` pairs, such as an ES2015 `Map` | ||
`render` | ✅ | A [render prop](https://reactjs.org/docs/render-props.html) for the list element. Also accepts a tagname (e.g., `"div"`) | ||
`renderItem` | ❌ | A [render prop](https://reactjs.org/docs/render-props.html) for the list item elements. Also accepts a tagname (e.g., `"div"`) | ||
`onSelectionChange` | ❌ | A callback fired when the selection state changes | ||
`selectionModel` | ❌ | A string specifying whether to use a `radio` or `checkbox` selection model |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { createElement } from 'react'; | ||
import { storiesOf } from '@storybook/react'; | ||
import { withReadme } from 'storybook-readme'; | ||
|
||
import Item from '..'; | ||
import docs from '../__docs__/item.md'; | ||
|
||
const stories = storiesOf('Item', module); | ||
|
||
stories.add( | ||
'default', | ||
withReadme(docs, () => ( | ||
<Item classes={{ root: 'foo' }} item={{ id: 's', value: 'Small' }} /> | ||
)) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { createElement } from 'react'; | ||
import { storiesOf } from '@storybook/react'; | ||
import { withReadme } from 'storybook-readme'; | ||
|
||
import Items from '..'; | ||
import docs from '../__docs__/items.md'; | ||
|
||
const data = { | ||
s: { id: 's', value: 'Small' }, | ||
m: { id: 'm', value: 'Medium' }, | ||
l: { id: 'l', value: 'Large' } | ||
}; | ||
|
||
const stories = storiesOf('Items', module); | ||
|
||
stories.add( | ||
'default', | ||
withReadme(docs, () => <Items items={Object.entries(data)} />) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { createElement } from 'react'; | ||
import { storiesOf } from '@storybook/react'; | ||
import { withReadme } from 'storybook-readme'; | ||
|
||
import List from '..'; | ||
import docs from '../__docs__/list.md'; | ||
|
||
const stories = storiesOf('List', module); | ||
|
||
// simple example with string values | ||
const simpleData = new Map() | ||
.set('s', 'Small') | ||
.set('m', 'Medium') | ||
.set('l', 'Large'); | ||
|
||
stories.add( | ||
'simple', | ||
withReadme(docs, () => ( | ||
<List | ||
classes={{ root: 'foo' }} | ||
items={simpleData} | ||
render={'ul'} | ||
renderItem={'li'} | ||
/> | ||
)) | ||
); | ||
|
||
// complex example with object values | ||
const complexData = new Map() | ||
.set('s', { id: 's', value: 'Small' }) | ||
.set('m', { id: 'm', value: 'Medium' }) | ||
.set('l', { id: 'l', value: 'Large' }); | ||
|
||
stories.add( | ||
'complex', | ||
withReadme(docs, () => ( | ||
<List | ||
classes={{ root: 'bar' }} | ||
items={complexData} | ||
render={props => <ul>{props.children}</ul>} | ||
renderItem={props => <li>{props.item.value}</li>} | ||
/> | ||
)) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { createElement } from 'react'; | ||
import { configure, shallow } from 'enzyme'; | ||
import Adapter from 'enzyme-adapter-react-16'; | ||
|
||
import { Item } from '../index.js'; | ||
|
||
configure({ adapter: new Adapter() }); | ||
|
||
const classes = { | ||
root: 'abc' | ||
}; | ||
|
||
test('renders a div by default', () => { | ||
const props = { item: 'a' }; | ||
const wrapper = shallow(<Item {...props} />).dive(); | ||
|
||
expect(wrapper.type()).toEqual('div'); | ||
}); | ||
|
||
test('renders a provided tagname', () => { | ||
const props = { item: 'a', render: 'p' }; | ||
const wrapper = shallow(<Item {...props} />).dive(); | ||
|
||
expect(wrapper.type()).toEqual('p'); | ||
}); | ||
|
||
test('renders a provided component', () => { | ||
const Span = () => <span />; | ||
const props = { item: 'a', render: Span }; | ||
const wrapper = shallow(<Item {...props} />); | ||
|
||
expect(wrapper.type()).toEqual(Span); | ||
expect(wrapper.dive().type()).toEqual('span'); | ||
}); | ||
|
||
test('passes only rest props to basic `render`', () => { | ||
const props = { classes, item: 'a', render: 'p' }; | ||
const wrapper = shallow(<Item {...props} data-id="b" />).dive(); | ||
|
||
expect(wrapper.props()).toHaveProperty('data-id'); | ||
expect(wrapper.props()).not.toHaveProperty('classes'); | ||
expect(wrapper.props()).not.toHaveProperty('hasFocus'); | ||
expect(wrapper.props()).not.toHaveProperty('isSelected'); | ||
expect(wrapper.props()).not.toHaveProperty('item'); | ||
expect(wrapper.props()).not.toHaveProperty('render'); | ||
}); | ||
|
||
test('passes custom and rest props to composite `render`', () => { | ||
const Span = () => <span />; | ||
const props = { classes, item: 'a', render: Span }; | ||
const wrapper = shallow(<Item {...props} data-id="b" />); | ||
|
||
expect(wrapper.props()).toHaveProperty('data-id'); | ||
expect(wrapper.props()).toHaveProperty('classes'); | ||
expect(wrapper.props()).toHaveProperty('hasFocus'); | ||
expect(wrapper.props()).toHaveProperty('isSelected'); | ||
expect(wrapper.props()).toHaveProperty('item'); | ||
expect(wrapper.props()).not.toHaveProperty('render'); | ||
}); | ||
|
||
test('passes `item` as `children` if `item` is a string', () => { | ||
const props = { item: 'a', render: 'p' }; | ||
const wrapper = shallow(<Item {...props} />).dive(); | ||
|
||
expect(wrapper.text()).toEqual('a'); | ||
}); | ||
|
||
test('does not pass `children` if `item` is not a string', () => { | ||
const props = { item: { id: 1 }, render: 'p' }; | ||
const wrapper = shallow(<Item {...props} />).dive(); | ||
|
||
expect(wrapper.text()).toBe(''); | ||
}); |
Oops, something went wrong.