5 | AccordionFormList({"addNewItemButtonText":"Add a new address","components":"[Object]","deleteItemButtonText":"Delete address","entryFormSubmitButtonText":"Save Changes","itemAddFormProps":"[Object]","items":"[Object]"})
6 |
32 | `;
33 |
--------------------------------------------------------------------------------
/docs/clone.md:
--------------------------------------------------------------------------------
1 | # Cloning the Project for Development
2 |
3 | ## Prerequisites
4 |
5 | We use [Docker](https://www.docker.com/products/docker-desktop) for development, so install it and make sure you understand how [developing in Docker](https://docs.reactioncommerce.com/docs/installation-docker-development) works.
6 |
7 | ## Install
8 |
9 | ```sh
10 | # Clone
11 | git clone git@github.com:reactioncommerce/reaction-component-library.git
12 |
13 | cd reaction-component-library
14 |
15 | # Setup - puts an .env in place
16 | bin/setup
17 |
18 | yarn install
19 | ```
20 |
21 | Then check the `.env` file to see if there are any placeholder values that need to be replaced with real values.
22 |
23 | Now that you're set up, you'll want to read about [Running and Developing Locally](./developing.md).
24 |
--------------------------------------------------------------------------------
/package/src/components/StockWarning/v1/StockWarning.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | The `StockWarning` displays a low inventory warning when the `isLowInventoryQuantity` prop is true.
3 |
4 | ### Usage
5 |
6 | An inventory warning will be rendered when the `isLowInventoryQuantity` prop is `true`, and does not render when a product has a normal inventory level.
7 |
8 | #### Low inventory
9 | ```jsx
10 |
11 | ```
12 |
13 | #### Regular inventory
14 | ```jsx
15 |
16 | ```
17 |
18 | ### Theme
19 |
20 | See [Theming Components](./#!/Theming%20Components).
21 |
22 | #### Typography
23 |
24 | - The text uses `labelText` style with `rui_components.StockWarning` override
25 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutActionComplete/v1/CheckoutActionComplete.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import mockComponents from "../../../tests/mockComponents";
4 | import CheckoutActionComplete from "./CheckoutActionComplete";
5 |
6 | test("basic snapshot", () => {
7 | const onClick = () => {};
8 |
9 | const Address = "
123 Main Street
Anytown, USA 01776
";
10 |
11 | const component = renderer.create((
12 |
19 | ));
20 |
21 | const tree = component.toJSON();
22 | expect(tree).toMatchSnapshot();
23 | });
24 |
--------------------------------------------------------------------------------
/package/src/components/Link/v1/__snapshots__/Link.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Link component with image snapshot 1`] = `
4 | .c0 {
5 | -webkit-text-decoration: none;
6 | text-decoration: none;
7 | }
8 |
9 |
14 |
20 |
21 | `;
22 |
23 | exports[`Link component with text snapshot 1`] = `
24 | .c0 {
25 | -webkit-text-decoration: none;
26 | text-decoration: none;
27 | }
28 |
29 |
34 | Click here
35 |
36 | `;
37 |
--------------------------------------------------------------------------------
/package/src/utils/applyTheme.js:
--------------------------------------------------------------------------------
1 | import getFromTheme from "./getFromTheme";
2 |
3 | /**
4 | * @summary A function for use in styled-components template string, which
5 | * returns a props function that returns CSS for proper typography styling
6 | * @param {String} themePath An object path describing where to look
7 | * in this group object in the theme to find the value that is needed.
8 | * @param {String} [group] Custom theme object group. `rui_` will be prepended.
9 | * Default is "components", i.e. `rui_components`.
10 | * @returns {Function} A function that takes `props` argument and returns
11 | * the value from a custom or default theme
12 | */
13 | export default function applyTheme(themePath, group = "components") {
14 | return (props) => getFromTheme(props, `rui_${group}.${themePath}`);
15 | }
16 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4 | env_file=${__dir}/../.env
5 | env_example_file=${__dir}/../.env.example
6 |
7 | function main {
8 | set -e
9 |
10 | add_new_env_vars
11 | }
12 |
13 | function add_new_env_vars {
14 | # create .env and set perms if it does not exist
15 | [ ! -f "${env_file}" ] && { touch "${env_file}" ; chmod 0600 "${env_file}" ; }
16 |
17 | export IFS=$'\n'
18 | for var in $(cat "${env_example_file}"); do
19 | key="${var%%=*}" # get var key
20 | var=$(eval echo "$var") # generate dynamic values
21 |
22 | # If .env doesn't contain this env key, add it
23 | if ! $(grep -qLE "^$key=" "${env_file}"); then
24 | echo "Adding $key to .env"
25 | echo "$var" >> "${env_file}"
26 | fi
27 | done
28 | }
29 |
30 | main
31 |
--------------------------------------------------------------------------------
/package/src/utils/getRequiredValidator.js:
--------------------------------------------------------------------------------
1 | import get from "lodash.get";
2 |
3 | /**
4 | *
5 | * @method getRequiredValidator
6 | * @summary check if a inputs value is undefined, null or empty string and creates an errors array
7 | * @param {String} requiredFields - name of required field you want to validate
8 | * @return {Object[]} errors - array of field error objects
9 | */
10 | export default function getRequiredValidator(...requiredFields) {
11 | return async (obj) => {
12 | const errors = [];
13 | requiredFields.forEach((requiredField) => {
14 | const value = get(obj, requiredField);
15 | if (value === null || value === undefined || value === "") {
16 | errors.push({ name: requiredField, message: `${requiredField} is required` });
17 | }
18 | });
19 | return errors;
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/package/src/utils/addressToString.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @method addressToString
4 | * @summary Converts an `address` object to a string
5 | * @param {Object} address - Address object to be converted
6 | * @param {Object} [options] - Options that affect the resulting string
7 | * @param {Boolean} [options.includeFullName] - If true, the string will begin with address.fullName.
8 | * @return {String} - address as a flat string
9 | */
10 | export default function addressToString({
11 | address1,
12 | address2,
13 | city,
14 | country,
15 | fullName,
16 | postal,
17 | region
18 | }, options = {}) {
19 | const result = `${address1}${address2 ? `, ${address2}` : ""}, ${city}, ${region} ${postal} ${country}`;
20 |
21 | if (options.includeFullName && fullName) {
22 | return `${fullName}, ${result}`;
23 | }
24 |
25 | return result;
26 | }
27 |
--------------------------------------------------------------------------------
/package/src/components/ErrorsBlock/v1/ErrorsBlock.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import ErrorsBlock from "./ErrorsBlock";
4 |
5 | test("has isFormErrors property set to true", () => {
6 | expect(ErrorsBlock.isFormErrors).toBe(true);
7 | });
8 |
9 | test("renders all error messages", () => {
10 | const errors = [
11 | { name: "a", message: "Message One" },
12 | { name: "b", message: "Message Two" }
13 | ];
14 |
15 | const component = renderer.create();
16 |
17 | const tree = component.toJSON();
18 | expect(tree).toMatchSnapshot();
19 | });
20 |
21 | test("renders nothing when there are no errors", () => {
22 | const component = renderer.create();
23 |
24 | const tree = component.toJSON();
25 | expect(tree).toMatchSnapshot();
26 | });
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/component_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Component request
3 | about: Suggest a new component for this project
4 |
5 | ---
6 |
7 | # Component Name
8 |
9 | ## Overview / Summary
10 | ### Summary description of UI component
11 |
12 | ### Rationale for why this component is necessary
13 |
14 | ### Expected use cases
15 |
16 | ### Images / Designs of UI component in context
17 |
18 | ### React State
19 |
20 | ### Data Fetching
21 |
22 | ### State Store
23 |
24 | ### Routing / Query String
25 |
26 | ### Render Criteria
27 |
28 | ### Breakpoints
29 | #### Desktop, Tablet, Mobile
30 |
31 | ## UI States
32 | #### Normal
33 |
34 | #### Empty
35 |
36 | #### Loading / retrieving data
37 |
38 | #### Processing / Waiting for response
39 |
40 | #### Error
41 |
42 | #### Active (e.g. button is depressed)
43 |
44 | #### Disabled
45 |
46 | #### Focus
47 |
--------------------------------------------------------------------------------
/package/src/components/StockWarning/v1/__snapshots__/StockWarning.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Displays error warning about required props 1`] = `undefined`;
4 |
5 | exports[`Renders nothing when stock level is normal 1`] = `null`;
6 |
7 | exports[`Renders stock warning when inventory is low 1`] = `
8 | .c0 {
9 | -webkit-font-smoothing: antialiased;
10 | color: #cd3f4c;
11 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif;
12 | font-size: 14px;
13 | font-style: normal;
14 | font-stretch: normal;
15 | font-weight: 400;
16 | -webkit-letter-spacing: .02em;
17 | -moz-letter-spacing: .02em;
18 | -ms-letter-spacing: .02em;
19 | letter-spacing: .02em;
20 | line-height: 1.25;
21 | }
22 |
23 |
26 | Only
27 | 10
28 | in stock
29 |
30 | `;
31 |
--------------------------------------------------------------------------------
/docs/architecture/decisions/0005-test-components.md:
--------------------------------------------------------------------------------
1 | # 5. Test Components
2 |
3 | Date: 2018-02-23
4 |
5 | ## Status
6 |
7 | STATUS:accepted
8 |
9 | 2018-02-23 proposed
10 | 2018-03-01 accepted
11 |
12 | ## Context
13 |
14 | Our React Components need to be well tested. At a minimum:
15 |
16 | - Snapshots of what is rendered for the most common props, to see when the output changes and confirm that it was intentional.
17 | - Test that all function props are called at the proper time with the proper arguments, as documented.
18 | - Generate a coverage report to prove that everything is tested.
19 |
20 | ## Decision
21 |
22 | Use Jest.
23 |
24 | It is popular, backed by Facebook, runs tests in parallel and reruns only changed tests, has built-in coverage, mocking, and `expect` patterns, has a snapshot feature, runs well on CI, and is built on Jasmine, which is battle tested.
25 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutEmailAddress/v1/CheckoutEmailAddress.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import CheckoutEmailAddress from "./CheckoutEmailAddress";
4 |
5 | test("render email address of a user with an account", () => {
6 | const component = renderer.create((
7 |
11 | ));
12 |
13 | const tree = component.toJSON();
14 | expect(tree).toMatchSnapshot();
15 | });
16 |
17 | test("render email address of a guest", () => {
18 | const component = renderer.create((
19 |
23 | ));
24 |
25 | const tree = component.toJSON();
26 | expect(tree).toMatchSnapshot();
27 | });
28 |
--------------------------------------------------------------------------------
/package/src/utils/getPhoneNumberValidator.js:
--------------------------------------------------------------------------------
1 | import get from "lodash.get";
2 |
3 | /**
4 | *
5 | * @method getPhoneNumberValidator
6 | * @summary
7 | * @param {String} phoneFields - name of phone number field you want to validate
8 | * @return {Object[]} errors - array of field error objects
9 | */
10 | export default function getPhoneNumberValidator(...phoneFields) {
11 | // eslint-disable-next-line
12 | const phoneRegx = /^[\s()+-]*([0-9][\s()+-]*){6,20}(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(\d+))?$/i;
13 | return async (obj) => {
14 | const errors = [];
15 | phoneFields.forEach((phoneField) => {
16 | const value = get(obj, phoneField);
17 | if (!phoneRegx.test(String(value).toLowerCase())) {
18 | errors.push({ name: phoneField, message: `${phoneField} is invalid` });
19 | }
20 | });
21 | return errors;
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/package/src/components/SelectableList/v1/SelectableList.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import mockComponents from "../../../tests/mockComponents";
4 | import SelectableList from "./SelectableList";
5 |
6 | test("basic snapshot", () => {
7 | const items = [{
8 | id: "1",
9 | label: "Priority shipping",
10 | detail: "$12.00",
11 | value: "123",
12 | checked: true
13 | },
14 | {
15 | id: "2",
16 | label: "Expedited shipping",
17 | detail: "$5.00",
18 | value: "333"
19 | },
20 | {
21 | id: "3",
22 | label: "Free shipping",
23 | detail: "$0.00",
24 | value: "2455"
25 | }];
26 | const component = renderer.create();
27 |
28 | const tree = component.toJSON();
29 | expect(tree).toMatchSnapshot();
30 | });
31 |
--------------------------------------------------------------------------------
/package/src/components/InventoryStatus/v1/utils/inventoryStatus.js:
--------------------------------------------------------------------------------
1 | import { STATUS_TYPES } from "./";
2 |
3 | /**
4 | * Determines a product's badge status
5 | *
6 | * @param {Object} product - The product
7 | * @param {Object} statusLabels - Labels to use for badges
8 | * @returns {Object} - The computed product status
9 | */
10 | export default function inventoryStatus(product, statusLabels) {
11 | let status;
12 |
13 | if (product.isSoldOut && product.isBackorder) {
14 | status = { type: STATUS_TYPES.BACKORDER, label: statusLabels.BACKORDER };
15 | } else if (product.isSoldOut && !product.isBackorder) {
16 | status = { type: STATUS_TYPES.SOLD_OUT, label: statusLabels.SOLD_OUT };
17 | } else if (product.isLowQuantity && !product.isSoldOut) {
18 | status = { type: STATUS_TYPES.LOW_QUANTITY, label: statusLabels.LOW_QUANTITY };
19 | }
20 |
21 | return status;
22 | }
23 |
--------------------------------------------------------------------------------
/package/src/components/CartItems/v1/__snapshots__/CartItems.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`basic snapshot with empty props 1`] = `
4 |
7 | `;
8 |
9 | exports[`basic snapshot with required props 1`] = `
10 |
39 | `;
40 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutTopHat/v1/CheckoutTopHat.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 |
3 | Displays a message at the top of the checkout page.
4 |
5 | ### Usage
6 |
7 | ```jsx
8 |
11 | ```
12 |
13 | ### Theme
14 |
15 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
16 |
17 | | Theme Prop | Default | Description |
18 | | -------------------------------------------------- | ------- | --------------------------------------------------------------------------------------- |
19 | | `CheckoutTopHat.backgroundColor` | black05 | Background color |
20 | | `CheckoutTopHat.height` | 35px | Height |
21 |
22 | #### Typography
23 |
24 | - The message text uses `labelTextBold` style with `rui_components.CheckoutTopHatMessage` override
25 |
--------------------------------------------------------------------------------
/.reaction/project-hooks/pre-build:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Pre Build Hook
3 | # Invoked by the reaction-next project bootstrapping process.
4 | #
5 | # Invoked before Docker build.
6 | # Perform any actions here that are required before docker-compose build. For
7 | # example, copying values from .env.example to .env.
8 | #
9 | # Important Notes:
10 | #
11 | # - Expect that services are NOT running at this time.
12 | # - Do not assume that this hook script will run from this local directory.
13 | # The $__dir var is provided for convenience and may be used to invoke other
14 | # scripts.
15 | # - It is good practice to keep this script lightweight and invoke setup
16 | # scripts in your project.
17 |
18 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
20 | __root_name=$(basename "${__root_dir}")
21 |
22 | echo "${__root_name} post-project-start script invoked." 2>&1
23 |
24 | "${__root_dir}/bin/setup"
25 |
--------------------------------------------------------------------------------
/package/src/components/GuestForm/v1/__snapshots__/GuestForm.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`basic snapshot 1`] = `
4 | .c0 {
5 | display: -webkit-box;
6 | display: -webkit-flex;
7 | display: -ms-flexbox;
8 | display: flex;
9 | -webkit-box-pack: end;
10 | -webkit-justify-content: flex-end;
11 | -ms-flex-pack: end;
12 | justify-content: flex-end;
13 | padding: 1rem 0 0 0;
14 | }
15 |
16 | .c0 > * {
17 | width: 100%;
18 | }
19 |
20 | @media (min-width:600px) {
21 | .c0 > * {
22 | width: auto;
23 | }
24 | }
25 |
26 |
30 | Field({"name":"email","label":"Email address","isRequired":true,"helpText":"You will have the option to create an account and save your details after checkout.","children":"[Object]"})
31 |
34 | Button({"actionType":"secondary","isWaiting":false,"children":"Continue as guest"})
35 |
36 |
37 | `;
38 |
--------------------------------------------------------------------------------
/package/src/components/StockWarning/v1/StockWarning.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import checkPropTypes from "check-prop-types";
4 | import StockWarning from "./StockWarning";
5 |
6 | test("Displays error warning about required props", () => {
7 | const errorMessage = checkPropTypes(StockWarning.propTypes, {});
8 | expect(errorMessage).toMatchSnapshot();
9 | });
10 |
11 | test("Renders stock warning when inventory is low", () => {
12 | const component = renderer.create((
13 |
14 | ));
15 |
16 | const tree = component.toJSON();
17 | expect(tree).toMatchSnapshot();
18 | });
19 |
20 | test("Renders nothing when stock level is normal", () => {
21 | const component = renderer.create((
22 |
23 | ));
24 |
25 | const tree = component.toJSON();
26 | expect(tree).toMatchSnapshot();
27 | });
28 |
--------------------------------------------------------------------------------
/package/src/svg/spinner.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const SpinnerSvg = styled.svg`
5 | width: 1.125em;
6 | height: 1.125em;
7 | `;
8 |
9 | const spinner = (
10 |
19 |
23 |
32 |
33 |
34 | );
35 |
36 | export default spinner;
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | Type: **breaking|critical|major|minor**
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 | 1. Go to '...'
15 | 2. Click on '....'
16 | 3. Scroll down to '....'
17 | 4. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Desktop (please complete the following information):**
26 | - OS: [e.g. iOS]
27 | - Browser [e.g. chrome, safari]
28 | - Version [e.g. 22]
29 |
30 | **Smartphone (please complete the following information):**
31 | - Device: [e.g. iPhone6]
32 | - OS: [e.g. iOS8.1]
33 | - Browser [e.g. stock browser, safari]
34 | - Version [e.g. 22]
35 |
36 | **Additional context**
37 | Add any other context about the problem here.
38 |
--------------------------------------------------------------------------------
/package/src/components/StripePaymentInput/v1/__snapshots__/StripePaymentInput.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`basic snapshot 1`] = `
4 | .c0 {
5 | -webkit-font-smoothing: antialiased;
6 | color: #b3b3b3;
7 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif;
8 | font-size: 14px;
9 | font-style: normal;
10 | font-stretch: normal;
11 | font-weight: 400;
12 | -webkit-letter-spacing: .02em;
13 | -moz-letter-spacing: .02em;
14 | -ms-letter-spacing: .02em;
15 | letter-spacing: .02em;
16 | line-height: 1.25;
17 | }
18 |
19 | .c1 {
20 | display: inline-block;
21 | height: 20px;
22 | width: 20px;
23 | }
24 |
25 | .c2 {
26 | vertical-align: super;
27 | }
28 |
29 |
30 | StripeForm({})
31 |
34 |
37 |
38 |
41 | Your Information is private and secure.
42 |
43 |
44 |
45 | `;
46 |
--------------------------------------------------------------------------------
/docs/style-guide-development.md:
--------------------------------------------------------------------------------
1 | # Style Guide Development
2 |
3 | We use the `react-styleguidist` package to run and build the style guide, and running the style guide locally doubles as an interactive playground for developing and testing the components. Refer to [Styleguidist docs](https://react-styleguidist.js.org/docs/cookbook.html).
4 |
5 | ## Theming the Guide
6 |
7 | You can change the theme styles for the style guide app in `/src/styleguide/styles.css`. Be careful to define styles only for specific style guide classes. If you define styles for something generic, like `div`, then it may alter the appearance of all of the components that render that element and will confuse people.
8 |
9 | ## Adding or Editing Sections in the Guide
10 |
11 | Sections are defined in `styleguide.config.js`. The format is easy to understand by looking at the existing section definitions. Put the markdown content for a section in the `/src/styleguide/sections` folder, and name the `.md` file the same as the section `name` from `styleguide.config.js`, with spaces removed.
12 |
--------------------------------------------------------------------------------
/package/src/components/ErrorsBlock/v1/__snapshots__/ErrorsBlock.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders all error messages 1`] = `
4 | .c0 {
5 | margin-bottom: 10px;
6 | margin-left: 0;
7 | margin-right: 0;
8 | margin-top: 10px;
9 | }
10 |
11 | .c1 {
12 | -webkit-font-smoothing: antialiased;
13 | color: #cd3f4c;
14 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif;
15 | font-size: 14px;
16 | font-style: normal;
17 | font-stretch: normal;
18 | font-weight: 400;
19 | -webkit-letter-spacing: .02em;
20 | -moz-letter-spacing: .02em;
21 | -ms-letter-spacing: .02em;
22 | letter-spacing: .02em;
23 | line-height: 1.25;
24 | }
25 |
26 |
29 |
33 |
34 | Message One
35 |
36 |
40 |
41 | Message Two
42 |
43 |
44 | `;
45 |
46 | exports[`renders nothing when there are no errors 1`] = `null`;
47 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.4'
2 |
3 | services:
4 | web:
5 | build:
6 | context: .
7 | args:
8 | BUILD_ENV: "development"
9 | command: sh -c "yarn install && (cd package && yarn install) && yarn start"
10 | env_file:
11 | - ./.env
12 | environment:
13 | REACTION_APP_NAME: "styleguide.web"
14 | ports:
15 | - 4040:4040
16 | volumes:
17 | - $HOME/.cache/yarn-offline-mirror:/home/node/.cache/yarn-offline-mirror
18 | - web-yarn:/home/node/.cache/yarn
19 | - .:/usr/local/src/reaction-app # link everything from the root folder on the host machine into the container
20 | - node_modules:/usr/local/src/reaction-app/node_modules # except do not link the host /node_modules in because it will override the container node_modules
21 | - package_node_modules:/usr/local/src/reaction-app/package/node_modules # except do not link the host /package/node_modules in because it will override the container node_modules
22 |
23 | volumes:
24 | web-yarn:
25 | node_modules:
26 | package_node_modules:
27 |
--------------------------------------------------------------------------------
/styleguide/src/styles.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Global
3 | *
4 | * Define styles that should apply to the style guide and the components within it here.
5 | * This should not be very many things.
6 | */
7 | body {
8 | font-family: 'Source Sans Pro', "Helvetica Neue", Helvetica, sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | }
11 |
12 | /**
13 | * Style Guide Theme
14 | *
15 | * Define styles for the style guide application here.
16 | * Be careful not to define any styles that will apply generally to the components
17 | * or any of the HTML elements they render. (In general, only style .rsg--* classes here.)
18 | */
19 |
20 | .columns {
21 | display: flex;
22 | flex-wrap: wrap;
23 | margin-right: -20px;
24 | margin-bottom: 10px;
25 | }
26 |
27 | .columns > .column {
28 | margin-bottom: 20px;
29 | }
30 |
31 | @media(min-width: 879px) {
32 | .columns > .column {
33 | flex: 1;
34 | min-width: 280px;
35 | max-width: 280px;
36 | margin-right: 30px;
37 | }
38 | }
39 |
40 | .column img {
41 | width: 100%;
42 | margin-bottom: 15px;
43 | }
--------------------------------------------------------------------------------
/package/src/components/MultiSelect/v1/MultiSelect.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import MultiSelect from "./MultiSelect";
4 |
5 | const OPTIONS = [
6 | { label: "A", value: "a" },
7 | { label: "B", value: "b" },
8 | { label: "C", value: "c" }
9 | ];
10 |
11 | const PROPS = {
12 | className: "react-select",
13 | classNamePrefix: "react-select",
14 | menuIsOpen: true
15 | };
16 |
17 | test("basic snapshot", () => {
18 | const component = renderer.create();
19 | const tree = component.toJSON();
20 | expect(tree).toMatchSnapshot();
21 | });
22 |
23 | test("alphabetize option snapshot", () => {
24 | const UNORDERED_OPTIONS = [
25 | { label: "C", value: "c" },
26 | { label: "A", value: "a" },
27 | { label: "Z", value: "z" },
28 | { label: "E", value: "e" }
29 | ];
30 | const component = renderer.create();
31 | const tree = component.toJSON();
32 | expect(tree).toMatchSnapshot();
33 | });
34 |
--------------------------------------------------------------------------------
/package/src/components/BadgeOverlay/v1/utils/badgeStatus.js:
--------------------------------------------------------------------------------
1 | import { BADGE_TYPES } from "./";
2 |
3 | /**
4 | * Determines a product's badge status
5 | *
6 | * @param {Object} product - The product
7 | * @param {Object} badgeLabels - Labels to use for badges
8 | * @returns {Object} - The computed product status
9 | */
10 | export default function badgeStatus(product, badgeLabels) {
11 | let status;
12 |
13 | if (product.isSoldOut && product.isBackorder) {
14 | status = { type: BADGE_TYPES.BACKORDER, label: badgeLabels.BACKORDER };
15 | } else if (product.isSoldOut && !product.isBackorder) {
16 | status = { type: BADGE_TYPES.SOLD_OUT, label: badgeLabels.SOLD_OUT };
17 | } else if (product.isOnSale) {
18 | status = { type: BADGE_TYPES.SALE, label: badgeLabels.SALE };
19 | } else if (product.isLowQuantity && !product.isSoldOut) {
20 | status = { type: BADGE_TYPES.LOW_QUANTITY, label: badgeLabels.LOW_QUANTITY };
21 | } else if (product.isBestseller) {
22 | status = { type: BADGE_TYPES.BESTSELLER, label: badgeLabels.BESTSELLER };
23 | }
24 |
25 | return status;
26 | }
27 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutActions/v1/CheckoutActions.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import mockComponents from "../../../tests/mockComponents";
4 | import CheckoutActions from "./CheckoutActions";
5 |
6 | class mockCheckoutAction extends React.Component {
7 | static renderComplete = () => ;
8 | render() {
9 | return ;
10 | }
11 | }
12 |
13 | const mockActions = [
14 | {
15 | activeLabel: "mock active action one",
16 | completeLabel: "mock complete action one",
17 | component: mockCheckoutAction,
18 | id: "123",
19 | incompleteLabel: "mock inactive action one",
20 | onSubmit: () => true,
21 | status: "incomplete",
22 | props: {
23 | cartData: {
24 | data: null
25 | },
26 | cartMutation() {}
27 | }
28 | }
29 | ];
30 |
31 | test("basic snapshot", () => {
32 | const component = renderer.create();
33 |
34 | const tree = component.toJSON();
35 | expect(tree).toMatchSnapshot();
36 | });
37 |
--------------------------------------------------------------------------------
/styleguide/src/sections/Introduction.md:
--------------------------------------------------------------------------------
1 | The Reaction Storefront Component Library was created alongside the [Reaction Example Storefront](https://github.com/reactioncommerce/example-storefront/). The Storefront uses React components from this library.
2 |
3 | #### Designers
4 |
5 | - Use the Style documentation to learn about Colors and Typography.
6 |
7 | #### Developers
8 |
9 | - Use the documentation here to learn how to install, import and theme components with the [NPM package](https://www.npmjs.com/package/@reactioncommerce/components).
10 | - These React components are styled with [styled-components](https://www.styled-components.com) and tested with [Jest](https://jestjs.io/) and [Enzyme](https://github.com/airbnb/enzyme).
11 | - Use the [GitHub documentation](https://github.com/reactioncommerce/reaction-component-library/blob/master/docs/README.md) for instructions on how to contribute to this package and the docs.
12 |
13 | #### Contribute
14 | Have feedback or questions about a component? Make an issue on the [GitHub repository](https://github.com/reactioncommerce/reaction-component-library/).
--------------------------------------------------------------------------------
/package/src/components/InPageMenuItem/v1/InPageMenuItem.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import mockComponents from "../../../tests/mockComponents";
4 | import InPageMenuItem from "./InPageMenuItem";
5 |
6 | test("InPageMenuItem basic component", () => {
7 | const component = renderer.create();
8 |
9 | const tree = component.toJSON();
10 | expect(tree).toMatchSnapshot();
11 | });
12 |
13 | test("InPageMenuItem with onClick instead of href", () => {
14 | const onClick = () => {};
15 | const component = renderer.create();
16 |
17 | const tree = component.toJSON();
18 | expect(tree).toMatchSnapshot();
19 | });
20 |
21 | test("InPageMenuItem selected", () => {
22 | const component = renderer.create();
23 |
24 | const tree = component.toJSON();
25 | expect(tree).toMatchSnapshot();
26 | });
27 |
--------------------------------------------------------------------------------
/package/src/components/ShopLogo/v1/ShopLogo.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | Renders a shop's logo if a logo URL is provided, otherwise, it will render the shop's name.
3 |
4 | ### Usage
5 |
6 | #### Default
7 |
8 | ```jsx
9 |
10 | ```
11 |
12 | #### Without a logo
13 |
14 | ```jsx
15 |
16 | ```
17 |
18 | ### Theme
19 |
20 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
21 |
22 | | Theme Prop | Default | Description |
23 | | -------------------------------------------------- | ------- | --------------------------------------------------------------------------------------- |
24 | | `ShopLogo.height` | `auto` | The height of `ShopLogo`'s `img` tag, when providing a logo URL |
25 |
26 | #### Typography
27 |
28 | - The text rendered when there is not a logo uses `titleTextBold` style with `rui_components.ShopLogo` override
29 |
30 |
--------------------------------------------------------------------------------
/.reaction/project-hooks/post-system-start:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Post System Start Hook
3 | # Invoked by the reaction-next project bootstrapping process.
4 | #
5 | # Invoked after all services in the system have been started.
6 | #
7 | # Important Notes:
8 | #
9 | # - The hook runs after all Docker Compose services in ALL projects are
10 | # started. Though started, there is no guarantee that these services are
11 | # ready (i.e. that they will respond to requests.) It is your responsibility
12 | # to test that services are available before using them to avoid race
13 | # conditions.
14 | # - Do not assume that this hook script will run from this local directory.
15 | # The $__dir var is provided for convenience and may be used to invoke other
16 | # scripts.
17 | # - It is good practice to keep this script lightweight and invoke setup
18 | # scripts in your project.
19 |
20 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
22 | __root_name=$(basename "${__root_dir}")
23 |
24 | echo "${__root_name} post-system-start script invoked." 2>&1
25 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutActions/v1/__snapshots__/CheckoutActions.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`basic snapshot 1`] = `
4 | .c0 {
5 | border-bottom-color: #e6e6e6;
6 | border-bottom-style: solid;
7 | border-bottom-width: 1px;
8 | border-left-color: #e6e6e6;
9 | border-left-style: solid;
10 | border-left-width: 0;
11 | border-right-color: #e6e6e6;
12 | border-right-style: solid;
13 | border-right-width: 0;
14 | border-top-color: #e6e6e6;
15 | border-top-style: solid;
16 | border-top-width: 0;
17 | padding-bottom: 16px;
18 | padding-left: 0;
19 | padding-right: 0;
20 | padding-top: 16px;
21 | }
22 |
23 | .c0:first-of-type {
24 | border-top-width: 1px;
25 | }
26 |
27 | .c0:last-of-type {
28 | border-bottom-width: 0;
29 | }
30 |
31 |
38 | `;
39 |
--------------------------------------------------------------------------------
/package/src/components/Price/v1/Price.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import checkPropTypes from "check-prop-types";
4 | import Price from "./Price";
5 |
6 | test("Display error warning about required prop", () => {
7 | const errorMessage = checkPropTypes(Price.propTypes, {});
8 | expect(errorMessage).toMatchSnapshot();
9 | });
10 |
11 | test("Renders price without a compare at price", () => {
12 | const component = renderer.create();
13 | const tree = component.toJSON();
14 | expect(tree).toMatchSnapshot();
15 | });
16 |
17 |
18 | test("Renders price with a compare at price", () => {
19 | const component = renderer.create();
20 | const tree = component.toJSON();
21 | expect(tree).toMatchSnapshot();
22 | });
23 |
24 | test("Renders price without a compare at price, due to the fact that the prices are equal.", () => {
25 | const component = renderer.create();
26 | const tree = component.toJSON();
27 | expect(tree).toMatchSnapshot();
28 | });
29 |
--------------------------------------------------------------------------------
/package/src/components/Button/v1/utils/applyThemeWithActionType.js:
--------------------------------------------------------------------------------
1 | import { getFromTheme } from "../../../../utils";
2 |
3 | /**
4 | * @summary A function for use in styled-components template string, which
5 | * returns a props function that applies variable values from the theme,
6 | * with button action type and state variants applied.
7 | * @param {String} themeProp The name of the theme variable to get the value for
8 | * @param {String} [stateSuffix] An optional suffix describing the button state
9 | * @returns {Function} A function that takes `props` argument and returns the
10 | * value from a custom theme or the default theme.
11 | */
12 | export default function applyThemeWithActionType(themeProp, stateSuffix) {
13 | return (props) => {
14 | const { actionType, isDisabled, isTextOnly } = props;
15 |
16 | const finalSuffix = isTextOnly ? "textOnly" : actionType;
17 | const finalStateSuffix = isDisabled ? "disabled" : stateSuffix;
18 |
19 | let key = `rui_components.${themeProp}_${finalSuffix}`;
20 | if (typeof finalStateSuffix === "string") key += `_${finalStateSuffix}`;
21 |
22 | return getFromTheme(props, key);
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/docs/architecture/decisions/0008-keep-styles-with-components.md:
--------------------------------------------------------------------------------
1 | # 8. Keep Styles With Components
2 |
3 | Date: 2018-05-23
4 |
5 | ## Status
6 |
7 | STATUS:accepted
8 |
9 | 2018-05-22 proposed
10 | 2018-05-23 accepted
11 |
12 | ## Context
13 |
14 | So that developers do not have to think too hard, we want a simple rule about where the styles related to a React component should live. The options are "always in a separate styles.js file in the same folder" or "always in the same file as the React component, above the component". (This is referring to JSX or styled-components styles, and not CSS/SASS styles.)
15 |
16 | ### Separate File
17 |
18 | Pros:
19 |
20 | - Smaller files, easier to read
21 |
22 | Cons:
23 |
24 | - Overkill for a single style
25 | - More files for Babel to transform and watchers to watch
26 | - Extra work for dev (minimal)
27 |
28 | ### Same File
29 |
30 | Pros:
31 |
32 | - Simple and less work for dev
33 | - Fewer files for Babel to transform and watchers to watch
34 |
35 | Cons:
36 |
37 | - Larger files, harder to read (but you can code fold in IDE)
38 |
39 | ## Decision
40 |
41 | All styles always live in the same file as the React component, above the component.
42 |
--------------------------------------------------------------------------------
/package/src/components/SelectableList/v1/__snapshots__/SelectableList.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`basic snapshot 1`] = `
4 | .c1 {
5 | padding-bottom: 0;
6 | padding-left: 10px;
7 | padding-right: 10px;
8 | padding-top: 0;
9 | }
10 |
11 | .c0 {
12 | width: 100%;
13 | }
14 |
15 | .c0 fieldset {
16 | border-color: transparent;
17 | margin: 0;
18 | padding: 0;
19 | }
20 |
21 |
22 |
25 |
42 |
43 |
44 | `;
45 |
--------------------------------------------------------------------------------
/package/src/components/Price/v1/Price.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | The `Price` component will be used anywhere a product's price is displayed.
3 |
4 | ### Usage
5 |
6 | #### Default, without a Compare At price
7 |
8 | ```jsx
9 |
10 |
11 |
12 | ```
13 |
14 | #### With a price and Compare At price that are different
15 |
16 | ```jsx
17 |
18 |
19 |
20 | ```
21 |
22 | #### With a price and Compare At price are the equal
23 |
24 | The component expects string values of the prices to be strictly equal.
25 |
26 | ```jsx
27 |
28 |
29 |
30 | ```
31 |
32 | #### With the price below the Compare At price
33 |
34 | ```jsx
35 |
36 |
37 |
38 | ```
39 |
40 | ### Theme
41 |
42 | See [Theming Components](./#!/Theming%20Components).
43 |
44 | #### Typography
45 |
46 | - The price text uses `labelText` style with `rui_components.Price` override
47 | - The comparison price text uses `labelText` style with `rui_components.PriceCompare` override
48 |
--------------------------------------------------------------------------------
/package/src/components/ExampleIOUPaymentForm/v1/ExampleIOUPaymentForm.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 |
3 | The `ExampleIOUPaymentForm` component is intended to be used as the `InputComponent` for the `iou_example` payment method in a Reaction client UI. Provide it in the `paymentMethods` array passed to the [PaymentsCheckoutAction](./#!/PaymentsCheckoutAction) component.
4 |
5 | ### Usage
6 |
7 | ```jsx
8 | import Button from "../../Button/v1/Button";
9 | class Example extends React.Component {
10 | constructor(props) {
11 | super(props);
12 |
13 | this.state = { isReady: false };
14 | }
15 |
16 | render() {
17 | return (
18 |
30 | );
31 | }
32 | }
33 |
34 |
35 | ```
36 |
--------------------------------------------------------------------------------
/package/src/utils/getFromTheme.js:
--------------------------------------------------------------------------------
1 | import get from "lodash.get";
2 | import defaultComponentTheme from "../theme/defaultComponentTheme";
3 |
4 | /**
5 | * @summary Get a value from the theme, falling back to the default theme,
6 | * given the `props` and the object path.
7 | * @param {Object} props The props object, with `theme` prop present if
8 | * there is a custom styled-components theme provided by context.
9 | * @param {String} objectPath The path within the theme object from which to get a value
10 | * @returns {any} The value. If a value was not found in a custom theme or
11 | * the default theme, an error is thrown.
12 | */
13 | export default function getFromTheme(props, objectPath) {
14 | if (!props) throw new Error("Error in getFromTheme. props argument is required");
15 | if (typeof objectPath !== "string" || objectPath.length === 0) {
16 | throw new Error("Error in getFromTheme. objectPath argument must be a non-empty string");
17 | }
18 |
19 | const value = get(props, `theme.${objectPath}`) || get(defaultComponentTheme, objectPath, null);
20 | if (!value && value !== 0 && value !== false) {
21 | throw new Error(`Error in getFromTheme. Add ${objectPath} to defaultComponentTheme`);
22 | }
23 | return value;
24 | }
25 |
--------------------------------------------------------------------------------
/package/scripts/prebuild.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This script auto-creates an `index.js` file in each components/Component/v*
3 | * directory. It runs just before we do the Babel build for publishing.
4 | */
5 |
6 | const path = require("path");
7 | const fs = require("fs-extra");
8 |
9 | const SRC_PATH = path.join(process.cwd(), "src");
10 | const BASE_PATH = path.join(SRC_PATH, "components");
11 |
12 | // Loop through each item in the /package/src/components directory
13 | const directoryContents = fs.readdirSync(BASE_PATH);
14 | directoryContents.forEach((componentName) => {
15 | // Ignore files
16 | if (componentName.indexOf(".") !== -1) return;
17 |
18 | // For directories, loop through each item in them
19 | const compDirectoryContents = fs.readdirSync(path.join(BASE_PATH, componentName));
20 | compDirectoryContents.forEach((versionName) => {
21 | // Ignore anything that isn't a version directory
22 | if (!versionName.startsWith("v")) return;
23 |
24 | // In all version directories, auto-create an index.js file if there isn't one
25 | const filePath = path.join(BASE_PATH, componentName, versionName, "index.js");
26 | fs.ensureFileSync(filePath);
27 | fs.writeFileSync(filePath, `export { default } from "./${componentName}";\n`);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | Resolves #issueNumber
2 | Impact: **breaking|critical|major|minor**
3 | Type: **feature|bugfix|performance|test|style|refactor|docs|chore**
4 |
5 |
6 |
7 |
8 |
9 | ## Component
10 | Description of the issue this component this PR adds. Also include, as necessary:
11 | - Any other components this component requires
12 | - Any NPM dependencies added
13 |
14 | ## Screenshots
15 | Include mobile and desktop screenshots.
16 |
17 | ## Breaking changes
18 | You should almost never include "BREAKING CHANGES" because we’re duplicating components to avoid that. Consult with others before doing it.
19 |
20 | ## Testing
21 | 1. List the steps needed for testing your change in this section.
22 | 2. Assume that testers already know how to start the app, and do the basic setup tasks.
23 | 3. Be detailed enough that someone can work through it without being too granular
24 |
25 | 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)
26 |
--------------------------------------------------------------------------------
/package/src/components/CartEmptyMessage/v1/CartEmptyMessage.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | The `CartEmptyMessage` displays when viewing an empty shopping cart.
3 |
4 | ### Usage
5 |
6 | #### Default
7 |
8 | ```jsx
9 | const onClick = () => {};
10 |
11 |
14 | ```
15 |
16 | #### Custom cart and button message
17 |
18 | Pass in custom copy in `buttonText` and `messageText`.
19 |
20 | ```jsx
21 | const onClick = () => {};
22 |
23 |
28 | ```
29 |
30 | ### Theme
31 |
32 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
33 |
34 | | Theme Prop | Default | Description |
35 | | -------------------------------------- | ------- | ----------------------------------------------------------------------------- |
36 | | `CartEmptyMessage.textToButtonSpacing` | 54px | Vertical space between the bottom of the text block and the top of the button |
37 |
38 | #### Typography
39 |
40 | - The message uses `bodyText` style with `rui_components.CartEmptyMessage` override
41 |
--------------------------------------------------------------------------------
/package/src/components/ProfileImage/v1/ProfileImage.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | import ProfileImage from "./ProfileImage";
4 |
5 | const viewer = {
6 | firstName: "John",
7 | lastName: "Doe",
8 | name: "John Doe",
9 | primaryEmailAddress: "john@doe.com",
10 | profileImage: "https://www.gravatar.com/avatar/00000000000000000000000000000000?d=identicon&f=y"
11 | };
12 |
13 | const viewerInitials = {
14 | firstName: "John",
15 | lastName: "Doe",
16 | name: "John Doe",
17 | primaryEmailAddress: "john@doe.com"
18 | };
19 |
20 |
21 | test("ProfileImage component with image snapshot", () => {
22 | const component = renderer.create();
23 |
24 | const tree = component.toJSON();
25 | expect(tree).toMatchSnapshot();
26 | });
27 |
28 | test("ProfileImage component with custom size", () => {
29 | const component = renderer.create();
30 |
31 | const tree = component.toJSON();
32 | expect(tree).toMatchSnapshot();
33 | });
34 |
35 | test("ProfileImage component with initials snapshot", () => {
36 | const component = renderer.create();
37 |
38 | const tree = component.toJSON();
39 | expect(tree).toMatchSnapshot();
40 | });
41 |
--------------------------------------------------------------------------------
/.reaction/project-hooks/post-project-start:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Post Project Start Hook
3 | # Invoked by the reaction-next project bootstrapping process.
4 | #
5 | # Invoked after this service is started. Can be used for project specific
6 | # actions that should be performed after the project is running, like database
7 | # setup, migrations, seeds, etc. Do not depend on other projects in this hook!
8 | #
9 | # Important Notes:
10 | #
11 | # - The hook runs after all Docker Compose services in THIS project are
12 | # started. Though started, there is no guarantee that these services are
13 | # ready (i.e. that they will respond to requests.) It is your responsibility
14 | # to test that services are available before using them to avoid race
15 | # conditions.
16 | # - Do not assume that this hook script will run from this local directory.
17 | # The $__dir var is provided for convenience and may be used to invoke other
18 | # scripts.
19 | # - It is good practice to keep this script lightweight and invoke setup
20 | # scripts in your project.
21 |
22 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
24 | __root_name=$(basename "${__root_dir}")
25 |
26 | echo "${__root_name} post-project-start script invoked." 2>&1
27 |
--------------------------------------------------------------------------------
/package/src/components/StockWarning/v1/StockWarning.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "styled-components";
4 | import { addTypographyStyles } from "../../../utils";
5 |
6 | const Span = styled.div`
7 | ${addTypographyStyles("StockWarning", "labelText")}
8 | `;
9 |
10 | class StockWarning extends Component {
11 | static propTypes = {
12 | /**
13 | * You can provide a `className` prop that will be applied to the outermost DOM element
14 | * rendered by this component. We do not recommend using this for styling purposes, but
15 | * it can be useful as a selector in some situations.
16 | */
17 | className: PropTypes.string,
18 | /**
19 | * The product's current stock level
20 | */
21 | inventoryQuantity: PropTypes.number,
22 | /**
23 | * When true, indicates that a product's inventory level has reached
24 | * the low level threshold.
25 | */
26 | isLowInventoryQuantity: PropTypes.bool
27 | };
28 |
29 | render() {
30 | const { className, inventoryQuantity, isLowInventoryQuantity } = this.props;
31 |
32 | if (!isLowInventoryQuantity) return null;
33 |
34 | return Only {inventoryQuantity} in stock;
35 | }
36 | }
37 |
38 | export default StockWarning;
39 |
--------------------------------------------------------------------------------
/package/src/components/InventoryStatus/v1/utils/inventoryStatus.test.js:
--------------------------------------------------------------------------------
1 | import inventoryStatus from "./inventoryStatus";
2 | import STATUS_TYPES from "./statusTypes";
3 | import STATUS_LABELS from "./statusLabels";
4 |
5 | const backorderProduct = { isSoldOut: true, isBackorder: true };
6 | const soldOutProduct = { isSoldOut: true, isBackorder: false };
7 | const isLowQuantity = { isLowQuantity: true };
8 |
9 |
10 | test("inventoryStatus util should return `backorder` status", () => {
11 | const callFunction = inventoryStatus(backorderProduct, STATUS_LABELS);
12 |
13 | expect(typeof inventoryStatus).toBe("function");
14 | expect(callFunction).toEqual({ type: STATUS_TYPES.BACKORDER, label: "Backordered - ships when available" });
15 | });
16 |
17 | test("inventoryStatus util should return `sold out` status", () => {
18 | const callFunction = inventoryStatus(soldOutProduct, STATUS_LABELS);
19 |
20 | expect(typeof inventoryStatus).toBe("function");
21 | expect(callFunction).toEqual({ type: STATUS_TYPES.SOLD_OUT, label: "Out of stock" });
22 | });
23 |
24 | test("inventoryStatus util should return `low inventory` status", () => {
25 | const callFunction = inventoryStatus(isLowQuantity, STATUS_LABELS);
26 |
27 | expect(typeof inventoryStatus).toBe("function");
28 | expect(callFunction).toEqual({ type: STATUS_TYPES.LOW_QUANTITY, label: "Low Inventory" });
29 | });
30 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutTopHat/v1/__snapshots__/CheckoutTopHat.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`render Top Hat with message 1`] = `
4 | .c0 {
5 | background-color: #f5f5f5;
6 | display: -webkit-box;
7 | display: -webkit-flex;
8 | display: -ms-flexbox;
9 | display: flex;
10 | height: 35px;
11 | -webkit-box-pack: center;
12 | -webkit-justify-content: center;
13 | -ms-flex-pack: center;
14 | justify-content: center;
15 | width: 100%;
16 | }
17 |
18 | .c1 {
19 | -webkit-font-smoothing: antialiased;
20 | color: #3c3c3c;
21 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif;
22 | font-size: 14px;
23 | font-style: normal;
24 | font-stretch: normal;
25 | font-weight: 700;
26 | -webkit-letter-spacing: .02em;
27 | -moz-letter-spacing: .02em;
28 | -ms-letter-spacing: .02em;
29 | letter-spacing: .02em;
30 | line-height: 1.25;
31 | -webkit-align-items: center;
32 | -webkit-box-align: center;
33 | -ms-flex-align: center;
34 | align-items: center;
35 | display: -webkit-box;
36 | display: -webkit-flex;
37 | display: -ms-flexbox;
38 | display: flex;
39 | }
40 |
41 |
44 |
47 | Free Shipping + Free Returns
48 |
49 |
50 | `;
51 |
52 | exports[`render nothing when no message is present 1`] = `null`;
53 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutActionIncomplete/v1/CheckoutActionIncomplete.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "styled-components";
4 | import { addTypographyStyles } from "../../../utils";
5 |
6 | const CheckoutActionIncompleteContainer = styled.div`
7 | ${addTypographyStyles("CheckoutActionIncomplete", "captionText")}
8 | `;
9 |
10 | class CheckoutActionIncomplete extends Component {
11 | static propTypes = {
12 | /**
13 | * You can provide a `className` prop that will be applied to the outermost DOM element
14 | * rendered by this component. We do not recommend using this for styling purposes, but
15 | * it can be useful as a selector in some situations.
16 | */
17 | className: PropTypes.string,
18 | /**
19 | * The incomplete action name
20 | */
21 | label: PropTypes.string,
22 | /**
23 | * Checkout process step number
24 | */
25 | stepNumber: PropTypes.number
26 | };
27 |
28 | render() {
29 | const { className, label, stepNumber } = this.props;
30 | const stepAndLabel = stepNumber ? `${stepNumber}. ${label || ""}` : label;
31 |
32 | return (
33 |
34 | {stepAndLabel}
35 |
36 | );
37 | }
38 | }
39 |
40 | export default CheckoutActionIncomplete;
41 |
--------------------------------------------------------------------------------
/package/src/components/ShopLogo/v1/ShopLogo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "styled-components";
4 | import { addTypographyStyles, applyTheme } from "../../../utils";
5 |
6 | const Container = styled.div`
7 | ${addTypographyStyles("ShopLogo", "titleTextBold")}
8 | `;
9 |
10 | const Logo = styled.img`
11 | height: ${applyTheme("ShopLogo.height")};
12 | `;
13 |
14 | export default class ShopLogo extends Component {
15 | static propTypes = {
16 | /**
17 | * You can provide a `className` prop that will be applied to the outermost DOM element
18 | * rendered by this component. We do not recommend using this for styling purposes, but
19 | * it can be useful as a selector in some situations.
20 | */
21 | className: PropTypes.string,
22 | /**
23 | * The primary shop's logo url
24 | */
25 | shopLogoUrl: PropTypes.string,
26 | /**
27 | * The primary shop's name
28 | */
29 | shopName: PropTypes.string.isRequired
30 | }
31 |
32 | render() {
33 | const { className, shopLogoUrl, shopName } = this.props;
34 |
35 | return (
36 |
37 | {
38 | shopLogoUrl ? (
39 |
40 | ) : (
41 | shopName
42 | )
43 | }
44 |
45 | );
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/docs/architecture/decisions/0003-choose-a-style-guide-generator-framework.md:
--------------------------------------------------------------------------------
1 | # 3. Choose a Style Guide Generator Framework
2 |
3 | Date: 2018-02-23
4 |
5 | ## Status
6 |
7 | STATUS:accepted
8 |
9 | 2018-02-23 proposed
10 | 2018-03-01 accepted
11 |
12 | ## Context
13 |
14 | We want:
15 |
16 | - Write all simple React components in one repository
17 | - Document the React components with code comments
18 | - Add additional markdown documentation for components when necessary
19 | - Allow both designers and engineers to edit the docs
20 | - Run the tool locally to make component development and testing easier
21 | - Build into a hostable web app, which can be used by anyone to learn our style, pick an appropriate component, and edit the component on the page
22 | - Be able to style/theme anything about the style guide app as a whole to match our other docs
23 |
24 | ### Options
25 |
26 | [React Storybook](https://storybook.js.org/)
27 | [React Styleguidist](https://react-styleguidist.js.org/)
28 |
29 | ## Decision
30 |
31 | Use Styleguidist. They way it is built from markdown is more user-friendly for designers to edit vs. React Storybook. Also, it is more aimed at generating a living style guide, whereas Storybook is more of a developer's tool.
32 |
33 | ## Consequences
34 |
35 | Styleguidist seems to be missing the action logger the Storybook has, which is useful, but we should be able to easily develop a plugin for this if one does not exist.
36 |
--------------------------------------------------------------------------------
/package/src/components/Accordion/v1/Accordion.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 |
3 | #### Usage
4 |
5 | ```jsx
6 | const props = {
7 | className: "address-book-option",
8 | label: "Susan Doe",
9 | detail: "2300 Buckwheat Ave, Salt Lake Sity, UT 84111 USA"
10 | };
11 |
12 | Content;
13 | ```
14 |
15 | **Accordion list**
16 |
17 | ```jsx
18 | const accordion1 = {
19 | className: "address-book-option",
20 | label: "Susan Doe",
21 | detail: "2300 Buckwheat Ave, Salt Lake Sity, UT 84111 USA"
22 | };
23 |
24 | const accordion2 = {
25 | className: "something-else",
26 | label: "French Market",
27 | detail: "700-1010 Decatur St, New Orleans, LA 70116"
28 | };
29 |
30 |
31 | Content
32 | Content
33 |
;
34 | ```
35 |
36 | ### Theme
37 |
38 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
39 |
40 | | Theme Prop | Default | Description |
41 | | ------------------------ | --------- | ------------------------------- |
42 | | `Accordion.borderColor` | `black10` | Border color for the Accordion |
43 | | `Accordion.borderStyle` | `solid` | Border style for the Accordion |
44 | | `Accordion.borderWidth` | `1px` | Border width for the Accordion |
45 | | `Accordion.borderRadius` | `2px` | Border radius for the Accordion |
46 |
--------------------------------------------------------------------------------
/package/src/components/Link/v1/Link.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { shallow } from "enzyme";
3 | import renderer from "react-test-renderer";
4 | import Link from "./Link";
5 |
6 | test("Link component with image snapshot", () => {
7 | const component = renderer.create((
8 |
9 |
10 |
11 | ));
12 |
13 | const tree = component.toJSON();
14 | expect(tree).toMatchSnapshot();
15 | });
16 |
17 | test("Link component with text snapshot", () => {
18 | const component = renderer.create((
19 | Click here
20 | ));
21 |
22 | const tree = component.toJSON();
23 | expect(tree).toMatchSnapshot();
24 | });
25 |
26 | test("Link component with onClick hander", () => {
27 | const testClickHandler = jest.fn();
28 | const component = shallow((
29 | Click here
30 | ));
31 |
32 | component.simulate("click");
33 |
34 | expect(testClickHandler).toHaveBeenCalled();
35 | });
36 |
37 | test("Link component with onClick hander and no href", () => {
38 | const testClickHandler = jest.fn();
39 | const component = shallow((
40 | Click here
41 | ));
42 |
43 | component.simulate("click");
44 |
45 | expect(testClickHandler).toHaveBeenCalled();
46 | });
47 |
--------------------------------------------------------------------------------
/package/src/components/CheckoutTopHat/v1/CheckoutTopHat.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "styled-components";
4 | import { addTypographyStyles, applyTheme } from "../../../utils";
5 |
6 | const TopHatContainer = styled.div`
7 | background-color: ${applyTheme("CheckoutTopHat.backgroundColor")};
8 | display: flex;
9 | height: ${applyTheme("CheckoutTopHat.height")};
10 | justify-content: center;
11 | width: 100%;
12 | `;
13 |
14 | const TopHatMessage = styled.div`
15 | ${addTypographyStyles("CheckoutTopHatMessage", "labelTextBold")}
16 | align-items: center;
17 | display: flex;
18 | `;
19 |
20 | class CheckoutTopHat extends Component {
21 | static propTypes = {
22 | checkoutMessage: PropTypes.string,
23 | /**
24 | * You can provide a `className` prop that will be applied to the outermost DOM element
25 | * rendered by this component. We do not recommend using this for styling purposes, but
26 | * it can be useful as a selector in some situations.
27 | */
28 | className: PropTypes.string
29 | };
30 |
31 | render() {
32 | const { className, checkoutMessage } = this.props;
33 |
34 | if (checkoutMessage) {
35 | return (
36 |
37 | {checkoutMessage}
38 |
39 | );
40 | }
41 |
42 | return null;
43 | }
44 | }
45 |
46 | export default CheckoutTopHat;
47 |
--------------------------------------------------------------------------------
/docs/architecture/decisions/0006-style-components.md:
--------------------------------------------------------------------------------
1 | # 6. Style Components
2 |
3 | Date: 2018-02-23
4 |
5 | ## Status
6 |
7 | STATUS:accepted
8 |
9 | 2018-02-23 proposed
10 | 2018-03-01 accepted
11 |
12 | ## Context
13 |
14 | These are our requirements for component styling:
15 |
16 | - A component has baked-in styles that make it look good out of the box. These are used in the Style Guide app.
17 | - Some but not all aspects of a component's style are overrideable by the user, i.e., theming
18 | - Try as much as possible to isolate components from any generic app styles. For example, when rendered in an app that pulls in all Bootstrap CSS, it should still appear as expected. Conversely, no styles included with the component should affect the appearance of any other component in an app.
19 |
20 | Also potential requirement? Works in a React Native app
21 |
22 | ## Decision
23 |
24 | Use [styled-components](https://www.styled-components.com/)
25 |
26 | The primary reason to use styled-components library is because they have a good theming solution that works for our use case: https://www.styled-components.com/docs/advanced#theming
27 |
28 | Also:
29 |
30 | - Uses normal CSS that people know
31 | - Supports media queries, :hover, animations, etc.
32 | - Injects stylesheets that take precedence over global stylesheets, though you can override them in a global stylesheet in an emergency if you use tricks.
33 | - Server side rendering
34 | - Works with React Native if we eventually try to make the components universal
35 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Reaction Storefront Component Library - Contributor Docs
2 |
3 | ## Getting Started
4 |
5 | - [Cloning the Project for Development](./clone.md)
6 | - [Running and Developing Locally](./developing.md)
7 | - [Repo Structure](./repo-structure.md)
8 | - [Style Guide Development](./style-guide-development.md)
9 | - [Contributing](./contributing.md)
10 |
11 | ### Editor Extensions
12 |
13 | https://www.styled-components.com/docs/tooling#syntax-highlighting
14 |
15 | ## Creating and Modifying Components
16 |
17 | - [Writing a Component ticket](../.github/ISSUE_TEMPLATE/component_request.md)
18 | - [Creating a New Component](./creating-new-component.md)
19 | - [Creating a New Version of a Component](./creating-new-component-version.md)
20 | - [Component Development Guidelines](./component-development-guidelines.md)
21 |
22 | ## Component Styling Conventions
23 |
24 | - [Styling Conventions](./styling-conventions.md)
25 |
26 | ## Component Review process
27 |
28 | - [Reviewing Components](./reviewing-components.md)
29 | - [Browser Compatibility](./browser-compatibility.md)
30 |
31 | ## Architectural Decisions Records
32 |
33 | Information around the architectural decisions made for this project should be
34 | added to the [architecture/decisions](./architecture/decisions) directory.
35 |
36 | The NPM package [adr](https://www.npmjs.com/package/adr) is installed with this
37 | project and can be used to add new decisions.
38 |
39 | ```sh
40 | docker-compose run --rm web adr new "Implement the Torpedos"
41 | ```
42 |
--------------------------------------------------------------------------------
/bin/yarn-link:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | # Helper to automatically link any NPM modules that are in development.
3 | #
4 | # Mounting Custom Modules to the Docker Container:
5 | # Custom NPM modules must be mounted into the Docker container at
6 | #
7 | # /usr/local/src/${module}
8 | #
9 | # Here's an example Docker Compose mount for two modules, rimraf and eslint:
10 | #
11 | # volumes:
12 | # - web-yarn:/home/node/.cache/yarn
13 | # - .:/usr/local/src/reaction-app
14 | # - ../rimraf:/usr/local/src/rimraf
15 | # - ../eslint:/usr/local/src/eslint
16 | #
17 | #
18 | # Run this Script on Container Start:
19 | # Add this script to the Docker CMD to ensure that it links all modules
20 | # before starting the project process.
21 | #
22 | # Example command:
23 | #
24 | # command: [sh, -c, "bin/yarn-link && yarn run build && yarn run start"]
25 |
26 |
27 | # NPM modules that will be linked.
28 | # Set in .env
29 | custom_modules="${LINKED_NPM_MODULES}"
30 |
31 | custom_modules_folder=/usr/local/src
32 | modules_folder=/usr/local/src/node_modules
33 | project_folder=/usr/local/src/reaction-app
34 |
35 | for module in ${custom_modules}; do
36 | cd "${custom_modules_folder}/${module}" \
37 | || ( echo "Attempted to link NPM module that doesn't exist" 2>&1 && exit 1 )
38 | yarn --modules-folder "${modules_folder}" link
39 | cd "${project_folder}" \
40 | || ( echo "Project directory doesn't exist while NPM module linking!" 2>&1 && exit 1 )
41 | yarn --modules-folder "${modules_folder}" link ${module}
42 | done
43 |
--------------------------------------------------------------------------------
/package/src/components/StripePaymentInput/v1/StripePaymentInput.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 |
3 | The `StripePaymentInput` component is intended to be used as the `InputComponent` for the `stripe_card` payment method in a Reaction client UI. Provide it in the `paymentMethods` array passed to the [PaymentsCheckoutAction](./#!/PaymentsCheckoutAction) component.
4 |
5 | ### Usage
6 |
7 | ```jsx
8 | import Button from "../../Button/v1/Button";
9 | class Example extends React.Component {
10 | constructor(props) {
11 | super(props);
12 |
13 | this.state = { isReady: false };
14 | }
15 |
16 | render() {
17 | return (
18 |
,
64 | ]
65 | `;
66 |
--------------------------------------------------------------------------------
/package/src/components/Address/v1/Address.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import renderer from "react-test-renderer";
3 | // import { shallow } from "enzyme";
4 | import Address from "./Address";
5 |
6 | const mockAddress = {
7 | _id: "1",
8 | address1: "7742 Hwy 23",
9 | address2: "",
10 | country: "US",
11 | city: "Belle Chasse",
12 | fullName: "Salvos Seafood",
13 | postal: "70037",
14 | region: "LA",
15 | phone: "(504) 393-7303"
16 | };
17 |
18 | test("basic snapshot with required props", () => {
19 | const component = renderer.create();
20 |
21 | const tree = component.toJSON();
22 | expect(tree).toMatchSnapshot();
23 | });
24 |
25 | test("basic snapshot with is flat prop", () => {
26 | const component = renderer.create();
27 |
28 | const tree = component.toJSON();
29 | expect(tree).toMatchSnapshot();
30 | });
31 |
32 | test("basic snapshot with address order prop", () => {
33 | const addressOrder = ["fullName", "phone"];
34 | const address = mockAddress;
35 | const component = renderer.create();
36 |
37 | const tree = component.toJSON();
38 | expect(tree).toMatchSnapshot();
39 | });
40 |
41 | test("basic snapshot with invalid address properties prop", () => {
42 | const invalidAddressProperties = ["country", "address1"];
43 | const address = mockAddress;
44 | const component = renderer.create();
45 |
46 | const tree = component.toJSON();
47 | expect(tree).toMatchSnapshot();
48 | });
49 |
--------------------------------------------------------------------------------
/docs/creating-new-component-version.md:
--------------------------------------------------------------------------------
1 | ## Creating a New Version of a Component
2 |
3 | When a component needs to be updated, first determine whether the changes can be made in a backwards compatible way. This includes not changing styles in an unexpected way. For example, fixing a bug where the component did not look or work correctly in a particular browser is backwards compatible. However, changing component styles so that it now looks different from before is a breaking change.
4 |
5 | The goal is to allow any app depending on this library to take new minor or patch versions without worrying about anything magically looking different, unless it's an appearance fix. To do this, we create new versions of components within the codebase by copying all of their related files to a `v#` folder, and all imports include this version.
6 |
7 | ### Create the Component Files
8 |
9 | Only when you have to make changes that are not backwards compatible, you can use the following script to copy the most recent version folder for a component to a next version folder:
10 |
11 | ```bash
12 | docker-compose run --rm web node .reaction/scripts/addcomponent MyComponent next
13 | ```
14 |
15 | Where `MyComponent` is the name of the component, which must already exist and have at least one `v`-prefixed subfolder.
16 |
17 | ### Make Changes
18 |
19 | In either a newly created version of the component or the existing component, for non-breaking changes, change the documentation and tests to reflect the desired changes. Then change the component code until all tests pass again.
20 |
21 | Refer to the steps in [Creating a New Component](./creating-new-component)
22 |
--------------------------------------------------------------------------------
/package/src/components/GuestForm/v1/GuestForm.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | The `GuestForm` component is based on the [Composable Form Spec](http://forms.dairystatedesigns.com/) and uses [reacto-form](http://forms.dairystatedesigns.com/reacto-form/) to handle form state and validation.
3 |
4 | ### Usage
5 | The `GuestForm` is a simple form that captures a users email address.
6 | ```jsx
7 | console.log("GuestForm value", value)} />
8 | ```
9 |
10 | #### Saving example
11 | Using an `async` function or a function that returns a `Promise` will make the form wait until the async task has completed before clearing the form.
12 | This also provides a way to display a saving state while waiting.
13 | ```jsx
14 | initialState = { isSavingEmail:false };
15 |
16 | const setEmailAddress = (value) => new Promise((resolve, reject) => {
17 | setState({isSavingEmail: true});
18 | setTimeout(() => {
19 | console.log("GuestForm value", value)
20 | setState({isSavingEmail: false});
21 | resolve(value);
22 | }, 5000);
23 | });
24 |
25 |
26 | ```
27 |
28 | ### Theme
29 |
30 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
31 |
32 | | Theme Prop | Default | Description |
33 | | -------------------- | ------- | ---------------------------------------------------------------------- |
34 | | `rui_breakpoints.sm` | 320px | Below this breakpoint, the component renders the button as full width. |
35 |
--------------------------------------------------------------------------------
/package/src/components/InPageMenuItem/v1/InPageMenuItem.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | This component renders an item in an In-Page Menu list.
3 |
4 | #### Usage
5 |
6 | Can be used in the `InPageMenu` to provide a side-bar sub-navigation for a page.
7 |
8 | ```jsx
9 |
10 | ```
11 |
12 | #### With onClick
13 | ```jsx
14 | alert("onClick() fired")} />
15 | ```
16 |
17 | #### Selected Item
18 | ```jsx
19 | alert("onClick() fired")} />
20 | ```
21 |
22 | #### In a list / InPageMenu
23 | ```jsx
24 |
36 | Ms. Jane Doe
37 | 123 Main Street
38 | Anytown, USA 01776
39 |
40 | );
41 |
42 |
43 |
48 |
49 | ```
50 |
51 | ### Theme
52 |
53 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components).
54 |
55 | | Theme Prop | Default | Description |
56 | | ------------------------------------- | ------- | -------------------------------------------------------------------- |
57 | | `CheckoutActionComplete.mobileMargin` | 10px | Bottom margin prior to the "md" breakpoint |
58 | | `rui_breakpoints.sm` | 960px | Used to determine when the component begins to set the mobile margin |
59 |
60 | #### Typography
61 |
62 | - The details text uses `labelText` style with `rui_components.CheckoutActionCompleteDetail` override
63 | - The title text uses `labelText` style with `rui_components.CheckoutActionCompleteTitle` override
64 |
--------------------------------------------------------------------------------