Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
feat(project): Folder restructure AKA "The Flattening" (#142) by @rob…
Browse files Browse the repository at this point in the history
…inheinze

BREAKING CHANGE:

* Re-organized folder structure in boilerplate
* Adjusted generators for new folder structure

Other notable changes:

* Updated READMEs to reflect new folder structure
* Remove extraneous config files from root
  • Loading branch information
robinheinze authored and jamonholmgren committed Jan 22, 2019
1 parent 28856f1 commit 98ba911
Show file tree
Hide file tree
Showing 142 changed files with 261 additions and 498 deletions.
19 changes: 11 additions & 8 deletions boilerplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,20 @@ async function install(context) {
})
if (rnInstall.exitCode > 0) process.exit(rnInstall.exitCode)

// remove the __tests__ directory and App.js that come with React Native
filesystem.remove('__tests__')
filesystem.remove('App.js')
// remove the __tests__ directory, App.js, and unnecessary config files that come with React Native
const filesToRemove = [
'__tests__',
'App.js',
'.babelrc',
'.flowconfig',
'.buckconfig',
]
filesToRemove.map(filesystem.remove)

// copy our App, Tests & storybook directories
spinner.text = '▸ copying files'
spinner.start()
filesystem.copy(`${__dirname}/boilerplate/src`, `${process.cwd()}/src`, {
filesystem.copy(`${__dirname}/boilerplate/app`, `${process.cwd()}/app`, {
overwrite: true,
matching: '!*.ejs'
})
Expand All @@ -79,15 +85,12 @@ async function install(context) {
{ template: 'index.js.ejs', target: 'index.js' },
{ template: 'README.md', target: 'README.md' },
{ template: 'ignite.json.ejs', target: 'ignite/ignite.json' },
{ template: '.editorconfig', target: '.editorconfig' },
{ template: '.babelrc', target: '.babelrc' },
{ template: '.gitignore', target: '.gitignore' },
{ template: '.prettierignore', target: '.prettierignore' },
{ template: '.prettierrc', target: '.prettierrc' },
{ template: '.solidarity', target: '.solidarity' },
{ template: 'tsconfig.json', target: 'tsconfig.json' },
{ template: 'tslint.json', target: 'tslint.json' },
{ template: 'src/app/main.tsx.ejs', target: 'src/app/main.tsx' }
{ template: 'app/app.tsx.ejs', target: 'app/app.tsx' }
]
const templateProps = {
name,
Expand Down
24 changes: 0 additions & 24 deletions boilerplate/.babelrc

This file was deleted.

14 changes: 0 additions & 14 deletions boilerplate/.editorconfig

This file was deleted.

6 changes: 0 additions & 6 deletions boilerplate/.prettierrc

This file was deleted.

60 changes: 28 additions & 32 deletions boilerplate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ The Ignite Bowser boilerplate project's structure will look similar to this:

```
ignite-project
├── src
│   ├── app
├── app
│   ├── components
│   ├── i18n
│   ├── lib
│   ├── utils
│   ├── models
│   ├── navigation
│   ├── screens
│   ├── services
│   ├── theme
│   ├── views
│   ├── app.tsx
│   ├── environment-variables.ts
├── storybook
│   ├── views
│   ├── index.ts
Expand Down Expand Up @@ -64,72 +66,66 @@ ignite-project
└── package.json
```

The directory structure uses a ["feature first, function second"](https://alligator.io/react/index-js-public-interfaces/) approach to organization. Files are grouped by the feature they are supporting rather than the type of file.

For example: A custom `Button` component would have the main component file, and test, and any assets or helper files all grouped together in one folder.
### ./app directory

This is a departure from the previous boilerplate, which grouped files by type (components together, styles together, tests together, images together, etc.). One feature of this new approach is the use of index files which export specific functions from files in the directory to create a public interface for each "thing", or "feature. You'll see that pattern quite a bit in this boilerplate.


## ./src directory

Included in an Ignite boilerplate project is the src directory. This is a directory you would normally have to create when using vanilla React Native.
Included in an Ignite boilerplate project is the `app` directory. This is a directory you would normally have to create when using vanilla React Native.

The inside of the src directory looks similar to the following:

```
src
── app
app
── components
│── i18n
├── lib
├── models
├── navigation
├── screens
├── services
├── theme
├── views
├── utils
├── app.tsx
├── environment-variables.ts
```

**app**
This is where a lot of your app's initialization takes place. Here you'll find:
* root-component.tsx - This is the root component of your app that will render your navigators and other views.
**components**
This is where your React components will live. Each component will have a directory containing the `.tsx` file, along with a story file, and optionally `.presets`, and `.props` files for larger components. The app will come with some commonly used components like Button.

**i18n**
This is where your translations will live if you are using `react-native-i18n`.

**lib**
This is a great place to put miscellaneous helpers and utilities. Things like date helpers, formatters, etc. are often found here. However, it should only be used for things that are truely shared across your application. If a helper or utility is only used by a specific component or model, consider co-locating your helper with that component or model.

**models**
This is where your app's models will live. Each model has a directory which will contain the `mobx-state-tree` model file, test file, and any other supporting files like actions, types, etc.

**navigation**
This is where your `react-navigation` navigators will live.

**screens**
This is where your screen components will live. A screen is a React component which will take up the entire screen and be part of the navigation hierarchy. Each screen will have a directory containing the `.tsx` file, along with any assets or other helper files.

**services**
Any services that interface with the outside world will live here (think REST APIs, Push Notifications, etc.).

**theme**
Here lives the theme for your application, including spacing, colors, and typography.

**views**
This is where all of your components will live. Both dumb components and screen components will be located here. Each component will have a directory containing the `.tsx` file, along with a story file, and optionally `.presets`, and `.props` files for larger components.
**utils**
This is a great place to put miscellaneous helpers and utilities. Things like date helpers, formatters, etc. are often found here. However, it should only be used for things that are truely shared across your application. If a helper or utility is only used by a specific component or model, consider co-locating your helper with that component or model.

**app.tsx** This is the entry point to your app. This is where you will find the main App component which renders the rest of the application. This is also where you will specify whether you want to run the app in storybook mode.

You may choose to futher break down this directory by organizing your components into "domains", which represent cohesive areas of your application. For example, a "user" domain could hold all components and screens related to managing a user.
### ./ignite directory
The `ignite` directory stores all things Ignite, including CLI and boilerplate items. Here you will find generators, plugins and examples to help you get started with React Native.

**storybook**
### ./storybook directory
This is where your stories will be registered and where the Storybook configs will live

**test**
### ./test directory
This directory will hold your Jest configs and mocks, as well as your [storyshots](https://github.com/storybooks/storybook/tree/master/addons/storyshots) test file. This is a file that contains the snapshots of all your component storybooks.

**ignite**
The `ignite` directory stores all things Ignite, including CLI and boilerplate items. Here you will find generators, plugins and examples to help you get started with React Native.

## Running Storybook
From the command line in your generated app's root directory, enter `yarn run storybook`
This starts up the storybook server.

In `src/app/main.tsx`, change `SHOW_STORYBOOK` to `true` and reload the app.
In `app/app.tsx`, change `SHOW_STORYBOOK` to `true` and reload the app.

For Visual Studio Code users, there is a handy extension that makes it easy to load Storybook use cases into a running emulator via tapping on items in the editor sidebar. Install the `React Native Storybook` extension by `Orta`, hit `cmd + shift + P` and select "Reconnect Storybook to VSCode". Expand the STORYBOOK section in the sidebar to see all use cases for components that have `.story.tsx` files in their directories.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import "../i18n"
// Welcome to the main entry point of the app.
//
// In this file, we'll be kicking off our app or storybook.

import "./i18n"
import * as React from "react"
import { setupRootStore } from "./setup-root-store"
import { StatefulNavigator } from "../navigation"
import { RootStore } from "./root-store"
import { AppRegistry } from "react-native"
import { StatefulNavigator } from "./navigation"
import { StorybookUIRoot } from "../storybook"
import { RootStore, setupRootStore } from "./models/root-store"
import { Provider } from "mobx-react"
import { BackButtonHandler } from "../navigation/back-button-handler"
import { BackButtonHandler } from "./navigation/back-button-handler"
import { contains } from "ramda"
import { DEFAULT_NAVIGATION_CONFIG } from "../navigation/navigation-config"
import { DEFAULT_NAVIGATION_CONFIG } from "./navigation/navigation-config"

interface RootComponentState {
interface AppState {
rootStore?: RootStore
}

/**
* This is the root component of our app.
*/
export class RootComponent extends React.Component<{}, RootComponentState> {
export class App extends React.Component<{}, AppState> {
/**
* When the component is mounted. This happens asynchronously and simply
* re-renders when we're good to go.
Expand Down Expand Up @@ -66,3 +71,17 @@ export class RootComponent extends React.Component<{}, RootComponentState> {
)
}
}

/**
* This needs to match what's found in your app_delegate.m and MainActivity.java.
*/
const APP_NAME = "<%= props.name %>"

// Should we show storybook instead of our app?
//
// ⚠️ Leave this as `false` when checking into git.
const SHOW_STORYBOOK = false

const RootComponent = SHOW_STORYBOOK && __DEV__ ? StorybookUIRoot : App
AppRegistry.registerComponent(APP_NAME, () => RootComponent)

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as React from "react"
import { View, ViewStyle, ImageStyle, TextStyle } from "react-native"
import { Text } from "../../shared/text"
import { Icon } from "../../shared/icon"
import { spacing } from "../../../theme"
import { Text } from "../text"
import { Icon } from "../icon"
import { spacing } from "../../theme"

const BULLET_ITEM: ViewStyle = {
flexDirection: "row",
marginTop: spacing[4],
paddingBottom: spacing[4],
borderBottomWidth: 1,
borderBottomColor: "#3A3048",
paddingBottom: spacing[4],
borderBottomWidth: 1,
borderBottomColor: "#3A3048",
}
const BULLET_CONTAINER: ViewStyle = {
marginRight: spacing[4] - 1,
marginRight: spacing[4] - 1,
marginTop: spacing[2],
}
const BULLET: ImageStyle = {
Expand All @@ -21,11 +21,11 @@ const BULLET: ImageStyle = {
}
const BULLET_TEXT: TextStyle = {
flex: 1,
fontFamily: "Montserrat-Regular",
color: "#BAB6C8",
fontFamily: "Montserrat-Regular",
color: "#BAB6C8",
fontSize: 15,
lineHeight: 22,
}
}

export interface BulletItemProps {
text: string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ViewStyle, TextStyle } from "react-native"
import { color } from "../../../theme/color"
import { spacing } from "../../../theme/spacing"
import { color, spacing } from "../../theme"

/**
* All text will start off looking like this.
Expand All @@ -27,7 +26,7 @@ export const viewPresets = {
* A smaller piece of secondard information.
*/
primary: { ...BASE_VIEW, backgroundColor: color.palette.orange } as ViewStyle,

/**
* A button without extras.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react"
import { ViewStyle } from "react-native"
import { ViewStyle, TextStyle } from "react-native"
import { storiesOf } from "@storybook/react-native"
import { StoryScreen, Story, UseCase } from "../../../../storybook/views"
import { StoryScreen, Story, UseCase } from "../../../storybook/views"
import { Button } from "./"

const buttonStyleArray: ViewStyle[] = [
Expand All @@ -14,7 +14,7 @@ const buttonTextStyleArray: TextStyle[] = [
{color: "#a511dc"},
]

storiesOf("Button", module)
storiesOf("Button")
.addDecorator(fn => <StoryScreen>{fn()}</StoryScreen>)
.add("Style Presets", () => (
<Story>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react"
import { View, ViewStyle } from "react-native"
import { storiesOf } from "@storybook/react-native"
import { StoryScreen, Story, UseCase } from "../../../../storybook/views"
import { StoryScreen, Story, UseCase } from "../../../storybook/views"
import { Checkbox } from "./"
import { Toggle } from "react-powerplug"

Expand All @@ -18,7 +18,7 @@ const arrayFillStyle: ViewStyle[] = [
{backgroundColor: "#55e0ff"},
]

storiesOf("Checkbox", module)
storiesOf("Checkbox")
.addDecorator(fn => <StoryScreen>{fn()}</StoryScreen>)
.add("Behaviour", () => (
<Story>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react"
import { TouchableOpacity, TextStyle, ViewStyle, View } from "react-native"
import { Text } from "../text"
import { color, spacing } from "../../../theme"
import { color, spacing } from "../../theme"
import { CheckboxProps } from "./checkbox.props"
import { reduce } from "ramda"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ViewStyle } from "react-native"
import { color, spacing } from "../../../theme"
import { color, spacing } from "../../theme"

/**
* The size of the border radius.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from "react"
import { storiesOf } from "@storybook/react-native"
import { StoryScreen, Story, UseCase } from "../../../../storybook/views"
import { StoryScreen, Story, UseCase } from "../../../storybook/views"
import { FormRow } from "./form-row"
import { Text } from "../text"
import { color } from "../../../theme/color"
import { color } from "../../theme/color"
import { ViewStyle } from "react-native"

const TEXT_STYLE_OVERRIDE = {
Expand All @@ -14,7 +14,7 @@ const arrayStyle: ViewStyle[] = [
{borderColor: "#32cd32"},
]

storiesOf("FormRow", module)
storiesOf("FormRow")
.addDecorator(fn => <StoryScreen>{fn()}</StoryScreen>)
.add("Assembled", () => (
<Story>
Expand Down
Loading

0 comments on commit 98ba911

Please sign in to comment.