diff --git a/README.md b/README.md index 5d51ba2ffd..77aea61c04 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ Be sure to check out the above migrations before embarking on a new component. | x Button | x Breadcrumb | _ Advertisement | x Accordion | x Form Validation | | x Container | x Form | _ Card | x Checkbox | *API (NA)* | | x Divider | x Grid | _ Comment | _ Dimmer | *Visibility (NA)* | -| x Flag | x Menu | _ Feed | x Dropdown | | +| x Flag | x Menu | x Feed | x Dropdown | | | x Header | x Message | x Item | _ Embed | | | x Icon | x Table | x Statistic | x Modal | | | x Image | | | _ Nag | | diff --git a/docs/app/Examples/views/Feed/Content/AdditionalInformation.js b/docs/app/Examples/views/Feed/Content/AdditionalInformation.js new file mode 100644 index 0000000000..614b9420fa --- /dev/null +++ b/docs/app/Examples/views/Feed/Content/AdditionalInformation.js @@ -0,0 +1,82 @@ +import _ from 'lodash' +import React from 'react' +import { Feed } from 'stardust' + +const { Content, Event, Extra, Label, Summary } = Feed +const images = _.times(2, () => 'http://semantic-ui.com/images/wireframe/image.png') + +const AdditionalInformation = () => { + return ( + + + + + + + + + + + + + + + + + + + + ) +} + +export default AdditionalInformation diff --git a/docs/app/Examples/views/Feed/Content/Date.js b/docs/app/Examples/views/Feed/Content/Date.js new file mode 100644 index 0000000000..2e2dd0c387 --- /dev/null +++ b/docs/app/Examples/views/Feed/Content/Date.js @@ -0,0 +1,49 @@ +import React from 'react' +import { Feed } from 'stardust' + +const imageSrc = 'http://semantic-ui.com/images/avatar/small/jenny.jpg' + +const Date = () => { + return ( + + + + + 3 days ago + + You added Jenny Hess to your coworker group. + + + + + + + + + + You added Jenny Hess to your coworker group. + + + + + + + + + You added Jenny Hess to your coworker group. + + + + + + + + + + ) +} + +export default Date diff --git a/docs/app/Examples/views/Feed/Content/DateSummary.js b/docs/app/Examples/views/Feed/Content/DateSummary.js new file mode 100644 index 0000000000..ff00dbdb1f --- /dev/null +++ b/docs/app/Examples/views/Feed/Content/DateSummary.js @@ -0,0 +1,44 @@ +import React from 'react' +import { Feed } from 'stardust' + +const { Content, Date, Event, Label, Summary } = Feed +const imageSrc = 'http://semantic-ui.com/images/avatar/small/jenny.jpg' + +const DateSummary = () => { + return ( + + + + + + + + + + + ) +} + +export default DateSummary diff --git a/docs/app/Examples/views/Feed/Content/LabelIcon.js b/docs/app/Examples/views/Feed/Content/LabelIcon.js new file mode 100644 index 0000000000..4b18d31acc --- /dev/null +++ b/docs/app/Examples/views/Feed/Content/LabelIcon.js @@ -0,0 +1,29 @@ +import React from 'react' +import { Feed } from 'stardust' + +const LabelImage = () => { + return ( + + + + + + You posted on your friend Stevie Feliciano's wall. + Today + + + + + + + + You posted on your friend Stevie Feliciano's wall. + Today + + + + + ) +} + +export default LabelImage diff --git a/docs/app/Examples/views/Feed/Content/LabelImage.js b/docs/app/Examples/views/Feed/Content/LabelImage.js new file mode 100644 index 0000000000..b772252b8d --- /dev/null +++ b/docs/app/Examples/views/Feed/Content/LabelImage.js @@ -0,0 +1,35 @@ +import React from 'react' +import { Feed } from 'stardust' + +const image = + +const LabelImage = () => { + return ( +
+ + + + + + + You added Elliot Fu to the group Coworkers + + + + + + You added Elliot Fu to the group Coworkers + + + + + + You added Elliot Fu to the group Coworkers + + + +
+ ) +} + +export default LabelImage diff --git a/docs/app/Examples/views/Feed/Content/index.js b/docs/app/Examples/views/Feed/Content/index.js new file mode 100644 index 0000000000..3f58f56692 --- /dev/null +++ b/docs/app/Examples/views/Feed/Content/index.js @@ -0,0 +1,31 @@ +import React from 'react' +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const FeedContentExamples = () => { + return ( + + + + + + + + + + ) +} + +export default FeedContentExamples diff --git a/docs/app/Examples/views/Feed/Types/Basic.js b/docs/app/Examples/views/Feed/Types/Basic.js new file mode 100644 index 0000000000..ea3ae861e0 --- /dev/null +++ b/docs/app/Examples/views/Feed/Types/Basic.js @@ -0,0 +1,83 @@ +import React from 'react' +import { Feed } from 'stardust' + +const { Content, Date, Event, Extra, Label, Like, Meta, Summary, User } = Feed + +const Basic = () => { + return ( + + + + + + Elliot Fu added you as a friend + 1 Hour Ago + + + 4 Likes + + + + + + + + + + + + + + + + + ) +} + +export default Basic diff --git a/docs/app/Examples/views/Feed/Types/Props.js b/docs/app/Examples/views/Feed/Types/Props.js new file mode 100644 index 0000000000..a1b2bd8d1b --- /dev/null +++ b/docs/app/Examples/views/Feed/Types/Props.js @@ -0,0 +1,52 @@ +import React from 'react' +import { Feed } from 'stardust' + +const events = [ + { + date: '1 Hour Ago', + image: 'http://semantic-ui.com/images/avatar/small/elliot.jpg', + meta: '4 Likes', + summary: 'Elliot Fu added you as a friend', + }, + { + date: '4 days ago', + image: 'http://semantic-ui.com/images/avatar/small/helen.jpg', + meta: '1 Like', + summary: 'Helen Troy added 2 new illustrations', + extraImages: [ + 'http://semantic-ui.com/images/wireframe/image.png', + 'http://semantic-ui.com/images/wireframe/image.png', + ], + }, + { + date: '2 Days Ago', + image: 'http://semantic-ui.com/images/avatar/small/jenny.jpg', + meta: '8 Likes', + summary: 'Jenny Hess added you as a friend', + }, + { + date: '3 days ago', + image: 'http://semantic-ui.com/images/avatar/small/joe.jpg', + meta: '8 Likes', + summary: 'Joe Henderson posted on his page', + extraText: [ + "Ours is a life of constant reruns. We're always circling back to where we'd we started, then starting all", + "over again. Even if we don't run extra laps that day, we surely will come back for more of the same another", + 'day soon.', + ].join(' '), + }, + { + date: '4 days ago', + image: 'http://semantic-ui.com/images/avatar/small/justen.jpg', + meta: '41 Likes', + summary: 'Justen Kitsune added 2 new photos of you', + extraImages: [ + 'http://semantic-ui.com/images/wireframe/image.png', + 'http://semantic-ui.com/images/wireframe/image.png', + ], + }, +] + +const Props = () => + +export default Props diff --git a/docs/app/Examples/views/Feed/Types/index.js b/docs/app/Examples/views/Feed/Types/index.js new file mode 100644 index 0000000000..52e629938b --- /dev/null +++ b/docs/app/Examples/views/Feed/Types/index.js @@ -0,0 +1,18 @@ +import React from 'react' +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const FeedTypesExamples = () => { + return ( + + + + + ) +} + +export default FeedTypesExamples diff --git a/docs/app/Examples/views/Feed/Variations/SizeLarge.js b/docs/app/Examples/views/Feed/Variations/SizeLarge.js new file mode 100644 index 0000000000..9cb604e189 --- /dev/null +++ b/docs/app/Examples/views/Feed/Variations/SizeLarge.js @@ -0,0 +1,55 @@ +import React from 'react' +import { Feed } from 'stardust' + +const { Content, Event, Extra, Label, Like, Meta, Summary, User } = Feed + +const SizeLarge = () => { + return ( + + + + + + + + + + + ) +} + +export default SizeLarge diff --git a/docs/app/Examples/views/Feed/Variations/SizeSmall.js b/docs/app/Examples/views/Feed/Variations/SizeSmall.js new file mode 100644 index 0000000000..9e4a606465 --- /dev/null +++ b/docs/app/Examples/views/Feed/Variations/SizeSmall.js @@ -0,0 +1,46 @@ +import React from 'react' +import { Feed, Header } from 'stardust' + +const { Content, Event, Summary } = Feed + +const SizeSmall = () => { + return ( + + Followers Activity + + + + + Elliot Fu added Jenny Hess as a friend + + + + + + + + Stevie Feliciano added Elliot Fu as a friend + + + + + + + + Helen Troy added Christian Rocha as a friend + + + + + + + + Christian Rocha signed up for the site. + + + + + ) +} + +export default SizeSmall diff --git a/docs/app/Examples/views/Feed/Variations/index.js b/docs/app/Examples/views/Feed/Variations/index.js new file mode 100644 index 0000000000..07fa656b7c --- /dev/null +++ b/docs/app/Examples/views/Feed/Variations/index.js @@ -0,0 +1,18 @@ +import React from 'react' +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const FeedVariationsExamples = () => { + return ( + + + + + ) +} + +export default FeedVariationsExamples diff --git a/docs/app/Examples/views/Feed/index.js b/docs/app/Examples/views/Feed/index.js new file mode 100644 index 0000000000..dbb835c2f2 --- /dev/null +++ b/docs/app/Examples/views/Feed/index.js @@ -0,0 +1,17 @@ +import React from 'react' + +import Content from './Content' +import Types from './Types' +import Variations from './Variations' + +const FeedExamples = () => { + return ( +
+ + + +
+ ) +} + +export default FeedExamples diff --git a/src/collections/Message/Message.js b/src/collections/Message/Message.js index 28030143ec..6bb295bef3 100644 --- a/src/collections/Message/Message.js +++ b/src/collections/Message/Message.js @@ -71,7 +71,7 @@ function Message(props) { return (
{dismissIcon} - {icon && createIcon(icon)} + {createIcon(icon)} {(header || content || list) && ( {header && {header}} diff --git a/src/views/Feed/Feed.js b/src/views/Feed/Feed.js new file mode 100644 index 0000000000..5bd547efc2 --- /dev/null +++ b/src/views/Feed/Feed.js @@ -0,0 +1,95 @@ +import _ from 'lodash' +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, + SUI, +} from '../../lib' +import FeedContent from './FeedContent' +import FeedDate from './FeedDate' +import FeedEvent from './FeedEvent' +import FeedExtra from './FeedExtra' +import FeedLabel from './FeedLabel' +import FeedLike from './FeedLike' +import FeedMeta from './FeedMeta' +import FeedSummary from './FeedSummary' +import FeedUser from './FeedUser' + +function Feed(props) { + const { children, className, events, size } = props + const classes = cx('ui', className, size, 'feed') + const rest = getUnhandledProps(Feed, props) + + if (!events) { + return
{children}
+ } + + const eventsJSX = events.map(eventProps => { + const { childKey, date, meta, summary, ...eventData } = eventProps + const finalKey = childKey || [date, meta, summary].join('-') + + return ( + + ) + }) + + return
{eventsJSX}
+} + +Feed._meta = { + name: 'Feed', + type: META.TYPES.VIEW, + props: { + size: _.without(SUI.SIZES, 'mini', 'tiny', 'medium', 'big', 'huge', 'massive'), + }, +} + +Feed.propTypes = { + /** Primary content of the Feed. */ + children: customPropTypes.every([ + customPropTypes.disallow(['events']), + PropTypes.node, + ]), + + /** Classes that will be added to the Feed className. */ + className: PropTypes.string, + + /** Array of props for FeedEvent. */ + events: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.arrayOf(PropTypes.shape({ + childKey: PropTypes.string, + date: PropTypes.string, + image: PropTypes.node, + icon: PropTypes.node, + meta: PropTypes.string, + summary: PropTypes.string, + extraText: PropTypes.string, + extraImages: PropTypes.arrayOf(PropTypes.node), + })), + ]), + + /** A feed can have different sizes. */ + size: PropTypes.oneOf(Feed._meta.props.size), +} + +Feed.Content = FeedContent +Feed.Date = FeedDate +Feed.Event = FeedEvent +Feed.Extra = FeedExtra +Feed.Label = FeedLabel +Feed.Like = FeedLike +Feed.Meta = FeedMeta +Feed.Summary = FeedSummary +Feed.User = FeedUser + +export default Feed diff --git a/src/views/Feed/FeedContent.js b/src/views/Feed/FeedContent.js new file mode 100644 index 0000000000..c3c0e47240 --- /dev/null +++ b/src/views/Feed/FeedContent.js @@ -0,0 +1,75 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' +import FeedDate from './FeedDate' +import FeedExtra from './FeedExtra' +import FeedMeta from './FeedMeta' +import FeedSummary from './FeedSummary' + +function FeedContent(props) { + const { children, content, className, extraImages, extraText, date, meta, summary } = props + const classes = cx(className, 'content') + const rest = getUnhandledProps(FeedContent, props) + + return ( +
+ {date && } + {summary && } + {extraImages && } + {extraText && } + {meta && } + {children || content} +
+ ) +} + +FeedContent._meta = { + name: 'FeedContent', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedContent.propTypes = { + /** Primary content of the FeedContent. */ + children: customPropTypes.every([ + customPropTypes.disallow(['content']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedContent className. */ + className: PropTypes.string, + + /** Primary content of the FeedContent. Mutually exclusive with children. */ + content: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.string, + ]), + + /** An event can contain a date. */ + date: PropTypes.string, + + /** Shorthand for FeedExtra with prop images. */ + extraImages: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.arrayOf(PropTypes.string), + ]), + + /** Shorthand for FeedExtra with prop text. */ + extraText: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.string, + ]), + + /** A shorthand for FeedMeta. */ + meta: PropTypes.string, + + /** A shorthand for FeedSummary. */ + summary: PropTypes.string, +} + +export default FeedContent diff --git a/src/views/Feed/FeedDate.js b/src/views/Feed/FeedDate.js new file mode 100644 index 0000000000..417a6e4d8d --- /dev/null +++ b/src/views/Feed/FeedDate.js @@ -0,0 +1,41 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' + +function FeedDate(props) { + const { children, className, date } = props + const classes = cx(className, 'date') + const rest = getUnhandledProps(FeedDate, props) + + return
{children || date}
+} + +FeedDate._meta = { + name: 'FeedDate', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedDate.propTypes = { + /** Primary content of the FeedDate. Mutually exclusive with the date prop. */ + children: customPropTypes.every([ + customPropTypes.disallow(['date']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedDate className. */ + className: PropTypes.string, + + /** Shorthand for primary content of the FeedDate. Mutually exclusive with the children prop. */ + date: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.string, + ]), +} + +export default FeedDate diff --git a/src/views/Feed/FeedEvent.js b/src/views/Feed/FeedEvent.js new file mode 100644 index 0000000000..2d1decbaaf --- /dev/null +++ b/src/views/Feed/FeedEvent.js @@ -0,0 +1,89 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' +import FeedContent from './FeedContent' +import FeedLabel from './FeedLabel' + +function FeedEvent(props) { + const { content, children, className, date, extraImages, extraText, image, icon, meta, summary } = props + const classes = cx(className, 'event') + const rest = getUnhandledProps(FeedEvent, props) + + const hasContentProp = (content || date || extraImages || extraText || meta || summary) + const contentProps = { content, date, extraImages, extraText, meta, summary } + + return ( +
+ {icon && } + {image && } + {hasContentProp && } + {children} +
+ ) +} + +FeedEvent._meta = { + name: 'FeedEvent', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedEvent.propTypes = { + /** Primary content of the FeedEvent. */ + children: customPropTypes.every([ + customPropTypes.disallow(['content', 'date', 'extraImages', 'extraText', 'meta', 'summary']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedEvent className. */ + className: PropTypes.string, + + /** Shorthand for FeedContent. */ + content: customPropTypes.every([ + customPropTypes.disallow(['children', 'date', 'extraImages', 'extraText', 'meta', 'summary']), + PropTypes.string, + ]), + + /** Shorthand for FeedDate. */ + date: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.string, + ]), + + /** Shorthand for FeedExtra with prop images. */ + extraImages: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.arrayOf(PropTypes.node), + ]), + + /** Shorthand for FeedExtra with prop text. */ + extraText: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.string, + ]), + + /** An event can contain icon label. */ + icon: PropTypes.node, + + /** An event can contain image label. */ + image: PropTypes.node, + + /** Shorthand for FeedMeta. */ + meta: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.string, + ]), + + /** Shorthand for FeedSummary. */ + summary: customPropTypes.every([ + customPropTypes.disallow(['children', 'content']), + PropTypes.string, + ]), +} + +export default FeedEvent diff --git a/src/views/Feed/FeedExtra.js b/src/views/Feed/FeedExtra.js new file mode 100644 index 0000000000..4569227b34 --- /dev/null +++ b/src/views/Feed/FeedExtra.js @@ -0,0 +1,67 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, + useKeyOnly, +} from '../../lib' +import { createImg } from '../../factories' + +function FeedExtra(props) { + const { children, className, images, text } = props + const classes = cx( + className, + useKeyOnly(images, 'images'), + useKeyOnly(text, 'text'), + 'extra' + ) + const rest = getUnhandledProps(FeedExtra, props) + + if (Array.isArray(images)) { + const imagesJSX = images.map((image, index) => { + const key = [index, image].join('-') + + return createImg(image, { key }) + }) + + return
{imagesJSX}
+ } + + return
{children || text}
+} + +FeedExtra._meta = { + name: 'FeedExtra', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedExtra.propTypes = { + /** Primary content of the FeedExtra. */ + children: PropTypes.node, + + /** Classes that will be added to the FeedExtra className. */ + className: PropTypes.string, + + /** An event can contain additional information like a set of images. */ + images: customPropTypes.every([ + customPropTypes.disallow(['text']), + PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.arrayOf(PropTypes.string), + ]), + ]), + + /** An event can contain additional information like a set of images. */ + text: customPropTypes.every([ + customPropTypes.disallow(['images']), + PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.string, + ]), + ]), +} + +export default FeedExtra diff --git a/src/views/Feed/FeedLabel.js b/src/views/Feed/FeedLabel.js new file mode 100644 index 0000000000..5eb591eec2 --- /dev/null +++ b/src/views/Feed/FeedLabel.js @@ -0,0 +1,54 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' +import { createIcon, createImg } from '../../factories' + +function FeedLabel(props) { + const { children, className, icon, image } = props + const classes = cx(className, 'label') + const rest = getUnhandledProps(FeedLabel, props) + + return ( +
+ {children} + {createIcon(icon)} + {createImg(image)} +
+ ) +} + +FeedLabel._meta = { + name: 'FeedLabel', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedLabel.propTypes = { + /** Primary content of the FeedLabel. */ + children: customPropTypes.every([ + customPropTypes.disallow(['icon', 'image']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedLabel className. */ + className: PropTypes.string, + + /** An event can contain icon label. */ + icon: customPropTypes.every([ + customPropTypes.disallow(['children', 'image']), + PropTypes.node, + ]), + + /** An event can contain image label. */ + image: customPropTypes.every([ + customPropTypes.disallow(['children', 'icon']), + PropTypes.node, + ]), +} + +export default FeedLabel diff --git a/src/views/Feed/FeedLike.js b/src/views/Feed/FeedLike.js new file mode 100644 index 0000000000..1d8ec395c0 --- /dev/null +++ b/src/views/Feed/FeedLike.js @@ -0,0 +1,54 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' +import { createIcon } from '../../factories' + +function FeedLike(props) { + const { children, className, icon, like } = props + const classes = cx(className, 'like') + const rest = getUnhandledProps(FeedLike, props) + + return ( + + {createIcon(icon)} + {children || like} + + ) +} + +FeedLike._meta = { + name: 'FeedLike', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedLike.defaultProps = { + icon: 'like', +} + +FeedLike.propTypes = { + /** Primary content of the FeedLike. */ + children: customPropTypes.every([ + customPropTypes.disallow(['like']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedLike className. */ + className: PropTypes.string, + + /** Name of icon for FeedLike. */ + icon: PropTypes.node, + + /** Primary content of the FeedLike, mutually exclusive with children prop. */ + like: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.string, + ]), +} + +export default FeedLike diff --git a/src/views/Feed/FeedMeta.js b/src/views/Feed/FeedMeta.js new file mode 100644 index 0000000000..2e9b83e780 --- /dev/null +++ b/src/views/Feed/FeedMeta.js @@ -0,0 +1,50 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' +import FeedLike from './FeedLike' + +function FeedMeta(props) { + const { children, className, like, meta } = props + const classes = cx(className, 'meta') + const rest = getUnhandledProps(FeedMeta, props) + + return ( +
+ {like && } + {children || meta} +
+ ) +} + +FeedMeta._meta = { + name: 'FeedMeta', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedMeta.propTypes = { + /** Primary content of the FeedMeta. */ + children: customPropTypes.every([ + customPropTypes.disallow(['meta']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedMeta className. */ + className: PropTypes.string, + + /** Shorthand for FeedLike. */ + like: PropTypes.node, + + /** Primary content of the FeedMeta. Mutually exclusive with children. */ + meta: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.string, + ]), +} + +export default FeedMeta diff --git a/src/views/Feed/FeedSummary.js b/src/views/Feed/FeedSummary.js new file mode 100644 index 0000000000..1ec882b1a7 --- /dev/null +++ b/src/views/Feed/FeedSummary.js @@ -0,0 +1,49 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' +import FeedDate from './FeedDate' + +function FeedSummary(props) { + const { children, className, date, summary } = props + const classes = cx(className, 'summary') + const rest = getUnhandledProps(FeedSummary, props) + + return ( +
+ {children || summary } + {date && } +
+ ) +} + +FeedSummary._meta = { + name: 'FeedSummary', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedSummary.propTypes = { + /** Primary content of the FeedSummary. */ + children: customPropTypes.every([ + customPropTypes.disallow(['summary']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedSummary className. */ + className: PropTypes.string, + + /** An event summary can contain a date. */ + date: PropTypes.string, + + summary: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.string, + ]), +} + +export default FeedSummary diff --git a/src/views/Feed/FeedUser.js b/src/views/Feed/FeedUser.js new file mode 100644 index 0000000000..ab20925c22 --- /dev/null +++ b/src/views/Feed/FeedUser.js @@ -0,0 +1,41 @@ +import cx from 'classnames' +import React, { PropTypes } from 'react' + +import { + customPropTypes, + getUnhandledProps, + META, +} from '../../lib' + +function FeedUser(props) { + const { children, className, user } = props + const classes = cx(className, 'user') + const rest = getUnhandledProps(FeedUser, props) + + return {children || user} +} + +FeedUser._meta = { + name: 'FeedUser', + parent: 'Feed', + type: META.TYPES.VIEW, +} + +FeedUser.propTypes = { + /** Primary content of the FeedUser. */ + children: customPropTypes.every([ + customPropTypes.disallow(['user']), + PropTypes.node, + ]), + + /** Classes that will be added to the FeedUser className. */ + className: PropTypes.string, + + /** Shorthand for primary content of the FeedUser. Mutually exclusive with the children prop. */ + user: customPropTypes.every([ + customPropTypes.disallow(['children']), + PropTypes.string, + ]), +} + +export default FeedUser diff --git a/src/views/index.js b/src/views/index.js index 43a1b0d17a..2a89872908 100644 --- a/src/views/index.js +++ b/src/views/index.js @@ -1,2 +1,3 @@ +export { default as Feed } from './Feed/Feed' export { default as Item } from './Item/Item' export { default as Statistic } from './Statistic/Statistic' diff --git a/test/specs/views/Feed/Feed-test.js b/test/specs/views/Feed/Feed-test.js new file mode 100644 index 0000000000..6bf46016ef --- /dev/null +++ b/test/specs/views/Feed/Feed-test.js @@ -0,0 +1,24 @@ +import _ from 'lodash' +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import Feed from 'src/views/Feed/Feed' + +describe('Feed', () => { + common.hasUIClassName(Feed) + common.isConformant(Feed) + common.propValueOnlyToClassName(Feed, 'size') + common.rendersChildren(Feed) + + describe('events prop', () => { + it('renders ', () => { + const events = _.times(3, () => { + return { summary: faker.hacker.phrase() } + }) + + shallow() + .should.have.exactly(3).descendants('FeedEvent') + }) + }) +}) diff --git a/test/specs/views/Feed/FeedContent-test.js b/test/specs/views/Feed/FeedContent-test.js new file mode 100644 index 0000000000..f06e6bd73a --- /dev/null +++ b/test/specs/views/Feed/FeedContent-test.js @@ -0,0 +1,44 @@ +import _ from 'lodash' +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedContent from 'src/views/Feed/FeedContent' + +describe('FeedContent', () => { + common.isConformant(FeedContent) + common.rendersChildren(FeedContent) + + it('renders text with content prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain.text(text) + }) + + it('renders with date prop', () => { + shallow() + .should.have.descendants('FeedDate') + }) + + it('renders with extraImages prop', () => { + const images = _.times(3, () => faker.image.imageUrl()) + + shallow() + .should.have.descendants('FeedExtra') + }) + + it('renders with extraText prop', () => { + shallow() + .should.have.descendants('FeedExtra') + }) + + it('renders with meta prop', () => { + shallow() + .should.have.descendants('FeedMeta') + }) + + it('renders with summary prop', () => { + shallow() + .should.have.descendants('FeedSummary') + }) +}) diff --git a/test/specs/views/Feed/FeedDate-test.js b/test/specs/views/Feed/FeedDate-test.js new file mode 100644 index 0000000000..e5d2ce3070 --- /dev/null +++ b/test/specs/views/Feed/FeedDate-test.js @@ -0,0 +1,16 @@ +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedDate from 'src/views/Feed/FeedDate' + +describe('FeedDate', () => { + common.isConformant(FeedDate) + common.rendersChildren(FeedDate) + + it('renders text with date prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain.text(text) + }) +}) diff --git a/test/specs/views/Feed/FeedEvent-test.js b/test/specs/views/Feed/FeedEvent-test.js new file mode 100644 index 0000000000..088bc4479b --- /dev/null +++ b/test/specs/views/Feed/FeedEvent-test.js @@ -0,0 +1,37 @@ +import _ from 'lodash' +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedEvent from 'src/views/Feed/FeedEvent' + +describe('FeedEvent', () => { + common.isConformant(FeedEvent) + common.rendersChildren(FeedEvent) + + it('renders with icon prop', () => { + shallow() + .should.have.descendants('FeedLabel') + }) + + it('renders with image prop', () => { + shallow() + .should.have.descendants('FeedLabel') + }) + + describe('content props', () => { + it('renders with extraImages prop', () => { + const images = _.times(3, () => faker.image.imageUrl()) + shallow().should.have.descendants('FeedContent') + }) + + it('renders with other content props', () => { + const contentProps = ['content', 'date', 'extraText', 'meta', 'summary'] + + contentProps.forEach(propKey => { + const props = { [propKey]: faker.hacker.phrase() } + shallow().should.have.descendants('FeedContent') + }) + }) + }) +}) diff --git a/test/specs/views/Feed/FeedExtra-test.js b/test/specs/views/Feed/FeedExtra-test.js new file mode 100644 index 0000000000..a69e1ee589 --- /dev/null +++ b/test/specs/views/Feed/FeedExtra-test.js @@ -0,0 +1,25 @@ +import _ from 'lodash' +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedExtra from 'src/views/Feed/FeedExtra' + +describe('FeedExtra', () => { + common.isConformant(FeedExtra) + common.propKeyOnlyToClassName(FeedExtra, 'images') + common.propKeyOnlyToClassName(FeedExtra, 'text') + common.rendersChildren(FeedExtra) + + it('renders text with text prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain(text) + }) + + it('renders with images prop', () => { + const images = _.times(3, () => faker.image.imageUrl()) + + shallow().should.have.exactly(3).descendants('img') + }) +}) diff --git a/test/specs/views/Feed/FeedLabel-test.js b/test/specs/views/Feed/FeedLabel-test.js new file mode 100644 index 0000000000..d3179ca9ac --- /dev/null +++ b/test/specs/views/Feed/FeedLabel-test.js @@ -0,0 +1,30 @@ +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedLabel from 'src/views/Feed/FeedLabel' + +describe('FeedLabel', () => { + common.isConformant(FeedLabel) + common.implementsIconProp(FeedLabel) + common.rendersChildren(FeedLabel) + + describe('image prop', () => { + it('renders with string', () => { + const src = faker.image.imageUrl() + const wrapper = shallow() + + wrapper.should.have.descendants('img') + wrapper.find('img').should.have.prop('src', src) + }) + + it('renders node', () => { + const src = faker.image.imageUrl() + const img = + const wrapper = shallow() + + wrapper.should.have.descendants('img') + wrapper.find('img').should.have.prop('src', src) + }) + }) +}) diff --git a/test/specs/views/Feed/FeedLike-test.js b/test/specs/views/Feed/FeedLike-test.js new file mode 100644 index 0000000000..4ffc290852 --- /dev/null +++ b/test/specs/views/Feed/FeedLike-test.js @@ -0,0 +1,17 @@ +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedLike from 'src/views/Feed/FeedLike' + +describe('FeedLike', () => { + common.isConformant(FeedLike) + common.implementsIconProp(FeedLike) + common.rendersChildren(FeedLike) + + it('renders text with like prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain.text(text) + }) +}) diff --git a/test/specs/views/Feed/FeedMeta-test.js b/test/specs/views/Feed/FeedMeta-test.js new file mode 100644 index 0000000000..5d668023ee --- /dev/null +++ b/test/specs/views/Feed/FeedMeta-test.js @@ -0,0 +1,21 @@ +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedMeta from 'src/views/Feed/FeedMeta' + +describe('FeedMeta', () => { + common.isConformant(FeedMeta) + common.rendersChildren(FeedMeta) + + it('renders with like prop', () => { + shallow() + .should.have.descendants('FeedLike') + }) + + it('renders text with meta prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain.text(text) + }) +}) diff --git a/test/specs/views/Feed/FeedSummary-test.js b/test/specs/views/Feed/FeedSummary-test.js new file mode 100644 index 0000000000..235cd95922 --- /dev/null +++ b/test/specs/views/Feed/FeedSummary-test.js @@ -0,0 +1,21 @@ +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedSummary from 'src/views/Feed/FeedSummary' + +describe('FeedSummary', () => { + common.isConformant(FeedSummary) + common.rendersChildren(FeedSummary) + + it('renders with date prop', () => { + shallow() + .should.have.descendants('FeedDate') + }) + + it('renders text with summary prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain.text(text) + }) +}) diff --git a/test/specs/views/Feed/FeedUser-test.js b/test/specs/views/Feed/FeedUser-test.js new file mode 100644 index 0000000000..54e7f89122 --- /dev/null +++ b/test/specs/views/Feed/FeedUser-test.js @@ -0,0 +1,16 @@ +import faker from 'faker' +import React from 'react' + +import * as common from 'test/specs/commonTests' +import FeedUser from 'src/views/Feed/FeedUser' + +describe('FeedUser', () => { + common.isConformant(FeedUser) + common.rendersChildren(FeedUser) + + it('renders text with user prop', () => { + const text = faker.hacker.phrase() + + shallow().should.contain.text(text) + }) +})