diff --git a/.adr.json b/.adr.json
new file mode 100644
index 00000000..4aecda99
--- /dev/null
+++ b/.adr.json
@@ -0,0 +1,6 @@
+{
+ "digits": 4,
+ "language": "en",
+ "path": "docs/architecture/decisions/",
+ "prefix": ""
+}
diff --git a/.circleci/bin/docker-tags b/.circleci/bin/docker-tags
new file mode 100755
index 00000000..453e77d9
--- /dev/null
+++ b/.circleci/bin/docker-tags
@@ -0,0 +1,50 @@
+#!/bin/bash
+# outputs each tag we're attaching to this docker image
+set -e
+
+SHA=$1
+BRANCH=$2
+SHA1=$(git rev-parse --verify "${SHA}")
+
+# Echo to stderr
+echoerr() { echo "$@" 1>&2; }
+
+# Ensure that git SHA was provided
+if [[ -x "${SHA1}" ]]; then
+ echoerr "Error, no git SHA provided"
+ exit 1;
+fi
+
+# tag with the branch
+if [[ -n "${BRANCH}" ]]; then
+ echo "${BRANCH}"
+fi
+
+# Tag with each git tag
+git show-ref --tags -d | grep "^${SHA1}" | sed -e 's,.* refs/tags/,,' -e 's/\^{}//' 2> /dev/null \
+ | xargs -I % \
+ echo "%"
+
+# Tag with latest if certain conditions are met
+if [[ "$BRANCH" == "master" ]]; then
+ # Check to see if we have a valid `vX.X.X` tag and assign to CURRENT_TAG
+ CURRENT_TAG=$( \
+ git show-ref --tags -d \
+ | grep "^${SHA1}" \
+ | sed -e 's,.* refs/tags/,,' -e 's/\^{}//' \
+ | grep "^v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+$" \
+ | sort \
+ )
+
+ # Find the highest tagged version number
+ HIGHEST_TAG=$(git --no-pager tag | grep "^v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+$" | sort -r | head -n 1)
+
+ # We tag :latest only if
+ # 1. We have a current tag
+ # 2. The current tag is equal to the highest tag, OR the highest tag does not exist
+ if [[ -n "${CURRENT_TAG}" ]]; then
+ if [[ "${CURRENT_TAG}" == "${HIGHEST_TAG}" ]] || [[ -z "${HIGHEST_TAG}" ]]; then
+ echo "latest"
+ fi
+ fi
+fi
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000..4d1d0d3e
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,241 @@
+# This CircleCI configuration uses workflows to fan-out to multiple jobs. The
+# workflow is Dockerized. The first job builds the Docker image which is used
+# in all future steps.
+#
+# Assumes that the Docker image is published to Docker Hub.
+version: 2
+
+# The following stanza defines a map named defaults with a variable that may be
+# inserted using the YAML merge (<<: *) key later in the file to save some
+# typing. See http://yaml.org/type/merge.html for details.
+defaults: &defaults
+ environment:
+ - DOCKER_REPOSITORY: "reactioncommerce/styleguide"
+ - DOCKER_NAMESPACE: "reactioncommerce"
+ - DOCKER_NAME: "styleguide"
+
+ docker:
+ - image: circleci/node:8-stretch
+
+jobs:
+ docker-build:
+ <<: *defaults
+ steps:
+ - checkout
+ - setup_remote_docker
+ - run:
+ name: Discover Docker Tags
+ command: |
+ mkdir -p docker-cache
+ .circleci/bin/docker-tags "$CIRCLE_SHA1" "$CIRCLE_BRANCH" \
+ > docker-cache/docker-tags.txt
+ cat docker-cache/docker-tags.txt
+ - run:
+ name: Docker Build
+ command: |
+ docker build \
+ --build-arg "BUILD_COMPARE_URL=$CIRCLE_COMPARE_URL" \
+ --build-arg "BUILD_DATE=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
+ --build-arg "BUILD_ENV=test" \
+ --build-arg "BUILD_NUMBER=$CIRCLE_BUILD_NUM" \
+ --build-arg "BUILD_PLATFORM=circleci" \
+ --build-arg "BUILD_PLATFORM_PROJECT_REPONAME=$CIRCLE_PROJECT_REPONAME" \
+ --build-arg "BUILD_PLATFORM_PROJECT_USERNAME=$CIRCLE_PROJECT_USERNAME" \
+ --build-arg "BUILD_PULL_REQUESTS=$CI_PULL_REQUESTS" \
+ --build-arg "BUILD_TRIGGERED_BY_TAG=$CIRCLE_TAG" \
+ --build-arg "BUILD_URL=$CIRCLE_BUILD_URL" \
+ --build-arg "CIRCLE_WORKFLOW_ID=$CIRCLE_WORKFLOW_ID" \
+ --build-arg "CIRCLE_WORKFLOW_JOB_ID=$CIRCLE_WORKFLOW_JOB_ID" \
+ --build-arg "CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS=$CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS" \
+ --build-arg "CIRCLE_WORKSPACE_ID=$CIRCLE_WORKSPACE_ID" \
+ --build-arg "GIT_REPOSITORY_URL=$CIRCLE_REPOSITORY_URL" \
+ --build-arg "GIT_SHA1=$CIRCLE_SHA1" \
+ --build-arg "LICENSE=Apache-2.0" \
+ --build-arg "VCS_REF=$CIRCLE_SHA1" \
+ --build-arg "VENDOR=Reaction Commerce" \
+ -t "$DOCKER_REPOSITORY:$CIRCLE_SHA1" .
+ mkdir -p docker-cache
+ docker save \
+ -o docker-cache/docker-image.tar \
+ "$DOCKER_REPOSITORY:$CIRCLE_SHA1"
+ - run:
+ name: Save Test .env for Workspace Jobs
+ command: cp .env.example docker-cache/.env
+ - persist_to_workspace:
+ root: docker-cache
+ paths:
+ - docker-image.tar
+ - docker-tags.txt
+ - .env
+
+ docker-push:
+ <<: *defaults
+ steps:
+ - setup_remote_docker
+ - attach_workspace:
+ at: docker-cache
+ - run:
+ name: Load Docker Image
+ command: |
+ docker load < docker-cache/docker-image.tar
+ - run:
+ name: Tag Docker Image
+ command: |
+ cat docker-cache/docker-tags.txt \
+ | xargs -t -I % \
+ docker tag \
+ "$DOCKER_REPOSITORY:$CIRCLE_SHA1" \
+ "$DOCKER_REPOSITORY:%"
+ - run:
+ # Creates a new Docker repository. This is not strictly required if
+ # the Docker Hub defaults are set appropriately.
+ name: Create Private Docker Hub Repository
+ command: |
+ # Fetch a login token from environment credentials.
+ TOKEN=$(curl \
+ -H "Content-Type: application/json" \
+ -X POST \
+ -d "{\"username\":\"$DOCKER_USER\",\"password\":\"$DOCKER_PASS\"}" \
+ -s \
+ https://hub.docker.com/v2/users/login/ \
+ | jq -r .token)
+
+ # Try to create the private repo. It exits with success on fail.
+ curl \
+ -H "Authorization: JWT $TOKEN" \
+ -H "Content-Type: application/json" \
+ -d "{\"namespace\":\"$DOCKER_NAMESPACE\",
+ \"name\":\"$DOCKER_NAME\",
+ \"description\":\"$DESCRIPTION\",
+ \"full_description\":\"\",
+ \"is_private\":false}" \
+ https://hub.docker.com/v2/repositories/
+ - run:
+ name: Docker Push
+ command: |
+ docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
+ docker push "$DOCKER_REPOSITORY:$CIRCLE_SHA1"
+ cat docker-cache/docker-tags.txt \
+ | xargs -t -I % \
+ docker push "$DOCKER_REPOSITORY:%"
+
+
+ lint:
+ <<: *defaults
+ steps:
+ - setup_remote_docker
+ - attach_workspace:
+ at: docker-cache
+ - run:
+ name: Load Docker Image
+ command: |
+ docker load < docker-cache/docker-image.tar
+ - run:
+ name: Lint
+ command: |
+ docker run \
+ --env-file docker-cache/.env \
+ --name reactionapp_web_1 \
+ "$DOCKER_REPOSITORY:$CIRCLE_SHA1" \
+ yarn run lint
+
+ test:
+ <<: *defaults
+ steps:
+ - setup_remote_docker
+ - attach_workspace:
+ at: docker-cache
+ - run:
+ name: Load Docker Image
+ command: |
+ docker load < docker-cache/docker-image.tar
+ - run:
+ name: Test
+ command: |
+ docker run \
+ --env-file docker-cache/.env \
+ --name reactionapp_web_1 \
+ "$DOCKER_REPOSITORY:$CIRCLE_SHA1" \
+ yarn run test
+ - run:
+ name: Copy test artifacts from Remote Docker
+ command: |
+ docker cp \
+ reactionapp_web_1:/usr/local/src/reaction-app/reports \
+ reports
+ - store_test_results:
+ path: reports/junit
+ - store_artifacts:
+ path: reports
+
+ snyk-security:
+ <<: *defaults
+ steps:
+ - setup_remote_docker
+ - attach_workspace:
+ at: docker-cache
+ - run:
+ name: Load Docker Image
+ command: |
+ docker load < docker-cache/docker-image.tar
+ - run:
+ name: Snyk
+ command: |
+ # Snyk doesn't look up the directory tree for node_modules as
+ # NodeJS does so we have to take some extra measures to test in the
+ # Docker image. Copy package.json up a directory so that it is a
+ # sibling to node_modules, then run snyk test.
+ docker run \
+ --env-file docker-cache/.env \
+ -e "SNYK_TOKEN=$SNYK_TOKEN" \
+ --name reactionapp_web_1 \
+ "$DOCKER_REPOSITORY:$CIRCLE_SHA1" \
+ sh -c "snyk test"
+
+ publish-npm-package:
+ docker:
+ - image: node:8
+
+ dependencies:
+ pre:
+ - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
+
+ steps:
+ - checkout
+
+ - run: yarn install
+ - run: cd package && yarn install
+ - run: cd package && yarn run build
+ - run: npx semantic-release
+
+workflows:
+ version: 2
+ build_and_test:
+ jobs:
+ - docker-build:
+ context: reaction-build-read
+ - docker-push:
+ context: reaction-publish-docker
+ requires:
+ - docker-build
+ - lint:
+ context: reaction-validation
+ requires:
+ - docker-build
+ - test:
+ context: reaction-validation
+ requires:
+ - docker-build
+ - snyk-security:
+ context: reaction-validation
+ requires:
+ - docker-build
+ - publish-npm-package:
+ context: reaction-publish-semantic-release
+ requires:
+ - lint
+ - test
+ - snyk-security
+ filters:
+ branches:
+ only: master
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..f06235c4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+node_modules
+dist
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..80da7b80
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.js]
+max_line_length = 120
+indent_brace_style = 1TBS
+spaces_around_operators = true
+quote_type = double
\ No newline at end of file
diff --git a/.env.example b/.env.example
new file mode 100644
index 00000000..67887f23
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,6 @@
+NODE_ENV=development
+NODE_PATH=src
+PORT=4040
+REACTION_APP_NAME=styleguide.web
+REACTION_LOG_LEVEL=INFO
+REACTION_LOG_FORMAT=json
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..b730fc73
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,37 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+Type: **breaking|critical|major|minor**
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/component_request.md b/.github/ISSUE_TEMPLATE/component_request.md
new file mode 100644
index 00000000..d2593176
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/component_request.md
@@ -0,0 +1,46 @@
+---
+name: Component request
+about: Suggest a new component for this project
+
+---
+
+# Component Name
+
+## Overview / Summary
+### Summary description of UI component
+
+### Rationale for why this component is necessary
+
+### Expected use cases
+
+### Images / Designs of UI component in context
+
+### React State
+
+### Data Fetching
+
+### State Store
+
+### Routing / Query String
+
+### Render Criteria
+
+### Breakpoints
+#### Desktop, Tablet, Mobile
+
+## UI States
+#### Normal
+
+#### Empty
+
+#### Loading / retrieving data
+
+#### Processing / Waiting for response
+
+#### Error
+
+#### Active (e.g. button is depressed)
+
+#### Disabled
+
+#### Focus
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..b6970f49
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,25 @@
+Resolves #issueNumber
+Impact: **breaking|critical|major|minor**
+Type: **feature|bugfix|performance|test|style|refactor|docs|chore**
+
+
+
+
+
+## Component
+Description of the issue this component this PR adds. Also include, as necessary:
+- Any other components this component requires
+- Any NPM dependencies added
+
+## Screenshots
+Include mobile and desktop screenshots.
+
+## Breaking changes
+You should almost never include "BREAKING CHANGES" because we’re duplicating components to avoid that. Consult with others before doing it.
+
+## Testing
+1. List the steps needed for testing your change in this section.
+2. Assume that testers already know how to start the app, and do the basic setup tasks.
+3. Be detailed enough that someone can work through it without being too granular
+
+More detail for what each of these sections should include are available in our [Contributing Docs](https://docs.reactioncommerce.com/reaction-docs/master/contributing-to-reaction)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..0eca75f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+!.env.example
+.env
+yarn-error.log
+coverage
+node_modules
+!reports/README.md
+reports/*
+**/package-lock.json
+dist
+dist-modules-temp
+
+styleguide/build/
+styleguide/favicon.ico
+styleguide/favicon.png
+styleguide/fonts/
+styleguide/index.html
+styleguide/manifest.json
+styleguide/reaction-design-system-logo.svg
diff --git a/.reaction/project-hooks/README.md b/.reaction/project-hooks/README.md
new file mode 100644
index 00000000..9818d63f
--- /dev/null
+++ b/.reaction/project-hooks/README.md
@@ -0,0 +1,41 @@
+This directory contains project hooks for the Reaction Next development
+environment. They are invoked from the reaction-next build tools which operate
+across all projects to aid with orchestration.
+
+These hooks provide means for developers of an application to ensure that the
+application configure itself without higher-level coordination outside of the
+project.
+
+
+## Included Hooks
+
+| Name | Description |
+| -------------------- | ----------- |
+| `pre-build` | Invoked before Docker build.
+| `post-build` | Invoked after Docker build and before project start.
+| `post-project-start` | Invoked after project start.
+| `post-system-start` | Invoked after entire system start.
+
+More information can be found in the header documentation of each script.
+
+## General Best Practices
+
+### Check that Services Are Available Before Using Them
+
+The `post-project-start` and `post-system-start` hooks are called after the
+services are started with Docker. Though they have been started, it's possible
+that they will not yet be available to perform any script actions. This depends
+on the startup time for you application.
+
+This can lead to race conditions if you try to use a service in a hook script.
+
+Always check that any service is available before using it.
+
+Tools like [await](https://github.com/betalo-sweden/await) can help.
+
+
+### Keep Hook Scripts Lightweight
+
+It can be tempting to edit the hook script to directly perform a task. However,
+it's likely that the setup task is commonly performed. It is better to create
+a script in your project and call it from the hook.
diff --git a/.reaction/project-hooks/post-build b/.reaction/project-hooks/post-build
new file mode 100755
index 00000000..e3c33e35
--- /dev/null
+++ b/.reaction/project-hooks/post-build
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# Post Build Hook
+# Invoked by the reaction-next project bootstrapping process.
+#
+# Invoked after Docker build.
+# Perform any actions here that are required after docker-compose build but
+# before the project is started.
+#
+# Important Notes:
+#
+# - Expect that services are NOT running at this time.
+# - Do not assume that this hook script will run from this local directory.
+# The $__dir var is provided for convenience and may be used to invoke other
+# scripts.
+# - It is good practice to keep this script lightweight and invoke setup
+# scripts in your project.
+
+__current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+__root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+__root_name=$(basename "${__root_dir}")
+
+echo "${__root_name} post-build script invoked." 2>&1
diff --git a/.reaction/project-hooks/post-project-start b/.reaction/project-hooks/post-project-start
new file mode 100755
index 00000000..9a48ad09
--- /dev/null
+++ b/.reaction/project-hooks/post-project-start
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# Post Project Start Hook
+# Invoked by the reaction-next project bootstrapping process.
+#
+# Invoked after this service is started. Can be used for project specific
+# actions that should be performed after the project is running, like database
+# setup, migrations, seeds, etc. Do not depend on other projects in this hook!
+#
+# Important Notes:
+#
+# - The hook runs after all Docker Compose services in THIS project are
+# started. Though started, there is no guarantee that these services are
+# ready (i.e. that they will respond to requests.) It is your responsibility
+# to test that services are available before using them to avoid race
+# conditions.
+# - Do not assume that this hook script will run from this local directory.
+# The $__dir var is provided for convenience and may be used to invoke other
+# scripts.
+# - It is good practice to keep this script lightweight and invoke setup
+# scripts in your project.
+
+__current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+__root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+__root_name=$(basename "${__root_dir}")
+
+echo "${__root_name} post-project-start script invoked." 2>&1
diff --git a/.reaction/project-hooks/post-system-start b/.reaction/project-hooks/post-system-start
new file mode 100755
index 00000000..9590f131
--- /dev/null
+++ b/.reaction/project-hooks/post-system-start
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# Post System Start Hook
+# Invoked by the reaction-next project bootstrapping process.
+#
+# Invoked after all services in the system have been started.
+#
+# Important Notes:
+#
+# - The hook runs after all Docker Compose services in ALL projects are
+# started. Though started, there is no guarantee that these services are
+# ready (i.e. that they will respond to requests.) It is your responsibility
+# to test that services are available before using them to avoid race
+# conditions.
+# - Do not assume that this hook script will run from this local directory.
+# The $__dir var is provided for convenience and may be used to invoke other
+# scripts.
+# - It is good practice to keep this script lightweight and invoke setup
+# scripts in your project.
+
+__current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+__root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+__root_name=$(basename "${__root_dir}")
+
+echo "${__root_name} post-system-start script invoked." 2>&1
diff --git a/.reaction/project-hooks/pre-build b/.reaction/project-hooks/pre-build
new file mode 100755
index 00000000..cba6ad9f
--- /dev/null
+++ b/.reaction/project-hooks/pre-build
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# Pre Build Hook
+# Invoked by the reaction-next project bootstrapping process.
+#
+# Invoked before Docker build.
+# Perform any actions here that are required before docker-compose build. For
+# example, copying values from .env.example to .env.
+#
+# Important Notes:
+#
+# - Expect that services are NOT running at this time.
+# - Do not assume that this hook script will run from this local directory.
+# The $__dir var is provided for convenience and may be used to invoke other
+# scripts.
+# - It is good practice to keep this script lightweight and invoke setup
+# scripts in your project.
+
+__current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+__root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+__root_name=$(basename "${__root_dir}")
+
+echo "${__root_name} post-project-start script invoked." 2>&1
+
+"${__root_dir}/bin/setup"
diff --git a/.reaction/scripts/addcomponent.js b/.reaction/scripts/addcomponent.js
new file mode 100644
index 00000000..0f77942a
--- /dev/null
+++ b/.reaction/scripts/addcomponent.js
@@ -0,0 +1,71 @@
+const fs = require("fs-extra");
+const path = require("path");
+
+// Arguments are positional: componentName
+let componentName;
+let shouldBumpVersion = false;
+process.argv.forEach(function (value, index) {
+ switch (index) {
+ case 2:
+ componentName = value;
+ break;
+
+ default:
+ break;
+ }
+});
+
+function errorAndExit(message) {
+ console.error(`\n${message}\n`);
+ process.exit(1);
+}
+
+if (!componentName) {
+ errorAndExit("You must include the name of the component to create as the first argument of this script");
+}
+
+const BASE_PATH = path.join(process.cwd(), "package/src/components");
+const componentDirectory = path.join(BASE_PATH, componentName);
+
+function createComponentIndexFile(name) {
+ const filePath = path.join(componentDirectory, `index.js`);
+ let template = fs.readFileSync(".reaction/scripts/templates/index.js.template", { encoding: "utf8" });
+ template = template.replace(/COMPONENT/g, name);
+ fs.ensureFileSync(filePath);
+ fs.writeFileSync(filePath, template);
+}
+
+function createComponentFile(name) {
+ const filePath = path.join(componentDirectory, `${name}.js`);
+ let template = fs.readFileSync(".reaction/scripts/templates/Component.js.template", { encoding: "utf8" });
+ template = template.replace(/COMPONENT/g, name);
+ template = template.replace(/cOMPONENT/g, name.charAt(0).toLowerCase() + name.slice(1));
+ fs.ensureFileSync(filePath);
+ fs.writeFileSync(filePath, template);
+}
+
+function createComponentTestFile(name) {
+ const filePath = path.join(componentDirectory, `${name}.test.js`);
+ let template = fs.readFileSync(".reaction/scripts/templates/Component.test.js.template", { encoding: "utf8" });
+ template = template.replace(/COMPONENT/g, name);
+ fs.ensureFileSync(filePath);
+ fs.writeFileSync(filePath, template);
+}
+
+function createComponentMarkdownFile(name) {
+ const filePath = path.join(componentDirectory, `${name}.md`);
+ let template = fs.readFileSync(".reaction/scripts/templates/Component.md.template", { encoding: "utf8" });
+ template = template.replace(/COMPONENT/g, name);
+ fs.ensureFileSync(filePath);
+ fs.writeFileSync(filePath, template);
+}
+
+/**
+ * Main logic below
+ */
+if (fs.existsSync(componentDirectory)) errorAndExit(`${componentDirectory} already exists`);
+
+createComponentIndexFile(componentName);
+createComponentFile(componentName);
+createComponentTestFile(componentName);
+createComponentMarkdownFile(componentName);
diff --git a/.reaction/scripts/templates/Component.js.template b/.reaction/scripts/templates/Component.js.template
new file mode 100644
index 00000000..69140d31
--- /dev/null
+++ b/.reaction/scripts/templates/Component.js.template
@@ -0,0 +1,45 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+import styled from "styled-components";
+import { applyTheme } from "../../../utils";
+
+const StyledDiv = styled.div`
+ padding-bottom: ${applyTheme("COMPONENT.paddingBottom")};
+ padding-left: ${applyTheme("COMPONENT.paddingLeft")};
+ padding-right: ${applyTheme("COMPONENT.paddingRight")};
+ padding-top: ${applyTheme("COMPONENT.paddingTop")};
+`;
+
+class COMPONENT extends Component {
+ static propTypes = {
+ /**
+ * You can provide a `className` prop that will be applied to the outermost DOM element
+ * rendered by this component. We do not recommend using this for styling purposes, but
+ * it can be useful as a selector in some situations.
+ */
+ className: PropTypes.string,
+ /**
+ * If you've set up a components context using
+ * [@reactioncommerce/components-context](https://github.com/reactioncommerce/components-context)
+ * (recommended), then this prop will come from there automatically. If you have not
+ * set up a components context or you want to override one of the components in a
+ * single spot, you can pass in the components prop directly.
+ */
+ components: PropTypes.shape({
+ }).isRequired
+ };
+
+ static defaultProps = {
+
+ };
+
+ render() {
+ const { className } = this.props;
+
+ return (
+ TEST
+ );
+ }
+}
+
+export default COMPONENT;
diff --git a/.reaction/scripts/templates/Component.md.template b/.reaction/scripts/templates/Component.md.template
new file mode 100644
index 00000000..004f1c41
--- /dev/null
+++ b/.reaction/scripts/templates/Component.md.template
@@ -0,0 +1,11 @@
+### Overview
+
+### Usage
+
+Document various live component examples here. See https://react-styleguidist.js.org/docs/documenting.html
+
+### Theme
+
+Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
+
+| Theme Prop | Default | Description |
diff --git a/.reaction/scripts/templates/Component.test.js.template b/.reaction/scripts/templates/Component.test.js.template
new file mode 100644
index 00000000..9fb48a2a
--- /dev/null
+++ b/.reaction/scripts/templates/Component.test.js.template
@@ -0,0 +1,11 @@
+import React from "react";
+import renderer from "react-test-renderer";
+import { shallow } from "enzyme";
+import COMPONENT from "./COMPONENT";
+
+test("basic snapshot", () => {
+ const component = renderer.create();
+
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+});
diff --git a/.reaction/scripts/templates/index.js.template b/.reaction/scripts/templates/index.js.template
new file mode 100644
index 00000000..244ed6bc
--- /dev/null
+++ b/.reaction/scripts/templates/index.js.template
@@ -0,0 +1 @@
+export { default } from "./COMPONENT";
diff --git a/.reaction/yarnrc-docker.template b/.reaction/yarnrc-docker.template
new file mode 100644
index 00000000..b2bb0fd0
--- /dev/null
+++ b/.reaction/yarnrc-docker.template
@@ -0,0 +1,14 @@
+# Yarn Config - configured for running inside Docker Compose
+
+# Configure Yarn offline mirror for improved performance.
+# https://yarnpkg.com/blog/2016/11/24/offline-mirror/
+#
+# If you need to ensure clean cached modules follow the guide:
+# https://yarnpkg.com/blog/2016/11/24/offline-mirror/#updating-your-package
+#
+yarn-offline-mirror "/home/node/.cache/yarn-offline-mirror"
+yarn-offline-mirror-pruning true
+
+--install.cache-folder /home/node/.cache/yarn
+--install.ignore-scripts true
+--install.prefer-offline true
diff --git a/.snyk b/.snyk
new file mode 100644
index 00000000..d5f02ac2
--- /dev/null
+++ b/.snyk
@@ -0,0 +1,81 @@
+# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
+version: v1.12.0
+# ignores vulnerabilities until expiry date; change duration by modifying expiry date
+ignore:
+ 'npm:hoek:20180212':
+ - boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.655Z'
+ - cryptiles > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.655Z'
+ - hawk > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.655Z'
+ - hawk > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.655Z'
+ - hawk > sntp > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.655Z'
+ - hawk > cryptiles > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.655Z'
+ - node-pre-gyp > hawk > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > hawk > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > hawk > sntp > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > hawk > cryptiles > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > request > hawk > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > request > hawk > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > request > hawk > sntp > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > request > hawk > cryptiles > boom > hoek:
+ reason: No path to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ 'npm:stringstream:20180511':
+ - node-pre-gyp > request > stringstream:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ 'npm:cryptiles:20180710':
+ - cryptiles:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - hawk > cryptiles:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > hawk > cryptiles:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ - node-pre-gyp > request > hawk > cryptiles:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ 'npm:deep-extend:20180409':
+ - node-pre-gyp > rc > deep-extend:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ 'npm:extend:20180424':
+ - node-pre-gyp > request > extend:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ 'npm:sshpk:20180409':
+ - node-pre-gyp > request > http-signature > sshpk:
+ reason: No way to fix
+ expires: '2018-08-31T14:45:41.656Z'
+ 'npm:mem:20180117':
+ - libnpx > yargs > os-locale > mem:
+ reason: No update available
+ expires: '2019-07-14T20:24:25.499Z'
+patch: {}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..63b7f2b3
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,38 @@
+# Reaction Commerce Code of Conduct
+
+All Community Guidelines content is licensed under a [Creative Commons Attribution](https://creativecommons.org/licenses/by/3.0/) license.
+
+Like the technical community as a whole, the Reaction team and community is made up of a mixture of professionals and volunteers from all over the world, working on every aspect of the mission - including mentorship, teaching, and connecting people.
+
+Diversity is one of our huge strengths, but it can also lead to communication issues and unhappiness. To that end, we have a few ground rules that we ask people to adhere to. This code applies equally to founders, mentors, and those seeking help and guidance.
+
+This isn’t an exhaustive list of things that you can’t do. Rather, take it in the spirit in which it’s intended - a guide to make it easier to enrich all of us and the broader communities in which we participate.
+
+This code of conduct applies to all spaces managed by Reaction Commerce. This includes our [development chat room](https://gitter.im/reactioncommerce/reaction), [forums](https://forums.reactioncommerce.com), [blog](https://blog.reactioncommerce.com), mailing lists, [issue tracker](https://github.com/reactioncommerce/reaction/issues), [project boards](https://github.com/reactioncommerce/reaction/projects), Reaction events and meetups, and any other forums or service created by the core project team which the community uses for communication. In addition, violations of this code outside these spaces may affect a person's ability to participate within them.
+
+If you believe someone is violating the code of conduct, we ask that you report it by emailing . For more details, please see our [Reporting Guidelines](https://docs.reactioncommerce.com/reaction-docs/master/reporting-guide).
+
+- **Be friendly and patient.**
+
+- **Be welcoming.** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
+
+- **Be considerate.** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
+
+- **Be respectful.** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. Members of the Reaction community should be respectful when dealing with other members as well as with people outside the Reaction community.
+
+- **Be careful in the words that you choose.** We are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. This includes, but is not limited to:
+
+ - Violent threats or language directed against another person.
+ - Discriminatory jokes and language.
+ - Posting sexually explicit or violent material.
+ - Posting (or threatening to post) other people's personally identifying information ("doxing").
+ - Personal insults, especially those using racist or sexist terms.
+ - Unwelcome sexual attention.
+ - Advocating for, or encouraging, any of the above behavior.
+ - Repeated harassment of others. In general, if someone asks you to stop, then stop.
+
+- **When we disagree, try to understand why.** Disagreements, both social and technical, happen all the time and Reaction is no exception. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of Reaction comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
+
+## Questions?
+
+If you have questions, please see the [FAQs](https://docs.reactioncommerce.com/reaction-docs/master/guideline-faqs). If that doesn't answer your questions, feel free to [contact us](mailto:hello@reactioncommerce.com).
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..257801d6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,118 @@
+FROM node:8-alpine
+
+ARG NAME=styleguide
+ARG DESCRIPTION="Reaction Catalyst Documentation"
+ARG URL=https://github.com/reactioncommerce/catalyst
+ARG DOC_URL=https://github.com/reactioncommerce/catalyst
+ARG VCS_URL=https://github.com/reactioncommerce/catalyst
+ARG VCS_REF
+ARG VENDOR
+ARG BUILD_DATE
+ARG BUILD_COMPARE_URL
+ARG BUILD_ENV=test
+ARG BUILD_NUMBER
+ARG BUILD_PLATFORM
+ARG BUILD_PLATFORM_PROJECT_USERNAME
+ARG BUILD_PLATFORM_PROJECT_REPONAME
+ARG BUILD_PULL_REQUESTS
+ARG BUILD_TRIGGERED_BY_TAG
+ARG BUILD_URL
+ARG CIRCLE_WORKSPACE_ID
+ARG CIRCLE_WORKFLOW_ID
+ARG CIRCLE_WORKFLOW_JOB_ID
+ARG CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS
+ARG CIRCLE_WORKSPACE_ID
+ARG GIT_REPOSITORY_URL
+ARG GIT_SHA1
+ARG LICENSE
+
+ENV APP_SOURCE_DIR=/usr/local/src/reaction-app \
+ PATH=$PATH:/usr/local/src/reaction-app/node_modules/.bin
+
+LABEL maintainer="Reaction Commerce " \
+ com.reactioncommerce.build-date=$BUILD_DATE \
+ com.reactioncommerce.name=$NAME \
+ com.reactioncommerce.description=$DESCRIPTION \
+ com.reactioncommerce.url=$URL \
+ com.reactioncommerce.vcs-url=$VCS_URL \
+ com.reactioncommerce.vcs-ref=$VCS_REF \
+ com.reactioncommerce.vendor=$VENDOR \
+ com.reactioncommerce.docker.build.compare-url=$BUILD_COMPARE_URL \
+ com.reactioncommerce.docker.build.number=$BUILD_NUMBER \
+ com.reactioncommerce.docker.build.platform=$BUILD_PLATFORM \
+ com.reactioncommerce.docker.build.platform.project.username=$BUILD_PLATFORM_PROJECT_USERNAME \
+ com.reactioncommerce.docker.build.platform.project.reponame=$BUILD_PLATFORM_PROJECT_REPONAME \
+ com.reactioncommerce.docker.build.pull-requests=$BUILD_PULL_REQUESTS \
+ com.reactioncommerce.docker.build.triggered-by-tag=$BUILD_TRIGGERED_BY_TAG \
+ com.reactioncommerce.docker.build.url=$BUILD_URL \
+ com.reactioncommerce.docker.build.circle.workflow.id=$CIRCLE_WORKFLOW_ID \
+ com.reactioncommerce.docker.build.circle.workflow.job.id=$CIRCLE_WORKFLOW_JOB_ID \
+ com.reactioncommerce.docker.build.circle.workflow.upstream.job.ids=$CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS \
+ com.reactioncommerce.docker.build.circle.workflow.url=https://circleci.com/workflow-run/$CIRCLE_WORKFLOW_ID \
+ com.reactioncommerce.docker.build.circle.workspace.id=$CIRCLE_WORKSPACE_ID \
+ com.reactioncommerce.docker.git.repository.url=$GIT_REPOSITORY_URL \
+ com.reactioncommerce.docker.git.sha1=$GIT_SHA1 \
+ com.reactioncommerce.docker.license=$LICENSE
+
+# Because Docker Compose uses a volume for node_modules and volumes are owned
+# by root by default, we have to initially create node_modules here with correct owner.
+# Without this Yarn cannot write packages into node_modules later, when running in a container.
+RUN mkdir -p "/usr/local/src/reaction-app/node_modules"
+RUN mkdir -p "/usr/local/src/reaction-app/package/node_modules"
+RUN chown node "/usr/local/src"
+RUN chown node "/usr/local/src/reaction-app"
+RUN chown node "/usr/local/src/reaction-app/node_modules"
+RUN chown node "/usr/local/src/reaction-app/package/node_modules"
+
+WORKDIR $APP_SOURCE_DIR
+
+# Note that the two node_modules directories will not be copied during
+# this due to being listed in the `.dockerignore` file.
+COPY --chown=node . $APP_SOURCE_DIR
+
+# Build the dependencies into the Docker image in a cacheable way. Dependencies
+# are only rebuilt when package.json or yarn.lock is modified.
+#
+# The project directory will be mounted during development. Therefore, we'll
+# install dependencies into an external directory (one level up.) This works
+# because Node traverses up the fs to find node_modules.
+RUN set -ex; \
+ if [ "$BUILD_ENV" = "production" ]; then \
+ yarn install \
+ --frozen-lockfile \
+ --ignore-scripts \
+ --no-cache; \
+ cd package && yarn install \
+ --frozen-lockfile \
+ --ignore-scripts \
+ --no-cache; \
+ elif [ "$BUILD_ENV" = "test" ]; then \
+ yarn install \
+ --frozen-lockfile \
+ --ignore-scripts \
+ --no-cache; \
+ cd package && yarn install \
+ --frozen-lockfile \
+ --ignore-scripts \
+ --no-cache; \
+ fi;
+
+# For development, we will yarn install on each container start.
+# This ensures that we use our Docker development .yarnrc config
+# and get any add/changed dependencies without needing a full rebuild.
+#
+# We are copying in a .yarnrc file that is specific to running within Docker.
+# Thus, we don't want it in the main repo because it breaks yarn on the host
+# machine. We also don't want it in APP_SOURCE_DIR where docker-compose will
+# link in host files, so we create it as the user-level config.
+# Note that this will be copied in for a prod build, too, but since
+# we already ran yarn install above, it doesn't matter.
+COPY --chown=node ./.reaction/yarnrc-docker.template /home/node/.yarnrc
+
+# Important: Make sure we're the "node" user before we begin doing things because
+# our tools use "/home/node" as the HOME dir.
+USER node
+
+RUN yarn run build
+
+CMD ["yarn start"]
diff --git a/LICENSE b/LICENSE
index 261eeb9e..0f8fb3a0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2018 Reaction Commerce
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..6e93591e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Reaction Component Library & Design System
+
+[](https://www.npmjs.com/package/@reactioncommerce/catalyst)
+ [](https://circleci.com/gh/reactioncommerce/catalyst)
+
+
+
+This is a single project with a package of commerce-focused React UI components and the code for the Reaction Design System website:
+
+- [`@reactioncommerce/catalyst`](https://www.npmjs.com/package/@reactioncommerce/catalyst): See the [package.json](https://github.com/reactioncommerce/catalyst/blob/master/package/package.json) in [`/package`](https://github.com/reactioncommerce/catalyst/tree/master/package) folder.
+- [Reaction Design System](https://designsystem.reactioncommerce.com/): See the root [package.json](https://github.com/reactioncommerce/catalyst/blob/master/package.json).
+
+We use the [React Styleguidist](https://react-styleguidist.js.org/) package to run and build the Reaction Design System website, and running the style guide locally doubles as an interactive playground for developing and testing the components.
+
+## Use the React components in your project
+
+Refer to the [Reaction Design System docs](https://designsystem.reactioncommerce.com/#!/Using%20Components)
+
+## Contribute to this project
+
+Refer to the [contributor docs](./docs)
+
+## License
+
+Copyright 2018 Reaction Commerce
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 00000000..68b64a21
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,94 @@
+// See https://babeljs.io/docs/en/config-files#root-babelconfigjs-files
+module.exports = function (api) {
+ const isTest = api.env("test");
+
+ // Config for when running Jest tests
+ if (isTest) {
+ return {
+ presets: [
+ [
+ "@babel/env",
+ {
+ // https://babeljs.io/docs/en/babel-preset-env#targets
+ targets: {
+ node: "current",
+ browsers: [
+ "last 2 versions",
+ "ie 11"
+ ]
+ }
+ }
+ ],
+ "@babel/preset-react"
+ ],
+ plugins: [
+ "babel-plugin-styled-components",
+ ["@babel/plugin-proposal-decorators", { legacy: true }],
+ ["@babel/plugin-proposal-class-properties", { loose: true }],
+ "@babel/plugin-syntax-dynamic-import"
+ ]
+ };
+ }
+
+ // We set this in the `build:modules` package.json script
+ const esmodules = process.env.BABEL_MODULES === "1";
+
+ const presets = [
+ [
+ "@babel/env",
+ {
+ modules: esmodules ? false : "auto",
+ // https://babeljs.io/docs/en/babel-preset-env#targets
+ targets: {
+ node: "8",
+ browsers: [
+ "last 2 versions",
+ "ie 11"
+ ]
+ // Note: If we eventually drop IE11 supports, it should be safe
+ // to go back to passing `esmodules: true` here. But for now,
+ // we want the mjs files to be transformed to be IE11 compatible
+ // EXCEPT for `import`. This allows Webpack 4 to tree shake this
+ // package but yet still remain compatible with IE11 without
+ // further transformation by the app using this package.
+ // esmodules
+ },
+ // https://babeljs.io/docs/en/babel-preset-env#usebuiltins-usage-experimental
+ useBuiltIns: "usage"
+ }
+ ],
+ "@babel/preset-react"
+ ];
+
+ const plugins = [
+ "babel-plugin-styled-components",
+ ["@babel/plugin-proposal-decorators", { legacy: true }],
+ ["@babel/plugin-proposal-class-properties", { loose: true }],
+ "@babel/plugin-syntax-dynamic-import",
+ [
+ "@babel/plugin-transform-runtime",
+ {
+ useESModules: esmodules
+ }
+ ]
+ ];
+
+ let ignore;
+ if (process.env.NODE_ENV === "production") {
+ ignore = [
+ "**/*.test.js",
+ "__snapshots__",
+ "**/setupTests.js",
+ "**/tests",
+ "**/scripts"
+ ];
+ }
+
+ return {
+ ignore,
+ presets,
+ plugins,
+ sourceMaps: true
+ };
+};
+
diff --git a/babel.jest.js b/babel.jest.js
new file mode 100644
index 00000000..3273ea8b
--- /dev/null
+++ b/babel.jest.js
@@ -0,0 +1,4 @@
+// https://babeljs.io/docs/en/config-files#jest
+module.exports = require("babel-jest").createTransformer({
+ rootMode: "upward"
+});
diff --git a/bin/build.sh b/bin/build.sh
new file mode 100755
index 00000000..a6206892
--- /dev/null
+++ b/bin/build.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+# Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml
+
+# ---- Start unofficial bash strict mode boilerplate
+# http://redsymbol.net/articles/unofficial-bash-strict-mode/
+set -o errexit # always exit on error
+set -o errtrace # trap errors in functions as well
+set -o pipefail # don't ignore exit codes when piping output
+set -o posix # more strict failures in subshells
+# set -x # enable debugging
+
+IFS=$'\n\t'
+# ---- End unofficial bash strict mode boilerplate
+
+cd "$(dirname "${BASH_SOURCE[0]}")/.."
+export PATH="${PWD}/node_modules/.bin:${PATH}"
+declare -a args=(
+ --ignore-scripts
+ --no-lockfile
+ --no-progress
+ --non-interactive
+ --silent
+)
+yarn "${args[@]}"
+(cd package && yarn "${args[@]}" && yarn build)
+yarn styleguide:build
diff --git a/bin/setup b/bin/setup
new file mode 100755
index 00000000..689d5725
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+env_file=${__dir}/../.env
+env_example_file=${__dir}/../.env.example
+
+function main {
+ set -e
+
+ add_new_env_vars
+}
+
+function add_new_env_vars {
+ # create .env and set perms if it does not exist
+ [ ! -f "${env_file}" ] && { touch "${env_file}" ; chmod 0600 "${env_file}" ; }
+
+ export IFS=$'\n'
+ for var in $(cat "${env_example_file}"); do
+ key="${var%%=*}" # get var key
+ var=$(eval echo "$var") # generate dynamic values
+
+ # If .env doesn't contain this env key, add it
+ if ! $(grep -qLE "^$key=" "${env_file}"); then
+ echo "Adding $key to .env"
+ echo "$var" >> "${env_file}"
+ fi
+ done
+}
+
+main
diff --git a/bin/yarn-link b/bin/yarn-link
new file mode 100755
index 00000000..2f1bf435
--- /dev/null
+++ b/bin/yarn-link
@@ -0,0 +1,42 @@
+#!/usr/bin/env sh
+# Helper to automatically link any NPM modules that are in development.
+#
+# Mounting Custom Modules to the Docker Container:
+# Custom NPM modules must be mounted into the Docker container at
+#
+# /usr/local/src/${module}
+#
+# Here's an example Docker Compose mount for two modules, rimraf and eslint:
+#
+# volumes:
+# - web-yarn:/home/node/.cache/yarn
+# - .:/usr/local/src/reaction-app
+# - ../rimraf:/usr/local/src/rimraf
+# - ../eslint:/usr/local/src/eslint
+#
+#
+# Run this Script on Container Start:
+# Add this script to the Docker CMD to ensure that it links all modules
+# before starting the project process.
+#
+# Example command:
+#
+# command: [sh, -c, "bin/yarn-link && yarn run build && yarn run start"]
+
+
+# NPM modules that will be linked.
+# Set in .env
+custom_modules="${LINKED_NPM_MODULES}"
+
+custom_modules_folder=/usr/local/src
+modules_folder=/usr/local/src/node_modules
+project_folder=/usr/local/src/reaction-app
+
+for module in ${custom_modules}; do
+ cd "${custom_modules_folder}/${module}" \
+ || ( echo "Attempted to link NPM module that doesn't exist" 2>&1 && exit 1 )
+ yarn --modules-folder "${modules_folder}" link
+ cd "${project_folder}" \
+ || ( echo "Project directory doesn't exist while NPM module linking!" 2>&1 && exit 1 )
+ yarn --modules-folder "${modules_folder}" link ${module}
+done
diff --git a/config/env.js b/config/env.js
new file mode 100644
index 00000000..30a6c7f1
--- /dev/null
+++ b/config/env.js
@@ -0,0 +1,93 @@
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const paths = require('./paths');
+
+// Make sure that including paths.js after env.js will read .env variables.
+delete require.cache[require.resolve('./paths')];
+
+const NODE_ENV = process.env.NODE_ENV;
+if (!NODE_ENV) {
+ throw new Error(
+ 'The NODE_ENV environment variable is required but was not specified.'
+ );
+}
+
+// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
+var dotenvFiles = [
+ `${paths.dotenv}.${NODE_ENV}.local`,
+ `${paths.dotenv}.${NODE_ENV}`,
+ // Don't include `.env.local` for `test` environment
+ // since normally you expect tests to produce the same
+ // results for everyone
+ NODE_ENV !== 'test' && `${paths.dotenv}.local`,
+ paths.dotenv,
+].filter(Boolean);
+
+// Load environment variables from .env* files. Suppress warnings using silent
+// if this file is missing. dotenv will never modify any environment variables
+// that have already been set. Variable expansion is supported in .env files.
+// https://github.com/motdotla/dotenv
+// https://github.com/motdotla/dotenv-expand
+dotenvFiles.forEach(dotenvFile => {
+ if (fs.existsSync(dotenvFile)) {
+ require('dotenv-expand')(
+ require('dotenv').config({
+ path: dotenvFile,
+ })
+ );
+ }
+});
+
+// We support resolving modules according to `NODE_PATH`.
+// This lets you use absolute paths in imports inside large monorepos:
+// https://github.com/facebookincubator/create-react-app/issues/253.
+// It works similar to `NODE_PATH` in Node itself:
+// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
+// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
+// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
+// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
+// We also resolve them to make sure all tools using them work consistently.
+const appDirectory = fs.realpathSync(process.cwd());
+process.env.NODE_PATH = (process.env.NODE_PATH || '')
+ .split(path.delimiter)
+ .filter(folder => folder && !path.isAbsolute(folder))
+ .map(folder => path.resolve(appDirectory, folder))
+ .join(path.delimiter);
+
+// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
+// injected into the application via DefinePlugin in Webpack configuration.
+const REACT_APP = /^REACT_APP_/i;
+
+function getClientEnvironment(publicUrl) {
+ const raw = Object.keys(process.env)
+ .filter(key => REACT_APP.test(key))
+ .reduce(
+ (env, key) => {
+ env[key] = process.env[key];
+ return env;
+ },
+ {
+ // Useful for determining whether we’re running in production mode.
+ // Most importantly, it switches React into the correct mode.
+ NODE_ENV: process.env.NODE_ENV || 'development',
+ // Useful for resolving the correct path to static assets in `public`.
+ // For example,
.
+ // This should only be used as an escape hatch. Normally you would put
+ // images into the `src` and `import` them in code to get their paths.
+ PUBLIC_URL: publicUrl,
+ }
+ );
+ // Stringify all values so we can feed into Webpack DefinePlugin
+ const stringified = {
+ 'process.env': Object.keys(raw).reduce((env, key) => {
+ env[key] = JSON.stringify(raw[key]);
+ return env;
+ }, {}),
+ };
+
+ return { raw, stringified };
+}
+
+module.exports = getClientEnvironment;
diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js
new file mode 100644
index 00000000..8f651148
--- /dev/null
+++ b/config/jest/cssTransform.js
@@ -0,0 +1,14 @@
+'use strict';
+
+// This is a custom Jest transformer turning style imports into empty objects.
+// http://facebook.github.io/jest/docs/en/webpack.html
+
+module.exports = {
+ process() {
+ return 'module.exports = {};';
+ },
+ getCacheKey() {
+ // The output is always the same.
+ return 'cssTransform';
+ },
+};
diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js
new file mode 100644
index 00000000..9e4047d3
--- /dev/null
+++ b/config/jest/fileTransform.js
@@ -0,0 +1,12 @@
+'use strict';
+
+const path = require('path');
+
+// This is a custom Jest transformer turning file imports into filenames.
+// http://facebook.github.io/jest/docs/en/webpack.html
+
+module.exports = {
+ process(src, filename) {
+ return `module.exports = ${JSON.stringify(path.basename(filename))};`;
+ },
+};
diff --git a/config/paths.js b/config/paths.js
new file mode 100644
index 00000000..6d16efc9
--- /dev/null
+++ b/config/paths.js
@@ -0,0 +1,55 @@
+'use strict';
+
+const path = require('path');
+const fs = require('fs');
+const url = require('url');
+
+// Make sure any symlinks in the project folder are resolved:
+// https://github.com/facebookincubator/create-react-app/issues/637
+const appDirectory = fs.realpathSync(process.cwd());
+const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
+
+const envPublicUrl = process.env.PUBLIC_URL;
+
+function ensureSlash(path, needsSlash) {
+ const hasSlash = path.endsWith('/');
+ if (hasSlash && !needsSlash) {
+ return path.substr(path, path.length - 1);
+ } else if (!hasSlash && needsSlash) {
+ return `${path}/`;
+ } else {
+ return path;
+ }
+}
+
+const getPublicUrl = appPackageJson =>
+ envPublicUrl || require(appPackageJson).homepage;
+
+// We use `PUBLIC_URL` environment variable or "homepage" field to infer
+// "public path" at which the app is served.
+// Webpack needs to know it to put the right
+