Skip to content

Commit

Permalink
fix(app): Fix continuing with no tips selected during Error Recovery (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mjhuff authored Aug 27, 2024
1 parent 80a446a commit 73bee80
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ interface UseTipSelectionUtilsResult {
tipSelectorDef: LabwareDefinition2
selectTips: (tipGroup: WellGroup) => void
deselectTips: (locations: string[]) => void
areTipsSelected: boolean
}

// TODO(jh, 06-18-24): Enforce failure/warning when accessing tipSelectionUtils
Expand Down Expand Up @@ -215,11 +216,15 @@ function useTipSelectionUtils(
[]
)

const areTipsSelected =
selectedLocs != null && Object.keys(selectedLocs).length > 0

return {
selectedTipLocations: selectedLocs,
tipSelectorDef,
selectTips,
deselectTips,
areTipsSelected,
}
}

Expand Down
2 changes: 2 additions & 0 deletions app/src/organisms/ErrorRecoveryFlows/shared/SelectTips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function SelectTips(props: RecoveryContentProps): JSX.Element | null {
routeUpdateActions,
recoveryCommands,
isOnDevice,
failedLabwareUtils,
} = props
const { ROBOT_PICKING_UP_TIPS } = RECOVERY_MAP
const { pickUpTips } = recoveryCommands
Expand Down Expand Up @@ -75,6 +76,7 @@ export function SelectTips(props: RecoveryContentProps): JSX.Element | null {
</TwoColumn>
<RecoveryFooterButtons
primaryBtnOnClick={primaryBtnOnClick}
primaryBtnDisabled={!failedLabwareUtils.areTipsSelected}
secondaryBtnOnClick={goBackPrevStep}
primaryBtnTextOverride={t('pick_up_tips')}
{...buildTertiaryBtnProps()}
Expand Down
14 changes: 10 additions & 4 deletions app/src/organisms/ErrorRecoveryFlows/shared/TipSelectionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,23 @@ type TipSelectionModalProps = TipSelectionProps & {
export function TipSelectionModal(
props: TipSelectionModalProps
): JSX.Element | null {
const { toggleModal } = props
const { isOnDevice, toggleModal, failedLabwareUtils } = props
const { areTipsSelected } = failedLabwareUtils
const { t } = useTranslation('error_recovery')

// If users end up in a state in which they deselect all wells, don't let them escape this modal.
const modalHeader: ModalHeaderBaseProps = {
title: t('change_tip_pickup_location'),
hasExitIcon: true,
hasExitIcon: areTipsSelected,
}

if (props.isOnDevice) {
if (isOnDevice) {
return createPortal(
<Modal header={modalHeader} onOutsideClick={toggleModal} zIndex={15}>
<Modal
header={modalHeader}
onOutsideClick={areTipsSelected ? toggleModal : undefined}
zIndex={15}
>
<TipSelection {...props} />
</Modal>,
getTopPortalEl()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ describe('SelectTips', () => {
channels: 8,
},
} as any,
failedLabwareUtils: {
selectedTipLocations: { A1: null },
areTipsSelected: true,
} as any,
}

vi.mocked(TipSelectionModal).mockReturnValue(
Expand Down Expand Up @@ -138,4 +142,22 @@ describe('SelectTips', () => {
})
expect(tertiaryBtn[0]).toBeDisabled()
})

it('disables the primary button if tips are not selected', () => {
props = {
...props,
failedLabwareUtils: {
selectedTipLocations: null,
areTipsSelected: false,
} as any,
}

render(props)

const primaryBtn = screen.getAllByRole('button', {
name: 'Pick up tips',
})

expect(primaryBtn[0]).toBeDisabled()
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { describe, it, vi, beforeEach } from 'vitest'
import { describe, it, vi, beforeEach, expect } from 'vitest'
import { screen } from '@testing-library/react'

import { mockRecoveryContentProps } from '../../__fixtures__'
Expand All @@ -24,6 +24,10 @@ describe('TipSelectionModal', () => {
...mockRecoveryContentProps,
allowTipSelection: true,
toggleModal: vi.fn(),
failedLabwareUtils: {
selectedTipLocations: { A1: null },
areTipsSelected: true,
} as any,
}

vi.mocked(TipSelection).mockReturnValue(<div>MOCK TIP SELECTION</div>)
Expand All @@ -39,5 +43,17 @@ describe('TipSelectionModal', () => {
render(props)

screen.getByText('MOCK TIP SELECTION')
screen.getByLabelText('closeIcon')
})

it('prevents from users from exiting the modal if no well(s) are selected', () => {
props = {
...props,
failedLabwareUtils: { areTipsSelected: false } as any,
}

render(props)

expect(screen.queryByLabelText('closeIcon')).not.toBeInTheDocument()
})
})

0 comments on commit 73bee80

Please sign in to comment.