# sewing_kit [![Build status](https://badge.buildkite.com/e7cdc87e61b9fe80e91e8686b6bfba53ca26a36366eb811a50.svg)](https://buildkite.com/shopify/sewing-kit-gem-ci) Zero configuration, high performance front end development at organization scale. `sewing_kit` facilitates legacy Rails integration using ERB tags. For a more complete modern stack with performance-as-a-feature, consider [quilt_rails](https://github.com/Shopify/quilt/tree/master/gems/quilt_rails). For details of the `sewing-kit` node package's configuration and usage, see the [sewing-kit README](/README.md). ## Quick Start Create a Rails project using `dev init` then: ### Install Sewing Kits ```sh # Add Ruby/Node dependencies bundle add sewing_kit yarn add @shopify/sewing-kit # Optional - add Polaris yarn add @shopify/polaris react react-dom yarn dev up ``` ### Add JavaScript sewing_kit looks for JavaScript in `app/ui/index.js`. The code in `index.js` (and any imported JS/CSS) will be built into a `main` bundle. ### Link to JS/CSS with `erb` Helpers The `main` bundle is imported into `erb` files using Rails helpers: ```erb <%= sewing_kit_link_tag *sewing_kit_assets('main', extension: 'css') %> <%= sewing_kit_script_tag *sewing_kit_assets('main') %> ``` **Note:** CSS `` tags appear only in production; in development, CSS is embedded within the `main.js` bundle. ## Minimal Project Layout ``` ├── Gemfile (must contain "gem 'sewing_kit") ├── package.json (must specify '@shopify/sewing-kit' as a 'devDependency') │ └── app └── ui │ └─- index.js └── views └─- layouts └─- application.html.erb (must link to JS / CSS using sewing_kit_script_tag / sewing_kit_link_tag ``` ## Rails, Polaris, and React Layout A typical Polaris app will use React to render views and components. The following layout shows best practice locations for: - Global SCSS settings - App sections (roughly analogous to Rails routes) - Components - Co-located CSS modules - Co-located unit tests ``` └── app └── ui ├─- index.js (renders React into the DOM) ├── styles (optional) │ └── settings.scss (global vars and @polaris overrides) │ └── tests (optional) │ └── each-test.ts │ └── setup.ts └── components (optional) ├── App │ ├── index.js │ ├── App.js │ └── tests │ └── App.test.js │ ├-─ MyComponent │ ├-─ index.js │ ├-─ MyComponent.js │ ├── MyComponent.scss (optional; component-scoped CSS styles, mixins, etc) │ └── tests │ └── MyComponent.test.js │ └── sections (optional; container views that compose presentation components into UI blocks) └── Home ├-─ index.js └── Home.js ``` ## React Boilerplate - Create a React app in `app/ui/App.js` ([example](https://github.com/Shopify/sewing-kit-gem-example/blob/master/app/ui/App.jsx)) - In an `erb` view, add a placeholder for React content ([example](https://github.com/Shopify/sewing-kit-gem-example/blob/master/app/views/home/index.html.erb#L4)) - In `index.js`, render a React component into the placeholder element ([example](https://github.com/Shopify/sewing-kit-gem-example/blob/master/app/ui/index.js)) - Use `sewing_kit_script_tag`/`sewing_kit_link_tag` helpers to link erb/js ([example](https://github.com/Shopify/sewing-kit-gem-example/blob/master/app/views/layouts/application.html.erb#L8)) ## Testing the front end For fast tests with consistent results, test front-end components using Jest instead of Rails integration tests. Use [`sewing-kit test`](https://github.com/Shopify/sewing-kit/blob/master/docs/commands/test.md#L3) to run all `.test.tsx` files in the `app/ui` directory. [Jest](https://jestjs.io) is used as a test runner, with customization available via [its sewing-kit plugin](https://github.com/Shopify/sewing-kit/blob/master/docs/plugins/jest.md). ### Testing React For testing React applications we provide and support [`@shopify/react-testing`](https://github.com/Shopify/quilt/tree/master/packages/react-testing). #### Example Given a component `MyComponent.tsx` ```tsx // app/components/MyComponent/MyComponent.tsx export function MyComponent({name}: {name: string}) { return
Hello, {name}!
; } ``` A test would be written using Jest and `@shopify/react-testing`'s `mount` feature. ```tsx // app/components/MyComponent/tests/MyComponent.test.tsx import {MyComponent} from '../MyComponent'; describe('MyComponent', () => { it('greets the given named person', () => { const wrapper = mount(); // toContainReactText is a custom matcher provided by @shopify/react-testing/matchers expect(wrapper).toContainReactText('Hello, Kokusho'); }); }); ``` ### Test setup files By default, the jest plugin will look for test setup files under `/app/ui/tests`. `setup` can be used to add any custom polyfills needed for the testing environment. ```tsx // app/ui/tests/setup.ts import 'isomorphic-fetch'; import 'raf/polyfill'; import {URL, URLSearchParams} from 'url'; (global as any).URL = URL; (global as any).URLSearchParams = URLSearchParams; ``` `each-test` can be used for any logic that needs to run for each individual test suite. Any setup logic that needs to happen with `jest` globals in scope, such as importing custom matchers, should also be done here. ```tsx // app/ui/tests/each-test.ts // we cannot import these in `setup` because `expect` will not be defined import '@shopify/react-testing/matchers'; beforeAll(() => { console.log('I will run before every test suite'); }); beforeEach(() => { console.log('I will run before every test case'); }); afterEach(() => { console.log('I will run after every test case'); }); afterAll(() => { console.log('I will run after every test suite'); }); ``` For more complete documentation of the jest plugin see [it's documentation](/docs/plugins/jest.md). ### Rails tests Building JavaScript assets for Rails tests adds friction to test driven development, but some projects rely on tests that intermingle erb / JavaScript behaviour. To accommodate different test styles, sewing_kit offers two test modes. #### `:return_no_assets` - sewing_kit helpers return empty arrays (default) As noted above, the Web Foundation team highly recommends `sewing-kit test` for front end testing. Projects following this recommendation can use sewing_kit's default behaviour for controller, integration tests, and e2e tests. Note that no sewing_kit content will appear in pages with this mode enabled. The default behaviour is equivalent to this configuration: ```rb # config/initializers/sewing_kit.rb SewingKit.configure do |config| config.test_manifest_mode = :return_no_asets end ``` #### Use precompiled assets If end-to-end tests are unavoidable: - Add extra steps to your test commands and CI pipelines that run `sewing-kit build --mode test` before test startup - Configure sewing_kit's `test_manifest_mode` to use precompiled assets: ```rb # config/initializers/sewing_kit.rb SewingKit.configure do |config| config.test_manifest_mode = :use_precompiled_assets end ``` If your tests require production assets, precompile using `sewing-kit build --mode production` instead. ## FAQ ### Which version of sewing-kit can I use? Assume that the sewing*kit gem's latest minor version requires \_at least* the same minor version of the sewing-kit package. If sewing-kit makes a breaking change, this gem's minor version will be bumped to match the required sewing-kit version. ### How can I fix production builds that are failing due to missing devDependencies? By moving everything into `package.json#dependencies`. This is necessary because Rails 5.2 prunes `devDependencies` during asset compilation. ### How can I test a production verison of my changes? Ideally, by deploying to a `staging` environment. If that is not possible, a production-like local development experience is available via: ```sh NODE_ENV=production bundle exec rake assets:precompile NODE_ENV=production SK_SIMULATE_PRODUCTION=1 dev run ``` Note that code changes will not be automatically recompiled in this state. After verifying production behaviour, run `bundle exec rake assets:clobber` to get back to development mode. ### My project is using `sprockets-commenoner`, can I use sewing_kit too? No. sprockets-commoner uses an outdated Babel version, and is no longer compatible with sewing-kit.