Skip to content

Commit

Permalink
feat(list): allow semantic tags (mainly, nav on List and a on ListIte…
Browse files Browse the repository at this point in the history
…m) (#405)
  • Loading branch information
4cm4k1 authored and Matt Goo committed Nov 20, 2018
1 parent f5c9c35 commit 9672cdb
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 4 deletions.
7 changes: 5 additions & 2 deletions packages/list/ListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,19 @@ export default class ListItem extends Component {
/* eslint-enable */
attributesFromList,
children,
tag: Tag,
...otherProps
} = this.props;

return (
<li
<Tag
className={this.classes}
{...otherProps}
{...attributesFromList} // overrides attributes in otherProps
ref={this.listItemElement_}
>
{React.Children.map(children, this.renderChild)}
</li>
</Tag>
);
}

Expand All @@ -118,6 +119,7 @@ ListItem.propTypes = {
onClick: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
tag: PropTypes.string,
};

ListItem.defaultProps = {
Expand All @@ -133,4 +135,5 @@ ListItem.defaultProps = {
onClick: () => {},
onFocus: () => {},
onBlur: () => {},
tag: 'li',
};
4 changes: 4 additions & 0 deletions packages/list/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class MyApp extends Component {
```

> _NOTE_: Please use the `ListItem` component to specify list items. `List` will not recognize custom list item components.
>
> Also, you can override the element that the `List` or `ListItem` renders by passing in a `tag` prop. By default, `List` renders a `ul` and `ListItem` renders an `li`. For semantic HTML and a11y, as well as working with routing libraries such as [React Router](https://github.com/ReactTraining/react-router) and [Next.js' Link](https://github.com/zeit/next.js#with-link), you may wish to use `nav` and `a` respectively if using the components to render a page's navigation.
## Variants

Expand Down Expand Up @@ -188,6 +190,7 @@ wrapFocus | Boolean | Sets the list to allow the up arrow on the first element t
selectedIndex | Number | Toggles the selected state of the list item at the given index
handleSelect | Function(selectedIndex: Number) => void | Callback for handling a list item selection event
aria-orientation | String | Indicates the list orientation
tag | String | Customizes the list tag type (defaults to `'ul'`)

### ListItem

Expand All @@ -204,6 +207,7 @@ onClick | Function(evt: Event) => void | Callback for handling a click event
onKeyDown | Function(evt: Event) => void | Callback for handling a keydown event
onFocus | Function(evt: Event) => void | Callback for handling a focus event
onBlur | Function(evt: Event) => void | Callback for handling a blur event
tag | String | Customizes the list tag type (defaults to `'li'`)

### ListItemText

Expand Down
7 changes: 5 additions & 2 deletions packages/list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,18 @@ export default class List extends Component {
wrapFocus,
/* eslint-enable no-unused-vars */
children,
tag: Tag,
...otherProps
} = this.props;

this.listItemCount = 0;
return (
<ul
<Tag
className={this.classes}
{...otherProps}
>
{React.Children.map(children, this.renderChild)}
</ul>
</Tag>
);
}

Expand Down Expand Up @@ -288,6 +289,7 @@ List.propTypes = {
handleSelect: PropTypes.func,
wrapFocus: PropTypes.bool,
'aria-orientation': PropTypes.string,
tag: PropTypes.string,
};

List.defaultProps = {
Expand All @@ -301,6 +303,7 @@ List.defaultProps = {
handleSelect: () => {},
wrapFocus: true,
'aria-orientation': VERTICAL,
tag: 'ul',
};

/* eslint-enable quote-props */
Expand Down
10 changes: 10 additions & 0 deletions test/unit/list/ListItem.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,13 @@ test('passes props.childrenTabIndex to children as props.tabIndex', () => {
);
assert.equal(wrapper.find('.list-item-child').props().tabIndex, 2);
});

test('renders a list item with default tag', () => {
const wrapper = shallow(<ListItem />);
assert.equal(wrapper.type(), 'li');
});

test('renders a list item with an anchor tag', () => {
const wrapper = shallow(<ListItem tag='a' />);
assert.equal(wrapper.type(), 'a');
});
10 changes: 10 additions & 0 deletions test/unit/list/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,13 @@ test('first item is selected if props.selectedIndex is 0', () => {
);
assert.isTrue(wrapper.state().listItemAttributes[0]['aria-selected']);
});

test('renders a list with default tag', () => {
const wrapper = shallow(<List />);
assert.equal(wrapper.type(), 'ul');
});

test('renders a list with a nav tag', () => {
const wrapper = shallow(<List tag='nav' />);
assert.equal(wrapper.type(), 'nav');
});

0 comments on commit 9672cdb

Please sign in to comment.