Skip to content

Commit

Permalink
feat(app): oDD labware setup map migration to using baseDeck
Browse files Browse the repository at this point in the history
closes RAUT-822
  • Loading branch information
jerader committed Oct 27, 2023
1 parent 53f9045 commit e4f78ba
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 66 deletions.
92 changes: 32 additions & 60 deletions app/src/organisms/ProtocolSetupLabware/LabwareMapViewModal.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,52 @@
import * as React from 'react'
import map from 'lodash/map'
import { useTranslation } from 'react-i18next'
import {
BaseDeck,
EXTENDED_DECK_CONFIG_FIXTURE,
LabwareRender,
} from '@opentrons/components'
import {
FLEX_ROBOT_TYPE,
getDeckDefFromRobotType,
LabwareDefinition2,
THERMOCYCLER_MODULE_V1,
} from '@opentrons/shared-data'
import { RunTimeCommand } from '@opentrons/shared-data'
import { BaseDeck, EXTENDED_DECK_CONFIG_FIXTURE } from '@opentrons/components'
import { FLEX_ROBOT_TYPE, THERMOCYCLER_MODULE_V1 } from '@opentrons/shared-data'

import { Modal } from '../../molecules/Modal'
import { getDeckConfigFromProtocolCommands } from '../../resources/deck_configuration/utils'
import { useFeatureFlag } from '../../redux/config'
import { getStandardDeckViewLayerBlockList } from '../Devices/ProtocolRun/utils/getStandardDeckViewLayerBlockList'
import { getLabwareRenderInfo } from '../Devices/ProtocolRun/utils/getLabwareRenderInfo'
import { useMostRecentCompletedAnalysis } from '../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { AttachedProtocolModuleMatch } from '../ProtocolSetupModulesAndDeck/utils'

import type {
CompletedProtocolAnalysis,
DeckDefinition,
LabwareDefinition2,
} from '@opentrons/shared-data'
import type { LoadedLabwareByAdapter } from '@opentrons/api-client'
import type { ModalHeaderBaseProps } from '../../molecules/Modal/types'

interface LabwareMapViewModalProps {
runId: string
attachedProtocolModuleMatches: AttachedProtocolModuleMatch[]
handleLabwareClick: (
labwareDef: LabwareDefinition2,
labwareId: string
) => void
onCloseClick: () => void
initialLoadedLabwareByAdapter: LoadedLabwareByAdapter
commands: RunTimeCommand[]
deckDef: DeckDefinition
mostRecentAnalysis: CompletedProtocolAnalysis | null
}

