commit
6e5d5df875
23
README.md
23
README.md
|
@ -18,7 +18,7 @@ Follow the [React Native Getting Started Guide](https://facebook.github.io/react
|
||||||
$ brew install watchman
|
$ brew install watchman
|
||||||
$ brew install yarn
|
$ brew install yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
- Clone repository and configure:
|
- Clone repository and configure:
|
||||||
```bash
|
```bash
|
||||||
$ git clone git@github.com:RocketChat/Rocket.Chat.ReactNative.git
|
$ git clone git@github.com:RocketChat/Rocket.Chat.ReactNative.git
|
||||||
|
@ -69,3 +69,24 @@ Follow the [React Native Getting Started Guide](https://facebook.github.io/react
|
||||||
- Start emulator
|
- Start emulator
|
||||||
- Start react packager: `$ react-native start`
|
- Start react packager: `$ react-native start`
|
||||||
- Run in emulator: `$ react-native run-android`
|
- Run in emulator: `$ react-native run-android`
|
||||||
|
|
||||||
|
# Storybook
|
||||||
|
- General requirements
|
||||||
|
- Install storybook
|
||||||
|
```bash
|
||||||
|
$ npm i -g @storybook/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
- Running storybook
|
||||||
|
- Run storybook application
|
||||||
|
```bash
|
||||||
|
$ npm run storybook
|
||||||
|
```
|
||||||
|
- Run application in other shell
|
||||||
|
```bash
|
||||||
|
$ react-native run-ios
|
||||||
|
```
|
||||||
|
- Running storybook on browser to help stories navigation
|
||||||
|
```
|
||||||
|
open http://localhost:7007/
|
||||||
|
```
|
||||||
|
|
|
@ -8,10 +8,10 @@ import avatarInitialsAndColor from '../utils/avatarInitialsAndColor';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
// flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
paddingLeft: 16,
|
paddingLeft: 16,
|
||||||
paddingRight: 56,
|
paddingRight: 16,
|
||||||
height: 56,
|
height: 56,
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
},
|
},
|
||||||
|
@ -23,15 +23,15 @@ const styles = StyleSheet.create({
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
right: 16,
|
paddingLeft: 5,
|
||||||
marginLeft: 16,
|
paddingRight: 5
|
||||||
position: 'absolute'
|
|
||||||
},
|
},
|
||||||
roomName: {
|
roomName: {
|
||||||
flexGrow: 1,
|
flex: 1,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: '#444',
|
color: '#444',
|
||||||
marginLeft: 16
|
marginLeft: 16,
|
||||||
|
marginRight: 4
|
||||||
},
|
},
|
||||||
iconContainer: {
|
iconContainer: {
|
||||||
height: 40,
|
height: 40,
|
||||||
|
@ -52,36 +52,39 @@ const styles = StyleSheet.create({
|
||||||
borderRadius: 20
|
borderRadius: 20
|
||||||
},
|
},
|
||||||
avatarInitials: {
|
avatarInitials: {
|
||||||
fontSize: 22,
|
fontSize: 20,
|
||||||
color: '#ffffff'
|
color: '#ffffff'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class RoomItem extends React.PureComponent {
|
export default class RoomItem extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
item: PropTypes.object.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
unread: PropTypes.number,
|
||||||
baseUrl: PropTypes.string
|
baseUrl: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
get icon() {
|
get icon() {
|
||||||
|
const { type, name, baseUrl } = this.props;
|
||||||
|
|
||||||
const icon = {
|
const icon = {
|
||||||
d: 'at',
|
d: 'at',
|
||||||
c: 'pound',
|
c: 'pound',
|
||||||
p: 'lock',
|
p: 'lock',
|
||||||
l: 'account'
|
l: 'account'
|
||||||
}[this.props.item.t];
|
}[type];
|
||||||
|
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name } = this.props.item;
|
if (type === 'd') {
|
||||||
|
|
||||||
if (this.props.item.t === 'd') {
|
|
||||||
const { initials, color } = avatarInitialsAndColor(name);
|
const { initials, color } = avatarInitialsAndColor(name);
|
||||||
return (
|
return (
|
||||||
<View style={[styles.iconContainer, { backgroundColor: color }]}>
|
<View style={[styles.iconContainer, { backgroundColor: color }]}>
|
||||||
<Text style={styles.avatarInitials}>{initials}</Text>
|
<Text style={styles.avatarInitials}>{initials}</Text>
|
||||||
<CachedImage style={styles.avatar} source={{ uri: `${ this.props.baseUrl }/avatar/${ name }` }} />
|
<CachedImage style={styles.avatar} source={{ uri: `${ baseUrl }/avatar/${ name }` }} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -95,26 +98,30 @@ export default class RoomItem extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNumber = (item) => {
|
renderNumber = (unread) => {
|
||||||
if (item.unread) {
|
if (!unread || unread <= 0) {
|
||||||
return (
|
return;
|
||||||
<Text style={styles.number}>
|
|
||||||
{ item.unread }
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unread >= 1000) {
|
||||||
|
unread = '999+';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text style={styles.number}>
|
||||||
|
{ unread }
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let extraSpace = {};
|
const { unread, name } = this.props;
|
||||||
if (this.props.item.unread) {
|
|
||||||
extraSpace = { paddingRight: 92 };
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, extraSpace]}>
|
<View style={styles.container}>
|
||||||
{this.icon}
|
{this.icon}
|
||||||
<Text style={styles.roomName} ellipsizeMode='tail' numberOfLines={1}>{ this.props.item.name }</Text>
|
<Text style={styles.roomName} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
|
||||||
{this.renderNumber(this.props.item)}
|
{this.renderNumber(unread)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,9 @@ class RoomsListItem extends React.PureComponent {
|
||||||
<TouchableOpacity key={item._id} onPress={() => this.props.onPress(item._id, item)}>
|
<TouchableOpacity key={item._id} onPress={() => this.props.onPress(item._id, item)}>
|
||||||
<RoomItem
|
<RoomItem
|
||||||
id={item._id}
|
id={item._id}
|
||||||
item={item}
|
type={item.t}
|
||||||
|
name={item.name}
|
||||||
|
unread={item.unread}
|
||||||
baseUrl={this.props.baseUrl}
|
baseUrl={this.props.baseUrl}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"ios": "react-native run-ios",
|
"ios": "react-native run-ios",
|
||||||
"log-android": "react-native log-android",
|
"log-android": "react-native log-android",
|
||||||
"android": "react-native run-android"
|
"android": "react-native run-android",
|
||||||
|
"storybook": "storybook start -p 7007"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||||
|
@ -56,7 +57,9 @@
|
||||||
"eslint-plugin-react": "^7.1.0",
|
"eslint-plugin-react": "^7.1.0",
|
||||||
"eslint-plugin-react-native": "^3.0.1",
|
"eslint-plugin-react-native": "^3.0.1",
|
||||||
"jest": "20.0.4",
|
"jest": "20.0.4",
|
||||||
"react-test-renderer": "16.0.0-alpha.12"
|
"react-test-renderer": "16.0.0-alpha.12",
|
||||||
|
"@storybook/react-native": "^3.2.4",
|
||||||
|
"react-dom": "16.0.0-alpha.12"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "react-native"
|
"preset": "react-native"
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
|
|
||||||
|
import '@storybook/addon-actions/register';
|
||||||
|
import '@storybook/addon-links/register';
|
|
@ -0,0 +1,3 @@
|
||||||
|
import StorybookUI from './storybook';
|
||||||
|
|
||||||
|
export default StorybookUI;
|
|
@ -0,0 +1,3 @@
|
||||||
|
import StorybookUI from './storybook';
|
||||||
|
|
||||||
|
export default StorybookUI;
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
|
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { TouchableNativeFeedback } from 'react-native';
|
||||||
|
|
||||||
|
export default function Button(props) {
|
||||||
|
return (
|
||||||
|
<TouchableNativeFeedback onPress={props.onPress}>
|
||||||
|
{props.children}
|
||||||
|
</TouchableNativeFeedback>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button.defaultProps = {
|
||||||
|
children: null,
|
||||||
|
onPress: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
Button.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
onPress: PropTypes.func,
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
|
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { TouchableHighlight } from 'react-native';
|
||||||
|
|
||||||
|
export default function Button(props) {
|
||||||
|
return (
|
||||||
|
<TouchableHighlight onPress={props.onPress}>
|
||||||
|
{props.children}
|
||||||
|
</TouchableHighlight>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button.defaultProps = {
|
||||||
|
children: null,
|
||||||
|
onPress: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
Button.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
onPress: PropTypes.func,
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
|
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import style from './style';
|
||||||
|
|
||||||
|
export default function CenterView(props) {
|
||||||
|
return (
|
||||||
|
<View style={style.main}>
|
||||||
|
{props.children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CenterView.defaultProps = {
|
||||||
|
children: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
CenterView.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
export default {
|
||||||
|
main: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#F5FCFF',
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,65 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ScrollView } from 'react-native';
|
||||||
|
|
||||||
|
import RoomItem from '../../../app/components/RoomItem';
|
||||||
|
|
||||||
|
export default (
|
||||||
|
<ScrollView>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
name='rocket.cat'
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
unread={0}
|
||||||
|
name='rocket.cat'
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
unread={1}
|
||||||
|
name='rocket.cat'
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
unread={9}
|
||||||
|
name="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries"
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
unread={99}
|
||||||
|
name="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries"
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
unread={100}
|
||||||
|
name="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries"
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
unread={100000}
|
||||||
|
name="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries"
|
||||||
|
baseUrl='https://demo.rocket.chat'
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
name='W'
|
||||||
|
unread={-100}
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
name='WW'
|
||||||
|
unread={-100}
|
||||||
|
/>
|
||||||
|
<RoomItem
|
||||||
|
type='d'
|
||||||
|
name=''
|
||||||
|
unread={-100}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
|
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { View, Text, Button } from 'react-native';
|
||||||
|
|
||||||
|
export default class Welcome extends React.Component {
|
||||||
|
styles = {
|
||||||
|
wrapper: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 24,
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
fontSize: 18,
|
||||||
|
marginBottom: 18
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
fontSize: 12,
|
||||||
|
marginBottom: 10,
|
||||||
|
lineHeight: 18
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
showApp(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (this.props.showApp) { this.props.showApp(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View style={this.styles.wrapper}>
|
||||||
|
<Text style={this.styles.header}>Welcome to React Native Storybook</Text>
|
||||||
|
<Text style={this.styles.content}>
|
||||||
|
This is a UI Component development environment for your React Native app. Here you can
|
||||||
|
display and interact with your UI components as stories. A story is a single state of one
|
||||||
|
or more UI components. You can have as many stories as you want. In other words a story is
|
||||||
|
like a visual test case.
|
||||||
|
</Text>
|
||||||
|
<Text style={this.styles.content}>
|
||||||
|
We have added some stories inside the "storybook/stories" directory for examples. Try
|
||||||
|
editing the "storybook/stories/Welcome.js" file to edit this message.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Welcome.defaultProps = {
|
||||||
|
showApp: null
|
||||||
|
};
|
||||||
|
|
||||||
|
Welcome.propTypes = {
|
||||||
|
showApp: PropTypes.func
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
|
|
||||||
|
// import React from 'react';
|
||||||
|
|
||||||
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
// import { action } from '@storybook/addon-actions';
|
||||||
|
// import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
|
import DirectMessage from './Channels/DirectMessage';
|
||||||
|
|
||||||
|
storiesOf('Channel Cell', module).add('Direct Messages', () => DirectMessage);
|
||||||
|
|
||||||
|
// storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
|
||||||
|
|
||||||
|
// storiesOf('Button', module)
|
||||||
|
// .addDecorator(getStory => (
|
||||||
|
// <CenterView>
|
||||||
|
// {getStory()}
|
||||||
|
// </CenterView>
|
||||||
|
// ))
|
||||||
|
// .add('with text', () => (
|
||||||
|
// <Button onPress={action('clicked-text')}>
|
||||||
|
// <Text>Hello Button</Text>
|
||||||
|
// </Button>
|
||||||
|
// ))
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions, global-require */
|
||||||
|
|
||||||
|
import { Navigation } from 'react-native-navigation';
|
||||||
|
import { getStorybookUI, configure } from '@storybook/react-native';
|
||||||
|
|
||||||
|
// import stories
|
||||||
|
configure(() => {
|
||||||
|
require('./stories');
|
||||||
|
}, module);
|
||||||
|
|
||||||
|
// This assumes that storybook is running on the same host as your RN packager,
|
||||||
|
// to set manually use, e.g. host: 'localhost' option
|
||||||
|
const StorybookUI = getStorybookUI({ port: 7007, onDeviceUI: true });
|
||||||
|
Navigation.registerComponent('storybook.UI', () => StorybookUI);
|
||||||
|
Navigation.startSingleScreenApp({
|
||||||
|
screen: {
|
||||||
|
screen: 'storybook.UI',
|
||||||
|
title: 'Storybook',
|
||||||
|
navigatorStyle: {
|
||||||
|
navBarHidden: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default StorybookUI;
|
Loading…
Reference in New Issue