Skip to content

Commit

Permalink
fix(app): ODD LPC copy and styling (#13925)
Browse files Browse the repository at this point in the history
closes RQA-1052
closes RQA-1353
closes RQA-1358

Fix copy for preparing space conditional on step index and module presence. Correctly style
ExitConfirmation step. Fix button alignment in TipConfirmation.
  • Loading branch information
ncdiehl11 authored Nov 8, 2023
1 parent 89b795f commit 2655f2b
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 17 deletions.
1 change: 1 addition & 0 deletions app/src/assets/localization/en/labware_position_check.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"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",
"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_pick_up_tip_modal_title": "Did the pipette pick up a tip successfully?",
Expand Down
5 changes: 4 additions & 1 deletion app/src/organisms/LabwarePositionCheck/CheckItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,10 @@ export const CheckItem = (props: CheckItemProps): JSX.Element | null => {
})}
body={
<UnorderedList
items={[t('clear_all_slots'), placeItemInstruction]}
items={[
isOnDevice ? t('clear_all_slots_odd') : t('clear_all_slots'),
placeItemInstruction,
]}
/>
}
labwareDef={labwareDef}
Expand Down
46 changes: 37 additions & 9 deletions app/src/organisms/LabwarePositionCheck/ExitConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ import {
RESPONSIVENESS,
SecondaryButton,
JUSTIFY_FLEX_END,
TEXT_ALIGN_CENTER,
} from '@opentrons/components'
import { StyledText } from '../../atoms/text'
import { NeedHelpLink } from '../CalibrationPanels'
import { useSelector } from 'react-redux'
import { getIsOnDevice } from '../../redux/config'
import { SmallButton } from '../../atoms/buttons'

