From 3e2673b96297a948fa11ed040b61b81686385a94 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 24 May 2017 23:34:29 +1000 Subject: [PATCH 1/5] Add CRNA support 1. detect CRNA projects 2. simplify RN and CRNA templates by consolidating app into single `index.js` instead of `index.ios.js` and `index.android.js` 3. notify user of extra setup steps in CLI 4. document CRNA idiosyncrasies in README --- app/react-native/readme.md | 41 ++++++++++++++++--- lib/cli/bin/generate.js | 21 ++++++++++ lib/cli/generators/REACT_NATIVE/index.js | 3 +- .../storybook/{index.android.js => index.js} | 1 + .../generators/REACT_NATIVE_SCRIPTS/index.js | 26 ++++++++++++ .../template/storybook/addons.js | 2 + .../template/storybook/index.js} | 2 +- .../storybook/stories/Button/index.android.js | 10 +++++ .../storybook/stories/Button/index.ios.js | 10 +++++ .../storybook/stories/CenterView/index.js | 11 +++++ .../storybook/stories/CenterView/style.js | 8 ++++ .../storybook/stories/Welcome/index.js | 40 ++++++++++++++++++ .../template/storybook/stories/index.js | 25 +++++++++++ lib/cli/lib/detect.js | 4 ++ lib/cli/lib/project_types.js | 1 + 15 files changed, 197 insertions(+), 8 deletions(-) rename lib/cli/generators/REACT_NATIVE/template/storybook/{index.android.js => index.js} (92%) create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/addons.js rename lib/cli/generators/{REACT_NATIVE/template/storybook/index.ios.js => REACT_NATIVE_SCRIPTS/template/storybook/index.js} (80%) create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.android.js create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.ios.js create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/index.js create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/style.js create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Welcome/index.js create mode 100644 lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/index.js diff --git a/app/react-native/readme.md b/app/react-native/readme.md index 5a0bda54c8c1..cccc6dfde3ca 100644 --- a/app/react-native/readme.md +++ b/app/react-native/readme.md @@ -21,7 +21,30 @@ npm -g i @storybook/cli getstorybook ``` -## Start the Storybook +After you have installed, there are additional steps for `create-react-native-app` apps. See the section for details, otherwise skip to [Start Storybook](#start-storybook) +to see the next step. + +## Create React Native App (CRNA) + +If you run `getstorybook` inside a CRNA app, you'll be notified that there is an extra step required to use Storybook. + +The easiest way to use Storybook inside CRNA is to simply replace your App with the Storybook UI, which is possible by replacing `App.js` with a single line of code: + +```js +export default from './storybook'; +``` + +This will get you up and running quickly, but then you lose your app! +There are multiple options here. for example, you can export conditionally: + +```js +import StorybookUI from './storybook'; +module.exports = __DEV__ ? StorybookUI : App; +``` + +Alternatively, `StorybookUI` is simply a RN `View` component that can be embedded anywhere in your RN application, e.g. on a tab or within an admin screen. + +## Start Storybook After initial setup start the storybook server with the storybook npm script. @@ -29,14 +52,22 @@ After initial setup start the storybook server with the storybook npm script. npm run storybook ``` -also start your mobile app with the `react-native` command. +Now, you can open to view your storybook menus in the browser. + +## Start App +To see your Storybook stories on the device, you should start your mobile app for the `` of your choice (typically `ios` or `android`). + +For CRNA apps: +``` +npm run +``` +For RN apps: ``` -react-native run-ios -react-native run-android +react-native run- ``` -Now, you can open to view your storybook. +Once your app is started, changing the selected story in web browser will update the story displayed within your mobile app. ## Learn More diff --git a/lib/cli/bin/generate.js b/lib/cli/bin/generate.js index 5b73e5e03ce3..433414d694f9 100755 --- a/lib/cli/bin/generate.js +++ b/lib/cli/bin/generate.js @@ -61,6 +61,9 @@ const end = () => { logger.log(); }; +const CRNA_DISCUSSION = + 'https://github.com/storybooks/storybook/blob/master/app/react-native/docs/manual-setup.md'; + switch (projectType) { case types.ALREADY_HAS_STORYBOOK: logger.log(); @@ -83,6 +86,24 @@ switch (projectType) { .then(end); break; + case types.REACT_NATIVE_SCRIPTS: + require('../generators/REACT_NATIVE_SCRIPTS') + .then(commandLog('Adding storybook support to your "Create React Native App" app')) + .then(end) + .then(() => { + logger.log(chalk.red('NOTE: CRNA app installation is not 100% automated.')); + logger.log( + 'To quickly run storybook, replace contents of', + chalk.bold('\'./App.js\''), + 'with:\n' + ); + codeLog(["export default from './storybook';"]); + logger.log('\nFor a more complete discussion of options, see:\n'); + logger.log(chalk.cyan(CRNA_DISCUSSION)); + logger.log(); + }); + break; + case types.REACT_NATIVE: require('../generators/REACT_NATIVE') .then(commandLog('Adding storybook support to your "React Native" app')) diff --git a/lib/cli/generators/REACT_NATIVE/index.js b/lib/cli/generators/REACT_NATIVE/index.js index 0b0dd6410153..0d707edafed9 100644 --- a/lib/cli/generators/REACT_NATIVE/index.js +++ b/lib/cli/generators/REACT_NATIVE/index.js @@ -13,8 +13,7 @@ module.exports = latestVersion('@storybook/react-native').then(version => { const projectName = dirname && dirname.slice('ios/'.length, dirname.length - '.xcodeproj'.length - 1); if (projectName) { - shell.sed('-i', '%APP_NAME%', projectName, 'storybook/index.ios.js'); - shell.sed('-i', '%APP_NAME%', projectName, 'storybook/index.android.js'); + shell.sed('-i', '%APP_NAME%', projectName, 'storybook/index.js'); } const packageJson = helpers.getPackageJson(); diff --git a/lib/cli/generators/REACT_NATIVE/template/storybook/index.android.js b/lib/cli/generators/REACT_NATIVE/template/storybook/index.js similarity index 92% rename from lib/cli/generators/REACT_NATIVE/template/storybook/index.android.js rename to lib/cli/generators/REACT_NATIVE/template/storybook/index.js index 72e2ce02ca71..87262c017095 100644 --- a/lib/cli/generators/REACT_NATIVE/template/storybook/index.android.js +++ b/lib/cli/generators/REACT_NATIVE/template/storybook/index.js @@ -8,3 +8,4 @@ configure(() => { const StorybookUI = getStorybookUI({ port: 7007, host: 'localhost' }); AppRegistry.registerComponent('%APP_NAME%', () => StorybookUI); +export default StorybookUI; diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js new file mode 100644 index 000000000000..802e9df0268c --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js @@ -0,0 +1,26 @@ +const mergeDirs = require('merge-dirs').default; +const helpers = require('../../lib/helpers'); +const path = require('path'); +const shell = require('shelljs'); +const latestVersion = require('latest-version'); + +module.exports = latestVersion('@storybook/react-native').then(version => { + // copy all files from the template directory to project directory + mergeDirs(path.resolve(__dirname, 'template/'), '.', 'overwrite'); + + const packageJson = helpers.getPackageJson(); + + packageJson.dependencies = packageJson.dependencies || {}; + packageJson.devDependencies = packageJson.devDependencies || {}; + + packageJson.devDependencies['@storybook/react-native'] = `^${version}`; + + if (!packageJson.dependencies['react-dom'] && !packageJson.devDependencies['react-dom']) { + packageJson.devDependencies['react-dom'] = '^15.5.4'; + } + + packageJson.scripts = packageJson.scripts || {}; + packageJson.scripts['storybook'] = 'storybook start -p 7007'; + + helpers.writePackageJson(packageJson); +}); diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/addons.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/addons.js new file mode 100644 index 000000000000..6aed412d04af --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/addons.js @@ -0,0 +1,2 @@ +import '@storybook/addon-actions/register'; +import '@storybook/addon-links/register'; diff --git a/lib/cli/generators/REACT_NATIVE/template/storybook/index.ios.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js similarity index 80% rename from lib/cli/generators/REACT_NATIVE/template/storybook/index.ios.js rename to lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js index 72e2ce02ca71..9b59334992dd 100644 --- a/lib/cli/generators/REACT_NATIVE/template/storybook/index.ios.js +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/index.js @@ -7,4 +7,4 @@ configure(() => { }, module); const StorybookUI = getStorybookUI({ port: 7007, host: 'localhost' }); -AppRegistry.registerComponent('%APP_NAME%', () => StorybookUI); +export default StorybookUI; diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.android.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.android.js new file mode 100644 index 000000000000..2bdb364d5e7c --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.android.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { TouchableNativeFeedback } from 'react-native'; + +export default function Button(props) { + return ( + + {props.children} + + ); +} diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.ios.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.ios.js new file mode 100644 index 000000000000..0447eb95078c --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Button/index.ios.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { TouchableHighlight } from 'react-native'; + +export default function Button(props) { + return ( + + {props.children} + + ); +} diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/index.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/index.js new file mode 100644 index 000000000000..ab98f2df0a80 --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import { View } from 'react-native'; +import style from './style'; + +export default function CenterView(props) { + return ( + + {props.children} + + ); +} diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/style.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/style.js new file mode 100644 index 000000000000..ff347fd9841f --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/CenterView/style.js @@ -0,0 +1,8 @@ +export default { + main: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#F5FCFF', + }, +}; diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Welcome/index.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Welcome/index.js new file mode 100644 index 000000000000..2305b830c30f --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/Welcome/index.js @@ -0,0 +1,40 @@ +import React 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(e) { + e.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. + + + ); + } +} diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/index.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/index.js new file mode 100644 index 000000000000..63f1a6266717 --- /dev/null +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/template/storybook/stories/index.js @@ -0,0 +1,25 @@ +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/lib/cli/lib/detect.js b/lib/cli/lib/detect.js index ef62f94435a1..0394655e4fd4 100644 --- a/lib/cli/lib/detect.js +++ b/lib/cli/lib/detect.js @@ -34,6 +34,10 @@ module.exports = function detect(options) { return types.REACT_PROJECT; } + if (packageJson.dependencies && packageJson.devDependencies['react-native-scripts']) { + return types.REACT_NATIVE_SCRIPTS; + } + if (packageJson.dependencies && packageJson.dependencies['react-native']) { return types.REACT_NATIVE; } diff --git a/lib/cli/lib/project_types.js b/lib/cli/lib/project_types.js index 1c3bbe537434..e38b0c969481 100644 --- a/lib/cli/lib/project_types.js +++ b/lib/cli/lib/project_types.js @@ -4,6 +4,7 @@ module.exports = { METEOR: 'METEOR', REACT: 'REACT', REACT_NATIVE: 'REACT_NATIVE', + REACT_NATIVE_SCRIPTS: 'REACT_NATIVE_SCRIPTS', REACT_PROJECT: 'REACT_PROJECT', WEBPACK_REACT: 'WEBPACK_REACT', ALREADY_HAS_STORYBOOK: 'ALREADY_HAS_STORYBOOK', From cfe52395d3121855fed88b707e1060682bb2dfd5 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Thu, 25 May 2017 21:51:23 +1000 Subject: [PATCH 2/5] Fix linting and simplify code in CRNA generator --- lib/cli/bin/generate.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/cli/bin/generate.js b/lib/cli/bin/generate.js index 433414d694f9..4ed176a39705 100755 --- a/lib/cli/bin/generate.js +++ b/lib/cli/bin/generate.js @@ -87,16 +87,13 @@ switch (projectType) { break; case types.REACT_NATIVE_SCRIPTS: + const app = chalk.bold('"./App.js"'); require('../generators/REACT_NATIVE_SCRIPTS') .then(commandLog('Adding storybook support to your "Create React Native App" app')) .then(end) .then(() => { logger.log(chalk.red('NOTE: CRNA app installation is not 100% automated.')); - logger.log( - 'To quickly run storybook, replace contents of', - chalk.bold('\'./App.js\''), - 'with:\n' - ); + logger.log(`To quickly run storybook, replace contents of ${app} with:\n`); codeLog(["export default from './storybook';"]); logger.log('\nFor a more complete discussion of options, see:\n'); logger.log(chalk.cyan(CRNA_DISCUSSION)); From fbb5a86d5e7c1b84858f08380a1d137b18919849 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Thu, 25 May 2017 22:06:50 +1000 Subject: [PATCH 3/5] Fix linting err in CRNA script --- lib/cli/bin/generate.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/cli/bin/generate.js b/lib/cli/bin/generate.js index 4ed176a39705..0bb023b1d38f 100755 --- a/lib/cli/bin/generate.js +++ b/lib/cli/bin/generate.js @@ -86,7 +86,7 @@ switch (projectType) { .then(end); break; - case types.REACT_NATIVE_SCRIPTS: + case types.REACT_NATIVE_SCRIPTS: { const app = chalk.bold('"./App.js"'); require('../generators/REACT_NATIVE_SCRIPTS') .then(commandLog('Adding storybook support to your "Create React Native App" app')) @@ -100,6 +100,7 @@ switch (projectType) { logger.log(); }); break; + } case types.REACT_NATIVE: require('../generators/REACT_NATIVE') From b7df231b958adb9d6ae12294fb0c45e375156bb5 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Thu, 25 May 2017 22:09:05 +1000 Subject: [PATCH 4/5] Remove unused variable in CRNA --- lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js b/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js index 802e9df0268c..9712064c96d6 100644 --- a/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js +++ b/lib/cli/generators/REACT_NATIVE_SCRIPTS/index.js @@ -1,7 +1,6 @@ const mergeDirs = require('merge-dirs').default; const helpers = require('../../lib/helpers'); const path = require('path'); -const shell = require('shelljs'); const latestVersion = require('latest-version'); module.exports = latestVersion('@storybook/react-native').then(version => { From 01db55980b899a3b7b7e8fad50c071daa4343ae0 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Fri, 26 May 2017 01:58:27 +1000 Subject: [PATCH 5/5] Suppress lint error --- lib/cli/bin/generate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/bin/generate.js b/lib/cli/bin/generate.js index 0bb023b1d38f..8dce91cf2298 100755 --- a/lib/cli/bin/generate.js +++ b/lib/cli/bin/generate.js @@ -94,7 +94,7 @@ switch (projectType) { .then(() => { logger.log(chalk.red('NOTE: CRNA app installation is not 100% automated.')); logger.log(`To quickly run storybook, replace contents of ${app} with:\n`); - codeLog(["export default from './storybook';"]); + codeLog(["export default from './storybook';"]); // eslint-disable-line logger.log('\nFor a more complete discussion of options, see:\n'); logger.log(chalk.cyan(CRNA_DISCUSSION)); logger.log();