Skip to content

Commit

Permalink
fix(app): fix desktop app attach flow for the 96ch
Browse files Browse the repository at this point in the history
The right pipette card on the desktop app conditionally renders based on whether or not a 96 channel
is attached. This poses an issue when a user tries to attach a 96ch from the right card, since the
card will unrender as soon as the pipette is  recognized, unrendering the pipette wizard. This is a
natural spot for our Nice Modal library, which decouples the conditional logic for rendering the
modal from what is required to persist the modal.
  • Loading branch information
mjhuff committed Feb 29, 2024
1 parent ed5692c commit 8121cf2
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 65 deletions.
45 changes: 23 additions & 22 deletions app/src/organisms/Devices/PipetteCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import {
useCurrentSubsystemUpdateQuery,
usePipetteSettingsQuery,
useHost,
} from '@opentrons/react-api-client'

import { LEFT } from '../../../redux/pipettes'
Expand All @@ -36,7 +37,7 @@ import { useMenuHandleClickOutside } from '../../../atoms/MenuList/hooks'
import { InstrumentCard } from '../../../molecules/InstrumentCard'
import { ChangePipette } from '../../ChangePipette'
import { FLOWS } from '../../PipetteWizardFlows/constants'
import { PipetteWizardFlows } from '../../PipetteWizardFlows'
import { handlePipetteWizardFlows } from '../../PipetteWizardFlows'
import { ChoosePipette } from '../../PipetteWizardFlows/ChoosePipette'
import { useIsFlex } from '../hooks'
import { PipetteOverflowMenu } from './PipetteOverflowMenu'
Expand All @@ -50,6 +51,7 @@ import type {
SelectablePipettes,
} from '../../PipetteWizardFlows/types'
import { DropTipWizard } from '../../DropTipWizard'
import { HostConfig } from '@opentrons/api-client'

