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): add labware selection and volume entry screens #15074

Merged
merged 10 commits into from
May 3, 2024
Prev Previous commit
Next Next commit
Add exit prevention screen
  • Loading branch information
smb2268 committed May 2, 2024
commit f4ca1c223253d5624a35d96b6017d74224ed6dcb
54 changes: 54 additions & 0 deletions app/src/organisms/QuickTransferFlow/ConfirmExitModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import {
SPACING,
COLORS,
StyledText,
Flex,
DIRECTION_COLUMN,
TYPOGRAPHY,
} from '@opentrons/components'
import { Modal } from '../../molecules/Modal'
import { SmallButton } from '../../atoms/buttons'

interface ConfirmExitModalProps {
confirmExit: () => void
cancelExit: () => void
}

export const ConfirmExitModal = (props: ConfirmExitModalProps): JSX.Element => {
const { i18n, t } = useTranslation(['quick_transfer', 'shared'])

return (
<Modal
header={{
title: t('exit_quick_transfer'),
iconName: 'alert-circle',
iconColor: COLORS.yellow50,
}}
>
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing10}
width="100%"
>
<StyledText css={TYPOGRAPHY.bodyTextRegular}>
{t('lose_all_progress')}
</StyledText>
<Flex gridGap={SPACING.spacing8}>
<SmallButton
width="50%"
buttonText={i18n.format(t('shared:cancel'), 'capitalize')}
onClick={props.cancelExit}
/>
<SmallButton
width="50%"
buttonText={i18n.format(t('shared:delete'), 'capitalize')}
onClick={props.confirmExit}
buttonType="alert"
/>
</Flex>
</Flex>
</Modal>
)
}
11 changes: 6 additions & 5 deletions app/src/organisms/QuickTransferFlow/SelectDestLabware.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,28 @@
): JSX.Element | null {
const { onNext, onBack, exitButtonProps, state, dispatch } = props
const { i18n, t } = useTranslation(['quick_transfer', 'shared'])
if (state.pipette == null) return null
const labwareDisplayCategoryFilters: LabwareFilter[] = [
'all',
'wellPlate',
'reservoir',
]
if (state.pipette.channels === 1) {

Check failure on line 42 in app/src/organisms/QuickTransferFlow/SelectDestLabware.tsx

View workflow job for this annotation

GitHub Actions / js checks

'state.pipette' is possibly 'undefined'.

Check failure on line 42 in app/src/organisms/QuickTransferFlow/SelectDestLabware.tsx

View workflow job for this annotation

GitHub Actions / js checks

'state.pipette' is possibly 'undefined'.
labwareDisplayCategoryFilters.push('tubeRack')
}
const [selectedCategory, setSelectedCategory] = React.useState<LabwareFilter>(
'all'
)
const [selectedLabware, setSelectedLabware] = React.useState<
LabwareDefinition2 | 'source' | undefined
>(state.destination)

if (state.pipette == null) return null

const compatibleLabwareDefinitions = getCompatibleLabwareByCategory(
state.pipette.channels,
selectedCategory
)

const [selectedLabware, setSelectedLabware] = React.useState<
LabwareDefinition2 | 'source' | undefined
>(state.destination)

const handleClickNext = (): void => {
// the button will be disabled if this values is null
if (selectedLabware != null) {
Expand Down
13 changes: 7 additions & 6 deletions app/src/organisms/QuickTransferFlow/SelectSourceLabware.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,29 @@ export function SelectSourceLabware(
): JSX.Element | null {
const { onNext, onBack, exitButtonProps, state, dispatch } = props
const { i18n, t } = useTranslation(['quick_transfer', 'shared'])
if (state.pipette == null) return null
const labwareDisplayCategoryFilters: LabwareFilter[] = [
'all',
'wellPlate',
'reservoir',
]
if (state.pipette.channels === 1) {
if (state.pipette?.channels === 1) {
labwareDisplayCategoryFilters.push('tubeRack')
}
const [selectedCategory, setSelectedCategory] = React.useState<LabwareFilter>(
'all'
)

const [selectedLabware, setSelectedLabware] = React.useState<
LabwareDefinition2 | undefined
>(state.source)

if (state.pipette == null) return null

const compatibleLabwareDefinitions = getCompatibleLabwareByCategory(
state.pipette.channels,
selectedCategory
)

const [selectedLabware, setSelectedLabware] = React.useState<
LabwareDefinition2 | undefined
>(state.source)

const handleClickNext = (): void => {
// the button will be disabled if this values is null
if (selectedLabware != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest'

import { renderWithProviders } from '../../../__testing-utils__'
import { i18n } from '../../../i18n'
import { ConfirmExitModal } from '../ConfirmExitModal'

const render = (props: React.ComponentProps<typeof ConfirmExitModal>) => {
return renderWithProviders(<ConfirmExitModal {...props} />, {
i18nInstance: i18n,
})
}

describe('ConfirmExitModal', () => {
let props: React.ComponentProps<typeof ConfirmExitModal>

beforeEach(() => {
props = {
confirmExit: vi.fn(),
cancelExit: vi.fn(),
}
})
afterEach(() => {
vi.resetAllMocks()
})

it('renders the create new transfer screen and header', () => {
render(props)
screen.getByText('Exit quick transfer?')
screen.getByText('You will lose all progress on this quick transfer.')
})
it('renders exit and cancel buttons and they work as expected', () => {
render(props)
const cancelBtn = screen.getByText('Cancel')
fireEvent.click(cancelBtn)
expect(props.cancelExit).toHaveBeenCalled()
const deleteBtn = screen.getByText('Delete')
fireEvent.click(deleteBtn)
expect(props.confirmExit).toHaveBeenCalled()
})
})
37 changes: 26 additions & 11 deletions app/src/organisms/QuickTransferFlow/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import * as React from 'react'
import { useHistory } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { StepMeter, POSITION_STICKY } from '@opentrons/components'
import {
useConditionalConfirm,
StepMeter,
POSITION_STICKY,
} from '@opentrons/components'
import { SmallButton } from '../../atoms/buttons'
import { ConfirmExitModal } from './ConfirmExitModal'
import { CreateNewTransfer } from './CreateNewTransfer'
import { SelectPipette } from './SelectPipette'
import { SelectTipRack } from './SelectTipRack'
Expand All @@ -27,12 +32,16 @@ export const QuickTransferFlow = (): JSX.Element => {
)
const [currentStep, setCurrentStep] = React.useState(1)

const {
confirm: confirmExit,
showConfirmation: showConfirmExit,
cancel: cancelExit,
} = useConditionalConfirm(() => history.push('protocols'), true)

const exitButtonProps: React.ComponentProps<typeof SmallButton> = {
buttonType: 'tertiaryLowLight',
buttonText: i18n.format(t('shared:exit'), 'capitalize'),
onClick: () => {
history.push('protocols')
},
onClick: confirmExit,
}

React.useEffect(() => {
Expand Down Expand Up @@ -128,13 +137,19 @@ export const QuickTransferFlow = (): JSX.Element => {

return (
<>
<StepMeter
totalSteps={QUICK_TRANSFER_WIZARD_STEPS}
currentStep={currentStep}
position={POSITION_STICKY}
top="0"
/>
{modalContent}
{showConfirmExit ? (
<ConfirmExitModal confirmExit={confirmExit} cancelExit={cancelExit} />
) : (
<>
<StepMeter
totalSteps={QUICK_TRANSFER_WIZARD_STEPS}
currentStep={currentStep}
position={POSITION_STICKY}
top="0"
/>
{modalContent}
</>
)}
</>
)
}
10 changes: 4 additions & 6 deletions app/src/organisms/QuickTransferFlow/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,10 @@ export function getVolumeLimits(

const destLabwareVolume = Math.min(
...state.destinationWells.map(well => {
{
if (state.source == null || state.destination == null) return 0
return state.destination === 'source'
? state.source.wells[well].totalLiquidVolume
: state.destination.wells[well].totalLiquidVolume
}
if (state.source == null || state.destination == null) return 0
return state.destination === 'source'
? state.source.wells[well].totalLiquidVolume
: state.destination.wells[well].totalLiquidVolume
})
)
let maxVolume = maxPipetteVolume
Expand Down
Loading