From 192f12f3af5fdfbfbe306be753b15710a6e12da8 Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 28 May 2024 13:32:34 -0400 Subject: [PATCH 1/8] refactor the interventionmodal to support new designs --- .../__tests__/InterventionModal.test.tsx | 2 +- app/src/molecules/InterventionModal/index.tsx | 55 +++++++++++++++---- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx index 79512ce47d0..aba1dbe1f1e 100644 --- a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx +++ b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx @@ -16,7 +16,7 @@ describe('InterventionModal', () => { beforeEach(() => { props = { - heading: 'mock intervention heading', + iconHeading: 'mock intervention heading', children: 'mock intervention children', iconName: 'alert-circle', type: 'intervention-required', diff --git a/app/src/molecules/InterventionModal/index.tsx b/app/src/molecules/InterventionModal/index.tsx index c02adc18b64..d14385be987 100644 --- a/app/src/molecules/InterventionModal/index.tsx +++ b/app/src/molecules/InterventionModal/index.tsx @@ -1,4 +1,6 @@ import * as React from 'react' +import { useSelector } from 'react-redux' + import { ALIGN_CENTER, BORDERS, @@ -7,6 +9,7 @@ import { Flex, Icon, JUSTIFY_CENTER, + JUSTIFY_SPACE_BETWEEN, OVERFLOW_AUTO, POSITION_ABSOLUTE, POSITION_RELATIVE, @@ -15,6 +18,8 @@ import { } from '@opentrons/components' import type { IconName } from '@opentrons/components' +import { getIsOnDevice } from '../../redux/config' + export type ModalType = 'intervention-required' | 'error' const BASE_STYLE = { @@ -32,20 +37,29 @@ const BASE_STYLE = { const BORDER_STYLE_BASE = `6px ${BORDERS.styleSolid}` -const MODAL_STYLE = { +const MODAL_BASE_STYLE = { backgroundColor: COLORS.white, position: POSITION_RELATIVE, overflowY: OVERFLOW_AUTO, - maxHeight: '100%', - width: '47rem', borderRadius: BORDERS.borderRadius8, boxShadow: BORDERS.smallDropShadow, 'data-testid': '__otInterventionModal', } as const +const MODAL_DESKTOP_STYLE = { + ...MODAL_BASE_STYLE, + maxHeight: '100%', + width: '47rem', +} as const + +const MODAL_ODD_STYLE = { + ...MODAL_BASE_STYLE, + width: '62rem', + height: '35.5rem', +} as const + const HEADER_STYLE = { alignItems: ALIGN_CENTER, - gridGap: SPACING.spacing12, padding: `${SPACING.spacing20} ${SPACING.spacing32}`, color: COLORS.white, position: POSITION_STICKY, @@ -69,8 +83,12 @@ const INTERVENTION_REQUIRED_COLOR = COLORS.blue50 const ERROR_COLOR = COLORS.red50 export interface InterventionModalProps { - /** optional modal heading **/ - heading?: React.ReactNode + /** Optional modal title heading. Aligned to the left. */ + titleHeading?: React.ReactNode + /** Optional modal heading right of the icon. Aligned right if titleHeading is supplied, otherwise aligned left. **/ + iconHeading?: React.ReactNode + /** Optional onClick for the icon heading and icon. */ + iconHeadingOnClick?: () => void /** overall style hint */ type?: ModalType /** optional icon name */ @@ -86,21 +104,34 @@ export function InterventionModal(props: InterventionModalProps): JSX.Element { const border = `${BORDER_STYLE_BASE} ${ modalType === 'error' ? ERROR_COLOR : INTERVENTION_REQUIRED_COLOR }` + const headerJustifyContent = + props.titleHeading != null ? JUSTIFY_SPACE_BETWEEN : undefined + + const isOnDevice = useSelector(getIsOnDevice) + const modalStyle = isOnDevice ? MODAL_ODD_STYLE : MODAL_DESKTOP_STYLE + return ( { e.stopPropagation() }} > - - {props.iconName != null ? ( - - ) : null} - {props.heading != null ? props.heading : null} + + {props.titleHeading} + + {props.iconName != null ? ( + + ) : null} + {props.iconHeading != null ? props.iconHeading : null} + {props.children} From e431fdd32f76e2193e14f82af33855537ce609c0 Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 28 May 2024 13:32:53 -0400 Subject: [PATCH 2/8] create new singlecolumncontentwrapper component --- .../ErrorRecoveryFlows/BeforeBeginning.tsx | 10 ++---- .../ErrorRecoveryHeader.tsx | 2 ++ .../ErrorRecoveryWizard.tsx | 21 ++++++------- .../RecoveryOptions/CancelRun.tsx | 10 ++---- .../RecoveryOptions/RetryStep.tsx | 10 ++---- .../RecoveryOptions/SelectRecoveryOption.tsx | 14 +++------ .../RecoveryOptions/shared/index.ts | 1 - .../shared/RecoveryFooterButtons.tsx | 2 +- .../shared/RecoverySingleColumnContent.tsx | 31 +++++++++++++++++++ .../__tests__/RecoveryFooterButtons.test.tsx | 0 .../ErrorRecoveryFlows/shared/index.ts | 2 ++ .../IncompatibleModuleDesktopModalBody.tsx | 2 +- app/src/organisms/InterventionModal/index.tsx | 2 +- 13 files changed, 62 insertions(+), 45 deletions(-) delete mode 100644 app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/index.ts rename app/src/organisms/ErrorRecoveryFlows/{RecoveryOptions => }/shared/RecoveryFooterButtons.tsx (96%) create mode 100644 app/src/organisms/ErrorRecoveryFlows/shared/RecoverySingleColumnContent.tsx rename app/src/organisms/ErrorRecoveryFlows/{RecoveryOptions => }/shared/__tests__/RecoveryFooterButtons.test.tsx (100%) create mode 100644 app/src/organisms/ErrorRecoveryFlows/shared/index.ts diff --git a/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx b/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx index 02c8ecc4ba7..a297b714bbc 100644 --- a/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx @@ -16,6 +16,7 @@ import { BODY_TEXT_STYLE, ODD_SECTION_TITLE_STYLE, } from './constants' +import { RecoverySingleColumnContent } from './shared' import type { RecoveryContentProps } from './types' @@ -28,12 +29,7 @@ export function BeforeBeginning({ if (isOnDevice) { return ( - + {t('before_you_begin')} @@ -52,7 +48,7 @@ export function BeforeBeginning({ marginTop="auto" /> - + ) } else { return null diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx index 0c501149b82..7a594796a87 100644 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx @@ -31,6 +31,8 @@ export function ErrorRecoveryHeader({ const { t } = useTranslation('error_recovery') const errorName = useErrorName(errorKind) + // TOME: Delete this file. + return ( diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx index 3a721903985..758c8052794 100644 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx @@ -8,10 +8,12 @@ import { DIRECTION_COLUMN, Flex, POSITION_ABSOLUTE, + StyledText, } from '@opentrons/components' import { getIsOnDevice } from '../../redux/config' import { getTopPortalEl } from '../../App/portal' +import { InterventionModal } from '../../molecules/InterventionModal' import { BeforeBeginning } from './BeforeBeginning' import { SelectRecoveryOption, RetryStep, CancelRun } from './RecoveryOptions' import { ErrorRecoveryHeader } from './ErrorRecoveryHeader' @@ -86,21 +88,18 @@ export function ErrorRecoveryWizard( ) } +//TOME: Update types and fill stuff in as needed. function ErrorRecoveryComponent(props: RecoveryContentProps): JSX.Element { return createPortal( - View error details} + titleHeading={Error Recovery} + type={'error'} + iconHeadingOnClick={() => null} > - - , + , getTopPortalEl() ) } diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx index 91ff0f7985a..b1835b68d2f 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx @@ -13,7 +13,7 @@ import { } from '@opentrons/components' import { RECOVERY_MAP } from '../constants' -import { RecoveryFooterButtons } from './shared' +import { RecoveryFooterButtons, RecoverySingleColumnContent } from '../shared' import type { RecoveryContentProps } from '../types' @@ -34,13 +34,9 @@ export function CancelRun({ if (isOnDevice) { return ( - - + ) } else { return null diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx index 1f750a7df1c..778213fc112 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx @@ -12,7 +12,7 @@ import { } from '@opentrons/components' import { RECOVERY_MAP } from '../constants' -import { RecoveryFooterButtons } from './shared' +import { RecoveryFooterButtons, RecoverySingleColumnContent } from '../shared' import type { RecoveryContentProps } from '../types' @@ -35,13 +35,9 @@ export function RetryStep({ if (isOnDevice) { return ( - - + ) } else { return null diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx index 8acf05cedd3..7ac084ff458 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx @@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex, - JUSTIFY_SPACE_BETWEEN, SPACING, StyledText, } from '@opentrons/components' @@ -16,7 +15,7 @@ import { ODD_SECTION_TITLE_STYLE, } from '../constants' import { RadioButton } from '../../../atoms/buttons' -import { RecoveryFooterButtons } from './shared' +import { RecoveryFooterButtons, RecoverySingleColumnContent } from '../shared' import type { ErrorKind, RecoveryContentProps, RecoveryRoute } from '../types' @@ -34,14 +33,11 @@ export function SelectRecoveryOption({ head(validRecoveryOptions) as RecoveryRoute ) + // TOME: You need to abstract the wrapper component for all of these! Move the shared library up a bit and adjust it there. + // Then, do the if (isOnDevice) { return ( - + {t('how_do_you_want_to_proceed')} @@ -58,7 +54,7 @@ export function SelectRecoveryOption({ proceedToRoute(selectedRoute as RecoveryRoute) } /> - + ) } else { return null diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/index.ts b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/index.ts deleted file mode 100644 index 82d6cdb7120..00000000000 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { RecoveryFooterButtons } from './RecoveryFooterButtons' diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/RecoveryFooterButtons.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryFooterButtons.tsx similarity index 96% rename from app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/RecoveryFooterButtons.tsx rename to app/src/organisms/ErrorRecoveryFlows/shared/RecoveryFooterButtons.tsx index 316744a2d17..7f81e95c661 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/RecoveryFooterButtons.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryFooterButtons.tsx @@ -9,7 +9,7 @@ import { SPACING, } from '@opentrons/components' -import { SmallButton } from '../../../../atoms/buttons' +import { SmallButton } from '../../../atoms/buttons' interface RecoveryOptionProps { isOnDevice: boolean diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/RecoverySingleColumnContent.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/RecoverySingleColumnContent.tsx new file mode 100644 index 00000000000..202efc161a6 --- /dev/null +++ b/app/src/organisms/ErrorRecoveryFlows/shared/RecoverySingleColumnContent.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' + +import { + DIRECTION_COLUMN, + JUSTIFY_SPACE_BETWEEN, + SPACING, + Flex, +} from '@opentrons/components' + +import type { StyleProps } from '@opentrons/components' + +interface SingleColumnContentWrapperProps extends StyleProps { + children: React.ReactNode +} +// For flex-direction: column recovery content with one column only. +export function RecoverySingleColumnContent({ + children, + ...styleProps +}: SingleColumnContentWrapperProps): JSX.Element { + return ( + + {children} + + ) +} diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/__tests__/RecoveryFooterButtons.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx similarity index 100% rename from app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/shared/__tests__/RecoveryFooterButtons.test.tsx rename to app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/index.ts b/app/src/organisms/ErrorRecoveryFlows/shared/index.ts new file mode 100644 index 00000000000..7a4d99f06a3 --- /dev/null +++ b/app/src/organisms/ErrorRecoveryFlows/shared/index.ts @@ -0,0 +1,2 @@ +export { RecoveryFooterButtons } from './RecoveryFooterButtons' +export { RecoverySingleColumnContent } from './RecoverySingleColumnContent' diff --git a/app/src/organisms/IncompatibleModule/IncompatibleModuleDesktopModalBody.tsx b/app/src/organisms/IncompatibleModule/IncompatibleModuleDesktopModalBody.tsx index ac4c8c5993d..020ad066760 100644 --- a/app/src/organisms/IncompatibleModule/IncompatibleModuleDesktopModalBody.tsx +++ b/app/src/organisms/IncompatibleModule/IncompatibleModuleDesktopModalBody.tsx @@ -31,7 +31,7 @@ export function IncompatibleModuleDesktopModalBody({ const displayName = isFlex ? 'Flex' : 'OT-2' return ( ) : ( {headerTitle}} + iconHeading={{headerTitle}} iconName={iconName} type="intervention-required" > From 00d480997ca2e215d05b80f339f87b9d3a3c7555 Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 28 May 2024 16:37:22 -0400 Subject: [PATCH 3/8] delete the old ErrorRecoveryHeader --- .../ErrorRecoveryHeader.tsx | 92 ------------------- .../ErrorRecoveryWizard.tsx | 1 - .../__tests__/ErrorRecoveryHeader.test.tsx | 36 -------- 3 files changed, 129 deletions(-) delete mode 100644 app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx delete mode 100644 app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryHeader.test.tsx diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx deleted file mode 100644 index 7a594796a87..00000000000 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryHeader.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import * as React from 'react' -import { useTranslation } from 'react-i18next' -import { css } from 'styled-components' - -import { - Box, - DIRECTION_ROW, - BORDERS, - ALIGN_CENTER, - Flex, - JUSTIFY_SPACE_BETWEEN, - TYPOGRAPHY, - COLORS, - SPACING, - RESPONSIVENESS, - StyledText, - Icon, -} from '@opentrons/components' - -import { useErrorName } from './utils' -import { NON_DESIGN_SANCTIONED_COLOR_1 } from './constants' - -import type { ErrorKind } from './types' - -interface ErrorRecoveryHeaderProps { - errorKind: ErrorKind -} -export function ErrorRecoveryHeader({ - errorKind, -}: ErrorRecoveryHeaderProps): JSX.Element { - const { t } = useTranslation('error_recovery') - const errorName = useErrorName(errorKind) - - // TOME: Delete this file. - - return ( - - - - {t('recovery_mode')} - - - {errorName} - - - - - ) -} - -function AlertHeaderIcon(): JSX.Element { - return ( - - ) -} - -const BOX_STYLE = css` - background-color: ${NON_DESIGN_SANCTIONED_COLOR_1}; - @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { - border-radius: ${BORDERS.borderRadius12} ${BORDERS.borderRadius12} 0 0; - } -` -const HEADER_CONTAINER_STYLE = css` - flex-direction: ${DIRECTION_ROW}; - justify-content: ${JUSTIFY_SPACE_BETWEEN}; - padding: ${SPACING.spacing16} ${SPACING.spacing32}; - @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { - padding: 1.75rem ${SPACING.spacing32}; - } -` -const HEADER_TEXT_STYLE = css` - ${TYPOGRAPHY.pSemiBold} - color: ${COLORS.white}; - cursor: default; - - @media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} { - font-size: ${TYPOGRAPHY.fontSize22}; - font-weight: ${TYPOGRAPHY.fontWeightBold}; - line-height: ${TYPOGRAPHY.lineHeight28}; - } -` diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx index 758c8052794..bb9a12c8f5a 100644 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx @@ -16,7 +16,6 @@ import { getTopPortalEl } from '../../App/portal' import { InterventionModal } from '../../molecules/InterventionModal' import { BeforeBeginning } from './BeforeBeginning' import { SelectRecoveryOption, RetryStep, CancelRun } from './RecoveryOptions' -import { ErrorRecoveryHeader } from './ErrorRecoveryHeader' import { RecoveryInProgress } from './RecoveryInProgress' import { getErrorKind } from './utils' import { RECOVERY_MAP } from './constants' diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryHeader.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryHeader.test.tsx deleted file mode 100644 index 31e9f596728..00000000000 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryHeader.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react' -import { screen } from '@testing-library/react' -import { beforeEach, describe, it } from 'vitest' - -import { renderWithProviders } from '../../../__testing-utils__' -import { i18n } from '../../../i18n' -import { ErrorRecoveryHeader } from '../ErrorRecoveryHeader' -import { ERROR_KINDS } from '../constants' - -const render = (props: React.ComponentProps) => { - return renderWithProviders(, { - i18nInstance: i18n, - })[0] -} - -describe('ErrorRecoveryHeader', () => { - let props: React.ComponentProps - - beforeEach(() => { - props = { - errorKind: ERROR_KINDS.GENERAL_ERROR, - } - }) - - it('renders appropriate copy independent of errorKind', () => { - render(props) - - screen.getByText('Recovery Mode') - }) - - it('renders the appropriate header for a general error kind', () => { - render(props) - - screen.getByText('General error') - }) -}) From 354fdb84d8a194caa0c96dd489be4bdf2c6c4234 Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 28 May 2024 16:54:08 -0400 Subject: [PATCH 4/8] update interventionmodal values --- .../localization/en/error_recovery.json | 1 + app/src/molecules/InterventionModal/index.tsx | 1 + .../ErrorRecoveryWizard.tsx | 20 +++++++++++++++---- .../RecoveryOptions/SelectRecoveryOption.tsx | 2 -- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/src/assets/localization/en/error_recovery.json b/app/src/assets/localization/en/error_recovery.json index b80e6a04121..3b070cd3cac 100644 --- a/app/src/assets/localization/en/error_recovery.json +++ b/app/src/assets/localization/en/error_recovery.json @@ -21,5 +21,6 @@ "stand_back_resuming": "Stand back, resuming current step", "stand_back_retrying": "Stand back, retrying current command", "tip_not_detected": "Tip not detected", + "view_error_details": "View error details", "view_recovery_options": "View recovery options" } diff --git a/app/src/molecules/InterventionModal/index.tsx b/app/src/molecules/InterventionModal/index.tsx index d14385be987..f43ce12b16c 100644 --- a/app/src/molecules/InterventionModal/index.tsx +++ b/app/src/molecules/InterventionModal/index.tsx @@ -124,6 +124,7 @@ export function InterventionModal(props: InterventionModalProps): JSX.Element { {...HEADER_STYLE} backgroundColor={headerColor} justifyContent={headerJustifyContent} + onClick={props.iconHeadingOnClick} > {props.titleHeading} diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx index bb9a12c8f5a..4a16588124d 100644 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx @@ -29,6 +29,7 @@ import type { useRecoveryCommands, UseRecoveryCommandsResult, } from './useRecoveryCommands' +import { useTranslation } from 'react-i18next' interface UseERWizardResult { hasLaunchedRecovery: boolean @@ -87,15 +88,26 @@ export function ErrorRecoveryWizard( ) } -//TOME: Update types and fill stuff in as needed. function ErrorRecoveryComponent(props: RecoveryContentProps): JSX.Element { + const { t } = useTranslation('error_recovery') + + const buildTitleHeading = (): JSX.Element => { + const titleText = props.hasLaunchedRecovery + ? t('cancel_run') + : t('recovery_mode') + return {titleText} + } + + const buildIconHeading = (): JSX.Element => ( + {t('view_error_details')} + ) + return createPortal( View error details} - titleHeading={Error Recovery} + iconHeading={buildIconHeading()} + titleHeading={buildTitleHeading()} type={'error'} - iconHeadingOnClick={() => null} > , diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx index 7ac084ff458..88206c3eab2 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/SelectRecoveryOption.tsx @@ -33,8 +33,6 @@ export function SelectRecoveryOption({ head(validRecoveryOptions) as RecoveryRoute ) - // TOME: You need to abstract the wrapper component for all of these! Move the shared library up a bit and adjust it there. - // Then, do the if (isOnDevice) { return ( From 826d059437f93bbeb88886b82267fbaefc2b382b Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 28 May 2024 17:12:46 -0400 Subject: [PATCH 5/8] testsss --- .../__tests__/InterventionModal.test.tsx | 65 +++++++++++++++++-- app/src/molecules/InterventionModal/index.tsx | 2 +- .../ErrorRecoveryWizard.tsx | 10 +-- .../__tests__/ErrorRecoveryWizard.test.tsx | 48 +++++++++++--- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx index aba1dbe1f1e..060a96eb401 100644 --- a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx +++ b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx @@ -1,14 +1,33 @@ import * as React from 'react' -import { describe, it, expect, beforeEach } from 'vitest' +import { vi, describe, it, expect, beforeEach } from 'vitest' +import { when } from 'vitest-when' import '@testing-library/jest-dom/vitest' -import { screen } from '@testing-library/react' + +import { screen, fireEvent } from '@testing-library/react' import { COLORS, BORDERS } from '@opentrons/components' + +import { i18n } from '../../../i18n' import { renderWithProviders } from '../../../__testing-utils__' +import { getIsOnDevice } from '../../../redux/config' + import { InterventionModal } from '../' + import type { ModalType } from '../' +import type { State } from '../../../redux/types' + +vi.mock('../../../redux/config') + +const MOCK_STATE: State = { + config: { + isOnDevice: false, + }, +} as any const render = (props: React.ComponentProps) => { - return renderWithProviders()[0] + return renderWithProviders(, { + i18nInstance: i18n, + initialState: MOCK_STATE, + })[0] } describe('InterventionModal', () => { @@ -16,11 +35,12 @@ describe('InterventionModal', () => { beforeEach(() => { props = { - iconHeading: 'mock intervention heading', + iconHeading: 'mock intervention icon heading', children: 'mock intervention children', iconName: 'alert-circle', type: 'intervention-required', } + when(vi.mocked(getIsOnDevice)).calledWith(MOCK_STATE).thenReturn(false) }) ;(['intervention-required', 'error'] as ModalType[]).forEach(type => { const color = @@ -45,7 +65,7 @@ describe('InterventionModal', () => { it('renders passed elements', () => { render(props) screen.getByText('mock intervention children') - screen.getByText('mock intervention heading') + screen.getByText('mock intervention icon heading') }) it('renders an icon if an icon is specified', () => { const { container } = render(props) @@ -63,4 +83,39 @@ describe('InterventionModal', () => { ) expect(icon).toBeNull() }) + + it('renders title heading text if passed', () => { + props = { ...props, titleHeading: 'mock intervention title heading' } + render(props) + screen.getByText('mock intervention title heading') + }) + + it('fires an onClick when clicking on the iconHeading if passed', () => { + const mockOnClick = vi.fn() + props = { ...props, iconHeadingOnClick: mockOnClick } + render(props) + + fireEvent.click(screen.getByText('mock intervention icon heading')) + + expect(mockOnClick).toHaveBeenCalled() + }) + + it('renders the alternative desktop style when isOnDevice is false', () => { + render(props) + + expect(screen.getByTestId('__otInterventionModal')).toHaveStyle({ + width: '47rem', + maxHeight: '100%', + }) + }) + + it('renders the alternative ODD style when isOnDevice is true', () => { + when(vi.mocked(getIsOnDevice)).calledWith(MOCK_STATE).thenReturn(true) + render(props) + + expect(screen.getByTestId('__otInterventionModal')).toHaveStyle({ + width: '62rem', + height: '35.5rem', + }) + }) }) diff --git a/app/src/molecules/InterventionModal/index.tsx b/app/src/molecules/InterventionModal/index.tsx index f43ce12b16c..d0f60416b20 100644 --- a/app/src/molecules/InterventionModal/index.tsx +++ b/app/src/molecules/InterventionModal/index.tsx @@ -96,7 +96,7 @@ export interface InterventionModalProps { /** modal contents */ children: React.ReactNode } - +// TOME: Do ODD test styling and mocking! export function InterventionModal(props: InterventionModalProps): JSX.Element { const modalType = props.type ?? 'intervention-required' const headerColor = diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx index 4a16588124d..69e8f58fc6c 100644 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx @@ -88,13 +88,15 @@ export function ErrorRecoveryWizard( ) } -function ErrorRecoveryComponent(props: RecoveryContentProps): JSX.Element { +export function ErrorRecoveryComponent( + props: RecoveryContentProps +): JSX.Element { const { t } = useTranslation('error_recovery') const buildTitleHeading = (): JSX.Element => { const titleText = props.hasLaunchedRecovery - ? t('cancel_run') - : t('recovery_mode') + ? t('recovery_mode') + : t('cancel_run') return {titleText} } @@ -107,7 +109,7 @@ function ErrorRecoveryComponent(props: RecoveryContentProps): JSX.Element { iconName="information" iconHeading={buildIconHeading()} titleHeading={buildTitleHeading()} - type={'error'} + type="error" > , diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx index 3111bff953a..5c7ce7cef75 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx @@ -7,6 +7,7 @@ import { i18n } from '../../../i18n' import { mockRecoveryContentProps } from '../__fixtures__' import { ErrorRecoveryContent, + ErrorRecoveryComponent, useInitialPipetteHome, useERWizard, } from '../ErrorRecoveryWizard' @@ -47,7 +48,9 @@ describe('useERWizard', () => { }) }) -const render = (props: React.ComponentProps) => { +const renderRecoveryContent = ( + props: React.ComponentProps +) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -78,7 +81,7 @@ describe('ErrorRecoveryContent', () => { }) it(`returns SelectRecoveryOption when the route is ${OPTION_SELECTION.ROUTE}`, () => { - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_SELECT_RECOVERY_OPTION') }) @@ -91,7 +94,7 @@ describe('ErrorRecoveryContent', () => { route: BEFORE_BEGINNING.ROUTE, }, } - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_BEFORE_BEGINNING') }) @@ -104,7 +107,7 @@ describe('ErrorRecoveryContent', () => { route: RETRY_FAILED_COMMAND.ROUTE, }, } - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_RESUME_RUN') }) @@ -117,7 +120,7 @@ describe('ErrorRecoveryContent', () => { route: ROBOT_CANCELING.ROUTE, }, } - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_IN_PROGRESS') }) @@ -130,7 +133,7 @@ describe('ErrorRecoveryContent', () => { route: ROBOT_IN_MOTION.ROUTE, }, } - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_IN_PROGRESS') }) @@ -143,7 +146,7 @@ describe('ErrorRecoveryContent', () => { route: ROBOT_IN_MOTION.ROUTE, }, } - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_IN_PROGRESS') }) @@ -156,7 +159,7 @@ describe('ErrorRecoveryContent', () => { route: ROBOT_IN_MOTION.ROUTE, }, } - render(props) + renderRecoveryContent(props) screen.getByText('MOCK_IN_PROGRESS') }) @@ -231,3 +234,32 @@ describe('useInitialPipetteHome', () => { }) }) }) + +const renderRecoveryComponent = ( + props: React.ComponentProps +) => { + return renderWithProviders(, { + i18nInstance: i18n, + })[0] +} + +describe('ErrorRecoveryComponent', () => { + let props: React.ComponentProps + + beforeEach(() => { + props = mockRecoveryContentProps + }) + + it('renders an intervention modal with appropriate text', () => { + renderRecoveryComponent(props) + screen.getByTestId('__otInterventionModal') + screen.getByText('Recovery Mode') + screen.getByText('View error details') + }) + + it('renders alternative header text if the recovery mode has not been launched', () => { + props = { ...props, hasLaunchedRecovery: false } + renderRecoveryComponent(props) + screen.getByText('Cancel run') + }) +}) From ddd9c1ea483b2def2938d75c4e210d658891c8c6 Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Tue, 28 May 2024 21:35:30 -0400 Subject: [PATCH 6/8] lint --- app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx | 2 -- .../organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx | 9 +-------- .../ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx | 1 - .../ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx | 1 - 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx b/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx index a297b714bbc..0f51169032c 100644 --- a/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/BeforeBeginning.tsx @@ -4,8 +4,6 @@ import { Trans, useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex, - JUSTIFY_SPACE_BETWEEN, - SPACING, JUSTIFY_CENTER, StyledText, } from '@opentrons/components' diff --git a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx index 69e8f58fc6c..4dfdc7af784 100644 --- a/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/ErrorRecoveryWizard.tsx @@ -2,14 +2,7 @@ import * as React from 'react' import { createPortal } from 'react-dom' import { useSelector } from 'react-redux' -import { - BORDERS, - COLORS, - DIRECTION_COLUMN, - Flex, - POSITION_ABSOLUTE, - StyledText, -} from '@opentrons/components' +import { StyledText } from '@opentrons/components' import { getIsOnDevice } from '../../redux/config' import { getTopPortalEl } from '../../App/portal' diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx index b1835b68d2f..da9980a21fd 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/CancelRun.tsx @@ -7,7 +7,6 @@ import { COLORS, Flex, Icon, - JUSTIFY_SPACE_BETWEEN, SPACING, StyledText, } from '@opentrons/components' diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx index 778213fc112..283da580c9a 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/RetryStep.tsx @@ -6,7 +6,6 @@ import { DIRECTION_COLUMN, Flex, Icon, - JUSTIFY_SPACE_BETWEEN, SPACING, StyledText, } from '@opentrons/components' From 8d2489ac5b3dc42cc5df887778cfdf2cd413a70b Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Wed, 29 May 2024 09:24:16 -0400 Subject: [PATCH 7/8] test lint --- .../shared/__tests__/RecoveryFooterButtons.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx index e08ad726fa4..42ea142c246 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx @@ -2,8 +2,8 @@ import * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { screen, fireEvent } from '@testing-library/react' -import { renderWithProviders } from '../../../../../__testing-utils__' -import { i18n } from '../../../../../i18n' +import { renderWithProviders } from '../../../../__testing-utils__' +import { i18n } from '../../../../i18n' import { RecoveryFooterButtons } from '../RecoveryFooterButtons' import type { Mock } from 'vitest' From e900cbd0129e5a3db722801769fbded77b79f0b2 Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Wed, 29 May 2024 10:15:51 -0400 Subject: [PATCH 8/8] remove TOME --- app/src/molecules/InterventionModal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/molecules/InterventionModal/index.tsx b/app/src/molecules/InterventionModal/index.tsx index d0f60416b20..f43ce12b16c 100644 --- a/app/src/molecules/InterventionModal/index.tsx +++ b/app/src/molecules/InterventionModal/index.tsx @@ -96,7 +96,7 @@ export interface InterventionModalProps { /** modal contents */ children: React.ReactNode } -// TOME: Do ODD test styling and mocking! + export function InterventionModal(props: InterventionModalProps): JSX.Element { const modalType = props.type ?? 'intervention-required' const headerColor =