From 12b4a70f26d5678d05d6abd69b2e5b5fe0c32630 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Tue, 2 Jul 2024 17:19:23 -0400 Subject: [PATCH] refactor: use deckmap in error recovery Two places in error recovery, refactoring as we go. One big change was that there was a bunch of stuff called RecoveryMap as in recovery (deck) map, but recovery also has the Recovery Map, the big data structure that holds how all the steps of error recovery flow together. This is very confusing, so now what used to be RecoveryMap (component, utils) is now DeckMap. --- .../RecoveryOptions/FillWellAndSkip.tsx | 12 +- .../ErrorRecoveryFlows/__fixtures__/index.ts | 2 +- ...Utils.test.tsx => useDeckMapUtils.test.ts} | 24 ++-- ...ecoveryMapUtils.tsx => useDeckMapUtils.ts} | 103 +++++++++--------- .../ErrorRecoveryFlows/hooks/useERUtils.ts | 10 +- .../ErrorRecoveryFlows/shared/RecoveryMap.tsx | 74 ------------- .../ErrorRecoveryFlows/shared/ReplaceTips.tsx | 6 +- .../shared/__tests__/RecoveryMap.test.tsx | 62 ----------- .../ErrorRecoveryFlows/shared/index.ts | 1 - 9 files changed, 79 insertions(+), 215 deletions(-) rename app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/{useRecoveryMapUtils.test.tsx => useDeckMapUtils.test.ts} (94%) rename app/src/organisms/ErrorRecoveryFlows/hooks/{useRecoveryMapUtils.tsx => useDeckMapUtils.ts} (80%) delete mode 100644 app/src/organisms/ErrorRecoveryFlows/shared/RecoveryMap.tsx delete mode 100644 app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryMap.test.tsx diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/FillWellAndSkip.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/FillWellAndSkip.tsx index 7b7aaf7d68eb..fb21adc202a3 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/FillWellAndSkip.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/FillWellAndSkip.tsx @@ -14,10 +14,9 @@ import { RecoveryFooterButtons, RecoverySingleColumnContent, LeftColumnLabwareInfo, - RecoveryMap, TwoColTextAndFailedStepNextStep, } from '../shared' -import { TwoColumn } from '../../../molecules/InterventionModal' +import { TwoColumn, DeckMapContent } from '../../../molecules/InterventionModal' import { SelectRecoveryOption } from './SelectRecoveryOption' import type { RecoveryContentProps } from '../types' @@ -45,7 +44,12 @@ export function FillWellAndSkip(props: RecoveryContentProps): JSX.Element { } export function FillWell(props: RecoveryContentProps): JSX.Element | null { - const { isOnDevice, routeUpdateActions, failedLabwareUtils } = props + const { + isOnDevice, + routeUpdateActions, + failedLabwareUtils, + deckMapUtils, + } = props const { t } = useTranslation('error_recovery') const { goBackPrevStep, proceedNextStep } = routeUpdateActions @@ -63,7 +67,7 @@ export function FillWell(props: RecoveryContentProps): JSX.Element | null { /> - + null, diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryMapUtils.test.tsx b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useDeckMapUtils.test.ts similarity index 94% rename from app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryMapUtils.test.tsx rename to app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useDeckMapUtils.test.ts index 1a521443d787..4e341acda99e 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryMapUtils.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useDeckMapUtils.test.ts @@ -1,4 +1,3 @@ -import * as React from 'react' import { describe, it, expect, beforeEach, vi } from 'vitest' import { @@ -17,8 +16,7 @@ import { getRunCurrentModulesInfo, getRunCurrentLabwareOnDeck, getRunCurrentModulesOnDeck, -} from '../useRecoveryMapUtils' -import { LabwareHighlight } from '../../shared' +} from '../useDeckMapUtils' import type { LabwareDefinition2 } from '@opentrons/shared-data' @@ -78,13 +76,11 @@ describe('getRunCurrentModulesOnDeck', () => { moduleLocation: { slotName: 'A1' }, innerProps: {}, nestedLabwareDef: mockLabwareDef, - moduleChildren: ( - - ), + highlight: 'MOCK_MODULE_ID', }, ]) }) - it('should set moduleChildren to null if getIsLabwareMatch returns false', () => { + it('should set highlight to null if getIsLabwareMatch returns false', () => { const result = getRunCurrentModulesOnDeck({ failedLabwareUtils: mockFailedLabwareUtils, currentModulesInfo: [ @@ -95,10 +91,10 @@ describe('getRunCurrentModulesOnDeck', () => { ], }) - expect(result[0].moduleChildren).toBeNull() + expect(result[0].highlight).toBeNull() }) - it('should set moduleChildren to null if nestedLabwareDef is null', () => { + it('should set highlight to null if nestedLabwareDef is null', () => { const result = getRunCurrentModulesOnDeck({ failedLabwareUtils: mockFailedLabwareUtils, currentModulesInfo: [ @@ -106,7 +102,7 @@ describe('getRunCurrentModulesOnDeck', () => { ], }) - expect(result[0].moduleChildren).toBeNull() + expect(result[0].highlight).toBeNull() }) }) @@ -139,14 +135,12 @@ describe('getRunCurrentLabwareOnDeck', () => { { labwareLocation: { slotName: 'A1' }, definition: mockLabwareDef, - labwareChildren: ( - - ), + highlight: 'A1', }, ]) }) - it('should set labwareChildren to null if getIsLabwareMatch returns false', () => { + it('should set highlight to null if getIsLabwareMatch returns false', () => { const result = getRunCurrentLabwareOnDeck({ failedLabwareUtils: { ...mockFailedLabwareUtils, @@ -158,7 +152,7 @@ describe('getRunCurrentLabwareOnDeck', () => { currentLabwareInfo: [mockCurrentLabwareInfo], }) - expect(result[0].labwareChildren).toBeNull() + expect(result[0].highlight).toBeNull() }) }) diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryMapUtils.tsx b/app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts similarity index 80% rename from app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryMapUtils.tsx rename to app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts index e0e749be0b9a..3c8a45faabba 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryMapUtils.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/useDeckMapUtils.ts @@ -10,8 +10,6 @@ import { THERMOCYCLER_MODULE_V1, } from '@opentrons/shared-data' -import { LabwareHighlight } from '../shared' - import type { Run } from '@opentrons/api-client' import type { DeckDefinition, @@ -21,29 +19,33 @@ import type { LabwareLocation, CutoutConfigProtocolSpec, LoadedLabware, + RobotType, } from '@opentrons/shared-data' import type { ErrorRecoveryFlowsProps } from '..' import type { UseFailedLabwareUtilsResult } from './useFailedLabwareUtils' -interface UseRecoveryMapUtilsProps { +interface UseDeckMapUtilsProps { runId: ErrorRecoveryFlowsProps['runId'] protocolAnalysis: ErrorRecoveryFlowsProps['protocolAnalysis'] failedLabwareUtils: UseFailedLabwareUtilsResult runRecord?: Run } -export interface UseRecoveryMapUtilsResult { +export interface UseDeckMapUtilsResult { deckConfig: CutoutConfigProtocolSpec[] - runCurrentModules: RunCurrentModulesOnDeck[] - runCurrentLabware: RunCurrentLabwareOnDeck[] + modulesOnDeck: RunCurrentModulesOnDeck[] + labwareOnDeck: RunCurrentLabwareOnDeck[] + highlightLabwareEventuallyIn: string[] + kind: 'intervention' + robotType: RobotType } // Returns the utilities needed by the Recovery Deck Map. -export function useRecoveryMapUtils({ +export function useDeckMapUtils({ protocolAnalysis, runRecord, runId, failedLabwareUtils, -}: UseRecoveryMapUtilsProps): UseRecoveryMapUtilsResult { +}: UseDeckMapUtilsProps): UseDeckMapUtilsResult { const robotType = protocolAnalysis?.robotType ?? OT2_ROBOT_TYPE const deckConfig = getSimplestDeckConfigForProtocol(protocolAnalysis) const deckDef = getDeckDefFromRobotType(robotType) @@ -83,8 +85,23 @@ export function useRecoveryMapUtils({ return { deckConfig, - runCurrentModules, - runCurrentLabware, + modulesOnDeck: runCurrentModules.map( + ({ moduleModel, moduleLocation, innerProps, nestedLabwareDef }) => ({ + moduleModel, + moduleLocation, + innerProps, + nestedLabwareDef, + }) + ), + labwareOnDeck: runCurrentLabware.map(({ labwareLocation, definition }) => ({ + labwareLocation, + definition, + })), + highlightLabwareEventuallyIn: [...runCurrentModules, ...runCurrentLabware] + .map(el => el.highlight) + .filter(maybeSlot => maybeSlot != null) as string[], + kind: 'intervention', + robotType, } } @@ -101,7 +118,6 @@ interface RunCurrentModulesOnDeck { lidMotorState?: undefined } nestedLabwareDef: LabwareDefinition2 | null - moduleChildren: JSX.Element | null } // Builds the necessary module object expected by BaseDeck. @@ -109,62 +125,49 @@ export function getRunCurrentModulesOnDeck({ failedLabwareUtils, currentModulesInfo, }: { - failedLabwareUtils: UseRecoveryMapUtilsProps['failedLabwareUtils'] + failedLabwareUtils: UseDeckMapUtilsProps['failedLabwareUtils'] currentModulesInfo: RunCurrentModuleInfo[] -}): RunCurrentModulesOnDeck[] { +}): Array { const { failedLabware } = failedLabwareUtils return currentModulesInfo.map( - ({ moduleDef, slotName, nestedLabwareDef, nestedLabwareSlotName }) => { - const isLabwareMatch = getIsLabwareMatch( - nestedLabwareSlotName, - failedLabware - ) - - return { - moduleModel: moduleDef.model, - moduleLocation: { slotName }, - innerProps: - moduleDef.model === THERMOCYCLER_MODULE_V1 - ? { lidMotorState: 'open' } - : {}, - - nestedLabwareDef, - moduleChildren: - isLabwareMatch && nestedLabwareDef != null ? ( - - ) : null, - } - } + ({ moduleDef, slotName, nestedLabwareDef, nestedLabwareSlotName }) => ({ + moduleModel: moduleDef.model, + moduleLocation: { slotName }, + innerProps: + moduleDef.model === THERMOCYCLER_MODULE_V1 + ? { lidMotorState: 'open' } + : {}, + + nestedLabwareDef, + highlight: getIsLabwareMatch(nestedLabwareSlotName, failedLabware) + ? nestedLabwareSlotName + : null, + }) ) } interface RunCurrentLabwareOnDeck { labwareLocation: LabwareLocation definition: LabwareDefinition2 - labwareChildren: JSX.Element | null } // Builds the necessary labware object expected by BaseDeck. export function getRunCurrentLabwareOnDeck({ currentLabwareInfo, failedLabwareUtils, }: { - failedLabwareUtils: UseRecoveryMapUtilsProps['failedLabwareUtils'] + failedLabwareUtils: UseDeckMapUtilsProps['failedLabwareUtils'] currentLabwareInfo: RunCurrentLabwareInfo[] -}): RunCurrentLabwareOnDeck[] { +}): Array { const { failedLabware } = failedLabwareUtils - return currentLabwareInfo.map(({ slotName, labwareDef, labwareLocation }) => { - const isLabwareMatch = getIsLabwareMatch(slotName, failedLabware) - - return { + return currentLabwareInfo.map( + ({ slotName, labwareDef, labwareLocation }) => ({ labwareLocation, definition: labwareDef, - labwareChildren: isLabwareMatch ? ( - - ) : null, - } - }) + highlight: getIsLabwareMatch(slotName, failedLabware) ? slotName : null, + }) + ) } interface RunCurrentModuleInfo { @@ -181,8 +184,8 @@ export const getRunCurrentModulesInfo = ({ deckDef, protocolAnalysis, }: { - protocolAnalysis: UseRecoveryMapUtilsProps['protocolAnalysis'] - runRecord: UseRecoveryMapUtilsProps['runRecord'] + protocolAnalysis: UseDeckMapUtilsProps['protocolAnalysis'] + runRecord: UseDeckMapUtilsProps['runRecord'] deckDef: DeckDefinition }): RunCurrentModuleInfo[] => { if (runRecord == null || protocolAnalysis == null) { @@ -248,8 +251,8 @@ export function getRunCurrentLabwareInfo({ runRecord, protocolAnalysis, }: { - runRecord: UseRecoveryMapUtilsProps['runRecord'] - protocolAnalysis: UseRecoveryMapUtilsProps['protocolAnalysis'] + runRecord: UseDeckMapUtilsProps['runRecord'] + protocolAnalysis: UseDeckMapUtilsProps['protocolAnalysis'] }): RunCurrentLabwareInfo[] { if (runRecord == null || protocolAnalysis == null) { return [] diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/useERUtils.ts b/app/src/organisms/ErrorRecoveryFlows/hooks/useERUtils.ts index b5f414b05461..926d4ee8ff8a 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/useERUtils.ts +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/useERUtils.ts @@ -6,7 +6,7 @@ import { useRecoveryTipStatus } from './useRecoveryTipStatus' import { useRecoveryRouting } from './useRecoveryRouting' import { useFailedLabwareUtils } from './useFailedLabwareUtils' import { getFailedCommandPipetteInfo, getNextStep } from '../utils' -import { useRecoveryMapUtils } from './useRecoveryMapUtils' +import { useDeckMapUtils } from './useDeckMapUtils' import { useNotifyAllCommandsQuery, useNotifyRunQuery, @@ -21,7 +21,7 @@ import type { UseRouteUpdateActionsResult } from './useRouteUpdateActions' import type { UseRecoveryCommandsResult } from './useRecoveryCommands' import type { RecoveryTipStatusUtils } from './useRecoveryTipStatus' import type { UseFailedLabwareUtilsResult } from './useFailedLabwareUtils' -import type { UseRecoveryMapUtilsResult } from './useRecoveryMapUtils' +import type { UseDeckMapUtilsResult } from './useDeckMapUtils' import type { CurrentRecoveryOptionUtils } from './useRecoveryRouting' import type { StepCounts } from '../../../resources/protocols/hooks' @@ -37,7 +37,7 @@ export interface ERUtilsResults { recoveryCommands: UseRecoveryCommandsResult tipStatusUtils: RecoveryTipStatusUtils failedLabwareUtils: UseFailedLabwareUtilsResult - recoveryMapUtils: UseRecoveryMapUtilsResult + deckMapUtils: UseDeckMapUtilsResult getRecoveryOptionCopy: ReturnType failedPipetteInfo: PipetteData | null hasLaunchedRecovery: boolean @@ -109,7 +109,7 @@ export function useERUtils({ routeUpdateActions, }) - const recoveryMapUtils = useRecoveryMapUtils({ + const deckMapUtils = useDeckMapUtils({ runId, runRecord, protocolAnalysis, @@ -133,7 +133,7 @@ export function useERUtils({ tipStatusUtils, failedLabwareUtils, failedPipetteInfo, - recoveryMapUtils, + deckMapUtils, getRecoveryOptionCopy, stepCounts, commandAfterFailedCommand, diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryMap.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryMap.tsx deleted file mode 100644 index 9f6c1573bb58..000000000000 --- a/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryMap.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react' -import { css } from 'styled-components' - -import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' -import { - BaseDeck, - Box, - RobotCoordsForeignDiv, - COLORS, - DIRECTION_COLUMN, - DISPLAY_FLEX, - JUSTIFY_FLEX_END, -} from '@opentrons/components' - -import type { LabwareDefinition2 } from '@opentrons/shared-data' -import type { RecoveryContentProps } from '../types' - -export function RecoveryMap({ - isOnDevice, - recoveryMapUtils, -}: RecoveryContentProps): JSX.Element | null { - const { deckConfig, runCurrentModules, runCurrentLabware } = recoveryMapUtils - - if (isOnDevice) { - return ( - - ) - } else { - return null - } -} - -export function LabwareHighlight({ - highlight, - definition, -}: { - highlight: boolean - definition: LabwareDefinition2 -}): JSX.Element { - const width = definition.dimensions.xDimension - const height = definition.dimensions.yDimension - - return ( - - - - ) -} - -const HIGHLIGHT_STYLE = css` - border-radius: 7.04px; - border: 3px solid ${COLORS.blue50}; - box-shadow: 0 0 4px 3px #74b0ff; -` diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/ReplaceTips.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/ReplaceTips.tsx index 62815b403e89..cbf795778c87 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/ReplaceTips.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/ReplaceTips.tsx @@ -4,10 +4,9 @@ import { Flex } from '@opentrons/components' import { useTranslation } from 'react-i18next' import { RecoverySingleColumnContent } from './RecoverySingleColumnContent' -import { TwoColumn } from '../../../molecules/InterventionModal' +import { TwoColumn, DeckMapContent } from '../../../molecules/InterventionModal' import { RecoveryFooterButtons } from './RecoveryFooterButtons' import { LeftColumnLabwareInfo } from './LeftColumnLabwareInfo' -import { RecoveryMap } from './RecoveryMap' import type { RecoveryContentProps } from '../types' @@ -17,6 +16,7 @@ export function ReplaceTips(props: RecoveryContentProps): JSX.Element | null { routeUpdateActions, failedPipetteInfo, failedLabwareUtils, + deckMapUtils, } = props const { relevantWellName } = failedLabwareUtils const { proceedNextStep } = routeUpdateActions @@ -47,7 +47,7 @@ export function ReplaceTips(props: RecoveryContentProps): JSX.Element | null { bannerText={t('replace_tips_and_select_location')} /> - + { - const actual = await importOriginal() - return { - ...actual, - BaseDeck: vi - .fn() - .mockImplementation(props =>
MOCK_BASE_DECK
), - } -}) - -const render = (props: React.ComponentProps) => { - return renderWithProviders(, { - i18nInstance: i18n, - })[0] -} - -describe('RecoveryMap', () => { - let props: React.ComponentProps - const mockDeckConfig = 'MOCK_DECK_CONFIG' - const mockRunCurrentModules = 'MOCK_RUN_MODULES' - const mockRunCurrentLw = 'MOCK_RUN_LW' - - const mockRecoveryMapUtils = { - deckConfig: mockDeckConfig, - runCurrentModules: mockRunCurrentModules, - runCurrentLabware: mockRunCurrentLw, - } as any - - beforeEach(() => { - props = { - ...mockRecoveryContentProps, - recoveryMapUtils: mockRecoveryMapUtils, - } - }) - - it('renders the BaseDeck with appropriate props when on ODD', () => { - render(props) - - screen.getByText('MOCK_BASE_DECK') - expect(vi.mocked(BaseDeck)).toHaveBeenCalledWith( - { - deckConfig: mockDeckConfig, - robotType: FLEX_ROBOT_TYPE, - modulesOnDeck: mockRunCurrentModules, - labwareOnDeck: mockRunCurrentLw, - }, - {} - ) - }) -}) diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/index.ts b/app/src/organisms/ErrorRecoveryFlows/shared/index.ts index 72ca995e2677..48c78d1ddd13 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/index.ts +++ b/app/src/organisms/ErrorRecoveryFlows/shared/index.ts @@ -6,7 +6,6 @@ export { export { ReplaceTips } from './ReplaceTips' export { SelectTips } from './SelectTips' export { TwoColTextAndFailedStepNextStep } from './TwoColTextAndFailedStepNextStep' -export { RecoveryMap, LabwareHighlight } from './RecoveryMap' export { LeftColumnLabwareInfo } from './LeftColumnLabwareInfo' export { TipSelection } from './TipSelection' export { TipSelectionModal } from './TipSelectionModal'