diff --git a/app/react-native/package.json b/app/react-native/package.json index 93c59b79be54..a4ed6233ccc3 100644 --- a/app/react-native/package.json +++ b/app/react-native/package.json @@ -59,6 +59,7 @@ "react-native-compat": "0.0.2", "shelljs": "^0.7.8", "style-loader": "^0.17.0", + "url-parse": "^1.1.9", "url-loader": "^0.5.8", "util-deprecate": "^1.0.2", "uuid": "^3.1.0", diff --git a/app/react-native/src/preview/index.js b/app/react-native/src/preview/index.js index e54a07a38d8a..0d8cdf0ffe3f 100644 --- a/app/react-native/src/preview/index.js +++ b/app/react-native/src/preview/index.js @@ -1,6 +1,8 @@ /* eslint no-underscore-dangle: 0 */ import React from 'react'; +import { NativeModules } from 'react-native'; +import parse from 'url-parse'; import addons from '@storybook/addons'; import createChannel from '@storybook/channel-websocket'; import { EventEmitter } from 'events'; @@ -55,8 +57,7 @@ export default class Preview { let webUrl = null; let channel = addons.getChannel(); if (params.resetStorybook || !channel) { - const host = params.host || 'localhost'; - + const host = params.host || parse(NativeModules.SourceCode.scriptURL).hostname; const port = params.port !== false ? `:${params.port || 7007}` : ''; const query = params.query || ''; diff --git a/examples/crna-kitchen-sink/.babelrc b/examples/crna-kitchen-sink/.babelrc new file mode 100644 index 000000000000..2bcd546dbfd2 --- /dev/null +++ b/examples/crna-kitchen-sink/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": ["babel-preset-expo"], + "env": { + "development": { + "plugins": ["transform-react-jsx-source"] + } + } +} diff --git a/examples/crna-kitchen-sink/.flowconfig b/examples/crna-kitchen-sink/.flowconfig new file mode 100644 index 000000000000..bfe0bb12ccc0 --- /dev/null +++ b/examples/crna-kitchen-sink/.flowconfig @@ -0,0 +1,63 @@ +[ignore] +; We fork some components by platform +.*/*[.]android.js + +; Ignore "BUCK" generated dirs +/\.buckd/ + +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* + +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +.*/Libraries/react-native/React.js +.*/Libraries/react-native/ReactNative.js + +; Additional create-react-native-app ignores + +; Ignore duplicate module providers +.*/node_modules/fbemitter/lib/* + +; Ignore misbehaving dev-dependencies +.*/node_modules/xdl/build/* +.*/node_modules/reqwest/tests/* + +; Ignore missing expo-sdk dependencies (temporarily) +; https://github.com/expo/expo/issues/162 +.*/node_modules/expo/src/* + +; Ignore react-native-fbads dependency of the expo sdk +.*/node_modules/react-native-fbads/* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow +flow/ + +[options] +module.system=haste + +emoji=true + +experimental.strict_type_args=true + +munge_underscores=true + +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FixMe + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-7]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-7]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +unsafe.enable_getters_and_setters=true + +[version] +^0.47.0 diff --git a/examples/crna-kitchen-sink/.gitignore b/examples/crna-kitchen-sink/.gitignore new file mode 100644 index 000000000000..1025e17bd3c6 --- /dev/null +++ b/examples/crna-kitchen-sink/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.expo/ +npm-debug.* diff --git a/examples/crna-kitchen-sink/.watchmanconfig b/examples/crna-kitchen-sink/.watchmanconfig new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/examples/crna-kitchen-sink/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/examples/crna-kitchen-sink/App.js b/examples/crna-kitchen-sink/App.js new file mode 100644 index 000000000000..cf84ee1adb00 --- /dev/null +++ b/examples/crna-kitchen-sink/App.js @@ -0,0 +1,29 @@ +export default from './storybook'; + +// NOTE: The code below is what CRNA generates out of the box. We currently +// have no clever way of replacing this with Storybook's UI (Vanilla RN does!) +// so for now we just replace the code outright. Keeping this here for clarity. +// +// import React from 'react'; +// import { StyleSheet, Text, View } from 'react-native'; +// +// export default class App extends React.Component { +// render() { +// return ( +// +// Open up App.js to start working on your app! +// Changes you make will automatically reload. +// Shake your phone to open the developer menu. +// +// ); +// } +// } +// +// const styles = StyleSheet.create({ +// container: { +// flex: 1, +// backgroundColor: '#fff', +// alignItems: 'center', +// justifyContent: 'center', +// }, +// }); diff --git a/examples/crna-kitchen-sink/App.test.js b/examples/crna-kitchen-sink/App.test.js new file mode 100644 index 000000000000..0b2ae61c645f --- /dev/null +++ b/examples/crna-kitchen-sink/App.test.js @@ -0,0 +1,9 @@ +import renderer from 'react-test-renderer'; + +import React from 'react'; +import App from './App'; + +it('renders without crashing', () => { + const rendered = renderer.create().toJSON(); + expect(rendered).toBeTruthy(); +}); diff --git a/examples/crna-kitchen-sink/README.md b/examples/crna-kitchen-sink/README.md new file mode 100644 index 000000000000..56849c877ef7 --- /dev/null +++ b/examples/crna-kitchen-sink/README.md @@ -0,0 +1,3 @@ +# CRNA Kitchen Sink + +This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app) and storybook using [getstorybook](https://www.npmjs.com/package/@storybook/cli). diff --git a/examples/crna-kitchen-sink/app.json b/examples/crna-kitchen-sink/app.json new file mode 100644 index 000000000000..150dcb6e1a82 --- /dev/null +++ b/examples/crna-kitchen-sink/app.json @@ -0,0 +1,5 @@ +{ + "expo": { + "sdkVersion": "19.0.0" + } +} diff --git a/examples/crna-kitchen-sink/package.json b/examples/crna-kitchen-sink/package.json new file mode 100644 index 000000000000..d95f8fab950c --- /dev/null +++ b/examples/crna-kitchen-sink/package.json @@ -0,0 +1,36 @@ +{ + "name": "crna-kitchen-sink", + "version": "0.1.0", + "private": true, + "devDependencies": { + "@storybook/addon-actions": "file:../../addons/actions", + "@storybook/addon-links": "file:../../addons/links", + "@storybook/addon-options": "file:../../addons/options", + "@storybook/addon-storyshots": "file:../../addons/storyshots", + "@storybook/addons": "file:../../lib/addons", + "@storybook/channels": "file:../../lib/channels", + "@storybook/channel-postmessage": "file:../../lib/channel-postmessage", + "@storybook/react-native": "file:../../app/react-native", + "@storybook/ui": "file:../../lib/ui", + "react-native-scripts": "1.1.0", + "jest-expo": "~19.0.0", + "react-test-renderer": "16.0.0-alpha.12" + }, + "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js", + "scripts": { + "start": "react-native-scripts start", + "eject": "react-native-scripts eject", + "android": "react-native-scripts android", + "ios": "react-native-scripts ios", + "test": "node node_modules/jest/bin/jest.js --watch", + "storybook": "storybook start -p 7007" + }, + "jest": { + "preset": "jest-expo" + }, + "dependencies": { + "expo": "^19.0.0", + "react": "16.0.0-alpha.12", + "react-native": "^0.46.1" + } +} diff --git a/examples/crna-kitchen-sink/storybook/addons.js b/examples/crna-kitchen-sink/storybook/addons.js new file mode 100644 index 000000000000..f649113317fc --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/addons.js @@ -0,0 +1,3 @@ +import '@storybook/addon-actions/register'; +import '@storybook/addon-links/register'; +import '@storybook/addon-options/register'; diff --git a/examples/crna-kitchen-sink/storybook/index.js b/examples/crna-kitchen-sink/storybook/index.js new file mode 100644 index 000000000000..112638e950a1 --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/index.js @@ -0,0 +1,24 @@ +import { getStorybookUI, configure } from '@storybook/react-native'; +import { setOptions } from '@storybook/addon-options'; + +// import stories +configure(() => { + // eslint-disable-next-line global-require + require('./stories'); +}, module); + +const StorybookUI = getStorybookUI({ + port: 7007, + onDeviceUI: true, +}); + +setTimeout( + () => + setOptions({ + name: 'CRNA React Native App', + onDeviceUI: true, + }), + 100 +); + +export default StorybookUI; diff --git a/examples/crna-kitchen-sink/storybook/stories/Button/index.android.js b/examples/crna-kitchen-sink/storybook/stories/Button/index.android.js new file mode 100644 index 000000000000..7a285bc65e46 --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/stories/Button/index.android.js @@ -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 ( + + {props.children} + + ); +} + +Button.defaultProps = { + children: null, + onPress: () => {}, +}; + +Button.propTypes = { + children: PropTypes.node, + onPress: PropTypes.func, +}; diff --git a/examples/crna-kitchen-sink/storybook/stories/Button/index.ios.js b/examples/crna-kitchen-sink/storybook/stories/Button/index.ios.js new file mode 100644 index 000000000000..035cacc61e99 --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/stories/Button/index.ios.js @@ -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 ( + + {props.children} + + ); +} + +Button.defaultProps = { + children: null, + onPress: () => {}, +}; + +Button.propTypes = { + children: PropTypes.node, + onPress: PropTypes.func, +}; diff --git a/examples/crna-kitchen-sink/storybook/stories/CenterView/index.js b/examples/crna-kitchen-sink/storybook/stories/CenterView/index.js new file mode 100644 index 000000000000..e6c8fc60ee19 --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/stories/CenterView/index.js @@ -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 ( + + {props.children} + + ); +} + +CenterView.defaultProps = { + children: null, +}; + +CenterView.propTypes = { + children: PropTypes.node, +}; diff --git a/examples/crna-kitchen-sink/storybook/stories/CenterView/style.js b/examples/crna-kitchen-sink/storybook/stories/CenterView/style.js new file mode 100644 index 000000000000..ff347fd9841f --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/stories/CenterView/style.js @@ -0,0 +1,8 @@ +export default { + main: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#F5FCFF', + }, +}; diff --git a/examples/crna-kitchen-sink/storybook/stories/Welcome/index.js b/examples/crna-kitchen-sink/storybook/stories/Welcome/index.js new file mode 100644 index 000000000000..1106a2ba934d --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/stories/Welcome/index.js @@ -0,0 +1,54 @@ +/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ + +import React, { PropTypes } from 'react'; +import { View, Text } 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 ( + + Welcome to React Native Storybook + + 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. + + + We have added some stories inside the "storybook/stories" directory for examples. Try + editing the "storybook/stories/Welcome.js" file to edit this message. + + + ); + } +} + +Welcome.defaultProps = { + showApp: null, +}; + +Welcome.propTypes = { + showApp: PropTypes.func, +}; diff --git a/examples/crna-kitchen-sink/storybook/stories/index.js b/examples/crna-kitchen-sink/storybook/stories/index.js new file mode 100644 index 000000000000..99ab2205a9d0 --- /dev/null +++ b/examples/crna-kitchen-sink/storybook/stories/index.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { Text } from 'react-native'; + +import { storiesOf } from '@storybook/react-native'; +import { action } from '@storybook/addon-actions'; +import { linkTo } from '@storybook/addon-links'; + +import Button from './Button'; +import CenterView from './CenterView'; +import Welcome from './Welcome'; + +storiesOf('Welcome', module).add('to Storybook', () => ); + +storiesOf('Button', module) + .addDecorator(getStory => + + {getStory()} + + ) + .add('with text', () => + + ) + .add('with some emoji', () => + + ); diff --git a/examples/react-native-vanilla/storybook/storybook.js b/examples/react-native-vanilla/storybook/storybook.js index f9f44af13664..22cec04d709a 100644 --- a/examples/react-native-vanilla/storybook/storybook.js +++ b/examples/react-native-vanilla/storybook/storybook.js @@ -10,9 +10,7 @@ configure(() => { const StorybookUI = getStorybookUI({ port: 7007, - host: 'localhost', onDeviceUI: true, - resetStorybook: true, }); setTimeout( diff --git a/lerna.json b/lerna.json index f0ef0425f12c..dfc344d02301 100644 --- a/lerna.json +++ b/lerna.json @@ -3,6 +3,7 @@ "commands": { "bootstrap": { "ignore": [ + "crna-kitchen-sink", "test-cra", "react-native-vanilla" ] @@ -10,6 +11,7 @@ "publish": { "ignore": [ "cra-kitchen-sink", + "crna-kitchen-sink", "test-cra", "react-native-vanilla", "vue-example", diff --git a/lib/cli/generators/REACT_NATIVE/template/storybook/storybook.js b/lib/cli/generators/REACT_NATIVE/template/storybook/storybook.js index df5905385fa0..a15d8fefb124 100644 --- a/lib/cli/generators/REACT_NATIVE/template/storybook/storybook.js +++ b/lib/cli/generators/REACT_NATIVE/template/storybook/storybook.js @@ -8,6 +8,8 @@ configure(() => { require('./stories'); }, module); -const StorybookUI = getStorybookUI({ port: 7007, host: 'localhost' }); +// 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 }); AppRegistry.registerComponent('%APP_NAME%', () => StorybookUI); export default StorybookUI; diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js index ec0bbf0a0107..62a3819b56b4 100644 --- a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js @@ -5,5 +5,7 @@ configure(() => { require('./stories'); }, module); -const StorybookUI = getStorybookUI({ port: 7007, host: 'localhost' }); +// 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 }); export default StorybookUI; diff --git a/package.json b/package.json index c7321376abd7..a3446c912f72 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "bootstrap": "lerna bootstrap --concurrency 1 --npm-client=\"yarn\" --hoist && node ./scripts/hoist-internals.js", "bootstrap:docs": "cd docs && yarn install", "bootstrap:react-native-vanilla": "lerna exec --scope react-native-vanilla -- yarn install", + "bootstrap:crna-kitchen-sink": "lerna exec --scope crna-kitchen-sink -- yarn install", "bootstrap:test-cra": "npm run build-packs && lerna exec --scope test-cra -- yarn install", "build-packs": "lerna exec --scope '@storybook/*' --parallel -- ../../scripts/build-pack.sh ../../packs", "changelog": "pr-log --sloppy",