Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add shorthand specs for List
Browse files Browse the repository at this point in the history
jeffcarbs committed Oct 7, 2016
1 parent 8b2ad59 commit 76e5136
Showing 6 changed files with 121 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React from 'react'
import { List } from 'semantic-ui-react'

const items = [
{ icon: 'users', content: 'Semantic UI' },
{ icon: 'marker', content: 'New York, NY' },
{ icon: 'mail', content: <a href='mailto:jack@semantic-ui.com'>jack@semantic-ui.com</a> },
{ icon: 'linkify', content: <a href='http://www.semantic-ui.com'>semantic-ui.com</a> },
]

const ListExampleIconShorthand = () => (
<List items={items} />
<List>
<List.Item icon='users' content='Semantic UI' />
<List.Item icon='marker' content='New York, NY' />
<List.Item icon='mail' content={<a href='mailto:jack@semantic-ui.com'>jack@semantic-ui.com</a>} />
<List.Item icon='linkify' content={<a href='http://www.semantic-ui.com'>semantic-ui.com</a>} />
</List>
)

export default ListExampleIconShorthand
8 changes: 4 additions & 4 deletions src/elements/Header/Header.js
Original file line number Diff line number Diff line change
@@ -52,13 +52,13 @@ function Header(props) {
return <ElementType {...rest} className={classes}>{children}</ElementType>
}

const iconNode = Icon.create(icon)
const imageNode = Image.create(image)
const iconElement = Icon.create(icon)
const imageElement = Image.create(image)

