diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx index 36b22c6ed3c..8acc69c8ab6 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx @@ -14,14 +14,8 @@ import { RECOVERY_MAP, ERROR_KINDS, ODD_SECTION_TITLE_STYLE, - ODD_ONLY, - DESKTOP_ONLY, } from '../constants' -import { - RecoveryODDOneDesktopTwoColumnContentWrapper, - RecoveryRadioGroup, - FailedStepNextStep, -} from '../shared' +import { RecoverySingleColumnContentWrapper } from '../shared' import type { ErrorKind, RecoveryContentProps, RecoveryRoute } from '../types' import type { PipetteWithTip } from '/app/organisms/DropTipWizardFlows' @@ -52,7 +46,7 @@ export function SelectRecoveryOptionHome({ currentRecoveryOptionUtils, getRecoveryOptionCopy, analytics, - ...rest + isOnDevice, }: RecoveryContentProps): JSX.Element | null { const { t } = useTranslation('error_recovery') const { proceedToRouteAndStep } = routeUpdateActions @@ -66,7 +60,7 @@ export function SelectRecoveryOptionHome({ useCurrentTipStatus(determineTipStatus) return ( - { analytics.reportActionSelectedEvent(selectedRoute) @@ -83,27 +77,16 @@ export function SelectRecoveryOptionHome({ > {t('choose_a_recovery_action')} - - - - - - + - - + ) } @@ -111,16 +94,18 @@ interface RecoveryOptionsProps { validRecoveryOptions: RecoveryRoute[] setSelectedRoute: (route: RecoveryRoute) => void getRecoveryOptionCopy: RecoveryContentProps['getRecoveryOptionCopy'] - errorKind: ErrorKind + errorKind: RecoveryContentProps['errorKind'] + isOnDevice: RecoveryContentProps['isOnDevice'] selectedRoute?: RecoveryRoute } -// For ODD use only. -export function ODDRecoveryOptions({ + +export function RecoveryOptions({ errorKind, validRecoveryOptions, selectedRoute, setSelectedRoute, getRecoveryOptionCopy, + isOnDevice, }: RecoveryOptionsProps): JSX.Element { return ( ) })} @@ -147,38 +133,6 @@ export function ODDRecoveryOptions({ ) } -export function DesktopRecoveryOptions({ - errorKind, - validRecoveryOptions, - selectedRoute, - setSelectedRoute, - getRecoveryOptionCopy, -}: RecoveryOptionsProps): JSX.Element { - return ( - { - setSelectedRoute(e.currentTarget.value) - }} - value={selectedRoute} - options={validRecoveryOptions.map( - (option: RecoveryRoute) => - ({ - value: option, - children: ( - - {getRecoveryOptionCopy(option, errorKind)} - - ), - } as const) - )} - /> - ) -} // Pre-fetch tip attachment status. Users are not blocked from proceeding at this step. export function useCurrentTipStatus( determineTipStatus: () => Promise @@ -254,7 +208,3 @@ export const GENERAL_ERROR_OPTIONS: RecoveryRoute[] = [ RECOVERY_MAP.RETRY_STEP.ROUTE, RECOVERY_MAP.CANCEL_RUN.ROUTE, ] - -const RADIO_GAP = ` - gap: ${SPACING.spacing4}; -` diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx index 0d9fae0f958..a0dd0c778ca 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx @@ -8,8 +8,7 @@ import { i18n } from '/app/i18n' import { mockRecoveryContentProps } from '../../__fixtures__' import { SelectRecoveryOption, - ODDRecoveryOptions, - DesktopRecoveryOptions, + RecoveryOptions, getRecoveryOptions, GENERAL_ERROR_OPTIONS, OVERPRESSURE_WHILE_ASPIRATING_OPTIONS, @@ -36,21 +35,13 @@ const renderSelectRecoveryOption = ( )[0] } -const renderODDRecoveryOptions = ( - props: React.ComponentProps +const renderRecoveryOptions = ( + props: React.ComponentProps ) => { - return renderWithProviders(, { + return renderWithProviders(, { i18nInstance: i18n, })[0] } -const renderDesktopRecoveryOptions = ( - props: React.ComponentProps -) => { - return renderWithProviders(, { - i18nInstance: i18n, - })[0] -} - describe('SelectRecoveryOption', () => { const { RETRY_STEP, RETRY_NEW_TIPS } = RECOVERY_MAP let props: React.ComponentProps @@ -241,194 +232,188 @@ describe('SelectRecoveryOption', () => { ) }) }) -;([ - ['desktop', renderDesktopRecoveryOptions] as const, - ['odd', renderODDRecoveryOptions] as const, -] as const).forEach(([target, renderer]) => { - describe(`RecoveryOptions on ${target}`, () => { - let props: React.ComponentProps - let mockSetSelectedRoute: Mock - let mockGetRecoveryOptionCopy: Mock - - beforeEach(() => { - mockSetSelectedRoute = vi.fn() - mockGetRecoveryOptionCopy = vi.fn() - const generalRecoveryOptions = getRecoveryOptions( - ERROR_KINDS.GENERAL_ERROR +describe('RecoveryOptions', () => { + let props: React.ComponentProps + let mockSetSelectedRoute: Mock + let mockGetRecoveryOptionCopy: Mock + + beforeEach(() => { + mockSetSelectedRoute = vi.fn() + mockGetRecoveryOptionCopy = vi.fn() + const generalRecoveryOptions = getRecoveryOptions(ERROR_KINDS.GENERAL_ERROR) + + props = { + errorKind: ERROR_KINDS.GENERAL_ERROR, + validRecoveryOptions: generalRecoveryOptions, + setSelectedRoute: mockSetSelectedRoute, + getRecoveryOptionCopy: mockGetRecoveryOptionCopy, + isOnDevice: true, + } + + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.RETRY_STEP.ROUTE, expect.any(String)) + .thenReturn('Retry step') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.RETRY_STEP.ROUTE, ERROR_KINDS.TIP_DROP_FAILED) + .thenReturn('Retry dropping tip') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.CANCEL_RUN.ROUTE, expect.any(String)) + .thenReturn('Cancel run') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.RETRY_NEW_TIPS.ROUTE, expect.any(String)) + .thenReturn('Retry with new tips') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.MANUAL_FILL_AND_SKIP.ROUTE, expect.any(String)) + .thenReturn('Manually fill well and skip to next step') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.RETRY_SAME_TIPS.ROUTE, expect.any(String)) + .thenReturn('Retry with same tips') + when(mockGetRecoveryOptionCopy) + .calledWith( + RECOVERY_MAP.SKIP_STEP_WITH_SAME_TIPS.ROUTE, + expect.any(String) ) + .thenReturn('Skip to next step with same tips') + when(mockGetRecoveryOptionCopy) + .calledWith( + RECOVERY_MAP.SKIP_STEP_WITH_NEW_TIPS.ROUTE, + expect.any(String) + ) + .thenReturn('Skip to next step with new tips') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.IGNORE_AND_SKIP.ROUTE, expect.any(String)) + .thenReturn('Ignore error and skip to next step') + when(mockGetRecoveryOptionCopy) + .calledWith(RECOVERY_MAP.MANUAL_MOVE_AND_SKIP.ROUTE, expect.any(String)) + .thenReturn('Manually move labware and skip to next step') + when(mockGetRecoveryOptionCopy) + .calledWith( + RECOVERY_MAP.MANUAL_REPLACE_AND_RETRY.ROUTE, + expect.any(String) + ) + .thenReturn('Manually replace labware on deck and retry step') + }) - props = { - errorKind: ERROR_KINDS.GENERAL_ERROR, - validRecoveryOptions: generalRecoveryOptions, - setSelectedRoute: mockSetSelectedRoute, - getRecoveryOptionCopy: mockGetRecoveryOptionCopy, - } - - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.RETRY_STEP.ROUTE, expect.any(String)) - .thenReturn('Retry step') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.RETRY_STEP.ROUTE, ERROR_KINDS.TIP_DROP_FAILED) - .thenReturn('Retry dropping tip') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.CANCEL_RUN.ROUTE, expect.any(String)) - .thenReturn('Cancel run') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.RETRY_NEW_TIPS.ROUTE, expect.any(String)) - .thenReturn('Retry with new tips') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.MANUAL_FILL_AND_SKIP.ROUTE, expect.any(String)) - .thenReturn('Manually fill well and skip to next step') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.RETRY_SAME_TIPS.ROUTE, expect.any(String)) - .thenReturn('Retry with same tips') - when(mockGetRecoveryOptionCopy) - .calledWith( - RECOVERY_MAP.SKIP_STEP_WITH_SAME_TIPS.ROUTE, - expect.any(String) - ) - .thenReturn('Skip to next step with same tips') - when(mockGetRecoveryOptionCopy) - .calledWith( - RECOVERY_MAP.SKIP_STEP_WITH_NEW_TIPS.ROUTE, - expect.any(String) - ) - .thenReturn('Skip to next step with new tips') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.IGNORE_AND_SKIP.ROUTE, expect.any(String)) - .thenReturn('Ignore error and skip to next step') - when(mockGetRecoveryOptionCopy) - .calledWith(RECOVERY_MAP.MANUAL_MOVE_AND_SKIP.ROUTE, expect.any(String)) - .thenReturn('Manually move labware and skip to next step') - when(mockGetRecoveryOptionCopy) - .calledWith( - RECOVERY_MAP.MANUAL_REPLACE_AND_RETRY.ROUTE, - expect.any(String) - ) - .thenReturn('Manually replace labware on deck and retry step') - }) + it('renders valid recovery options for a general error errorKind', () => { + renderRecoveryOptions(props) - it('renders valid recovery options for a general error errorKind', () => { - renderer(props) + screen.getByRole('label', { name: 'Retry step' }) + screen.getByRole('label', { name: 'Cancel run' }) + }) - screen.getByRole('label', { name: 'Retry step' }) - screen.getByRole('label', { name: 'Cancel run' }) - }) + it(`renders valid recovery options for a ${ERROR_KINDS.OVERPRESSURE_WHILE_ASPIRATING} errorKind`, () => { + props = { + ...props, + validRecoveryOptions: OVERPRESSURE_WHILE_ASPIRATING_OPTIONS, + } - it(`renders valid recovery options for a ${ERROR_KINDS.OVERPRESSURE_WHILE_ASPIRATING} errorKind`, () => { - props = { - ...props, - validRecoveryOptions: OVERPRESSURE_WHILE_ASPIRATING_OPTIONS, - } + renderRecoveryOptions(props) - renderer(props) + screen.getByRole('label', { name: 'Retry with new tips' }) + screen.getByRole('label', { name: 'Cancel run' }) + }) - screen.getByRole('label', { name: 'Retry with new tips' }) - screen.getByRole('label', { name: 'Cancel run' }) - }) + it('updates the selectedRoute when a new option is selected', () => { + renderRecoveryOptions(props) - it('updates the selectedRoute when a new option is selected', () => { - renderer(props) + fireEvent.click(screen.getByRole('label', { name: 'Cancel run' })) - fireEvent.click(screen.getByRole('label', { name: 'Cancel run' })) + expect(mockSetSelectedRoute).toHaveBeenCalledWith( + RECOVERY_MAP.CANCEL_RUN.ROUTE + ) + }) - expect(mockSetSelectedRoute).toHaveBeenCalledWith( - RECOVERY_MAP.CANCEL_RUN.ROUTE - ) + it(`renders valid recovery options for a ${ERROR_KINDS.NO_LIQUID_DETECTED} errorKind`, () => { + props = { + ...props, + validRecoveryOptions: NO_LIQUID_DETECTED_OPTIONS, + } + + renderRecoveryOptions(props) + + screen.getByRole('label', { + name: 'Manually fill well and skip to next step', }) + screen.getByRole('label', { name: 'Ignore error and skip to next step' }) + screen.getByRole('label', { name: 'Cancel run' }) + }) - it(`renders valid recovery options for a ${ERROR_KINDS.NO_LIQUID_DETECTED} errorKind`, () => { - props = { - ...props, - validRecoveryOptions: NO_LIQUID_DETECTED_OPTIONS, - } + it(`renders valid recovery options for a ${ERROR_KINDS.OVERPRESSURE_PREPARE_TO_ASPIRATE} errorKind`, () => { + props = { + ...props, + validRecoveryOptions: OVERPRESSURE_PREPARE_TO_ASPIRATE, + } - renderer(props) + renderRecoveryOptions(props) - screen.getByRole('label', { - name: 'Manually fill well and skip to next step', - }) - screen.getByRole('label', { name: 'Ignore error and skip to next step' }) - screen.getByRole('label', { name: 'Cancel run' }) - }) + screen.getByRole('label', { name: 'Retry with new tips' }) + screen.getByRole('label', { name: 'Retry with same tips' }) + screen.getByRole('label', { name: 'Cancel run' }) + }) + + it(`renders valid recovery options for a ${ERROR_KINDS.OVERPRESSURE_WHILE_DISPENSING} errorKind`, () => { + props = { + ...props, + validRecoveryOptions: OVERPRESSURE_WHILE_DISPENSING_OPTIONS, + } + + renderRecoveryOptions(props) + + screen.getByRole('label', { name: 'Skip to next step with same tips' }) + screen.getByRole('label', { name: 'Skip to next step with new tips' }) + screen.getByRole('label', { name: 'Cancel run' }) + }) - it(`renders valid recovery options for a ${ERROR_KINDS.OVERPRESSURE_PREPARE_TO_ASPIRATE} errorKind`, () => { - props = { - ...props, - validRecoveryOptions: OVERPRESSURE_PREPARE_TO_ASPIRATE, - } + it(`renders valid recovery options for a ${ERROR_KINDS.TIP_NOT_DETECTED} errorKind`, () => { + props = { + ...props, + validRecoveryOptions: TIP_NOT_DETECTED_OPTIONS, + } - renderer(props) + renderRecoveryOptions(props) - screen.getByRole('label', { name: 'Retry with new tips' }) - screen.getByRole('label', { name: 'Retry with same tips' }) - screen.getByRole('label', { name: 'Cancel run' }) + screen.getByRole('label', { + name: 'Retry step', }) + screen.getByRole('label', { + name: 'Ignore error and skip to next step', + }) + screen.getByRole('label', { name: 'Cancel run' }) + }) - it(`renders valid recovery options for a ${ERROR_KINDS.OVERPRESSURE_WHILE_DISPENSING} errorKind`, () => { - props = { - ...props, - validRecoveryOptions: OVERPRESSURE_WHILE_DISPENSING_OPTIONS, - } + it(`renders valid recovery options for a ${ERROR_KINDS.TIP_DROP_FAILED} errorKind`, () => { + props = { + ...props, + errorKind: ERROR_KINDS.TIP_DROP_FAILED, + validRecoveryOptions: TIP_DROP_FAILED_OPTIONS, + } - renderer(props) + renderRecoveryOptions(props) - screen.getByRole('label', { name: 'Skip to next step with same tips' }) - screen.getByRole('label', { name: 'Skip to next step with new tips' }) - screen.getByRole('label', { name: 'Cancel run' }) + screen.getByRole('label', { + name: 'Retry dropping tip', }) - - it(`renders valid recovery options for a ${ERROR_KINDS.TIP_NOT_DETECTED} errorKind`, () => { - props = { - ...props, - validRecoveryOptions: TIP_NOT_DETECTED_OPTIONS, - } - - renderer(props) - - screen.getByRole('label', { - name: 'Retry step', - }) - screen.getByRole('label', { - name: 'Ignore error and skip to next step', - }) - screen.getByRole('label', { name: 'Cancel run' }) + screen.getByRole('label', { + name: 'Ignore error and skip to next step', }) + screen.getByRole('label', { name: 'Cancel run' }) + }) - it(`renders valid recovery options for a ${ERROR_KINDS.TIP_DROP_FAILED} errorKind`, () => { - props = { - ...props, - errorKind: ERROR_KINDS.TIP_DROP_FAILED, - validRecoveryOptions: TIP_DROP_FAILED_OPTIONS, - } - - renderer(props) - - screen.getByRole('label', { - name: 'Retry dropping tip', - }) - screen.getByRole('label', { - name: 'Ignore error and skip to next step', - }) - screen.getByRole('label', { name: 'Cancel run' }) - }) + it(`renders valid recovery options for a ${ERROR_KINDS.GRIPPER_ERROR} errorKind`, () => { + props = { + ...props, + validRecoveryOptions: GRIPPER_ERROR_OPTIONS, + } + + renderRecoveryOptions(props) - it(`renders valid recovery options for a ${ERROR_KINDS.GRIPPER_ERROR} errorKind`, () => { - props = { - ...props, - validRecoveryOptions: GRIPPER_ERROR_OPTIONS, - } - - renderer(props) - - screen.getByRole('label', { - name: 'Manually move labware and skip to next step', - }) - screen.getByRole('label', { - name: 'Manually replace labware on deck and retry step', - }) - screen.getByRole('label', { name: 'Cancel run' }) + screen.getByRole('label', { + name: 'Manually move labware and skip to next step', + }) + screen.getByRole('label', { + name: 'Manually replace labware on deck and retry step', }) + screen.getByRole('label', { name: 'Cancel run' }) }) })