diff --git a/.codecov.yml b/.codecov.yml index 9f60c13ba77..fb0ac5308b8 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,15 +1,14 @@ fixes: - -"api/src/opentrons/"::"opentrons/" + - 'api/src/opentrons/::opentrons/' ignore: - - "architecture-and-planning" - - "audio" - - "compute" - - "**/node_modules" - - "protocol-library-kludge" - - "shared-data/definitions" - - "shared-data/definitions2" - - "shared-data/robot-data" - - "shared-data/protocol-json-schema" - - "shared-data/labware-json-schema" - - "webpack-config" - + - 'architecture-and-planning' + - 'audio' + - 'compute' + - '**/node_modules' + - 'protocol-library-kludge' + - 'shared-data/definitions' + - 'shared-data/definitions2' + - 'shared-data/robot-data' + - 'shared-data/protocol-json-schema' + - 'shared-data/labware-json-schema' + - 'webpack-config' diff --git a/.eslintignore b/.eslintignore index 236617aaf62..0fece7d27ff 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ api/** +update-server/** **/node_modules/** **/coverage/** **/dist/** @@ -12,3 +13,7 @@ api/** # compiled app-shell/lib/** discovery-client/lib/** + +# prettier +**/package.json +**/CHANGELOG.md diff --git a/.eslintrc.js b/.eslintrc.js index 1ccd2e3a356..f17d7e7fa44 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,15 +7,16 @@ module.exports = { 'standard', 'plugin:react/recommended', 'plugin:flowtype/recommended', + 'plugin:prettier/recommended', + 'prettier/flowtype', + 'prettier/react', + 'prettier/standard', ], - plugins: ['flowtype', 'react', 'json'], + plugins: ['flowtype', 'react', 'json', 'prettier'], rules: { camelcase: 'off', - 'object-curly-spacing': 'off', - 'comma-dangle': ['error', 'always-multiline'], - 'flowtype/delimiter-dangle': ['error', 'always-multiline'], }, globals: {}, diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000000..d9888254456 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,15 @@ +'use strict' + +module.exports = { + printWidth: 80, // default + tabWidth: 2, // default + useTabs: false, // default + semi: false, + singleQuote: true, + jsxSingleQuote: false, // default + trailingComma: 'es5', + bracketSpacing: true, // default + jsxBracketSameLine: false, // default + arrowParens: 'avoid', // default + endOfLine: 'lf', +} diff --git a/.stylelintrc.js b/.stylelintrc.js index bb5aacb233f..80567d6f42c 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -1,63 +1,60 @@ module.exports = { - extends: [ - 'stylelint-config-standard', - ], + extends: ['stylelint-config-standard'], - ignoreFiles: [ - 'api/**', - '**/dist/**', - '**/coverage/**', - '**/venv/**', - ], + ignoreFiles: ['api/**', '**/dist/**', '**/coverage/**', '**/venv/**'], rules: { 'selector-class-pattern': /^[a-z0-9_]+$/, // support css-modules - 'selector-pseudo-class-no-unknown': [ true, { - ignorePseudoClasses: [ - 'export', - 'import', - 'global', - 'local', - ], - }], + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['export', 'import', 'global', 'local'], + }, + ], - 'property-no-unknown': [ true, { - ignoreProperties: [ - // css-modules - // TODO(mc, 2018-02-09): stop using composes - 'composes', - 'compose-with', + 'property-no-unknown': [ + true, + { + ignoreProperties: [ + // css-modules + // TODO(mc, 2018-02-09): stop using composes + 'composes', + 'compose-with', - // lost grid (http://lostgrid.org/docs.html) - // TODO(mc, 2018-02-09): use stylelint-config-lost once stylelint- - // config-css-modules property-no-unknown no longer conflicts - 'lost-align', - 'lost-center', - 'lost-column', - 'lost-flex-container', - 'lost-masonry-column', - 'lost-masonry-wrap', - 'lost-move', - 'lost-offset', - 'lost-row', - 'lost-unit', - 'lost-utility', - 'lost-waffle', - ], - }], + // lost grid (http://lostgrid.org/docs.html) + // TODO(mc, 2018-02-09): use stylelint-config-lost once stylelint- + // config-css-modules property-no-unknown no longer conflicts + 'lost-align', + 'lost-center', + 'lost-column', + 'lost-flex-container', + 'lost-masonry-column', + 'lost-masonry-wrap', + 'lost-move', + 'lost-offset', + 'lost-row', + 'lost-unit', + 'lost-utility', + 'lost-waffle', + ], + }, + ], - 'at-rule-no-unknown': [ true, { - ignoreAtRules: [ - // TODO(mc, 2018-02-09): stop using @value - 'value', + 'at-rule-no-unknown': [ + true, + { + ignoreAtRules: [ + // TODO(mc, 2018-02-09): stop using @value + 'value', - // lost grid (http://lostgrid.org/docs.html) - // TODO(mc, 2018-02-09): use stylelint-config-lost once stylelint- - // config-css-modules at-rule-no-unknown no longer conflicts - 'lost', - ], - }], + // lost grid (http://lostgrid.org/docs.html) + // TODO(mc, 2018-02-09): use stylelint-config-lost once stylelint- + // config-css-modules at-rule-no-unknown no longer conflicts + 'lost', + ], + }, + ], }, } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 619ab58c47f..ee4981246e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,24 +2,24 @@ Thanks for your interest in contributing to the Opentrons platform! This Contributing Guide is intended to ensure best practices for both internal Opentrons contributors as well as any external contributors. We want to make sure you’re set up to contribute effectively, no matter if you’re helping us out with bug reports, code, documentation, feature suggestions, or anything else. This guide covers: -* [Opening Issues](#opening-issues) -* [Opening Pull Requests](#opening-pull-requests) -* [Commit Guidelines](#commit-guidelines) -* [Project and Repository Structure](#project-and-repository-structure) -* [Development Setup](#development-setup) -* [Prior Art](#prior-art) +- [Opening Issues](#opening-issues) +- [Opening Pull Requests](#opening-pull-requests) +- [Commit Guidelines](#commit-guidelines) +- [Project and Repository Structure](#project-and-repository-structure) +- [Development Setup](#development-setup) +- [Prior Art](#prior-art) ## Opening Issues Filing an issue is a great way to contribute to the project! Bug reports and feature requests are really useful for us as we plan our work. If you’d like to open an issue, please consider the following questions before opening: -* Is this issue for a bug, a feature request, or something else? - * Please make this is clear in your description so it’s easier to address -* Has this issue already been opened? - * Duplicate tickets slow things down, so make sure to search before you open! - * If there’s already a ticket, please comment on the existing thread! -* Is this a support request? - * If yes, you're better off checking out our [support page][support] rather than opening a GitHub issue +- Is this issue for a bug, a feature request, or something else? + - Please make this is clear in your description so it’s easier to address +- Has this issue already been opened? + - Duplicate tickets slow things down, so make sure to search before you open! + - If there’s already a ticket, please comment on the existing thread! +- Is this a support request? + - If yes, you're better off checking out our [support page][support] rather than opening a GitHub issue To ensure your issue can be addressed quickly, please fill out the sections in the existing issue template to the best of your ability! @@ -31,25 +31,25 @@ Please note that by contributing to the Opentrons platform, you agree to share t Before opening any PR, please run through the following questions: -* Does this PR address an already open issue? - * If not, please consider opening an issue first - * This is to ensure you don't end up duplicating work or wasting time on a PR that won't be accepted -* Does this PR incorporate many different changes? - * If yes, would the PR work better as a series of smaller PR's? - * Our team is more than happy to help you figure out an incremental plan -* Does this PR include code changes without test and/or documentation updates? - * If yes, your PR may not be ready to open - * Tests and documentation are a vital part of any code contribution -* Are there a reasonable number of commits and are they properly informative? - * The best kind of PR is a tiny PR with a single commit - * To avoid introducing problems into our Git history, we may have to ask you to squash or otherwise amend your commit(s) - * See [Commit Guidelines](#commit-guidelines) below for tips on keeping a good Git history +- Does this PR address an already open issue? + - If not, please consider opening an issue first + - This is to ensure you don't end up duplicating work or wasting time on a PR that won't be accepted +- Does this PR incorporate many different changes? + - If yes, would the PR work better as a series of smaller PR's? + - Our team is more than happy to help you figure out an incremental plan +- Does this PR include code changes without test and/or documentation updates? + - If yes, your PR may not be ready to open + - Tests and documentation are a vital part of any code contribution +- Are there a reasonable number of commits and are they properly informative? + - The best kind of PR is a tiny PR with a single commit + - To avoid introducing problems into our Git history, we may have to ask you to squash or otherwise amend your commit(s) + - See [Commit Guidelines](#commit-guidelines) below for tips on keeping a good Git history To ensure your code is reviewed quickly and thoroughly, please fill out the sections in the existing pull request template best of your ability! If you’d like some recommended reading for writing good pull requests, check out: -* [How to write the perfect pull request][how-to-write-pr] -* [The (written) unwritten guide to pull requests][unwritten-guide-to-pr] -* [The Art of a Pull Request][art-of-pr] +- [How to write the perfect pull request][how-to-write-pr] +- [The (written) unwritten guide to pull requests][unwritten-guide-to-pr] +- [The Art of a Pull Request][art-of-pr] After your Pull Request is merged (or otherwise closed), you’ll want to make sure to delete the branch in GitHub. You probably want to delete your local branch, too, depending on your own personal organizational strategies / general paranoia. @@ -59,6 +59,20 @@ If you're looking for something to work on, especially for a first contribution, ## Commit Guidelines +### Before you commit + +Before you're ready to make a commit, you should do you best to make sure that: + +- All tests are passing + - `make test` + - See [Testing](#Testing) section for more details +- All code quality checks are passing + - `make format lint` + - See [Code quality](#Code-quality) section for more details + - Especially consider [setting up your code editor](#Editor-setup) to run formatting and quality checks automatically as you code + +### Making your commit + Good commit messages are essential to keeping an organized and readable Git history. A readable Git history makes our lives easier when doing necessary work like writing changelogs or tracking down regressions. Please read [How to Write a Git Commit Message][commit-message-how-to] by Chris Beams and then come back here. These selected guidelines (copied and pasted from that article) are a very good starting point to think about when writing your commit message: 1. Separate subject from body with a blank line @@ -88,14 +102,14 @@ This will launch the commitizen wizard, which will ask you to: 9. `ci` - Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) 10. `chore` - Other changes that don't modify src or test files 2. Select a scope - * For `feat`, `fix`, `refactor`, and `perf`, this should a top-level project, e.g. `app` or `api` - * For other commit types, use your best judgement or omit + - For `feat`, `fix`, `refactor`, and `perf`, this should a top-level project, e.g. `app` or `api` + - For other commit types, use your best judgement or omit 3. Write a short commit title - * Written according to the guidelines above + - Written according to the guidelines above 4. Write a longer description if necessary - * Also written according to the guidelines above + - Also written according to the guidelines above 5. Mention any tickets addressed by the commit - * e.g. `Closes #xyz` + - e.g. `Closes #xyz` ![commitizen](https://user-images.githubusercontent.com/2963448/40452320-776de7e0-5eaf-11e8-9aa7-ad706713b197.gif) @@ -105,13 +119,13 @@ Most of Opentrons’ projects live in the [Opentrons/opentrons][repo] repository Generally, the directory / file structure of our monorepo looks something like this: -* \[Project] -* \[Another Project] -* etc. -* `scripts` - Repository level scripts (mostly for CI) -* `Makefile` - Top level makefile for CI -* Various repository level dotfiles (CI and git config) -* `README.md`, `CONTRIBUTING.md`, `LICENSE`, etc. +- \[Project] +- \[Another Project] +- etc. +- `scripts` - Repository level scripts (mostly for CI) +- `Makefile` - Top level makefile for CI +- Various repository level dotfiles (CI and git config) +- `README.md`, `CONTRIBUTING.md`, `LICENSE`, etc. Our projects use a mix of languages, but mostly Python (backend + robotics) and JavaScript (frontend). Each project has its own `README` + `Makefile` + dependency management. @@ -125,31 +139,31 @@ Individual projects may have additional instructions, so be sure to check out th Your computer will need the following tools installed to be able to develop with the Opentrons platform: -* macOS 10.11+, Linux, or Windows 10 - * On Windows, please configure Git’s `core.autocrlf` setting (see the [Git config docs](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration)) to `input` so that shell scripts required for the robot’s boot process in `api/opentrons/resources` do not have carriage returns inserted. -* Python 3.6 ([pyenv](https://github.com/pyenv/pyenv) is optional, but recommended for macOS / Linux. If `pyenv` is not available for your system or you do not want to use it, you can set the environment variable `OT_PYTHON` to the full path to the Python 3.6 executable) +- macOS 10.11+, Linux, or Windows 10 + - On Windows, please configure Git’s `core.autocrlf` setting (see the [Git config docs](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration)) to `input` so that shell scripts required for the robot’s boot process in `api/opentrons/resources` do not have carriage returns inserted. +- Python 3.6 ([pyenv](https://github.com/pyenv/pyenv) is optional, but recommended for macOS / Linux. If `pyenv` is not available for your system or you do not want to use it, you can set the environment variable `OT_PYTHON` to the full path to the Python 3.6 executable) - ```shell - pyenv install 3.6.4 - ``` + ```shell + pyenv install 3.6.4 + ``` -* Node v8 LTS (Carbon) - [nvm][] is optional, but recommended +- Node v8 LTS (Carbon) - [nvm][] is optional, but recommended - ```shell - nvm install lts/carbon - ``` + ```shell + nvm install lts/carbon + ``` -* [yarn][yarn-install] - JavaScript package manager +- [yarn][yarn-install] - JavaScript package manager -* [commitizen][] - Commit message formatter +- [commitizen][] - Commit message formatter - ```shell - yarn global add commitizen - ``` + ```shell + yarn global add commitizen + ``` -* GNU Make - we use [Makefiles][] to manage our builds +- GNU Make - we use [Makefiles][] to manage our builds -* cURL - used to push development updates to robots +- cURL - used to push development updates to robots Once you're set up, clone the repository and install all project dependencies: @@ -161,20 +175,37 @@ make install In addition, if (and only if) you want to build a PDF version of the Opentrons API documentation, you must install a latex distribution that includes a callable pdflatex. If that is installed, you can do `make -C api docs-pdf`. -### Testing and Linting +### Testing -You can test with: +We use: + +- [pytest][] to test Python +- [Jest][jest] to test JavaScript + - To run tests in watch mode, you should also install [watchman][] + +You can tests with: ```shell # run all tests make test -# run a specific project's tests -make -C api test -make -C update-server test -make -C components test -make -C protocol-designer test -make -C app test +# run tests per language +make test-py +make test-js +``` + +You can pass some options to the JavaScript tests: + +```shell +# run JavaScript tests in watch mode +make test-js watch=true + +# disable test coverage +make test-js cover=false + +# update snapshot tests +# https://jestjs.io/docs/en/snapshot-testing +make test-js updateSnapshot=true ``` And you can run code linting / typechecking with: @@ -190,6 +221,69 @@ make lint-css make check-js ``` +[pytest]: https://docs.pytest.org/en/latest/ +[jest]: https://jestjs.io/ +[watchman]: https://facebook.github.io/watchman/ + +### Code quality + +To help with code quality and maintainability, we use a collection of tools that can be roughly sorted into the following categories (with some overlaps): + +- [Linters][lint] + - Analyze the code for various potential bugs and errors + - [Pylama][pylama] - Python code audit tool + - [ESLint][eslint] - JavsScript/JSON linter + - [stylelint][] - CSS linter +- [Typecheckers][type-check] + - Verify that the code is [type safe][type-safe] + - [mypy][] - Static type checker for Python + - [Flow][flow] - Static type checker for JavaScript +- Formatters + - (Re)format source code to adhere to a consistent [style][code-style] + - [Prettier][prettier] - Code formatter for JavaScript, JSON, Markdown, and YAML + +These tools can be run with the following commands: + +```shell +# lint all code and run all typechecks +make lint + +# lint by language +# note: Python linting also includes typechecking +make lint-py +make lint-js +make lint-json +make lint-css + +# typecheck JavaScript code +make check-js + +# format JavaScript, JSON, Markdown, and YAML +make format +``` + +[lint]: https://en.wikipedia.org/wiki/Lint_(software) +[type-check]: https://en.wikipedia.org/wiki/Type_system#Type_checking +[type-safe]: https://en.wikipedia.org/wiki/Type_safety +[code-style]: https://en.wikipedia.org/wiki/Programming_style +[eslint]: https://eslint.org/ +[pylama]: https://github.com/klen/pylama +[stylelint]: https://stylelint.io/ +[flow]: https://flow.org/ +[mypy]: http://mypy-lang.org/ +[prettier]: https://prettier.io/ + +#### Editor setup + +Most, if not all, of the tools above have plugins available for your code editor that will run quality checks and formatting as you write and/or save. We **highly recommend** setting up your editor to format and check your code automatically. + +- Pylama - Search your editor's package manager +- ESLint - +- stylelint - +- mypy - Search your editor's package manager +- Flow - +- Prettier - + ### Opentrons API Be sure to check out the [API `README`][api-readme] for additional instructions. To run the Opentrons API in development mode: @@ -219,7 +313,7 @@ Our release process is still a work-in-progress. All projects are currently vers 1. `make bump` (see details below) 2. Inspect version bumps and changelogs 3. Edit the user-facing changelog at `app-shell/build/release-notes.md` to add the new notes -4. `git checkout -b release_${version}` (The branch name _must_ match `release_*` to trigger signed builds that can be used as RCs) +4. `git checkout -b release_${version}` (The branch name _must_ match `release_*` to trigger signed builds that can be used as RCs) 5. `git add --all` 6. `git cz` - Type: `chore` @@ -228,8 +322,8 @@ Our release process is still a work-in-progress. All projects are currently vers 7. Open a PR into `edge` 8. Squash merge the PR once approved 9. Verify that CI is green on `edge` and test the build artifacts -10. Pull latest `edge` to your machine -11. `git tag -a v${version} -m 'chore(release): ${version}'` +10. Pull latest `edge` to your machine +11. `git tag -a v${version} -m 'chore(release): ${version}'` 12. `git push --tags` #### `make bump` usage @@ -237,12 +331,12 @@ Our release process is still a work-in-progress. All projects are currently vers `make bump` runs `lerna publish` (with npm and git push disabled) to bump all required files. You can pass options to lerna with the `opts` environment variable. See the [lerna publish docs][lerna-publish] for available options. The most important options are: - `--preid` - Used to specify the pre-release identifier - - Default: `alpha` - - Valid: `alpha`, `beta` + - Default: `alpha` + - Valid: `alpha`, `beta` - `--cd-version` - Used to specify a semantic version bump - - Default: `prerelease` - - Valid: `major`, `minor`, `patch`, `premajor`, `preminor`, `prepatch`, `prerelease` - - See [semver.inc][semver-inc] for keyword meanings + - Default: `prerelease` + - Valid: `major`, `minor`, `patch`, `premajor`, `preminor`, `prepatch`, `prerelease` + - See [semver.inc][semver-inc] for keyword meanings - `--repo-version` - Used to specify an explicit version ```shell @@ -281,9 +375,9 @@ yarn run lerna [opts] This Contributing Guide was influenced by a lot of work done on existing Contributing Guides. They're great reads if you have the time! -* [React.js Contributing Guide][react-contributing] -* [Node.js Contributing Guide][node-contributing] -* [Kibana Contributing Guide][kibana-contributing] +- [React.js Contributing Guide][react-contributing] +- [Node.js Contributing Guide][node-contributing] +- [Kibana Contributing Guide][kibana-contributing] ## Developer "Gotchas" diff --git a/Makefile b/Makefile index 37eb832ba41..646af7f4d80 100755 --- a/Makefile +++ b/Makefile @@ -101,6 +101,10 @@ test-js: .PHONY: lint lint: lint-py lint-js lint-json lint-css check-js +.PHONY: format +format: + prettier --ignore-path .eslintignore --write ".*.@(js|yml)" "**/*.@(js|json|md|yml)" + .PHONY: lint-py lint-py: $(MAKE) -C $(API_DIR) lint diff --git a/README.md b/README.md index 196478e81b4..60ffe95c028 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ [![AppVeyor][appveyor-badge]][appveyor] [![Codecov][codecov-badge]][codecov] -* [Overview](#overview) -* [Opentrons API](#opentrons-api) -* [Opentrons App](#opentrons-app) -* [Contributing/Building](#contributing) +- [Overview](#overview) +- [Opentrons API](#opentrons-api) +- [Opentrons App](#opentrons-app) +- [Contributing/Building](#contributing) ## Overview @@ -29,15 +29,15 @@ pipette.aspirate(tube_1).dispense(tube_2) That is how you tell the Opentrons robot to pipette its max volume from one tube to another. Learn more here: -* [Documentation](http://docs.opentrons.com) -* [Source code](./api) +- [Documentation](http://docs.opentrons.com) +- [Source code](./api) ## Opentrons App Easily upload a protocol, calibrate positions, and run your experiment from your computer. -* [Documentation](https://support.opentrons.com/) -* [Source code](./app) +- [Documentation](https://support.opentrons.com/) +- [Source code](./app) ![ot-app](https://s3.amazonaws.com/opentrons-images/standalone/ot-2-app.png) diff --git a/__mocks__/electron-updater.js b/__mocks__/electron-updater.js index bd207ab44a9..d5b9fdac857 100644 --- a/__mocks__/electron-updater.js +++ b/__mocks__/electron-updater.js @@ -10,7 +10,7 @@ module.exports.__mockReset = () => { autoDownload: true, autoInstallOnAppQuit: true, allowDowngrade: false, - currentVersion: {version: '0.0.0-mock'}, + currentVersion: { version: '0.0.0-mock' }, channel: null, checkForUpdates: jest.fn(), diff --git a/__mocks__/electron.js b/__mocks__/electron.js index b46f6c93a05..836d880aa9b 100644 --- a/__mocks__/electron.js +++ b/__mocks__/electron.js @@ -13,10 +13,10 @@ jest.mock( const __mockRemotes = {} const __clearMock = () => { - Object.keys(__mockRemotes).forEach((remoteName) => { + Object.keys(__mockRemotes).forEach(remoteName => { const remote = __mockRemotes[remoteName] - Object.keys(remote).forEach((property) => { + Object.keys(remote).forEach(property => { const value = remote[property] value && value.mockClear && value.mockClear() }) @@ -28,7 +28,7 @@ module.exports = { __clearMock, // return jest mocked versions of remote modules remote: { - require: jest.fn((name) => { + require: jest.fn(name => { if (__mockRemotes[name]) return __mockRemotes[name] const remote = jest.genMockFromModule( diff --git a/app-shell/README.md b/app-shell/README.md index 41f083178d9..b2acdca742d 100644 --- a/app-shell/README.md +++ b/app-shell/README.md @@ -12,9 +12,9 @@ This directory contains the code for the [Electron main process][electron-main] The app uses [`electron-store`][electron-store] to store its configuration in a JSON file located at: -* `%APPDATA%\Opentrons\config.json` on Windows -* `~/.config/Opentrons/config.json` on Linux -* `~/Library/Application Support/Opentrons/config.json` on macOS +- `%APPDATA%\Opentrons\config.json` on Windows +- `~/.config/Opentrons/config.json` on Linux +- `~/Library/Application Support/Opentrons/config.json` on macOS Configuration values will be determined by: @@ -22,12 +22,12 @@ Configuration values will be determined by: 2. If no CLI argument, checking for an environment variable 3. If no environment variable, checking the `config.json` file 4. If no value in `config.json`, default value from code will be used - * Default value will also be written to `config.json` + - Default value will also be written to `config.json` **If overriding boolean values**: -* For CLI arguments, use `--value` for true, and `--disable_value` for false -* For environment variables, use `OT_APP_VALUE=1` for true, and `OT_APP_VALUE=0` for false +- For CLI arguments, use `--value` for true, and `--disable_value` for false +- For environment variables, use `OT_APP_VALUE=1` for true, and `OT_APP_VALUE=0` for false #### overriding config for end-users @@ -69,163 +69,163 @@ The easiest way to override config on Windows is to modify the Opentrons desktop ##### devtools -* CLI argument: `--devtools` or `--disable_devtools` -* Environment variable: `OT_APP_DEVTOOLS` -* JSON path: `devtools` -* Default: `false` +- CLI argument: `--devtools` or `--disable_devtools` +- Environment variable: `OT_APP_DEVTOOLS` +- JSON path: `devtools` +- Default: `false` Enables and opens the Chrome devtools. ##### modules -* CLI argument: `--modules` or `--disable_modules` -* Environment variable: `OT_APP_MODULES` -* JSON path: `modules` -* Default: `false` +- CLI argument: `--modules` or `--disable_modules` +- Environment variable: `OT_APP_MODULES` +- JSON path: `modules` +- Default: `false` Enables experimental support for [Opentrons Modules](http://opentrons.com/modules). ##### update.channel -* CLI argument: `--channel` -* Environment variable: `OT_APP_UPDATE__CHANNEL` -* JSON path: `update.channel` -* Default: `"latest"` +- CLI argument: `--channel` +- Environment variable: `OT_APP_UPDATE__CHANNEL` +- JSON path: `update.channel` +- Default: `"latest"` Sets the app's self-update channel. Options are `alpha`, `beta`, or `latest`. `alpha` is the least tested/stable, followed by `beta`, followed by `latest`. `alpha` and `beta` get new features earlier than `latest`. ##### log.level.file -* CLI argument: `--log.level.file` -* Environment variable: `OT_APP_LOG__LEVEL__FILE` -* JSON path: `log.level.file` -* Default: `"debug"` +- CLI argument: `--log.level.file` +- Environment variable: `OT_APP_LOG__LEVEL__FILE` +- JSON path: `log.level.file` +- Default: `"debug"` Default log level of the `combined.log` log file. See logging section below. ##### log.level.console -* CLI argument: `--log.level.console` -* Environment variable: `OT_APP_LOG__LEVEL__CONSOLE` -* JSON path: `log.level.console` -* Default: `"info"` +- CLI argument: `--log.level.console` +- Environment variable: `OT_APP_LOG__LEVEL__CONSOLE` +- JSON path: `log.level.console` +- Default: `"info"` Default log level of the console log. See logging section below. ##### ui.webPreferences.webSecurity -* CLI argument: `--ui.webPreferences.webSecurity` or `--disable_ui.webPreferences.webSecurity` -* Environment variable: `OT_APP_UI__WEB_PREFERENCES__WEB_SECURITY` -* JSON path: `ui.webPreferences.webSecurity` -* Default: `true` +- CLI argument: `--ui.webPreferences.webSecurity` or `--disable_ui.webPreferences.webSecurity` +- Environment variable: `OT_APP_UI__WEB_PREFERENCES__WEB_SECURITY` +- JSON path: `ui.webPreferences.webSecurity` +- Default: `true` Sets the `webPreferences.webSecurity` option of the Electron [BrowserWindow][electron-docs-browser-window] created for the UI. When disabled, the browsers same-origin policy will be disabled. This should only be disabled in development / testing environments. ##### ui.width -* CLI argument: `--ui.width` -* Environment variable: `OT_APP_UI__WIDTH` -* JSON path: `ui.width` -* Default: `1024` +- CLI argument: `--ui.width` +- Environment variable: `OT_APP_UI__WIDTH` +- JSON path: `ui.width` +- Default: `1024` [BrowserWindow][electron-docs-browser-window] width at launch. ##### ui.height -* CLI argument: `--ui.height` -* Environment variable: `OT_APP_UI__HEIGHT` -* JSON path: `ui.height` -* Default: `768` +- CLI argument: `--ui.height` +- Environment variable: `OT_APP_UI__HEIGHT` +- JSON path: `ui.height` +- Default: `768` [BrowserWindow][electron-docs-browser-window] height at launch. ##### ui.url.protocol -* CLI argument: `--ui.url.protocol` -* Environment variable: `OT_APP_UI__URL__PROTOCOL` -* JSON path: `ui.url.protocol` -* Default: `"file:"` +- CLI argument: `--ui.url.protocol` +- Environment variable: `OT_APP_UI__URL__PROTOCOL` +- JSON path: `ui.url.protocol` +- Default: `"file:"` Protocol used to fetch the UI's `index.html`. If you want to fetch the UI from the dev server in [`app`](../app), set this to `http:`. ##### ui.url.path -* CLI argument: `--ui.url.path` -* Environment variable: `OT_APP_UI__URL__PATH` -* JSON path: `ui.url.path` -* Default: `"ui/index.html"` +- CLI argument: `--ui.url.path` +- Environment variable: `OT_APP_UI__URL__PATH` +- JSON path: `ui.url.path` +- Default: `"ui/index.html"` Path to `index.html`. If `ui.url.protocol` is `file:`, this path is relative to the [application directory][electron-docs-get-app-path]. This path will be combined with the protocol to get the full path to `index.html`. If you want to fetch the UI from the dev server in [`app`](../app), set this to `localhost:8090`. ##### analytics.appId -* CLI argument: `--analytics.appId` -* Environment variable: `OT_APP_ANALYTICS__APP_ID` -* JSON path: `analytics.appId` -* Default: Random UUID generated at first launch +- CLI argument: `--analytics.appId` +- Environment variable: `OT_APP_ANALYTICS__APP_ID` +- JSON path: `analytics.appId` +- Default: Random UUID generated at first launch Random, persistent ID to use for anonymous analytics tracking if opted in. ##### analytics.optedIn -* CLI argument: `--analytics.optedIn` -* Environment variable: `OT_APP_ANALYTICS__OPTED_IN` -* JSON path: `analytics.optedIn` -* Default: `false` +- CLI argument: `--analytics.optedIn` +- Environment variable: `OT_APP_ANALYTICS__OPTED_IN` +- JSON path: `analytics.optedIn` +- Default: `false` Whether or not the user has opted into anonymous analytics tracking. ##### analytics.seenOptIn -* CLI argument: `--analytics.seenOptIn` -* Environment variable: `OT_APP_ANALYTICS__SEEN_OPT_IN` -* JSON path: `analytics.seenOptIn` -* Default: `false` +- CLI argument: `--analytics.seenOptIn` +- Environment variable: `OT_APP_ANALYTICS__SEEN_OPT_IN` +- JSON path: `analytics.seenOptIn` +- Default: `false` Whether or not the user has seen the initial analytics description pop-up. ##### support.userId -* CLI argument: `--support.userId` -* Environment variable: `OT_APP_SUPPORT__USER_ID` -* JSON path: `support.userId` -* Default: Random UUID generated at first launch +- CLI argument: `--support.userId` +- Environment variable: `OT_APP_SUPPORT__USER_ID` +- JSON path: `support.userId` +- Default: Random UUID generated at first launch Random, persistent ID to use for support tracking. Different than `analytics.appId`. ##### support.createdAt -* CLI argument: `--support.createdAt` -* Environment variable: `OT_APP_SUPPORT__CREATED_AT` -* JSON path: `support.createdAt` -* Default: Current Unix time at first launch +- CLI argument: `--support.createdAt` +- Environment variable: `OT_APP_SUPPORT__CREATED_AT` +- JSON path: `support.createdAt` +- Default: Current Unix time at first launch Timestamp of first app launch. ##### support.name -* CLI argument: `--support.name` -* Environment variable: `OT_APP_SUPPORT__NAME` -* JSON path: `support.name` -* Default: `"App User"` +- CLI argument: `--support.name` +- Environment variable: `OT_APP_SUPPORT__NAME` +- JSON path: `support.name` +- Default: `"App User"` Full name of app user to populate "Name" in support conversations. ##### support.email -* CLI argument: `--support.email` -* Environment variable: `OT_APP_SUPPORT__EMAIL` -* JSON path: `support.email` -* Default: `null` +- CLI argument: `--support.email` +- Environment variable: `OT_APP_SUPPORT__EMAIL` +- JSON path: `support.email` +- Default: `null` Email of app user to populate "Email" in support conversations. ##### discovery.candidates -* CLI argument: `--discovery.candidates` -* Environment variable: `OT_APP_DISCOVERY__CANDIDATES` -* JSON path: `--discovery.candidates` -* Default: `[]` +- CLI argument: `--discovery.candidates` +- Environment variable: `OT_APP_DISCOVERY__CANDIDATES` +- JSON path: `--discovery.candidates` +- Default: `[]` `string` or `Array` of extra IP address(es)/hosts for the discovery client to track. For example, to get the discovery client to find an instance of the API server running on your own computer, you could do `--discovery.candidates=localhost`. @@ -233,25 +233,25 @@ Email of app user to populate "Email" in support conversations. Logs from both the main and renderer processes are logged to rolling files as well as the terminal by [winston][winston]. The logs are stored in a `logs` directory at: -* `%APPDATA%\Opentrons\logs` on Windows -* `~/.config/Opentrons/logs` on Linux -* `~/Library/Application Support/Opentrons/logs` on macOS +- `%APPDATA%\Opentrons\logs` on Windows +- `~/.config/Opentrons/logs` on Linux +- `~/Library/Application Support/Opentrons/logs` on macOS #### available log levels -* error -* warn -* info -* http -* verbose -* debug -* silly +- error +- warn +- info +- http +- verbose +- debug +- silly #### logs written -* `logs/combined.log` - JSON logs at level `log.level.file` and above -* `logs/error.log` - JSON logs at level `error` and above -* Console - Human-readable logs at level `log.level.console` and above +- `logs/combined.log` - JSON logs at level `log.level.file` and above +- `logs/error.log` - JSON logs at level `error` and above +- Console - Human-readable logs at level `log.level.console` and above ## developing @@ -274,8 +274,8 @@ You probably want to be developing from the [app directory](../app) instead. Its The desktop application shell uses: -* [Electron][] -* [Electron Builder][electron-builder] +- [Electron][] +- [Electron Builder][electron-builder] ### testing @@ -289,9 +289,9 @@ This section details production build instructions for the desktop application. `electron-builder` requires some native dependencies to build and package the app (see [the electron-builder docs][electron-builder-platforms]). -* macOS - None -* Windows - None -* Linux - icnsutils, graphicsmagick, and xz-utils +- macOS - None +- Windows - None +- Linux - icnsutils, graphicsmagick, and xz-utils ```shell sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils @@ -299,17 +299,17 @@ This section details production build instructions for the desktop application. ### build tasks -* `make` - Default target is "clean package" -* `make clean` - Delete the dist folder -* `make package` - Creates a production package of the app for running and inspection (does not create a distributable) +- `make` - Default target is "clean package" +- `make clean` - Delete the dist folder +- `make package` - Creates a production package of the app for running and inspection (does not create a distributable) #### production builds All packages and/or distributables will be placed in `app-shell/dist`. After running `make package`, you can launch the production app with: -* macOS: `./dist/mac/Opentrons.app/Contents/MacOS/Opentrons` -* Linux: `./dist/linux-unpacked/opentrons` -* Windows: `./dist/win-unpacked/Opentrons.exe` +- macOS: `./dist/mac/Opentrons.app/Contents/MacOS/Opentrons` +- Linux: `./dist/linux-unpacked/opentrons` +- Windows: `./dist/win-unpacked/Opentrons.exe` To run the production app in debug mode, set the `DEBUG` environment variable. For example, on macOS: @@ -338,6 +338,7 @@ make dist-win OT_BUCKET_APP=opentrons-app OT_FOLDER_APP=builds These tasks use the following environment variables defined: + | name | description | required | description | | ------------- | ------------- | -------- | -------------------------------------- | | OT_BUCKET_APP | AWS S3 bucket | yes | Artifact deploy bucket | @@ -348,9 +349,9 @@ These tasks use the following environment variables defined: The release channel is set according to the version string: -* `vM.m.p-alpha.x` - "alpha" channel -* `vM.m.p-beta.x` - "beta" channel -* `vM.m.p` - "latest" channel +- `vM.m.p-alpha.x` - "alpha" channel +- `vM.m.p-beta.x` - "beta" channel +- `vM.m.p` - "latest" channel The `electron-updater` autoupdate files (e.g. `beta-mac.yml`) will only be copied to the publish directory if `OT_TAG` is set. diff --git a/app-shell/src/__tests__/discovery.test.js b/app-shell/src/__tests__/discovery.test.js index e48dff68c47..fa0ee2cec65 100644 --- a/app-shell/src/__tests__/discovery.test.js +++ b/app-shell/src/__tests__/discovery.test.js @@ -2,8 +2,8 @@ import EventEmitter from 'events' import Store from 'electron-store' import DiscoveryClient from '@opentrons/discovery-client' -import {registerDiscovery} from '../discovery' -import {getConfig, getOverrides} from '../config' +import { registerDiscovery } from '../discovery' +import { getConfig, getOverrides } from '../config' jest.mock('electron-store') jest.mock('@opentrons/discovery-client') @@ -25,7 +25,7 @@ describe('app-shell/discovery', () => { setPollInterval: jest.fn().mockReturnThis(), }) - getConfig.mockReturnValue({enabled: true, candidates: []}) + getConfig.mockReturnValue({ enabled: true, candidates: [] }) getOverrides.mockReturnValue({}) dispatch = jest.fn() @@ -60,18 +60,18 @@ describe('app-shell/discovery', () => { }) test('calls client.start on "discovery:START"', () => { - registerDiscovery(dispatch)({type: 'discovery:START'}) + registerDiscovery(dispatch)({ type: 'discovery:START' }) expect(mockClient.start).toHaveBeenCalledTimes(2) }) test('sets poll speed on "discovery:START" and "discovery:FINISH"', () => { const handleAction = registerDiscovery(dispatch) - handleAction({type: 'discovery:START'}) + handleAction({ type: 'discovery:START' }) expect(mockClient.setPollInterval).toHaveBeenLastCalledWith( expect.any(Number) ) - handleAction({type: 'discovery:FINISH'}) + handleAction({ type: 'discovery:FINISH' }) expect(mockClient.setPollInterval).toHaveBeenLastCalledWith( expect.any(Number) ) @@ -84,14 +84,14 @@ describe('app-shell/discovery', () => { test('always sends "discovery:UPDATE_LIST" on "discovery:START"', () => { const expected = [ - {name: 'opentrons-dev', ip: '192.168.1.42', port: 31950, ok: true}, + { name: 'opentrons-dev', ip: '192.168.1.42', port: 31950, ok: true }, ] mockClient.services = expected - registerDiscovery(dispatch)({type: 'discovery:START'}) + registerDiscovery(dispatch)({ type: 'discovery:START' }) expect(dispatch).toHaveBeenCalledWith({ type: 'discovery:UPDATE_LIST', - payload: {robots: expected}, + payload: { robots: expected }, }) }) @@ -102,7 +102,7 @@ describe('app-shell/discovery', () => { { name: 'dispatches discovery:UPDATE_LIST on "service" event', services: [ - {name: 'opentrons-dev', ip: '192.168.1.42', port: 31950, ok: true}, + { name: 'opentrons-dev', ip: '192.168.1.42', port: 31950, ok: true }, ], }, ] @@ -114,7 +114,7 @@ describe('app-shell/discovery', () => { mockClient.emit('service') expect(dispatch).toHaveBeenCalledWith({ type: 'discovery:UPDATE_LIST', - payload: {robots: spec.services}, + payload: { robots: spec.services }, }) }) ) @@ -124,43 +124,43 @@ describe('app-shell/discovery', () => { registerDiscovery(dispatch) expect(Store).toHaveBeenCalledWith({ name: 'discovery', - defaults: {services: []}, + defaults: { services: [] }, }) - mockClient.services = [{name: 'foo'}, {name: 'bar'}] + mockClient.services = [{ name: 'foo' }, { name: 'bar' }] mockClient.emit('service') expect(Store.__store.set).toHaveBeenLastCalledWith('services', [ - {name: 'foo'}, - {name: 'bar'}, + { name: 'foo' }, + { name: 'bar' }, ]) }) test('stores services to file on serviceRemoved events', () => { registerDiscovery(dispatch) - mockClient.services = [{name: 'foo'}] + mockClient.services = [{ name: 'foo' }] mockClient.emit('serviceRemoved') expect(Store.__store.set).toHaveBeenLastCalledWith('services', [ - {name: 'foo'}, + { name: 'foo' }, ]) }) test('loads services from file on client initialization', () => { Store.__store.get.mockImplementation(key => { - if (key === 'services') return [{name: 'foo'}] + if (key === 'services') return [{ name: 'foo' }] return null }) registerDiscovery(dispatch) expect(DiscoveryClient).toHaveBeenCalledWith( expect.objectContaining({ - services: [{name: 'foo'}], + services: [{ name: 'foo' }], }) ) }) test('loads candidates from config on client initialization', () => { - getConfig.mockReturnValue({enabled: true, candidates: ['1.2.3.4']}) + getConfig.mockReturnValue({ enabled: true, candidates: ['1.2.3.4'] }) registerDiscovery(dispatch) expect(DiscoveryClient).toHaveBeenCalledWith( @@ -172,7 +172,7 @@ describe('app-shell/discovery', () => { // ensures config override works with only one candidate specified test('canidates in config can be single value', () => { - getConfig.mockReturnValue({enabled: true, candidates: '1.2.3.4'}) + getConfig.mockReturnValue({ enabled: true, candidates: '1.2.3.4' }) registerDiscovery(dispatch) expect(DiscoveryClient).toHaveBeenCalledWith( @@ -183,7 +183,7 @@ describe('app-shell/discovery', () => { }) test('services from overridden canidates are not persisted', () => { - getConfig.mockReturnValue({enabled: true, candidates: 'localhost'}) + getConfig.mockReturnValue({ enabled: true, candidates: 'localhost' }) getOverrides.mockImplementation(key => { if (key === 'discovery.candidates') return ['1.2.3.4', '5.6.7.8'] return null @@ -191,13 +191,15 @@ describe('app-shell/discovery', () => { registerDiscovery(dispatch) - mockClient.services = [{name: 'foo', ip: '5.6.7.8'}, {name: 'bar'}] + mockClient.services = [{ name: 'foo', ip: '5.6.7.8' }, { name: 'bar' }] mockClient.emit('service') - expect(Store.__store.set).toHaveBeenCalledWith('services', [{name: 'bar'}]) + expect(Store.__store.set).toHaveBeenCalledWith('services', [ + { name: 'bar' }, + ]) }) test('service from overridden single candidate is not persisted', () => { - getConfig.mockReturnValue({enabled: true, candidates: 'localhost'}) + getConfig.mockReturnValue({ enabled: true, candidates: 'localhost' }) getOverrides.mockImplementation(key => { if (key === 'discovery.candidates') return '1.2.3.4' return null @@ -205,8 +207,10 @@ describe('app-shell/discovery', () => { registerDiscovery(dispatch) - mockClient.services = [{name: 'foo', ip: '1.2.3.4'}, {name: 'bar'}] + mockClient.services = [{ name: 'foo', ip: '1.2.3.4' }, { name: 'bar' }] mockClient.emit('service') - expect(Store.__store.set).toHaveBeenCalledWith('services', [{name: 'bar'}]) + expect(Store.__store.set).toHaveBeenCalledWith('services', [ + { name: 'bar' }, + ]) }) }) diff --git a/app-shell/src/__tests__/update.test.js b/app-shell/src/__tests__/update.test.js index c7b1fb32a7b..3071933b2ad 100644 --- a/app-shell/src/__tests__/update.test.js +++ b/app-shell/src/__tests__/update.test.js @@ -1,7 +1,10 @@ // app-shell self-update tests -import {autoUpdater, __mockReset as __updaterMockReset} from 'electron-updater' -import {registerUpdate} from '../update' -import {getConfig} from '../config' +import { + autoUpdater, + __mockReset as __updaterMockReset, +} from 'electron-updater' +import { registerUpdate } from '../update' +import { getConfig } from '../config' jest.mock('electron-updater') jest.mock('../log') @@ -23,46 +26,46 @@ describe('update', () => { test('handles shell:CHECK_UPDATE with available update', () => { getConfig.mockReturnValue('dev') - handleAction({type: 'shell:CHECK_UPDATE'}) + handleAction({ type: 'shell:CHECK_UPDATE' }) expect(getConfig).toHaveBeenCalledWith('update.channel') expect(autoUpdater.channel).toEqual('dev') expect(autoUpdater.checkForUpdates).toHaveBeenCalledTimes(1) - autoUpdater.emit('update-available', {version: '1.0.0'}) + autoUpdater.emit('update-available', { version: '1.0.0' }) expect(dispatch).toHaveBeenCalledWith({ type: 'shell:CHECK_UPDATE_RESULT', - payload: {available: true, info: {version: '1.0.0'}}, + payload: { available: true, info: { version: '1.0.0' } }, }) }) test('handles shell:CHECK_UPDATE with no available update', () => { - handleAction({type: 'shell:CHECK_UPDATE'}) - autoUpdater.emit('update-not-available', {version: '1.0.0'}) + handleAction({ type: 'shell:CHECK_UPDATE' }) + autoUpdater.emit('update-not-available', { version: '1.0.0' }) expect(dispatch).toHaveBeenCalledWith({ type: 'shell:CHECK_UPDATE_RESULT', - payload: {available: false, info: {version: '1.0.0'}}, + payload: { available: false, info: { version: '1.0.0' } }, }) }) test('handles shell:CHECK_UPDATE with error', () => { - handleAction({type: 'shell:CHECK_UPDATE'}) + handleAction({ type: 'shell:CHECK_UPDATE' }) autoUpdater.emit('error', new Error('AH')) expect(dispatch).toHaveBeenCalledWith({ type: 'shell:CHECK_UPDATE_RESULT', - payload: {error: {name: 'Error', message: 'AH'}}, + payload: { error: { name: 'Error', message: 'AH' } }, }) }) test('handles shell:DOWNLOAD_UPDATE', () => { - handleAction({type: 'shell:DOWNLOAD_UPDATE'}) + handleAction({ type: 'shell:DOWNLOAD_UPDATE' }) expect(autoUpdater.downloadUpdate).toHaveBeenCalledTimes(1) - autoUpdater.emit('update-downloaded', {version: '1.0.0'}) + autoUpdater.emit('update-downloaded', { version: '1.0.0' }) expect(dispatch).toHaveBeenCalledWith({ type: 'shell:DOWNLOAD_UPDATE_RESULT', @@ -71,17 +74,17 @@ describe('update', () => { }) test('handles shell:DOWNLOAD_UPDATE with error', () => { - handleAction({type: 'shell:DOWNLOAD_UPDATE'}) + handleAction({ type: 'shell:DOWNLOAD_UPDATE' }) autoUpdater.emit('error', new Error('AH')) expect(dispatch).toHaveBeenCalledWith({ type: 'shell:DOWNLOAD_UPDATE_RESULT', - payload: {error: {name: 'Error', message: 'AH'}}, + payload: { error: { name: 'Error', message: 'AH' } }, }) }) test('handles shell:APPLY_UPDATE', () => { - handleAction({type: 'shell:APPLY_UPDATE'}) + handleAction({ type: 'shell:APPLY_UPDATE' }) expect(autoUpdater.quitAndInstall).toHaveBeenCalledTimes(1) }) }) diff --git a/app-shell/src/api-update.js b/app-shell/src/api-update.js index 499ead3f598..e7c9100137a 100644 --- a/app-shell/src/api-update.js +++ b/app-shell/src/api-update.js @@ -8,40 +8,40 @@ import semver from 'semver' import pkg from '../package.json' import createLogger from './log' -import type {ApiUpdateInfo} from '@opentrons/app/src/shell' +import type { ApiUpdateInfo } from '@opentrons/app/src/shell' const log = createLogger(__filename) const DIRECTORY = path.join(__dirname, '../../api/dist') const EXTENSION = '.whl' const RE_VERSION = /.+-(\d+\.\d+\.\d+)([ab])?(\d+)?-.+/ -const SHORT_LABEL_TO_LABEL = {a: 'alpha', b: 'beta'} +const SHORT_LABEL_TO_LABEL = { a: 'alpha', b: 'beta' } let updateFilename: string let updateVersion: string export const AVAILABLE_UPDATE = pkg.version -export function registerApiUpdate () { +export function registerApiUpdate() { initializeUpdateFileInfo() // API update does not need to handle actions at the moment return () => {} } -export function getUpdateInfo (): ApiUpdateInfo { +export function getUpdateInfo(): ApiUpdateInfo { assert(updateFilename && updateVersion, 'Update info was not initialized') - return {filename: path.basename(updateFilename), version: updateVersion} + return { filename: path.basename(updateFilename), version: updateVersion } } -export function getUpdateFileContents (): Promise { +export function getUpdateFileContents(): Promise { assert(updateFilename && updateVersion, 'Update info was not initialized') return fse.readFile(updateFilename) } -function initializeUpdateFileInfo () { +function initializeUpdateFileInfo() { try { const results = fse.readdirSync(DIRECTORY) const files = results.filter(f => f.endsWith(EXTENSION)) @@ -51,17 +51,17 @@ function initializeUpdateFileInfo () { `Expected 1 ${EXTENSION} file in ${DIRECTORY}, found ${results.join(' ')}` ) - log.info(`Found update file`, {filename: files[0]}) + log.info(`Found update file`, { filename: files[0] }) updateFilename = path.join(DIRECTORY, files[0]) updateVersion = getVersionFromFilename(updateFilename) } catch (error) { - log.error('Unable to load API update files', {error}) + log.error('Unable to load API update files', { error }) } return null } -function getVersionFromFilename (filename: string): string { +function getVersionFromFilename(filename: string): string { const match = filename.toLowerCase().match(RE_VERSION) assert(match, `could not parse version from "${filename}"`) diff --git a/app-shell/src/config.js b/app-shell/src/config.js index d24dcc3c452..aef4df1f306 100644 --- a/app-shell/src/config.js +++ b/app-shell/src/config.js @@ -2,7 +2,7 @@ // app configuration and settings import Store from 'electron-store' import mergeOptions from 'merge-options' -import {getIn} from '@thi.ng/paths' +import { getIn } from '@thi.ng/paths' import uuid from 'uuid/v4' import yargsParser from 'yargs-parser' @@ -10,8 +10,8 @@ import pkg from '../package.json' import createLogger from './log' // TODO(mc, 2018-08-08): figure out type exports from app -import type {Action} from '@opentrons/app/src/types' -import type {Config} from '@opentrons/app/src/config' +import type { Action } from '@opentrons/app/src/types' +import type { Config } from '@opentrons/app/src/config' // make sure all arguments are included in production // $FlowFixMe: process.defaultApp exists in electron @@ -82,15 +82,15 @@ const DEFAULTS: Config = { let _store let _over let _log -const store = () => _store || (_store = new Store({defaults: DEFAULTS})) +const store = () => _store || (_store = new Store({ defaults: DEFAULTS })) const overrides = () => _over || (_over = yargsParser(argv, PARSE_ARGS_OPTS)) const log = () => _log || (_log = createLogger(__filename)) // initialize and register the config module with dispatches from the UI -export function registerConfig (dispatch: Action => void) { - return function handleIncomingAction (action: Action) { +export function registerConfig(dispatch: Action => void) { + return function handleIncomingAction(action: Action) { if (action.type === 'config:UPDATE') { - const {payload} = action + const { payload } = action log().debug('Handling config:UPDATE', payload) @@ -99,21 +99,21 @@ export function registerConfig (dispatch: Action => void) { } else { log().info(`Updating "${payload.path}" to ${payload.value}`) store().set(payload.path, payload.value) - dispatch({type: 'config:SET', payload}) + dispatch({ type: 'config:SET', payload }) } } } } -export function getStore () { +export function getStore() { return store().store } -export function getOverrides (path?: string) { +export function getOverrides(path?: string) { return getIn(overrides(), path) } -export function getConfig (path?: string) { +export function getConfig(path?: string) { const result = store().get(path) const over = getIn(overrides(), path) @@ -128,7 +128,7 @@ export function getConfig (path?: string) { return result } -export function handleConfigChange ( +export function handleConfigChange( path: string, changeHandler: (newValue: any, oldValue: any) => mixed ) { diff --git a/app-shell/src/discovery.js b/app-shell/src/discovery.js index 104f7a3cb70..5e6b2d04942 100644 --- a/app-shell/src/discovery.js +++ b/app-shell/src/discovery.js @@ -8,13 +8,13 @@ import DiscoveryClient, { SERVICE_REMOVED_EVENT, } from '@opentrons/discovery-client' -import {getConfig, getOverrides, handleConfigChange} from './config' +import { getConfig, getOverrides, handleConfigChange } from './config' import createLogger from './log' -import type {Service} from '@opentrons/discovery-client' +import type { Service } from '@opentrons/discovery-client' // TODO(mc, 2018-08-08): figure out type exports from app -import type {Action} from '@opentrons/app/src/types' +import type { Action } from '@opentrons/app/src/types' const log = createLogger(__filename) @@ -27,11 +27,11 @@ let config let store let client -export function registerDiscovery (dispatch: Action => void) { +export function registerDiscovery(dispatch: Action => void) { const onServiceUpdate = throttle(handleServices, UPDATE_THROTTLE_MS) config = getConfig('discovery') - store = new Store({name: 'discovery', defaults: {services: []}}) + store = new Store({ name: 'discovery', defaults: { services: [] } }) client = DiscoveryClient({ pollInterval: SLOW_POLL_INTERVAL_MS, @@ -43,15 +43,15 @@ export function registerDiscovery (dispatch: Action => void) { client .on(SERVICE_EVENT, onServiceUpdate) .on(SERVICE_REMOVED_EVENT, onServiceUpdate) - .on('error', error => log.error('discovery error', {error})) + .on('error', error => log.error('discovery error', { error })) .start() handleConfigChange('discovery.candidates', value => client.setCandidates(['[fd00:0:cafe:fefe::1]'].concat(value)) ) - return function handleIncomingAction (action: Action) { - log.debug('handling action in discovery', {action}) + return function handleIncomingAction(action: Action) { + log.debug('handling action in discovery', { action }) switch (action.type) { case 'discovery:START': @@ -62,22 +62,22 @@ export function registerDiscovery (dispatch: Action => void) { } } - function handleServices () { + function handleServices() { store.set('services', filterServicesToPersist(client.services)) dispatch({ type: 'discovery:UPDATE_LIST', - payload: {robots: client.services}, + payload: { robots: client.services }, }) } } -export function getRobots () { +export function getRobots() { if (!client) return [] return client.services } -function filterServicesToPersist (services: Array) { +function filterServicesToPersist(services: Array) { const candidateOverrides = getOverrides('discovery.candidates') if (!candidateOverrides) return client.services diff --git a/app-shell/src/log.js b/app-shell/src/log.js index 5e32082c28c..d262dd0a691 100644 --- a/app-shell/src/log.js +++ b/app-shell/src/log.js @@ -1,12 +1,12 @@ // create logger function -import {app} from 'electron' -import {inspect} from 'util' +import { app } from 'electron' +import { inspect } from 'util' import fse from 'fs-extra' import path from 'path' import dateFormat from 'dateformat' import winston from 'winston' -import {getConfig} from './config' +import { getConfig } from './config' const LOG_DIR = path.join(app.getPath('userData'), 'logs') const ERROR_LOG = path.join(LOG_DIR, 'error.log') @@ -26,14 +26,14 @@ let config let transports let log -export default function initializeLogger (filename) { +export default function initializeLogger(filename) { if (!config) config = getConfig('log') if (!transports) initializeTransports() return createLogger(filename) } -function initializeTransports () { +function initializeTransports() { let error = null // sync is ok here because this only happens once @@ -46,37 +46,47 @@ function initializeTransports () { transports = createTransports() log = createLogger(__filename) - if (error) log.error('Could not create log directory', {error}) + if (error) log.error('Could not create log directory', { error }) log.info(`Level "error" and higher logging to ${ERROR_LOG}`) log.info(`Level "${config.level.file}" and higher logging to ${COMBINED_LOG}`) log.info(`Level "${config.level.console}" and higher logging to console`) } -function createTransports () { +function createTransports() { const timeFromStamp = ts => dateFormat(new Date(ts), 'HH:MM:ss.l') return [ // error file log - new winston.transports.File(Object.assign({ - level: 'error', - filename: ERROR_LOG, - }, FILE_OPTIONS)), + new winston.transports.File( + Object.assign( + { + level: 'error', + filename: ERROR_LOG, + }, + FILE_OPTIONS + ) + ), // regular combined file log - new winston.transports.File(Object.assign({ - level: config.level.file, - filename: COMBINED_LOG, - }, FILE_OPTIONS)), + new winston.transports.File( + Object.assign( + { + level: config.level.file, + filename: COMBINED_LOG, + }, + FILE_OPTIONS + ) + ), // console log new winston.transports.Console({ level: config.level.console, format: winston.format.combine( winston.format.printf(info => { - const {level, message, timestamp, label} = info + const { level, message, timestamp, label } = info const time = timeFromStamp(timestamp) const print = `${time} [${label}] ${level}: ${message}` - const meta = inspect(info.meta, {depth: 6}) + const meta = inspect(info.meta, { depth: 6 }) if (meta !== '{}') return `${print} ${meta}` @@ -87,7 +97,7 @@ function createTransports () { ] } -function createLogger (filename) { +function createLogger(filename) { log && log.debug(`Creating logger for ${filename}`) const formats = [ @@ -99,12 +109,9 @@ function createLogger (filename) { ] if (filename) { - const label = path.relative( - path.join(__dirname, '../..'), - filename - ) + const label = path.relative(path.join(__dirname, '../..'), filename) - formats.unshift(winston.format.label({label})) + formats.unshift(winston.format.label({ label })) } return winston.createLogger({ diff --git a/app-shell/src/main.js b/app-shell/src/main.js index 73dbd70d291..b71df1bb745 100644 --- a/app-shell/src/main.js +++ b/app-shell/src/main.js @@ -1,14 +1,14 @@ // electron main entry point -import {app, dialog, ipcMain, Menu} from 'electron' +import { app, dialog, ipcMain, Menu } from 'electron' import createUi from './ui' import initializeMenu from './menu' import createLogger from './log' -import {getConfig, getStore, getOverrides, registerConfig} from './config' -import {registerApiUpdate} from './api-update' -import {registerDiscovery} from './discovery' -import {registerRobotLogs} from './robot-logs' -import {registerUpdate} from './update' +import { getConfig, getStore, getOverrides, registerConfig } from './config' +import { registerApiUpdate } from './api-update' +import { registerDiscovery } from './discovery' +import { registerRobotLogs } from './robot-logs' +import { registerUpdate } from './update' const config = getConfig() const log = createLogger(__filename) @@ -20,7 +20,7 @@ log.debug('App config', { }) if (config.devtools) { - require('electron-debug')({enabled: true, showDevTools: true}) + require('electron-debug')({ enabled: true, showDevTools: true }) } // hold on to references so they don't get garbage collected @@ -29,9 +29,9 @@ let rendererLogger app.on('ready', startUp) -function startUp () { +function startUp() { log.info('Starting App') - process.on('uncaughtException', error => log.error('Uncaught: ', {error})) + process.on('uncaughtException', error => log.error('Uncaught: ', { error })) mainWindow = createUi() rendererLogger = createRendererLogger() @@ -40,7 +40,7 @@ function startUp () { // wire modules to UI dispatches const dispatch = action => { - log.debug('Sending action via IPC to renderer', {action}) + log.debug('Sending action via IPC to renderer', { action }) mainWindow.webContents.send('dispatch', action) } @@ -51,7 +51,7 @@ function startUp () { const updateHandler = registerUpdate(dispatch) ipcMain.on('dispatch', (_, action) => { - log.debug('Received action via IPC from renderer', {action}) + log.debug('Received action via IPC from renderer', { action }) apiUpdateHandler(action) configHandler(action) discoveryHandler(action) @@ -65,10 +65,10 @@ function startUp () { ) } - log.silly('Global references', {mainWindow, rendererLogger}) + log.silly('Global references', { mainWindow, rendererLogger }) } -function createRendererLogger () { +function createRendererLogger() { log.info('Creating renderer logger') const logger = createLogger() @@ -77,7 +77,7 @@ function createRendererLogger () { return logger } -function installAndOpenExtensions () { +function installAndOpenExtensions() { const devtools = require('electron-devtools-installer') const forceDownload = !!process.env.UPGRADE_EXTENSIONS const install = devtools.default @@ -87,7 +87,7 @@ function installAndOpenExtensions () { extensions.map(name => install(devtools[name], forceDownload)) ).then(() => mainWindow.webContents.on('context-menu', (_, props) => { - const {x, y} = props + const { x, y } = props Menu.buildFromTemplate([ { diff --git a/app-shell/src/menu.js b/app-shell/src/menu.js index 8d3ecb261b4..099f096efae 100644 --- a/app-shell/src/menu.js +++ b/app-shell/src/menu.js @@ -1,17 +1,15 @@ // application menu -import {app, Menu} from 'electron' +import { app, Menu } from 'electron' import pkg from '../package.json' -import {getConfig} from './config' +import { getConfig } from './config' const config = getConfig() // file or application menu const firstMenu = { label: 'File', - submenu: [ - {role: 'quit'}, - ], + submenu: [{ role: 'quit' }], } if (process.platform === 'darwin') { @@ -19,14 +17,14 @@ if (process.platform === 'darwin') { Object.assign(firstMenu, { label: app.getName(), submenu: [ - {role: 'about'}, - {type: 'separator'}, - {role: 'services', submenu: []}, - {type: 'separator'}, - {role: 'hide'}, - {role: 'hideothers'}, - {role: 'unhide'}, - {type: 'separator'}, + { role: 'about' }, + { type: 'separator' }, + { role: 'services', submenu: [] }, + { type: 'separator' }, + { role: 'hide' }, + { role: 'hideothers' }, + { role: 'unhide' }, + { type: 'separator' }, ...firstMenu.submenu, ], }) @@ -35,26 +33,24 @@ if (process.platform === 'darwin') { const editMenu = { label: 'Edit', submenu: [ - {role: 'cut'}, - {role: 'copy'}, - {role: 'paste'}, - {role: 'selectall'}, + { role: 'cut' }, + { role: 'copy' }, + { role: 'paste' }, + { role: 'selectall' }, ], } const viewMenu = { label: 'View', - submenu: [ - {role: 'togglefullscreen'}, - ], + submenu: [{ role: 'togglefullscreen' }], } if (config.devtools) { Object.assign(viewMenu, { submenu: [ - {role: 'reload'}, - {role: 'forcereload'}, - {type: 'separator'}, + { role: 'reload' }, + { role: 'forcereload' }, + { type: 'separator' }, ...viewMenu.submenu, ], }) @@ -62,11 +58,7 @@ if (config.devtools) { const windowMenu = { role: 'window', - submenu: [ - {role: 'minimize'}, - {type: 'separator'}, - {role: 'front'}, - ], + submenu: [{ role: 'minimize' }, { type: 'separator' }, { role: 'front' }], } const helpMenu = { @@ -89,6 +81,6 @@ const helpMenu = { const template = [firstMenu, editMenu, viewMenu, windowMenu, helpMenu] -export default function initializeMenu () { +export default function initializeMenu() { Menu.setApplicationMenu(Menu.buildFromTemplate(template)) } diff --git a/app-shell/src/preload.js b/app-shell/src/preload.js index c1507b393a6..b65571b0a1c 100644 --- a/app-shell/src/preload.js +++ b/app-shell/src/preload.js @@ -2,7 +2,7 @@ // preload script for renderer process // defines subset of Electron API that renderer process is allowed to access // for security reasons -import {ipcRenderer, remote} from 'electron' +import { ipcRenderer, remote } from 'electron' global.APP_SHELL = { ipcRenderer, diff --git a/app-shell/src/robot-logs.js b/app-shell/src/robot-logs.js index 01a34a95fe0..3db0aef8da5 100644 --- a/app-shell/src/robot-logs.js +++ b/app-shell/src/robot-logs.js @@ -1,9 +1,11 @@ // download robot logs manager -export function registerRobotLogs (dispatch, mainWindow) { - return function handleIncomingAction (action) { +export function registerRobotLogs(dispatch, mainWindow) { + return function handleIncomingAction(action) { if (action.type === 'shell:DOWNLOAD_LOGS') { - const {payload: {logUrls}} = action + const { + payload: { logUrls }, + } = action logUrls.forEach(url => mainWindow.webContents.downloadURL(url)) } } diff --git a/app-shell/src/ui.js b/app-shell/src/ui.js index 03457073efe..330f189b05e 100644 --- a/app-shell/src/ui.js +++ b/app-shell/src/ui.js @@ -1,15 +1,16 @@ // sets up the main window ui -import {app, shell, BrowserWindow} from 'electron' +import { app, shell, BrowserWindow } from 'electron' import path from 'path' -import {getConfig} from './config' +import { getConfig } from './config' import createLogger from './log' const config = getConfig('ui') const log = createLogger(__filename) -const urlPath = config.url.protocol === 'file:' - ? path.join(app.getAppPath(), config.url.path) - : config.url.path +const urlPath = + config.url.protocol === 'file:' + ? path.join(app.getAppPath(), config.url.path) + : config.url.path const url = `${config.url.protocol}//${urlPath}` @@ -19,27 +20,32 @@ const WINDOW_OPTS = { width: config.width, height: config.height, // allow webPreferences to be set at launchtime from config - webPreferences: Object.assign({ - preload: path.join(__dirname, './preload.js'), - nodeIntegration: false, - }, config.webPreferences), + webPreferences: Object.assign( + { + preload: path.join(__dirname, './preload.js'), + nodeIntegration: false, + }, + config.webPreferences + ), } -export default function createUi () { - log.debug('Creating main window', {options: WINDOW_OPTS}) +export default function createUi() { + log.debug('Creating main window', { options: WINDOW_OPTS }) - const mainWindow = new BrowserWindow(WINDOW_OPTS) - .once('ready-to-show', () => { + const mainWindow = new BrowserWindow(WINDOW_OPTS).once( + 'ready-to-show', + () => { log.debug('Main window ready to show') mainWindow.show() - }) + } + ) log.info(`Loading ${url}`) - mainWindow.loadURL(url, {'extraHeaders': 'pragma: no-cache\n'}) + mainWindow.loadURL(url, { extraHeaders: 'pragma: no-cache\n' }) // open new windows ( { - log.debug('Opening external link', {url}) + log.debug('Opening external link', { url }) event.preventDefault() shell.openExternal(url) }) diff --git a/app-shell/src/update.js b/app-shell/src/update.js index c3815f39ec1..d6afdcf413f 100644 --- a/app-shell/src/update.js +++ b/app-shell/src/update.js @@ -2,14 +2,14 @@ // app updater import path from 'path' import fs from 'fs' -import {autoUpdater as updater} from 'electron-updater' +import { autoUpdater as updater } from 'electron-updater' import createLogger from './log' -import {getConfig} from './config' +import { getConfig } from './config' // TODO(mc, 2018-08-08): figure out type exports from app -import type {Action, Error as PlainError} from '@opentrons/app/src/types' -import type {UpdateInfo} from '@opentrons/app/src/shell' +import type { Action, Error as PlainError } from '@opentrons/app/src/types' +import type { UpdateInfo } from '@opentrons/app/src/shell' type Dispatch = Action => void @@ -22,8 +22,8 @@ export const CURRENT_RELEASE_NOTES = fs.readFileSync( 'utf8' ) -export function registerUpdate (dispatch: Dispatch) { - return function handleAction (action: Action) { +export function registerUpdate(dispatch: Dispatch) { + return function handleAction(action: Action) { switch (action.type) { case 'shell:CHECK_UPDATE': return checkUpdate(dispatch) @@ -37,10 +37,10 @@ export function registerUpdate (dispatch: Dispatch) { } } -function checkUpdate (dispatch: Dispatch) { - const onAvailable = (info: UpdateInfo) => done({info, available: true}) - const onNotAvailable = (info: UpdateInfo) => done({info, available: false}) - const onError = (error: Error) => done({error: PlainObjectError(error)}) +function checkUpdate(dispatch: Dispatch) { + const onAvailable = (info: UpdateInfo) => done({ info, available: true }) + const onNotAvailable = (info: UpdateInfo) => done({ info, available: false }) + const onError = (error: Error) => done({ error: PlainObjectError(error) }) updater.once('update-available', onAvailable) updater.once('update-not-available', onNotAvailable) @@ -49,31 +49,31 @@ function checkUpdate (dispatch: Dispatch) { updater.channel = getConfig('update.channel') updater.checkForUpdates() - function done (payload: *) { + function done(payload: *) { updater.removeListener('update-available', onAvailable) updater.removeListener('update-not-available', onNotAvailable) updater.removeListener('error', onError) - dispatch({type: 'shell:CHECK_UPDATE_RESULT', payload}) + dispatch({ type: 'shell:CHECK_UPDATE_RESULT', payload }) } } -function downloadUpdate (dispatch: Dispatch) { +function downloadUpdate(dispatch: Dispatch) { const onDownloaded = () => done({}) - const onError = (error: Error) => done({error: PlainObjectError(error)}) + const onError = (error: Error) => done({ error: PlainObjectError(error) }) updater.once('update-downloaded', onDownloaded) updater.once('error', onError) updater.downloadUpdate() - function done (payload: *) { + function done(payload: *) { updater.removeListener('update-downloaded', onDownloaded) updater.removeListener('error', onError) - dispatch({type: 'shell:DOWNLOAD_UPDATE_RESULT', payload}) + dispatch({ type: 'shell:DOWNLOAD_UPDATE_RESULT', payload }) } } // TODO(mc, 2018-03-29): this only exists to support RPC in a webworker; // remove when RPC is gone -function PlainObjectError (error: Error): PlainError { - return {name: error.name, message: error.message} +function PlainObjectError(error: Error): PlainError { + return { name: error.name, message: error.message } } diff --git a/app/README.md b/app/README.md index 235a8426619..bb7a358d5f1 100644 --- a/app/README.md +++ b/app/README.md @@ -41,25 +41,25 @@ At this point, the Electron app will be running with [HMR][] and various Chrome The UI stack is built using: -* [React][] -* [Redux][] -* [CSS modules][css-modules] -* [Babel][] -* [Webpack][] +- [React][] +- [Redux][] +- [CSS modules][css-modules] +- [Babel][] +- [Webpack][] Some important directories: -* `app/src` — Client-side React app run in Electron's [renderer process][electron-renderer] -* API clients (see [`api/opentrons/server`][api-server-source]) - * `app/src/rpc` - RPC API client - * `app/src/http-api-client` - HTTP API client -* `app/webpack` - Webpack configuration helpers +- `app/src` — Client-side React app run in Electron's [renderer process][electron-renderer] +- API clients (see [`api/opentrons/server`][api-server-source]) + - `app/src/rpc` - RPC API client + - `app/src/http-api-client` - HTTP API client +- `app/webpack` - Webpack configuration helpers ## testing To run tests: -* `make test` - Run all tests +- `make test` - Run all tests Test tasks can also be run with the following arguments: diff --git a/app/__util__/mock-promise.js b/app/__util__/mock-promise.js index b545c8c0224..afb9cbedcea 100755 --- a/app/__util__/mock-promise.js +++ b/app/__util__/mock-promise.js @@ -1,26 +1,38 @@ // test utility functions // TODO(mc, 2018-05-12): upgrade jest to get builtin mock resolve and reject -export function mockResolvedValue (mock, value) { - mock.mockImplementation(() => new Promise((resolve) => { - process.nextTick(() => resolve(value)) - })) +export function mockResolvedValue(mock, value) { + mock.mockImplementation( + () => + new Promise(resolve => { + process.nextTick(() => resolve(value)) + }) + ) } -export function mockRejectedValue (mock, value) { - mock.mockImplementation(() => new Promise((resolve, reject) => { - process.nextTick(() => reject(value)) - })) +export function mockRejectedValue(mock, value) { + mock.mockImplementation( + () => + new Promise((resolve, reject) => { + process.nextTick(() => reject(value)) + }) + ) } -export function mockResolvedValueOnce (mock, value) { - mock.mockImplementationOnce(() => new Promise((resolve) => { - process.nextTick(() => resolve(value)) - })) +export function mockResolvedValueOnce(mock, value) { + mock.mockImplementationOnce( + () => + new Promise(resolve => { + process.nextTick(() => resolve(value)) + }) + ) } -export function mockRejectedValueOnce (mock, value) { - mock.mockImplementationOnce(() => new Promise((resolve, reject) => { - process.nextTick(() => reject(value)) - })) +export function mockRejectedValueOnce(mock, value) { + mock.mockImplementationOnce( + () => + new Promise((resolve, reject) => { + process.nextTick(() => reject(value)) + }) + ) } diff --git a/app/src/__mocks__/logger.js b/app/src/__mocks__/logger.js index af8f835bdad..70a0e202fb1 100644 --- a/app/src/__mocks__/logger.js +++ b/app/src/__mocks__/logger.js @@ -1,13 +1,16 @@ // mock logger for tests import path from 'path' -export default function createLogger (filename) { +export default function createLogger(filename) { const label = path.relative(path.join(__dirname, '../../..'), filename) - return new Proxy({}, { - get (_, level) { - return (message, meta) => - console.log(`[${label}] ${level}: ${message} %j`, meta) - }, - }) + return new Proxy( + {}, + { + get(_, level) { + return (message, meta) => + console.log(`[${label}] ${level}: ${message} %j`, meta) + }, + } + ) } diff --git a/app/src/__tests__/util.test.js b/app/src/__tests__/util.test.js index 16509493c14..4b071e15db1 100644 --- a/app/src/__tests__/util.test.js +++ b/app/src/__tests__/util.test.js @@ -2,7 +2,7 @@ import configureMockStore from 'redux-mock-store' import thunk from 'redux-thunk' -import {chainActions} from '../util' +import { chainActions } from '../util' jest.mock('../logger') @@ -19,160 +19,124 @@ describe('chainActions utility', () => { }) test('dispatches a chain of plain actions', () => { - const actions = [ - {type: 'foo'}, - {type: 'bar'}, - {type: 'baz'}, - ] + const actions = [{ type: 'foo' }, { type: 'bar' }, { type: 'baz' }] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(actions[2]) - expect(store.getActions()).toEqual(actions) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(actions[2]) + expect(store.getActions()).toEqual(actions) + }) }) test('dispatches a chain of thunk actions', () => { - const actions = [ - {type: 'foo'}, - {type: 'bar'}, - {type: 'baz'}, - ] + const actions = [{ type: 'foo' }, { type: 'bar' }, { type: 'baz' }] const thunks = actions.map(a => dispatch => dispatch(a)) - return store.dispatch(chainActions(...thunks)) - .then(result => { - expect(result).toEqual(actions[2]) - expect(store.getActions()).toEqual(actions) - }) + return store.dispatch(chainActions(...thunks)).then(result => { + expect(result).toEqual(actions[2]) + expect(store.getActions()).toEqual(actions) + }) }) test('dispatches a chain of thunk promise actions', () => { - const actions = [ - {type: 'foo'}, - {type: 'bar'}, - {type: 'baz'}, - ] + const actions = [{ type: 'foo' }, { type: 'bar' }, { type: 'baz' }] const thunks = actions.map(a => dispatch => Promise.resolve(dispatch(a))) - return store.dispatch(chainActions(...thunks)) - .then(result => { - expect(result).toEqual(actions[2]) - expect(store.getActions()).toEqual(actions) - }) + return store.dispatch(chainActions(...thunks)).then(result => { + expect(result).toEqual(actions[2]) + expect(store.getActions()).toEqual(actions) + }) }) test('dispatches a combination of action types', () => { - const actions = [ - {type: 'foo'}, - {type: 'bar'}, - {type: 'baz'}, - ] + const actions = [{ type: 'foo' }, { type: 'bar' }, { type: 'baz' }] const thunks = [ - (dispatch) => dispatch(actions[0]), - (dispatch) => Promise.resolve(dispatch(actions[1])), + dispatch => dispatch(actions[0]), + dispatch => Promise.resolve(dispatch(actions[1])), actions[2], ] - return store.dispatch(chainActions(...thunks)) - .then(result => { - expect(result).toEqual(actions[2]) - expect(store.getActions()).toEqual(actions) - }) + return store.dispatch(chainActions(...thunks)).then(result => { + expect(result).toEqual(actions[2]) + expect(store.getActions()).toEqual(actions) + }) }) test('bails out early if a plain action has an error', () => { - const actions = [ - {type: 'foo', error: new Error('AH')}, - {type: 'bar'}, - ] + const actions = [{ type: 'foo', error: new Error('AH') }, { type: 'bar' }] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(actions[0]) - expect(store.getActions()).toEqual(actions.slice(0, 1)) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(actions[0]) + expect(store.getActions()).toEqual(actions.slice(0, 1)) + }) }) test('bails out early if a thunk action has an error', () => { - const errorAction = {type: 'foo', error: new Error('AH')} - const actions = [ - (dispatch) => dispatch(errorAction), - {type: 'bar'}, - ] + const errorAction = { type: 'foo', error: new Error('AH') } + const actions = [dispatch => dispatch(errorAction), { type: 'bar' }] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(errorAction) - expect(store.getActions()).toEqual([errorAction]) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(errorAction) + expect(store.getActions()).toEqual([errorAction]) + }) }) test('bails out early if a thunk promise has an error', () => { - const errorAction = {type: 'foo', error: new Error('AH')} + const errorAction = { type: 'foo', error: new Error('AH') } const actions = [ - (dispatch) => Promise.resolve(dispatch(errorAction)), - {type: 'bar'}, + dispatch => Promise.resolve(dispatch(errorAction)), + { type: 'bar' }, ] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(errorAction) - expect(store.getActions()).toEqual([errorAction]) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(errorAction) + expect(store.getActions()).toEqual([errorAction]) + }) }) test('bails out early if a plain action has an error in the payload', () => { const actions = [ - {type: 'foo', payload: {error: new Error('AH')}}, - {type: 'bar'}, + { type: 'foo', payload: { error: new Error('AH') } }, + { type: 'bar' }, ] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(actions[0]) - expect(store.getActions()).toEqual(actions.slice(0, 1)) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(actions[0]) + expect(store.getActions()).toEqual(actions.slice(0, 1)) + }) }) test('bails out early if a thunk action has an error in the payload', () => { - const errorAction = {type: 'foo', payload: {error: new Error('AH')}} - const actions = [ - (dispatch) => dispatch(errorAction), - {type: 'bar'}, - ] + const errorAction = { type: 'foo', payload: { error: new Error('AH') } } + const actions = [dispatch => dispatch(errorAction), { type: 'bar' }] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(errorAction) - expect(store.getActions()).toEqual([errorAction]) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(errorAction) + expect(store.getActions()).toEqual([errorAction]) + }) }) test('bails out early if a thunk promise has an error in the payload', () => { - const errorAction = {type: 'foo', payload: {error: new Error('AH')}} + const errorAction = { type: 'foo', payload: { error: new Error('AH') } } const actions = [ - (dispatch) => Promise.resolve(dispatch(errorAction)), - {type: 'bar'}, + dispatch => Promise.resolve(dispatch(errorAction)), + { type: 'bar' }, ] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(errorAction) - expect(store.getActions()).toEqual([errorAction]) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(errorAction) + expect(store.getActions()).toEqual([errorAction]) + }) }) test('bails out early if a thunk promise rejects', () => { const actions = [ - (dispatch) => Promise.reject(new Error('AH')), - {type: 'bar'}, + dispatch => Promise.reject(new Error('AH')), + { type: 'bar' }, ] - return store.dispatch(chainActions(...actions)) - .then(result => { - expect(result).toEqual(null) - expect(store.getActions()).toEqual([]) - }) + return store.dispatch(chainActions(...actions)).then(result => { + expect(result).toEqual(null) + expect(store.getActions()).toEqual([]) + }) }) }) diff --git a/app/src/analytics/README.md b/app/src/analytics/README.md index adbee164e12..8be44a679fe 100644 --- a/app/src/analytics/README.md +++ b/app/src/analytics/README.md @@ -7,12 +7,12 @@ Utilities and middleware to send redux actions to mixpanel and Intercom. The following should be present in the application entry point: ```js -import {analyticsMiddleware} from './path/to/analytics' +import { analyticsMiddleware } from './path/to/analytics' // add the middleware to the store const middleware = applyMiddleware( // ... - analyticsMiddleware, + analyticsMiddleware // ... ) @@ -33,7 +33,7 @@ export default function makeEvent( switch (action.type) { // ... case 'some-action-type': - return {name: 'some-event-name', properties: {foo: 'bar'}} + return { name: 'some-event-name', properties: { foo: 'bar' } } // ... } diff --git a/app/src/analytics/__tests__/make-event.test.js b/app/src/analytics/__tests__/make-event.test.js index b3193b711d0..6ac9476f76c 100644 --- a/app/src/analytics/__tests__/make-event.test.js +++ b/app/src/analytics/__tests__/make-event.test.js @@ -1,6 +1,6 @@ // events map tests import makeEvent from '../make-event' -import {actions as robotActions} from '../../robot' +import { actions as robotActions } from '../../robot' import * as selectors from '../selectors' jest.mock('../selectors') @@ -52,27 +52,27 @@ describe('analytics events map', () => { expect(makeEvent(success, state('wired'))).toEqual({ name: 'robotConnect', - properties: {method: 'usb', success: true, error: ''}, + properties: { method: 'usb', success: true, error: '' }, }) expect(makeEvent(failure, state('wired'))).toEqual({ name: 'robotConnect', - properties: {method: 'usb', success: false, error: 'AH'}, + properties: { method: 'usb', success: false, error: 'AH' }, }) expect(makeEvent(success, state('wireless'))).toEqual({ name: 'robotConnect', - properties: {method: 'wifi', success: true, error: ''}, + properties: { method: 'wifi', success: true, error: '' }, }) expect(makeEvent(failure, state('wireless'))).toEqual({ name: 'robotConnect', - properties: {method: 'wifi', success: false, error: 'AH'}, + properties: { method: 'wifi', success: false, error: 'AH' }, }) }) describe('events with protocol data', () => { - var protocolData = {foo: 'bar'} + var protocolData = { foo: 'bar' } beforeEach(() => { selectors.getProtocolAnalyticsData.mockResolvedValue(protocolData) @@ -81,7 +81,7 @@ describe('analytics events map', () => { test('robot:PROTOCOL_UPLOAD > protocolUploadRequest', () => { const prevState = {} const nextState = {} - const success = {type: 'protocol:UPLOAD', payload: {}} + const success = { type: 'protocol:UPLOAD', payload: {} } return expect(makeEvent(success, nextState, prevState)).resolves.toEqual({ name: 'protocolUploadRequest', @@ -90,39 +90,43 @@ describe('analytics events map', () => { }) test('robot:SESSION_RESPONSE with upload in flight', () => { - const prevState = {robot: {session: {sessionRequest: {inProgress: true}}}} + const prevState = { + robot: { session: { sessionRequest: { inProgress: true } } }, + } const nextState = {} - const success = {type: 'robot:SESSION_RESPONSE', payload: {}} + const success = { type: 'robot:SESSION_RESPONSE', payload: {} } return expect(makeEvent(success, nextState, prevState)).resolves.toEqual({ name: 'protocolUploadResponse', - properties: {success: true, error: '', ...protocolData}, + properties: { success: true, error: '', ...protocolData }, }) }) test('robot:SESSION_ERROR with upload in flight', () => { - const prevState = {robot: {session: {sessionRequest: {inProgress: true}}}} + const prevState = { + robot: { session: { sessionRequest: { inProgress: true } } }, + } const nextState = {} const failure = { type: 'robot:SESSION_ERROR', - payload: {error: new Error('AH')}, + payload: { error: new Error('AH') }, } return expect(makeEvent(failure, nextState, prevState)).resolves.toEqual({ name: 'protocolUploadResponse', - properties: {success: false, error: 'AH', ...protocolData}, + properties: { success: false, error: 'AH', ...protocolData }, }) }) test('robot:SESSION_RESPONSE/ERROR with no upload in flight', () => { const prevState = { - robot: {session: {sessionRequest: {inProgress: false}}}, + robot: { session: { sessionRequest: { inProgress: false } } }, } const nextState = {} - const success = {type: 'robot:SESSION_RESPONSE', payload: {}} + const success = { type: 'robot:SESSION_RESPONSE', payload: {} } const failure = { type: 'robot:SESSION_ERROR', - payload: {error: new Error('AH')}, + payload: { error: new Error('AH') }, } expect(makeEvent(success, nextState, prevState)).toBeNull() @@ -131,7 +135,7 @@ describe('analytics events map', () => { test('robot:RUN -> runStart event', () => { const state = {} - const action = {type: 'robot:RUN'} + const action = { type: 'robot:RUN' } return expect(makeEvent(action, state)).resolves.toEqual({ name: 'runStart', @@ -148,11 +152,11 @@ describe('analytics events map', () => { }, }, } - const action = {type: 'robot:RUN_RESPONSE', error: false} + const action = { type: 'robot:RUN_RESPONSE', error: false } return expect(makeEvent(action, state)).resolves.toEqual({ name: 'runFinish', - properties: {...protocolData, runTime: 4, success: true, error: ''}, + properties: { ...protocolData, runTime: 4, success: true, error: '' }, }) }) @@ -173,7 +177,12 @@ describe('analytics events map', () => { return expect(makeEvent(action, state)).resolves.toEqual({ name: 'runFinish', - properties: {...protocolData, runTime: 4, success: false, error: 'AH'}, + properties: { + ...protocolData, + runTime: 4, + success: false, + error: 'AH', + }, }) }) @@ -186,7 +195,7 @@ describe('analytics events map', () => { }, }, } - const action = {type: 'robot:PAUSE'} + const action = { type: 'robot:PAUSE' } return expect(makeEvent(action, state)).resolves.toEqual({ name: 'runPause', @@ -206,7 +215,7 @@ describe('analytics events map', () => { }, }, } - const action = {type: 'robot:RESUME'} + const action = { type: 'robot:RESUME' } return expect(makeEvent(action, state)).resolves.toEqual({ name: 'runResume', @@ -226,7 +235,7 @@ describe('analytics events map', () => { }, }, } - const action = {type: 'robot:CANCEL'} + const action = { type: 'robot:CANCEL' } return expect(makeEvent(action, state)).resolves.toEqual({ name: 'runCancel', diff --git a/app/src/analytics/hash.js b/app/src/analytics/hash.js index dd66aff6719..b02a52ba336 100644 --- a/app/src/analytics/hash.js +++ b/app/src/analytics/hash.js @@ -4,7 +4,7 @@ // considered secure nor should they ever be released publicly const ALGORITHM = 'SHA-256' -export default function hash (source: string): Promise { +export default function hash(source: string): Promise { const encoder = new TextEncoder() const data = encoder.encode(source) @@ -15,7 +15,7 @@ export default function hash (source: string): Promise { } // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string -function arrayBufferToHex (source: ArrayBuffer): string { +function arrayBufferToHex(source: ArrayBuffer): string { const bytes = new Uint8Array(source) return [...bytes].map(b => b.toString(16).padStart(2, '0')).join('') diff --git a/app/src/analytics/index.js b/app/src/analytics/index.js index 83519d8fff4..c5ee3aafbac 100644 --- a/app/src/analytics/index.js +++ b/app/src/analytics/index.js @@ -3,14 +3,14 @@ import noop from 'lodash/noop' import mixpanel from 'mixpanel-browser' -import {version} from '../../package.json' -import {updateConfig} from '../config' +import { version } from '../../package.json' +import { updateConfig } from '../config' import createLogger from '../logger' import makeEvent from './make-event' -import type {State, Action, ThunkAction, Middleware} from '../types' -import type {Config} from '../config' -import type {AnalyticsEvent} from './types' +import type { State, Action, ThunkAction, Middleware } from '../types' +import type { Config } from '../config' +import type { AnalyticsEvent } from './types' export * from './selectors' @@ -33,23 +33,23 @@ const MIXPANEL_OPTS = { // mixpanel.track handler (default noop) let track = noop -export function initializeAnalytics (): ThunkAction { +export function initializeAnalytics(): ThunkAction { return (_, getState) => { const config = getState().config.analytics - log.debug('Analytics config', {config}) + log.debug('Analytics config', { config }) initializeMixpanel(config) } } -export function toggleAnalyticsOptedIn (): ThunkAction { +export function toggleAnalyticsOptedIn(): ThunkAction { return (dispatch, getState) => { const optedIn = getAnalyticsOptedIn(getState()) return dispatch(updateConfig('analytics.optedIn', !optedIn)) } } -export function setAnalyticsSeen () { +export function setAnalyticsSeen() { return updateConfig('analytics.seenOptIn', true) } @@ -80,27 +80,27 @@ export const analyticsMiddleware: Middleware = store => next => action => { return result } -export function getAnalyticsOptedIn (state: State) { +export function getAnalyticsOptedIn(state: State) { return state.config.analytics.optedIn } -export function getAnalyticsSeen (state: State) { +export function getAnalyticsSeen(state: State) { return state.config.analytics.seenOptIn } -function trackEvent ( +function trackEvent( action: Action, event: null | AnalyticsEvent | Promise ) { if (event && event instanceof Promise) { event.then(e => trackEvent(action, e)) } else if (event) { - log.debug('Trackable event', {type: action.type, event}) + log.debug('Trackable event', { type: action.type, event }) track(event.name, event.properties) } } -function initializeMixpanel (config: AnalyticsConfig) { +function initializeMixpanel(config: AnalyticsConfig) { if (MIXPANEL_ID) { log.debug('Initializing Mixpanel') @@ -113,19 +113,19 @@ function initializeMixpanel (config: AnalyticsConfig) { } } -function enableMixpanelTracking (config: AnalyticsConfig) { +function enableMixpanelTracking(config: AnalyticsConfig) { if (MIXPANEL_ID) { log.debug('User has opted into analytics; tracking with Mixpanel') mixpanel.identify(config.appId) mixpanel.opt_in_tracking() - mixpanel.register({appVersion: version, appId: config.appId}) + mixpanel.register({ appVersion: version, appId: config.appId }) track = mixpanel.track.bind(mixpanel) } } -function disableMixpanelTracking (config: AnalyticsConfig) { +function disableMixpanelTracking(config: AnalyticsConfig) { if (MIXPANEL_ID) { log.debug('User has opted out of analytics; stopping tracking') diff --git a/app/src/analytics/make-event.js b/app/src/analytics/make-event.js index 9855ae29338..002dd897abc 100644 --- a/app/src/analytics/make-event.js +++ b/app/src/analytics/make-event.js @@ -1,16 +1,16 @@ // @flow // redux action types to analytics events map import createLogger from '../logger' -import {selectors as robotSelectors} from '../robot' -import {getConnectedRobot} from '../discovery' -import {getProtocolAnalyticsData, getRobotAnalyticsData} from './selectors' +import { selectors as robotSelectors } from '../robot' +import { getConnectedRobot } from '../discovery' +import { getProtocolAnalyticsData, getRobotAnalyticsData } from './selectors' -import type {State, Action} from '../types' -import type {AnalyticsEvent} from './types' +import type { State, Action } from '../types' +import type { AnalyticsEvent } from './types' const log = createLogger(__filename) -export default function makeEvent ( +export default function makeEvent( action: Action, nextState: State, prevState: State @@ -49,7 +49,7 @@ export default function makeEvent ( // only fire event if we had a protocol upload in flight; we don't want // to fire if user connects to robot with protocol already loaded if (!prevState.robot.session.sessionRequest.inProgress) return null - const {type: actionType, payload: actionPayload} = action + const { type: actionType, payload: actionPayload } = action return getProtocolAnalyticsData(nextState).then(data => ({ name: 'protocolUploadResponse', @@ -100,7 +100,7 @@ export default function makeEvent ( return getProtocolAnalyticsData(nextState).then(data => ({ name: 'runPause', - properties: {...data, runTime}, + properties: { ...data, runTime }, })) } @@ -110,7 +110,7 @@ export default function makeEvent ( return getProtocolAnalyticsData(nextState).then(data => ({ name: 'runResume', - properties: {...data, runTime}, + properties: { ...data, runTime }, })) } @@ -120,7 +120,7 @@ export default function makeEvent ( return getProtocolAnalyticsData(nextState).then(data => ({ name: 'runCancel', - properties: {...data, runTime}, + properties: { ...data, runTime }, })) } diff --git a/app/src/analytics/selectors.js b/app/src/analytics/selectors.js index bf265a6e3fa..72ac3b6ae89 100644 --- a/app/src/analytics/selectors.js +++ b/app/src/analytics/selectors.js @@ -1,5 +1,5 @@ // @flow -import {createSelector} from 'reselect' +import { createSelector } from 'reselect' import { getProtocolType, @@ -24,9 +24,9 @@ import { import hash from './hash' -import type {OutputSelector} from 'reselect' -import type {State} from '../types' -import type {ProtocolAnalyticsData, RobotAnalyticsData} from './types' +import type { OutputSelector } from 'reselect' +import type { State } from '../types' +import type { ProtocolAnalyticsData, RobotAnalyticsData } from './types' type ProtocolDataSelector = OutputSelector @@ -52,7 +52,7 @@ const _getUnhashedProtocolAnalyticsData: ProtocolDataSelector = createSelector( // TODO(mc, 2019-01-22): it would be good to have some way of caching these // hashes; reselect isn't geared towards async, so perhaps RxJS / observables? -export function getProtocolAnalyticsData ( +export function getProtocolAnalyticsData( state: State ): Promise { const data = _getUnhashedProtocolAnalyticsData(state) @@ -61,11 +61,11 @@ export function getProtocolAnalyticsData ( return Promise.all(hashTasks).then(result => { const [protocolAuthor, protocolText] = result - return {...data, protocolAuthor, protocolText} + return { ...data, protocolAuthor, protocolText } }) } -export function getRobotAnalyticsData (state: State): RobotAnalyticsData | null { +export function getRobotAnalyticsData(state: State): RobotAnalyticsData | null { const robot = getConnectedRobot(state) if (robot) { @@ -78,10 +78,10 @@ export function getRobotAnalyticsData (state: State): RobotAnalyticsData | null const pipettes = pipettesRequest.response ? { - left: pipettesRequest.response.left.model, - right: pipettesRequest.response.right.model, - } - : {left: null, right: null} + left: pipettesRequest.response.left.model, + right: pipettesRequest.response.right.model, + } + : { left: null, right: null } return settings.reduce( (result, setting) => ({ diff --git a/app/src/components/App.js b/app/src/components/App.js index e1d2717e6c7..cf40940ac94 100644 --- a/app/src/components/App.js +++ b/app/src/components/App.js @@ -1,34 +1,34 @@ import React from 'react' -import {Switch, Route, Redirect} from 'react-router' +import { Switch, Route, Redirect } from 'react-router' import NavBar from './nav-bar' -import {PageWrapper} from '../components/Page' +import { PageWrapper } from '../components/Page' import SidePanel from '../pages/SidePanel' import Robots from '../pages/Robots' import More from '../pages/More' import Upload from '../pages/Upload' import Calibrate from '../pages/Calibrate' import Run from '../pages/Run' -import {PortalRoot as ModalPortalRoot} from './portal' +import { PortalRoot as ModalPortalRoot } from './portal' import './App.global.css' import styles from './App.css' -export default function App () { +export default function App() { return (
- - - - - - - + + + + + + + @@ -36,6 +36,6 @@ export default function App () { ) } -function stopEvent (event) { +function stopEvent(event) { event.preventDefault() } diff --git a/app/src/components/AppSettings/AddManualIp/IpField.js b/app/src/components/AppSettings/AddManualIp/IpField.js index 0e0dde7e58e..2e609698f8a 100644 --- a/app/src/components/AppSettings/AddManualIp/IpField.js +++ b/app/src/components/AppSettings/AddManualIp/IpField.js @@ -1,17 +1,17 @@ // @flow import * as React from 'react' -import {IconButton} from '@opentrons/components' +import { IconButton } from '@opentrons/components' import styles from './styles.css' type Props = { field: any, form: any, - inputRef: {current: null | HTMLInputElement}, + inputRef: { current: null | HTMLInputElement }, } -export default function IpField (props: Props) { +export default function IpField(props: Props) { const { field, - form: {submitForm, dirty}, + form: { submitForm, dirty }, inputRef, } = props diff --git a/app/src/components/AppSettings/AddManualIp/IpItem.js b/app/src/components/AppSettings/AddManualIp/IpItem.js index b8161ba8982..5cca04fe3a7 100644 --- a/app/src/components/AppSettings/AddManualIp/IpItem.js +++ b/app/src/components/AppSettings/AddManualIp/IpItem.js @@ -1,9 +1,9 @@ // @flow import * as React from 'react' -import {IconButton, Icon} from '@opentrons/components' +import { IconButton, Icon } from '@opentrons/components' import styles from './styles.css' -import type {IconName} from '@opentrons/components' +import type { IconName } from '@opentrons/components' type Props = { candidate: string, discovered: boolean, @@ -11,7 +11,7 @@ type Props = { } export default class IpItem extends React.Component { remove = () => this.props.removeIp(this.props.candidate) - render () { + render() { const iconName = this.props.discovered ? 'check' : 'ot-spinner' return (
@@ -30,7 +30,7 @@ export default class IpItem extends React.Component { type DiscoveryIconProps = { iconName: IconName, } -function DiscoveryIcon (props: DiscoveryIconProps) { +function DiscoveryIcon(props: DiscoveryIconProps) { const spin = props.iconName === 'ot-spinner' return (
diff --git a/app/src/components/AppSettings/AddManualIp/IpList.js b/app/src/components/AppSettings/AddManualIp/IpList.js index 934b3daefde..14854ccf52e 100644 --- a/app/src/components/AppSettings/AddManualIp/IpList.js +++ b/app/src/components/AppSettings/AddManualIp/IpList.js @@ -1,12 +1,12 @@ // @flow import * as React from 'react' -import {connect} from 'react-redux' -import {getConfig, removeManualIp} from '../../../config' -import {getLiveRobots} from '../../../discovery' +import { connect } from 'react-redux' +import { getConfig, removeManualIp } from '../../../config' +import { getLiveRobots } from '../../../discovery' -import type {State, Dispatch} from '../../../types' -import type {DiscoveryCandidates} from '../../../config' -import type {Robot, ReachableRobot} from '../../../discovery' +import type { State, Dispatch } from '../../../types' +import type { DiscoveryCandidates } from '../../../config' +import type { Robot, ReachableRobot } from '../../../discovery' import IpItem from './IpItem' @@ -19,10 +19,10 @@ type DP = {| removeManualIp: (ip: string) => mixed, |} -type Props = {...SP, ...DP} +type Props = { ...SP, ...DP } -function IpList (props: Props) { - const {candidates, removeManualIp, robots} = props +function IpList(props: Props) { + const { candidates, removeManualIp, robots } = props const candidateList = [].concat(candidates) return ( @@ -43,14 +43,14 @@ function IpList (props: Props) { ) } -function mapStateToProps (state: State): SP { +function mapStateToProps(state: State): SP { return { robots: getLiveRobots(state), candidates: getConfig(state).discovery.candidates, } } -function mapDispatchToProps (dispatch: Dispatch): DP { +function mapDispatchToProps(dispatch: Dispatch): DP { return { removeManualIp: ip => dispatch(removeManualIp(ip)), } diff --git a/app/src/components/AppSettings/AddManualIp/ManualIpForm.js b/app/src/components/AppSettings/AddManualIp/ManualIpForm.js index 86e899d88aa..a213a6dc605 100644 --- a/app/src/components/AppSettings/AddManualIp/ManualIpForm.js +++ b/app/src/components/AppSettings/AddManualIp/ManualIpForm.js @@ -1,14 +1,14 @@ // @flow // import * as React from 'react' -import {connect} from 'react-redux' -import {getConfig, addManualIp} from '../../../config' -import {startDiscovery} from '../../../discovery' +import { connect } from 'react-redux' +import { getConfig, addManualIp } from '../../../config' +import { startDiscovery } from '../../../discovery' -import {Formik, Form, Field} from 'formik' +import { Formik, Form, Field } from 'formik' import IpField from './IpField' -import type {State, Dispatch} from '../../../types' -import type {DiscoveryCandidates} from '../../../config' +import type { State, Dispatch } from '../../../types' +import type { DiscoveryCandidates } from '../../../config' type SP = {| candidates: DiscoveryCandidates, @@ -18,18 +18,18 @@ type DP = {| addManualIp: (ip: string) => mixed, |} -type Props = {...SP, ...DP} +type Props = { ...SP, ...DP } class IpForm extends React.Component { - inputRef: {current: null | HTMLInputElement} - constructor (props) { + inputRef: { current: null | HTMLInputElement } + constructor(props) { super(props) this.inputRef = React.createRef() } - render () { + render() { return ( { this.props.addManualIp(values.ip) @@ -55,13 +55,13 @@ export default connect( DTP )(IpForm) -function STP (state: State): SP { +function STP(state: State): SP { return { candidates: getConfig(state).discovery.candidates, } } -function DTP (dispatch: Dispatch): DP { +function DTP(dispatch: Dispatch): DP { return { addManualIp: ip => { dispatch(addManualIp(ip)) diff --git a/app/src/components/AppSettings/AddManualIp/index.js b/app/src/components/AppSettings/AddManualIp/index.js index 7d131af30ae..f06f8286824 100644 --- a/app/src/components/AppSettings/AddManualIp/index.js +++ b/app/src/components/AppSettings/AddManualIp/index.js @@ -1,9 +1,9 @@ // @flow import * as React from 'react' -import {Link} from 'react-router-dom' +import { Link } from 'react-router-dom' -import {AlertModal} from '@opentrons/components' -import {Portal} from '../../portal' +import { AlertModal } from '@opentrons/components' +import { Portal } from '../../portal' import ManualIpForm from './ManualIpForm' import IpList from './IpList' @@ -11,14 +11,14 @@ export type AddManualIpProps = { backUrl: string, } -export default function AddManualIp (props: AddManualIpProps) { +export default function AddManualIp(props: AddManualIpProps) { return (

Enter an IP address or hostname to connect to your robot if automatic diff --git a/app/src/components/AppSettings/AdvancedSettingsCard.js b/app/src/components/AppSettings/AdvancedSettingsCard.js index 8e660625a1d..894af35459b 100644 --- a/app/src/components/AppSettings/AdvancedSettingsCard.js +++ b/app/src/components/AppSettings/AdvancedSettingsCard.js @@ -1,17 +1,17 @@ // @flow // app info card with version and updated import * as React from 'react' -import {connect} from 'react-redux' -import {withRouter, Route, Link} from 'react-router-dom' +import { connect } from 'react-redux' +import { withRouter, Route, Link } from 'react-router-dom' -import {getConfig, updateConfig, toggleDevTools} from '../../config' -import {Card} from '@opentrons/components' -import {LabeledToggle, LabeledSelect, LabeledButton} from '../controls' +import { getConfig, updateConfig, toggleDevTools } from '../../config' +import { Card } from '@opentrons/components' +import { LabeledToggle, LabeledSelect, LabeledButton } from '../controls' import AddManualIp from './AddManualIp' -import type {ContextRouter} from 'react-router' -import type {State, Dispatch} from '../../types' -import type {UpdateChannel} from '../../config' +import type { ContextRouter } from 'react-router' +import type { State, Dispatch } from '../../types' +import type { UpdateChannel } from '../../config' type OP = { ...ContextRouter, @@ -28,14 +28,14 @@ type DP = {| handleChannel: (event: SyntheticInputEvent) => mixed, |} -type Props = {...$Exact, ...SP, ...DP} +type Props = { ...$Exact, ...SP, ...DP } const TITLE = 'Advanced Settings' // TODO(mc, 2018-08-03): enable "alpha" option const CHANNEL_OPTIONS = [ - {name: 'Stable', value: (('latest': UpdateChannel): string)}, - {name: 'Beta', value: (('beta': UpdateChannel): string)}, + { name: 'Stable', value: (('latest': UpdateChannel): string) }, + { name: 'Beta', value: (('beta': UpdateChannel): string) }, ] export default withRouter( @@ -45,7 +45,7 @@ export default withRouter( )(AdvancedSettingsCard) ) -function AdvancedSettingsCard (props: Props) { +function AdvancedSettingsCard(props: Props) { return ( @@ -95,7 +95,7 @@ function AdvancedSettingsCard (props: Props) { ) } -function mapStateToProps (state: State): SP { +function mapStateToProps(state: State): SP { const config = getConfig(state) return { @@ -104,7 +104,7 @@ function mapStateToProps (state: State): SP { } } -function mapDispatchToProps (dispatch: Dispatch, ownProps: OP) { +function mapDispatchToProps(dispatch: Dispatch, ownProps: OP) { return { toggleDevTools: () => dispatch(toggleDevTools()), handleChannel: event => { diff --git a/app/src/components/AppSettings/AppInfoCard.js b/app/src/components/AppSettings/AppInfoCard.js index fbe952831e1..5b3c41a73bc 100644 --- a/app/src/components/AppSettings/AppInfoCard.js +++ b/app/src/components/AppSettings/AppInfoCard.js @@ -1,11 +1,11 @@ // @flow // app info card with version and updated import * as React from 'react' -import {Link} from 'react-router-dom' +import { Link } from 'react-router-dom' -import {CURRENT_VERSION} from '../../shell' -import {RefreshCard, LabeledValue, OutlineButton} from '@opentrons/components' -import {CardContentHalf} from '../layout' +import { CURRENT_VERSION } from '../../shell' +import { RefreshCard, LabeledValue, OutlineButton } from '@opentrons/components' +import { CardContentHalf } from '../layout' import styles from './styles.css' @@ -20,8 +20,8 @@ const VERSION_LABEL = 'Software Version' const UPDATE_AVAILABLE = 'view available update' const UPDATE_NOT_AVAILABLE = 'up to date' -export default function AppInfoCard (props: Props) { - const {checkUpdate, availableVersion} = props +export default function AppInfoCard(props: Props) { + const { checkUpdate, availableVersion } = props return ( diff --git a/app/src/components/AppSettings/UpdateApp/RestartAppModal.js b/app/src/components/AppSettings/UpdateApp/RestartAppModal.js index f755fb07f18..11a88a7f51b 100644 --- a/app/src/components/AppSettings/UpdateApp/RestartAppModal.js +++ b/app/src/components/AppSettings/UpdateApp/RestartAppModal.js @@ -1,8 +1,8 @@ // @flow import * as React from 'react' -import {Portal} from '../../portal' -import {ScrollableAlertModal} from '../../modals' +import { Portal } from '../../portal' +import { ScrollableAlertModal } from '../../modals' import UpdateAppMessage from './UpdateAppMessage' type Props = { @@ -10,15 +10,15 @@ type Props = { applyUpdate: () => mixed, closeModal: () => mixed, } -export default function RestartAppModal (props: Props) { - const {availableVersion, applyUpdate, closeModal} = props +export default function RestartAppModal(props: Props) { + const { availableVersion, applyUpdate, closeModal } = props return ( diff --git a/app/src/components/AppSettings/UpdateApp/UpdateAppMessage.js b/app/src/components/AppSettings/UpdateApp/UpdateAppMessage.js index 39fffc66a83..221e4c34c48 100644 --- a/app/src/components/AppSettings/UpdateApp/UpdateAppMessage.js +++ b/app/src/components/AppSettings/UpdateApp/UpdateAppMessage.js @@ -21,7 +21,7 @@ const RESTART_MESSAGE = (

) -export default function UpdateAppMessage (props: Props) { +export default function UpdateAppMessage(props: Props) { const message = props.downloaded ? RESTART_MESSAGE : UPDATE_MESSAGE return ( diff --git a/app/src/components/AppSettings/UpdateApp/UpdateAppModal.js b/app/src/components/AppSettings/UpdateApp/UpdateAppModal.js index 94288799f08..5dd01968d8c 100644 --- a/app/src/components/AppSettings/UpdateApp/UpdateAppModal.js +++ b/app/src/components/AppSettings/UpdateApp/UpdateAppModal.js @@ -1,13 +1,13 @@ // @flow import * as React from 'react' -import {Portal} from '../../portal' -import {ScrollableAlertModal} from '../../modals' +import { Portal } from '../../portal' +import { ScrollableAlertModal } from '../../modals' import UpdateAppMessage from './UpdateAppMessage' import ReleaseNotes from '../../ReleaseNotes' -import type {ShellUpdateState} from '../../../shell' -import type {ButtonProps} from '@opentrons/components' +import type { ShellUpdateState } from '../../../shell' +import type { ButtonProps } from '@opentrons/components' type Props = { update: ShellUpdateState, @@ -25,23 +25,23 @@ export default class UpdateAppModal extends React.Component< Props, UpdateAppState > { - constructor (props: Props) { + constructor(props: Props) { super(props) - this.state = {showReleaseNotes: false} + this.state = { showReleaseNotes: false } } setShowReleaseNotes = () => { - this.setState({showReleaseNotes: true}) + this.setState({ showReleaseNotes: true }) } - render () { + render() { const { downloadUpdate, closeModal, availableVersion, - update: {info}, + update: { info }, } = this.props - const {showReleaseNotes} = this.state + const { showReleaseNotes } = this.state let children: ?React.Node let button: ?ButtonProps @@ -64,7 +64,7 @@ export default class UpdateAppModal extends React.Component< {children} diff --git a/app/src/components/AppSettings/UpdateApp/index.js b/app/src/components/AppSettings/UpdateApp/index.js index cff45606bd2..1e1c96ade09 100644 --- a/app/src/components/AppSettings/UpdateApp/index.js +++ b/app/src/components/AppSettings/UpdateApp/index.js @@ -1,11 +1,11 @@ // @flow import * as React from 'react' -import {SpinnerModal} from '@opentrons/components' +import { SpinnerModal } from '@opentrons/components' import UpdateAppModal from './UpdateAppModal' import RestartAppModal from './RestartAppModal' -import type {ShellUpdateState} from '../../../shell' +import type { ShellUpdateState } from '../../../shell' type Props = { update: ShellUpdateState, @@ -15,8 +15,8 @@ type Props = { applyUpdate: () => mixed, closeModal: () => mixed, } -export default function UpdateApp (props: Props) { - const {downloaded, downloading} = props.update +export default function UpdateApp(props: Props) { + const { downloaded, downloading } = props.update if (downloaded) { return diff --git a/app/src/components/AppSettings/index.js b/app/src/components/AppSettings/index.js index 01a1a278f98..69e276e16af 100644 --- a/app/src/components/AppSettings/index.js +++ b/app/src/components/AppSettings/index.js @@ -2,17 +2,17 @@ // app status panel with connect button import * as React from 'react' -import {AnalyticsSettingsCard} from '../analytics-settings' +import { AnalyticsSettingsCard } from '../analytics-settings' import AdvancedSettingsCard from './AdvancedSettingsCard' import AppInfoCard from './AppInfoCard' -import {CardContainer, CardRow} from '../layout' +import { CardContainer, CardRow } from '../layout' type Props = { availableVersion: ?string, checkUpdate: () => mixed, } -export default function AppSettings (props: Props) { +export default function AppSettings(props: Props) { return ( diff --git a/app/src/components/CalibrateDeck/AttachTip.js b/app/src/components/CalibrateDeck/AttachTip.js index cd51084ec75..1c4c9a119fd 100644 --- a/app/src/components/CalibrateDeck/AttachTip.js +++ b/app/src/components/CalibrateDeck/AttachTip.js @@ -1,8 +1,8 @@ // @flow import * as React from 'react' -import type {PipetteModelSpecs} from '@opentrons/shared-data' -import type {CalibrateDeckStartedProps, CalibrationStep} from './types' -import {PrimaryButton} from '@opentrons/components' +import type { PipetteModelSpecs } from '@opentrons/shared-data' +import type { CalibrateDeckStartedProps, CalibrationStep } from './types' +import { PrimaryButton } from '@opentrons/components' import styles from './styles.css' @@ -17,7 +17,7 @@ type DiagramProps = { type Channels = 'single' | 'multi' -const DIAGRAMS: {[step: CalibrationStep]: {[Channels]: string}} = { +const DIAGRAMS: { [step: CalibrationStep]: { [Channels]: string } } = { '1': { single: require('./images/attach-tip-single@3x.png'), multi: require('./images/attach-tip-multi@3x.png'), @@ -28,11 +28,9 @@ const DIAGRAMS: {[step: CalibrationStep]: {[Channels]: string}} = { }, } -export default function AttachTipModal (props: Props) { +export default function AttachTipModal(props: Props) { const multi = props.pipette.channels === 8 - const tipLocation = multi - ? 'very first channel at front of' - : '' + const tipLocation = multi ? 'very first channel at front of' : '' const diagramSrc = getDiagramSrc(props) @@ -49,8 +47,11 @@ export default function AttachTipModal (props: Props) { instructions = (

Remove the GEB tip from the pipette.

-

You must restart your robot to finish the initial robot calibration process - and have the new settings take effect. It may take several minutes for your robot to restart.

+

+ You must restart your robot to finish the initial robot calibration + process and have the new settings take effect. It may take several + minutes for your robot to restart. +

) buttonText = 'finish and restart robot' @@ -60,10 +61,7 @@ export default function AttachTipModal (props: Props) {
{instructions} - + {buttonText}
@@ -74,11 +72,9 @@ export default function AttachTipModal (props: Props) { ) } -function getDiagramSrc (props: DiagramProps): string { - const {calibrationStep, pipette} = props - const channelsKey = pipette.channels === 8 - ? 'multi' - : 'single' +function getDiagramSrc(props: DiagramProps): string { + const { calibrationStep, pipette } = props + const channelsKey = pipette.channels === 8 ? 'multi' : 'single' return DIAGRAMS[calibrationStep][channelsKey] } diff --git a/app/src/components/CalibrateDeck/ClearDeckAlert.js b/app/src/components/CalibrateDeck/ClearDeckAlert.js index 5a2408c1bc1..0c1c9d71797 100644 --- a/app/src/components/CalibrateDeck/ClearDeckAlert.js +++ b/app/src/components/CalibrateDeck/ClearDeckAlert.js @@ -1,11 +1,11 @@ // @flow import * as React from 'react' -import {connect} from 'react-redux' -import {push} from 'react-router-redux' -import {deckCalibrationCommand as dcCommand} from '../../http-api-client' +import { connect } from 'react-redux' +import { push } from 'react-router-redux' +import { deckCalibrationCommand as dcCommand } from '../../http-api-client' import ClearDeckAlertModal from '../ClearDeckAlertModal' -import type {CalibrateDeckProps} from './types' +import type { CalibrateDeckProps } from './types' type OP = CalibrateDeckProps @@ -14,30 +14,36 @@ type DP = {| onCancel: () => mixed, |} -type Props = {...$Exact, ...DP} +type Props = { ...$Exact, ...DP } -export default connect(null, mapDispatchToProps)(ClearDeckAlert) +export default connect( + null, + mapDispatchToProps +)(ClearDeckAlert) -function ClearDeckAlert (props: Props) { +function ClearDeckAlert(props: Props) { return ( ) } -function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP { - const {robot, match: {url}} = ownProps +function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP { + const { + robot, + match: { url }, + } = ownProps return { onContinue: () => { - dispatch(dcCommand(robot, {command: 'move', point: 'attachTip'})) + dispatch(dcCommand(robot, { command: 'move', point: 'attachTip' })) dispatch(push(`${url}/step-1`)) }, - onCancel: () => dispatch(dcCommand(robot, {command: 'release'})), + onCancel: () => dispatch(dcCommand(robot, { command: 'release' })), } } diff --git a/app/src/components/CalibrateDeck/ConfirmPosition.js b/app/src/components/CalibrateDeck/ConfirmPosition.js index f5280701083..6a1d618a9bc 100644 --- a/app/src/components/CalibrateDeck/ConfirmPosition.js +++ b/app/src/components/CalibrateDeck/ConfirmPosition.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react' -import type {CalibrateDeckStartedProps} from './types' -import {PrimaryButton} from '@opentrons/components' +import type { CalibrateDeckStartedProps } from './types' +import { PrimaryButton } from '@opentrons/components' import JogControls from '../JogControls' import Instructions from './Instructions' @@ -9,7 +9,7 @@ type Props = CalibrateDeckStartedProps & { proceed: () => mixed, } -export default function ConfirmPosition (props: Props) { +export default function ConfirmPosition(props: Props) { return (
diff --git a/app/src/components/CalibrateDeck/ExitAlertModal.js b/app/src/components/CalibrateDeck/ExitAlertModal.js index 059d8c4b068..25326eedd4c 100644 --- a/app/src/components/CalibrateDeck/ExitAlertModal.js +++ b/app/src/components/CalibrateDeck/ExitAlertModal.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react' -import {AlertModal} from '@opentrons/components' +import { AlertModal } from '@opentrons/components' type Props = { back: () => mixed, @@ -12,18 +12,21 @@ const HEADING = 'Are you sure you want to exit initial robot calibration?' const CANCEL_TEXT = 'cancel' const EXIT_TEXT = 'exit calibration' -export default function ExitAlertModal (props: Props) { - const {back, exit} = props +export default function ExitAlertModal(props: Props) { + const { back, exit } = props return ( -

Doing so will home the robot and revert to using previously saved calibration settings.

+

+ Doing so will home the robot and revert to using previously saved + calibration settings. +

) } diff --git a/app/src/components/CalibrateDeck/InUseModal.js b/app/src/components/CalibrateDeck/InUseModal.js index c2cff206252..1934588b304 100644 --- a/app/src/components/CalibrateDeck/InUseModal.js +++ b/app/src/components/CalibrateDeck/InUseModal.js @@ -1,9 +1,9 @@ // @flow import * as React from 'react' -import {Link} from 'react-router-dom' +import { Link } from 'react-router-dom' -import {AlertModal, CheckboxField} from '@opentrons/components' -import type {CalibrateDeckProps} from './types' +import { AlertModal, CheckboxField } from '@opentrons/components' +import type { CalibrateDeckProps } from './types' type State = { checkOne: boolean, @@ -16,7 +16,7 @@ export default class InUseModal extends React.Component< CalibrateDeckProps, State > { - constructor (props: CalibrateDeckProps) { + constructor(props: CalibrateDeckProps) { super(props) this.state = { @@ -26,33 +26,39 @@ export default class InUseModal extends React.Component< } } - render () { - const {parentUrl, forceStart} = this.props + render() { + const { parentUrl, forceStart } = this.props const canContinue = Object.keys(this.state).every(k => this.state[k]) return (

Are you sure you want to interrupt this robot?

this.setState({checkOne: !this.state.checkOne})} + onChange={() => this.setState({ checkOne: !this.state.checkOne })} value={this.state.checkOne} /> this.setState({checkTwo: !this.state.checkTwo})} + onChange={() => this.setState({ checkTwo: !this.state.checkTwo })} value={this.state.checkTwo} /> this.setState({checkThree: !this.state.checkThree})} + onChange={() => + this.setState({ checkThree: !this.state.checkThree }) + } value={this.state.checkThree} />
diff --git a/app/src/components/CalibrateDeck/Instructions.js b/app/src/components/CalibrateDeck/Instructions.js index f75d8bcc832..f26da5b6686 100644 --- a/app/src/components/CalibrateDeck/Instructions.js +++ b/app/src/components/CalibrateDeck/Instructions.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react' import InstructionStep from '../InstructionStep' -import type {CalibrationStep} from './types' +import type { CalibrationStep } from './types' import styles from '../InstructionStep/styles.css' import calibrationTipSrc from './images/initial-calib-tip.svg' @@ -10,22 +10,38 @@ type Props = { calibrationStep: CalibrationStep, } -const DIAGRAMS: {[step: CalibrationStep]: ?string} = { +const DIAGRAMS: { [step: CalibrationStep]: ?string } = { '2': require('./images/initial-calib-z-5@3x.png'), '3': require('./images/initial-calib-xy-1@3x.png'), '4': require('./images/initial-calib-xy-3@3x.png'), '5': require('./images/initial-calib-xy-7@3x.png'), } -const INSTRUCTIONS: {[step: CalibrationStep]: ?React.Node} = { - '2': 'Jog robot until tip is positioned over a flat area in slot 5 (shown in blue).', - '3': (

Jog the robot until tip is centered above the + impression in slot 1.

), - '4': (

Jog the robot until tip is centered above the + impression in slot 3.

), - '5': (

Jog the robot until tip is centered above the + impression in slot 7.

), +const INSTRUCTIONS: { [step: CalibrationStep]: ?React.Node } = { + '2': + 'Jog robot until tip is positioned over a flat area in slot 5 (shown in blue).', + '3': ( +

+ Jog the robot until tip is centered above the +{' '} + impression in slot 1. +

+ ), + '4': ( +

+ Jog the robot until tip is centered above the +{' '} + impression in slot 3. +

+ ), + '5': ( +

+ Jog the robot until tip is centered above the +{' '} + impression in slot 7. +

+ ), } -export default function Instructions (props: Props) { - const {calibrationStep} = props +export default function Instructions(props: Props) { + const { calibrationStep } = props const diagram = getDiagramSrc(calibrationStep) const instructions = getInstructionsByStep(calibrationStep) @@ -33,26 +49,23 @@ export default function Instructions (props: Props) { return (
- + {instructions} - -

Jog the robot until tip is just barely touching the deck.

+ +

+ Jog the robot until tip is just barely touching the + deck. +

) } -function getDiagramSrc (calibrationStep) { +function getDiagramSrc(calibrationStep) { return DIAGRAMS[calibrationStep] } -function getInstructionsByStep (calibrationStep) { +function getInstructionsByStep(calibrationStep) { return INSTRUCTIONS[calibrationStep] } diff --git a/app/src/components/CalibrateDeck/InstructionsModal.js b/app/src/components/CalibrateDeck/InstructionsModal.js index b3af7961ea8..40a82da1417 100644 --- a/app/src/components/CalibrateDeck/InstructionsModal.js +++ b/app/src/components/CalibrateDeck/InstructionsModal.js @@ -1,19 +1,19 @@ // @flow import * as React from 'react' -import {connect} from 'react-redux' -import {push} from 'react-router-redux' -import {Link} from 'react-router-dom' +import { connect } from 'react-redux' +import { push } from 'react-router-redux' +import { Link } from 'react-router-dom' import capitalize from 'lodash/capitalize' -import type {CalibrateDeckStartedProps} from './types' +import type { CalibrateDeckStartedProps } from './types' import { restartRobotServer, deckCalibrationCommand as dcCommand, } from '../../http-api-client' -import {chainActions} from '../../util' -import {ModalPage, SpinnerModalPage} from '@opentrons/components' +import { chainActions } from '../../util' +import { ModalPage, SpinnerModalPage } from '@opentrons/components' import AttachTip from './AttachTip' import ConfirmPosition from './ConfirmPosition' @@ -23,17 +23,20 @@ type DP = {| proceed: () => mixed, |} -type Props = {...$Exact, ...DP} +type Props = { ...$Exact, ...DP } const TITLE = 'Deck Calibration' -export default connect(null, mapDispatchToProps)(InstructionsModal) +export default connect( + null, + mapDispatchToProps +)(InstructionsModal) -function InstructionsModal (props: Props) { - const {calibrationStep, exitUrl, commandRequest} = props +function InstructionsModal(props: Props) { + const { calibrationStep, exitUrl, commandRequest } = props const subtitle = `Step ${calibrationStep} of 6` - const titleBarBase = {title: TITLE, subtitle} - const backButtonBase = {children: 'exit'} + const titleBarBase = { title: TITLE, subtitle } + const backButtonBase = { children: 'exit' } if ( commandRequest.inProgress && @@ -44,7 +47,7 @@ function InstructionsModal (props: Props) { @@ -65,7 +68,7 @@ function InstructionsModal (props: Props) { @@ -75,79 +78,95 @@ function InstructionsModal (props: Props) { ) } -function getHeading (props: Props): string { - const {calibrationStep, mount} = props +function getHeading(props: Props): string { + const { calibrationStep, mount } = props switch (calibrationStep) { - case '1': return `place tip on ${mount} pipette` - case '2': return 'calibrate the z-axis' - case '6': return 'remove tip from pipette & restart robot' + case '1': + return `place tip on ${mount} pipette` + case '2': + return 'calibrate the z-axis' + case '6': + return 'remove tip from pipette & restart robot' } return 'calibrate the x-y axes' } -function getMovementDescription (props: Props): string { - const {commandRequest} = props +function getMovementDescription(props: Props): string { + const { commandRequest } = props const mount = capitalize(props.mount) switch ( commandRequest.request && - commandRequest.request.command === 'move' && - commandRequest.request.point + commandRequest.request.command === 'move' && + commandRequest.request.point ) { - case 'attachTip': return `${mount} pipette moving to the front and down.` - case 'safeZ': return `${mount} pipette moving to slot 5.` - case '1': return `${mount} pipette moving to slot 1.` - case '2': return `${mount} pipette moving to slot 3.` - case '3': return `${mount} pipette moving to slot 7.` + case 'attachTip': + return `${mount} pipette moving to the front and down.` + case 'safeZ': + return `${mount} pipette moving to slot 5.` + case '1': + return `${mount} pipette moving to slot 1.` + case '2': + return `${mount} pipette moving to slot 3.` + case '3': + return `${mount} pipette moving to slot 7.` } return '' } -function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP { - const {robot, pipette, calibrationStep: step, match: {url}} = ownProps +function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP { + const { + robot, + pipette, + calibrationStep: step, + match: { url }, + } = ownProps const goToNext = push(`${url}/step-${Number(step) + 1}`) let actions if (step === '1') { actions = [ - dcCommand(robot, {command: 'attach tip', tipLength: pipette.tipLength.value}), - dcCommand(robot, {command: 'move', point: 'safeZ'}), + dcCommand(robot, { + command: 'attach tip', + tipLength: pipette.tipLength.value, + }), + dcCommand(robot, { command: 'move', point: 'safeZ' }), goToNext, ] } else if (step === '2') { actions = [ - dcCommand(robot, {command: 'save z'}), - dcCommand(robot, {command: 'move', point: '1'}), + dcCommand(robot, { command: 'save z' }), + dcCommand(robot, { command: 'move', point: '1' }), goToNext, ] } else if (step === '3') { actions = [ - dcCommand(robot, {command: 'save xy', point: '1'}), - dcCommand(robot, {command: 'move', point: '2'}), + dcCommand(robot, { command: 'save xy', point: '1' }), + dcCommand(robot, { command: 'move', point: '2' }), goToNext, ] } else if (step === '4') { actions = [ - dcCommand(robot, {command: 'save xy', point: '2'}), - dcCommand(robot, {command: 'move', point: '3'}), + dcCommand(robot, { command: 'save xy', point: '2' }), + dcCommand(robot, { command: 'move', point: '3' }), goToNext, ] } else if (step === '5') { actions = [ - dcCommand(robot, {command: 'save xy', point: '3'}), - dcCommand(robot, {command: 'move', point: 'attachTip'}), + dcCommand(robot, { command: 'save xy', point: '3' }), + dcCommand(robot, { command: 'move', point: 'attachTip' }), goToNext, ] } else { actions = [ - dcCommand(robot, {command: 'save transform'}), + dcCommand(robot, { command: 'save transform' }), restartRobotServer(robot), push(ownProps.parentUrl), ] } - return {proceed: () => dispatch(chainActions(...actions))} + return { proceed: () => dispatch(chainActions(...actions)) } } diff --git a/app/src/components/CalibrateDeck/NoPipetteModal.js b/app/src/components/CalibrateDeck/NoPipetteModal.js index cf0f8e94259..e132fbd8c4a 100644 --- a/app/src/components/CalibrateDeck/NoPipetteModal.js +++ b/app/src/components/CalibrateDeck/NoPipetteModal.js @@ -1,20 +1,18 @@ // @flow import * as React from 'react' -import {Link} from 'react-router-dom' -import {AlertModal} from '@opentrons/components' +import { Link } from 'react-router-dom' +import { AlertModal } from '@opentrons/components' type Props = { parentUrl: string, } const HEADING = 'No pipette attached' -export default function NoPipetteModal (props: Props) { +export default function NoPipetteModal(props: Props) { return (

Please attach a pipette before attempting to calibrate robot.

diff --git a/app/src/components/CalibrateDeck/index.js b/app/src/components/CalibrateDeck/index.js index b01877a408a..9ec9c688079 100644 --- a/app/src/components/CalibrateDeck/index.js +++ b/app/src/components/CalibrateDeck/index.js @@ -1,14 +1,14 @@ // @flow import * as React from 'react' -import {connect} from 'react-redux' -import {push, goBack} from 'react-router-redux' -import {Switch, Route, withRouter} from 'react-router' +import { connect } from 'react-redux' +import { push, goBack } from 'react-router-redux' +import { Switch, Route, withRouter } from 'react-router' -import type {State, Dispatch} from '../../types' -import type {OP, SP, DP, CalibrateDeckProps, CalibrationStep} from './types' +import type { State, Dispatch } from '../../types' +import type { OP, SP, DP, CalibrateDeckProps, CalibrationStep } from './types' -import {getPipetteModelSpecs} from '@opentrons/shared-data' -import {chainActions} from '../../util' +import { getPipetteModelSpecs } from '@opentrons/shared-data' +import { chainActions } from '../../util' import createLogger from '../../logger' import { @@ -19,7 +19,7 @@ import { makeGetDeckCalibrationStartState, } from '../../http-api-client' -import {ErrorModal} from '../modals' +import { ErrorModal } from '../modals' import ClearDeckAlert from './ClearDeckAlert' import InUseModal from './InUseModal' import NoPipetteModal from './NoPipetteModal' @@ -30,19 +30,23 @@ const log = createLogger(__filename) const RE_STEP = '(1|2|3|4|5|6)' const BAD_PIPETTE_ERROR = 'Unexpected pipette response from robot' -const ERROR_DESCRIPTION = 'An unexpected error has cleared your deck calibration progress, please try again.' +const ERROR_DESCRIPTION = + 'An unexpected error has cleared your deck calibration progress, please try again.' export default withRouter( - connect(makeMapStateToProps, mapDispatchToProps)(CalibrateDeck) + connect( + makeMapStateToProps, + mapDispatchToProps + )(CalibrateDeck) ) -function CalibrateDeck (props: CalibrateDeckProps) { +function CalibrateDeck(props: CalibrateDeckProps) { const { startRequest, commandRequest, pipetteProps, parentUrl, - match: {path}, + match: { path }, } = props if (pipetteProps && !pipetteProps.pipette) { @@ -50,7 +54,7 @@ function CalibrateDeck (props: CalibrateDeckProps) { ) } @@ -67,78 +71,92 @@ function CalibrateDeck (props: CalibrateDeckProps) { return ( - { - const {error} = startRequest - - if (error) { - const {status} = error + { + const { error } = startRequest + + if (error) { + const { status } = error + + // conflict: token already issued + if (status === 409) { + return + } + + // forbidden: no pipette attached + if (status === 403) { + return + } + + return ( + + ) + } - // conflict: token already issued - if (status === 409) { - return () + if (pipetteProps && pipetteProps.pipette) { + return } - // forbidden: no pipette attached - if (status === 403) { - return () + return null + }} + /> + { + if (!pipetteProps || !pipetteProps.pipette) return null + + const { + match: { params, url: stepUrl }, + } = stepProps + const step: CalibrationStep = (params.step: any) + const exitUrl = `${stepUrl}/exit` + + const startedProps = { + ...props, + exitUrl, + pipette: pipetteProps.pipette, + mount: pipetteProps.mount, + calibrationStep: step, } return ( - ) - } - - if (pipetteProps && pipetteProps.pipette) { - return () - } - - return null - }} /> - { - if (!pipetteProps || !pipetteProps.pipette) return null - - const {match: {params, url: stepUrl}} = stepProps - const step: CalibrationStep = (params.step: any) - const exitUrl = `${stepUrl}/exit` - - const startedProps = { - ...props, - exitUrl, - pipette: pipetteProps.pipette, - mount: pipetteProps.mount, - calibrationStep: step, - } - - return ( -
- - ( - - )} /> -
- ) - }} /> +
+ + } + /> +
+ ) + }} + />
) } -function makeMapStateToProps (): (state: State, ownProps: OP) => SP { +function makeMapStateToProps(): (state: State, ownProps: OP) => SP { const getDeckCalCommand = makeGetDeckCalibrationCommandState() const getDeckCalStartState = makeGetDeckCalibrationStartState() return (state, ownProps) => { - const {robot} = ownProps + const { robot } = ownProps const startRequest = getDeckCalStartState(state, robot) const pipetteInfo = startRequest.response && startRequest.response.pipette const pipetteProps = pipetteInfo - ? {mount: pipetteInfo.mount, pipette: getPipetteModelSpecs(pipetteInfo.model)} + ? { + mount: pipetteInfo.mount, + pipette: getPipetteModelSpecs(pipetteInfo.model), + } : null if (pipetteProps && !pipetteProps.pipette) { - log.error('Invalid pipette received from API', {pipetteInfo}) + log.error('Invalid pipette received from API', { pipetteInfo }) } return { @@ -149,20 +167,24 @@ function makeMapStateToProps (): (state: State, ownProps: OP) => SP { } } -function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP { - const {robot, parentUrl} = ownProps +function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP { + const { robot, parentUrl } = ownProps return { - jog: (axis, direction, step) => dispatch( - deckCalibrationCommand(robot, {command: 'jog', axis, direction, step}) - ), + jog: (axis, direction, step) => + dispatch( + deckCalibrationCommand(robot, { command: 'jog', axis, direction, step }) + ), forceStart: () => dispatch(startDeckCalibration(robot, true)), // exit button click in title bar, opens exit alert modal, confirm exit click - exit: () => dispatch(chainActions( - deckCalibrationCommand(robot, {command: 'release'}), - push(parentUrl), - home(robot) - )), + exit: () => + dispatch( + chainActions( + deckCalibrationCommand(robot, { command: 'release' }), + push(parentUrl), + home(robot) + ) + ), // cancel button click in exit alert modal back: () => dispatch(goBack()), } diff --git a/app/src/components/CalibrateDeck/types.js b/app/src/components/CalibrateDeck/types.js index 588792c6e3f..a3b7ca52218 100644 --- a/app/src/components/CalibrateDeck/types.js +++ b/app/src/components/CalibrateDeck/types.js @@ -1,9 +1,12 @@ // @flow -import type {Match} from 'react-router' -import type {PipetteModelSpecs} from '@opentrons/shared-data' -import type {RobotService, Mount} from '../../robot' -import type {DeckCalCommandState, DeckCalStartState} from '../../http-api-client' -import type {Jog} from '../JogControls' +import type { Match } from 'react-router' +import type { PipetteModelSpecs } from '@opentrons/shared-data' +import type { RobotService, Mount } from '../../robot' +import type { + DeckCalCommandState, + DeckCalStartState, +} from '../../http-api-client' +import type { Jog } from '../JogControls' export type CalibrationStep = '1' | '2' | '3' | '4' | '5' | '6' @@ -29,7 +32,7 @@ export type DP = {| back: () => mixed, |} -export type CalibrateDeckProps = {...$Exact, ...SP, ...DP} +export type CalibrateDeckProps = { ...$Exact, ...SP, ...DP } export type CalibrateDeckStartedProps = { ...$Exact, diff --git a/app/src/components/CalibrateLabware/ConfirmModal.js b/app/src/components/CalibrateLabware/ConfirmModal.js index 3a145110707..a265c00687d 100644 --- a/app/src/components/CalibrateLabware/ConfirmModal.js +++ b/app/src/components/CalibrateLabware/ConfirmModal.js @@ -3,9 +3,9 @@ import * as React from 'react' import cx from 'classnames' -import type {Labware} from '../../robot' +import type { Labware } from '../../robot' -import {ModalPage} from '@opentrons/components' +import { ModalPage } from '@opentrons/components' import ConfirmModalContents from './ConfirmModalContents' import styles from './styles.css' @@ -15,24 +15,24 @@ type Props = { onBackClick: () => mixed, } -export default function ConfirmModal (props: Props) { - const {labware, onBackClick, calibrateToBottom} = props +export default function ConfirmModal(props: Props) { + const { labware, onBackClick, calibrateToBottom } = props // disable back click if we're moving or if we've loaded up with tips - const backClickDisabled = ( - labware.isMoving || - labware.calibration === 'picked-up' - ) + const backClickDisabled = + labware.isMoving || labware.calibration === 'picked-up' // TODO (ka 2018-4-18): this is a temporary workaround for a stlye over ride for in progress screens with transparate bg - const contentsStyle = labware.calibration.match(/^(moving-to-slot|picking-up|dropping-tip|confirming)$/) + const contentsStyle = labware.calibration.match( + /^(moving-to-slot|picking-up|dropping-tip|confirming)$/ + ) ? cx(styles.modal_contents, styles.in_progress_contents) : styles.modal_contents const titleBar = { title: 'Calibrate Deck', subtitle: labware.type, - back: {onClick: onBackClick, disabled: backClickDisabled}, + back: { onClick: onBackClick, disabled: backClickDisabled }, } return ( @@ -41,7 +41,10 @@ export default function ConfirmModal (props: Props) { contentsClassName={contentsStyle} heading={`Calibrate pipette to ${labware.type}`} > - +
) } diff --git a/app/src/components/CalibrateLabware/ConfirmModalContents.js b/app/src/components/CalibrateLabware/ConfirmModalContents.js index dc6e007b369..b41af69efc7 100644 --- a/app/src/components/CalibrateLabware/ConfirmModalContents.js +++ b/app/src/components/CalibrateLabware/ConfirmModalContents.js @@ -1,7 +1,7 @@ // @flow // contents container for ConfirmModal import * as React from 'react' -import {connect} from 'react-redux' +import { connect } from 'react-redux' import { selectors as robotSelectors, @@ -19,40 +19,39 @@ type SP = {| calibrator: ?Pipette, |} -type Props = {...$Exact, ...SP} +type Props = { ...$Exact, ...SP } export default connect(mapStateToProps)(ConfirmModalContents) -function ConfirmModalContents (props: Props) { +function ConfirmModalContents(props: Props) { if (!props.calibrator) return null switch (props.calibration) { case 'unconfirmed': case 'over-slot': case 'jogging': - return () + return case 'picked-up': - return () + return case 'moving-to-slot': case 'picking-up': case 'dropping-tip': case 'confirming': - return () + return default: return null } } -function mapStateToProps (state, ownProps: OP): SP { +function mapStateToProps(state, ownProps: OP): SP { const calibratorMount = ownProps.calibratorMount const pipettes = robotSelectors.getPipettes(state) - const calibrator = ( - pipettes.find((i) => i.mount === calibratorMount) || + const calibrator = + pipettes.find(i => i.mount === calibratorMount) || robotSelectors.getCalibrator(state) - ) - return {calibrator} + return { calibrator } } diff --git a/app/src/components/CalibrateLabware/ConfirmPickupContents.js b/app/src/components/CalibrateLabware/ConfirmPickupContents.js index f8cff0b8316..a2f415d5264 100644 --- a/app/src/components/CalibrateLabware/ConfirmPickupContents.js +++ b/app/src/components/CalibrateLabware/ConfirmPickupContents.js @@ -1,7 +1,7 @@ // @flow // pickup confirmation contents container for ConfirmModal -import type {Dispatch} from 'redux' -import {connect} from 'react-redux' +import type { Dispatch } from 'redux' +import { connect } from 'react-redux' import { actions as robotActions, @@ -20,13 +20,19 @@ type DispatchProps = { onYesClick: () => void, } -export default connect(null, mapDispatchToProps)(ConfirmPickupPrompt) +export default connect( + null, + mapDispatchToProps +)(ConfirmPickupPrompt) -function mapDispatchToProps ( +function mapDispatchToProps( dispatch: Dispatch<*>, ownProps: OwnProps ): DispatchProps { - const {slot, calibrator: {mount}} = ownProps + const { + slot, + calibrator: { mount }, + } = ownProps return { onNoClick: () => { diff --git a/app/src/components/CalibrateLabware/ConfirmPickupPrompt.js b/app/src/components/CalibrateLabware/ConfirmPickupPrompt.js index c9d766f32e4..4c0e050d05f 100644 --- a/app/src/components/CalibrateLabware/ConfirmPickupPrompt.js +++ b/app/src/components/CalibrateLabware/ConfirmPickupPrompt.js @@ -2,9 +2,9 @@ // pickup confirmation prompt component for ConfirmPickupContents import * as React from 'react' -import type {Labware, Pipette} from '../../robot' +import type { Labware, Pipette } from '../../robot' -import {PrimaryButton} from '@opentrons/components' +import { PrimaryButton } from '@opentrons/components' import styles from './styles.css' type Props = Labware & { @@ -13,8 +13,12 @@ type Props = Labware & { onYesClick: () => void, } -export default function ConfirmPickedUpPrompt (props: Props) { - const {onNoClick, onYesClick, calibrator: {mount, channels}} = props +export default function ConfirmPickedUpPrompt(props: Props) { + const { + onNoClick, + onYesClick, + calibrator: { mount, channels }, + } = props const multi = channels === 8 const maybePluralTip = `${multi ? '' : 'a '}tip${multi ? 's' : ''}` diff --git a/app/src/components/CalibrateLabware/ConfirmPositionContents.js b/app/src/components/CalibrateLabware/ConfirmPositionContents.js index a1852142609..58f72013815 100644 --- a/app/src/components/CalibrateLabware/ConfirmPositionContents.js +++ b/app/src/components/CalibrateLabware/ConfirmPositionContents.js @@ -1,15 +1,15 @@ // @flow // container for position confirmation logic in ConfirmationModal import * as React from 'react' -import {connect} from 'react-redux' +import { connect } from 'react-redux' -import type {Dispatch} from '../../types' -import type {Pipette, Labware} from '../../robot' +import type { Dispatch } from '../../types' +import type { Pipette, Labware } from '../../robot' -import {actions as robotActions} from '../../robot' -import {PrimaryButton} from '@opentrons/components' +import { actions as robotActions } from '../../robot' +import { PrimaryButton } from '@opentrons/components' import ConfirmPositionDiagram from './ConfirmPositionDiagram' -import JogControls, {type Jog} from '../JogControls' +import JogControls, { type Jog } from '../JogControls' type DP = { onConfirmClick: () => mixed, @@ -23,10 +23,17 @@ type OP = Labware & { type Props = DP & OP -export default connect(null, mapDispatchToProps)(ConfirmPositionContents) +export default connect( + null, + mapDispatchToProps +)(ConfirmPositionContents) -function ConfirmPositionContents (props: Props) { - const {isTiprack, onConfirmClick, calibrator: {channels}} = props +function ConfirmPositionContents(props: Props) { + const { + isTiprack, + onConfirmClick, + calibrator: { channels }, + } = props const confirmButtonText = isTiprack ? `pick up tip${channels === 8 ? 's' : ''}` : 'save calibration' @@ -36,15 +43,19 @@ function ConfirmPositionContents (props: Props) { {/* $FlowFixMe: `...props` type doesn't include necessary keys */} - + {confirmButtonText}
) } -function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP { - const {slot, isTiprack, calibrator: {mount}} = ownProps +function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP { + const { + slot, + isTiprack, + calibrator: { mount }, + } = ownProps const onConfirmAction = isTiprack ? robotActions.pickupAndHome(mount, slot) : robotActions.updateOffset(mount, slot) diff --git a/app/src/components/CalibrateLabware/ConfirmPositionDiagram.js b/app/src/components/CalibrateLabware/ConfirmPositionDiagram.js index df3679d2489..3fffcd1cbbd 100644 --- a/app/src/components/CalibrateLabware/ConfirmPositionDiagram.js +++ b/app/src/components/CalibrateLabware/ConfirmPositionDiagram.js @@ -2,10 +2,10 @@ // diagram and instructions for ConfirmPositionContents import * as React from 'react' -import type {Labware, Pipette} from '../../robot' +import type { Labware, Pipette } from '../../robot' import InstructionStep from '../InstructionStep' -import {getInstructionsByType, getDiagramSrc} from './instructions-data' +import { getInstructionsByType, getDiagramSrc } from './instructions-data' import styles from '../InstructionStep/styles.css' export type LabwareCalibrationProps = Labware & { @@ -14,23 +14,19 @@ export type LabwareCalibrationProps = Labware & { calibrateToBottom: boolean, } -export default function ConfirmPositionDiagram (props: LabwareCalibrationProps) { +export default function ConfirmPositionDiagram(props: LabwareCalibrationProps) { const instructions = getInstructionsByType(props) const diagrams = getDiagramSrc(props) - return diagrams && ( -
- - {instructions.one} - - - {instructions.two} - -
+ return ( + diagrams && ( +
+ + {instructions.one} + + + {instructions.two} + +
+ ) ) } diff --git a/app/src/components/CalibrateLabware/InProgressContents.js b/app/src/components/CalibrateLabware/InProgressContents.js index f123c526c88..a6b643e67aa 100644 --- a/app/src/components/CalibrateLabware/InProgressContents.js +++ b/app/src/components/CalibrateLabware/InProgressContents.js @@ -2,11 +2,9 @@ // in progress spinner for ConfirmModalContents import * as React from 'react' -import {Icon} from '@opentrons/components' +import { Icon } from '@opentrons/components' import styles from './styles.css' -export default function InProgressContent () { - return ( - - ) +export default function InProgressContent() { + return } diff --git a/app/src/components/CalibrateLabware/InfoBox.js b/app/src/components/CalibrateLabware/InfoBox.js index 9919a6e5d30..38b5bf2183e 100644 --- a/app/src/components/CalibrateLabware/InfoBox.js +++ b/app/src/components/CalibrateLabware/InfoBox.js @@ -1,9 +1,9 @@ // @flow // info panel for labware calibration page import * as React from 'react' -import type {Dispatch} from 'redux' -import {connect} from 'react-redux' -import {push} from 'react-router-redux' +import type { Dispatch } from 'redux' +import { connect } from 'react-redux' +import { push } from 'react-router-redux' import capitalize from 'lodash/capitalize' import { @@ -14,7 +14,7 @@ import { type LabwareType, } from '../../robot' -import {PrimaryButton} from '@opentrons/components' +import { PrimaryButton } from '@opentrons/components' import CalibrationInfoBox from '../CalibrationInfoBox' import CalibrationInfoContent from '../CalibrationInfoContent' @@ -44,10 +44,14 @@ type Props = OwnProps & { }, } -export default connect(mapStateToProps, null, mergeProps)(InfoBox) +export default connect( + mapStateToProps, + null, + mergeProps +)(InfoBox) -function InfoBox (props: Props) { - const {labware, button} = props +function InfoBox(props: Props) { + const { labware, button } = props let title = 'No labware selected' let confirmed = false let description = 'Please select labware to continue' @@ -78,28 +82,24 @@ function InfoBox (props: Props) { return ( - {description} -

- )} - rightChildren={( - button && showButton && ( - - {buttonText} - + leftChildren={

{description}

} + rightChildren={ + button && + showButton && ( + {buttonText} ) - )} + } />
) } -function mapStateToProps (state, ownProps: OwnProps): StateProps { - const {labware} = ownProps - const _nextLabware = !labware || labware.calibration === 'confirmed' - ? robotSelectors.getNextLabware(state) - : null +function mapStateToProps(state, ownProps: OwnProps): StateProps { + const { labware } = ownProps + const _nextLabware = + !labware || labware.calibration === 'confirmed' + ? robotSelectors.getNextLabware(state) + : null const _buttonTarget = _nextLabware || labware let _calibratorMount = robotSelectors.getCalibratorMount(state) @@ -111,20 +111,18 @@ function mapStateToProps (state, ownProps: OwnProps): StateProps { return { _calibratorMount, _buttonTarget, - _buttonTargetIsNext: ( - _buttonTarget != null && - _buttonTarget === _nextLabware - ), + _buttonTargetIsNext: + _buttonTarget != null && _buttonTarget === _nextLabware, } } -function mergeProps ( +function mergeProps( stateProps: StateProps, dispatchProps: DispatchProps, ownProps: OwnProps ): Props { - const {_buttonTarget, _buttonTargetIsNext, _calibratorMount} = stateProps - const {dispatch} = dispatchProps + const { _buttonTarget, _buttonTargetIsNext, _calibratorMount } = stateProps + const { dispatch } = dispatchProps const targetConfirmed = _buttonTarget && _buttonTarget.confirmed let button = null @@ -146,5 +144,5 @@ function mergeProps ( } } - return {...ownProps, button} + return { ...ownProps, button } } diff --git a/app/src/components/CalibrateLabware/index.js b/app/src/components/CalibrateLabware/index.js index 1511b4c3163..db0379348de 100644 --- a/app/src/components/CalibrateLabware/index.js +++ b/app/src/components/CalibrateLabware/index.js @@ -1,8 +1,8 @@ // @flow // info panel and controls for labware calibration page import * as React from 'react' -import {withRouter} from 'react-router' -import type {Labware} from '../../robot' +import { withRouter } from 'react-router' +import type { Labware } from '../../robot' import DeckMap from '../DeckMap' import InfoBox from './InfoBox' @@ -11,7 +11,7 @@ type Props = { } export default withRouter(CalibrateLabware) -function CalibrateLabware (props: Props) { +function CalibrateLabware(props: Props) { return (
diff --git a/app/src/components/CalibrateLabware/instructions-data.js b/app/src/components/CalibrateLabware/instructions-data.js index 066734c040d..dc2f7157a27 100644 --- a/app/src/components/CalibrateLabware/instructions-data.js +++ b/app/src/components/CalibrateLabware/instructions-data.js @@ -1,12 +1,12 @@ // @flow import * as React from 'react' -import {type LabwareCalibrationProps} from './ConfirmPositionDiagram' +import { type LabwareCalibrationProps } from './ConfirmPositionDiagram' type Step = 'one' | 'two' type Channels = 'single' | 'multi' type TypeKey = 'tiprack' | 'trough' | 'tuberack' | 'plate384' | 'plate96' -const DIAGRAMS: {[TypeKey]: {[Channels]: {[Step]: string}}} = { +const DIAGRAMS: { [TypeKey]: { [Channels]: { [Step]: string } } } = { tiprack: { single: { one: require('./images/step-1-tiprack-single@3x.png'), @@ -59,7 +59,7 @@ const DIAGRAMS: {[TypeKey]: {[Channels]: {[Step]: string}}} = { }, } -const DIAGRAMS_BOTTOM: {[TypeKey]: {[Channels]: {[Step]: string}}} = { +const DIAGRAMS_BOTTOM: { [TypeKey]: { [Channels]: { [Step]: string } } } = { tiprack: { single: { one: require('./images/step-1-tiprack-single@3x.png'), @@ -112,31 +112,53 @@ const DIAGRAMS_BOTTOM: {[TypeKey]: {[Channels]: {[Step]: string}}} = { }, } -const INSTRUCTIONS: {[TypeKey]: {[Channels]: {[Step]: string | React.Node}}} = { +const INSTRUCTIONS: { + [TypeKey]: { [Channels]: { [Step]: string | React.Node } }, +} = { tiprack: { single: { one: 'Jog pipette until it is centered above tip A1.', - two: (

Jog pipette until it is flush with the top of the tip.

), + two: ( +

+ Jog pipette until it is flush with the top of the + tip. +

+ ), }, multi: { one: 'Jog pipette until it is centered above tips in column 1.', - two: (

Jog pipette until it is flush with the top of the tips.

), + two:

Jog pipette until it is flush with the top of the tips.

, }, }, trough: { single: { one: 'Jog pipette until tip is centered by the back of trough A1.', - two: (

Jog pipette tip until it is flush with the top of the trough.

), + two: ( +

+ Jog pipette tip until it is flush with the top of the + trough. +

+ ), }, multi: { one: 'Jog pipette until tips are centered above trough A1.', - two: (

Jog pipette tips until they are flush with the top of the trough.

), + two: ( +

+ Jog pipette tips until they are flush with the top of + the trough. +

+ ), }, }, tuberack: { single: { one: 'Jog pipette until tip is centered above tube A1.', - two: (

Jog pipette tip until it is flush with the top of the tube.

), + two: ( +

+ Jog pipette tip until it is flush with the top of the + tube. +

+ ), }, multi: { one: 'warning: you can not use a multichannel pipette with a tube rack', @@ -146,50 +168,93 @@ const INSTRUCTIONS: {[TypeKey]: {[Channels]: {[Step]: string | React.Node}}} = { plate96: { single: { one: 'Jog pipette until tip is centered above well A1.', - two: (

Jog pipette tip until it is flush with the top of the well.

), + two: ( +

+ Jog pipette tip until it is flush with the top of the + well. +

+ ), }, multi: { one: 'Jog pipette until tips are centered above the wells in column 1.', - two: (

Jog pipette tips until they are flush with the top of the wells.

), + two: ( +

+ Jog pipette tips until they are flush with the top of + the wells. +

+ ), }, }, plate384: { single: { one: 'Jog pipette until tip is centered above well A1.', - two: (

Jog pipette tip until it is flush with the top of the well.

), + two: ( +

+ Jog pipette tip until it is flush with the top of the + well. +

+ ), }, multi: { - one: 'Jog pipette until tips are centered above the wells indicated in column 1.', - two: (

Jog pipette tips until they are flush with the top of the wells.

), + one: + 'Jog pipette until tips are centered above the wells indicated in column 1.', + two: ( +

+ Jog pipette tips until they are flush with the top of + the wells. +

+ ), }, }, } -const INSTRUCTIONS_BOTTOM: {[TypeKey]: {[Channels]: {[Step]: string | React.Node}}} = { +const INSTRUCTIONS_BOTTOM: { + [TypeKey]: { [Channels]: { [Step]: string | React.Node } }, +} = { tiprack: { single: { one: 'Jog pipette until it is centered above tip A1.', - two: (

Jog pipette until it is flush with the top of the tip.

), + two: ( +

+ Jog pipette until it is flush with the top of the + tip. +

+ ), }, multi: { one: 'Jog pipette until it is centered above tips in column 1.', - two: (

Jog pipette until it is flush with the top of the tips.

), + two:

Jog pipette until it is flush with the top of the tips.

, }, }, trough: { single: { one: 'Jog pipette until tip is centered by the back of trough A1.', - two: (

Jog pipette until the tip is just barely touching the bottom.

), + two: ( +

+ Jog pipette until the tip is just barely touching the + bottom. +

+ ), }, multi: { one: 'Jog pipette until tips are centered above trough A1.', - two: (

Jog pipette until tips are just barely touching the bottom.

), + two: ( +

+ Jog pipette until tips are just barely touching the + bottom. +

+ ), }, }, tuberack: { single: { one: 'Jog pipette until tip is centered above tube A1.', - two: (

Jog pipette until the tip is just barely touching the bottom.

), + two: ( +

+ Jog pipette until the tip is just barely touching the + bottom. +

+ ), }, multi: { one: 'warning: you can not use a multichannel pipette with a tube rack', @@ -199,26 +264,47 @@ const INSTRUCTIONS_BOTTOM: {[TypeKey]: {[Channels]: {[Step]: string | React.Node plate96: { single: { one: 'Jog pipette until tip is centered above well A1.', - two: (

Jog pipette until the tip is just barely touching the bottom.

), + two: ( +

+ Jog pipette until the tip is just barely touching the + bottom. +

+ ), }, multi: { one: 'Jog pipette until tips are centered above the wells in column 1.', - two: (

Jog pipette until tips are just barely touching the bottom.

), + two: ( +

+ Jog pipette until tips are just barely touching the + bottom. +

+ ), }, }, plate384: { single: { one: 'Jog pipette until tip is centered above well A1.', - two: (

Jog pipette until the tip is just barely touching the bottom.

), + two: ( +

+ Jog pipette until the tip is just barely touching the + bottom. +

+ ), }, multi: { - one: 'Jog pipette until tips are centered above the wells indicated in column 1.', - two: (

Jog pipette until tips are just barely touching the bottom.

), + one: + 'Jog pipette until tips are centered above the wells indicated in column 1.', + two: ( +

+ Jog pipette until tips are just barely touching the + bottom. +

+ ), }, }, } -export function getDiagramSrc (props: LabwareCalibrationProps) { +export function getDiagramSrc(props: LabwareCalibrationProps) { const typeKey = getTypeKey(props) const channelsKey = getChannelsKey(props) if (props.calibrateToBottom) { @@ -227,7 +313,7 @@ export function getDiagramSrc (props: LabwareCalibrationProps) { return DIAGRAMS[typeKey][channelsKey] } -export function getInstructionsByType (props: LabwareCalibrationProps) { +export function getInstructionsByType(props: LabwareCalibrationProps) { const typeKey = getTypeKey(props) const channelsKey = getChannelsKey(props) @@ -237,8 +323,8 @@ export function getInstructionsByType (props: LabwareCalibrationProps) { return INSTRUCTIONS[typeKey][channelsKey] } -function getTypeKey (props: LabwareCalibrationProps) { - const {type, isTiprack} = props +function getTypeKey(props: LabwareCalibrationProps) { + const { type, isTiprack } = props let typeKey if (isTiprack) { typeKey = 'tiprack' @@ -254,10 +340,10 @@ function getTypeKey (props: LabwareCalibrationProps) { return typeKey } -function getChannelsKey (props: LabwareCalibrationProps) { - const {calibrator: {channels}} = props - const channelsKey = channels === 8 - ? 'multi' - : 'single' +function getChannelsKey(props: LabwareCalibrationProps) { + const { + calibrator: { channels }, + } = props + const channelsKey = channels === 8 ? 'multi' : 'single' return channelsKey } diff --git a/app/src/components/CalibratePanel/LabwareGroup.js b/app/src/components/CalibratePanel/LabwareGroup.js index 141fc0c08b5..ce7c521180f 100644 --- a/app/src/components/CalibratePanel/LabwareGroup.js +++ b/app/src/components/CalibratePanel/LabwareGroup.js @@ -1,10 +1,8 @@ // @flow -import {connect} from 'react-redux' -import {SidePanelGroup} from '@opentrons/components' +import { connect } from 'react-redux' +import { SidePanelGroup } from '@opentrons/components' -import { - selectors as robotSelectors, -} from '../../robot' +import { selectors as robotSelectors } from '../../robot' const TITLE = 'Labware Calibration' @@ -15,7 +13,7 @@ type StateProps = { export default connect(mapStateToProps)(SidePanelGroup) -function mapStateToProps (state): StateProps { +function mapStateToProps(state): StateProps { const isRunning = robotSelectors.getIsRunning(state) const disabled = isRunning diff --git a/app/src/components/CalibratePanel/LabwareList.js b/app/src/components/CalibratePanel/LabwareList.js index e4a177b2b5e..94bdfe07613 100644 --- a/app/src/components/CalibratePanel/LabwareList.js +++ b/app/src/components/CalibratePanel/LabwareList.js @@ -1,15 +1,18 @@ // @flow import * as React from 'react' -import {connect} from 'react-redux' -import {withRouter} from 'react-router' +import { connect } from 'react-redux' +import { withRouter } from 'react-router' -import {TitledList} from '@opentrons/components' +import { TitledList } from '@opentrons/components' import LabwareListItem from './LabwareListItem' -import {selectors as robotSelectors, actions as robotActions} from '../../robot' +import { + selectors as robotSelectors, + actions as robotActions, +} from '../../robot' -import type {State, Dispatch} from '../../types' -import type {Labware, Mount} from '../../robot' +import type { State, Dispatch } from '../../types' +import type { Labware, Mount } from '../../robot' type SP = {| disabled: boolean, @@ -18,7 +21,7 @@ type SP = {| _deckPopulated: boolean, |} -type DP = {|dispatch: Dispatch|} +type DP = {| dispatch: Dispatch |} type Props = { labware: Array, @@ -34,8 +37,8 @@ export default withRouter( )(LabwareList) ) -function LabwareList (props: Props) { - const {labware, disabled, setLabware} = props +function LabwareList(props: Props) { + const { labware, disabled, setLabware } = props return ( @@ -52,7 +55,7 @@ function LabwareList (props: Props) { ) } -function mapStateToProps (state: State): SP { +function mapStateToProps(state: State): SP { return { labware: robotSelectors.getNotTipracks(state), disabled: !robotSelectors.getTipracksConfirmed(state), @@ -61,9 +64,9 @@ function mapStateToProps (state: State): SP { } } -function mergeProps (stateProps: SP, dispatchProps: DP): Props { - const {labware, disabled, _calibrator, _deckPopulated} = stateProps - const {dispatch} = dispatchProps +function mergeProps(stateProps: SP, dispatchProps: DP): Props { + const { labware, disabled, _calibrator, _deckPopulated } = stateProps + const { dispatch } = dispatchProps return { labware, diff --git a/app/src/components/CalibratePanel/LabwareListItem.js b/app/src/components/CalibratePanel/LabwareListItem.js index 2ae471c16ec..6793d993f8c 100644 --- a/app/src/components/CalibratePanel/LabwareListItem.js +++ b/app/src/components/CalibratePanel/LabwareListItem.js @@ -1,9 +1,9 @@ // @flow import * as React from 'react' -import type {Labware} from '../../robot' +import type { Labware } from '../../robot' -import {ListItem} from '@opentrons/components' +import { ListItem } from '@opentrons/components' import styles from './styles.css' type LabwareItemProps = { @@ -13,7 +13,7 @@ type LabwareItemProps = { type Props = Labware & LabwareItemProps -export default function LabwareListItem (props: Props) { +export default function LabwareListItem(props: Props) { const { type, slot, @@ -25,9 +25,7 @@ export default function LabwareListItem (props: Props) { } = props const url = `/calibrate/labware/${slot}` - const iconName = confirmed - ? 'check-circle' - : 'checkbox-blank-circle-outline' + const iconName = confirmed ? 'check-circle' : 'checkbox-blank-circle-outline' return ( )} - - {type} - + {type}
) diff --git a/app/src/components/CalibratePanel/PipetteList.js b/app/src/components/CalibratePanel/PipetteList.js index 8c16fbf479a..366d701d88c 100644 --- a/app/src/components/CalibratePanel/PipetteList.js +++ b/app/src/components/CalibratePanel/PipetteList.js @@ -1,7 +1,7 @@ // @flow import React from 'react' -import {connect} from 'react-redux' -import {withRouter} from 'react-router' +import { connect } from 'react-redux' +import { withRouter } from 'react-router' import { constants as robotConstants, @@ -9,7 +9,7 @@ import { type Pipette, } from '../../robot' -import {TitledList} from '@opentrons/components' +import { TitledList } from '@opentrons/components' import PipetteListItem from './PipetteListItem' type Props = { @@ -21,24 +21,24 @@ const TITLE = 'Pipette Calibration' export default withRouter(connect(mapStateToProps)(PipetteList)) -function PipetteList (props: Props) { - const {pipettes, isRunning} = props +function PipetteList(props: Props) { + const { pipettes, isRunning } = props return ( - {robotConstants.PIPETTE_MOUNTS.map((mount) => ( + {robotConstants.PIPETTE_MOUNTS.map(mount => ( i.mount === mount)} + pipette={pipettes.find(i => i.mount === mount)} /> ))} ) } -function mapStateToProps (state): Props { +function mapStateToProps(state): Props { return { pipettes: robotSelectors.getPipettes(state), isRunning: robotSelectors.getIsRunning(state), diff --git a/app/src/components/CalibratePanel/PipetteListItem.js b/app/src/components/CalibratePanel/PipetteListItem.js index 6c70950667c..7360e38ec41 100644 --- a/app/src/components/CalibratePanel/PipetteListItem.js +++ b/app/src/components/CalibratePanel/PipetteListItem.js @@ -2,12 +2,9 @@ import React from 'react' import capitalize from 'lodash/capitalize' -import { - ListItem, - type IconName, -} from '@opentrons/components' +import { ListItem, type IconName } from '@opentrons/components' -import type {Mount, Pipette} from '../../robot' +import type { Mount, Pipette } from '../../robot' import styles from './styles.css' type Props = { @@ -16,13 +13,11 @@ type Props = { pipette: ?Pipette, } -export default function PipetteListItem (props: Props) { - const {isRunning, mount, pipette} = props +export default function PipetteListItem(props: Props) { + const { isRunning, mount, pipette } = props const confirmed = pipette && pipette.probed const isDisabled = !pipette || isRunning - const url = !isDisabled - ? `/calibrate/pipettes/${mount}` - : '#' + const url = !isDisabled ? `/calibrate/pipettes/${mount}` : '#' const iconName: IconName = confirmed ? 'check-circle' @@ -32,9 +27,7 @@ export default function PipetteListItem (props: Props) { ? `${capitalize(pipette.channels === 8 ? 'multi' : 'single')}-channel` : 'N/A' - const name = pipette - ? pipette.name - : 'N/A' + const name = pipette ? pipette.name : 'N/A' return ( , @@ -34,8 +37,8 @@ export default withRouter( )(TipRackList) ) -function TipRackList (props: Props) { - const {tipracks, disabled, setLabware} = props +function TipRackList(props: Props) { + const { tipracks, disabled, setLabware } = props return ( @@ -52,7 +55,7 @@ function TipRackList (props: Props) { ) } -function mapStateToProps (state: State): SP { +function mapStateToProps(state: State): SP { return { tipracks: robotSelectors.getTipracks(state), disabled: robotSelectors.getTipracksConfirmed(state), @@ -61,9 +64,9 @@ function mapStateToProps (state: State): SP { } } -function mergeProps (stateProps: SP, dispatchProps: DP): Props { - const {tipracks, disabled, _calibrator, _deckPopulated} = stateProps - const {dispatch} = dispatchProps +function mergeProps(stateProps: SP, dispatchProps: DP): Props { + const { tipracks, disabled, _calibrator, _deckPopulated } = stateProps + const { dispatch } = dispatchProps return { tipracks, diff --git a/app/src/components/CalibratePanel/index.js b/app/src/components/CalibratePanel/index.js index 3005fda6c95..056934048d1 100644 --- a/app/src/components/CalibratePanel/index.js +++ b/app/src/components/CalibratePanel/index.js @@ -1,16 +1,16 @@ // @flow import * as React from 'react' -import {SidePanel} from '@opentrons/components' +import { SidePanel } from '@opentrons/components' import PipetteList from './PipetteList' import LabwareGroup from './LabwareGroup' import TipRackList from './TipRackList' import LabwareList from './LabwareList' import styles from './styles.css' -export default function CalibratePanel () { +export default function CalibratePanel() { return ( - +
diff --git a/app/src/components/CalibrationInfoBox.js b/app/src/components/CalibrationInfoBox.js index ada6bbe7d76..4faab226c46 100644 --- a/app/src/components/CalibrationInfoBox.js +++ b/app/src/components/CalibrationInfoBox.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react' import classnames from 'classnames' -import {Icon} from '@opentrons/components' +import { Icon } from '@opentrons/components' import styles from './calibration-info.css' type Props = { @@ -11,20 +11,16 @@ type Props = { children: React.Node, } -export default function CalibrationInfoBox (props: Props) { - const {className, confirmed, title, children} = props +export default function CalibrationInfoBox(props: Props) { + const { className, confirmed, title, children } = props - const iconName = confirmed - ? 'check-circle' - : 'checkbox-blank-circle-outline' + const iconName = confirmed ? 'check-circle' : 'checkbox-blank-circle-outline' return (
-

- {title} -

+

{title}

{children}
diff --git a/app/src/components/CalibrationInfoContent.js b/app/src/components/CalibrationInfoContent.js index bc72a962808..f2c0859f68c 100644 --- a/app/src/components/CalibrationInfoContent.js +++ b/app/src/components/CalibrationInfoContent.js @@ -7,17 +7,13 @@ type Props = { rightChildren?: React.Node, } -export default function CalibrationInfoContent (props: Props) { - const {leftChildren, rightChildren} = props +export default function CalibrationInfoContent(props: Props) { + const { leftChildren, rightChildren } = props return (
-
- {leftChildren} -
-
- {rightChildren} -
+
{leftChildren}
+
{rightChildren}
) } diff --git a/app/src/components/CenteredContent.js b/app/src/components/CenteredContent.js index 18273c00fbc..03e8e82db51 100644 --- a/app/src/components/CenteredContent.js +++ b/app/src/components/CenteredContent.js @@ -4,9 +4,9 @@ import * as React from 'react' import styles from './CenteredContent.css' -type Props = {children: React.Node} +type Props = { children: React.Node } -export default function CenteredContent (props: Props) { +export default function CenteredContent(props: Props) { return (
{props.children}
diff --git a/app/src/components/ChangePipette/ConfirmPipette.js b/app/src/components/ChangePipette/ConfirmPipette.js index 0f0dfce057c..5f324c46ad6 100644 --- a/app/src/components/ChangePipette/ConfirmPipette.js +++ b/app/src/components/ChangePipette/ConfirmPipette.js @@ -1,13 +1,13 @@ // @flow import * as React from 'react' import cx from 'classnames' -import {Link} from 'react-router-dom' +import { Link } from 'react-router-dom' -import {Icon, PrimaryButton, ModalPage} from '@opentrons/components' -import {getPipetteChannelsByDisplayName} from '@opentrons/shared-data' +import { Icon, PrimaryButton, ModalPage } from '@opentrons/components' +import { getPipetteChannelsByDisplayName } from '@opentrons/shared-data' -import type {ChangePipetteProps} from './types' -import {getDiagramSrc} from './InstructionStep' +import type { ChangePipetteProps } from './types' +import { getDiagramSrc } from './InstructionStep' import styles from './styles.css' const EXIT_BUTTON_MESSAGE = 'exit pipette setup' @@ -15,8 +15,8 @@ const EXIT_BUTTON_MESSAGE_WRONG = 'keep pipette and exit setup' // note: direction prop is not valid inside this component // display messages based on presence of wantedPipetteName and actualPipette -export default function ConfirmPipette (props: ChangePipetteProps) { - const {success, attachedWrong, actualPipette} = props +export default function ConfirmPipette(props: ChangePipetteProps) { + const { success, attachedWrong, actualPipette } = props return ( - {!success && ( - - )} - {success && !actualPipette && ( - - )} + {!success && } + {success && !actualPipette && } ) } -function Status (props: ChangePipetteProps) { - const {displayName, wantedPipetteName, attachedWrong, success} = props +function Status(props: ChangePipetteProps) { + const { displayName, wantedPipetteName, attachedWrong, success } = props const iconName = success ? 'check-circle' : 'close-circle' const iconClass = cx(styles.confirm_icon, { [styles.success]: success, @@ -59,9 +55,7 @@ function Status (props: ChangePipetteProps) { ? `Incorrect pipette attached (${displayName})` : `Unable to detect ${wantedPipetteName || ''}.` } else { - message = success - ? 'Pipette is detached' - : 'Pipette is not detached' + message = success ? 'Pipette is detached' : 'Pipette is not detached' } return ( @@ -72,14 +66,15 @@ function Status (props: ChangePipetteProps) { ) } -function StatusDetails (props: ChangePipetteProps) { - const {success, attachedWrong, wantedPipetteName, actualPipette} = props +function StatusDetails(props: ChangePipetteProps) { + const { success, attachedWrong, wantedPipetteName, actualPipette } = props if (!success) { if (wantedPipetteName && attachedWrong) { return (

- The attached pipette does not match the {wantedPipetteName} pipette you had originally selected. + The attached pipette does not match the {wantedPipetteName} pipette + you had originally selected.

) } @@ -97,7 +92,8 @@ function StatusDetails (props: ChangePipetteProps) { })} />

- Check again to ensure that white connector tab is plugged into pipette. + Check again to ensure that white connector tab is plugged into + pipette.

) @@ -106,7 +102,8 @@ function StatusDetails (props: ChangePipetteProps) { if (actualPipette) { return (

- Check again to ensure that pipette is unplugged and entirely detached from robot. + Check again to ensure that pipette is unplugged and entirely detached + from robot.

) } @@ -115,7 +112,7 @@ function StatusDetails (props: ChangePipetteProps) { return null } -function AttachAnotherButton (props: ChangePipetteProps) { +function AttachAnotherButton(props: ChangePipetteProps) { return ( - ) + return } -function ExitButton (props: ChangePipetteProps) { - const {exit, attachedWrong} = props +function ExitButton(props: ChangePipetteProps) { + const { exit, attachedWrong } = props const children = attachedWrong ? EXIT_BUTTON_MESSAGE_WRONG : EXIT_BUTTON_MESSAGE return ( - + {children} - + ) } diff --git a/app/src/components/ChangePipette/ExitAlertModal.js b/app/src/components/ChangePipette/ExitAlertModal.js index 138d3cdbe55..82cce28c2dd 100644 --- a/app/src/components/ChangePipette/ExitAlertModal.js +++ b/app/src/components/ChangePipette/ExitAlertModal.js @@ -1,8 +1,8 @@ // @flow import * as React from 'react' -import {AlertModal} from '@opentrons/components' -import {Portal} from '../portal' +import { AlertModal } from '@opentrons/components' +import { Portal } from '../portal' type Props = { back: () => mixed, @@ -13,16 +13,16 @@ const HEADING = 'Are you sure you want to go back?' const CANCEL_TEXT = 'cancel' const EXIT_TEXT = 'exit' -export default function ExitAlertModal (props: Props) { - const {back, exit} = props +export default function ExitAlertModal(props: Props) { + const { back, exit } = props return ( diff --git a/app/src/components/ChangePipette/InstructionStep.js b/app/src/components/ChangePipette/InstructionStep.js index 04e0364c174..381805cf3d1 100644 --- a/app/src/components/ChangePipette/InstructionStep.js +++ b/app/src/components/ChangePipette/InstructionStep.js @@ -1,9 +1,9 @@ // @flow import * as React from 'react' -import type {PipetteChannels} from '@opentrons/shared-data' -import type {Mount} from '../../robot' -import type {Direction} from './types' +import type { PipetteChannels } from '@opentrons/shared-data' +import type { Mount } from '../../robot' +import type { Direction } from './types' import screwdriverSrc from './images/screwdriver.svg' import styles from './styles.css' @@ -26,24 +26,26 @@ type Channels = 'single' | 'multi' // TODO(mc, 2018-04-06): there must be a better way... // this object is way nicer than a giant if/else ladder though -const DIAGRAMS: {[Direction]: {[Mount]: {[Channels]: {[Diagram]: string}}}} = { +const DIAGRAMS: { + [Direction]: { [Mount]: { [Channels]: { [Diagram]: string } } }, +} = { attach: { left: { - 'single': { + single: { screws: require('./images/attach-left-single-screws@3x.png'), tab: require('./images/attach-left-single-tab@3x.png'), }, - 'multi': { + multi: { screws: require('./images/attach-left-multi-screws@3x.png'), tab: require('./images/attach-left-multi-tab@3x.png'), }, }, right: { - 'single': { + single: { screws: require('./images/attach-right-single-screws@3x.png'), tab: require('./images/attach-right-single-tab@3x.png'), }, - 'multi': { + multi: { screws: require('./images/attach-right-multi-screws@3x.png'), tab: require('./images/attach-right-multi-tab@3x.png'), }, @@ -51,21 +53,21 @@ const DIAGRAMS: {[Direction]: {[Mount]: {[Channels]: {[Diagram]: string}}}} = { }, detach: { left: { - 'single': { + single: { screws: require('./images/detach-left-single-screws@3x.png'), tab: require('./images/detach-left-single-tab@3x.png'), }, - 'multi': { + multi: { screws: require('./images/detach-left-multi-screws@3x.png'), tab: require('./images/detach-left-multi-tab@3x.png'), }, }, right: { - 'single': { + single: { screws: require('./images/detach-right-single-screws@3x.png'), tab: require('./images/detach-right-single-tab@3x.png'), }, - 'multi': { + multi: { screws: require('./images/detach-right-multi-screws@3x.png'), tab: require('./images/detach-right-multi-tab@3x.png'), }, @@ -73,18 +75,14 @@ const DIAGRAMS: {[Direction]: {[Mount]: {[Channels]: {[Diagram]: string}}}} = { }, } -export default function InstructionStep (props: Props) { - const {diagram} = props +export default function InstructionStep(props: Props) { + const { diagram } = props const diagramSrc = getDiagramSrc(props) return (
- - Step {props.step} - -
- {props.children} -
+ Step {props.step} +
{props.children}
{diagram === 'screws' && ( )} @@ -93,11 +91,9 @@ export default function InstructionStep (props: Props) { ) } -export function getDiagramSrc (props: DiagramProps): string { - const {direction, mount, channels, diagram} = props - const channelsKey = channels === 8 - ? 'multi' - : 'single' +export function getDiagramSrc(props: DiagramProps): string { + const { direction, mount, channels, diagram } = props + const channelsKey = channels === 8 ? 'multi' : 'single' return DIAGRAMS[direction][mount][channelsKey][diagram] } diff --git a/app/src/components/ChangePipette/Instructions.js b/app/src/components/ChangePipette/Instructions.js index f7d6597b48b..9c841f178f9 100644 --- a/app/src/components/ChangePipette/Instructions.js +++ b/app/src/components/ChangePipette/Instructions.js @@ -1,12 +1,16 @@ // @flow import * as React from 'react' -import {Link} from 'react-router-dom' +import { Link } from 'react-router-dom' import capitalize from 'lodash/capitalize' -import type {ChangePipetteProps} from './types' +import type { ChangePipetteProps } from './types' -import {getPipetteChannelsByDisplayName} from '@opentrons/shared-data' -import {ModalPage, PrimaryButton, type ButtonProps} from '@opentrons/components' +import { getPipetteChannelsByDisplayName } from '@opentrons/shared-data' +import { + ModalPage, + PrimaryButton, + type ButtonProps, +} from '@opentrons/components' import PipetteSelection from './PipetteSelection' import InstructionStep from './InstructionStep' import styles from './styles.css' @@ -14,14 +18,14 @@ import styles from './styles.css' const ATTACH_CONFIRM = 'have robot check connection' const DETACH_CONFIRM = 'confirm pipette is detached' -export default function Instructions (props: ChangePipetteProps) { - const {wantedPipetteName, actualPipette, direction, displayName} = props +export default function Instructions(props: ChangePipetteProps) { + const { wantedPipetteName, actualPipette, direction, displayName } = props const titleBar = { ...props, back: wantedPipetteName - ? {onClick: props.back} - : {Component: Link, to: props.exitUrl, children: 'exit'}, + ? { onClick: props.back } + : { Component: Link, to: props.exitUrl, children: 'exit' }, } const heading = `${capitalize(direction)} ${displayName} Pipette` @@ -32,7 +36,6 @@ export default function Instructions (props: ChangePipetteProps) { heading={heading} contentsClassName={styles.modal_contents} > - {!actualPipette && !wantedPipetteName && ( )} @@ -49,8 +52,8 @@ export default function Instructions (props: ChangePipetteProps) { ) } -function Steps (props: ChangePipetteProps) { - const {direction} = props +function Steps(props: ChangePipetteProps) { + const { direction } = props const channels = props.actualPipette ? props.actualPipette.channels : getPipetteChannelsByDisplayName(props.wantedPipetteName) @@ -76,36 +79,27 @@ function Steps (props: ChangePipetteProps) { Attach pipette to mount, starting with screw 1.

) - stepTwo = 'Connect the pipette to robot by pushing in the white connector tab.' + stepTwo = + 'Connect the pipette to robot by pushing in the white connector tab.' } return (
{stepOne} - + {stepTwo}
) } -function CheckButton (props: ButtonProps) { - return ( - - ) +function CheckButton(props: ButtonProps) { + return } diff --git a/app/src/components/ChangePipette/PipetteSelection.js b/app/src/components/ChangePipette/PipetteSelection.js index 98556f721d0..45e5315b64d 100644 --- a/app/src/components/ChangePipette/PipetteSelection.js +++ b/app/src/components/ChangePipette/PipetteSelection.js @@ -1,8 +1,8 @@ // @flow import * as React from 'react' -import {getPipetteDisplayNames} from '@opentrons/shared-data' -import {DropdownField} from '@opentrons/components' +import { getPipetteDisplayNames } from '@opentrons/shared-data' +import { DropdownField } from '@opentrons/components' import styles from './styles.css' const LABEL = 'Select the pipette you wish to attach:' @@ -11,14 +11,12 @@ export type PipetteSelectionProps = { onChange: $PropertyType, 'onChange'>, } -const OPTIONS = getPipetteDisplayNames().map(name => ({name, value: name})) +const OPTIONS = getPipetteDisplayNames().map(name => ({ name, value: name })) -export default function PipetteSelection (props: PipetteSelectionProps) { +export default function PipetteSelection(props: PipetteSelectionProps) { return ( ) diff --git a/app/src/components/ChangePipette/RequestInProgressModal.js b/app/src/components/ChangePipette/RequestInProgressModal.js index 15e061637c4..c6ed96ca6e4 100644 --- a/app/src/components/ChangePipette/RequestInProgressModal.js +++ b/app/src/components/ChangePipette/RequestInProgressModal.js @@ -1,18 +1,18 @@ // @flow import * as React from 'react' -import type {ChangePipetteProps} from './types' -import {SpinnerModalPage} from '@opentrons/components' +import type { ChangePipetteProps } from './types' +import { SpinnerModalPage } from '@opentrons/components' // TODO (ka 2018-4-10): move this component to util/ or at least up a level for reuse for tip probe -export default function RequestInProgressModal (props: ChangePipetteProps) { - let message = props.mount === 'right' - ? 'Right pipette carriage moving' - : 'Left pipette carriage moving' +export default function RequestInProgressModal(props: ChangePipetteProps) { + let message = + props.mount === 'right' + ? 'Right pipette carriage moving' + : 'Left pipette carriage moving' if (props.moveRequest.inProgress) { - message += props.mount === 'right' - ? ' to front and left.' - : ' to front and right.' + message += + props.mount === 'right' ? ' to front and left.' : ' to front and right.' } else if (props.homeRequest.inProgress) { message += ' up.' } @@ -26,7 +26,6 @@ export default function RequestInProgressModal (props: ChangePipetteProps) { disabled: true, }, }} - message={message} /> ) diff --git a/app/src/components/ChangePipette/index.js b/app/src/components/ChangePipette/index.js index d305fb31017..31b86903703 100644 --- a/app/src/components/ChangePipette/index.js +++ b/app/src/components/ChangePipette/index.js @@ -1,17 +1,20 @@ // @flow import * as React from 'react' -import {connect} from 'react-redux' -import {push, goBack} from 'react-router-redux' -import {Switch, Route, withRouter, type Match} from 'react-router' -import {getPipetteModelSpecs, getPipetteDisplayNames} from '@opentrons/shared-data' +import { connect } from 'react-redux' +import { push, goBack } from 'react-router-redux' +import { Switch, Route, withRouter, type Match } from 'react-router' +import { + getPipetteModelSpecs, + getPipetteDisplayNames, +} from '@opentrons/shared-data' -import type {PipetteModelSpecs} from '@opentrons/shared-data' -import type {State, Dispatch} from '../../types' -import type {Mount} from '../../robot' -import type {Robot} from '../../discovery' -import type {Direction, ChangePipetteProps} from './types' +import type { PipetteModelSpecs } from '@opentrons/shared-data' +import type { State, Dispatch } from '../../types' +import type { Mount } from '../../robot' +import type { Robot } from '../../discovery' +import type { Direction, ChangePipetteProps } from './types' -import type {RobotHome, RobotMove} from '../../http-api-client' +import type { RobotHome, RobotMove } from '../../http-api-client' import { home, @@ -25,7 +28,7 @@ import { import ClearDeckAlertModal from '../ClearDeckAlertModal' import ExitAlertModal from './ExitAlertModal' -import type {PipetteSelectionProps} from './PipetteSelection' +import type { PipetteSelectionProps } from './PipetteSelection' import Instructions from './Instructions' import ConfirmPipette from './ConfirmPipette' import RequestInProgressModal from './RequestInProgressModal' @@ -43,17 +46,26 @@ const RE_MOUNT = '(left|right)' const RE_NAME = `(${getPipetteDisplayNames().join('|')})` const ConnectedChangePipetteRouter = withRouter( - connect(makeMapStateToProps, mapDispatchToProps)(ChangePipetteRouter) + connect( + makeMapStateToProps, + mapDispatchToProps + )(ChangePipetteRouter) ) -export default function ChangePipette (props: Props) { - const {robot, parentUrl, match: {path}} = props +export default function ChangePipette(props: Props) { + const { + robot, + parentUrl, + match: { path }, + } = props return ( { - const {match: {params, url: baseUrl}} = propsWithMount + render={propsWithMount => { + const { + match: { params, url: baseUrl }, + } = propsWithMount const mount: Mount = (params.mount: any) const wantedPipetteName = params.name || null @@ -106,8 +118,8 @@ type DP = { confirmPipette: () => mixed, } -function ChangePipetteRouter (props: ChangePipetteProps) { - const {baseUrl, confirmUrl, exitUrl, moveRequest, homeRequest} = props +function ChangePipetteRouter(props: ChangePipetteProps) { + const { baseUrl, confirmUrl, exitUrl, moveRequest, homeRequest } = props const clearDeckProps = { cancelText: 'cancel', continueText: 'move pipette to front', @@ -116,10 +128,10 @@ function ChangePipetteRouter (props: ChangePipetteProps) { } if (!moveRequest.inProgress && !moveRequest.response) { return ( - + {props.actualPipette && (

- Detaching a pipette will also clear its related calibration data + Detaching a pipette will also clear its related calibration data

)}
@@ -127,52 +139,38 @@ function ChangePipetteRouter (props: ChangePipetteProps) { } if (moveRequest.inProgress || homeRequest.inProgress) { - return () + return } return ( - ( - - )} /> - ( - - )} /> - ( - - )} /> + } /> + } /> + } /> ) } -function makeMapStateToProps () { +function makeMapStateToProps() { const getRobotMove = makeGetRobotMove() const getRobotHome = makeGetRobotHome() const getRobotPipettes = makeGetRobotPipettes() return (state: State, ownProps: OP): SP => { - const {mount, wantedPipetteName} = ownProps + const { mount, wantedPipetteName } = ownProps const pipettes = getRobotPipettes(state, ownProps.robot).response const model = pipettes && pipettes[mount] && pipettes[mount].model const actualPipette = model ? getPipetteModelSpecs(model) : null const direction = actualPipette ? 'detach' : 'attach' - const success = ( + const success = (actualPipette && actualPipette.displayName === wantedPipetteName) || (!actualPipette && !wantedPipetteName) - ) - const attachedWrong = !!( - !success && - wantedPipetteName && - actualPipette - ) + const attachedWrong = !!(!success && wantedPipetteName && actualPipette) - const displayName = ( - (actualPipette && actualPipette.displayName) || - (wantedPipetteName) || - '' - ) + const displayName = + (actualPipette && actualPipette.displayName) || wantedPipetteName || '' return { actualPipette, @@ -186,22 +184,25 @@ function makeMapStateToProps () { } } -function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP { - const {confirmUrl, parentUrl, baseUrl, robot, mount} = ownProps +function mapDispatchToProps(dispatch: Dispatch, ownProps: OP): DP { + const { confirmUrl, parentUrl, baseUrl, robot, mount } = ownProps const disengage = () => dispatch(disengagePipetteMotors(robot, mount)) - const checkPipette = () => disengage() - .then(() => dispatch(fetchPipettes(robot, true))) + const checkPipette = () => + disengage().then(() => dispatch(fetchPipettes(robot, true))) return { checkPipette, - exit: () => dispatch(home(robot, mount)) - .then(() => dispatch(push(parentUrl))), + exit: () => + dispatch(home(robot, mount)).then(() => dispatch(push(parentUrl))), back: () => dispatch(goBack()), - onPipetteSelect: (evt) => dispatch(push(`${baseUrl}/${evt.target.value}`)), - moveToFront: () => dispatch(moveRobotTo(robot, { - mount, - position: 'change_pipette', - })).then(disengage), + onPipetteSelect: evt => dispatch(push(`${baseUrl}/${evt.target.value}`)), + moveToFront: () => + dispatch( + moveRobotTo(robot, { + mount, + position: 'change_pipette', + }) + ).then(disengage), confirmPipette: () => checkPipette().then(() => dispatch(push(confirmUrl))), } } diff --git a/app/src/components/ChangePipette/types.js b/app/src/components/ChangePipette/types.js index 8b3fe5d9be9..cfe50dcfc73 100644 --- a/app/src/components/ChangePipette/types.js +++ b/app/src/components/ChangePipette/types.js @@ -1,10 +1,10 @@ // @flow -import type {PipetteModelSpecs} from '@opentrons/shared-data' -import type {Mount} from '../../robot' +import type { PipetteModelSpecs } from '@opentrons/shared-data' +import type { Mount } from '../../robot' -import type {RobotMove, RobotHome} from '../../http-api-client' +import type { RobotMove, RobotHome } from '../../http-api-client' -import type {PipetteSelectionProps} from './PipetteSelection' +import type { PipetteSelectionProps } from './PipetteSelection' export type Direction = 'attach' | 'detach' diff --git a/app/src/components/ClearDeckAlertModal/index.js b/app/src/components/ClearDeckAlertModal/index.js index 54b69163f37..bf765243b91 100644 --- a/app/src/components/ClearDeckAlertModal/index.js +++ b/app/src/components/ClearDeckAlertModal/index.js @@ -1,9 +1,9 @@ // @flow import * as React from 'react' -import {Link} from 'react-router-dom' +import { Link } from 'react-router-dom' -import {AlertModal} from '@opentrons/components' -import {Portal} from '../portal' +import { AlertModal } from '@opentrons/components' +import { Portal } from '../portal' import styles from './styles.css' type Props = { @@ -16,15 +16,26 @@ type Props = { } const HEADING = 'Before continuing, remove from deck:' -export default function ClearDeckAlertModal (props: Props) { - const {onContinueClick, onCancelClick, parentUrl, cancelText, continueText} = props +export default function ClearDeckAlertModal(props: Props) { + const { + onContinueClick, + onCancelClick, + parentUrl, + cancelText, + continueText, + } = props return ( {props.children && (
-

- Note: -

+

Note:

{props.children}
- ) - } + )}
) diff --git a/app/src/components/ConfigurePipette/ConfigErrorBanner.js b/app/src/components/ConfigurePipette/ConfigErrorBanner.js index bf53f663996..7e79fc7f566 100644 --- a/app/src/components/ConfigurePipette/ConfigErrorBanner.js +++ b/app/src/components/ConfigurePipette/ConfigErrorBanner.js @@ -1,28 +1,28 @@ // @flow import * as React from 'react' -import {AlertItem} from '@opentrons/components' +import { AlertItem } from '@opentrons/components' import styles from './styles.css' type Props = { message: ?string, } -type State = {dismissed: boolean} +type State = { dismissed: boolean } const TITLE = 'Error updating pipette settings' export default class ConfigBanner extends React.Component { - constructor (props: Props) { + constructor(props: Props) { super(props) - this.state = {dismissed: false} + this.state = { dismissed: false } } - render () { - const {message} = this.props + render() { + const { message } = this.props const isVisible = message && !this.state.dismissed if (!isVisible) return null return ( this.setState({dismissed: true})} + onCloseClick={() => this.setState({ dismissed: true })} title={TITLE} >

diff --git a/app/src/components/ConfigurePipette/ConfigForm.js b/app/src/components/ConfigurePipette/ConfigForm.js index d73b0e84845..3dcf6e8a71e 100644 --- a/app/src/components/ConfigurePipette/ConfigForm.js +++ b/app/src/components/ConfigurePipette/ConfigForm.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react' -import {Link} from 'react-router-dom' -import {Formik, Form} from 'formik' +import { Link } from 'react-router-dom' +import { Formik, Form } from 'formik' import startCase from 'lodash/startCase' import mapValues from 'lodash/mapValues' @@ -13,7 +13,7 @@ import set from 'lodash/set' import isEmpty from 'lodash/isEmpty' import FormButtonBar from './FormButtonBar' -import ConfigFormGroup, {FormColumn} from './ConfigFormGroup' +import ConfigFormGroup, { FormColumn } from './ConfigFormGroup' import type { Pipette, @@ -23,7 +23,7 @@ import type { PipetteConfigRequest, } from '../../http-api-client' -import type {FormValues} from './ConfigFormGroup' +import type { FormValues } from './ConfigFormGroup' export type DisplayFieldProps = PipetteSettingsField & { name: string, @@ -43,7 +43,7 @@ const POWER_KEYS = ['plungerCurrent', 'pickUpCurrent', 'dropTipCurrent'] const TIP_KEYS = ['dropTipSpeed', 'pickUpDistance'] export default class ConfigForm extends React.Component { - getFieldsByKey ( + getFieldsByKey( keys: Array, fields: PipetteConfigFields ): Array { @@ -80,12 +80,12 @@ export default class ConfigForm extends React.Component { handleSubmit = (values: FormValues) => { const params = mapValues(values, v => { - return v === '' ? null : {value: Number(v)} + return v === '' ? null : { value: Number(v) } }) - this.props.updateConfig(this.props.pipette.id, {fields: {...params}}) + this.props.updateConfig(this.props.pipette.id, { fields: { ...params } }) } - getFieldValue ( + getFieldValue( key: string, fields: Array, values: FormValues @@ -104,7 +104,7 @@ export default class ConfigForm extends React.Component { // validate all visible fields with min and max forOwn(fields, (field, name) => { const value = values[name].trim() - const {min, max} = field + const { min, max } = field if (value !== '') { const parsed = Number(value) if (Number.isNaN(parsed)) { @@ -132,8 +132,8 @@ export default class ConfigForm extends React.Component { return errors } - render () { - const {parentUrl} = this.props + render() { + const { parentUrl } = this.props const fields = this.getVisibleFields() const UNKNOWN_KEYS = this.getUnknownKeys() const initialValues = mapValues(fields, f => { @@ -151,7 +151,7 @@ export default class ConfigForm extends React.Component { validate={this.validate} validateOnChange={false} render={formProps => { - const {errors, values} = formProps + const { errors, values } = formProps const disableSubmit = !isEmpty(errors) const handleReset = () => formProps.resetForm(mapValues(values, () => '')) @@ -186,8 +186,8 @@ export default class ConfigForm extends React.Component { children: 'reset all', onClick: handleReset, }, - {children: 'cancel', Component: Link, to: parentUrl}, - {children: 'save', type: 'submit', disabled: disableSubmit}, + { children: 'cancel', Component: Link, to: parentUrl }, + { children: 'save', type: 'submit', disabled: disableSubmit }, ]} /> diff --git a/app/src/components/ConfigurePipette/ConfigFormGroup.js b/app/src/components/ConfigurePipette/ConfigFormGroup.js index a0e6c375b42..db2213d4fbe 100644 --- a/app/src/components/ConfigurePipette/ConfigFormGroup.js +++ b/app/src/components/ConfigurePipette/ConfigFormGroup.js @@ -1,22 +1,22 @@ // @flow import * as React from 'react' -import {Field} from 'formik' -import {FormGroup, InputField} from '@opentrons/components' +import { Field } from 'formik' +import { FormGroup, InputField } from '@opentrons/components' import styles from './styles.css' -import type {DisplayFieldProps} from './ConfigForm' +import type { DisplayFieldProps } from './ConfigForm' type FormColProps = { children: React.Node, className?: string, } -export function FormColumn (props: FormColProps) { +export function FormColumn(props: FormColProps) { return

{props.children}
} -export type FormValues = {[string]: ?string} +export type FormValues = { [string]: ?string } type FormGroupProps = { groupLabel: string, @@ -24,11 +24,11 @@ type FormGroupProps = { formFields: Array, } -export default function ConfigFormGroup (props: FormGroupProps) { - const {groupLabel, groupError, formFields} = props +export default function ConfigFormGroup(props: FormGroupProps) { + const { groupLabel, groupError, formFields } = props const formattedError = groupError && - groupError.split('\n').map(function (item, key) { + groupError.split('\n').map(function(item, key) { return ( {item} @@ -55,8 +55,8 @@ type FormRowProps = { const FIELD_ID_PREFIX = '__PipetteConfig__' const makeId = (name: *): string => `${FIELD_ID_PREFIX}.${name}` -export function ConfigFormRow (props: FormRowProps) { - const {labelFor, label} = props +export function ConfigFormRow(props: FormRowProps) { + const { labelFor, label } = props return (