diff --git a/app/src/components/ChangePipette/Instructions.js b/app/src/components/ChangePipette/Instructions.js index 6907bc0f19c..350c49c7e53 100644 --- a/app/src/components/ChangePipette/Instructions.js +++ b/app/src/components/ChangePipette/Instructions.js @@ -13,6 +13,7 @@ import type { PipetteModelSpecs, PipetteDisplayCategory, } from '@opentrons/shared-data' +import { shouldLevel } from '@opentrons/shared-data' import type { Mount } from '../../pipettes/types' import type { Direction } from './types' @@ -113,11 +114,20 @@ function Steps(props: Props) { ) } else { - stepOne = ( -

- Attach pipette to mount, starting with screw 1. -

- ) + if (wantedPipette && shouldLevel(wantedPipette)) { + stepOne = ( +

+ Loosely attach pipette to carriage,{' '} + starting with screw 1 +

+ ) + } else { + stepOne = ( +

+ Attach pipette to mount, starting with screw 1. +

+ ) + } stepTwo = 'Connect the pipette to robot by pushing in the white connector tab.' } diff --git a/app/src/components/ChangePipette/LevelPipette.js b/app/src/components/ChangePipette/LevelPipette.js new file mode 100644 index 00000000000..30de5a3ce98 --- /dev/null +++ b/app/src/components/ChangePipette/LevelPipette.js @@ -0,0 +1,108 @@ +// @flow + +import * as React from 'react' +import cx from 'classnames' + +import { Icon, ModalPage, PrimaryButton } from '@opentrons/components' +import styles from './styles.css' + +import type { + PipetteNameSpecs, + PipetteModelSpecs, + PipetteDisplayCategory, +} from '@opentrons/shared-data' + +import type { Mount } from '../../pipettes/types' + +// TODO: i18n +const EXIT_BUTTON_MESSAGE = 'confirm pipette is leveled' +const LEVEL_MESSAGE = (displayName: string) => `Next, level the ${displayName}` +const CONNECTED_MESSAGE = (displayName: string) => `${displayName} connected` + +type Props = {| + robotName: string, + mount: Mount, + title: string, + subtitle: string, + wantedPipette: PipetteNameSpecs | null, + actualPipette: PipetteModelSpecs | null, + displayName: string, + displayCategory: PipetteDisplayCategory | null, + pipetteModelName: string, + back: () => mixed, + exit: () => mixed, +|} + +function Status(props: { displayName: string }) { + const iconName = 'check-circle' + const iconClass = cx(styles.confirm_icon, { + [styles.success]: true, + [styles.failure]: false, + }) + + return ( +
+ + {CONNECTED_MESSAGE(props.displayName)} +
+ ) +} + +function ExitButton(props: { exit: () => mixed }) { + return ( + + {EXIT_BUTTON_MESSAGE} + + ) +} + +function LevelingInstruction(props: { displayName: string }) { + return ( +
+ {LEVEL_MESSAGE(props.displayName)} +
+ ) +} + +function LevelingVideo(props: { pipetteName: string, mount: Mount }) { + const { pipetteName, mount } = props + return ( +
+ +
+ ) +} + +export function LevelPipette(props: Props) { + const { + title, + subtitle, + pipetteModelName, + displayName, + mount, + back, + exit, + } = props + return ( + + + + + + + ) +} diff --git a/app/src/components/ChangePipette/index.js b/app/src/components/ChangePipette/index.js index 3a2fef6f2ef..63dd6d9910c 100644 --- a/app/src/components/ChangePipette/index.js +++ b/app/src/components/ChangePipette/index.js @@ -2,7 +2,7 @@ import * as React from 'react' import { useSelector, useDispatch } from 'react-redux' import last from 'lodash/last' -import { getPipetteNameSpecs } from '@opentrons/shared-data' +import { getPipetteNameSpecs, shouldLevel } from '@opentrons/shared-data' import { useDispatchApiRequest, getRequestById, PENDING } from '../../robot-api' import { getAttachedPipettes } from '../../pipettes' @@ -21,6 +21,7 @@ import { ExitAlertModal } from './ExitAlertModal' import { Instructions } from './Instructions' import { ConfirmPipette } from './ConfirmPipette' import { RequestInProgressModal } from './RequestInProgressModal' +import { LevelPipette } from './LevelPipette' import { ATTACH, DETACH, CLEAR_DECK, INSTRUCTIONS, CONFIRM } from './constants' @@ -143,21 +144,34 @@ export function ChangePipette(props: Props) { const attachedWrong = Boolean(!success && wantedPipette && actualPipette) - return ( - { - setWantedName(null) - setWizardStep(INSTRUCTIONS) - }, - back: () => setWizardStep(INSTRUCTIONS), - exit: homeAndExit, - }} - /> - ) + if (success && wantedPipette && shouldLevel(wantedPipette)) { + return ( + setWizardStep(INSTRUCTIONS), + exit: homeAndExit, + }} + /> + ) + } else { + return ( + { + setWantedName(null) + setWizardStep(INSTRUCTIONS) + }, + back: () => setWizardStep(INSTRUCTIONS), + exit: homeAndExit, + }} + /> + ) + } } // this will never be reached diff --git a/app/src/components/ChangePipette/styles.css b/app/src/components/ChangePipette/styles.css index a6f0f2a552e..44dfbdc67b2 100644 --- a/app/src/components/ChangePipette/styles.css +++ b/app/src/components/ChangePipette/styles.css @@ -27,6 +27,35 @@ padding: 2rem 1.25rem; } +.leveling_title { + width: 100%; + display: flex; + align-items: center; + margin-bottom: 3rem; + font-weight: var(--fw-semibold); +} + +.leveling_instruction { + width: 100%; + margin-bottom: 1.5rem; +} + +.leveling_video_wrapper { + max-height: 30rem; + margin-bottom: 1rem; +} + +.leveling_video { + max-width: 100%; +} + +.leveling_modal { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; +} + .confirm_status { display: flex; align-items: center; diff --git a/app/src/components/ChangePipette/videos/p20_multi_gen2-left.webm b/app/src/components/ChangePipette/videos/p20_multi_gen2-left.webm new file mode 100644 index 00000000000..19f7879ac7b Binary files /dev/null and b/app/src/components/ChangePipette/videos/p20_multi_gen2-left.webm differ diff --git a/app/src/components/ChangePipette/videos/p20_multi_gen2-right.webm b/app/src/components/ChangePipette/videos/p20_multi_gen2-right.webm new file mode 100644 index 00000000000..324c0011a35 Binary files /dev/null and b/app/src/components/ChangePipette/videos/p20_multi_gen2-right.webm differ diff --git a/app/src/components/ChangePipette/videos/p300_multi_gen2-left.webm b/app/src/components/ChangePipette/videos/p300_multi_gen2-left.webm new file mode 100644 index 00000000000..2cbe5233ebc Binary files /dev/null and b/app/src/components/ChangePipette/videos/p300_multi_gen2-left.webm differ diff --git a/app/src/components/ChangePipette/videos/p300_multi_gen2-right.webm b/app/src/components/ChangePipette/videos/p300_multi_gen2-right.webm new file mode 100644 index 00000000000..3475d7be52e Binary files /dev/null and b/app/src/components/ChangePipette/videos/p300_multi_gen2-right.webm differ diff --git a/shared-data/js/pipettes.js b/shared-data/js/pipettes.js index f47a4e94625..cef3357cac8 100644 --- a/shared-data/js/pipettes.js +++ b/shared-data/js/pipettes.js @@ -53,3 +53,7 @@ function comparePipettes(sortBy: Array) { return 0 } } + +export function shouldLevel(specs: PipetteNameSpecs) { + return specs.displayCategory === 'GEN2' && specs.channels === 8 +} diff --git a/webpack-config/lib/base-config.js b/webpack-config/lib/base-config.js index 895a87bd904..7e217f16f93 100644 --- a/webpack-config/lib/base-config.js +++ b/webpack-config/lib/base-config.js @@ -31,6 +31,7 @@ module.exports = { rules.handlebars, rules.fonts, rules.images, + rules.videos, ], }, diff --git a/webpack-config/lib/rules.js b/webpack-config/lib/rules.js index 354ed2f3db6..aa892bcf1ad 100644 --- a/webpack-config/lib/rules.js +++ b/webpack-config/lib/rules.js @@ -116,4 +116,17 @@ module.exports = { }, }, }, + + // videos + videos: { + test: /\.(?:mp4|webm)$/, + use: { + loader: 'file-loader', + options: { + name: '[name].[hash].[ext]', + outputPath: 'videos', + esModule: false, + }, + }, + }, }