Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): guide the user through leveling gen2 multis #5348

Merged
merged 12 commits into from
Apr 20, 2020
20 changes: 15 additions & 5 deletions app/src/components/ChangePipette/Instructions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -113,11 +114,20 @@ function Steps(props: Props) {
</div>
)
} else {
stepOne = (
<p>
Attach pipette to mount, <strong>starting with screw 1</strong>.
</p>
)
if (wantedPipette && shouldLevel(wantedPipette)) {
stepOne = (
<p>
<em>Loosely</em> attach pipette to carriage,{' '}
<strong>starting with screw 1</strong>
</p>
)
} else {
stepOne = (
<p>
Attach pipette to mount, <strong>starting with screw 1</strong>.
</p>
)
}
stepTwo =
'Connect the pipette to robot by pushing in the white connector tab.'
}
Expand Down
82 changes: 82 additions & 0 deletions app/src/components/ChangePipette/LevelPipette.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// @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'

const EXIT_BUTTON_MESSAGE = 'confirm pipette is leveled'

type Props = {|
robotName: string,
mount: Mount,
title: string,
subtitle: string,
wantedPipette: PipetteNameSpecs | null,
actualPipette: PipetteModelSpecs | null,
displayName: string,
displayCategory: PipetteDisplayCategory | null,
back: () => mixed,
exit: () => mixed,
|}

function Status(props: Props) {
const { displayName } = props
const iconName = 'check-circle'
const iconClass = cx(styles.confirm_icon, {
[styles.success]: true,
[styles.failure]: false,
})
const message = `${displayName} connected`
return (
<div className={styles.leveling_title}>
<Icon name={iconName} className={iconClass} />
{message}
</div>
)
}

function ExitButton(props: Props) {
Copy link
Contributor

@b-cooper b-cooper Apr 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: because this component is so shallow, I'd probably place the instance of PrimaryButton directly in the LevelPipette component for clarity in this file. This kind of abstraction feels unnecessary. You could probably make the same argument for the Status component. It's not really adding branching logic, just a haven for some variable declarations.

const { exit } = props

return (
<PrimaryButton className={styles.confirm_button} onClick={exit}>
{EXIT_BUTTON_MESSAGE}
</PrimaryButton>
)
}

export function LevelPipette(props: Props) {
const { title, subtitle, displayName, back } = props
return (
<ModalPage
titleBar={{
title: title,
subtitle: subtitle,
back: { onClick: back, disabled: false },
}}
>
<div className={styles.leveling_modal_wrapper}>
<Status {...props} />
<div className={styles.leveling_instruction}>
Next, level the {displayName}
</div>
<div className={styles.leveling_video_wrapper}>
<video width="100%" autoPlay={true} loop={true}>
<source src={require('./videos/calibration.webm')} />
</video>
</div>
<ExitButton {...props} />
</div>
</ModalPage>
)
}
45 changes: 29 additions & 16 deletions app/src/components/ChangePipette/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'

Expand Down Expand Up @@ -143,21 +144,33 @@ export function ChangePipette(props: Props) {

const attachedWrong = Boolean(!success && wantedPipette && actualPipette)

return (
<ConfirmPipette
{...{
...basePropsWithPipettes,
success,
attachedWrong,
tryAgain: () => {
setWantedName(null)
setWizardStep(INSTRUCTIONS)
},
back: () => setWizardStep(INSTRUCTIONS),
exit: homeAndExit,
}}
/>
)
if (success && wantedPipette && shouldLevel(wantedPipette)) {
return (
<LevelPipette
{...{
...basePropsWithPipettes,
back: () => setWizardStep(INSTRUCTIONS),
exit: homeAndExit,
}}
/>
)
} else {
return (
<ConfirmPipette
{...{
...basePropsWithPipettes,
success,
attachedWrong,
tryAgain: () => {
setWantedName(null)
setWizardStep(INSTRUCTIONS)
},
back: () => setWizardStep(INSTRUCTIONS),
exit: homeAndExit,
}}
/>
)
}
}

// this will never be reached
Expand Down
25 changes: 25 additions & 0 deletions app/src/components/ChangePipette/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@
padding: 2rem 1.25rem;
}

.leveling_title {
width: 100%;
display: flex;
align-items: center;
margin-bottom: 50px;
mcous marked this conversation as resolved.
Show resolved Hide resolved
font-weight: var(--fw-semibold);
}

.leveling_instruction {
width: 100%;
margin-bottom: 1.5rem;
}

.leveling_video_wrapper {
max-height: 480px;
mcous marked this conversation as resolved.
Show resolved Hide resolved
margin-bottom: 50px;
}

.leveling_modal_wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}

.confirm_status {
display: flex;
align-items: center;
Expand Down
Binary file not shown.
4 changes: 4 additions & 0 deletions shared-data/js/pipettes.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@ function comparePipettes(sortBy: Array<SortableProps>) {
return 0
}
}

export function shouldLevel(specs: PipetteNameSpecs) {
return specs.displayCategory === 'GEN2' && specs.channels === 8
}
1 change: 1 addition & 0 deletions webpack-config/lib/base-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
rules.handlebars,
rules.fonts,
rules.images,
rules.videos,
],
},

Expand Down
13 changes: 13 additions & 0 deletions webpack-config/lib/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,17 @@ module.exports = {
},
},
},

// videos
videos: {
test: /\.(?:mp4|webm)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]',
outputPath: 'videos',
esModule: false,
},
},
},
}