diff --git a/app/src/assets/images/lpc_level_probe_with_labware.svg b/app/src/assets/images/lpc_level_probe_with_labware.svg new file mode 100644 index 00000000000..5e75128d9fc --- /dev/null +++ b/app/src/assets/images/lpc_level_probe_with_labware.svg @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/assets/images/lpc_level_probe_with_tip.svg b/app/src/assets/images/lpc_level_probe_with_tip.svg new file mode 100644 index 00000000000..799e78dd3d1 --- /dev/null +++ b/app/src/assets/images/lpc_level_probe_with_tip.svg @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/assets/localization/en/labware_position_check.json b/app/src/assets/localization/en/labware_position_check.json index 58fe6550ddd..f08c465f7fa 100644 --- a/app/src/assets/localization/en/labware_position_check.json +++ b/app/src/assets/localization/en/labware_position_check.json @@ -6,22 +6,27 @@ "applied_offset_data": "Applied Labware Offset data", "apply_offset_data": "Apply labware offset data", "apply_offsets": "apply offsets", - "attach_probe": "Attach probe to pipette", + "attach_probe": "Attach calibration probe", + "backmost": "backmost", + "calibration_probe": "calibration probe", "check_item_in_location": "Check {{item}} in {{location}}", "check_labware_in_slot_title": "Check Labware {{labware_display_name}} in slot {{slot}}", "check_remaining_labware_with_primary_pipette_section": "Check remaining labware with {{primary_mount}} Pipette and tip", + "check_tip_location": "the top of the tip in the A1 position", + "check_well_location": "well A1 on the labware", "clear_all_slots": "Clear all deck slots of labware, leaving modules in place", "clear_all_slots_odd": "Clear all deck slots of labware", "cli_ssh": "Command Line Interface (SSH)", "close_and_apply_offset_data": "Close and apply labware offset data", + "confirm_detached": "Confirm removal", "confirm_pick_up_tip_modal_title": "Did the pipette pick up a tip successfully?", "confirm_pick_up_tip_modal_try_again_text": "No, try again", "confirm_position_and_move": "Confirm position, move to slot {{next_slot}}", "confirm_position_and_pick_up_tip": "Confirm position, pick up tip", "confirm_position_and_return_tip": "Confirm position, return tip to Slot {{next_slot}} and home", - "ensure_nozzle_is_above_tip_odd": "Ensure that the pipette nozzle furthest from you is centered above and level with the top of the tip in the A1 position. If it isn't, tap Move pipette and then jog the pipette until it is properly aligned.", - "ensure_nozzle_is_above_tip_desktop": "Ensure that the pipette nozzle furthest from you is centered above and level with the top of the tip in the A1 position. If it isn't, use the controls below or your keyboard to jog the pipette until it is properly aligned.", - "ensure_tip_is_above_well": "Ensure that the pipette tip furthest from you is centered above and level with well A1 on the labware.", + "detach_probe": "Remove calibration probe", + "ensure_nozzle_position_odd": "Ensure that the {{tip_type}} is centered above and level with {{item_location}}. If it isn't, tap Move pipette and then jog the pipette until it is properly aligned.", + "ensure_nozzle_position_desktop": "Ensure that the {{tip_type}} is centered above and level with {{item_location}}. If it isn't, use the controls below or your keyboard to jog the pipette until it is properly aligned.", "error_modal_header": "Something went wrong", "error_modal_problem_in_app": "There was an error performing Labware Position Check. Please restart the app. If the problem persists, please contact Opentrons Support", "error_modal_problem_on_robot": "There was an error processing your request on the robot", @@ -30,8 +35,7 @@ "exit_screen_subtitle": "If you exit now, all labware offsets will be discarded. This cannot be undone.", "exit_screen_title": "Exit before completing Labware Position Check?", "get_labware_offset_data": "Get Labware Offset Data", - "install_probe_8_channel": "Take the calibration probe from its storage location. Ensure its collar is unlocked. Push the pipette ejector up and press the probe firmly onto the backmost pipette nozzle. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.", - "install_probe_96_channel": "Take the calibration probe from its storage location. Ensure its collar is unlocked. Push the pipette ejector up and press the probe firmly onto the A1 (back left corner) pipette nozzle. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.", + "install_probe": "Take the calibration probe from its storage location. Ensure its collar is fully unlocked. Push the pipette ejector up and press the probe firmly onto the {{location}} pipette nozzle as far as it can go. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.", "jog_controls_adjustment": "Need to make an adjustment?", "jupyter_notebook": "Jupyter Notebook", "labware_display_location_text": "Deck Slot {{slot}}", @@ -70,6 +74,7 @@ "move_to_a1_position": "Move the pipette to line up in the A1 position", "moving_to_slot_title": "Moving to slot {{slot}}", "new_labware_offset_data": "New labware offset data", + "ninety_six_probe_location": "A1 (back left corner)", "no_labware_offsets": "No Labware Offset", "no_offset_data_available": "No labware offset data available", "no_offset_data_on_robot": "This robot has no useable labware offset data for this run.", @@ -77,6 +82,7 @@ "offsets": "offsets", "pick_up_tip_from_rack_in_location": "Pick up tip from tip rack in {{location}}", "picking_up_tip_title": "Picking up tip in slot {{slot}}", + "pipette_nozzle": "pipette nozzle furthest from you", "place_a_full_tip_rack_in_location": "Place a full {{tip_rack}} into {{location}}", "place_labware_in_adapter_in_location": "Place a {{adapter}} followed by a {{labware}} into {{location}}", "place_labware_in_location": "Place a {{labware}} into {{location}}", @@ -85,6 +91,9 @@ "position_check_description": "Labware Position Check is a guided workflow that checks every labware on the deck for an added degree of precision in your protocol.When you check a labware, the OT-2’s pipette nozzle or attached tip will stop at the center of the A1 well. If the pipette nozzle or tip is not centered, you can reveal the OT-2’s jog controls to make an adjustment. This Labware Offset will be applied to the entire labware. Offset data is measured to the nearest 1/10th mm and can be made in the X, Y and/or Z directions.", "prepare_item_in_location": "Prepare {{item}} in {{location}}", "primary_pipette_tipracks_section": "Check tip racks with {{primary_mount}} Pipette and pick up a tip", + "remove_calibration_probe": "Remove calibration probe", + "remove_probe": "Unlock the calibraiton probe, remove it from the nozzle, and return it to its storage location.", + "remove_probe_before_exit": "Remove the calibration probe before exiting", "return_tip_rack_to_location": "Return tip rack to {{location}}", "return_tip_section": "Return tip", "returning_tip_title": "Returning tip in slot {{slot}}", diff --git a/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx b/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx index 5381c23aed5..f54032e48f1 100644 --- a/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx +++ b/app/src/organisms/LabwarePositionCheck/AttachProbe.tsx @@ -1,15 +1,18 @@ import * as React from 'react' import { Trans, useTranslation } from 'react-i18next' import { RESPONSIVENESS, SPACING, TYPOGRAPHY } from '@opentrons/components' -import { css } from 'styled-components' -import { StyledText } from '../../atoms/text' -import { RobotMotionLoader } from './RobotMotionLoader' +import { useInstrumentsQuery } from '@opentrons/react-api-client' import { CompletedProtocolAnalysis, getPipetteNameSpecs, } from '@opentrons/shared-data' +import { css } from 'styled-components' +import { StyledText } from '../../atoms/text' +import { ProbeNotAttached } from '../PipetteWizardFlows/ProbeNotAttached' +import { RobotMotionLoader } from './RobotMotionLoader' import attachProbe1 from '../../assets/videos/pipette-wizard-flows/Pipette_Attach_Probe_1.webm' import attachProbe8 from '../../assets/videos/pipette-wizard-flows/Pipette_Attach_Probe_8.webm' +import attachProbe96 from '../../assets/videos/pipette-wizard-flows/Pipette_Attach_Probe_96.webm' import { useChainRunCommands } from '../../resources/runs/hooks' import { GenericWizardTile } from '../../molecules/GenericWizardTile' @@ -19,7 +22,7 @@ import type { RegisterPositionAction, WorkingOffset, } from './types' -import type { LabwareOffset } from '@opentrons/api-client' +import type { LabwareOffset, PipetteData } from '@opentrons/api-client' interface AttachProbeProps extends AttachProbeStep { protocolData: CompletedProtocolAnalysis @@ -31,7 +34,9 @@ interface AttachProbeProps extends AttachProbeStep { existingOffsets: LabwareOffset[] handleJog: Jog isRobotMoving: boolean + isOnDevice: boolean } + export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => { const { t, i18n } = useTranslation(['labware_position_check', 'shared']) const { @@ -41,43 +46,96 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => { chainRunCommands, isRobotMoving, setFatalError, + isOnDevice, } = props + const [isPending, setIsPending] = React.useState(false) + const [showUnableToDetect, setShowUnableToDetect] = React.useState( + false + ) const pipette = protocolData.pipettes.find(p => p.id === pipetteId) const pipetteName = pipette?.pipetteName const pipetteChannels = pipetteName != null ? getPipetteNameSpecs(pipetteName)?.channels ?? 1 : 1 - const pipetteMount = pipette?.mount - if (pipetteName == null || pipetteMount == null) return null + let probeVideoSrc = attachProbe1 + let probeLocation = '' + if (pipetteChannels === 8) { + probeLocation = t('backmost') + probeVideoSrc = attachProbe8 + } else if (pipetteChannels === 96) { + probeLocation = t('ninety_six_probe_location') + probeVideoSrc = attachProbe96 + } - const pipetteZMotorAxis: 'leftZ' | 'rightZ' = - pipetteMount === 'left' ? 'leftZ' : 'rightZ' + const pipetteMount = pipette?.mount + const { refetch, data: attachedInstrumentsData } = useInstrumentsQuery({ + enabled: false, + onSettled: () => { + setIsPending(false) + }, + }) + const attachedPipette = attachedInstrumentsData?.data.find( + (instrument): instrument is PipetteData => + instrument.ok && instrument.mount === pipetteMount + ) - const handleProbeAttached = (): void => { + React.useEffect(() => { + // move into correct position for probe attach on mount chainRunCommands( [ { - commandType: 'retractAxis' as const, + commandType: 'calibration/moveToMaintenancePosition' as const, params: { - axis: pipetteZMotorAxis, + mount: pipetteMount ?? 'left', }, }, - { - commandType: 'retractAxis' as const, - params: { axis: 'x' }, - }, - { - commandType: 'retractAxis' as const, - params: { axis: 'y' }, - }, ], false - ) - .then(() => proceed()) - .catch((e: Error) => { - setFatalError( - `AttachProbe failed to move to safe location after probe attach with message: ${e.message}` - ) + ).catch(error => setFatalError(error.message)) + }, []) + + if (pipetteName == null || pipetteMount == null) return null + + const pipetteZMotorAxis: 'leftZ' | 'rightZ' = + pipetteMount === 'left' ? 'leftZ' : 'rightZ' + + const handleProbeAttached = (): void => { + setIsPending(true) + refetch() + .then(() => { + if (attachedPipette?.state?.tipDetected) { + chainRunCommands( + [ + { commandType: 'home', params: { axes: [pipetteZMotorAxis] } }, + { + commandType: 'retractAxis' as const, + params: { + axis: pipetteZMotorAxis, + }, + }, + { + commandType: 'retractAxis' as const, + params: { axis: 'x' }, + }, + { + commandType: 'retractAxis' as const, + params: { axis: 'y' }, + }, + ], + false + ) + .then(() => proceed()) + .catch((e: Error) => { + setFatalError( + `AttachProbe failed to move to safe location after probe attach with message: ${e.message}` + ) + }) + } else { + setShowUnableToDetect(true) + } + }) + .catch(error => { + setFatalError(error.message) }) } @@ -85,11 +143,19 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => { return ( ) + else if (showUnableToDetect) + return ( + + ) return ( { loop={true} controls={false} > - 1 ? attachProbe8 : attachProbe1} /> + } bodyText={ - pipetteChannels > 1 ? ( + , - block: , + bold: , }} /> - ) : ( - {t('install_probe')} - ) + } - proceedButtonText={t('begin_calibration')} + proceedButtonText={i18n.format(t('shared:continue'), 'capitalize')} proceed={handleProbeAttached} /> ) diff --git a/app/src/organisms/LabwarePositionCheck/CheckItem.tsx b/app/src/organisms/LabwarePositionCheck/CheckItem.tsx index aaed13ca607..97fe3137690 100644 --- a/app/src/organisms/LabwarePositionCheck/CheckItem.tsx +++ b/app/src/organisms/LabwarePositionCheck/CheckItem.tsx @@ -55,6 +55,7 @@ interface CheckItemProps extends Omit { handleJog: Jog isRobotMoving: boolean robotType: RobotType + shouldUseMetalProbe: boolean } export const CheckItem = (props: CheckItemProps): JSX.Element | null => { const { @@ -73,6 +74,7 @@ export const CheckItem = (props: CheckItemProps): JSX.Element | null => { existingOffsets, setFatalError, robotType, + shouldUseMetalProbe, } = props const { t, i18n } = useTranslation(['labware_position_check', 'shared']) const isOnDevice = useSelector(getIsOnDevice) @@ -431,9 +433,17 @@ export const CheckItem = (props: CheckItemProps): JSX.Element | null => { t={t} i18nKey={ isOnDevice - ? 'ensure_nozzle_is_above_tip_odd' - : 'ensure_nozzle_is_above_tip_desktop' + ? 'ensure_nozzle_position_odd' + : 'ensure_nozzle_position_desktop' } + values={{ + tip_type: shouldUseMetalProbe + ? t('calibration_probe') + : t('pipette_nozzle'), + item_location: isTiprack + ? t('check_tip_location') + : t('check_well_location'), + }} components={{ block: , bold: }} /> } @@ -444,6 +454,7 @@ export const CheckItem = (props: CheckItemProps): JSX.Element | null => { handleJog={handleJog} initialPosition={initialPosition} existingOffset={existingOffset} + shouldUseMetalProbe={shouldUseMetalProbe} /> ) : ( { const pipetteName = pipette?.pipetteName const pipetteChannels = pipetteName != null ? getPipetteNameSpecs(pipetteName)?.channels ?? 1 : 1 + let probeVideoSrc = detachProbe1 + if (pipetteChannels === 8) { + probeVideoSrc = detachProbe8 + } else if (pipetteChannels === 96) { + probeVideoSrc = detachProbe96 + } const pipetteMount = pipette?.mount + + React.useEffect(() => { + // move into correct position for probe detach on mount + chainRunCommands( + [ + { + commandType: 'calibration/moveToMaintenancePosition' as const, + params: { + mount: pipetteMount ?? 'left', + }, + }, + ], + false + ).catch(error => setFatalError(error.message)) + }, []) + if (pipetteName == null || pipetteMount == null) return null const pipetteZMotorAxis: 'leftZ' | 'rightZ' = @@ -76,7 +99,7 @@ export const DetachProbe = (props: DetachProbeProps): JSX.Element | null => { .then(() => proceed()) .catch((e: Error) => { setFatalError( - `DetachProbe failed to move to safe location after probe attach with message: ${e.message}` + `DetachProbe failed to move to safe location after probe detach with message: ${e.message}` ) }) } @@ -101,7 +124,7 @@ export const DetachProbe = (props: DetachProbeProps): JSX.Element | null => { loop={true} controls={false} > - 1 ? detachProbe8 : detachProbe1} /> + } bodyText={ diff --git a/app/src/organisms/LabwarePositionCheck/ExitConfirmation.tsx b/app/src/organisms/LabwarePositionCheck/ExitConfirmation.tsx index 23f498392d9..7a86442779b 100644 --- a/app/src/organisms/LabwarePositionCheck/ExitConfirmation.tsx +++ b/app/src/organisms/LabwarePositionCheck/ExitConfirmation.tsx @@ -26,11 +26,12 @@ import { SmallButton } from '../../atoms/buttons' interface ExitConfirmationProps { onGoBack: () => void onConfirmExit: () => void + shouldUseMetalProbe: boolean } export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => { const { i18n, t } = useTranslation(['labware_position_check', 'shared']) - const { onGoBack, onConfirmExit } = props + const { onGoBack, onConfirmExit, shouldUseMetalProbe } = props const isOnDevice = useSelector(getIsOnDevice) return ( { {isOnDevice ? ( <> - {t('exit_screen_title')} + {shouldUseMetalProbe + ? t('remove_probe_before_exit') + : t('exit_screen_title')} @@ -59,7 +62,11 @@ export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => { ) : ( <> - {t('exit_screen_title')} + + {shouldUseMetalProbe + ? t('remove_probe_before_exit') + : t('exit_screen_title')} + {t('exit_screen_subtitle')} @@ -80,7 +87,11 @@ export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => { /> @@ -99,7 +110,9 @@ export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => { onClick={onConfirmExit} textTransform={TYPOGRAPHY.textTransformCapitalize} > - {t('shared:exit')} + {shouldUseMetalProbe + ? t('remove_calibration_probe') + : i18n.format(t('shared:exit'), 'capitalize')} diff --git a/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx b/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx index b0e112154fe..d5cf8dc0ee6 100644 --- a/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx +++ b/app/src/organisms/LabwarePositionCheck/IntroScreen/index.tsx @@ -34,6 +34,7 @@ import { css } from 'styled-components' import { Portal } from '../../../App/portal' import { LegacyModalShell } from '../../../molecules/LegacyModal' import { SmallButton } from '../../../atoms/buttons' +import { CALIBRATION_PROBE } from '../../PipetteWizardFlows/constants' import { TerseOffsetTable } from '../ResultsSummary' import { getLabwareDefinitionsFromCommands } from '../utils/labware' @@ -52,6 +53,7 @@ export const IntroScreen = (props: { isRobotMoving: boolean existingOffsets: LabwareOffset[] protocolName: string + shouldUseMetalProbe: boolean }): JSX.Element | null => { const { proceed, @@ -61,6 +63,7 @@ export const IntroScreen = (props: { setFatalError, existingOffsets, protocolName, + shouldUseMetalProbe, } = props const isOnDevice = useSelector(getIsOnDevice) const { t, i18n } = useTranslation(['labware_position_check', 'shared']) @@ -74,6 +77,19 @@ export const IntroScreen = (props: { ) }) } + const requiredEquipmentList = [ + { + loadName: t('all_modules_and_labware_from_protocol', { + protocol_name: protocolName, + }), + displayName: t('all_modules_and_labware_from_protocol', { + protocol_name: protocolName, + }), + }, + ] + if (shouldUseMetalProbe) { + requiredEquipmentList.push(CALIBRATION_PROBE) + } if (isRobotMoving) { return ( @@ -91,18 +107,7 @@ export const IntroScreen = (props: { /> } rightElement={ - + } footer={ diff --git a/app/src/organisms/LabwarePositionCheck/JogToWell.tsx b/app/src/organisms/LabwarePositionCheck/JogToWell.tsx index c634a2edb3e..4e91343b85a 100644 --- a/app/src/organisms/LabwarePositionCheck/JogToWell.tsx +++ b/app/src/organisms/LabwarePositionCheck/JogToWell.tsx @@ -29,6 +29,8 @@ import { import levelWithTip from '../../assets/images/lpc_level_with_tip.svg' import levelWithLabware from '../../assets/images/lpc_level_with_labware.svg' +import levelProbeWithTip from '../../assets/images/lpc_level_probe_with_tip.svg' +import levelProbeWithLabware from '../../assets/images/lpc_level_probe_with_labware.svg' import { getIsOnDevice } from '../../redux/config' import { Portal } from '../../App/portal' import { LegacyModalShell } from '../../molecules/LegacyModal' @@ -57,6 +59,7 @@ interface JogToWellProps { body: React.ReactNode initialPosition: VectorOffset existingOffset: VectorOffset + shouldUseMetalProbe: boolean } export const JogToWell = (props: JogToWellProps): JSX.Element | null => { const { t } = useTranslation(['labware_position_check', 'shared']) @@ -70,6 +73,7 @@ export const JogToWell = (props: JogToWellProps): JSX.Element | null => { handleJog, initialPosition, existingOffset, + shouldUseMetalProbe, } = props const [joggedPosition, setJoggedPosition] = React.useState( @@ -109,6 +113,10 @@ export const JogToWell = (props: JogToWellProps): JSX.Element | null => { getVectorDifference(joggedPosition, initialPosition) ) const isTipRack = getIsTiprack(labwareDef) + let levelSrc = isTipRack ? levelWithTip : levelWithLabware + if (shouldUseMetalProbe) { + levelSrc = isTipRack ? levelProbeWithTip : levelProbeWithLabware + } return ( { {`level diff --git a/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx b/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx index 95f9fc02c5e..8df5ad42bfa 100644 --- a/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx +++ b/app/src/organisms/LabwarePositionCheck/LabwarePositionCheckComponent.tsx @@ -338,6 +338,7 @@ export const LabwarePositionCheckComponent = ( ) } else if (currentStep.section === 'BEFORE_BEGINNING') { @@ -346,6 +347,7 @@ export const LabwarePositionCheckComponent = ( {...movementStepProps} {...{ existingOffsets }} protocolName={protocolName} + shouldUseMetalProbe={shouldUseMetalProbe} /> ) } else if ( @@ -353,9 +355,21 @@ export const LabwarePositionCheckComponent = ( currentStep.section === 'CHECK_TIP_RACKS' || currentStep.section === 'CHECK_LABWARE' ) { - modalContent = + modalContent = ( + + ) } else if (currentStep.section === 'ATTACH_PROBE') { - modalContent = + modalContent = ( + + ) } else if (currentStep.section === 'DETACH_PROBE') { modalContent = } else if (currentStep.section === 'PICK_UP_TIP') { diff --git a/app/src/organisms/LabwarePositionCheck/PickUpTip.tsx b/app/src/organisms/LabwarePositionCheck/PickUpTip.tsx index 9457fcea6e9..72141eb28ae 100644 --- a/app/src/organisms/LabwarePositionCheck/PickUpTip.tsx +++ b/app/src/organisms/LabwarePositionCheck/PickUpTip.tsx @@ -407,10 +407,14 @@ export const PickUpTip = (props: PickUpTipProps): JSX.Element | null => { t={t} i18nKey={ isOnDevice - ? 'ensure_nozzle_is_above_tip_odd' - : 'ensure_nozzle_is_above_tip_desktop' + ? 'ensure_nozzle_position_odd' + : 'ensure_nozzle_position_desktop' } components={{ block: , bold: }} + values={{ + tip_type: t('pipette_nozzle'), + item_location: t('check_tip_location'), + }} /> } labwareDef={labwareDef} @@ -420,6 +424,7 @@ export const PickUpTip = (props: PickUpTipProps): JSX.Element | null => { handleJog={handleJog} initialPosition={initialPosition} existingOffset={existingOffset} + shouldUseMetalProbe={false} /> ) : ( { existingOffsets: mockExistingOffsets, isRobotMoving: false, robotType: FLEX_ROBOT_TYPE, + shouldUseMetalProbe: false, } }) afterEach(() => { diff --git a/app/src/organisms/LabwarePositionCheck/__tests__/ExitConfirmation.test.tsx b/app/src/organisms/LabwarePositionCheck/__tests__/ExitConfirmation.test.tsx index 61552a0f1cb..3263e098e22 100644 --- a/app/src/organisms/LabwarePositionCheck/__tests__/ExitConfirmation.test.tsx +++ b/app/src/organisms/LabwarePositionCheck/__tests__/ExitConfirmation.test.tsx @@ -17,6 +17,7 @@ describe('ExitConfirmation', () => { props = { onGoBack: jest.fn(), onConfirmExit: jest.fn(), + shouldUseMetalProbe: false, } }) afterEach(() => { @@ -29,14 +30,26 @@ describe('ExitConfirmation', () => { getByText( 'If you exit now, all labware offsets will be discarded. This cannot be undone.' ) - getByRole('button', { name: 'exit' }) + getByRole('button', { name: 'Exit' }) getByRole('button', { name: 'Go back' }) }) it('should invoke callback props when ctas are clicked', () => { const { getByRole } = render(props) getByRole('button', { name: 'Go back' }).click() expect(props.onGoBack).toHaveBeenCalled() - getByRole('button', { name: 'exit' }).click() + getByRole('button', { name: 'Exit' }).click() expect(props.onConfirmExit).toHaveBeenCalled() }) + it('should render correct copy for golden tip LPC', () => { + const { getByText, getByRole } = render({ + ...props, + shouldUseMetalProbe: true, + }) + getByText('Remove the calibration probe before exiting') + getByText( + 'If you exit now, all labware offsets will be discarded. This cannot be undone.' + ) + getByRole('button', { name: 'Remove calibration probe' }) + getByRole('button', { name: 'Go back' }) + }) }) diff --git a/app/src/organisms/PipetteWizardFlows/AttachProbe.tsx b/app/src/organisms/PipetteWizardFlows/AttachProbe.tsx index 881d8e04631..f12c7065661 100644 --- a/app/src/organisms/PipetteWizardFlows/AttachProbe.tsx +++ b/app/src/organisms/PipetteWizardFlows/AttachProbe.tsx @@ -3,19 +3,13 @@ import { css } from 'styled-components' import { Trans, useTranslation } from 'react-i18next' import { Flex, - Btn, - PrimaryButton, TYPOGRAPHY, COLORS, SPACING, RESPONSIVENESS, - JUSTIFY_SPACE_BETWEEN, - ALIGN_FLEX_END, - ALIGN_CENTER, } from '@opentrons/components' import { LEFT, MotorAxes } from '@opentrons/shared-data' import { useInstrumentsQuery } from '@opentrons/react-api-client' -import { SmallButton } from '../../atoms/buttons' import { StyledText } from '../../atoms/text' import { GenericWizardTile } from '../../molecules/GenericWizardTile' import { SimpleWizardBody } from '../../molecules/SimpleWizardBody' @@ -25,6 +19,7 @@ import pipetteProbe8 from '../../assets/videos/pipette-wizard-flows/Pipette_Prob import probing96 from '../../assets/videos/pipette-wizard-flows/Pipette_Probing_96.webm' import { BODY_STYLE, SECTIONS, FLOWS } from './constants' import { getPipetteAnimations } from './utils' +import { ProbeNotAttached } from './ProbeNotAttached' import type { PipetteData } from '@opentrons/api-client' import type { PipetteWizardStepProps } from './types' @@ -43,32 +38,6 @@ const IN_PROGRESS_STYLE = css` } ` -const ALIGN_BUTTONS = css` - align-items: ${ALIGN_FLEX_END}; - - @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { - align-items: ${ALIGN_CENTER}; - } -` -const GO_BACK_BUTTON_STYLE = css` - ${TYPOGRAPHY.pSemiBold}; - color: ${COLORS.darkGreyEnabled}; - padding-left: ${SPACING.spacing32}; - - &:hover { - opacity: 70%; - } - - @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { - font-weight: ${TYPOGRAPHY.fontWeightSemiBold}; - font-size: ${TYPOGRAPHY.fontSize22}; - padding-left: 0rem; - &:hover { - opacity: 100%; - } - } -` - export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => { const { proceed, @@ -89,7 +58,6 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => { const [showUnableToDetect, setShowUnableToDetect] = React.useState( false ) - const [numberOfTryAgains, setNumberOfTryAgains] = React.useState(0) const pipetteId = attachedPipettes[mount]?.serialNumber const displayName = attachedPipettes[mount]?.displayName @@ -211,41 +179,12 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => { ) else if (showUnableToDetect) return ( - 2 ? t('something_seems_wrong') : undefined - } - iconColor={COLORS.errorEnabled} - isSuccess={false} - > - - setShowUnableToDetect(false)}> - - {t('shared:go_back')} - - - {isOnDevice ? ( - { - setNumberOfTryAgains(numberOfTryAgains + 1) - handleOnClick() - }} - /> - ) : ( - - {t('try_again')} - - )} - - + ) return errorMessage != null ? ( diff --git a/app/src/organisms/PipetteWizardFlows/ProbeNotAttached.tsx b/app/src/organisms/PipetteWizardFlows/ProbeNotAttached.tsx new file mode 100644 index 00000000000..ffee8350cbc --- /dev/null +++ b/app/src/organisms/PipetteWizardFlows/ProbeNotAttached.tsx @@ -0,0 +1,101 @@ +import * as React from 'react' +import { useTranslation } from 'react-i18next' +import { + Flex, + Btn, + PrimaryButton, + RESPONSIVENESS, + SPACING, + TYPOGRAPHY, + COLORS, + JUSTIFY_SPACE_BETWEEN, + ALIGN_FLEX_END, + ALIGN_CENTER, +} from '@opentrons/components' +import { css } from 'styled-components' +import { SimpleWizardBody } from '../../molecules/SimpleWizardBody' +import { StyledText } from '../../atoms/text' +import { SmallButton } from '../../atoms/buttons' + +interface ProbeNotAttachedProps { + handleOnClick: () => void + setShowUnableToDetect: (ableToDetect: boolean) => void + isOnDevice: boolean + isPending: boolean +} + +export const ProbeNotAttached = ( + props: ProbeNotAttachedProps +): JSX.Element | null => { + const { t, i18n } = useTranslation(['pipette_wizard_flows', 'shared']) + const { isOnDevice, isPending, handleOnClick, setShowUnableToDetect } = props + const [numberOfTryAgains, setNumberOfTryAgains] = React.useState(0) + + return ( + 2 ? t('something_seems_wrong') : undefined} + iconColor={COLORS.errorEnabled} + isSuccess={false} + > + + setShowUnableToDetect(false)}> + + {t('shared:go_back')} + + + {isOnDevice ? ( + { + setNumberOfTryAgains(numberOfTryAgains + 1) + handleOnClick() + }} + /> + ) : ( + { + setNumberOfTryAgains(numberOfTryAgains + 1) + handleOnClick() + }} + > + {i18n.format(t('shared:try_again'), 'capitalize')} + + )} + + + ) +} + +const ALIGN_BUTTONS = css` + align-items: ${ALIGN_FLEX_END}; + + @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { + align-items: ${ALIGN_CENTER}; + } +` +const GO_BACK_BUTTON_STYLE = css` + ${TYPOGRAPHY.pSemiBold}; + color: ${COLORS.darkGreyEnabled}; + padding-left: ${SPACING.spacing32}; + + &:hover { + opacity: 70%; + } + + @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { + font-weight: ${TYPOGRAPHY.fontWeightSemiBold}; + font-size: ${TYPOGRAPHY.fontSize22}; + padding-left: 0rem; + &:hover { + opacity: 100%; + } + } +`