const LPC_HELP_LINK_URL =
'https://support.opentrons.com/s/article/How-Labware-Offsets-work-on-the-OT-2'
interface ExitConfirmationProps {
onGoBack: () => void
onConfirmExit: () => void
Expand All @@ -45,12 +43,28 @@ export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => {
flexDirection={DIRECTION_COLUMN}
justifyContent={JUSTIFY_CENTER}
alignItems={ALIGN_CENTER}
paddingX={SPACING.spacing32}
>
<Icon name="ot-alert" size={SIZE_3} color={COLORS.warningEnabled} />
<ConfirmationHeader>{t('exit_screen_title')}</ConfirmationHeader>
<StyledText as="p" marginTop={SPACING.spacing8}>
{t('exit_screen_subtitle')}
</StyledText>
{isOnDevice ? (
<>
<ConfirmationHeaderODD>
{t('exit_screen_title')}
</ConfirmationHeaderODD>
<Flex textAlign={TEXT_ALIGN_CENTER}>
<ConfirmationBodyODD>
{t('exit_screen_subtitle')}
</ConfirmationBodyODD>
</Flex>
</>
) : (
<>
<ConfirmationHeader>{t('exit_screen_title')}</ConfirmationHeader>
<StyledText as="p" marginTop={SPACING.spacing8}>
{t('exit_screen_subtitle')}
</StyledText>
</>
)}
</Flex>
{isOnDevice ? (
<Flex
Expand All @@ -77,7 +91,6 @@ export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => {
justifyContent={JUSTIFY_SPACE_BETWEEN}
alignItems={ALIGN_CENTER}
>
<NeedHelpLink href={LPC_HELP_LINK_URL} />
<Flex gridGap={SPACING.spacing8}>
<SecondaryButton onClick={onGoBack}>
{t('shared:go_back')}
Expand All @@ -97,8 +110,23 @@ export const ExitConfirmation = (props: ExitConfirmationProps): JSX.Element => {

const ConfirmationHeader = styled.h1`
margin-top: ${SPACING.spacing24};
${TYPOGRAPHY.h1Default}
${TYPOGRAPHY.level3HeaderSemiBold}
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
${TYPOGRAPHY.level4HeaderSemiBold}
}
`

const ConfirmationHeaderODD = styled.h1`
margin-top: ${SPACING.spacing24};
${TYPOGRAPHY.level3HeaderBold}
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
${TYPOGRAPHY.level4HeaderSemiBold}
}
`
const ConfirmationBodyODD = styled.h1`
${TYPOGRAPHY.level4HeaderRegular}
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
${TYPOGRAPHY.level4HeaderRegular}
}
color: ${COLORS.darkBlack70};
`
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ export const LabwarePositionCheckComponent = (
const currentStep = LPCSteps?.[currentStepIndex]
if (currentStep == null) return null

const protocolHasModules = protocolData.modules.length > 0

const handleJog = (
axis: Axis,
dir: Sign,
Expand Down Expand Up @@ -357,7 +359,14 @@ export const LabwarePositionCheckComponent = (
} else if (currentStep.section === 'DETACH_PROBE') {
modalContent = <DetachProbe {...currentStep} {...movementStepProps} />
} else if (currentStep.section === 'PICK_UP_TIP') {
modalContent = <PickUpTip {...currentStep} {...movementStepProps} />
modalContent = (
<PickUpTip
{...currentStep}
{...movementStepProps}
protocolHasModules={protocolHasModules}
currentStepIndex={currentStepIndex}
/>
)
} else if (currentStep.section === 'RETURN_TIP') {
modalContent = (
<ReturnTip
Expand Down
9 changes: 8 additions & 1 deletion app/src/organisms/LabwarePositionCheck/PickUpTip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ interface PickUpTipProps extends PickUpTipStep {
handleJog: Jog
isRobotMoving: boolean
robotType: RobotType
protocolHasModules: boolean
currentStepIndex: number
}
export const PickUpTip = (props: PickUpTipProps): JSX.Element | null => {
const { t, i18n } = useTranslation(['labware_position_check', 'shared'])
Expand All @@ -67,6 +69,8 @@ export const PickUpTip = (props: PickUpTipProps): JSX.Element | null => {
setFatalError,
adapterId,
robotType,
protocolHasModules,
currentStepIndex,
} = props
const [showTipConfirmation, setShowTipConfirmation] = React.useState(false)
const isOnDevice = useSelector(getIsOnDevice)
Expand All @@ -87,7 +91,10 @@ export const PickUpTip = (props: PickUpTipProps): JSX.Element | null => {
)
const labwareDisplayName = getLabwareDisplayName(labwareDef)
const instructions = [
t('clear_all_slots'),
...(protocolHasModules && currentStepIndex === 1
? [t('place_modules')]
: []),
isOnDevice ? t('clear_all_slots_odd') : t('clear_all_slots'),
<Trans
key="place_a_full_tip_rack_in_location"
t={t}
Expand Down
6 changes: 5 additions & 1 deletion app/src/organisms/LabwarePositionCheck/ReturnTip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
import { getDisplayLocation } from './utils/getDisplayLocation'
import { RobotMotionLoader } from './RobotMotionLoader'
import { PrepareSpace } from './PrepareSpace'
import { useSelector } from 'react-redux'
import { getIsOnDevice } from '../../redux/config'

import type { VectorOffset } from '@opentrons/api-client'
import type { ReturnTipStep } from './types'
Expand Down Expand Up @@ -48,6 +50,8 @@ export const ReturnTip = (props: ReturnTipProps): JSX.Element | null => {
adapterId,
} = props

const isOnDevice = useSelector(getIsOnDevice)

const labwareDef = getLabwareDef(labwareId, protocolData)
if (labwareDef == null) return null

Expand All @@ -60,7 +64,7 @@ export const ReturnTip = (props: ReturnTipProps): JSX.Element | null => {
const labwareDisplayName = getLabwareDisplayName(labwareDef)

const instructions = [
t('clear_all_slots'),
isOnDevice ? t('clear_all_slots_odd') : t('clear_all_slots'),
<Trans
key="place_previous_tip_rack_in_location"
t={t}
Expand Down
8 changes: 7 additions & 1 deletion app/src/organisms/LabwarePositionCheck/TipConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SPACING,
ALIGN_CENTER,
COLORS,
JUSTIFY_FLEX_END,
} from '@opentrons/components'
import { useTranslation } from 'react-i18next'

Expand Down Expand Up @@ -37,7 +38,12 @@ export function TipConfirmation(props: TipConfirmationProps): JSX.Element {
iconColor={COLORS.warningEnabled}
header={t('did_pipette_pick_up_tip')}
>
<Flex alignItems={ALIGN_CENTER} gridGap={SPACING.spacing8}>
<Flex
alignItems={ALIGN_CENTER}
gridGap={SPACING.spacing8}
justifyContent={JUSTIFY_FLEX_END}
flex="1"
>
<SmallButton
buttonText={i18n.format(t('try_again'), 'capitalize')}
buttonType="secondary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,55 @@ describe('PickUpTip', () => {
existingOffsets: mockExistingOffsets,
isRobotMoving: false,
robotType: FLEX_ROBOT_TYPE,
protocolHasModules: false,
currentStepIndex: 1,
}
mockUseProtocolMetaData.mockReturnValue({ robotType: 'OT-3 Standard' })
})
afterEach(() => {
jest.resetAllMocks()
resetAllWhenMocks()
})
it('renders correct copy when preparing space', () => {
it('renders correct copy when preparing space on desktop if protocol has modules', () => {
props.protocolHasModules = true
const { getByText, getByRole } = render(props)
getByRole('heading', { name: 'Prepare tip rack in Slot D1' })
getByText('Place modules on deck')
getByText('Clear all deck slots of labware, leaving modules in place')
getByText(
matchTextWithSpans('Place a full Mock TipRack Definition into Slot D1')
)
getByRole('link', { name: 'Need help?' })
getByRole('button', { name: 'Confirm placement' })
})
it('renders correct copy when preparing space on touchscreen if protocol has modules', () => {
mockGetIsOnDevice.mockReturnValue(true)
props.protocolHasModules = true
const { getByText, getByRole } = render(props)
getByRole('heading', { name: 'Prepare tip rack in Slot D1' })
getByText('Place modules on deck')
getByText('Clear all deck slots of labware')
getByText(
matchTextWithSpans('Place a full Mock TipRack Definition into Slot D1')
)
})
it('renders correct copy when preparing space on desktop if protocol has no modules', () => {
const { getByText, getByRole } = render(props)
getByRole('heading', { name: 'Prepare tip rack in Slot D1' })
getByText('Clear all deck slots of labware, leaving modules in place')
getByText(
matchTextWithSpans('Place a full Mock TipRack Definition into Slot D1')
)
getByRole('button', { name: 'Confirm placement' })
})
it('renders correct copy when preparing space on touchscreen if protocol has no modules', () => {
mockGetIsOnDevice.mockReturnValue(true)
const { getByText, getByRole } = render(props)
getByRole('heading', { name: 'Prepare tip rack in Slot D1' })
getByText('Clear all deck slots of labware')
getByText(
matchTextWithSpans('Place a full Mock TipRack Definition into Slot D1')
)
})
it('renders correct copy when confirming position on desktop', () => {
const { getByText, getByRole } = render({
...props,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import { ReturnTip } from '../ReturnTip'
import { SECTIONS } from '../constants'
import { mockCompletedAnalysis } from '../__fixtures__'
import { useProtocolMetadata } from '../../Devices/hooks'
import { getIsOnDevice } from '../../../redux/config'

jest.mock('../../Devices/hooks')
jest.mock('../../../redux/config')

const mockUseProtocolMetaData = useProtocolMetadata as jest.MockedFunction<
typeof useProtocolMetadata
>
const mockGetIsOnDevice = getIsOnDevice as jest.MockedFunction<
typeof getIsOnDevice
>

const matchTextWithSpans: (text: string) => MatcherFunction = (
text: string
Expand All @@ -37,6 +42,7 @@ describe('ReturnTip', () => {

beforeEach(() => {
mockChainRunCommands = jest.fn().mockImplementation(() => Promise.resolve())
mockGetIsOnDevice.mockReturnValue(false)
props = {
section: SECTIONS.RETURN_TIP,
pipetteId: mockCompletedAnalysis.pipettes[0].id,
Expand All @@ -55,7 +61,7 @@ describe('ReturnTip', () => {
afterEach(() => {
jest.restoreAllMocks()
})
it('renders correct copy', () => {
it('renders correct copy on desktop', () => {
const { getByText, getByRole } = render(props)
getByRole('heading', { name: 'Return tip rack to Slot D1' })
getByText('Clear all deck slots of labware, leaving modules in place')
Expand All @@ -66,6 +72,17 @@ describe('ReturnTip', () => {
)
getByRole('link', { name: 'Need help?' })
})
it('renders correct copy on device', () => {
mockGetIsOnDevice.mockReturnValue(true)
const { getByText, getByRole } = render(props)
getByRole('heading', { name: 'Return tip rack to Slot D1' })
getByText('Clear all deck slots of labware')
getByText(
matchTextWithSpans(
'Place the Mock TipRack Definition that you used before back into Slot D1. The pipette will return tips to their original location in the rack.'
)
)
})
it('executes correct chained commands when CTA is clicked', async () => {
const { getByRole } = render(props)
await getByRole('button', { name: 'Confirm placement' }).click()
Expand Down

0 comments on commit 2655f2b

Please sign in to comment.