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
100 changes: 100 additions & 0 deletions app/src/components/ChangePipette/LevelPipette.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// @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,
pipetteModelName: string,
back: () => mixed,
exit: () => mixed,
|}

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

function ExitButton(props: { exit: () => mixed }) {
return (
<PrimaryButton className={styles.confirm_button} onClick={props.exit}>
{EXIT_BUTTON_MESSAGE}
</PrimaryButton>
)
}

function LevelingInstruction(props: { pipetteName: string }) {
return (
<div className={styles.leveling_instruction}>
Next, level the {props.pipetteName}
mcous marked this conversation as resolved.
Show resolved Hide resolved
</div>
)
}

function LevelingVideo(props: { pipetteName: string, mount: Mount }) {
const { pipetteName, mount } = props
return (
<div className={styles.leveling_video_wrapper}>
<video width="100%" autoPlay={true} loop={true} controls={true}>
mcous marked this conversation as resolved.
Show resolved Hide resolved
<source src={require(`./videos/${pipetteName}-${mount}.webm`)} />
</video>
</div>
)
}

export function LevelPipette(props: Props) {
const {
title,
subtitle,
pipetteModelName,
displayName,
mount,
back,
exit,
} = props
return (
<ModalPage
titleBar={{
title: title,
subtitle: subtitle,
back: { onClick: back, disabled: false },
}}
contentsClassName={styles.leveling_modal}
>
<Status pipetteName={displayName} />
<LevelingInstruction pipetteName={displayName} />
<LevelingVideo pipetteName={pipetteModelName} mount={mount} />
mcous marked this conversation as resolved.
Show resolved Hide resolved
<ExitButton exit={exit} />
</ModalPage>
)
}
46 changes: 30 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,34 @@ 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
{...{
pipetteModelName: actualPipette ? actualPipette.name : '',
...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 {
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.
Binary file not shown.
Binary file not shown.
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,
},
},
},
}