Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

docs(articles): getting started #354

Merged
merged 24 commits into from
Nov 12, 2020
Merged
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8cfdbad
docs(articles): getting started
Oct 20, 2020
a6ba1ff
Merge branch 'main' into docs/getting-started
Francois-Esquire Oct 20, 2020
9e7ff87
fix(corrections): getting started
Oct 20, 2020
821a619
fix(docs): frontend
Oct 21, 2020
890a3ba
Merge branch 'main' into docs/getting-started
nellyk Oct 22, 2020
bf555a9
fix(title): use header
Oct 22, 2020
262ce0d
Merge branch 'main' into docs/getting-started
Oct 27, 2020
554125e
Merge branch 'main' into docs/getting-started
Oct 28, 2020
d851005
Merge branch 'main' into docs/getting-started
Francois-Esquire Oct 29, 2020
6bc91df
fix(getting-started): selector example
Francois-Esquire Oct 29, 2020
e32575a
fix(getting-started): review suggestions
Francois-Esquire Oct 29, 2020
bca0f9f
fix(getting-started): add questions
Francois-Esquire Oct 29, 2020
3894600
fix(feedback): update
Oct 29, 2020
091561d
fix(getting-started): add readme
Oct 29, 2020
a036763
Merge branch 'main' into docs/getting-started
Oct 29, 2020
713f169
Merge branch 'main' into docs/getting-started
Oct 29, 2020
789f657
docs(getting-started): move to overview
Oct 30, 2020
3d357fe
Merge branch 'main' into docs/getting-started
Francois-Esquire Nov 10, 2020
77e7c6a
Merge branch 'main' into docs/getting-started
Nov 10, 2020
0cbb872
Merge branch 'main' into docs/getting-started
Francois-Esquire Nov 10, 2020
5888063
Merge branch 'main' into docs/getting-started
Nov 10, 2020
b9ef49e
Merge branch 'main' into docs/getting-started
Nov 10, 2020
bf13507
Merge branch 'main' into docs/getting-started
Francois-Esquire Nov 11, 2020
c6536d7
Merge branch 'main' into docs/getting-started
Francois-Esquire Nov 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
387 changes: 387 additions & 0 deletions docs/articles/Getting-Started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,387 @@
Getting Started
---------------

One App is a web application that combines together multiple experiences into a single,
cohesive runtime. These experiences, or Holocron modules as we call them empower teams
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that "micro frontends" would make more sense here than "experiences". Is it really an accurate description to say Holocron modules are experiences? They are more versatile than that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with you, I'll update

to craft a rich user experience that can be used to compose parts of a web page or as
route destinations all on their own. One App was built from the ground up with Holocron
and its micro frontend architecture, allowing engineers to seamlessly update each module
independently and collaborate across teams working together to create a product. While
One App comes with a security standard in practice, progressive web app capability and
many other features, its feature set can be configured by Holocron modules to your
exact specification.

This guide will get us started with One App and will go over some of the
fundamental concepts of the framework. We will start from the beginning
by creating our first Holocron module, then we will go through a few
One App essentials to get up and running quickly.

## 📖 Table of Contents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have seen the CSP is a common pitfall...

What about adding a section for a basic CSP configuration at least explaining that one-app Blocks everything by default and that you won't be able to make any api calls unless you configure your CSP.

This should be explained as one of the main advantages of using one app as a best practice security feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you however CSP is an advanced topic that deserves an article or a set of reference documentation on its own. Another concern with adding a section is it starts to make the document heavy for a getting started guide, no?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we should add it to separate PR and maybe mention the article here once we have the CSP content