if (iconNode || imageNode) {
if (iconElement || imageElement) {
return (
<ElementType {...rest} className={classes}>
{iconNode || imageNode}
{iconElement || imageElement}
{(content || subheader) && (
<HeaderContent>
{content}
47 changes: 31 additions & 16 deletions src/elements/List/ListItem.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash'
import cx from 'classnames'
import React, { PropTypes } from 'react'
import React, { isValidElement, PropTypes } from 'react'

import {
createShorthandFactory,
@@ -45,29 +45,30 @@ function ListItem(props) {
return <ElementType {...rest} className={classes} {...valueProp}>{children}</ElementType>
}

const iconNode = ListIcon.create(icon)
const imageNode = Image.create(image)
const iconElement = ListIcon.create(icon)
const imageElement = Image.create(image)

if (_.isPlainObject(content)) {
// See description of `content` prop for explanation about why this is necessary.
if (!isValidElement(content) && _.isPlainObject(content)) {
return (
<ElementType {...rest} className={classes} {...valueProp}>
{iconNode || imageNode}
{iconElement || imageElement}
{ListContent.create(content, { header, description })}
</ElementType>
)
}

const headerNode = ListHeader.create(header)
const descriptionNode = ListDescription.create(description)
const headerElement = ListHeader.create(header)
const descriptionElement = ListDescription.create(description)

if (iconNode || imageNode) {
if (iconElement || imageElement) {
return (
<ElementType {...rest} className={classes} {...valueProp}>
{iconNode || imageNode}
{(content || headerNode || descriptionNode) && (
{iconElement || imageElement}
{(content || headerElement || descriptionElement) && (
<ListContent>
{headerNode}
{descriptionNode}
{headerElement}
{descriptionElement}
{content}
</ListContent>
)}
@@ -77,8 +78,8 @@ function ListItem(props) {

return (
<ElementType {...rest} className={classes} {...valueProp}>
{headerNode}
{descriptionNode}
{headerElement}
{descriptionElement}
{content}
</ElementType>
)
@@ -103,8 +104,22 @@ ListItem.propTypes = {
/** Additional classes. */
className: PropTypes.string,

/** Shorthand for primary content. */
content: customPropTypes.contentShorthand,
/**
* Shorthand for primary content.
*
* Heads up!
*
* This is handled slightly differently than the typical `content` prop since
* the wrapping ListContent is not used when there's no icon or image.
*
* If you pass content as:
* - an element/literal, it's treated as the sibling node to
* header/description (whether wrapped in Item.Content or not).
* - a props object, it forces the presence of Item.Content and passes those
* props to it. If you pass a content prop within that props object, it
* will be treated as the sibling node to header/description.
*/
content: customPropTypes.itemShorthand,

/** Shorthand for ListDescription. */
description: customPropTypes.itemShorthand,
18 changes: 18 additions & 0 deletions test/specs/elements/List/List-test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from 'react'

import * as common from 'test/specs/commonTests'
import List from 'src/elements/List/List'
import ListContent from 'src/elements/List/ListContent'
@@ -27,4 +29,20 @@ describe('List', () => {
common.propKeyOrValueAndKeyToClassName(List, 'relaxed')
common.propValueOnlyToClassName(List, 'size')
common.implementsVerticalAlignProp(List)

describe('shorthand', () => {
const items = ['Name', 'Status', 'Notes']

it('renders empty tr with no shorthand', () => {
const wrapper = shallow(<List />)

wrapper.find('ListItem').should.have.lengthOf(0)
})

it('renders the items', () => {
const wrapper = shallow(<List items={items} />)

wrapper.find('ListItem').should.have.lengthOf(items.length)
})
})
})
19 changes: 19 additions & 0 deletions test/specs/elements/List/ListContent-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import faker from 'faker'
import React from 'react'
import * as common from 'test/specs/commonTests'

import ListContent from 'src/elements/List/ListContent'

describe('ListContent', () => {
@@ -7,4 +10,20 @@ describe('ListContent', () => {

common.implementsVerticalAlignProp(ListContent)
common.propKeyAndValueToClassName(ListContent, 'floated')

describe('shorthand', () => {
const baseProps = {
content: faker.hacker.phrase(),
description: faker.hacker.phrase(),
header: faker.hacker.phrase(),
}

it('renders content without wrapping ListContent', () => {
const wrapper = shallow(<ListContent {...baseProps} />)

wrapper.find('ListHeader').should.have.prop('content', baseProps.header)
wrapper.find('ListDescription').should.have.prop('content', baseProps.description)
wrapper.should.contain.text(baseProps.content)
})
})
})
43 changes: 43 additions & 0 deletions test/specs/elements/List/ListItem-test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import faker from 'faker'
import React from 'react'
import * as common from 'test/specs/commonTests'
import { sandbox } from 'test/utils'

import ListItem from 'src/elements/List/ListItem'
import ListContent from 'src/elements/List/ListContent'

describe('ListItem', () => {
common.isConformant(ListItem)
@@ -33,4 +35,45 @@ describe('ListItem', () => {
.should.have.attr('value', value)
})
})

describe('shorthand', () => {
const baseProps = {
content: faker.hacker.phrase(),
description: faker.hacker.phrase(),
header: faker.hacker.phrase(),
}

it('renders without wrapping ListContent', () => {
const wrapper = shallow(<ListItem {...baseProps} />)

wrapper.find('ListContent').should.have.lengthOf(0)
})

it('renders without wrapping ListContent when content passed as element', () => {
const spy = sandbox.spy(ListContent, 'create')
shallow(<ListItem {...baseProps} content={<div />} />)

spy.should.not.have.been.called()
})

it('renders wrapping ListContent when content passed as props', () => {
const wrapper = shallow(<ListItem content={baseProps} />)

wrapper.find('ListContent').should.have.lengthOf(1)
})

it('renders wrapping ListContent when icon present', () => {
const wrapper = shallow(<ListItem {...baseProps} icon='user' />)

wrapper.find('ListIcon').should.have.lengthOf(1)
wrapper.find('ListContent').should.have.lengthOf(1)
})

it('renders wrapping ListContent when image present', () => {
const wrapper = shallow(<ListItem {...baseProps} image='foo.png' />)

wrapper.find('Image').should.have.lengthOf(1)
wrapper.find('ListContent').should.have.lengthOf(1)
})
})
})

0 comments on commit 76e5136

Please sign in to comment.