├── package ├── CHANGELOG.md ├── src │ ├── components │ │ ├── Link │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── Link.md │ │ │ │ ├── __snapshots__ │ │ │ │ └── Link.test.js.snap │ │ │ │ ├── Link.test.js │ │ │ │ └── Link.js │ │ ├── Address │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── Address.test.js │ │ ├── Button │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── utils │ │ │ │ ├── index.js │ │ │ │ └── applyThemeWithActionType.js │ │ │ │ └── Button.test.js │ │ ├── Field │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── Price │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── Price.test.js │ │ │ │ └── Price.md │ │ ├── Select │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── Accordion │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── Accordion.test.js │ │ │ │ └── Accordion.md │ │ ├── CartItem │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── CartItem.test.js │ │ ├── CartItems │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── __snapshots__ │ │ │ │ └── CartItems.test.js.snap │ │ ├── Checkbox │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── Checkbox.test.js │ │ ├── GuestForm │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── GuestForm.test.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── GuestForm.test.js.snap │ │ │ │ └── GuestForm.md │ │ ├── MiniCart │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── MiniCart.test.js │ │ ├── ShopLogo │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── ShopLogo.test.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── ShopLogo.test.js.snap │ │ │ │ ├── ShopLogo.md │ │ │ │ └── ShopLogo.js │ │ ├── TextInput │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── AddressBook │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── AddressBook.test.js.snap │ │ │ │ └── AddressBook.test.js │ │ ├── AddressForm │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── BadgeOverlay │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── utils │ │ │ │ ├── badgeLabels.js │ │ │ │ ├── badgeTypes.js │ │ │ │ ├── index.js │ │ │ │ ├── isProductLowQuantity.js │ │ │ │ ├── badgeTypes.test.js │ │ │ │ ├── badgeLabels.test.js │ │ │ │ ├── isProductBestseller.js │ │ │ │ ├── isProductBestseller.test.js │ │ │ │ ├── isProductLowQuantity.test.js │ │ │ │ ├── badgeStatus.js │ │ │ │ └── badgeStatus.test.js │ │ ├── CartSummary │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── CartSummary.test.js │ │ ├── CatalogGrid │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── ErrorsBlock │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── ErrorsBlock.test.js │ │ │ │ └── __snapshots__ │ │ │ │ └── ErrorsBlock.test.js.snap │ │ ├── InPageMenu │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── InPageMenu.md │ │ │ │ ├── __snapshots__ │ │ │ │ └── InPageMenu.test.js.snap │ │ │ │ └── InPageMenu.test.js │ │ ├── InlineAlert │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── MultiSelect │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── MultiSelect.test.js │ │ ├── ProfileImage │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── ProfileImage.test.js │ │ │ │ └── ProfileImage.md │ │ ├── RegionInput │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── __snapshots__ │ │ │ │ └── RegionInput.test.js.snap │ │ ├── StockWarning │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── StockWarning.md │ │ │ │ ├── __snapshots__ │ │ │ │ └── StockWarning.test.js.snap │ │ │ │ ├── StockWarning.test.js │ │ │ │ └── StockWarning.js │ │ ├── StripeForm │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── ViewerInfo │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── AddressCapture │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── AddressCapture.test.js.snap │ │ │ │ └── AddressCapture.test.js │ │ ├── AddressChoice │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── __snapshots__ │ │ │ │ └── AddressChoice.test.js.snap │ │ ├── AddressReview │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── AddressReview.test.js.snap │ │ │ │ └── AddressReview.test.js │ │ ├── CartItemDetail │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── CheckoutAction │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── CheckoutTopHat │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── CheckoutTopHat.test.js │ │ │ │ ├── CheckoutTopHat.md │ │ │ │ ├── __snapshots__ │ │ │ │ └── CheckoutTopHat.test.js.snap │ │ │ │ └── CheckoutTopHat.js │ │ ├── InPageMenuItem │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── InPageMenuItem.test.js.snap │ │ │ │ ├── InPageMenuItem.test.js │ │ │ │ └── InPageMenuItem.md │ │ ├── QuantityInput │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── QuantityInput.test.js │ │ │ │ └── QuantityInput.md │ │ ├── SelectableItem │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── SelectableItem.test.js │ │ ├── SelectableList │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── SelectableList.test.js │ │ │ │ └── __snapshots__ │ │ │ │ └── SelectableList.test.js.snap │ │ ├── AccordionFormList │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── AccordionFormList.test.js │ │ │ │ └── __snapshots__ │ │ │ │ └── AccordionFormList.test.js.snap │ │ ├── CartEmptyMessage │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── CartEmptyMessage.test.js │ │ │ │ └── CartEmptyMessage.md │ │ ├── CatalogGridItem │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── utils │ │ │ │ ├── index.js │ │ │ │ └── priceByCurrencyCode.js │ │ │ │ └── __snapshots__ │ │ │ │ └── CatalogGridItem.test.js.snap │ │ ├── CheckoutActions │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── CheckoutActions.test.js │ │ │ │ └── __snapshots__ │ │ │ │ └── CheckoutActions.test.js.snap │ │ ├── InventoryStatus │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── utils │ │ │ │ ├── statusTypes.js │ │ │ │ ├── statusLabels.js │ │ │ │ ├── index.js │ │ │ │ ├── isProductLowQuantity.js │ │ │ │ ├── statusTypes.test.js │ │ │ │ ├── statusLabels.test.js │ │ │ │ ├── isProductBestseller.js │ │ │ │ ├── isProductBestseller.test.js │ │ │ │ ├── isProductLowQuantity.test.js │ │ │ │ ├── inventoryStatus.js │ │ │ │ └── inventoryStatus.test.js │ │ │ │ ├── InventoryStatus.md │ │ │ │ └── __snapshots__ │ │ │ │ └── InventoryStatus.test.js.snap │ │ ├── MiniCartSummary │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── MiniCartSummary.test.js │ │ ├── PhoneNumberInput │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── PhoneNumberInput.test.js │ │ ├── ProgressiveImage │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── ProgressiveImage.test.js │ │ ├── AccountProfileInfo │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── StripePaymentInput │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── StripePaymentInput.test.js.snap │ │ │ │ └── StripePaymentInput.md │ │ ├── CheckoutActionComplete │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── CheckoutActionComplete.test.js │ │ │ │ └── CheckoutActionComplete.md │ │ ├── CheckoutEmailAddress │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── CheckoutEmailAddress.test.js │ │ │ │ └── CheckoutEmailAddress.md │ │ ├── ExampleIOUPaymentForm │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── ExampleIOUPaymentForm.test.js.snap │ │ │ │ └── ExampleIOUPaymentForm.md │ │ ├── PaymentsCheckoutAction │ │ │ └── v1 │ │ │ │ └── index.js │ │ ├── CheckoutActionIncomplete │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── CheckoutActionIncomplete.test.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── CheckoutActionIncomplete.test.js.snap │ │ │ │ ├── CheckoutActionIncomplete.md │ │ │ │ └── CheckoutActionIncomplete.js │ │ ├── FinalReviewCheckoutAction │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── FinalReviewCheckoutAction.test.js.snap │ │ │ │ └── FinalReviewCheckoutAction.test.js │ │ ├── StripePaymentCheckoutAction │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ └── StripePaymentCheckoutAction.test.js │ │ ├── ShippingAddressCheckoutAction │ │ │ └── v1 │ │ │ │ ├── index.js │ │ │ │ ├── __snapshots__ │ │ │ │ └── ShippingAddressCheckoutAction.test.js.snap │ │ │ │ └── ShippingAddressCheckoutAction.test.js │ │ └── FulfillmentOptionsCheckoutAction │ │ │ └── v1 │ │ │ └── index.js │ ├── index.js │ ├── setupTests.js │ ├── svg │ │ ├── iconExpand.js │ │ ├── iconLock.js │ │ ├── iconDismiss.js │ │ ├── iconPlus.js │ │ ├── iconClear.js │ │ ├── iconError.js │ │ ├── spinner.js │ │ └── iconValid.js │ ├── utils │ │ ├── preventAccidentalDoubleClick.js │ │ ├── index.js │ │ ├── withStripeElements.js │ │ ├── applyTheme.js │ │ ├── getRequiredValidator.js │ │ ├── addressToString.js │ │ ├── getPhoneNumberValidator.js │ │ ├── getFromTheme.js │ │ └── formatMoney.js │ └── theme │ │ └── padding.js ├── .snyk ├── scripts │ └── prebuild.js └── README.md ├── .dockerignore ├── styleguide └── src │ ├── sections │ ├── Product.md │ ├── Cart.md │ ├── Checkout.md │ ├── Content.md │ ├── General.md │ ├── Account.md │ ├── Actions.md │ ├── Forms.md │ ├── Menus.md │ ├── StorefrontForms.md │ ├── Introduction.md │ └── LocalDevelopment.md │ ├── assets │ ├── favicon.ico │ ├── favicon.png │ ├── images │ │ ├── mug │ │ │ ├── large.jpg │ │ │ ├── small.png │ │ │ ├── medium.jpg │ │ │ └── thumbnail.png │ │ ├── beans │ │ │ ├── large.jpg │ │ │ ├── small.png │ │ │ ├── medium.jpg │ │ │ └── thumbnail.png │ │ ├── placeholder.gif │ │ ├── pouch │ │ │ ├── large.jpg │ │ │ ├── small.png │ │ │ ├── medium.jpg │ │ │ └── thumbnail.png │ │ ├── portrait │ │ │ ├── large.jpg │ │ │ ├── small.png │ │ │ ├── medium.jpg │ │ │ └── thumbnail.png │ │ ├── sticker │ │ │ ├── large.jpg │ │ │ ├── medium.jpg │ │ │ ├── small.png │ │ │ └── thumbnail.png │ │ ├── landscape │ │ │ ├── large.jpg │ │ │ ├── medium.jpg │ │ │ ├── small.png │ │ │ └── thumbnail.png │ │ ├── mens-shirt │ │ │ ├── large.jpg │ │ │ ├── small.png │ │ │ ├── medium.jpg │ │ │ └── thumbnail.png │ │ └── womens-shirt │ │ │ ├── large.jpg │ │ │ ├── medium.jpg │ │ │ ├── small.png │ │ │ └── thumbnail.png │ ├── colors │ │ ├── dark-colors.png │ │ ├── grey-scale.png │ │ ├── medium-colors.png │ │ └── support-colors.png │ ├── fonts │ │ └── post-grotesk │ │ │ ├── PostGrotesk-Bold.eot │ │ │ ├── PostGrotesk-Bold.woff │ │ │ ├── PostGrotesk-Book.eot │ │ │ ├── PostGrotesk-Book.woff │ │ │ ├── PostGrotesk-Light.eot │ │ │ ├── PostGrotesk-Light.woff │ │ │ ├── PostGrotesk-Medium.eot │ │ │ ├── PostGrotesk-Medium.woff │ │ │ ├── PostGrotesk-BoldItalic.eot │ │ │ ├── PostGrotesk-BookItalic.eot │ │ │ ├── PostGrotesk-BoldItalic.woff │ │ │ ├── PostGrotesk-BookItalic.woff │ │ │ ├── PostGrotesk-LightItalic.eot │ │ │ ├── PostGrotesk-LightItalic.woff │ │ │ ├── PostGrotesk-MediumItalic.eot │ │ │ └── PostGrotesk-MediumItalic.woff │ └── manifest.json │ ├── components │ ├── README.md │ ├── Wrapper.js │ ├── utils │ │ └── locales │ │ │ └── pottermore.json │ ├── TwoColumnExamples.js │ └── withLocales.js │ └── styles.css ├── .reaction ├── scripts │ └── templates │ │ ├── index.js.template │ │ ├── Component.test.js.template │ │ ├── Component.md.template │ │ └── Component.js.template ├── yarnrc-docker.template └── project-hooks │ ├── post-build │ ├── pre-build │ ├── post-system-start │ ├── post-project-start │ └── README.md ├── docs ├── architecture │ ├── README.md │ └── decisions │ │ ├── README.md │ │ ├── 0005-test-components.md │ │ ├── 0008-keep-styles-with-components.md │ │ ├── 0003-choose-a-style-guide-generator-framework.md │ │ ├── 0006-style-components.md │ │ └── 0004-define-project-structure.md ├── browser-compatibility.md ├── clone.md ├── reviewing-components.md ├── style-guide-development.md ├── README.md ├── creating-new-component-version.md └── repo-structure.md ├── .adr.json ├── .env.example ├── babel.jest.js ├── netlify.toml ├── .editorconfig ├── config ├── jest │ ├── fileTransform.js │ └── cssTransform.js └── polyfills.js ├── .gitignore ├── bin ├── setup ├── build.sh └── yarn-link ├── .github ├── ISSUE_TEMPLATE │ ├── component_request.md │ └── bug_report.md └── pull_request_template.md └── docker-compose.yml /package/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /styleguide/src/sections/Product.md: -------------------------------------------------------------------------------- 1 | TODO Write introduction to Product 2 | -------------------------------------------------------------------------------- /package/src/components/Link/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Link"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Cart.md: -------------------------------------------------------------------------------- 1 | TODO: Add information related to the Cart 2 | -------------------------------------------------------------------------------- /package/src/components/Address/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Address"; 2 | -------------------------------------------------------------------------------- /package/src/components/Button/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Button"; 2 | -------------------------------------------------------------------------------- /package/src/components/Field/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Field"; 2 | -------------------------------------------------------------------------------- /package/src/components/Price/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Price"; 2 | -------------------------------------------------------------------------------- /package/src/components/Select/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Select"; 2 | -------------------------------------------------------------------------------- /.reaction/scripts/templates/index.js.template: -------------------------------------------------------------------------------- 1 | export { default } from "./COMPONENT"; 2 | -------------------------------------------------------------------------------- /package/src/components/Accordion/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Accordion"; 2 | -------------------------------------------------------------------------------- /package/src/components/CartItem/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartItem"; 2 | -------------------------------------------------------------------------------- /package/src/components/CartItems/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartItems"; 2 | -------------------------------------------------------------------------------- /package/src/components/Checkbox/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Checkbox"; 2 | -------------------------------------------------------------------------------- /package/src/components/GuestForm/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GuestForm"; 2 | -------------------------------------------------------------------------------- /package/src/components/MiniCart/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MiniCart"; 2 | -------------------------------------------------------------------------------- /package/src/components/ShopLogo/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ShopLogo"; 2 | -------------------------------------------------------------------------------- /package/src/components/TextInput/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TextInput"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Checkout.md: -------------------------------------------------------------------------------- 1 | TODO: Add information related to the Checkout 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Content.md: -------------------------------------------------------------------------------- 1 | These components render various types of content. 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/General.md: -------------------------------------------------------------------------------- 1 | TODO Write introduction General components 2 | 3 | -------------------------------------------------------------------------------- /package/src/components/AddressBook/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AddressBook"; 2 | -------------------------------------------------------------------------------- /package/src/components/AddressForm/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AddressForm"; 2 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BadgeOverlay"; 2 | -------------------------------------------------------------------------------- /package/src/components/CartSummary/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartSummary"; 2 | -------------------------------------------------------------------------------- /package/src/components/CatalogGrid/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CatalogGrid"; 2 | -------------------------------------------------------------------------------- /package/src/components/ErrorsBlock/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ErrorsBlock"; 2 | -------------------------------------------------------------------------------- /package/src/components/InPageMenu/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InPageMenu"; 2 | -------------------------------------------------------------------------------- /package/src/components/InlineAlert/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InlineAlert"; 2 | -------------------------------------------------------------------------------- /package/src/components/MultiSelect/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MultiSelect"; 2 | -------------------------------------------------------------------------------- /package/src/components/ProfileImage/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ProfileImage"; 2 | -------------------------------------------------------------------------------- /package/src/components/RegionInput/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RegionInput"; 2 | -------------------------------------------------------------------------------- /package/src/components/StockWarning/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StockWarning"; 2 | -------------------------------------------------------------------------------- /package/src/components/StripeForm/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StripeForm"; 2 | -------------------------------------------------------------------------------- /package/src/components/ViewerInfo/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ViewerInfo"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Account.md: -------------------------------------------------------------------------------- 1 | These are components related to your account profile. 2 | -------------------------------------------------------------------------------- /package/src/components/AddressCapture/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AddressCapture"; 2 | -------------------------------------------------------------------------------- /package/src/components/AddressChoice/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AddressChoice"; 2 | -------------------------------------------------------------------------------- /package/src/components/AddressReview/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AddressReview"; 2 | -------------------------------------------------------------------------------- /package/src/components/CartItemDetail/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartItemDetail"; 2 | -------------------------------------------------------------------------------- /package/src/components/CheckoutAction/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutAction"; 2 | -------------------------------------------------------------------------------- /package/src/components/CheckoutTopHat/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutTopHat"; 2 | -------------------------------------------------------------------------------- /package/src/components/InPageMenuItem/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InPageMenuItem"; 2 | -------------------------------------------------------------------------------- /package/src/components/QuantityInput/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./QuantityInput"; 2 | -------------------------------------------------------------------------------- /package/src/components/SelectableItem/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SelectableItem"; 2 | -------------------------------------------------------------------------------- /package/src/components/SelectableList/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SelectableList"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Actions.md: -------------------------------------------------------------------------------- 1 | These are components that allow you to perform actions. 2 | -------------------------------------------------------------------------------- /package/src/components/AccordionFormList/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AccordionFormList"; 2 | -------------------------------------------------------------------------------- /package/src/components/CartEmptyMessage/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartEmptyMessage"; 2 | -------------------------------------------------------------------------------- /package/src/components/CatalogGridItem/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CatalogGridItem"; 2 | -------------------------------------------------------------------------------- /package/src/components/CheckoutActions/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutActions"; 2 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InventoryStatus"; 2 | -------------------------------------------------------------------------------- /package/src/components/MiniCartSummary/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MiniCartSummary"; 2 | -------------------------------------------------------------------------------- /package/src/components/PhoneNumberInput/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PhoneNumberInput"; 2 | -------------------------------------------------------------------------------- /package/src/components/ProgressiveImage/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ProgressiveImage"; 2 | -------------------------------------------------------------------------------- /package/src/components/AccountProfileInfo/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AccountProfileInfo"; 2 | -------------------------------------------------------------------------------- /package/src/components/StripePaymentInput/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StripePaymentInput"; 2 | -------------------------------------------------------------------------------- /package/src/index.js: -------------------------------------------------------------------------------- 1 | export { default as defaultComponentTheme } from "./theme/defaultComponentTheme"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Forms.md: -------------------------------------------------------------------------------- 1 | These are components that can be used to build forms for entering data. 2 | -------------------------------------------------------------------------------- /docs/architecture/README.md: -------------------------------------------------------------------------------- 1 | # Architecture Documentation 2 | 3 | See [decision records](decisions/README.md) 4 | -------------------------------------------------------------------------------- /package/src/components/CheckoutActionComplete/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutActionComplete"; 2 | -------------------------------------------------------------------------------- /package/src/components/CheckoutEmailAddress/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutEmailAddress"; 2 | -------------------------------------------------------------------------------- /package/src/components/ExampleIOUPaymentForm/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ExampleIOUPaymentForm"; 2 | -------------------------------------------------------------------------------- /package/src/components/PaymentsCheckoutAction/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PaymentsCheckoutAction"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/Menus.md: -------------------------------------------------------------------------------- 1 | These are components that can be used to build in page menus for navigation. 2 | -------------------------------------------------------------------------------- /package/src/components/CheckoutActionIncomplete/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutActionIncomplete"; 2 | -------------------------------------------------------------------------------- /package/src/components/FinalReviewCheckoutAction/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FinalReviewCheckoutAction"; 2 | -------------------------------------------------------------------------------- /package/src/components/StripePaymentCheckoutAction/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StripePaymentCheckoutAction"; 2 | -------------------------------------------------------------------------------- /package/src/components/ShippingAddressCheckoutAction/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ShippingAddressCheckoutAction"; 2 | -------------------------------------------------------------------------------- /.adr.json: -------------------------------------------------------------------------------- 1 | { 2 | "digits": 4, 3 | "language": "en", 4 | "path": "docs/architecture/decisions/", 5 | "prefix": "" 6 | } 7 | -------------------------------------------------------------------------------- /package/src/components/Button/v1/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as applyThemeWithActionType } from "./applyThemeWithActionType"; 2 | -------------------------------------------------------------------------------- /package/src/components/CatalogGridItem/v1/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as priceByCurrencyCode } from "./priceByCurrencyCode"; 2 | -------------------------------------------------------------------------------- /package/src/components/FulfillmentOptionsCheckoutAction/v1/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FulfillmentOptionsCheckoutAction"; 2 | -------------------------------------------------------------------------------- /styleguide/src/sections/StorefrontForms.md: -------------------------------------------------------------------------------- 1 | These are prebuilt forms that can be dropped in wherever this information needs to be collected. 2 | -------------------------------------------------------------------------------- /styleguide/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/favicon.ico -------------------------------------------------------------------------------- /styleguide/src/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/favicon.png -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | NODE_PATH=src 3 | PORT=4040 4 | REACTION_APP_NAME=styleguide.web 5 | REACTION_LOG_LEVEL=INFO 6 | REACTION_LOG_FORMAT=json 7 | -------------------------------------------------------------------------------- /babel.jest.js: -------------------------------------------------------------------------------- 1 | // https://babeljs.io/docs/en/config-files#jest 2 | module.exports = require("babel-jest").createTransformer({ 3 | rootMode: "upward" 4 | }); 5 | -------------------------------------------------------------------------------- /styleguide/src/assets/images/mug/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mug/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/mug/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mug/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/colors/dark-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/colors/dark-colors.png -------------------------------------------------------------------------------- /styleguide/src/assets/colors/grey-scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/colors/grey-scale.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/beans/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/beans/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/beans/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/beans/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/mug/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mug/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/placeholder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/placeholder.gif -------------------------------------------------------------------------------- /styleguide/src/assets/images/pouch/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/pouch/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/pouch/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/pouch/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/colors/medium-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/colors/medium-colors.png -------------------------------------------------------------------------------- /styleguide/src/assets/colors/support-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/colors/support-colors.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/beans/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/beans/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/mug/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mug/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/portrait/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/portrait/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/portrait/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/portrait/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/pouch/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/pouch/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/sticker/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/sticker/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/sticker/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/sticker/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/sticker/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/sticker/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/beans/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/beans/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/landscape/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/landscape/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/landscape/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/landscape/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/landscape/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/landscape/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/mens-shirt/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mens-shirt/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/mens-shirt/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mens-shirt/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/portrait/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/portrait/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/pouch/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/pouch/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/landscape/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/landscape/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/mens-shirt/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mens-shirt/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/portrait/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/portrait/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/sticker/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/sticker/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/womens-shirt/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/womens-shirt/large.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/womens-shirt/medium.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/womens-shirt/medium.jpg -------------------------------------------------------------------------------- /styleguide/src/assets/images/womens-shirt/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/womens-shirt/small.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/mens-shirt/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/mens-shirt/thumbnail.png -------------------------------------------------------------------------------- /styleguide/src/assets/images/womens-shirt/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/images/womens-shirt/thumbnail.png -------------------------------------------------------------------------------- /package/src/setupTests.js: -------------------------------------------------------------------------------- 1 | import { configure } from "enzyme"; 2 | import Adapter from "enzyme-adapter-react-16"; 3 | import "jest-styled-components"; 4 | 5 | configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Bold.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Bold.woff -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Book.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Book.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Book.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Book.woff -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Light.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Light.woff -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Medium.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-Medium.woff -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | environment = { YARN_VERSION = "1.16.0" } 3 | command = "./bin/build.sh" 4 | 5 | [dev] 6 | command = "yarn styleguide:build" # Command to start your dev server 7 | port = 6060 8 | -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BoldItalic.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BookItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BookItalic.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BoldItalic.woff -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BookItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-BookItalic.woff -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-LightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-LightItalic.eot -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-LightItalic.woff -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-MediumItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-MediumItalic.eot -------------------------------------------------------------------------------- /styleguide/src/components/README.md: -------------------------------------------------------------------------------- 1 | Put components in this folder if they're used only by the style guide itself or required by code examples in Component.md files, but not part of the published component library. 2 | -------------------------------------------------------------------------------- /styleguide/src/assets/fonts/post-grotesk/PostGrotesk-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactioncommerce/reaction-component-library/HEAD/styleguide/src/assets/fonts/post-grotesk/PostGrotesk-MediumItalic.woff -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/statusTypes.js: -------------------------------------------------------------------------------- 1 | const STATUS_TYPES = { 2 | BACKORDER: "BACKORDER", 3 | LOW_QUANTITY: "LOW_QUANTITY", 4 | SOLD_OUT: "SOLD_OUT" 5 | }; 6 | 7 | export default STATUS_TYPES; 8 | -------------------------------------------------------------------------------- /package/src/components/AddressCapture/v1/__snapshots__/AddressCapture.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot 1`] = ` 4 |
5 | AddressForm({"value":null,"isSaving":false}) 6 |
7 | `; 8 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/statusLabels.js: -------------------------------------------------------------------------------- 1 | const STATUS_LABELS = { 2 | BACKORDER: "Backordered - ships when available", 3 | LOW_QUANTITY: "Low Inventory", 4 | SOLD_OUT: "Out of stock" 5 | }; 6 | export default STATUS_LABELS; 7 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/badgeLabels.js: -------------------------------------------------------------------------------- 1 | const BADGE_LABELS = { 2 | BACKORDER: "Backorder", 3 | BESTSELLER: "Best Seller", 4 | LOW_QUANTITY: "Low Inventory", 5 | SOLD_OUT: "Sold Out", 6 | SALE: "Sale" 7 | }; 8 | export default BADGE_LABELS; 9 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/badgeTypes.js: -------------------------------------------------------------------------------- 1 | const BADGE_TYPES = { 2 | BACKORDER: "BACKORDER", 3 | BESTSELLER: "BESTSELLER", 4 | LOW_QUANTITY: "LOW_QUANTITY", 5 | SOLD_OUT: "SOLD_OUT", 6 | SALE: "SALE" 7 | }; 8 | 9 | export default BADGE_TYPES; 10 | -------------------------------------------------------------------------------- /docs/browser-compatibility.md: -------------------------------------------------------------------------------- 1 | # Browser compatibility 2 | 3 | The Reaction Commerce component library aims to support all modern browsers and Internet Explorer 11. 4 | 5 | Our browser compatibility list: 6 | - Google Chrome 7 | - Internet Explorer 11 8 | - Microsoft Edge 9 | - Mozilla Firefox 10 | - Safari 11 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as STATUS_TYPES } from "./statusTypes"; 2 | export { default as STATUS_LABELS } from "./statusLabels"; 3 | export { default as inventoryStatus } from "./inventoryStatus"; 4 | 5 | export { default as isProductLowQuantity } from "./isProductLowQuantity"; 6 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as BADGE_TYPES } from "./badgeTypes"; 2 | export { default as BADGE_LABELS } from "./badgeLabels"; 3 | export { default as badgeStatus } from "./badgeStatus"; 4 | export { default as isProductBestseller } from "./isProductBestseller"; 5 | export { default as isProductLowQuantity } from "./isProductLowQuantity"; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.js] 15 | max_line_length = 120 16 | indent_brace_style = 1TBS 17 | spaces_around_operators = true 18 | quote_type = double -------------------------------------------------------------------------------- /package/src/components/QuantityInput/v1/QuantityInput.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import QuantityInput from "./QuantityInput"; 4 | 5 | test("basic snapshot", () => { 6 | const component = renderer.create(); 7 | 8 | const tree = component.toJSON(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/isProductLowQuantity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if a product has low inventory. 3 | * 4 | * @param {Object} product - The product 5 | * @returns {Boolean} - Indicates whether the product has low inventory 6 | */ 7 | export default function isProductLowQuantity(product) { 8 | return product.isLowQuantity && !product.isSoldOut; 9 | } 10 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/isProductLowQuantity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if a product has low inventory. 3 | * 4 | * @param {Object} product - The product 5 | * @returns {Boolean} - Indicates whether the product has low inventory 6 | */ 7 | export default function isProductLowQuantity(product) { 8 | return product.isLowQuantity && !product.isSoldOut; 9 | } 10 | -------------------------------------------------------------------------------- /.reaction/scripts/templates/Component.test.js.template: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import { shallow } from "enzyme"; 4 | import COMPONENT from "./COMPONENT"; 5 | 6 | test("basic snapshot", () => { 7 | const component = renderer.create(); 8 | 9 | const tree = component.toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /package/.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | # ignores vulnerabilities until expiry date; change duration by modifying expiry date 4 | ignore: 5 | 'npm:mem:20180117': 6 | - libnpx > yargs > os-locale > mem: 7 | reason: package cannot be upgraded 8 | expires: '2019-10-24T18:15:12.496Z' 9 | patch: {} 10 | -------------------------------------------------------------------------------- /package/src/components/AddressBook/v1/__snapshots__/AddressBook.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot 1`] = ` 4 |
5 | AccordionFormList({"addNewItemButtonText":"Add a new address","components":"[Object]","deleteItemButtonText":"Delete address","entryFormSubmitButtonText":"Save Changes","itemAddFormProps":"[Object]","items":"[Object]"}) 6 |
7 | `; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.env.example 2 | .env 3 | yarn-error.log 4 | coverage 5 | node_modules 6 | !reports/README.md 7 | reports/* 8 | **/package-lock.json 9 | dist 10 | dist-modules-temp 11 | 12 | styleguide/build/ 13 | styleguide/favicon.ico 14 | styleguide/favicon.png 15 | styleguide/fonts/ 16 | styleguide/index.html 17 | styleguide/manifest.json 18 | styleguide/storefront-component-library-logo.svg 19 | 20 | .DS_Store -------------------------------------------------------------------------------- /.reaction/scripts/templates/Component.md.template: -------------------------------------------------------------------------------- 1 | ### Overview 2 | 3 | ### Usage 4 | 5 | Document various live component examples here. See https://react-styleguidist.js.org/docs/documenting.html 6 | 7 | ### Theme 8 | 9 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components). 10 | 11 | | Theme Prop | Default | Description | 12 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/statusTypes.test.js: -------------------------------------------------------------------------------- 1 | import STATUS_TYPES from "./statusTypes"; 2 | 3 | const STATUS_TYPES_VALUES = { 4 | BACKORDER: "BACKORDER", 5 | LOW_QUANTITY: "LOW_QUANTITY", 6 | SOLD_OUT: "SOLD_OUT" 7 | }; 8 | 9 | test("badge types values", () => { 10 | expect(typeof STATUS_TYPES).toBe("object"); 11 | expect(STATUS_TYPES).toEqual(STATUS_TYPES_VALUES); 12 | }); 13 | -------------------------------------------------------------------------------- /package/src/components/ShopLogo/v1/ShopLogo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import ShopLogo from "./ShopLogo"; 4 | 5 | test("basic snapshot", () => { 6 | const component = renderer.create(( 7 | 8 | )); 9 | 10 | const tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /package/src/components/SelectableItem/v1/SelectableItem.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import SelectableItem from "./SelectableItem"; 4 | 5 | test("basic snapshot with empty props", () => { 6 | const component = renderer.create(); 7 | 8 | const tree = component.toJSON(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /package/src/components/GuestForm/v1/GuestForm.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import GuestForm from "./GuestForm"; 5 | 6 | test("basic snapshot", () => { 7 | const component = renderer.create(); 8 | 9 | const tree = component.toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/badgeTypes.test.js: -------------------------------------------------------------------------------- 1 | import BADGE_TYPES from "./badgeTypes"; 2 | 3 | const BADGE_TYPES_VALUES = { 4 | BACKORDER: "BACKORDER", 5 | BESTSELLER: "BESTSELLER", 6 | LOW_QUANTITY: "LOW_QUANTITY", 7 | SOLD_OUT: "SOLD_OUT", 8 | SALE: "SALE" 9 | }; 10 | 11 | test("badge types values", () => { 12 | expect(typeof BADGE_TYPES).toBe("object"); 13 | expect(BADGE_TYPES).toEqual(BADGE_TYPES_VALUES); 14 | }); 15 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/statusLabels.test.js: -------------------------------------------------------------------------------- 1 | import STATUS_LABELS from "./statusLabels"; 2 | 3 | const STATUS_LABELS_VALUES = { 4 | BACKORDER: "Backordered - ships when available", 5 | LOW_QUANTITY: "Low Inventory", 6 | SOLD_OUT: "Out of stock" 7 | }; 8 | 9 | 10 | test("badge label values", () => { 11 | expect(typeof STATUS_LABELS).toBe("object"); 12 | expect(STATUS_LABELS).toEqual(STATUS_LABELS_VALUES); 13 | }); 14 | -------------------------------------------------------------------------------- /package/src/svg/iconExpand.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const IconExpandSVG = styled.svg` 5 | path { 6 | fill: inherit; 7 | } 8 | `; 9 | 10 | const IconExpand = ( 11 | 12 | 13 | 14 | ); 15 | 16 | export default IconExpand; 17 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/badgeLabels.test.js: -------------------------------------------------------------------------------- 1 | import BADGE_LABELS from "./badgeLabels"; 2 | 3 | const BADGE_LABELS_VALUES = { 4 | BACKORDER: "Backorder", 5 | BESTSELLER: "Best Seller", 6 | LOW_QUANTITY: "Low Inventory", 7 | SOLD_OUT: "Sold Out", 8 | SALE: "Sale" 9 | }; 10 | 11 | test("badge label values", () => { 12 | expect(typeof BADGE_LABELS).toBe("object"); 13 | expect(BADGE_LABELS).toEqual(BADGE_LABELS_VALUES); 14 | }); 15 | -------------------------------------------------------------------------------- /package/src/components/CatalogGridItem/v1/__snapshots__/CatalogGridItem.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CatalogGridItem basic snapshot 1`] = ` 4 |
5 | Link({"href":"/product/basic-reaction-product","children":"[Object]"}) 6 |
7 | `; 8 | 9 | exports[`CatalogGridItem with placeholder image 1`] = ` 10 |
11 | Link({"href":"/product/basic-reaction-product","children":"[Object]"}) 12 |
13 | `; 14 | -------------------------------------------------------------------------------- /styleguide/src/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Reaction Storefront Components", 3 | "name": "Reaction Commerce's Example Storefront Component Library", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /package/src/components/AddressCapture/v1/AddressCapture.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import AddressCapture from "./AddressCapture"; 5 | 6 | test("basic snapshot", () => { 7 | const component = renderer.create(); 8 | 9 | const tree = component.toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | -------------------------------------------------------------------------------- /package/src/components/InPageMenuItem/v1/__snapshots__/InPageMenuItem.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`InPageMenuItem basic component 1`] = `"Link({\\"href\\":\\"/test/url/\\",\\"children\\":\\"[Object]\\"})"`; 4 | 5 | exports[`InPageMenuItem selected 1`] = `"Link({\\"href\\":\\"/test/url/\\",\\"children\\":\\"[Object]\\"})"`; 6 | 7 | exports[`InPageMenuItem with onClick instead of href 1`] = `"Link({\\"children\\":\\"[Object]\\"})"`; 8 | -------------------------------------------------------------------------------- /package/src/components/ExampleIOUPaymentForm/v1/__snapshots__/ExampleIOUPaymentForm.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot 1`] = ` 4 |
5 | Field({"name":"fullName","errors":"[Object]","label":"Full name","labelFor":"fullName_ExampleIOUPaymentForm1","children":"[Object]"}) 6 | Field({"name":"amount","errors":"[Object]","label":"Amount (optional)","labelFor":"amount_ExampleIOUPaymentForm1","children":"[Object]"}) 7 |
8 | `; 9 | -------------------------------------------------------------------------------- /package/src/components/PhoneNumberInput/v1/PhoneNumberInput.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import PhoneNumberInput from "./PhoneNumberInput"; 5 | 6 | test("basic snapshot", () => { 7 | const component = renderer.create(); 8 | 9 | const tree = component.toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | -------------------------------------------------------------------------------- /package/src/components/AccordionFormList/v1/AccordionFormList.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import AccordionFormList from "./AccordionFormList"; 5 | 6 | test("basic snapshot", () => { 7 | const component = renderer.create(); 8 | 9 | const tree = component.toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | -------------------------------------------------------------------------------- /package/src/components/CheckoutActionIncomplete/v1/CheckoutActionIncomplete.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import CheckoutActionIncomplete from "./CheckoutActionIncomplete"; 4 | 5 | test("basic snapshot", () => { 6 | const component = renderer.create(( 7 | 8 | )); 9 | 10 | const tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /package/src/components/AddressBook/v1/AddressBook.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | // import { shallow } from "enzyme"; 4 | import mockComponents from "../../../tests/mockComponents"; 5 | import AddressBook from "./AddressBook"; 6 | 7 | test("basic snapshot", () => { 8 | const component = renderer.create(); 9 | 10 | const tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /package/src/components/Accordion/v1/Accordion.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | // import { shallow } from "enzyme"; 4 | import mockComponents from "../../../tests/mockComponents"; 5 | import Accordion from "./Accordion"; 6 | 7 | test("basic snapshot", () => { 8 | const component = renderer.create(); 9 | 10 | const tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/isProductBestseller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if a product is a best seller. 3 | * TODO: this is a placeholder, as we don't have "Best Seller" at this moment 4 | * https://github.com/reactioncommerce/reaction-next-starterkit/issues/130 5 | * 6 | * @param {Object} product - The product 7 | * @returns {Boolean} - Indicates whether the product is a best seller 8 | */ 9 | export default function isProductBestseller(product) { 10 | return product.isBestseller || false; 11 | } 12 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/isProductBestseller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if a product is a best seller. 3 | * TODO: this is a placeholder, as we don't have "Best Seller" at this moment 4 | * https://github.com/reactioncommerce/reaction-next-starterkit/issues/130 5 | * 6 | * @param {Object} product - The product 7 | * @returns {Boolean} - Indicates whether the product is a best seller 8 | */ 9 | export default function isProductBestseller(product) { 10 | return product.isBestseller || false; 11 | } 12 | -------------------------------------------------------------------------------- /package/src/components/AddressChoice/v1/__snapshots__/AddressChoice.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`simple snapshot 1`] = ` 4 |
5 | AddressForm({"isReadOnly":false}) 6 |
7 | `; 8 | 9 | exports[`simple snapshot read only 1`] = ` 10 |
11 | AddressForm({"isReadOnly":true}) 12 |
13 | `; 14 | 15 | exports[`snapshot with addresses 1`] = ` 16 |
17 | SelectableList({"name":"addressList","isReadOnly":false,"options":"[Object]","value":"0"}) 18 |
19 | `; 20 | -------------------------------------------------------------------------------- /docs/architecture/decisions/README.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision Records 2 | 3 | * [1. use-adrs](0001-use-adrs.md) 4 | * [2. 12factor-config-from-env](0002-12factor-config-from-env.md) 5 | * [3. choose-a-style-guide-generator-framework](0003-choose-a-style-guide-generator-framework.md) 6 | * [4. define-project-structure](0004-define-project-structure.md) 7 | * [5. test-components](0005-test-components.md) 8 | * [6. style-components](0006-style-components.md) 9 | * [7. publish-components](0007-publish-components.md) 10 | * [8. keep-styles-with-components](0008-keep-styles-with-components.md) -------------------------------------------------------------------------------- /package/src/components/CatalogGridItem/v1/utils/priceByCurrencyCode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines the pricing a product given a currency code. 3 | * 4 | * @param {String} currencyCode - the shop's set currency code, i.e. "USD" 5 | * @param {Array} pricing - An array of pricing objects with different currencies. 6 | * @returns {Object} - pricing object, or null if none found. 7 | */ 8 | export default function priceByCurrencyCode(currencyCode, pricing) { 9 | const _pricing = pricing.find((price) => price.currency.code === currencyCode); 10 | 11 | return _pricing || null; 12 | } 13 | -------------------------------------------------------------------------------- /package/src/components/Checkbox/v1/Checkbox.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import Checkbox from "./Checkbox"; 4 | 5 | test("renders with props", () => { 6 | const component = renderer.create(); 7 | 8 | const tree = component.toJSON(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | 12 | test("renders disabled", () => { 13 | const component = renderer.create(); 14 | 15 | const tree = component.toJSON(); 16 | expect(tree).toMatchSnapshot(); 17 | }); 18 | -------------------------------------------------------------------------------- /package/src/components/QuantityInput/v1/QuantityInput.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | 3 | The `QuantityInput` component currently is built with [material-ui](https://material-ui.com/) components. 4 | * [TextField](https://material-ui.com/demos/text-fields/) 5 | * [InputAdornment](https://material-ui.com/api/input-adornment/) 6 | * [ButtonBase](https://material-ui.com/api/button-base/) 7 | * [withStyles](https://material-ui.com/customization/overrides/) 8 | 9 | #### Usage 10 | 11 | ```jsx 12 | console.log("QuantityInput changed", value))} /> 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /.reaction/yarnrc-docker.template: -------------------------------------------------------------------------------- 1 | # Yarn Config - configured for running inside Docker Compose 2 | 3 | # Configure Yarn offline mirror for improved performance. 4 | # https://yarnpkg.com/blog/2016/11/24/offline-mirror/ 5 | # 6 | # If you need to ensure clean cached modules follow the guide: 7 | # https://yarnpkg.com/blog/2016/11/24/offline-mirror/#updating-your-package 8 | # 9 | yarn-offline-mirror "/home/node/.cache/yarn-offline-mirror" 10 | yarn-offline-mirror-pruning true 11 | 12 | --install.cache-folder /home/node/.cache/yarn 13 | --install.ignore-scripts true 14 | --install.prefer-offline true 15 | -------------------------------------------------------------------------------- /package/src/utils/preventAccidentalDoubleClick.js: -------------------------------------------------------------------------------- 1 | import debounce from "lodash.debounce"; 2 | 3 | // For most OS, it seems 600ms is the slowest you can set to be a "double click" 4 | const DEBOUNCE_MS = 600; 5 | 6 | /** 7 | * @summary Wraps a function to prevent accidental double-clicks from executing it too often. 8 | * @param {Function} func - The onClick function to return, debounced 9 | * @returns {undefined} 10 | */ 11 | export default function preventAccidentalDoubleClick(func) { 12 | return debounce(func, DEBOUNCE_MS, { 13 | leading: true, 14 | trailing: false 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /package/src/theme/padding.js: -------------------------------------------------------------------------------- 1 | const paddingBasePixels = 10; 2 | 3 | export default { 4 | two: `${paddingBasePixels * 0.2}px`, 5 | four: `${paddingBasePixels * 0.4}px`, 6 | five: `${paddingBasePixels * 0.5}px`, 7 | six: `${paddingBasePixels * 0.6}px`, 8 | eight: `${paddingBasePixels * 0.8}px`, 9 | ten: `${paddingBasePixels * 1}px`, 10 | twelve: `${paddingBasePixels * 1.2}px`, 11 | fourteen: `${paddingBasePixels * 1.4}px`, 12 | fifteen: `${paddingBasePixels * 1.5}px`, 13 | sixteen: `${paddingBasePixels * 1.6}px`, 14 | eighteen: `${paddingBasePixels * 1.8}px`, 15 | twenty: `${paddingBasePixels * 2}px` 16 | }; 17 | -------------------------------------------------------------------------------- /package/src/svg/iconLock.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const IconLockSvg = styled.svg` 5 | vertical-align: baseline; 6 | `; 7 | 8 | const IconLock = ( 9 | 10 | 15 | 16 | ); 17 | 18 | export default IconLock; 19 | -------------------------------------------------------------------------------- /package/src/components/CheckoutTopHat/v1/CheckoutTopHat.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import CheckoutTopHat from "./CheckoutTopHat"; 4 | 5 | test("render Top Hat with message", () => { 6 | const component = renderer.create(); 7 | 8 | const tree = component.toJSON(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | 12 | test("render nothing when no message is present", () => { 13 | const component = renderer.create(); 14 | 15 | const tree = component.toJSON(); 16 | expect(tree).toMatchSnapshot(); 17 | }); 18 | -------------------------------------------------------------------------------- /package/src/svg/iconDismiss.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const IconDismissSvg = styled.svg` 5 | height: 100%; 6 | max-height: 100%; 7 | vertical-align: middle; 8 | `; 9 | 10 | const IconDismiss = ( 11 | // credit: https://material.io/tools/icons/?icon=clear&style=baseline 12 | 16 | 17 | 18 | 19 | ); 20 | 21 | export default IconDismiss; 22 | -------------------------------------------------------------------------------- /package/src/components/CheckoutActionIncomplete/v1/__snapshots__/CheckoutActionIncomplete.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 |
22 | 2. Shipping information 23 |
24 | `; 25 | -------------------------------------------------------------------------------- /package/src/svg/iconPlus.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | import React from "react"; 3 | import styled from "styled-components"; 4 | 5 | const IconPlusSVG = styled.svg` 6 | path { 7 | fill: inherit; 8 | } 9 | `; 10 | 11 | const IconPlus = ( 12 | 13 | plus 14 | 15 | 16 | ); 17 | 18 | export default IconPlus; 19 | -------------------------------------------------------------------------------- /package/src/components/CheckoutActionIncomplete/v1/CheckoutActionIncomplete.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | 3 | The `CheckoutActionIncomplete` component will be used to display an incomplete step in the checkout process. 4 | 5 | ### Usage 6 | 7 | #### Default 8 | ```jsx 9 | 10 | ``` 11 | 12 | #### Without a step number 13 | ```jsx 14 | 15 | ``` 16 | 17 | ### Theme 18 | 19 | See [Theming Components](./#!/Theming%20Components). 20 | 21 | #### Typography 22 | 23 | - The initials text shown when there is no image uses `captionText` style with `rui_components.CheckoutActionIncomplete` override 24 | -------------------------------------------------------------------------------- /package/src/components/MiniCartSummary/v1/MiniCartSummary.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import MiniCartSummary from "./MiniCartSummary"; 4 | 5 | test("Renders only subtotal", () => { 6 | const component = renderer.create(( 7 | 8 | )); 9 | 10 | const tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | 14 | test("Renders only subtotal + computed taxes", () => { 15 | const component = renderer.create(( 16 | 17 | )); 18 | 19 | const tree = component.toJSON(); 20 | expect(tree).toMatchSnapshot(); 21 | }); 22 | -------------------------------------------------------------------------------- /package/src/components/StripePaymentCheckoutAction/v1/StripePaymentCheckoutAction.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import StripePaymentCheckoutAction from "./StripePaymentCheckoutAction"; 5 | 6 | test("basic snapshot", () => { 7 | const component = renderer.create(( 8 | true} 12 | onSubmit={() => true} 13 | components={mockComponents} 14 | /> 15 | )); 16 | 17 | const tree = component.toJSON(); 18 | expect(tree).toMatchSnapshot(); 19 | }); 20 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/isProductBestseller.test.js: -------------------------------------------------------------------------------- 1 | import isProductBestseller from "./isProductBestseller"; 2 | 3 | const isBestseller = { isBestseller: true }; 4 | const isNotBestseller = { isBestseller: false }; 5 | 6 | test("isProductBestseller should return false", () => { 7 | const callFunction = isProductBestseller(isNotBestseller); 8 | 9 | expect(typeof isProductBestseller).toBe("function"); 10 | expect(callFunction).toEqual(false); 11 | }); 12 | 13 | test("isProductBestseller should return true", () => { 14 | const callFunction = isProductBestseller(isBestseller); 15 | 16 | expect(typeof isProductBestseller).toBe("function"); 17 | expect(callFunction).toEqual(true); 18 | }); 19 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/isProductBestseller.test.js: -------------------------------------------------------------------------------- 1 | import isProductBestseller from "./isProductBestseller"; 2 | 3 | const isBestseller = { isBestseller: true }; 4 | const isNotBestseller = { isBestseller: false }; 5 | 6 | test("isProductBestseller should return false", () => { 7 | const callFunction = isProductBestseller(isNotBestseller); 8 | 9 | expect(typeof isProductBestseller).toBe("function"); 10 | expect(callFunction).toEqual(false); 11 | }); 12 | 13 | test("isProductBestseller should return true", () => { 14 | const callFunction = isProductBestseller(isBestseller); 15 | 16 | expect(typeof isProductBestseller).toBe("function"); 17 | expect(callFunction).toEqual(true); 18 | }); 19 | -------------------------------------------------------------------------------- /package/src/components/Link/v1/Link.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | Renders a link with an onClick handler and text, icons, or any combination of React and HTML components 3 | 4 | #### Usage 5 | 6 | Simple text link without onClick handler: 7 | 8 | ```jsx 9 | Click here 10 | ``` 11 | 12 | Link with image and custom onClick handler: 13 | 14 | ```jsx 15 | alert("clicked")}> 16 | Reaction Storefront Component Library 17 | 18 | ``` 19 | 20 | Link with onClick handler but no href: 21 | 22 | ```jsx 23 | alert("clicked")}>Click here 24 | ``` 25 | -------------------------------------------------------------------------------- /styleguide/src/components/Wrapper.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import DefaultWrapper from "react-styleguidist/lib/client/rsg-components/Wrapper/Wrapper"; 3 | import { ComponentsProvider } from "@reactioncommerce/components-context"; 4 | import { StripeProvider } from "react-stripe-elements"; 5 | import appComponents from "../appComponents"; 6 | 7 | class Wrapper extends Component { 8 | render() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | } 18 | 19 | export default Wrapper; 20 | -------------------------------------------------------------------------------- /package/src/utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as addTypographyStyles } from "./addTypographyStyles"; 2 | export { default as applyTheme } from "./applyTheme"; 3 | export { default as CustomPropTypes } from "./CustomPropTypes"; 4 | export { default as getFromTheme } from "./getFromTheme"; 5 | export { default as preventAccidentalDoubleClick } from "./preventAccidentalDoubleClick"; 6 | export { default as getRequiredValidator } from "./getRequiredValidator"; 7 | export { default as getPhoneNumberValidator } from "./getPhoneNumberValidator"; 8 | export { default as withStripeElements } from "./withStripeElements"; 9 | export { default as addressToString } from "./addressToString"; 10 | export { default as formatMoney } from "./formatMoney"; 11 | -------------------------------------------------------------------------------- /package/src/utils/withStripeElements.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Elements } from "react-stripe-elements"; 3 | 4 | /** 5 | * @summary A HOC creator that wraps the component with `Elements` from react-stripe-elements 6 | * @param {React.Component|Function} WrappedComponent The component class or function to wrap 7 | * @returns {React.Component} Higher order component 8 | */ 9 | export default function withStripeElements(WrappedComponent) { 10 | return class extends Component { 11 | static displayName = "withStripeElements"; 12 | 13 | render() { 14 | return ( 15 | 16 | 17 | 18 | ); 19 | } 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /package/src/components/InPageMenu/v1/InPageMenu.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | The `InPageMenu` component creates a responsive menu that displays `InPageMenuItem`s. 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 | const menuItems = [ 10 | { 11 | href: "/label/a", 12 | label: "Label A" 13 | }, 14 | { 15 | href: "/label/b", 16 | label: "Label B" 17 | }, 18 | { 19 | href: "/label/c", 20 | label: "Label C (Selected / Active)", 21 | isSelected: true 22 | }, 23 | { 24 | href: "/label/d", 25 | label: "Label D" 26 | }, 27 | { 28 | href: "/label/e", 29 | label: "Label E" 30 | } 31 | ]; 32 | 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /package/src/components/BadgeOverlay/v1/utils/isProductLowQuantity.test.js: -------------------------------------------------------------------------------- 1 | import isProductLowQuantity from "./isProductLowQuantity"; 2 | 3 | const isLowQuantity = { isLowQuantity: true, isSoldOut: false }; 4 | const isNotLowQuantity = { isLowQuantity: false, isSoldOut: false }; 5 | 6 | test("isProductLowQuantity should return false", () => { 7 | const callFunction = isProductLowQuantity(isNotLowQuantity); 8 | 9 | expect(typeof isProductLowQuantity).toBe("function"); 10 | expect(callFunction).toEqual(false); 11 | }); 12 | 13 | test("isProductLowQuantity should return true", () => { 14 | const callFunction = isProductLowQuantity(isLowQuantity); 15 | 16 | expect(typeof isProductLowQuantity).toBe("function"); 17 | expect(callFunction).toEqual(true); 18 | }); 19 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/utils/isProductLowQuantity.test.js: -------------------------------------------------------------------------------- 1 | import isProductLowQuantity from "./isProductLowQuantity"; 2 | 3 | const isLowQuantity = { isLowQuantity: true, isSoldOut: false }; 4 | const isNotLowQuantity = { isLowQuantity: false, isSoldOut: false }; 5 | 6 | test("isProductLowQuantity should return false", () => { 7 | const callFunction = isProductLowQuantity(isNotLowQuantity); 8 | 9 | expect(typeof isProductLowQuantity).toBe("function"); 10 | expect(callFunction).toEqual(false); 11 | }); 12 | 13 | test("isProductLowQuantity should return true", () => { 14 | const callFunction = isProductLowQuantity(isLowQuantity); 15 | 16 | expect(typeof isProductLowQuantity).toBe("function"); 17 | expect(callFunction).toEqual(true); 18 | }); 19 | -------------------------------------------------------------------------------- /package/src/components/ShopLogo/v1/__snapshots__/ShopLogo.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: #505558; 7 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif; 8 | font-size: 24px; 9 | font-style: normal; 10 | font-stretch: normal; 11 | font-weight: 700; 12 | -webkit-letter-spacing: .03em; 13 | -moz-letter-spacing: .03em; 14 | -ms-letter-spacing: .03em; 15 | letter-spacing: .03em; 16 | line-height: 1.25; 17 | } 18 | 19 | .c1 { 20 | height: auto; 21 | } 22 | 23 |
26 | Reaction 31 |
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 | Reaction Storefront Component Library Logo 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 |
13 | CartItem({"item":"[Object]","components":"[Object]","isMiniCart":false,"isReadOnly":false}) 14 | CartItem({"item":"[Object]","components":"[Object]","isMiniCart":false,"isReadOnly":false}) 15 |
16 | `; 17 | 18 | exports[`basic snapshot with required props and optional prop 1`] = ` 19 |
22 | CartItem({"item":"[Object]","components":"[Object]","productURLPath":"product/","isMiniCart":false,"isReadOnly":false}) 23 | CartItem({"item":"[Object]","components":"[Object]","productURLPath":"product/","isMiniCart":false,"isReadOnly":false}) 24 |
25 | `; 26 | -------------------------------------------------------------------------------- /package/src/components/InPageMenu/v1/__snapshots__/InPageMenu.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot 1`] = ` 4 | .c0 { 5 | box-sizing: border-box; 6 | display: -webkit-box; 7 | display: -webkit-flex; 8 | display: -ms-flexbox; 9 | display: flex; 10 | -webkit-flex-direction: column; 11 | -ms-flex-direction: column; 12 | flex-direction: column; 13 | -webkit-flex-wrap: wrap; 14 | -ms-flex-wrap: wrap; 15 | flex-wrap: wrap; 16 | width: 100%; 17 | } 18 | 19 |
22 | InPageMenuItem({"href":"/label/a","label":"Label A"}) 23 | InPageMenuItem({"href":"/label/b","label":"Label B"}) 24 | InPageMenuItem({"href":"/label/c","isSelected":true,"label":"Label C (Selected / Active)"}) 25 | InPageMenuItem({"href":"/label/d","label":"Label D"}) 26 | InPageMenuItem({"href":"/label/e","label":"Label E"}) 27 |
28 | `; 29 | -------------------------------------------------------------------------------- /bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml 4 | 5 | # ---- Start unofficial bash strict mode boilerplate 6 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 7 | set -o errexit # always exit on error 8 | set -o errtrace # trap errors in functions as well 9 | set -o pipefail # don't ignore exit codes when piping output 10 | set -o posix # more strict failures in subshells 11 | # set -x # enable debugging 12 | 13 | IFS=$'\n\t' 14 | # ---- End unofficial bash strict mode boilerplate 15 | 16 | cd "$(dirname "${BASH_SOURCE[0]}")/.." 17 | export PATH="${PWD}/node_modules/.bin:${PATH}" 18 | declare -a args=( 19 | --ignore-scripts 20 | --no-lockfile 21 | --no-progress 22 | --non-interactive 23 | --silent 24 | ) 25 | yarn "${args[@]}" 26 | (cd package && yarn "${args[@]}" && yarn build) 27 | yarn styleguide:build 28 | -------------------------------------------------------------------------------- /.reaction/project-hooks/post-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Post Build Hook 3 | # Invoked by the reaction-next project bootstrapping process. 4 | # 5 | # Invoked after Docker build. 6 | # Perform any actions here that are required after docker-compose build but 7 | # before the project is started. 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-build script invoked." 2>&1 23 | -------------------------------------------------------------------------------- /package/src/components/CartEmptyMessage/v1/CartEmptyMessage.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import Button from "../../Button/v1"; 4 | import CartEmptyMessage from "./CartEmptyMessage"; 5 | 6 | test("default cart empty button", () => { 7 | const onClick = () => {}; 8 | const component = renderer.create(); 9 | 10 | const tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | 14 | test("custom cart empty button", () => { 15 | const onClick = () => {}; 16 | const component = renderer.create(( 17 | 23 | )); 24 | 25 | const tree = component.toJSON(); 26 | expect(tree).toMatchSnapshot(); 27 | }); 28 | -------------------------------------------------------------------------------- /docs/reviewing-components.md: -------------------------------------------------------------------------------- 1 | # Reviewing Components 2 | 3 | A Pull Request for a component will be reviewed by three groups: 4 | 1. **Development** - Developer checks for code style, code tests and component functionality 5 | 1. **Design** - Designer checks for design specs of the component itself. 6 | 1. **Documentation** - Documentation checks for how the component and documentation render on the Component Library site. 7 | 8 | Reviewers should be able to view the component live in a Netlify deployed branch. 9 | 10 | After the component is approved, the NPM module will be released with semantic release. 11 | 12 | Once the component is published, developers can use the new component in any project, like the [Reaction Storefront Next.js Starter Kit](https://github.com/reactioncommerce/reaction-next-starterkit). Designers will then review the component again, to check that the component has been implemented to proper design usage guidelines. 13 | 14 | -------------------------------------------------------------------------------- /package/src/components/InPageMenu/v1/InPageMenu.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import InPageMenu from "./InPageMenu"; 5 | 6 | test("basic snapshot", () => { 7 | const menuItems = [ 8 | { 9 | href: "/label/a", 10 | label: "Label A" 11 | }, 12 | { 13 | href: "/label/b", 14 | label: "Label B" 15 | }, 16 | { 17 | href: "/label/c", 18 | label: "Label C (Selected / Active)", 19 | isSelected: true 20 | }, 21 | { 22 | href: "/label/d", 23 | label: "Label D" 24 | }, 25 | { 26 | href: "/label/e", 27 | label: "Label E" 28 | } 29 | ]; 30 | 31 | const component = renderer.create(); 32 | 33 | const tree = component.toJSON(); 34 | expect(tree).toMatchSnapshot(); 35 | }); 36 | -------------------------------------------------------------------------------- /package/src/components/RegionInput/v1/__snapshots__/RegionInput.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot with only the components and required props should render a TextInput 1`] = `"TextInput({\\"name\\":\\"region\\"})"`; 4 | 5 | exports[`basic snapshot with options should render a Select 1`] = `"Select({\\"alphabetize\\":true,\\"isSearchable\\":true,\\"options\\":\\"[Object]\\",\\"name\\":\\"region\\"})"`; 6 | 7 | exports[`basic snapshot with other form props 1`] = `"TextInput({\\"value\\":\\"California\\",\\"name\\":\\"region\\",\\"isReadOnly\\":true})"`; 8 | 9 | exports[`basic snapshot with pre-filled value in Select 1`] = `"Select({\\"alphabetize\\":true,\\"isSearchable\\":true,\\"options\\":\\"[Object]\\",\\"value\\":\\"BB\\",\\"name\\":\\"region\\"})"`; 10 | 11 | exports[`basic snapshot with pre-filled value in TextInput 1`] = `"TextInput({\\"value\\":\\"California\\",\\"name\\":\\"region\\"})"`; 12 | -------------------------------------------------------------------------------- /package/src/svg/iconClear.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const IconClearSvg = styled.svg` 5 | height: 100%; 6 | max-height: 100%; 7 | vertical-align: middle; 8 | `; 9 | 10 | const IconClear = ( 11 | // credit: https://fontawesome.com/icons/times-circle?style=regular 12 | 16 | 20 | 21 | ); 22 | 23 | export default IconClear; 24 | -------------------------------------------------------------------------------- /styleguide/src/components/utils/locales/pottermore.json: -------------------------------------------------------------------------------- 1 | { 2 | "HW": { 3 | "name": "Hogwarts School of Witchcraft and Wizardry", 4 | "states": { 5 | "GD": { 6 | "name": "Gryffindor" 7 | }, 8 | "HP": { 9 | "name": "Hufflepuff" 10 | }, 11 | "RC": { 12 | "name": "Ravenslaw" 13 | }, 14 | "SR": { 15 | "name": "Slytherin" 16 | } 17 | } 18 | }, 19 | "IV": { 20 | "name": "Ilvermorny School of Witchcraft and Wizardry", 21 | "states": { 22 | "HS": { 23 | "name": "Horned Serpent" 24 | }, 25 | "PG": { 26 | "name": "Pukwudgie" 27 | }, 28 | "TB": { 29 | "name": "Thunderbird" 30 | }, 31 | "WS": { 32 | "name": "Wampus" 33 | } 34 | } 35 | }, 36 | "BA": { 37 | "name": "Beauxbatons Academy of Magic " 38 | }, 39 | "DI": { 40 | "name": "Durmstrang Institute" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | 18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 19 | // We don't polyfill it in the browser--this is user's responsibility. 20 | if (process.env.NODE_ENV === 'test') { 21 | require('raf').polyfill(global); 22 | } 23 | -------------------------------------------------------------------------------- /package/src/svg/iconError.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const IconErrorSvg = styled.svg` 5 | path { 6 | fill: #cd3f4c; 7 | } 8 | `; 9 | 10 | const IconError = ( 11 | // credit: https://fontawesome.com/icons/exclamation-triangle?style=solid 12 | 16 | 19 | 20 | ); 21 | 22 | export default IconError; 23 | -------------------------------------------------------------------------------- /package/src/components/AddressReview/v1/__snapshots__/AddressReview.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot with all props 1`] = ` 4 | .c0 { 5 | margin-top: 40px; 6 | } 7 | 8 |
9 |
12 |
16 | SelectableList({"isHorizontal":true,"isStacked":true,"options":"[Object]","name":"AddressReview","value":"entered","isReadOnly":false}) 17 |
18 |
19 |
20 | `; 21 | 22 | exports[`basic snapshot with only required props 1`] = ` 23 | .c0 { 24 | margin-top: 40px; 25 | } 26 | 27 |
28 |
31 |
35 | SelectableList({"isHorizontal":true,"isStacked":true,"options":"[Object]","name":"AddressReview","value":"suggested","isReadOnly":false}) 36 |
37 |
38 |
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 |
32 |
35 | CheckoutAction({"activeLabel":"mock active action one","activeStepElement":"[Object]","completeLabel":"mock complete action one","completeStepElement":"[Object]","incompleteLabel":"mock inactive action one","incompleteStepElement":"[Object]","status":"active","stepNumber":1}) 36 |
37 |
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 |
26 |
29 | SelectableItem({"detail":"$12.00","isChecked":false,"isLeftAligned":false,"isReadOnly":false,"label":"Priority shipping","value":"123"}) 30 |
31 |
34 | SelectableItem({"detail":"$5.00","isChecked":false,"isLeftAligned":false,"isReadOnly":false,"label":"Expedited shipping","value":"333"}) 35 |
36 |
39 | SelectableItem({"detail":"$0.00","isChecked":false,"isLeftAligned":false,"isReadOnly":false,"label":"Free shipping","value":"2455"}) 40 |
41 |
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 |
19 | { this.form = ref; }} 21 | onChange={(...args) => { console.log("onChange", ...args); }} 22 | onReadyForSaveChange={(isReady) => { 23 | console.log("onReadyForSaveChange", isReady); 24 | this.setState({ isReady }); 25 | }} 26 | onSubmit={(doc) => { console.log("onSubmit", doc); }} 27 | /> 28 | 29 |
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 | Reaction Storefront Component Library Logo 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 |
19 | { this.form = ref; }} 21 | onChange={(...args) => { console.log("onChange", ...args); }} 22 | onReadyForSaveChange={(isReady) => { 23 | console.log("onReadyForSaveChange", isReady); 24 | this.setState({ isReady }); 25 | }} 26 | onSubmit={(doc) => { console.log("onSubmit", doc); }} 27 | /> 28 | 29 |
30 | ); 31 | } 32 | } 33 | 34 | 35 | ``` 36 | 37 | ### Theme 38 | 39 | Assume that any theme prop that does not begin with "rui" is within `rui_components`. See [Theming Components](./#!/Theming%20Components). 40 | 41 | #### Typography 42 | 43 | - The "Your Information is private and secure" text uses `captionText` style with `rui_components.StripePaymentInputCaption` override 44 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/InventoryStatus.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | The `InventoryStatus` displays a low inventory warning when the `isLowQuantity` prop is true. 3 | 4 | ### Usage 5 | 6 | An inventory warning will be rendered when the `isLowQuantity` prop is `true`, and does not render when a product has a normal inventory level. 7 | 8 | #### Backorder 9 | ```jsx 10 | const productData = { 11 | isBackorder: true, 12 | isLowQuantity: true, 13 | isSoldOut: true, 14 | inventoryAvailableToSell: 0 15 | }; 16 | 17 | 18 | ``` 19 | 20 | #### Low inventory 21 | ```jsx 22 | const productData = { 23 | isBackorder: false, 24 | isLowQuantity: true, 25 | isSoldOut: false, 26 | inventoryAvailableToSell: 4 27 | }; 28 | 29 | 30 | ``` 31 | 32 | #### Regular inventory 33 | ```jsx 34 | const productData = { 35 | isBackorder: false, 36 | isLowQuantity: false, 37 | isSoldOut: false, 38 | inventoryAvailableToSell: 4 39 | }; 40 | 41 | 42 | ``` 43 | 44 | #### Sold out 45 | ```jsx 46 | const productData = { 47 | isBackorder: false, 48 | isLowQuantity: true, 49 | isSoldOut: true, 50 | inventoryAvailableToSell: 0 51 | }; 52 | 53 | 54 | ``` 55 | 56 | ### Theme 57 | 58 | See [Theming Components](./#!/Theming%20Components). 59 | 60 | #### Typography 61 | 62 | - The text uses `labelText` style with `rui_components.InventoryStatus` override 63 | -------------------------------------------------------------------------------- /package/src/components/FinalReviewCheckoutAction/v1/__snapshots__/FinalReviewCheckoutAction.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot 1`] = ` 4 | Array [ 5 | .c0 { 6 | -webkit-font-smoothing: antialiased; 7 | color: #505558; 8 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif; 9 | font-size: 18px; 10 | font-style: normal; 11 | font-stretch: normal; 12 | font-weight: 700; 13 | -webkit-letter-spacing: .03em; 14 | -moz-letter-spacing: .03em; 15 | -ms-letter-spacing: .03em; 16 | letter-spacing: .03em; 17 | line-height: 1.25; 18 | } 19 | 20 |

23 | 4 24 | . 25 | Payment Information 26 |

, 27 | .c0 { 28 | border-color: #e6e6e6; 29 | border-style: solid; 30 | border-width: 1px; 31 | } 32 | 33 | .c2 { 34 | padding-bottom: 0; 35 | padding-left: 16px; 36 | padding-right: 16px; 37 | padding-top: 0; 38 | } 39 | 40 | .c1 { 41 | border-bottom-color: #e6e6e6; 42 | border-bottom-style: solid; 43 | border-bottom-width: 1px; 44 | padding-bottom: 0; 45 | padding-left: 16px; 46 | padding-right: 16px; 47 | padding-top: 0; 48 | } 49 | 50 |
53 |
56 | CartItems({"isReadOnly":true,"items":"[Object]"}) 57 |
58 |
61 | CartSummary({"isDense":true,"displayShipping":"$5.25","displaySubtotal":"$275.77","displayTax":"$7.62","displayTotal":"$288.64"}) 62 |
63 |
, 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 |
25 | alert("InPageMenuItem 1 onClick() fired")} /> 26 | alert("InPageMenuItem 2 onClick() fired")} /> 27 | alert("InPageMenuItem 3 onClick() fired")} /> 28 | alert("InPageMenuItem 4 onClick() fired")} /> 29 | alert("InPageMenuItem 5 onClick() fired")} /> 30 |
31 | ``` 32 | -------------------------------------------------------------------------------- /package/src/components/ShippingAddressCheckoutAction/v1/__snapshots__/ShippingAddressCheckoutAction.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`basic snapshot with address 1`] = ` 4 | Array [ 5 | .c0 { 6 | -webkit-font-smoothing: antialiased; 7 | color: #505558; 8 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif; 9 | font-size: 18px; 10 | font-style: normal; 11 | font-stretch: normal; 12 | font-weight: 700; 13 | -webkit-letter-spacing: .03em; 14 | -moz-letter-spacing: .03em; 15 | -ms-letter-spacing: .03em; 16 | letter-spacing: .03em; 17 | line-height: 1.25; 18 | } 19 | 20 |

23 | 1 24 | . 25 | Shipping Address 26 |

, 27 | "", 28 | "AddressCapture({\\"addressFormProps\\":\\"[Object]\\",\\"addressReviewProps\\":\\"[Object]\\",\\"isSaving\\":false})", 29 | ] 30 | `; 31 | 32 | exports[`basic snapshot with empty address 1`] = ` 33 | Array [ 34 | .c0 { 35 | -webkit-font-smoothing: antialiased; 36 | color: #505558; 37 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif; 38 | font-size: 18px; 39 | font-style: normal; 40 | font-stretch: normal; 41 | font-weight: 700; 42 | -webkit-letter-spacing: .03em; 43 | -moz-letter-spacing: .03em; 44 | -ms-letter-spacing: .03em; 45 | letter-spacing: .03em; 46 | line-height: 1.25; 47 | } 48 | 49 |

52 | 1 53 | . 54 | Shipping Address 55 |

, 56 | "", 57 | "AddressCapture({\\"addressFormProps\\":\\"[Object]\\",\\"addressReviewProps\\":\\"[Object]\\",\\"isSaving\\":false})", 58 | ] 59 | `; 60 | -------------------------------------------------------------------------------- /package/src/components/ShippingAddressCheckoutAction/v1/ShippingAddressCheckoutAction.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import mockComponents from "../../../tests/mockComponents"; 4 | import ShippingAddressCheckoutAction from "./ShippingAddressCheckoutAction"; 5 | 6 | test("basic snapshot with empty address", () => { 7 | /* eslint-disable */ 8 | const component = renderer.create( 9 | true} 13 | onSubmit={() => true} 14 | components={mockComponents} 15 | fulfillmentGroup={{ data: { shippingAddress: null } }} 16 | /> 17 | ); 18 | /* eslint-enable */ 19 | 20 | const tree = component.toJSON(); 21 | expect(tree).toMatchSnapshot(); 22 | }); 23 | 24 | test("basic snapshot with address", () => { 25 | const address = { 26 | address1: "7742 Hwy 23", 27 | address2: "", 28 | country: "US", 29 | city: "Belle Chasse", 30 | fullName: "Salvos Seafood", 31 | postal: "70037", 32 | region: "LA", 33 | phone: "(504) 393-7303" 34 | }; 35 | /* eslint-disable */ 36 | const component = renderer.create( 37 | true} 41 | onSubmit={() => true} 42 | components={mockComponents} 43 | fulfillmentGroup={{ data: { shippingAddress: address } }} 44 | /> 45 | ); 46 | /* eslint-enable */ 47 | 48 | const tree = component.toJSON(); 49 | expect(tree).toMatchSnapshot(); 50 | }); 51 | -------------------------------------------------------------------------------- /package/src/components/AddressReview/v1/AddressReview.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | // import { shallow } from "enzyme"; 4 | import mockComponents from "../../../tests/mockComponents"; 5 | import AddressReview from "./AddressReview"; 6 | 7 | const mockAddressEntered = { 8 | address1: "7742 Hwy 25", 9 | address2: "", 10 | country: "US", 11 | city: "Belle Chasse", 12 | fullName: "Salvos Seafood", 13 | postal: "70047", 14 | region: "LA", 15 | phone: "(504) 393-7303" 16 | }; 17 | 18 | const mockAddressSuggestion = { 19 | address1: "7742 Hwy 23", 20 | address2: "", 21 | country: "US", 22 | city: "Belle Chasse", 23 | fullName: "Salvos Seafood", 24 | postal: "70037", 25 | region: "LA", 26 | phone: "(504) 393-7303" 27 | }; 28 | 29 | test("basic snapshot with only required props", () => { 30 | const component = renderer.create(( 31 | 36 | )); 37 | 38 | const tree = component.toJSON(); 39 | expect(tree).toMatchSnapshot(); 40 | }); 41 | 42 | test("basic snapshot with all props", () => { 43 | const component = renderer.create(( 44 | 52 | )); 53 | 54 | const tree = component.toJSON(); 55 | expect(tree).toMatchSnapshot(); 56 | }); 57 | -------------------------------------------------------------------------------- /.reaction/scripts/templates/Component.js.template: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import styled from "styled-components"; 4 | import { applyTheme } from "../../../utils"; 5 | 6 | const StyledDiv = styled.div` 7 | padding-bottom: ${applyTheme("COMPONENT.paddingBottom")}; 8 | padding-left: ${applyTheme("COMPONENT.paddingLeft")}; 9 | padding-right: ${applyTheme("COMPONENT.paddingRight")}; 10 | padding-top: ${applyTheme("COMPONENT.paddingTop")}; 11 | `; 12 | 13 | class COMPONENT extends Component { 14 | static propTypes = { 15 | /** 16 | * You can provide a `className` prop that will be applied to the outermost DOM element 17 | * rendered by this component. We do not recommend using this for styling purposes, but 18 | * it can be useful as a selector in some situations. 19 | */ 20 | className: PropTypes.string, 21 | /** 22 | * If you've set up a components context using 23 | * [@reactioncommerce/components-context](https://github.com/reactioncommerce/components-context) 24 | * (recommended), then this prop will come from there automatically. If you have not 25 | * set up a components context or you want to override one of the components in a 26 | * single spot, you can pass in the components prop directly. 27 | */ 28 | components: PropTypes.shape({ 29 | }).isRequired 30 | }; 31 | 32 | static defaultProps = { 33 | 34 | }; 35 | 36 | render() { 37 | const { className } = this.props; 38 | 39 | return ( 40 | TEST 41 | ); 42 | } 43 | } 44 | 45 | export default COMPONENT; 46 | -------------------------------------------------------------------------------- /package/src/utils/formatMoney.js: -------------------------------------------------------------------------------- 1 | import accounting from "accounting-js"; 2 | import currencyDefinitions from "./currencyDefinitions"; 3 | 4 | /** 5 | * A wrapper around accounting.formatMoney that handles minor differences between Reaction 6 | * API and accounting.js API. 7 | * @param {Number} price - A price (float) 8 | * @param {String} [currencyCode] A currency code, case insensitive. Defaults to "USD". 9 | * @returns {String} Formatted currency string such as "$15.99". If a matching currency is not provided, 10 | * returns `accounting.toFixed(price, 2)`. 11 | */ 12 | export default function formatMoney(price, currencyCode = "USD") { 13 | const currencyInfo = currencyDefinitions[currencyCode.toUpperCase()]; 14 | 15 | // Implementation of toFixed() that treats floats more like decimal values than binary, 16 | // fixing inconsistent precision rounding in JavaScript (where some .05 values round up, 17 | // while others round down): 18 | if (!currencyInfo) return accounting.toFixed(price, 2); 19 | 20 | // If there are no decimal places, in the case of the Japanese Yen, we adjust it here. 21 | let priceToFormat = price; 22 | if (currencyInfo.scale === 0) { 23 | priceToFormat = price * 100; 24 | } 25 | 26 | const currencyFormatSettings = { ...currencyInfo }; 27 | 28 | // Precision is mis-used in accounting js. Scale is the proper term for number 29 | // of decimal places. Let's adjust it here so accounting.js does not break. 30 | if (typeof currencyInfo.scale === "number") { 31 | currencyFormatSettings.precision = currencyInfo.scale; 32 | } 33 | 34 | return accounting.formatMoney(priceToFormat, currencyFormatSettings); 35 | } 36 | -------------------------------------------------------------------------------- /package/src/svg/iconValid.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const IconValidSvg = styled.svg` 5 | height: 100%; 6 | max-height: 100%; 7 | vertical-align: middle; 8 | `; 9 | 10 | const iconValid = ( 11 | 15 | 16 | 17 | 18 | 21 | 26 | 27 | 28 | 29 | 30 | ); 31 | 32 | export default iconValid; 33 | -------------------------------------------------------------------------------- /package/src/components/InventoryStatus/v1/__snapshots__/InventoryStatus.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 backorder notification when inventory is sold out and backorder is allowed 1`] = ` 6 | .c0 { 7 | -webkit-font-smoothing: antialiased; 8 | color: #505558; 9 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif; 10 | font-size: 14px; 11 | font-style: normal; 12 | font-stretch: normal; 13 | font-weight: 400; 14 | -webkit-letter-spacing: .02em; 15 | -moz-letter-spacing: .02em; 16 | -ms-letter-spacing: .02em; 17 | letter-spacing: .02em; 18 | line-height: 1.25; 19 | } 20 | 21 |
24 | Backordered - ships when available 25 |
26 | `; 27 | 28 | exports[`Renders low inventory notification when inventory is lower than threshold 1`] = `"StockWarning({\\"inventoryQuantity\\":6,\\"isLowInventoryQuantity\\":true})"`; 29 | 30 | exports[`Renders nothing when inventory is ready to be sold 1`] = `null`; 31 | 32 | exports[`Renders sold out notification when inventory is sold out and backorder is not allowed 1`] = ` 33 | .c0 { 34 | -webkit-font-smoothing: antialiased; 35 | color: #cd3f4c; 36 | font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,sans-serif; 37 | font-size: 14px; 38 | font-style: normal; 39 | font-stretch: normal; 40 | font-weight: 400; 41 | -webkit-letter-spacing: .02em; 42 | -moz-letter-spacing: .02em; 43 | -ms-letter-spacing: .02em; 44 | letter-spacing: .02em; 45 | line-height: 1.25; 46 | } 47 | 48 |
51 | Out of stock 52 |
53 | `; 54 | -------------------------------------------------------------------------------- /package/src/components/Button/v1/Button.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import renderer from "react-test-renderer"; 3 | import { mount } from "enzyme"; 4 | import mockComponents from "../../../tests/mockComponents"; 5 | import Button from "./Button"; 6 | 7 | const fakeEvent = { preventDefault() {} }; 8 | 9 | test("basic snapshot", () => { 10 | const component = renderer.create(); 11 | 12 | const tree = component.toJSON(); 13 | expect(tree).toMatchSnapshot(); 14 | }); 15 | 16 | test("calls onClick when clicked", () => { 17 | const clickSpy = jest.fn(); 18 | const item = mount(