Skip to content

Commit

Permalink
feat(app): resolve single slot location conflict (#14158)
Browse files Browse the repository at this point in the history
adds the necessary processing to useDeckConfigurationCompatibility to surface a required protocol
single slot fixture conflict and provide the missing on-deck labware display name for that single
slot. updates ODD FixtureTable to fix a location conflict modal bug where incorrect data was shown
in the modal when multiple location conflicts exist

closes RAUT-885, RQA-1985
  • Loading branch information
brenthagen authored Dec 11, 2023
1 parent 11d65b3 commit 018d7bc
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 173 deletions.
15 changes: 6 additions & 9 deletions app/src/organisms/Devices/ProtocolRun/ProtocolRunSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ import {
SPACING,
TYPOGRAPHY,
} from '@opentrons/components'
import {
FLEX_ROBOT_TYPE,
getSimplestDeckConfigForProtocol,
OT2_ROBOT_TYPE,
} from '@opentrons/shared-data'
import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data'

import { Line } from '../../../atoms/structure'
import { StyledText } from '../../../atoms/text'
Expand Down Expand Up @@ -148,11 +144,12 @@ export function ProtocolRunSetup({

const hasModules = protocolAnalysis != null && modules.length > 0

const protocolDeckConfig = getSimplestDeckConfigForProtocol(protocolAnalysis)

const requiredDeckConfig = getRequiredDeckConfig(protocolDeckConfig)
// need config compatibility (including check for single slot conflicts)
const requiredDeckConfigCompatibility = getRequiredDeckConfig(
deckConfigCompatibility
)

const hasFixtures = requiredDeckConfig.length > 0
const hasFixtures = requiredDeckConfigCompatibility.length > 0

let moduleDescription: string = t(`${MODULE_SETUP_KEY}_description`, {
count: modules.length,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import type {
interface LocationConflictModalProps {
onCloseClick: () => void
cutoutId: CutoutId
missingLabwareDisplayName?: string | null
requiredFixtureId?: CutoutFixtureId
requiredModule?: ModuleModel
isOnDevice?: boolean
Expand All @@ -55,6 +56,7 @@ export const LocationConflictModal = (
const {
onCloseClick,
cutoutId,
missingLabwareDisplayName,
requiredFixtureId,
requiredModule,
isOnDevice = false,
Expand Down Expand Up @@ -96,6 +98,15 @@ export const LocationConflictModal = (
onCloseClick()
}

let protocolSpecifiesDisplayName = ''
if (missingLabwareDisplayName != null) {
protocolSpecifiesDisplayName = missingLabwareDisplayName
} else if (requiredFixtureId != null) {
protocolSpecifiesDisplayName = getFixtureDisplayName(requiredFixtureId)
} else if (requiredModule != null) {
protocolSpecifiesDisplayName = getModuleDisplayName(requiredModule)
}

return (
<Portal level="top">
{isOnDevice ? (
Expand Down Expand Up @@ -149,12 +160,7 @@ export const LocationConflictModal = (
{t('protocol_specifies')}
</StyledText>

<StyledText as="p">
{requiredFixtureId != null &&
getFixtureDisplayName(requiredFixtureId)}
{requiredModule != null &&
getModuleDisplayName(requiredModule)}
</StyledText>
<StyledText as="p">{protocolSpecifiesDisplayName}</StyledText>
</Flex>
<Flex
padding={SPACING.spacing24}
Expand Down Expand Up @@ -248,10 +254,7 @@ export const LocationConflictModal = (
</StyledText>
</Box>
<StyledText as="label">
{requiredFixtureId != null &&
getFixtureDisplayName(requiredFixtureId)}
{requiredModule != null &&
getModuleDisplayName(requiredModule)}
{protocolSpecifiesDisplayName}
</StyledText>
</Flex>
<Flex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ export function FixtureListItem({
cutoutId,
cutoutFixtureId,
compatibleCutoutFixtureIds,
missingLabwareDisplayName,
}: FixtureListItemProps): JSX.Element {
const { t } = useTranslation('protocol_setup')

const isCurrentFixtureCompatible =
cutoutFixtureId != null &&
compatibleCutoutFixtureIds.includes(cutoutFixtureId)
const isRequiredSingleSlotMissing = missingLabwareDisplayName != null
const isConflictingFixtureConfigured =
cutoutFixtureId != null && !SINGLE_SLOT_FIXTURES.includes(cutoutFixtureId)
let statusLabel
Expand Down Expand Up @@ -153,6 +155,7 @@ export function FixtureListItem({
<LocationConflictModal
onCloseClick={() => setShowLocationConflictModal(false)}
cutoutId={cutoutId}
missingLabwareDisplayName={missingLabwareDisplayName}
requiredFixtureId={compatibleCutoutFixtureIds[0]}
/>
) : null}
Expand Down Expand Up @@ -180,7 +183,8 @@ export function FixtureListItem({
width="60px"
height="54px"
src={
isCurrentFixtureCompatible
// show the current fixture for a missing single slot
isCurrentFixtureCompatible || isRequiredSingleSlotMissing
? getFixtureImage(cutoutFixtureId)
: getFixtureImage(compatibleCutoutFixtureIds?.[0])
}
Expand All @@ -191,7 +195,7 @@ export function FixtureListItem({
css={TYPOGRAPHY.pSemiBold}
marginLeft={SPACING.spacing20}
>
{isCurrentFixtureCompatible
{isCurrentFixtureCompatible || isRequiredSingleSlotMissing
? getFixtureDisplayName(cutoutFixtureId)
: getFixtureDisplayName(compatibleCutoutFixtureIds?.[0])}
</StyledText>
Expand Down Expand Up @@ -232,7 +236,7 @@ export function FixtureListItem({
}
>
<StyledText as="label" cursor="pointer">
{t('update_deck')}
{t('resolve')}
</StyledText>
</TertiaryButton>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react'
import { UseQueryResult } from 'react-query'
import { renderWithProviders } from '@opentrons/components'
import {
SINGLE_RIGHT_SLOT_FIXTURE,
STAGING_AREA_RIGHT_SLOT_FIXTURE,
TRASH_BIN_ADAPTER_FIXTURE,
} from '@opentrons/shared-data'
Expand Down Expand Up @@ -50,6 +51,9 @@ describe('LocationConflictModal', () => {
updateDeckConfiguration: mockUpdate,
} as any)
})
afterEach(() => {
jest.resetAllMocks()
})
it('should render the modal information for a fixture conflict', () => {
const { getByText, getAllByText, getByRole } = render(props)
getByText('Deck location conflict')
Expand Down Expand Up @@ -78,6 +82,33 @@ describe('LocationConflictModal', () => {
getByRole('button', { name: 'Update deck' }).click()
expect(mockUpdate).toHaveBeenCalled()
})
it('should render the modal information for a single slot fixture conflict', () => {
mockUseDeckConfigurationQuery.mockReturnValue({
data: [
{
cutoutId: 'cutoutB1',
cutoutFixtureId: TRASH_BIN_ADAPTER_FIXTURE,
},
],
} as UseQueryResult<DeckConfiguration>)
props = {
onCloseClick: jest.fn(),
cutoutId: 'cutoutB1',
requiredFixtureId: SINGLE_RIGHT_SLOT_FIXTURE,
missingLabwareDisplayName: 'a tiprack',
}
const { getByText, getAllByText, getByRole } = render(props)
getByText('Deck location conflict')
getByText('Slot B1')
getByText('Protocol specifies')
getByText('Currently configured')
getAllByText('Trash bin')
getByText('a tiprack')
getByRole('button', { name: 'Cancel' }).click()
expect(props.onCloseClick).toHaveBeenCalled()
getByRole('button', { name: 'Update deck' }).click()
expect(mockUpdate).toHaveBeenCalled()
})
it('should render correct info for a odd', () => {
props = {
...props,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const mockDeckConfigCompatibility: CutoutConfigAndCompatibility[] = [
compatibleCutoutFixtureIds: [
STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE,
],
missingLabwareDisplayName: null,
},
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ describe('SetupModuleAndDeck', () => {
cutoutId: 'cutoutA1',
cutoutFixtureId: 'trashBinAdapter',
requiredAddressableAreas: ['movableTrashA1'],
compatibleCutoutFixtureIds: ['trashBinAdapter'],
missingLabwareDisplayName: null,
},
])
const { getByRole, getByText } = render(props)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { useDeckConfigurationCompatibility } from '../../../../resources/deck_co
import {
getIsFixtureMismatch,
getRequiredDeckConfig,
// getUnmatchedSingleSlotFixtures,
} from '../../../../resources/deck_configuration/utils'
import { Tooltip } from '../../../../atoms/Tooltip'
import {
Expand Down Expand Up @@ -66,12 +65,6 @@ export const SetupModuleAndDeck = ({

const isFixtureMismatch = getIsFixtureMismatch(deckConfigCompatibility)

// TODO(bh, 2023-11-28): there is an unimplemented scenario where unmatched single slot fixtures need to be updated
// will need to additionally filter out module conflict unmatched fixtures, as these are represented in SetupModulesList
// const unmatchedSingleSlotFixtures = getUnmatchedSingleSlotFixtures(
// deckConfigCompatibility
// )

const requiredDeckConfigCompatibility = getRequiredDeckConfig(
deckConfigCompatibility
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ describe('ProtocolRunHeader', () => {
compatibleCutoutFixtureIds: [
STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE,
],
missingLabwareDisplayName: null,
},
])
when(mockGetIsFixtureMismatch).mockReturnValue(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ describe('ProtocolRunSetup', () => {
compatibleCutoutFixtureIds: [
STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE,
],
missingLabwareDisplayName: null,
},
])
when(mockGetRequiredDeckConfig).mockReturnValue([
Expand Down
Loading

0 comments on commit 018d7bc

Please sign in to comment.