diff --git a/app/src/molecules/DeckThumbnail/__tests__/DeckThumbnail.test.tsx b/app/src/molecules/DeckThumbnail/__tests__/DeckThumbnail.test.tsx
index a9cbf8e909b..d1d5fe35282 100644
--- a/app/src/molecules/DeckThumbnail/__tests__/DeckThumbnail.test.tsx
+++ b/app/src/molecules/DeckThumbnail/__tests__/DeckThumbnail.test.tsx
@@ -1,50 +1,108 @@
import * as React from 'react'
import { when, resetAllWhenMocks } from 'jest-when'
import {
- getRobotTypeFromLoadedLabware,
+ FLEX_ROBOT_TYPE,
getDeckDefFromRobotType,
+ getRobotTypeFromLoadedLabware,
+ OT2_ROBOT_TYPE,
} from '@opentrons/shared-data'
import ot2StandardDeckDef from '@opentrons/shared-data/deck/definitions/3/ot2_standard.json'
-import { renderWithProviders } from '@opentrons/components'
-import { simpleAnalysisFileFixture } from '@opentrons/api-client'
+import ot3StandardDeckDef from '@opentrons/shared-data/deck/definitions/3/ot3_standard.json'
+import fixture_tiprack_300_ul from '@opentrons/shared-data/labware/fixtures/2/fixture_tiprack_300_ul.json'
+import {
+ BaseDeck,
+ EXTENDED_DECK_CONFIG_FIXTURE,
+ partialComponentPropsMatcher,
+ renderWithProviders,
+} from '@opentrons/components'
+import {
+ parseInitialLoadedLabwareByAdapter,
+ parseLabwareInfoByLiquidId,
+ simpleAnalysisFileFixture,
+} from '@opentrons/api-client'
+
import { i18n } from '../../../i18n'
+import { useAttachedModules } from '../../../organisms/Devices/hooks'
+import { getStandardDeckViewLayerBlockList } from '../utils/getStandardDeckViewLayerBlockList'
+import { getDeckConfigFromProtocolCommands } from '../../../resources/deck_configuration/utils'
+import { getAttachedProtocolModuleMatches } from '../../../organisms/ProtocolSetupModulesAndDeck/utils'
+import { getProtocolModulesInfo } from '../../../organisms/Devices/ProtocolRun/utils/getProtocolModulesInfo'
+import { getLabwareRenderInfo } from '../../../organisms/Devices/ProtocolRun/utils/getLabwareRenderInfo'
+import { mockProtocolModuleInfo } from '../../../organisms/ProtocolSetupLabware/__fixtures__'
+import { mockFetchModulesSuccessActionPayloadModules } from '../../../redux/modules/__fixtures__'
import { DeckThumbnail } from '../'
-import type { LoadedLabware, RunTimeCommand } from '@opentrons/shared-data'
-jest.mock('@opentrons/shared-data', () => {
- const actualSharedData = jest.requireActual('@opentrons/shared-data')
- return {
- ...actualSharedData,
- getRobotTypeFromLoadedLabware: jest.fn(),
- getDeckDefFromRobotType: jest.fn(),
- }
-})
-jest.mock('@opentrons/components', () => {
- const actualComponents = jest.requireActual('@opentrons/components')
- return {
- ...actualComponents,
- Module: jest.fn(({ def, x, y, children }) => (
-
- mock Module ({x},{y}) {def.model} {children}
-
- )),
- LabwareRender: jest.fn(({ definition }) => (
- mock LabwareRender {definition.parameters.loadName}
- )),
- }
-})
+import type {
+ LabwareDefinition2,
+ LoadedLabware,
+ ModuleModel,
+ ModuleType,
+ RunTimeCommand,
+} from '@opentrons/shared-data'
+
+jest.mock('@opentrons/components/src/hardware-sim/BaseDeck')
+jest.mock('@opentrons/api-client')
+jest.mock('@opentrons/shared-data/js/helpers')
jest.mock('../../../redux/config')
+jest.mock('../../../resources/deck_configuration/utils')
+jest.mock('../../../organisms/ProtocolSetupModulesAndDeck/utils')
+jest.mock('../../../organisms/Devices/ProtocolRun/utils/getProtocolModulesInfo')
+jest.mock('../../../organisms/Devices/hooks')
+jest.mock('../../../organisms/Devices/ProtocolRun/utils/getLabwareRenderInfo')
-const mockgetRobotTypeFromLoadedLabware = getRobotTypeFromLoadedLabware as jest.MockedFunction<
+const mockGetRobotTypeFromLoadedLabware = getRobotTypeFromLoadedLabware as jest.MockedFunction<
typeof getRobotTypeFromLoadedLabware
>
-const mockgetDeckDefFromRobotType = getDeckDefFromRobotType as jest.MockedFunction<
+const mockGetDeckDefFromRobotType = getDeckDefFromRobotType as jest.MockedFunction<
typeof getDeckDefFromRobotType
>
+const mockParseInitialLoadedLabwareByAdapter = parseInitialLoadedLabwareByAdapter as jest.MockedFunction<
+ typeof parseInitialLoadedLabwareByAdapter
+>
+const mockParseLabwareInfoByLiquidId = parseLabwareInfoByLiquidId as jest.MockedFunction<
+ typeof parseLabwareInfoByLiquidId
+>
+const mockUseAttachedModules = useAttachedModules as jest.MockedFunction<
+ typeof useAttachedModules
+>
+const mockGetDeckConfigFromProtocolCommands = getDeckConfigFromProtocolCommands as jest.MockedFunction<
+ typeof getDeckConfigFromProtocolCommands
+>
+const mockGetLabwareRenderInfo = getLabwareRenderInfo as jest.MockedFunction<
+ typeof getLabwareRenderInfo
+>
+const mockGetProtocolModulesInfo = getProtocolModulesInfo as jest.MockedFunction<
+ typeof getProtocolModulesInfo
+>
+const mockGetAttachedProtocolModuleMatches = getAttachedProtocolModuleMatches as jest.MockedFunction<
+ typeof getAttachedProtocolModuleMatches
+>
+const mockBaseDeck = BaseDeck as jest.MockedFunction
+const protocolAnalysis = simpleAnalysisFileFixture as any
const commands: RunTimeCommand[] = simpleAnalysisFileFixture.commands as any
const labware: LoadedLabware[] = simpleAnalysisFileFixture.labware as any
+const MOCK_300_UL_TIPRACK_ID = '300_ul_tiprack_id'
+const MOCK_MAGNETIC_MODULE_COORDS = [10, 20, 0]
+const MOCK_SECOND_MAGNETIC_MODULE_COORDS = [100, 200, 0]
+const MOCK_300_UL_TIPRACK_COORDS = [30, 40, 0]
+const mockMagneticModule = {
+ moduleId: 'someMagneticModule',
+ model: 'magneticModuleV2' as ModuleModel,
+ type: 'magneticModuleType' as ModuleType,
+ labwareOffset: { x: 5, y: 5, z: 5 },
+ cornerOffsetFromSlot: { x: 1, y: 1, z: 1 },
+ dimensions: {
+ xDimension: 100,
+ yDimension: 100,
+ footprintXDimension: 50,
+ footprintYDimension: 50,
+ labwareInterfaceXDimension: 80,
+ labwareInterfaceYDimension: 120,
+ },
+ twoDimensionalRendering: { children: [] },
+}
const render = (props: React.ComponentProps) => {
return renderWithProviders(, {
@@ -54,47 +112,184 @@ const render = (props: React.ComponentProps) => {
describe('DeckThumbnail', () => {
beforeEach(() => {
- when(mockgetRobotTypeFromLoadedLabware)
+ when(mockGetRobotTypeFromLoadedLabware)
.calledWith(labware)
- .mockReturnValue('OT-2 Standard')
- when(mockgetDeckDefFromRobotType)
- .calledWith('OT-2 Standard')
+ .mockReturnValue(OT2_ROBOT_TYPE)
+ when(mockGetDeckDefFromRobotType)
+ .calledWith(OT2_ROBOT_TYPE)
.mockReturnValue(ot2StandardDeckDef as any)
+ when(mockParseInitialLoadedLabwareByAdapter)
+ .calledWith(commands)
+ .mockReturnValue({})
+ when(mockParseLabwareInfoByLiquidId)
+ .calledWith(commands)
+ .mockReturnValue({})
+ mockUseAttachedModules.mockReturnValue(
+ mockFetchModulesSuccessActionPayloadModules
+ )
+ when(mockGetDeckConfigFromProtocolCommands)
+ .calledWith(commands)
+ .mockReturnValue(EXTENDED_DECK_CONFIG_FIXTURE)
+ when(mockGetLabwareRenderInfo).mockReturnValue({})
+ when(mockGetProtocolModulesInfo)
+ .calledWith(protocolAnalysis, ot2StandardDeckDef as any)
+ .mockReturnValue(mockProtocolModuleInfo)
+ when(mockGetAttachedProtocolModuleMatches)
+ .calledWith(
+ mockFetchModulesSuccessActionPayloadModules,
+ mockProtocolModuleInfo
+ )
+ .mockReturnValue([
+ {
+ moduleId: mockMagneticModule.moduleId,
+ x: MOCK_MAGNETIC_MODULE_COORDS[0],
+ y: MOCK_MAGNETIC_MODULE_COORDS[1],
+ z: MOCK_MAGNETIC_MODULE_COORDS[2],
+ moduleDef: mockMagneticModule as any,
+ nestedLabwareDef: null,
+ nestedLabwareDisplayName: null,
+ nestedLabwareId: null,
+ slotName: '1',
+ protocolLoadOrder: 1,
+ attachedModuleMatch: null,
+ },
+ {
+ moduleId: mockMagneticModule.moduleId,
+ x: MOCK_SECOND_MAGNETIC_MODULE_COORDS[0],
+ y: MOCK_SECOND_MAGNETIC_MODULE_COORDS[1],
+ z: MOCK_SECOND_MAGNETIC_MODULE_COORDS[2],
+ moduleDef: mockMagneticModule as any,
+ nestedLabwareDef: null,
+ nestedLabwareDisplayName: null,
+ nestedLabwareId: null,
+ slotName: '2',
+ protocolLoadOrder: 0,
+ attachedModuleMatch: null,
+ },
+ ])
+ when(mockBaseDeck)
+ .calledWith(
+ partialComponentPropsMatcher({
+ robotType: OT2_ROBOT_TYPE,
+ deckLayerBlocklist: getStandardDeckViewLayerBlockList(OT2_ROBOT_TYPE),
+ })
+ )
+ .mockReturnValue(mock BaseDeck
)
})
afterEach(() => {
resetAllWhenMocks()
+ jest.clearAllMocks()
})
- it('renders loaded equipment from protocol analysis file', () => {
- const { queryByText } = render({ commands, labware })
- expect(queryByText('mock Module (0,0) magneticModuleV2')).not.toBeFalsy()
- expect(
- queryByText('mock Module (265,0) temperatureModuleV2')
- ).not.toBeFalsy()
- expect(
- queryByText('mock LabwareRender opentrons_96_tiprack_300ul')
- ).not.toBeFalsy()
- expect(
- queryByText(
- 'mock LabwareRender opentrons_24_aluminumblock_generic_2ml_screwcap'
- )
- ).not.toBeFalsy()
- expect(
- queryByText('mock LabwareRender nest_96_wellplate_100ul_pcr_full_skirt')
- ).not.toBeFalsy()
- })
it('renders an OT-2 deck view when the protocol is an OT-2 protocol', () => {
- when(mockgetRobotTypeFromLoadedLabware)
- .calledWith(labware)
- .mockReturnValue('OT-2 Standard')
- render({ commands, labware })
- expect(mockgetDeckDefFromRobotType).toHaveBeenCalledWith('OT-2 Standard')
+ const { getByText } = render({
+ protocolAnalysis: protocolAnalysis,
+ })
+ getByText('mock BaseDeck')
})
+
it('renders an OT-3 deck view when the protocol is an OT-3 protocol', () => {
- when(mockgetRobotTypeFromLoadedLabware)
+ // ToDo (kk:11/06/2023) update this test later
+ // const mockLabwareLocations = [
+ // {
+ // labwareLocation: { slotName: 'C1' },
+ // definition: fixture_tiprack_300_ul as LabwareDefinition2,
+ // topLabwareId: '300_ul_tiprack_id',
+ // topLabwareDisplayName: 'fresh tips',
+ // },
+ // ]
+ // const mockModuleLocations = [
+ // {
+ // moduleModel: 'magneticModuleV2',
+ // moduleLocation: { slotName: 'C1' },
+ // innerProps: {},
+ // nestedLabwareDef: null,
+ // },
+ // {
+ // moduleModel: 'magneticModuleV2',
+ // moduleLocation: { slotName: 'B1' },
+ // innerProps: {},
+ // nestedLabwareDef: null,
+ // },
+ // ]
+ when(mockGetRobotTypeFromLoadedLabware)
.calledWith(labware)
- .mockReturnValue('OT-3 Standard')
- render({ commands, labware })
- expect(mockgetDeckDefFromRobotType).toHaveBeenCalledWith('OT-3 Standard')
+ .mockReturnValue(FLEX_ROBOT_TYPE)
+ when(mockGetDeckDefFromRobotType)
+ .calledWith(FLEX_ROBOT_TYPE)
+ .mockReturnValue(ot3StandardDeckDef as any)
+ when(mockParseInitialLoadedLabwareByAdapter)
+ .calledWith(commands)
+ .mockReturnValue({})
+ mockUseAttachedModules.mockReturnValue(
+ mockFetchModulesSuccessActionPayloadModules
+ )
+ when(mockGetDeckConfigFromProtocolCommands)
+ .calledWith(commands)
+ .mockReturnValue(EXTENDED_DECK_CONFIG_FIXTURE)
+ when(mockGetLabwareRenderInfo).mockReturnValue({
+ [MOCK_300_UL_TIPRACK_ID]: {
+ labwareDef: fixture_tiprack_300_ul as LabwareDefinition2,
+ displayName: 'fresh tips',
+ x: MOCK_300_UL_TIPRACK_COORDS[0],
+ y: MOCK_300_UL_TIPRACK_COORDS[1],
+ z: MOCK_300_UL_TIPRACK_COORDS[2],
+ slotName: 'C1',
+ },
+ })
+ when(mockGetProtocolModulesInfo)
+ .calledWith(protocolAnalysis, ot3StandardDeckDef as any)
+ .mockReturnValue(mockProtocolModuleInfo)
+ when(mockGetAttachedProtocolModuleMatches)
+ .calledWith(
+ mockFetchModulesSuccessActionPayloadModules,
+ mockProtocolModuleInfo
+ )
+ .mockReturnValue([
+ {
+ moduleId: mockMagneticModule.moduleId,
+ x: MOCK_MAGNETIC_MODULE_COORDS[0],
+ y: MOCK_MAGNETIC_MODULE_COORDS[1],
+ z: MOCK_MAGNETIC_MODULE_COORDS[2],
+ moduleDef: mockMagneticModule as any,
+ nestedLabwareDef: null,
+ nestedLabwareDisplayName: null,
+ nestedLabwareId: null,
+ slotName: 'C1',
+ protocolLoadOrder: 1,
+ attachedModuleMatch: null,
+ },
+ {
+ moduleId: mockMagneticModule.moduleId,
+ x: MOCK_SECOND_MAGNETIC_MODULE_COORDS[0],
+ y: MOCK_SECOND_MAGNETIC_MODULE_COORDS[1],
+ z: MOCK_SECOND_MAGNETIC_MODULE_COORDS[2],
+ moduleDef: mockMagneticModule as any,
+ nestedLabwareDef: null,
+ nestedLabwareDisplayName: null,
+ nestedLabwareId: null,
+ slotName: 'B1',
+ protocolLoadOrder: 0,
+ attachedModuleMatch: null,
+ },
+ ])
+ when(mockBaseDeck)
+ .calledWith(
+ partialComponentPropsMatcher({
+ robotType: FLEX_ROBOT_TYPE,
+ deckLayerBlocklist: getStandardDeckViewLayerBlockList(
+ FLEX_ROBOT_TYPE
+ ),
+ deckConfig: EXTENDED_DECK_CONFIG_FIXTURE,
+ labwareLocations: expect.anything(),
+ moduleLocations: expect.anything(),
+ })
+ )
+ .mockReturnValue(mock BaseDeck
)
+
+ const { getByText } = render({
+ protocolAnalysis: protocolAnalysis,
+ })
+ getByText('mock BaseDeck')
})
})
diff --git a/app/src/molecules/DeckThumbnail/index.tsx b/app/src/molecules/DeckThumbnail/index.tsx
index 314896186f2..fed45696e55 100644
--- a/app/src/molecules/DeckThumbnail/index.tsx
+++ b/app/src/molecules/DeckThumbnail/index.tsx
@@ -1,179 +1,128 @@
import * as React from 'react'
-import { useSelector } from 'react-redux'
import map from 'lodash/map'
+import { BaseDeck } from '@opentrons/components'
import {
- RobotWorkSpace,
- Module,
- LabwareRender,
- SlotLabels,
- COLORS,
-} from '@opentrons/components'
-import {
- inferModuleOrientationFromXCoordinate,
- getModuleDef2,
getDeckDefFromRobotType,
getRobotTypeFromLoadedLabware,
THERMOCYCLER_MODULE_V1,
} from '@opentrons/shared-data'
import {
- parseInitialLoadedLabwareBySlot,
- parseInitialLoadedLabwareByModuleId,
- parseInitialLoadedModulesBySlot,
- parseLiquidsInLoadOrder,
- parseLabwareInfoByLiquidId,
parseInitialLoadedLabwareByAdapter,
+ parseLabwareInfoByLiquidId,
} from '@opentrons/api-client'
-import { getWellFillFromLabwareId } from '../../organisms/Devices/ProtocolRun/SetupLiquids/utils'
-import { getIsOnDevice } from '../../redux/config'
+
import { getStandardDeckViewLayerBlockList } from './utils/getStandardDeckViewLayerBlockList'
-import { getStandardDeckViewBox } from './utils/getStandardViewBox'
+import { getDeckConfigFromProtocolCommands } from '../../resources/deck_configuration/utils'
+import { getLabwareRenderInfo } from '../../organisms/Devices/ProtocolRun/utils/getLabwareRenderInfo'
+import { getProtocolModulesInfo } from '../../organisms/Devices/ProtocolRun/utils/getProtocolModulesInfo'
+import { useAttachedModules } from '../../organisms/Devices/hooks'
+import { getAttachedProtocolModuleMatches } from '../../organisms/ProtocolSetupModulesAndDeck/utils'
+import { getWellFillFromLabwareId } from '../../organisms/Devices/ProtocolRun/SetupLiquids/utils'
import type { StyleProps } from '@opentrons/components'
import type {
- DeckSlot,
- Liquid,
- LoadedLabware,
- RunTimeCommand,
+ CompletedProtocolAnalysis,
+ ProtocolAnalysisOutput,
} from '@opentrons/shared-data'
interface DeckThumbnailProps extends StyleProps {
- commands: RunTimeCommand[]
- labware: LoadedLabware[]
- liquids?: Liquid[]
+ protocolAnalysis: CompletedProtocolAnalysis | ProtocolAnalysisOutput | null
showSlotLabels?: boolean
}
-export function DeckThumbnail(props: DeckThumbnailProps): JSX.Element {
- const {
- commands,
- liquids,
- labware = [],
- showSlotLabels = false,
- ...styleProps
- } = props
- const robotType = getRobotTypeFromLoadedLabware(labware)
+export function DeckThumbnail(props: DeckThumbnailProps): JSX.Element | null {
+ const { protocolAnalysis, showSlotLabels = false, ...styleProps } = props
+ const attachedModules = useAttachedModules()
+
+ if (protocolAnalysis == null) return null
+
+ const robotType = getRobotTypeFromLoadedLabware(protocolAnalysis.labware)
const deckDef = getDeckDefFromRobotType(robotType)
- const initialLoadedLabwareBySlot = parseInitialLoadedLabwareBySlot(commands)
const initialLoadedLabwareByAdapter = parseInitialLoadedLabwareByAdapter(
- commands
+ protocolAnalysis.commands
+ )
+
+ const deckConfig = getDeckConfigFromProtocolCommands(
+ protocolAnalysis.commands
)
- const initialLoadedModulesBySlot = parseInitialLoadedModulesBySlot(commands)
- const initialLoadedLabwareByModuleId = parseInitialLoadedLabwareByModuleId(
- commands
+ const liquids = protocolAnalysis.liquids
+
+ const labwareRenderInfo =
+ protocolAnalysis != null
+ ? getLabwareRenderInfo(protocolAnalysis, deckDef)
+ : {}
+ const protocolModulesInfo =
+ protocolAnalysis != null
+ ? getProtocolModulesInfo(protocolAnalysis, deckDef)
+ : []
+ const attachedProtocolModuleMatches = getAttachedProtocolModuleMatches(
+ attachedModules,
+ protocolModulesInfo
)
- const liquidsInLoadOrder = parseLiquidsInLoadOrder(
- liquids != null ? liquids : [],
- commands
+ const labwareByLiquidId = parseLabwareInfoByLiquidId(
+ protocolAnalysis.commands
)
- const labwareByLiquidId = parseLabwareInfoByLiquidId(commands)
- const isOnDevice = useSelector(getIsOnDevice)
- // TODO(bh, 2023-7-12): replace with color constant when added to design system
- const deckFill = isOnDevice ? COLORS.light1 : '#e6e6e6'
- return (
- // PR #10488 changed size
- // revert the height
- // Note add offset 18px to right and left
-
- {({ deckSlotsById }) => (
- <>
- {map(deckSlotsById, (slot: DeckSlot, slotId: string) => {
- if (slot.matingSurfaceUnitVector == null) return null // if slot has no mating surface, don't render anything in it
+ const moduleLocations = attachedProtocolModuleMatches.map(module => {
+ const labwareInAdapterInMod =
+ module.nestedLabwareId != null
+ ? initialLoadedLabwareByAdapter[module.nestedLabwareId]
+ : null
+ // only rendering the labware on top most layer so
+ // either the adapter or the labware are rendered but not both
+ const topLabwareDefinition =
+ labwareInAdapterInMod?.result?.definition ?? module.nestedLabwareDef
+ const nestedLabwareWellFill = getWellFillFromLabwareId(
+ module.nestedLabwareId ?? '',
+ liquids,
+ labwareByLiquidId
+ )
+ // const labwareHasLiquid = !isEmpty(wellFill)
+ return {
+ moduleModel: module.moduleDef.model,
+ moduleLocation: { slotName: module.slotName },
+ nestedLabwareWellFill,
+ innerProps:
+ module.moduleDef.model === THERMOCYCLER_MODULE_V1
+ ? { lidMotorState: 'open' }
+ : {},
+ nestedLabwareDef: topLabwareDefinition,
+ }
+ })
- const moduleInSlot =
- slotId in initialLoadedModulesBySlot
- ? initialLoadedModulesBySlot[slotId]
- : null
- const labwareInSlot =
- slotId in initialLoadedLabwareBySlot
- ? initialLoadedLabwareBySlot[slotId]
- : null
- const labwareInModule =
- moduleInSlot?.result?.moduleId != null &&
- moduleInSlot.result.moduleId in initialLoadedLabwareByModuleId
- ? initialLoadedLabwareByModuleId[moduleInSlot.result.moduleId]
- : null
+ const labwareLocations = map(
+ labwareRenderInfo,
+ ({ labwareDef, displayName, slotName }, labwareId) => {
+ const labwareInAdapter = initialLoadedLabwareByAdapter[labwareId]
+ // only rendering the labware on top most layer so
+ // either the adapter or the labware are rendered but not both
+ const topLabwareDefinition =
+ labwareInAdapter?.result?.definition ?? labwareDef
+ const topLabwareId = labwareInAdapter?.result?.labwareId ?? labwareId
- let labwareId =
- labwareInSlot != null ? labwareInSlot.result?.labwareId : null
- let labwareInAdapter = null
+ const wellFill = getWellFillFromLabwareId(
+ topLabwareId ?? '',
+ liquids,
+ labwareByLiquidId
+ )
+ return {
+ labwareLocation: { slotName },
+ definition: topLabwareDefinition,
+ wellFill: wellFill,
+ }
+ }
+ )
- if (labwareInModule != null) {
- if (
- labwareInModule?.result != null &&
- 'labwareId' in labwareInModule.result &&
- labwareInModule.result.labwareId in
- initialLoadedLabwareByAdapter
- ) {
- labwareInAdapter =
- initialLoadedLabwareByAdapter[
- labwareInModule?.result.labwareId
- ]
- labwareId = labwareInAdapter.result?.labwareId
- } else {
- labwareId = labwareInModule.params.labwareId
- }
- }
- const wellFill =
- labwareId != null && liquids != null
- ? getWellFillFromLabwareId(
- labwareId,
- liquidsInLoadOrder,
- labwareByLiquidId
- )
- : null
- return (
-
- {moduleInSlot != null ? (
-
- {labwareInModule?.result?.definition != null ? (
-
- ) : null}
-
- ) : null}
- {labwareInSlot?.result?.definition != null ? (
-
-
-
- ) : null}
-
- )
- })}
- {showSlotLabels ? : null}
- >
- )}
-
+ return (
+
)
}
diff --git a/app/src/organisms/ChooseProtocolSlideout/index.tsx b/app/src/organisms/ChooseProtocolSlideout/index.tsx
index 3e7b7b1fdfe..67a4148d54e 100644
--- a/app/src/organisms/ChooseProtocolSlideout/index.tsx
+++ b/app/src/organisms/ChooseProtocolSlideout/index.tsx
@@ -36,6 +36,7 @@ import { useCreateRunFromProtocol } from '../ChooseRobotToRunProtocolSlideout/us
import { ApplyHistoricOffsets } from '../ApplyHistoricOffsets'
import { useOffsetCandidatesForAnalysis } from '../ApplyHistoricOffsets/hooks/useOffsetCandidatesForAnalysis'
+import { ProtocolAnalysisOutput } from '@opentrons/shared-data'
import type { Robot } from '../../redux/discovery/types'
import type { StoredProtocolData } from '../../redux/protocol-storage'
import type { State } from '../../redux/types'
@@ -162,6 +163,7 @@ export function ChooseProtocolSlideoutComponent(
}}
robotName={robot.name}
{...{ selectedProtocol, runCreationError, runCreationErrorCode }}
+ protocolAnalysis={selectedProtocol?.mostRecentAnalysis}
/>
) : null}
@@ -180,6 +182,7 @@ interface StoredProtocolListProps {
runCreationError: string | null
runCreationErrorCode: number | null
robotName: string
+ protocolAnalysis?: ProtocolAnalysisOutput | null
}
function StoredProtocolList(props: StoredProtocolListProps): JSX.Element {
@@ -189,6 +192,7 @@ function StoredProtocolList(props: StoredProtocolListProps): JSX.Element {
runCreationError,
runCreationErrorCode,
robotName,
+ protocolAnalysis,
} = props
const { t } = useTranslation(['device_details', 'shared'])
const storedProtocols = useSelector((state: State) =>
@@ -223,12 +227,9 @@ function StoredProtocolList(props: StoredProtocolListProps): JSX.Element {
height="4.25rem"
width="4.75rem"
>
-
+ {protocolAnalysis != null ? (
+
+ ) : null}
- )
+ const deckThumbnail =
const deckViewByAnalysisStatus = {
missing: ,
diff --git a/app/src/organisms/ProtocolsLanding/ProtocolCard.tsx b/app/src/organisms/ProtocolsLanding/ProtocolCard.tsx
index 03325d54046..33c89cdd094 100644
--- a/app/src/organisms/ProtocolsLanding/ProtocolCard.tsx
+++ b/app/src/organisms/ProtocolsLanding/ProtocolCard.tsx
@@ -170,12 +170,12 @@ function AnalysisInfo(props: AnalysisInfoProps): JSX.Element {
missing: ,
loading: ,
error: ,
- complete: (
-
- ),
+ complete:
+ mostRecentAnalysis != null ? (
+
+ ) : (
+
+ ),
}[analysisStatus]
}
diff --git a/app/src/pages/OnDeviceDisplay/ProtocolDetails/Deck.tsx b/app/src/pages/OnDeviceDisplay/ProtocolDetails/Deck.tsx
index 286213fb915..ce1f640a6ce 100644
--- a/app/src/pages/OnDeviceDisplay/ProtocolDetails/Deck.tsx
+++ b/app/src/pages/OnDeviceDisplay/ProtocolDetails/Deck.tsx
@@ -1,5 +1,7 @@
import * as React from 'react'
import last from 'lodash/last'
+
+import { Flex } from '@opentrons/components'
import {
useProtocolAnalysisAsDocumentQuery,
useProtocolQuery,
@@ -7,8 +9,6 @@ import {
import { DeckThumbnail } from '../../../molecules/DeckThumbnail'
-import type { CompletedProtocolAnalysis } from '@opentrons/shared-data'
-
export const Deck = (props: { protocolId: string }): JSX.Element => {
const { data: protocolData } = useProtocolQuery(props.protocolId)
const {
@@ -20,18 +20,10 @@ export const Deck = (props: { protocolId: string }): JSX.Element => {
)
return (
-
+
+ {mostRecentAnalysis != null ? (
+
+ ) : null}
+
)
}
diff --git a/app/src/pages/OnDeviceDisplay/ProtocolDetails/__tests__/Deck.test.tsx b/app/src/pages/OnDeviceDisplay/ProtocolDetails/__tests__/Deck.test.tsx
index 851d72ebec0..e634fe6ab68 100644
--- a/app/src/pages/OnDeviceDisplay/ProtocolDetails/__tests__/Deck.test.tsx
+++ b/app/src/pages/OnDeviceDisplay/ProtocolDetails/__tests__/Deck.test.tsx
@@ -1,17 +1,154 @@
import * as React from 'react'
-import { resetAllWhenMocks } from 'jest-when'
+import { when, resetAllWhenMocks } from 'jest-when'
+
import { renderWithProviders } from '@opentrons/components'
+import {
+ useProtocolAnalysisAsDocumentQuery,
+ useProtocolQuery,
+} from '@opentrons/react-api-client'
+
import { i18n } from '../../../../i18n'
import { DeckThumbnail } from '../../../../molecules/DeckThumbnail'
import { Deck } from '../Deck'
+import type { UseQueryResult } from 'react-query'
+import type { CompletedProtocolAnalysis } from '@opentrons/shared-data'
+import type { Protocol } from '@opentrons/api-client'
+
+jest.mock('@opentrons/react-api-client')
jest.mock('../../../../molecules/DeckThumbnail')
const mockDeckThumbnail = DeckThumbnail as jest.MockedFunction<
typeof DeckThumbnail
>
+const mockUseProtocolAnalysisAsDocumentQuery = useProtocolAnalysisAsDocumentQuery as jest.MockedFunction<
+ typeof useProtocolAnalysisAsDocumentQuery
+>
+const mockUseProtocolQuery = useProtocolQuery as jest.MockedFunction<
+ typeof useProtocolQuery
+>
-const MOCK_PROTOCOL_ID = 'mock_protocol_id'
+const MOCK_PROTOCOL_ID = 'mockProtocolId'
+const MOCK_PROTOCOL_ANALYSIS = {
+ id: 'fake_protocol_analysis',
+ commands: [
+ {
+ id: '97ba49a5-04f6-4f91-986a-04a0eb632882',
+ createdAt: '2022-09-07T19:47:42.781065+00:00',
+ commandType: 'loadPipette',
+ key: '0feeecaf-3895-46d7-ab71-564601265e35',
+ status: 'succeeded',
+ params: {
+ pipetteName: 'p20_single_gen2',
+ mount: 'left',
+ pipetteId: '90183a18-a1df-4fd6-9636-be3bcec63fe4',
+ },
+ result: {
+ pipetteId: '90183a18-a1df-4fd6-9636-be3bcec63fe4',
+ },
+ startedAt: '2022-09-07T19:47:42.782665+00:00',
+ completedAt: '2022-09-07T19:47:42.785061+00:00',
+ },
+ {
+ id: '846e0b7b-1e54-4f42-9ab1-964ebda45da5',
+ createdAt: '2022-09-07T19:47:42.781281+00:00',
+ commandType: 'loadLiquid',
+ key: '1870d1a2-8dcd-46f2-9e27-16578365913b',
+ status: 'succeeded',
+ params: {
+ liquidId: '1',
+ labwareId: 'mockLabwareId1',
+ volumeByWell: {
+ A2: 20,
+ B2: 20,
+ C2: 20,
+ D2: 20,
+ E2: 20,
+ F2: 20,
+ G2: 20,
+ H2: 20,
+ },
+ },
+ result: {},
+ startedAt: '2022-09-07T19:47:42.785987+00:00',
+ completedAt: '2022-09-07T19:47:42.786087+00:00',
+ },
+ {
+ id: '1e03ae10-7e9b-465c-bc72-21ab5706bfb0',
+ createdAt: '2022-09-07T19:47:42.781323+00:00',
+ commandType: 'loadLiquid',
+ key: '48df9766-04ff-4927-9f2d-4efdcf0b3df8',
+ status: 'succeeded',
+ params: {
+ liquidId: '1',
+ labwareId: 'mockLabwareId2',
+ volumeByWell: {
+ D3: 40,
+ },
+ },
+ result: {},
+ startedAt: '2022-09-07T19:47:42.786212+00:00',
+ completedAt: '2022-09-07T19:47:42.786285+00:00',
+ },
+ {
+ id: '1e03ae10-7e9b-465c-bc72-21ab5706bfb0',
+ createdAt: '2022-09-07T19:47:42.781323+00:00',
+ commandType: 'loadLiquid',
+ key: '48df9766-04ff-4927-9f2d-4efdcf0b3df8',
+ status: 'succeeded',
+ params: {
+ liquidId: '1',
+ labwareId: 'mockLabwareId2',
+ volumeByWell: {
+ A3: 33,
+ B3: 33,
+ C3: 33,
+ },
+ },
+ result: {},
+ startedAt: '2022-09-07T19:47:42.786212+00:00',
+ completedAt: '2022-09-07T19:47:42.786285+00:00',
+ },
+ {
+ id: 'e8596bb3-b650-4d62-9bb5-dfc6e9e63249',
+ createdAt: '2022-09-07T19:47:42.781363+00:00',
+ commandType: 'loadLiquid',
+ key: '69d19b03-fdcc-4964-a2f8-3cbb30f4ddf3',
+ status: 'succeeded',
+ params: {
+ liquidId: '0',
+ labwareId: 'mockLabwareId1',
+ volumeByWell: {
+ A1: 33,
+ B1: 33,
+ C1: 33,
+ D1: 33,
+ E1: 33,
+ F1: 33,
+ G1: 33,
+ H1: 33,
+ },
+ },
+ result: {},
+ startedAt: '2022-09-07T19:47:42.786347+00:00',
+ completedAt: '2022-09-07T19:47:42.786412+00:00',
+ },
+ ],
+ liquids: [
+ {
+ id: '1',
+ displayName: 'Saline',
+ description: 'mock liquid 2',
+ displayColor: '#b925ff',
+ },
+ {
+ id: '0',
+ displayName: 'Water',
+ description: 'mock liquid 1',
+ displayColor: '#50d5ff',
+ },
+ ],
+}
const render = (props: React.ComponentProps) => {
return renderWithProviders(, {
@@ -19,13 +156,27 @@ const render = (props: React.ComponentProps) => {
})
}
-describe('Hardware', () => {
+describe('Deck', () => {
let props: React.ComponentProps
beforeEach(() => {
props = {
protocolId: MOCK_PROTOCOL_ID,
}
mockDeckThumbnail.mockReturnValue(mock Deck Thumbnail
)
+ when(mockUseProtocolQuery)
+ .calledWith(MOCK_PROTOCOL_ID)
+ .mockReturnValue({
+ data: {
+ data: { analysisSummaries: [{ id: MOCK_PROTOCOL_ANALYSIS.id }] },
+ } as any,
+ } as UseQueryResult)
+ when(mockUseProtocolAnalysisAsDocumentQuery)
+ .calledWith(MOCK_PROTOCOL_ID, MOCK_PROTOCOL_ANALYSIS.id, {
+ enabled: true,
+ })
+ .mockReturnValue({
+ data: MOCK_PROTOCOL_ANALYSIS as any,
+ } as UseQueryResult)
})
afterEach(() => {
resetAllWhenMocks()
diff --git a/app/src/pages/OnDeviceDisplay/ProtocolDetails/index.tsx b/app/src/pages/OnDeviceDisplay/ProtocolDetails/index.tsx
index 5d6138cd626..a30684a038b 100644
--- a/app/src/pages/OnDeviceDisplay/ProtocolDetails/index.tsx
+++ b/app/src/pages/OnDeviceDisplay/ProtocolDetails/index.tsx
@@ -19,6 +19,7 @@ import {
truncateString,
TYPOGRAPHY,
POSITION_STICKY,
+ JUSTIFY_CENTER,
} from '@opentrons/components'
import {
useCreateRunMutation,
@@ -267,7 +268,14 @@ const ProtocolSectionContent = ({
protocolSection =
break
}
- return {protocolSection}
+ return (
+
+ {protocolSection}
+
+ )
}
export function ProtocolDetails(): JSX.Element | null {
diff --git a/components/src/hardware-sim/BaseDeck/BaseDeck.tsx b/components/src/hardware-sim/BaseDeck/BaseDeck.tsx
index 9cd4ff9b4c2..1fe12ccdb91 100644
--- a/components/src/hardware-sim/BaseDeck/BaseDeck.tsx
+++ b/components/src/hardware-sim/BaseDeck/BaseDeck.tsx
@@ -1,4 +1,5 @@
import * as React from 'react'
+
import {
RobotType,
getDeckDefFromRobotType,
@@ -35,12 +36,14 @@ import type { DeckConfiguration } from '@opentrons/shared-data'
import type { TrashLocation } from '../Deck/FlexTrash'
import type { StagingAreaLocation } from './StagingAreaFixture'
import type { WasteChuteLocation } from './WasteChuteFixture'
+import type { WellFill } from '../Labware'
interface BaseDeckProps {
robotType: RobotType
labwareLocations: Array<{
labwareLocation: LabwareLocation
definition: LabwareDefinition2
+ wellFill?: WellFill
// generic prop to render self-positioned children for each labware
labwareChildren?: React.ReactNode
onLabwareClick?: () => void
@@ -49,6 +52,7 @@ interface BaseDeckProps {
moduleModel: ModuleModel
moduleLocation: ModuleLocation
nestedLabwareDef?: LabwareDefinition2 | null
+ nestedLabwareWellFill?: WellFill
innerProps?: React.ComponentProps['innerProps']
// generic prop to render self-positioned children for each module
moduleChildren?: React.ReactNode
@@ -60,6 +64,7 @@ interface BaseDeckProps {
lightFill?: string
darkFill?: string
children?: React.ReactNode
+ showSlotLabels?: boolean
}
export function BaseDeck(props: BaseDeckProps): JSX.Element {
@@ -75,6 +80,7 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
deckConfig = STANDARD_SLOT_DECK_CONFIG_FIXTURE,
showExpansion = true,
children,
+ showSlotLabels = false,
} = props
const deckDef = getDeckDefFromRobotType(robotType)
@@ -153,6 +159,7 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
moduleModel,
moduleLocation,
nestedLabwareDef,
+ nestedLabwareWellFill,
innerProps,
moduleChildren,
onLabwareClick,
@@ -176,6 +183,7 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
) : null}
{moduleChildren}
@@ -184,7 +192,13 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
}
)}
{labwareLocations.map(
- ({ labwareLocation, definition, labwareChildren, onLabwareClick }) => {
+ ({
+ labwareLocation,
+ definition,
+ labwareChildren,
+ wellFill,
+ onLabwareClick,
+ }) => {
const slotDef = deckDef.locations.orderedSlots.find(
s =>
labwareLocation !== 'offDeck' &&
@@ -195,17 +209,21 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
{labwareChildren}
) : null
}
)}
-
+ {showSlotLabels ? (
+
+ ) : null}
{children}
)