interface PipetteCardProps {
pipetteModelSpecs: PipetteModelSpecs | null
Expand Down Expand Up @@ -95,6 +97,7 @@ export const PipetteCard = (props: PipetteCardProps): JSX.Element => {
setShowOverflowMenu,
} = useMenuHandleClickOutside()
const isFlex = useIsFlex(robotName)
const host = useHost() as HostConfig
const pipetteName = pipetteModelSpecs?.name
const isFlexPipetteAttached = isFlexPipette(pipetteName as PipetteName)
const pipetteDisplayName = pipetteModelSpecs?.displayName
Expand All @@ -104,10 +107,6 @@ export const PipetteCard = (props: PipetteCardProps): JSX.Element => {
const [showChangePipette, setChangePipette] = React.useState(false)
const [showDropTipWizard, setShowDropTipWizard] = React.useState(false)
const [showSlideout, setShowSlideout] = React.useState(false)
const [
pipetteWizardFlow,
setPipetteWizardFlow,
] = React.useState<PipetteWizardFlow | null>(null)
const [showAttachPipette, setShowAttachPipette] = React.useState(false)
const [showAboutSlideout, setShowAboutSlideout] = React.useState(false)
const subsystem = mount === LEFT ? 'pipette_left' : 'pipette_right'
Expand Down Expand Up @@ -148,10 +147,12 @@ export const PipetteCard = (props: PipetteCardProps): JSX.Element => {
selectedPipette,
setSelectedPipette,
] = React.useState<SelectablePipettes>(SINGLE_MOUNT_PIPETTES)
const selectedPipetteForWizard =
pipetteName === 'p1000_96' ? NINETY_SIX_CHANNEL : selectedPipette

const handleChangePipette = (): void => {
if (isFlexPipetteAttached && isFlex) {
setPipetteWizardFlow(FLOWS.DETACH)
handleLaunchPipetteWizardFlows(FLOWS.DETACH)

Check warning on line 155 in app/src/organisms/Devices/PipetteCard/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/Devices/PipetteCard/index.tsx#L155

Added line #L155 was not covered by tests
} else if (!isFlexPipetteAttached && isFlex) {
setShowAttachPipette(true)
} else {
Expand All @@ -162,19 +163,32 @@ export const PipetteCard = (props: PipetteCardProps): JSX.Element => {
setShowDropTipWizard(true)
}
const handleCalibrate = (): void => {
if (isFlexPipetteAttached) setPipetteWizardFlow(FLOWS.CALIBRATE)
if (isFlexPipetteAttached) {
handleLaunchPipetteWizardFlows(FLOWS.CALIBRATE)

Check warning on line 167 in app/src/organisms/Devices/PipetteCard/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/Devices/PipetteCard/index.tsx#L167

Added line #L167 was not covered by tests
}
}
const handleAboutSlideout = (): void => {
setShowAboutSlideout(true)
}
const handleSettingsSlideout = (): void => {
setShowSlideout(true)
}

const handleAttachPipette = (): void => {
setShowAttachPipette(false)
setPipetteWizardFlow(FLOWS.ATTACH)
handleLaunchPipetteWizardFlows(FLOWS.ATTACH)

Check warning on line 178 in app/src/organisms/Devices/PipetteCard/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/Devices/PipetteCard/index.tsx#L178

Added line #L178 was not covered by tests
}
const setCloseFlow = (): void => {
setSelectedPipette(SINGLE_MOUNT_PIPETTES)

Check warning on line 181 in app/src/organisms/Devices/PipetteCard/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/Devices/PipetteCard/index.tsx#L181

Added line #L181 was not covered by tests
}
const handleLaunchPipetteWizardFlows = (flowType: PipetteWizardFlow): void =>
handlePipetteWizardFlows({

Check warning on line 184 in app/src/organisms/Devices/PipetteCard/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/Devices/PipetteCard/index.tsx#L184

Added line #L184 was not covered by tests
flowType,
mount,
closeFlow: setCloseFlow,
selectedPipette: selectedPipetteForWizard,
host,
})

return (
<Flex
backgroundColor={COLORS.grey10}
Expand All @@ -191,19 +205,6 @@ export const PipetteCard = (props: PipetteCardProps): JSX.Element => {
mount={mount}
/>
) : null}
{pipetteWizardFlow != null ? (
<PipetteWizardFlows
flowType={pipetteWizardFlow}
mount={mount}
closeFlow={() => {
setSelectedPipette(SINGLE_MOUNT_PIPETTES)
setPipetteWizardFlow(null)
}}
selectedPipette={
pipetteName === 'p1000_96' ? NINETY_SIX_CHANNEL : selectedPipette
}
/>
) : null}
{showChangePipette && (
<ChangePipette
robotName={robotName}
Expand Down
33 changes: 31 additions & 2 deletions app/src/organisms/PipetteWizardFlows/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react'
import { useSelector } from 'react-redux'
import { useTranslation } from 'react-i18next'
import NiceModal, { useModal } from '@ebay/nice-modal-react'

import { useConditionalConfirm } from '@opentrons/components'
import {
LEFT,
Expand All @@ -12,12 +14,13 @@ import {
import {
useHost,
useDeleteMaintenanceRunMutation,
ApiHostProvider,
} from '@opentrons/react-api-client'

import {
useCreateTargetedMaintenanceRunMutation,
useChainMaintenanceCommands,
} from '../../resources/runs/hooks'

import { useNotifyCurrentMaintenanceRun } from '../../resources/maintenance_runs/useNotifyCurrentMaintenanceRun'
import { LegacyModalShell } from '../../molecules/LegacyModal'
import { Portal } from '../../App/portal'
Expand All @@ -42,7 +45,7 @@ import { MountingPlate } from './MountingPlate'
import { UnskippableModal } from './UnskippableModal'

import type { PipetteMount } from '@opentrons/shared-data'
import type { CommandData } from '@opentrons/api-client'
import type { CommandData, HostConfig } from '@opentrons/api-client'
import type { PipetteWizardFlow, SelectablePipettes } from './types'

const RUN_REFETCH_INTERVAL = 5000
Expand Down Expand Up @@ -433,3 +436,29 @@ export const PipetteWizardFlows = (
</Portal>
)
}

type PipetteWizardFlowsPropsWithHost = PipetteWizardFlowsProps & {
host: HostConfig
}

export const handlePipetteWizardFlows = (
props: PipetteWizardFlowsPropsWithHost
): void => {
NiceModal.show(NiceModalPipetteWizardFlows, props)

Check warning on line 447 in app/src/organisms/PipetteWizardFlows/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/PipetteWizardFlows/index.tsx#L447

Added line #L447 was not covered by tests
}

const NiceModalPipetteWizardFlows = NiceModal.create(
(props: PipetteWizardFlowsPropsWithHost): JSX.Element => {
const modal = useModal()
const closeFlowAndModal = (): void => {
props.closeFlow()
modal.remove()

Check warning on line 455 in app/src/organisms/PipetteWizardFlows/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/PipetteWizardFlows/index.tsx#L452-L455

Added lines #L452 - L455 were not covered by tests
}

return (

Check warning on line 458 in app/src/organisms/PipetteWizardFlows/index.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/PipetteWizardFlows/index.tsx#L458

Added line #L458 was not covered by tests
<ApiHostProvider {...props.host}>
<PipetteWizardFlows {...props} closeFlow={closeFlowAndModal} />
</ApiHostProvider>
)
}
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import NiceModal from '@ebay/nice-modal-react'
import { fireEvent, waitFor } from '@testing-library/react'
import { fireEvent } from '@testing-library/react'

import { renderWithProviders } from '@opentrons/components'
import { getPipetteModelSpecs } from '@opentrons/shared-data'
Expand Down Expand Up @@ -167,46 +167,6 @@ describe('UpdateBuildroot', () => {
expect(queryByText('Drop tips')).not.toBeInTheDocument()
})

it('renders the pipette calibration wizard when recalibrate is clicked', () => {
const [{ getByTestId, getByText }] = render(MOCK_PIPETTE)
const btn = getByTestId('testButton')
fireEvent.click(btn)
fireEvent.click(getByText('Recalibrate'))

getByText('Calibrate Left Pipette')
})

it('renders the drop tip wizard when Drop tips is clicked', () => {
const [{ getByTestId, getByText, getAllByText }] = render(MOCK_PIPETTE)
const btn = getByTestId('testButton')
fireEvent.click(btn)
fireEvent.click(getByText('Drop tips'))

expect(getAllByText('Drop tips')).toHaveLength(2)
})

it('renders the gripper calibration wizard when recalibrate is clicked', () => {
const [{ getByTestId, getByText }] = render(MOCK_GRIPPER)
const btn = getByTestId('testButton')
fireEvent.click(btn)
fireEvent.click(getByText('Recalibrate'))

getByText('Calibrate Gripper')
})

it('closes the overflow menu when a launched wizard closes', async () => {
const [{ getByTestId, getByText, queryByText }] = render(MOCK_GRIPPER)
const btn = getByTestId('testButton')
fireEvent.click(btn)
fireEvent.click(getByText('Recalibrate'))

getByText('Calibrate Gripper')
fireEvent.click(getByText('exit'))
await waitFor(() =>
expect(queryByText('Recalibrate')).not.toBeInTheDocument()
)
})

it('closes the overflow menu when a click occurs outside of the overflow menu', () => {
const [{ queryByText, getByTestId, getByLabelText }] = render(MOCK_PIPETTE)
const btn = getByTestId('testButton')
Expand Down

0 comments on commit 8121cf2

Please sign in to comment.