* [Generating a Module](#generating-a-module)
* [Running One App](#running-one-app)
* [Adding CSS Styles](#adding-css-styles)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i really like how you've broken this down into different sections

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

* [Creating Routes](#creating-routes)
* [Module State & Data](#module-state-&-data)
* [Configuring One App](#configuring-one-app)
* [Development](#development)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should be more specific about this, what do you think? Setting up your development environment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree that this section is bare compared to the rest, I'll update it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated


### Generating a Module

The first step to get started with One App is to generate a Holocron module.
There are two different Holocron module types, root and child module. Let us
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should explain what the difference is between root and child module

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd love if we could add images to describe this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can plan to add images later, I agree would be so helpful!

start by creating a root Holocron module, the entry point to our micro frontend.

```bash
export NODE_ENV=development

npx -p yo -p @americanexpress/generator-one-app-module \\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why break this onto a separate line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a pretty long command and breaks flex positioning in mobile devices.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 but when it gets put to the docs it isn't a simple copy and paste

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, breaking it up like this is a little confusing. I suspect many would think these 2 lines are separate commands

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If putting it on one line would make it less confusing, I'm all for it. Will update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 789f657

-- yo @americanexpress/one-app-module
```

Once the command is executed, you will be prompted to fill out a few questions
about your new module before it is generated. Starting off, we will want to name
our module and make sure that it is a root module; feel free to answer as you
wish for the other questions. Once the root module is generated we will be able
to start developing with One App.
Francois-Esquire marked this conversation as resolved.
Show resolved Hide resolved

> #### More Info
>
> [One App](https://github.com/americanexpress/one-app)
>
> ##### Packages
>
> [`react`](https://reactjs.org/)
>
> [`react-dom`](https://reactjs.org/docs/react-dom.html)
>
> [`holocron`](https://github.com/americanexpress/holocron/tree/main/packages/holocron)

### Running One App

Every Holocron module that is generated comes with pre-installed scripts that can
be run afterwards. One of these scripts is the `npm start` script that starts up
`one-app-runner`, which is our primary tool for local development.
Francois-Esquire marked this conversation as resolved.
Show resolved Hide resolved

```bash
npm start
```

When `one-app-runner` is fully loaded and running, we can navigate to `http://localhost:3000`
and view our Holocron module in the browser. In another terminal window you can run

```bash
npm run watch:build
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When `one-app-runner` is fully loaded and running, we can navigate to `http://localhost:3000`
and view our Holocron module in the browser. In another terminal window you can run
```bash
npm run watch:build
When `one-app-runner` is fully loaded and running, we can navigate to `http://localhost:3000`
and view our Holocron module in the browser.
```suggestion
> It can take a few minutes when `one-app-runner` starts for the first time.

Open another terminal window and run:

npm run watch:build

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good. suggestion, I will be adding this manually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated 789f657

```

and this will watch for any changes made to your module, then update `one-app-runner`
with the rebuilt module bundle. The command above uses `one-app-bundler` which can
also be used to bundle our Holocron module.
Francois-Esquire marked this conversation as resolved.
Show resolved Hide resolved

```bash
npm run build
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```bash
npm run build
```
You can use also build the module outside of watch mode:`
```bash
npm run build

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add this manually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added these


When ready to publish to production, make sure to set `NODE_ENV=production` before building.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we mention why here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be in a blockquote

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points, I will update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated


**Local Configuration**

Inside the `package.json` of your Holocron module, you can
include a `"one-amex": {}` property to configure some of the
tools in our ecosystem.

```json
{
"name": "my-module",
"one-amex": {
"bundler": {
"webpackConfigPath": "webpack.config.js"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we recommend webpackConfigPath for the first setup

Copy link
Contributor

@infoxicator infoxicator Oct 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nellyk Agree.. a custom webpack config is usually a feature for advanced users and not necessarily required when you are just getting started

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point on that, I wanted readers to know that it is possible, however as you both mentioned, it is an advanced feature.

},
"runner": {
"rootModuleName": "my-module",
"modules": [
".",
"../my-adjacent-child-module"
]
}
}
}
```

When you start using child modules, `one-app-runner` can include
multiple local modules when it's configured to accept them.
`one-app-bundler` can have it's `webpack` config extended along
with other options.

> #### More Info
>
> **Recipes**
>
> [Run One App Locally](../recipes/running-one-app-locally)
>
> [Running In Production](../recipes/running-in-production)
>
> [Publishing Modules](../recipes/publishing-modules)
>
> ##### Packages
>
> [`@americanexpress/one-app-runner`](https://github.com/americanexpress/one-app-cli/tree/main/packages/one-app-runner)
>
> [`@americanexpress/one-app-bundler`](https://github.com/americanexpress/one-app-cli/tree/main/packages/one-app-bundler)

### Adding CSS Styles

Styling a Holocron module with `CSS` or `SCSS` can be accomplished by creating a
separate file that is used with the markup we write in our module.

`src/components/styles.scss`

```css
.myButton {
background-color: green;

&:active,
&:focus,
&:hover {
background-color: blue;
}
}
```

We can import the stylesheet into our module and use CSS modules to access the class name
for each selector by its name. The class names are unique when they are generated which
avoids unwanted cascading of styles in our document.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could expand a bit more here to explain that the reason we use cssModules is because modules should be independent and we don't want css from other modules to affect them... hence the unique class name / avoiding collision

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a bit to this sentence


`src/components/MyModule.jsx`

```jsx
import React from 'react';
import styles from './styles.scss';

export default function MyModule() {
return (
<div>
<button type="button" className={styles.myButton}>
Click Me
</button>
</div>
);
}
```

> #### More Info
>
> **Recipes**
>
> [Adding Styles Recipe](../recipes/adding-styles)

### Creating Routes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should adjust the order of this section to be before adding styles... it would be ideal if you could continue from the previous step, then show developers how to add a second module locally and load it using moduleRoute so it keeps the flow

Additionally we could also show how to load modules with RenderModule from holocron that way we have covered all the possible scenarios for module composition 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually did want to talk about RenderModule! The reason I left it out was the size of the article was growing... Inthe future, we should think about making a recipe on the different ways to render a module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About ordering sections, I get where you are coming from when switching the two sections.. the reason I'd want to keep the CSS as the first activity in the module is the ease to set up, a core part of web development (CSS) and direct visual feedback will give readers (especially new to One App or JavaScript) a soft start for the first thing they do in a module. I feel that would segway into more JavaScript heavy sections that follow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with your take @Francois-Esquire since users can still load the webpage from the root, afterwards they can look at adding routes


One App has built in dynamic routing that uses each Holocron module to
build out the router. There is a special property that we can assign to
our module `Module.childRoutes` which would allow us to add routes to
One App. `childRoutes` should be assigned a function that is given
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only needs to be a function when you need to use the store for something, otherwise it can be an array or just a route with or without nested routes

Copy link
Contributor Author

@Francois-Esquire Francois-Esquire Oct 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL, thank you for sharing that 😄 I'll update the wording.

the Redux `store` used by the app, which can be useful when using
route hooks like `onEnter`.

`src/components/MyModule.jsx`

```jsx
import React from 'react';
import { Route } from '@americanexpress/one-app-router';
import ModuleRoute from 'holocron-module-route';

export default function MyModule({ children }) {
return (
<div>
{children}
</div>
);
}

MyModule.childRoutes = () => [
<ModuleRoute path="home" moduleName="home-module" />,
<Route
path="about"
component={() => (
<p>
About Page
</p>
)}
/>,
];
```

> #### More Info
>
> **API**
>
> [Routing](../api/modules/routing)
>
> [Loading Modules](../api/modules/loading-modules)
>
> **Recipes**
>
> [Code Splitting Using Holocron](../recipes/code-splitting-using-holocron)
>
> ##### Packages
>
> [`@americanexpress/one-app-router`](https://github.com/americanexpress/one-app-router/tree/master)
>
> [`holocron-module-route`](https://github.com/americanexpress/holocron/tree/main/packages/holocron-module-route)

### Module State & Data

Holocron modules have another special property `Module.holocron` we can be added to allow us
to configure the module. Within `holocron`, we can set keys like `reducer` to include a module
Francois-Esquire marked this conversation as resolved.
Show resolved Hide resolved
reducer with the Redux `store` used by One App. There is also `loadModuleData` that is called
during server side render and when a Holocron module is loaded by One App. When we combine the
two, we can asynchronously load all the data needed for a module and add it to the `store` when
a `reducer` is supplied to the `holocron` configuration.

`src/components/MyModule.jsx`

```jsx
import React from 'react';
import { fromJS } from 'immutable';

function DataVisualizer({ data, loaded }) {
return (
<div>
{/* do something with the data when it loads */}
</div>
);
}

export default function MyModule({ moduleState = {}, moduleLoadStatus = 'loading' }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should recommend moduleLoadStatus to check for dataLoading events, after our discussion on on loadModuleData it seems like there is no correlation between the loadModuleStatus and the "Data loading status" we should double check that is the case before suggesting this pattern in our docs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, will update this

return (
<DataVisualizer data={moduleState} loaded={moduleLoadStatus === 'loaded'} />
);
}

MyModule.holocron = {
name: 'my-module',
reducer: (moduleState = fromJS({}), action) => {
if (action.type === 'my-action') return fromJS(action.data);
return moduleState;
},
loadModuleData: async ({ store: { dispatch }, fetchClient }) => {
const response = await fetchClient('url/to/data.json');
const data = await response.json();
dispatch({ type: 'my-action', data });
},
};
```

Both `moduleState` and `moduleLoadStatus` are props given to each Holocron module.
All state is `immutable` and the `reducer` provided will be expected to return
Immutable compliant types (which includes JavaScript primitives). `loadModuleData`
is passed the Redux `store`, along with the `fetchClient`, `ownProps` and the
`module` itself. `fetchClient` can be configured with `appConfig` which will
learn about in the next section.

> #### More Info
>
> **API**
>
> [Loading Data](../api/modules/loading-data)
>
> [State Management](../api/modules/state-management)
>
> **Recipes**
>
> [Enabling Server Side Render](../recipes/enabling-serverside-rendering)
>
> [Internationalization](../recipes/internationalizing-your-module)
>
> ##### Packages
>
> [`immutable`](https://immutable-js.github.io/immutable-js/)
>
> [`redux`](https://redux.js.org/)
>
> [`react-redux`](https://react-redux.js.org/)
>
> [`reselect`](https://github.com/reduxjs/reselect)

### Configuring One App

When One App first starts up on the server, it loads in all the modules
and looks for `Module.appConfig` in each module to configure the app runtime.
The root module is used to configure many aspects of One App, including the
state configuration that is available for every module to use when rendering.

`src/appConfig.js`

```js
export default {
csp: "default-src 'self';",
providedStateConfig: {
theme: {
client: 'my-theme-name',
server: 'my-theme-name',
},
},
};
```

The `appConfig` property is meant strictly for the server side,
take advantage of `global.BROWSER` to ensure that the `appConfig`
is only bundled with the server side build.

`src/components/MyModule.jsx`

```jsx
import React from 'react';
import { useSelector } from 'react-redux';

export default function MyModule() {
const { theme } = useSelector(
(state) => state.get('config'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe consider this example to load a single config value from the state? for best practices and performance we should recommend avoiding state.ToJS() if possible

(state) => state.toJS()
);
Francois-Esquire marked this conversation as resolved.
Show resolved Hide resolved

return (
<p>
Theme Configuration:
{theme}
</p>
);
}

if (!global.BROWSER) {
// eslint-disable-next-line global-require
MyModule.appConfig = require('../appConfig.js');
}
```

> #### More Info
>
> **API**
>
> [App Configuration](../api/modules/app-configuration)
>
> [Environment Variables](../api/server/environment-variables)
>
> [Module Map Schema](../api/server/module-map-schema)
>
> **Recipes**
>
> [Partial Rendering](../recipes/partial-rendering)
>
> [Progressive Web App](../recipes/PWA.md)
>
> ##### Packages
>
> [`@americanexpress/one-app-ducks`](https://github.com/americanexpress/one-app-ducks)
>
> [`@americanexpress/one-service-worker`](https://github.com/americanexpress/one-service-worker)

### Development

**Recipes**

* [Mocking API Calls](../recipes/mocking-api-calls)

**API**

* [CLI Commands](../api/server/cli-commands)