From e846054f4eeecda1362d38b8971cb1fb39b72f62 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Tue, 4 Apr 2017 11:47:56 -0700 Subject: [PATCH] Add 'Tweet' to performance benchmarks --- performance/{modules => }/benchmark.js | 0 performance/components/AppText/index.js | 115 ++++++++++++++ performance/components/AspectRatio/index.js | 40 +++++ .../Box/index.css-modules.js} | 2 +- .../Box/index.glamor.js} | 2 +- .../Box/index.js | 3 +- .../Box/index.platform.js} | 2 +- .../Box/index.styled.js} | 2 +- .../css-modules => components}/Box/styles.css | 0 performance/components/GridView/index.js | 50 ++++++ performance/components/Icons/DirectMessage.js | 20 +++ performance/components/Icons/Heart.js | 19 +++ performance/components/Icons/Reply.js | 19 +++ performance/components/Icons/Retweet.js | 19 +++ performance/components/Icons/styles.js | 15 ++ .../NestedTree/index.js} | 0 performance/components/Tweet/index.js | 144 ++++++++++++++++++ performance/components/TweetAction/index.js | 77 ++++++++++ .../components/TweetActionsBar/index.js | 51 +++++++ performance/components/TweetText/index.js | 28 ++++ performance/components/TweetTextPart/index.js | 112 ++++++++++++++ performance/components/UserAvatar/index.js | 64 ++++++++ performance/components/UserNames/index.js | 56 +++++++ .../View/index.css-modules.js} | 0 .../View/index.glamor.js} | 0 .../View/index.platform.js} | 0 .../View/index.styled.js} | 0 .../View/styles.css | 0 performance/components/css-modules.js | 7 + performance/components/glamor.js | 7 + performance/components/platform.js | 7 + performance/components/react-native-web.js | 9 ++ performance/components/styled-components.js | 7 + performance/components/theme.js | 37 +++++ .../{modules => }/createRenderBenchmark.js | 0 .../implementations/css-modules/index.js | 7 - performance/implementations/glamor/index.js | 7 - performance/implementations/platform/index.js | 7 - .../react-native-web/View/index.js | 2 - .../implementations/react-native-web/index.js | 7 - .../styled-components/index.js | 7 - performance/index.js | 13 +- performance/tests/renderDeepTree.js | 4 +- performance/tests/renderTweet.js | 112 ++++++++++++++ performance/tests/renderWideTree.js | 4 +- 45 files changed, 1032 insertions(+), 52 deletions(-) rename performance/{modules => }/benchmark.js (100%) create mode 100644 performance/components/AppText/index.js create mode 100644 performance/components/AspectRatio/index.js rename performance/{implementations/css-modules/Box/index.js => components/Box/index.css-modules.js} (90%) rename performance/{implementations/glamor/Box/index.js => components/Box/index.glamor.js} (95%) rename performance/{implementations/react-native-web => components}/Box/index.js (90%) rename performance/{implementations/platform/Box/index.js => components/Box/index.platform.js} (89%) rename performance/{implementations/styled-components/Box/index.js => components/Box/index.styled.js} (94%) rename performance/{implementations/css-modules => components}/Box/styles.css (100%) create mode 100644 performance/components/GridView/index.js create mode 100644 performance/components/Icons/DirectMessage.js create mode 100644 performance/components/Icons/Heart.js create mode 100644 performance/components/Icons/Reply.js create mode 100644 performance/components/Icons/Retweet.js create mode 100644 performance/components/Icons/styles.js rename performance/{modules/NestedTree.js => components/NestedTree/index.js} (100%) create mode 100644 performance/components/Tweet/index.js create mode 100644 performance/components/TweetAction/index.js create mode 100644 performance/components/TweetActionsBar/index.js create mode 100644 performance/components/TweetText/index.js create mode 100644 performance/components/TweetTextPart/index.js create mode 100644 performance/components/UserAvatar/index.js create mode 100644 performance/components/UserNames/index.js rename performance/{implementations/css-modules/View/index.js => components/View/index.css-modules.js} (100%) rename performance/{implementations/glamor/View/index.js => components/View/index.glamor.js} (100%) rename performance/{implementations/platform/View/index.js => components/View/index.platform.js} (100%) rename performance/{implementations/styled-components/View/index.js => components/View/index.styled.js} (100%) rename performance/{implementations/css-modules => components}/View/styles.css (100%) create mode 100644 performance/components/css-modules.js create mode 100644 performance/components/glamor.js create mode 100644 performance/components/platform.js create mode 100644 performance/components/react-native-web.js create mode 100644 performance/components/styled-components.js create mode 100644 performance/components/theme.js rename performance/{modules => }/createRenderBenchmark.js (100%) delete mode 100644 performance/implementations/css-modules/index.js delete mode 100644 performance/implementations/glamor/index.js delete mode 100644 performance/implementations/platform/index.js delete mode 100644 performance/implementations/react-native-web/View/index.js delete mode 100644 performance/implementations/react-native-web/index.js delete mode 100644 performance/implementations/styled-components/index.js create mode 100644 performance/tests/renderTweet.js diff --git a/performance/modules/benchmark.js b/performance/benchmark.js similarity index 100% rename from performance/modules/benchmark.js rename to performance/benchmark.js diff --git a/performance/components/AppText/index.js b/performance/components/AppText/index.js new file mode 100644 index 00000000..4447f4db --- /dev/null +++ b/performance/components/AppText/index.js @@ -0,0 +1,115 @@ +import theme from '../theme'; +import React, { PropTypes, PureComponent } from 'react'; +import { StyleSheet, Text } from 'react-native'; + +class AppText extends PureComponent { + static displayName = 'AppText'; + + static propTypes = { + align: PropTypes.oneOf(['center', 'left', 'right']), + color: PropTypes.oneOf(['blue', 'deepGray', 'normal', 'red', 'white']), + fontStyle: PropTypes.oneOf(['normal', 'italic']), + size: PropTypes.oneOf(['small', 'normal', 'large']), + uppercase: PropTypes.bool, + weight: PropTypes.oneOf(['normal', 'bold']) + }; + + render() { + const { + align, + color, + fontStyle, + size, + uppercase, + weight, + ...other + } = this.props; + + const style = [ + styles.root, + align && alignStyles[align], + color && colorStyles[color], + fontStyle && fontStyles[fontStyle], + size && sizeStyles[size], + weight && weightStyles[weight], + uppercase === true && styles.uppercase + ]; + + return ; + } +} + +const styles = StyleSheet.create({ + root: { + fontFamily: theme.fontFamily, + fontSize: theme.fontSize.normal, + fontWeight: 'normal', + lineHeight: theme.createLength(theme.lineHeight), + wordWrap: 'break-word' + }, + uppercase: { + textTransform: 'uppercase' + } +}); + +const alignStyles = StyleSheet.create({ + center: { + textAlign: 'center' + }, + left: { + textAlign: 'left' + }, + right: { + textAlign: 'right' + } +}); + +const colorStyles = StyleSheet.create({ + blue: { + color: theme.colors.blue + }, + deepGray: { + color: theme.colors.deepGray + }, + normal: { + color: theme.colors.textBlack + }, + red: { + color: theme.colors.red + }, + white: { + color: theme.colors.white + } +}); + +const fontStyles = StyleSheet.create({ + normal: { + fontStyle: 'normal' + }, + italic: { + fontStyle: 'italic' + } +}); + +const sizeStyles = StyleSheet.create({ + small: { + fontSize: theme.fontSize.small + }, + normal: { + fontSize: theme.fontSize.normal + }, + large: { + fontSize: theme.fontSize.large + } +}); + +const weightStyles = StyleSheet.create({ + normal: { + fontWeight: '400' + }, + bold: { + fontWeight: 'bold' + } +}); + +export default AppText; diff --git a/performance/components/AspectRatio/index.js b/performance/components/AspectRatio/index.js new file mode 100644 index 00000000..be85624e --- /dev/null +++ b/performance/components/AspectRatio/index.js @@ -0,0 +1,40 @@ +import React, { PureComponent, PropTypes } from 'react'; +import { StyleSheet, View } from 'react-native'; + +class AspectRatio extends PureComponent { + static displayName = 'AspectRatio'; + + static propTypes = { + children: PropTypes.any, + ratio: PropTypes.number, + style: PropTypes.object + }; + + static defaultProps = { + ratio: 1 + }; + + render() { + const { children, ratio, style } = this.props; + const percentage = 100 / ratio; + + return ( + + + {children} + + ); + } +} + +const styles = StyleSheet.create({ + root: { + overflow: 'hidden' + }, + shim: { + display: 'block', + width: '100%' + } +}); + +export default AspectRatio; diff --git a/performance/implementations/css-modules/Box/index.js b/performance/components/Box/index.css-modules.js similarity index 90% rename from performance/implementations/css-modules/Box/index.js rename to performance/components/Box/index.css-modules.js index c0acfa05..7123f1ee 100644 --- a/performance/implementations/css-modules/Box/index.js +++ b/performance/components/Box/index.css-modules.js @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ import classnames from 'classnames'; import React from 'react'; -import View from '../View'; +import View from '../View/index.css-modules'; import styles from './styles.css'; const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => ( diff --git a/performance/implementations/glamor/Box/index.js b/performance/components/Box/index.glamor.js similarity index 95% rename from performance/implementations/glamor/Box/index.js rename to performance/components/Box/index.glamor.js index 822c2ec4..24a324af 100644 --- a/performance/implementations/glamor/Box/index.js +++ b/performance/components/Box/index.glamor.js @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ import { css } from 'glamor'; import React from 'react'; -import View from '../View'; +import View from '../View/index.glamor'; const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => ( ( ( { switch (color) { diff --git a/performance/implementations/css-modules/Box/styles.css b/performance/components/Box/styles.css similarity index 100% rename from performance/implementations/css-modules/Box/styles.css rename to performance/components/Box/styles.css diff --git a/performance/components/GridView/index.js b/performance/components/GridView/index.js new file mode 100644 index 00000000..808f5254 --- /dev/null +++ b/performance/components/GridView/index.js @@ -0,0 +1,50 @@ +import { StyleSheet, View } from 'react-native'; +import React, { Component, PropTypes } from 'react'; +import theme from '../theme'; + +class GridView extends Component { + static displayName = 'GridView'; + + static propTypes = { + children: PropTypes.node, + hasGap: PropTypes.bool, + style: PropTypes.object + }; + + render() { + const { children, hasGap, style, ...other } = this.props; + + return ( + + {React.Children.map(children, child => { + return child && + React.cloneElement(child, { + style: [child.props.style, styles.column, hasGap && styles.hasGapColumn] + }); + })} + + ); + } +} + +const styles = StyleSheet.create({ + root: { + flexDirection: 'row' + }, + /** + * 1. Distribute all space (rather than extra space) + * 2. Prevent wide content from forcing wider flex columns + */ + column: { + flexBasis: 0, // 1 + minWidth: 0 // 2 + }, + hasGap: { + marginHorizontal: theme.createLength(theme.spaceX * (-0.5), 'rem') + }, + hasGapColumn: { + marginHorizontal: theme.createLength(theme.spaceX * 0.5, 'rem') + } +}); + +export default GridView; diff --git a/performance/components/Icons/DirectMessage.js b/performance/components/Icons/DirectMessage.js new file mode 100644 index 00000000..b6aa2161 --- /dev/null +++ b/performance/components/Icons/DirectMessage.js @@ -0,0 +1,20 @@ +import { createDOMElement } from 'react-native'; +import React from 'react'; +import styles from './styles'; + +const IconDirectMessage = props => createDOMElement('svg', { + children: ( + + + + + ), + style: [styles.icon, props.style], + viewBox: '0 0 56 72' +}); + +IconDirectMessage.metadata = { height: 72, width: 56 }; + +export default IconDirectMessage; diff --git a/performance/components/Icons/Heart.js b/performance/components/Icons/Heart.js new file mode 100644 index 00000000..d5695aa1 --- /dev/null +++ b/performance/components/Icons/Heart.js @@ -0,0 +1,19 @@ +import { createDOMElement } from 'react-native'; +import React from 'react'; +import styles from './styles'; + +const IconHeart = props => createDOMElement('svg', { + children: ( + + + + ), + style: [styles.icon, props.style], + viewBox: '0 0 54 72' +}); + +IconHeart.metadata = { height: 72, width: 54 }; + +export default IconHeart; diff --git a/performance/components/Icons/Reply.js b/performance/components/Icons/Reply.js new file mode 100644 index 00000000..699c5fec --- /dev/null +++ b/performance/components/Icons/Reply.js @@ -0,0 +1,19 @@ +import { createDOMElement } from 'react-native'; +import React from 'react'; +import styles from './styles'; + +const IconReply = props => createDOMElement('svg', { + children: ( + + + + ), + style: [styles.icon, props.style], + viewBox: '0 0 62 72' +}); + +IconReply.metadata = { height: 72, width: 62 }; + +export default IconReply; diff --git a/performance/components/Icons/Retweet.js b/performance/components/Icons/Retweet.js new file mode 100644 index 00000000..daff6649 --- /dev/null +++ b/performance/components/Icons/Retweet.js @@ -0,0 +1,19 @@ +import { createDOMElement } from 'react-native'; +import React from 'react'; +import styles from './styles'; + +const IconRetweet = props => createDOMElement('svg', { + children: ( + + + + ), + style: [styles.icon, props.style], + viewBox: '0 0 74 72' +}); + +IconRetweet.metadata = { height: 72, width: 74 }; + +export default IconRetweet; diff --git a/performance/components/Icons/styles.js b/performance/components/Icons/styles.js new file mode 100644 index 00000000..380d7594 --- /dev/null +++ b/performance/components/Icons/styles.js @@ -0,0 +1,15 @@ +import { StyleSheet } from 'react-native'; + +const styles = StyleSheet.create({ + icon: { + display: 'inline-block', + fill: 'currentcolor', + height: '1.25em', + maxWidth: '100%', + position: 'relative', + userSelect: 'none', + verticalAlign: 'text-bottom' + } +}); + +export default styles; diff --git a/performance/modules/NestedTree.js b/performance/components/NestedTree/index.js similarity index 100% rename from performance/modules/NestedTree.js rename to performance/components/NestedTree/index.js diff --git a/performance/components/Tweet/index.js b/performance/components/Tweet/index.js new file mode 100644 index 00000000..fc4a05df --- /dev/null +++ b/performance/components/Tweet/index.js @@ -0,0 +1,144 @@ +import AspectRatio from '../AspectRatio'; +import GridView from '../GridView'; +import TweetActionsBar from '../TweetActionsBar'; +import TweetText from '../TweetText'; +import UserAvatar from '../UserAvatar'; +import UserNames from '../UserNames'; +import { Image, StyleSheet, Text, View } from 'react-native'; +import React, { Component, PropTypes } from 'react'; +import theme from '../theme'; + +export class Tweet extends Component { + static displayName = 'Tweet'; + + static propTypes = { + tweet: PropTypes.object.isRequired + }; + + render() { + const { tweet } = this.props; + const { id, lang, media, textParts, timestamp, user } = tweet; + const { fullName, profileImageUrl, screenName } = user; + + return ( + + + + + + + + + + + + + + + + + + + + {media + ? + + + + + : null} + + + + + + + + ); + } +} + +const styles = StyleSheet.create({ + root: { + paddingVertical: theme.createLength(theme.spaceY * 0.75, 'rem'), + paddingHorizontal: theme.createLength(theme.spaceX, 'rem') + }, + avatarColumn: { + flexGrow: 1, + minWidth: 32 + }, + bodyColumn: { + flexGrow: 7 + }, + row: { + flexDirection: 'row', + justifyContent: 'space-between' + }, + avatarLink: { + display: 'block', + flexShrink: 1, + flexGrow: 0, + width: '100%' + }, + avatar: { + width: '100%' + }, + body: { + marginTop: '-0.15rem' + }, + timestamp: { + color: theme.colors.deepGray, + marginLeft: theme.createLength(theme.spaceX, 'rem'), + order: 1, + textDecorationLine: 'none', + whiteSpace: 'nowrap' + }, + actionBar: { + marginTop: theme.createLength(theme.spaceY * 0.5, 'rem') + }, + richContent: { + borderRadius: '0.35rem', + marginTop: theme.createLength(theme.spaceY * 0.5, 'rem'), + overflow: 'hidden' + }, + media: { + ...StyleSheet.absoluteFillObject, + margin: 'auto', + width: 'auto', + height: 'auto' + } +}); + +export default Tweet; diff --git a/performance/components/TweetAction/index.js b/performance/components/TweetAction/index.js new file mode 100644 index 00000000..d1c4ec4f --- /dev/null +++ b/performance/components/TweetAction/index.js @@ -0,0 +1,77 @@ +import IconReply from '../Icons/Reply'; +import IconHeart from '../Icons/Heart'; +import IconRetweet from '../Icons/Retweet'; +import IconDirectMessage from '../Icons/DirectMessage'; +import { Text, View, StyleSheet } from 'react-native'; +import React, { PropTypes } from 'react'; +import theme from '../theme'; + +const getIcon = (icon, highlighted) => { + switch (icon) { + case 'like': + return ; + case 'reply': + return ; + case 'retweet': + return ; + case 'directMessage': + return ; + default: + return null; + } +}; + +export default class TweetAction extends React.Component { + static displayName = 'TweetAction'; + + static propTypes = { + count: PropTypes.number, + displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']), + highlighted: PropTypes.bool, + onPress: PropTypes.func, + style: PropTypes.object + }; + + render() { + const { count, displayMode, highlighted, onPress, style } = this.props; + + return ( + + + {getIcon(displayMode, highlighted)} + {count > 0 ? {count} : null} + + + ); + } +} + +const styles = StyleSheet.create({ + root: { + minHeight: theme.createLength(theme.lineHeight, 'rem'), + overflow: 'visible', + userSelect: 'none', + whiteSpace: 'nowrap' + }, + inner: { + alignItems: 'center', + color: theme.colors.deepGray, + display: 'flex', + flexDirection: 'row' + }, + count: { + marginLeft: '0.25em' + }, + retweetedColor: { + color: theme.colors.green + }, + likedColor: { + color: theme.colors.red + } +}); diff --git a/performance/components/TweetActionsBar/index.js b/performance/components/TweetActionsBar/index.js new file mode 100644 index 00000000..7ca164d3 --- /dev/null +++ b/performance/components/TweetActionsBar/index.js @@ -0,0 +1,51 @@ +import TweetAction from '../TweetAction'; +import { View, StyleSheet } from 'react-native'; +import React, { PropTypes, PureComponent } from 'react'; + +const actionNames = ['reply', 'retweet', 'like', 'directMessage']; + +export default class TweetActionsBar extends PureComponent { + static propTypes = { + actions: PropTypes.arrayOf( + PropTypes.shape({ + count: PropTypes.number, + label: PropTypes.string, + highlighted: PropTypes.bool, + name: PropTypes.oneOf(actionNames).isRequired, + onPress: PropTypes.func + }) + ), + style: PropTypes.object + }; + + render() { + const { actions, style } = this.props; + + /* eslint-disable react/jsx-handler-names */ + return ( + + {actions.map((action, i) => ( + + ))} + + ); + } +} + +const styles = StyleSheet.create({ + root: { + flexDirection: 'row' + }, + action: { + display: 'block', + marginRight: '10%' + } +}); diff --git a/performance/components/TweetText/index.js b/performance/components/TweetText/index.js new file mode 100644 index 00000000..741c233a --- /dev/null +++ b/performance/components/TweetText/index.js @@ -0,0 +1,28 @@ +import AppText from '../AppText'; +import TweetTextPart from '../TweetTextPart'; +import React, { PropTypes } from 'react'; + +class TweetText extends React.Component { + static displayName = 'TweetText'; + + static propTypes = { + displayMode: TweetTextPart.propTypes.displayMode, + lang: PropTypes.string, + numberOfLines: PropTypes.number, + textParts: PropTypes.array.isRequired + }; + + render() { + const { displayMode, lang, numberOfLines, textParts, ...other } = this.props; + + return ( + + {textParts.map((part, i) => ( + + ))} + + ); + } +} + +export default TweetText; diff --git a/performance/components/TweetTextPart/index.js b/performance/components/TweetTextPart/index.js new file mode 100644 index 00000000..e12245d0 --- /dev/null +++ b/performance/components/TweetTextPart/index.js @@ -0,0 +1,112 @@ +/* eslint-disable react/prop-types */ +import { Image, StyleSheet, Text } from 'react-native'; +import React, { PropTypes } from 'react'; +import theme from '../theme'; + +const createTextEntity = ({ part }) => {`${part.prefix}${part.text}`}; + +const createTwemojiEntity = ({ part }) => ( + +); + +// @mention, #hashtag, $cashtag +const createSymbolEntity = ({ displayMode, part }) => { + const links = displayMode === 'links'; + return ( + + {`${part.prefix}${part.text}`} + + ); +}; + +// internal links +const createLinkEntity = ({ displayMode, part }) => { + const { displayUrl, linkRelation, url } = part; + const links = displayMode === 'links'; + + return ( + + {displayUrl} + + ); +}; + +// external links +const createExternalLinkEntity = ({ displayMode, part }) => { + const { displayUrl, linkRelation, url } = part; + const links = displayMode === 'links'; + + return ( + + {displayUrl} + + ); +}; + +class TweetTextPart extends React.Component { + static displayName = 'TweetTextPart'; + + static propTypes = { + displayMode: PropTypes.oneOf(['links', 'no-links']), + part: PropTypes.object + }; + + static defaultProps = { + displayMode: 'links' + }; + + render() { + let renderer; + const { isEmoji, isEntity, isHashtag, isMention, isMedia, isUrl } = this.props.part; + + if (isEmoji || isEntity || isUrl || isMedia) { + if (isUrl) { + renderer = createExternalLinkEntity; + } else if (isHashtag || isMention) { + renderer = createSymbolEntity; + } else if (isEmoji) { + renderer = createTwemojiEntity; + } else { + renderer = createLinkEntity; + } + } else { + renderer = createTextEntity; + } + + return renderer(this.props); + } +} + +const styles = StyleSheet.create({ + link: { + color: theme.colors.blue, + textDecorationLine: 'none', + unicodeBidi: 'embed' + }, + twemoji: { + display: 'inline-block', + height: '1.25em', + width: '1.25em', + paddingRight: '0.05em', + paddingLeft: '0.1em', + verticalAlign: '-0.2em' + } +}); + +export default TweetTextPart; diff --git a/performance/components/UserAvatar/index.js b/performance/components/UserAvatar/index.js new file mode 100644 index 00000000..d35c01a9 --- /dev/null +++ b/performance/components/UserAvatar/index.js @@ -0,0 +1,64 @@ +import AspectRatio from '../AspectRatio'; +import { Image, StyleSheet } from 'react-native'; +import React, { PropTypes, PureComponent } from 'react'; +import theme from '../theme'; + +class UserAvatar extends PureComponent { + static displayName = 'UserAvatar'; + + static propTypes = { + accessibilityLabel: PropTypes.string, + circle: PropTypes.bool, + style: PropTypes.object, + uri: PropTypes.string + }; + + static defaultProps = { + circle: false + }; + + render() { + const { accessibilityLabel, circle, style, uri } = this.props; + + return ( + + {uri + ? + : null} + + ); + } + + _handleLoad = () => { + this._imageRef && this._imageRef.setNativeProps(nativeProps); + }; + + _setImageRef = component => { + this._imageRef = component; + }; +} + +const nativeProps = { style: { backgroundColor: '#fff' } }; + +const styles = StyleSheet.create({ + root: { + borderRadius: '0.35rem' + }, + circle: { + borderRadius: '9999px' + }, + image: { + backgroundColor: theme.colors.fadedGray, + display: 'block', + height: '100%', + width: '100%' + } +}); + +export default UserAvatar; diff --git a/performance/components/UserNames/index.js b/performance/components/UserNames/index.js new file mode 100644 index 00000000..cce85ac4 --- /dev/null +++ b/performance/components/UserNames/index.js @@ -0,0 +1,56 @@ +import AppText from '../AppText'; +import { StyleSheet } from 'react-native'; +import React, { PropTypes, PureComponent } from 'react'; + +class UserNames extends PureComponent { + static displayName = 'UserNames'; + + static propTypes = { + fullName: PropTypes.string, + layout: PropTypes.oneOf(['nowrap', 'stack']), + onPress: PropTypes.func, + screenName: PropTypes.string, + style: PropTypes.object + }; + + static defaultProps = { + layout: 'nowrap' + }; + + render() { + const { + fullName, + layout, + onPress, + screenName, + style, + ...other + } = this.props; + + return ( + + {fullName} + {layout === 'stack' ? ' \u000A' : ' '} + {`@${screenName}`} + + ); + } +} + +const styles = StyleSheet.create({ + root: { + display: 'inline-block' + }, + screenName: { + unicodeBidi: 'embed', + writingDirection: 'ltr' + } +}); + +export default UserNames; diff --git a/performance/implementations/css-modules/View/index.js b/performance/components/View/index.css-modules.js similarity index 100% rename from performance/implementations/css-modules/View/index.js rename to performance/components/View/index.css-modules.js diff --git a/performance/implementations/glamor/View/index.js b/performance/components/View/index.glamor.js similarity index 100% rename from performance/implementations/glamor/View/index.js rename to performance/components/View/index.glamor.js diff --git a/performance/implementations/platform/View/index.js b/performance/components/View/index.platform.js similarity index 100% rename from performance/implementations/platform/View/index.js rename to performance/components/View/index.platform.js diff --git a/performance/implementations/styled-components/View/index.js b/performance/components/View/index.styled.js similarity index 100% rename from performance/implementations/styled-components/View/index.js rename to performance/components/View/index.styled.js diff --git a/performance/implementations/css-modules/View/styles.css b/performance/components/View/styles.css similarity index 100% rename from performance/implementations/css-modules/View/styles.css rename to performance/components/View/styles.css diff --git a/performance/components/css-modules.js b/performance/components/css-modules.js new file mode 100644 index 00000000..d2b2bcad --- /dev/null +++ b/performance/components/css-modules.js @@ -0,0 +1,7 @@ +import Box from './Box/index.css-modules'; +import View from './View/index.css-modules'; + +export default { + Box, + View +}; diff --git a/performance/components/glamor.js b/performance/components/glamor.js new file mode 100644 index 00000000..5bcb010b --- /dev/null +++ b/performance/components/glamor.js @@ -0,0 +1,7 @@ +import Box from './Box/index.glamor'; +import View from './View/index.glamor'; + +export default { + Box, + View +}; diff --git a/performance/components/platform.js b/performance/components/platform.js new file mode 100644 index 00000000..57cc08e0 --- /dev/null +++ b/performance/components/platform.js @@ -0,0 +1,7 @@ +import Box from './Box/index.platform'; +import View from './View/index.platform'; + +export default { + Box, + View +}; diff --git a/performance/components/react-native-web.js b/performance/components/react-native-web.js new file mode 100644 index 00000000..fdafe21a --- /dev/null +++ b/performance/components/react-native-web.js @@ -0,0 +1,9 @@ +import Box from './Box'; +import Tweet from './Tweet'; +import { View } from 'react-native'; + +export default { + Box, + Tweet, + View +}; diff --git a/performance/components/styled-components.js b/performance/components/styled-components.js new file mode 100644 index 00000000..87dbebec --- /dev/null +++ b/performance/components/styled-components.js @@ -0,0 +1,7 @@ +import Box from './Box/index.styled'; +import View from './View/index.styled'; + +export default { + Box, + View +}; diff --git a/performance/components/theme.js b/performance/components/theme.js new file mode 100644 index 00000000..08ed60e3 --- /dev/null +++ b/performance/components/theme.js @@ -0,0 +1,37 @@ +const colors = { + blue: '#1B95E0', + lightBlue: '#71C9F8', + green: '#17BF63', + orange: '#F45D22', + purple: '#794BC4', + red: '#E0245E', + white: '#FFFFFF', + yellow: '#FFAD1F', + deepGray: '#657786', + fadedGray: '#E6ECF0', + faintGray: '#F5F8FA', + gray: '#AAB8C2', + lightGray: '#CCD6DD', + textBlack: '#14171A' +}; + +const fontSize = { + root: '14px', + // font scale + small: '0.85rem', + normal: '1rem', + large: '1.25rem' +}; + +module.exports = { + colors, + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' + + '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts + fontSize, + lineHeight: 1.3125, + spaceX: 0.6, + spaceY: 1.3125, + createLength(num, unit) { + return `${num}${unit}`; + } +}; diff --git a/performance/modules/createRenderBenchmark.js b/performance/createRenderBenchmark.js similarity index 100% rename from performance/modules/createRenderBenchmark.js rename to performance/createRenderBenchmark.js diff --git a/performance/implementations/css-modules/index.js b/performance/implementations/css-modules/index.js deleted file mode 100644 index 0ca161ab..00000000 --- a/performance/implementations/css-modules/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import Box from './Box'; -import View from './View'; - -export default { - Box, - View -}; diff --git a/performance/implementations/glamor/index.js b/performance/implementations/glamor/index.js deleted file mode 100644 index 0ca161ab..00000000 --- a/performance/implementations/glamor/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import Box from './Box'; -import View from './View'; - -export default { - Box, - View -}; diff --git a/performance/implementations/platform/index.js b/performance/implementations/platform/index.js deleted file mode 100644 index 0ca161ab..00000000 --- a/performance/implementations/platform/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import Box from './Box'; -import View from './View'; - -export default { - Box, - View -}; diff --git a/performance/implementations/react-native-web/View/index.js b/performance/implementations/react-native-web/View/index.js deleted file mode 100644 index a7c7f7dd..00000000 --- a/performance/implementations/react-native-web/View/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import View from 'react-native/components/View'; -export default View; diff --git a/performance/implementations/react-native-web/index.js b/performance/implementations/react-native-web/index.js deleted file mode 100644 index 0ca161ab..00000000 --- a/performance/implementations/react-native-web/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import Box from './Box'; -import View from './View'; - -export default { - Box, - View -}; diff --git a/performance/implementations/styled-components/index.js b/performance/implementations/styled-components/index.js deleted file mode 100644 index 0ca161ab..00000000 --- a/performance/implementations/styled-components/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import Box from './Box'; -import View from './View'; - -export default { - Box, - View -}; diff --git a/performance/index.js b/performance/index.js index 5eb7f99f..9a55d141 100644 --- a/performance/index.js +++ b/performance/index.js @@ -1,13 +1,16 @@ -import cssModules from './implementations/css-modules'; -import glamor from './implementations/glamor'; -import platform from './implementations/platform'; -import reactNative from './implementations/react-native-web'; -import styledComponents from './implementations/styled-components'; +import cssModules from './components/css-modules'; +import glamor from './components/glamor'; +import platform from './components/platform'; +import reactNative from './components/react-native-web'; +import styledComponents from './components/styled-components'; import renderDeepTree from './tests/renderDeepTree'; +import renderTweet from './tests/renderTweet'; import renderWideTree from './tests/renderWideTree'; const tests = [ + // tweet + () => renderTweet('react-native-web', reactNative), // deep tree () => renderDeepTree('platform', platform), () => renderDeepTree('css-modules', cssModules), diff --git a/performance/tests/renderDeepTree.js b/performance/tests/renderDeepTree.js index 6deecfe5..dfd51031 100644 --- a/performance/tests/renderDeepTree.js +++ b/performance/tests/renderDeepTree.js @@ -1,5 +1,5 @@ -import createRenderBenchmark from '../modules/createRenderBenchmark'; -import NestedTree from '../modules/NestedTree'; +import createRenderBenchmark from '../createRenderBenchmark'; +import NestedTree from '../components/NestedTree'; import React from 'react'; const renderDeepTree = (label, components) => createRenderBenchmark({ diff --git a/performance/tests/renderTweet.js b/performance/tests/renderTweet.js new file mode 100644 index 00000000..2e0086f9 --- /dev/null +++ b/performance/tests/renderTweet.js @@ -0,0 +1,112 @@ +import createRenderBenchmark from '../createRenderBenchmark'; +import Tweet from '../components/Tweet'; +import React from 'react'; + +const tweet1 = { + favorite_count: 30, + favorited: true, + id: '834889712556875776', + lang: 'en', + retweet_count: 6, + retweeted: false, + textParts: [ + { + prefix: '', + text: 'Living burrito to burrito ' + }, + { + emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg', + isEmoji: true, + prefix: '', + text: '🌯' + }, + { + emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg', + isEmoji: true, + prefix: '', + text: '🌯' + }, + { + emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg', + isEmoji: true, + prefix: '', + text: '🌯' + } + ], + timestamp: 'Feb 23', + user: { + fullName: 'Nicolas', + screenName: 'necolas', + profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg' + } +}; + +const tweet2 = { + favorite_count: 84, + favorited: false, + id: '730896800060579840', + lang: 'en', + media: { + source: { + uri: 'https://pbs.twimg.com/media/CiSqvsJVEAAtLZ1.jpg', + width: 600, + height: 338 + } + }, + retweet_count: 4, + retweeted: true, + textParts: [ + { + prefix: '', + text: 'Presenting ' + }, + { + displayUrl: 'mobile.twitter.com', + expandedUrl: 'https://mobile.twitter.com', + isEntity: true, + isUrl: true, + linkRelation: 'nofollow', + prefix: '', + text: '', + textDirection: 'ltr', + url: 'https://t.co/4hRCAxiUUG' + }, + { + prefix: '', + text: ' with ' + }, + { + isEntity: true, + isMention: true, + prefix: '@', + text: 'davidbellona', + textDirection: 'ltr', + url: '/davidbellona' + }, + { + prefix: '', + text: " at Twitter's all hands meeting " + } + ], + timestamp: 'May 12', + user: { + fullName: 'Nicolas', + screenName: 'necolas', + profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg' + } +}; + +const renderTweet = (label, components) => createRenderBenchmark({ + name: `Tweet [${label}]`, + runs: 10, + getElement() { + return ( +
+ + +
+ ); + } +}); + +export default renderTweet; diff --git a/performance/tests/renderWideTree.js b/performance/tests/renderWideTree.js index 731256a7..3c6dcf84 100644 --- a/performance/tests/renderWideTree.js +++ b/performance/tests/renderWideTree.js @@ -1,5 +1,5 @@ -import createRenderBenchmark from '../modules/createRenderBenchmark'; -import NestedTree from '../modules/NestedTree'; +import createRenderBenchmark from '../createRenderBenchmark'; +import NestedTree from '../components/NestedTree'; import React from 'react'; const renderWideTree = (label, components) => createRenderBenchmark({