export function LabwareMapViewModal({
handleLabwareClick,
runId,
onCloseClick,
attachedProtocolModuleMatches,
initialLoadedLabwareByAdapter,
commands,
}: LabwareMapViewModalProps): JSX.Element {
export function LabwareMapViewModal(
props: LabwareMapViewModalProps
): JSX.Element {
const {
handleLabwareClick,
onCloseClick,
attachedProtocolModuleMatches,
initialLoadedLabwareByAdapter,
deckDef,
mostRecentAnalysis,
} = props
const { t } = useTranslation('protocol_setup')
const enableDeckConfig = useFeatureFlag('enableDeckConfiguration')

const deckConfig = enableDeckConfig
? EXTENDED_DECK_CONFIG_FIXTURE
: getDeckConfigFromProtocolCommands(commands)

const mostRecentAnalysis = useMostRecentCompletedAnalysis(runId)
const deckDef = getDeckDefFromRobotType(FLEX_ROBOT_TYPE)
: getDeckConfigFromProtocolCommands(mostRecentAnalysis?.commands ?? [])
const labwareRenderInfo =
mostRecentAnalysis != null
? getLabwareRenderInfo(mostRecentAnalysis, deckDef)
Expand All @@ -65,14 +58,7 @@ export function LabwareMapViewModal({
}

const moduleLocations = attachedProtocolModuleMatches.map(module => {
const {
moduleDef,
nestedLabwareDef,
nestedLabwareId,
slotName,
x,
y,
} = module
const { moduleDef, nestedLabwareDef, nestedLabwareId, slotName } = module
const labwareInAdapterInMod =
nestedLabwareId != null
? initialLoadedLabwareByAdapter[nestedLabwareId]
Expand All @@ -83,6 +69,7 @@ export function LabwareMapViewModal({
labwareInAdapterInMod?.result?.definition ?? nestedLabwareDef
const topLabwareId =
labwareInAdapterInMod?.result?.labwareId ?? nestedLabwareId

return {
moduleModel: moduleDef.model,
moduleLocation: { slotName },
Expand All @@ -91,25 +78,17 @@ export function LabwareMapViewModal({
? { lidMotorState: 'open' }
: {},
nestedLabwareDef: topLabwareDefinition,
moduleChildren:
topLabwareDefinition != null && topLabwareId != null ? (
<React.Fragment
key={`LabwareSetup_Labware_${topLabwareId}_${x}_${y}`}
>
<LabwareRender
definition={topLabwareDefinition}
onLabwareClick={() =>
handleLabwareClick(topLabwareDefinition, topLabwareId)
}
/>
</React.Fragment>
) : null,
onLabwareClick:
topLabwareDefinition != null && topLabwareId != null
? () => handleLabwareClick(topLabwareDefinition, topLabwareId)
: undefined,
moduleChildren: null,
}
})

const labwareLocations = map(
labwareRenderInfo,
({ x, y, labwareDef, slotName }, labwareId) => {
({ labwareDef, 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
Expand All @@ -121,16 +100,9 @@ export function LabwareMapViewModal({
labwareLocation: { slotName },
definition: topLabwareDefinition,
topLabwareId,
labwareChildren: (
<React.Fragment key={`LabwareSetup_Labware_${topLabwareId}_${x}${y}`}>
<LabwareRender
definition={topLabwareDefinition}
onLabwareClick={() =>
handleLabwareClick(topLabwareDefinition, topLabwareId)
}
/>
</React.Fragment>
),
onLabwareClick: () =>
handleLabwareClick(topLabwareDefinition, topLabwareId),
labwareChildren: null,
}
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import * as React from 'react'
import { StaticRouter } from 'react-router-dom'
import { when, resetAllWhenMocks } from 'jest-when'
import {
renderWithProviders,
BaseDeck,
EXTENDED_DECK_CONFIG_FIXTURE,
} from '@opentrons/components'
import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
import deckDefFixture from '@opentrons/shared-data/deck/fixtures/3/deckExample.json'
import fixture_tiprack_300_ul from '@opentrons/shared-data/labware/fixtures/2/fixture_tiprack_300_ul.json'
import { i18n } from '../../../i18n'
import { getDeckConfigFromProtocolCommands } from '../../../resources/deck_configuration/utils'
import { useFeatureFlag } from '../../../redux/config'
import { getLabwareRenderInfo } from '../../Devices/ProtocolRun/utils/getLabwareRenderInfo'
import { getStandardDeckViewLayerBlockList } from '../../Devices/ProtocolRun/utils/getStandardDeckViewLayerBlockList'
import { mockProtocolModuleInfo } from '../__fixtures__'
import { LabwareMapViewModal } from '../LabwareMapViewModal'

import type {
CompletedProtocolAnalysis,
DeckDefinition,
LabwareDefinition2,
ModuleModel,
} from '@opentrons/shared-data'

jest.mock('../../Devices/ProtocolRun/utils/getLabwareRenderInfo')
jest.mock('@opentrons/components/src/hardware-sim/Labware/LabwareRender')
jest.mock('@opentrons/components/src/hardware-sim/BaseDeck')
jest.mock('../../../resources/deck_configuration/utils')
jest.mock('../../../redux/config')

const mockGetLabwareRenderInfo = getLabwareRenderInfo as jest.MockedFunction<
typeof getLabwareRenderInfo
>
const mockGetDeckConfigFromProtocolCommands = getDeckConfigFromProtocolCommands as jest.MockedFunction<
typeof getDeckConfigFromProtocolCommands
>
const mockUseFeatureFlag = useFeatureFlag as jest.MockedFunction<
typeof useFeatureFlag
>

const mockBaseDeck = BaseDeck as jest.MockedFunction<typeof BaseDeck>
const MOCK_300_UL_TIPRACK_COORDS = [30, 40, 0]

const render = (props: React.ComponentProps<typeof LabwareMapViewModal>) => {
return renderWithProviders(
<StaticRouter>
<LabwareMapViewModal {...props} />
</StaticRouter>,
{
i18nInstance: i18n,
}
)[0]
}

describe('LabwareMapViewModal', () => {
beforeEach(() => {
mockGetLabwareRenderInfo.mockReturnValue({})
mockGetDeckConfigFromProtocolCommands.mockReturnValue([])
when(mockUseFeatureFlag)
.calledWith('enableDeckConfiguration')
.mockReturnValue(true)
})

afterEach(() => {
resetAllWhenMocks()
})
it('should render nothing on the deck and calls exit button', () => {
mockBaseDeck.mockReturnValue(<div>mock base deck</div>)

const props = {
handleLabwareClick: jest.fn(),
onCloseClick: jest.fn(),
deckDef: (deckDefFixture as unknown) as DeckDefinition,
mostRecentAnalysis: ({
commands: [],
labware: [],
} as unknown) as CompletedProtocolAnalysis,
initialLoadedLabwareByAdapter: {},
attachedProtocolModuleMatches: [],
}

const { getByText, getByLabelText } = render(props)
getByText('Map View')
getByText('mock base deck')
getByLabelText('closeIcon').click()
expect(props.onCloseClick).toHaveBeenCalled()
})

it('should render a deck with modules and labware', () => {
const mockLabwareLocations = [
{
labwareLocation: { slotName: 'C1' },
definition: fixture_tiprack_300_ul as LabwareDefinition2,
topLabwareId: '300_ul_tiprack_id',
onLabwareClick: expect.any(Function),
labwareChildren: null,
},
]
const mockModuleLocations = [
{
moduleModel: 'heaterShakerModuleV1' as ModuleModel,
moduleLocation: { slotName: 'B1' },
nestedLabwareDef: mockProtocolModuleInfo[0]
.nestedLabwareDef as LabwareDefinition2,
onLabwareClick: expect.any(Function),
moduleChildren: null,
innerProps: {},
},
]
when(mockBaseDeck)
.calledWith({
robotType: FLEX_ROBOT_TYPE,
deckLayerBlocklist: getStandardDeckViewLayerBlockList(FLEX_ROBOT_TYPE),
deckConfig: EXTENDED_DECK_CONFIG_FIXTURE,
labwareLocations: mockLabwareLocations,
moduleLocations: mockModuleLocations,
})
.mockReturnValue(<div>mock base deck</div>)
mockGetLabwareRenderInfo.mockReturnValue({
'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',
},
})
render({
handleLabwareClick: jest.fn(),
onCloseClick: jest.fn(),
deckDef: (deckDefFixture as unknown) as DeckDefinition,
mostRecentAnalysis: ({} as unknown) as CompletedProtocolAnalysis,
initialLoadedLabwareByAdapter: {},
attachedProtocolModuleMatches: [
{
...mockProtocolModuleInfo[0],
},
],
})
expect(mockBaseDeck).toHaveBeenCalled()
})
})
4 changes: 2 additions & 2 deletions app/src/organisms/ProtocolSetupLabware/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ export function ProtocolSetupLabware({
<Portal level="top">
{showDeckMapModal ? (
<LabwareMapViewModal
commands={mostRecentAnalysis?.commands ?? []}
runId={runId}
mostRecentAnalysis={mostRecentAnalysis}
deckDef={deckDef}
attachedProtocolModuleMatches={attachedProtocolModuleMatches}
handleLabwareClick={handleLabwareClick}
onCloseClick={() => setShowDeckMapModal(false)}
Expand Down
15 changes: 12 additions & 3 deletions components/src/hardware-sim/BaseDeck/BaseDeck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface BaseDeckProps {
definition: LabwareDefinition2
// generic prop to render self-positioned children for each labware
labwareChildren?: React.ReactNode
onLabwareClick?: () => void
}>
moduleLocations: Array<{
moduleModel: ModuleModel
Expand All @@ -51,6 +52,7 @@ interface BaseDeckProps {
innerProps?: React.ComponentProps<typeof Module>['innerProps']
// generic prop to render self-positioned children for each module
moduleChildren?: React.ReactNode
onLabwareClick?: () => void
}>
deckConfig?: DeckConfiguration
deckLayerBlocklist?: string[]
Expand Down Expand Up @@ -150,6 +152,7 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
nestedLabwareDef,
innerProps,
moduleChildren,
onLabwareClick,
}) => {
const slotDef = deckDef.locations.orderedSlots.find(
s => s.id === moduleLocation.slotName
Expand All @@ -167,15 +170,18 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
innerProps={innerProps}
>
{nestedLabwareDef != null ? (
<LabwareRender definition={nestedLabwareDef} />
<LabwareRender
definition={nestedLabwareDef}
onLabwareClick={onLabwareClick}
/>
) : null}
{moduleChildren}
</Module>
) : null
}
)}
{labwareLocations.map(
({ labwareLocation, definition, labwareChildren }) => {
({ labwareLocation, definition, labwareChildren, onLabwareClick }) => {
const slotDef = deckDef.locations.orderedSlots.find(
s =>
labwareLocation !== 'offDeck' &&
Expand All @@ -187,7 +193,10 @@ export function BaseDeck(props: BaseDeckProps): JSX.Element {
key={slotDef.id}
transform={`translate(${slotDef.position[0]},${slotDef.position[1]})`}
>
<LabwareRender definition={definition} />
<LabwareRender
definition={definition}
onLabwareClick={onLabwareClick}
/>
{labwareChildren}
</g>
) : null
Expand Down
2 changes: 1 addition & 1 deletion components/src/hardware-sim/Labware/LabwareRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export interface LabwareRenderProps {
export const LabwareRender = (props: LabwareRenderProps): JSX.Element => {
const { gRef } = props
const cornerOffsetFromSlot = props.definition.cornerOffsetFromSlot

return (
<g
transform={`translate(${cornerOffsetFromSlot.x}, ${cornerOffsetFromSlot.y})`}
Expand Down

0 comments on commit e4f78ba

Please sign in to comment.