diff --git a/components/src/hardware-sim/DeckConfigurator/index.tsx b/components/src/hardware-sim/DeckConfigurator/index.tsx
index 4a03ff6c866..65bb6968a45 100644
--- a/components/src/hardware-sim/DeckConfigurator/index.tsx
+++ b/components/src/hardware-sim/DeckConfigurator/index.tsx
@@ -38,6 +38,8 @@ import { MagneticBlockFixture } from './MagneticBlockFixture'
import { ThermocyclerFixture } from './ThermocyclerFixture'
import { AbsorbanceReaderFixture } from './AbsorbanceReaderFixture'
+export * from './constants'
+
interface DeckConfiguratorProps {
deckConfig: DeckConfiguration
handleClickAdd: (cutoutId: CutoutId) => void
diff --git a/components/src/molecules/DropdownMenu/index.tsx b/components/src/molecules/DropdownMenu/index.tsx
index 3fb38f8f531..3b692c0b3dc 100644
--- a/components/src/molecules/DropdownMenu/index.tsx
+++ b/components/src/molecules/DropdownMenu/index.tsx
@@ -24,6 +24,7 @@ import { MenuItem } from '../../atoms/MenuList/MenuItem'
import { Tooltip } from '../../atoms/Tooltip'
import { StyledText } from '../../atoms/StyledText'
import { LiquidIcon } from '../LiquidIcon'
+import { DeckInfoLabel } from '../DeckInfoLabel'
export interface DropdownOption {
name: string
@@ -32,6 +33,8 @@ export interface DropdownOption {
liquidColor?: string
/** optional dropdown option for adding the deck label */
deckLabel?: string
+ /** subtext below the name */
+ subtext?: string
disabled?: boolean
tooltipText?: string
}
@@ -250,7 +253,11 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
{currentOption.liquidColor != null ? (
) : null}
+ {currentOption.deckLabel != null ? (
+
+ ) : null}
) : null}
- {option.name}
+ {option.deckLabel != null ? (
+
+ ) : null}
+
+
+ {option.name}
+
+
+ {option.subtext}
+
+
{option.tooltipText != null ? (
diff --git a/protocol-designer/src/molecules/DropdownStepFormField/index.tsx b/protocol-designer/src/molecules/DropdownStepFormField/index.tsx
index 2714a73156b..d40fcc7063f 100644
--- a/protocol-designer/src/molecules/DropdownStepFormField/index.tsx
+++ b/protocol-designer/src/molecules/DropdownStepFormField/index.tsx
@@ -1,8 +1,10 @@
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import {
+ ALIGN_CENTER,
COLORS,
DIRECTION_COLUMN,
+ DeckInfoLabel,
DropdownMenu,
Flex,
ListItem,
@@ -108,10 +110,28 @@ export function DropdownStepFormField(
{title}
-
-
- {options[0].name}
-
+
+ {options[0].deckLabel != null ? (
+
+ ) : null}
+
+
+ {options[0].name}
+
+
+ {options[0].subtext}
+
+
diff --git a/protocol-designer/src/organisms/MaterialsListModal/__tests__/MaterialsListModal.test.tsx b/protocol-designer/src/organisms/MaterialsListModal/__tests__/MaterialsListModal.test.tsx
index d18fa1e52de..26e2d0fb098 100644
--- a/protocol-designer/src/organisms/MaterialsListModal/__tests__/MaterialsListModal.test.tsx
+++ b/protocol-designer/src/organisms/MaterialsListModal/__tests__/MaterialsListModal.test.tsx
@@ -137,7 +137,7 @@ describe('MaterialsListModal', () => {
lidTargetTemp: null,
lidOpen: false,
},
- slot: 'span7_8_10_11',
+ slot: '7',
type: 'thermocyclerModuleType',
},
] as ModuleOnDeck[]
diff --git a/protocol-designer/src/top-selectors/labware-locations/index.ts b/protocol-designer/src/top-selectors/labware-locations/index.ts
index 34cd5fad561..6a2c5143b00 100644
--- a/protocol-designer/src/top-selectors/labware-locations/index.ts
+++ b/protocol-designer/src/top-selectors/labware-locations/index.ts
@@ -166,8 +166,8 @@ export const getUnoccupiedLabwareLocationOptions: Selector<
{
name:
modIdWithAdapter != null
- ? `${adapterDisplayName} on top of ${moduleUnderAdapter} in slot ${moduleSlotInfo}`
- : `${adapterDisplayName} on slot ${adapterSlotInfo}`,
+ ? `${moduleSlotInfo} on ${moduleUnderAdapter} with ${adapterDisplayName}`
+ : `${adapterSlotInfo} with ${adapterDisplayName}`,
value: labwareId,
},
]
@@ -186,13 +186,9 @@ export const getUnoccupiedLabwareLocationOptions: Selector<
: [
...acc,
{
- name: `${getModuleDisplayName(
+ name: `${modOnDeck.slot} on ${getModuleDisplayName(
moduleEntities[modId].model
- )} in slot ${
- modOnDeck.slot === 'span7_8_10_11'
- ? '7, 8, 10, 11'
- : modOnDeck.slot
- }`,
+ )}`,
value: modId,
},
]
@@ -234,7 +230,7 @@ export const getUnoccupiedLabwareLocationOptions: Selector<
)
})
.map(slotId => ({ name: slotId, value: slotId }))
- const offDeck = { name: 'Off-Deck', value: 'offDeck' }
+ const offDeck = { name: 'Off-deck', value: 'offDeck' }
const wasteChuteSlot = {
name: 'Waste Chute in D3',
value: WASTE_CHUTE_CUTOUT,
diff --git a/protocol-designer/src/ui/labware/__tests__/selectors.test.ts b/protocol-designer/src/ui/labware/__tests__/selectors.test.ts
index 00359673dbb..e2c74b75508 100644
--- a/protocol-designer/src/ui/labware/__tests__/selectors.test.ts
+++ b/protocol-designer/src/ui/labware/__tests__/selectors.test.ts
@@ -9,7 +9,6 @@ import {
THERMOCYCLER_MODULE_TYPE,
THERMOCYCLER_MODULE_V1,
} from '@opentrons/shared-data'
-import { SPAN7_8_10_11_SLOT } from '../../../constants'
import {
getDisposalOptions,
getLabwareOptions,
@@ -102,7 +101,7 @@ describe('labware selectors', () => {
expect(
// @ts-expect-error(sa, 2021-6-15): resultFunc
getDisposalOptions.resultFunc(additionalEquipmentEntities)
- ).toEqual([{ name: 'Trash Bin', value: mockTrashId }])
+ ).toEqual([{ name: 'Trash bin', value: mockTrashId }])
})
it('filters out additional equipment that is NOT trash when multiple trash bins present', () => {
const mockTrashId = 'mockTrashId'
@@ -129,8 +128,8 @@ describe('labware selectors', () => {
// @ts-expect-error(sa, 2021-6-15): resultFunc
getDisposalOptions.resultFunc(additionalEquipmentEntities)
).toEqual([
- { name: 'Trash Bin', value: mockTrashId },
- { name: 'Trash Bin', value: mockTrashId2 },
+ { name: 'Trash bin', value: mockTrashId },
+ { name: 'Trash bin', value: mockTrashId2 },
])
})
})
@@ -142,7 +141,12 @@ describe('labware selectors', () => {
getLabwareOptions.resultFunc(
{},
{},
- { labware: {}, modules: {}, pipettes: {} },
+ {
+ labware: {},
+ modules: {},
+ pipettes: {},
+ additionalEquipmentOnDeck: {},
+ },
{},
{},
{}
@@ -153,13 +157,13 @@ describe('labware selectors', () => {
it('should return labware options when no modules are present, with no tipracks', () => {
const labwareEntities = {
...tipracks,
- ...trash,
...otherLabware,
}
const initialDeckSetup = {
labware: labwareEntities,
modules: {},
pipettes: {},
+ additionalEquipmentOnDeck: {},
}
expect(
// @ts-expect-error(sa, 2021-6-15): resultFunc
@@ -171,13 +175,10 @@ describe('labware selectors', () => {
{},
{}
)
- ).toEqual([
- { name: 'Source Plate', value: 'wellPlateId' },
- { name: 'Trash', value: mockTrash },
- ])
+ ).toEqual([{ name: 'Source Plate', value: 'wellPlateId' }])
})
- it('should return labware options with module prefixes when a labware is on module', () => {
+ it('should return labware options with no module prefixes even when a labware is on module', () => {
const labware = {
wellPlateId: {
...otherLabware.wellPlateId,
@@ -206,6 +207,9 @@ describe('labware selectors', () => {
...trash,
...labware,
},
+ additionalEquipmentOnDeck: {
+ trash: { id: 'trash', location: 'cutout12', name: 'trashBin' },
+ },
modules: {
magModuleId: {
id: 'magModuleId',
@@ -223,7 +227,7 @@ describe('labware selectors', () => {
id: 'thermocyclerId',
type: THERMOCYCLER_MODULE_TYPE,
model: THERMOCYCLER_MODULE_V1,
- slot: SPAN7_8_10_11_SLOT,
+ slot: '8',
},
heaterShakerId: {
id: 'heaterShakerId',
@@ -253,11 +257,11 @@ describe('labware selectors', () => {
{}
)
).toEqual([
- { name: 'HS Plate in Heater-Shaker', value: 'hsPlateId' },
- { name: 'TC Plate in Thermocycler', value: 'tcPlateId' },
- { name: 'Temp Plate in Temperature Module', value: 'tempPlateId' },
+ { name: 'HS Plate in 6', value: 'hsPlateId' },
+ { name: 'TC Plate in A1+B1', value: 'tcPlateId' },
+ { name: 'Temp Plate in 3', value: 'tempPlateId' },
{ name: 'Trash', value: mockTrash },
- { name: 'Well Plate in Magnetic Module', value: 'wellPlateId' },
+ { name: 'Well Plate in 1', value: 'wellPlateId' },
])
})
@@ -272,7 +276,6 @@ describe('labware selectors', () => {
const initialDeckSetup = {
pipettes: {},
labware: {
- ...trash,
...labware,
},
modules: {
@@ -283,6 +286,9 @@ describe('labware selectors', () => {
slot: '1',
},
},
+ additionalEquipmentOnDeck: {
+ trash: { id: 'trash', name: 'trashBin', location: 'cutout12' },
+ },
}
const nicknames: Record = {
@@ -312,14 +318,14 @@ describe('labware selectors', () => {
)
).toEqual([
{ name: 'Trash', value: mockTrash },
- { name: 'Well Plate in Magnetic Module', value: 'wellPlateId' },
+ { name: 'Well Plate in 1', value: 'wellPlateId' },
])
})
})
describe('_sortLabwareDropdownOptions', () => {
const trashOption = {
- name: 'Trash Bin',
+ name: 'Trash bin',
value: mockTrash,
}
const zzzPlateOption = { name: 'Zzz Plate', value: 'zzz' }
diff --git a/protocol-designer/src/ui/labware/selectors.ts b/protocol-designer/src/ui/labware/selectors.ts
index 2839d001078..34d5818611f 100644
--- a/protocol-designer/src/ui/labware/selectors.ts
+++ b/protocol-designer/src/ui/labware/selectors.ts
@@ -1,25 +1,32 @@
import { createSelector } from 'reselect'
import mapValues from 'lodash/mapValues'
import reduce from 'lodash/reduce'
-import { getIsTiprack, getLabwareDisplayName } from '@opentrons/shared-data'
+import {
+ TRASH_BIN_DISPLAY_NAME,
+ WASTE_CHUTE_DISPLAY_NAME,
+} from '@opentrons/components'
+import {
+ FLEX_ROBOT_TYPE,
+ OT2_ROBOT_TYPE,
+ getIsTiprack,
+ getLabwareDisplayName,
+} from '@opentrons/shared-data'
import * as stepFormSelectors from '../../step-forms/selectors'
import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors'
-import { getModuleShortNames, getModuleUnderLabware } from '../modules/utils'
-import { getLabwareOffDeck, getLabwareInColumn4 } from './utils'
+import { getLabwareLatestSlot } from './utils'
import type {
LabwareEntity,
AdditionalEquipmentEntity,
} from '@opentrons/step-generation'
import type { DropdownOption } from '@opentrons/components'
+import type { RobotType } from '@opentrons/shared-data'
import type { Selector } from '../../types'
import type {
AllTemporalPropertiesForTimelineFrame,
SavedStepFormState,
} from '../../step-forms'
-const TRASH = 'Trash Bin'
-
export const getLabwareNicknamesById: Selector<
Record
> = createSelector(
@@ -37,8 +44,8 @@ export const _sortLabwareDropdownOptions = (
): DropdownOption[] =>
options.sort((a, b) => {
// special case for trash (always at the bottom of the list)
- if (a.name === TRASH) return 1
- if (b.name === TRASH) return -1
+ if (a.name === TRASH_BIN_DISPLAY_NAME) return 1
+ if (b.name === TRASH_BIN_DISPLAY_NAME) return -1
// sort by name everything else by name
return a.name.localeCompare(b.name)
})
@@ -47,35 +54,21 @@ const getNickname = (
nicknamesById: Record,
initialDeckSetup: AllTemporalPropertiesForTimelineFrame,
labwareId: string,
- savedStepForms: SavedStepFormState
+ savedStepForms: SavedStepFormState,
+ robotType: RobotType
): string => {
- const isOffDeck = getLabwareOffDeck(
+ const latestSlot = getLabwareLatestSlot(
initialDeckSetup,
savedStepForms ?? {},
- labwareId
- )
-
- const moduleOnDeck = getModuleUnderLabware(
- initialDeckSetup,
- savedStepForms ?? {},
- labwareId
- )
- const module =
- moduleOnDeck != null ? getModuleShortNames(moduleOnDeck.type) : null
-
- const isLabwareInColumn4 = getLabwareInColumn4(
- initialDeckSetup,
- savedStepForms ?? {},
- labwareId
+ labwareId,
+ robotType
)
let nickName: string = nicknamesById[labwareId]
- if (module != null) {
- nickName = `${nicknamesById[labwareId]} in ${module}`
- } else if (isOffDeck) {
+ if (latestSlot != null && latestSlot !== 'offDeck') {
+ nickName = `${nicknamesById[labwareId]} in ${latestSlot}`
+ } else if (latestSlot != null && latestSlot === 'offDeck') {
nickName = `${nicknamesById[labwareId]} off-deck`
- } else if (isLabwareInColumn4) {
- nickName = `${nicknamesById[labwareId]} in staging area slot`
}
return nickName
}
@@ -110,6 +103,12 @@ export const getMoveLabwareOptions: Selector = createSelector(
const wasteChuteLocation = Object.values(additionalEquipmentEntities).find(
aE => aE.name === 'wasteChute'
)?.location
+ const trashBinLocation = Object.values(additionalEquipmentEntities).find(
+ aE => aE.name === 'trashBin'
+ )?.location
+ const robotType =
+ trashBinLocation === 'cutout12' ? OT2_ROBOT_TYPE : FLEX_ROBOT_TYPE
+
const moveLabwareOptions = reduce(
labwareEntities,
(
@@ -131,7 +130,8 @@ export const getMoveLabwareOptions: Selector = createSelector(
nicknamesById,
initialDeckSetup,
labwareId,
- savedStepForms
+ savedStepForms,
+ robotType
)
// filter out moving trash, adapters, and labware in
@@ -171,6 +171,12 @@ export const getLabwareOptions: Selector = createSelector(
const wasteChuteLocation = Object.values(additionalEquipmentEntities).find(
aE => aE.name === 'wasteChute'
)?.location
+ const trashBinLocation = Object.values(additionalEquipmentEntities).find(
+ aE => aE.name === 'trashBin'
+ )?.location
+ const robotType =
+ trashBinLocation === 'cutout12' ? OT2_ROBOT_TYPE : FLEX_ROBOT_TYPE
+
const labwareOptions = reduce(
labwareEntities,
(
@@ -191,7 +197,8 @@ export const getLabwareOptions: Selector = createSelector(
nicknamesById,
initialDeckSetup,
labwareId,
- savedStepForms
+ savedStepForms,
+ robotType
)
return getIsTiprack(labwareEntity.def) ||
@@ -222,7 +229,7 @@ export const getWasteChuteOption: Selector = createSelect
const wasteChuteOption: DropdownOption | null =
wasteChuteEntity != null
? {
- name: 'Waste Chute',
+ name: WASTE_CHUTE_DISPLAY_NAME,
value: wasteChuteEntity.id,
}
: null
@@ -246,7 +253,7 @@ export const getDisposalOptions = createSelector(
? [
...acc,
{
- name: TRASH,
+ name: TRASH_BIN_DISPLAY_NAME,
value: additionalEquipment.id ?? '',
},
]
diff --git a/protocol-designer/src/ui/labware/utils.ts b/protocol-designer/src/ui/labware/utils.ts
index 2377f7976db..d55c35b578f 100644
--- a/protocol-designer/src/ui/labware/utils.ts
+++ b/protocol-designer/src/ui/labware/utils.ts
@@ -1,39 +1,54 @@
-import { COLUMN_4_SLOTS } from '@opentrons/step-generation'
+import { getHasWasteChute } from '@opentrons/step-generation'
+import { WASTE_CHUTE_DISPLAY_NAME } from '@opentrons/components'
+import {
+ FLEX_ROBOT_TYPE,
+ TC_MODULE_LOCATION_OT2,
+ TC_MODULE_LOCATION_OT3,
+ THERMOCYCLER_MODULE_TYPE,
+} from '@opentrons/shared-data'
+import type { RobotType } from '@opentrons/shared-data'
import type { InitialDeckSetup, SavedStepFormState } from '../../step-forms'
-export function getLabwareOffDeck(
- initialDeckSetup: InitialDeckSetup,
- savedStepFormState: SavedStepFormState,
- labwareId: string
-): boolean {
- // latest moveLabware step related to labwareId
- const moveLabwareStep = Object.values(savedStepFormState)
- .filter(
- state =>
- state.stepType === 'moveLabware' &&
- labwareId != null &&
- state.labware === labwareId
- )
- .reverse()[0]
-
- if (moveLabwareStep?.newLocation === 'offDeck') {
- return true
- } else if (
- moveLabwareStep == null &&
- initialDeckSetup.labware[labwareId]?.slot === 'offDeck'
- ) {
- return true
- } else return false
+function resolveSlotLocation(
+ modules: InitialDeckSetup['modules'],
+ labware: InitialDeckSetup['labware'],
+ location: string,
+ robotType: RobotType
+): string {
+ const TCSlot =
+ robotType === FLEX_ROBOT_TYPE
+ ? TC_MODULE_LOCATION_OT3
+ : TC_MODULE_LOCATION_OT2
+ if (location === 'offDeck') {
+ return 'offDeck'
+ } else if (modules[location] != null) {
+ return modules[location].type === THERMOCYCLER_MODULE_TYPE
+ ? TCSlot
+ : modules[location].slot
+ } else if (labware[location] != null) {
+ const adapter = labware[location]
+ if (modules[adapter.slot] != null) {
+ return modules[adapter.slot].type === THERMOCYCLER_MODULE_TYPE
+ ? TCSlot
+ : modules[adapter.slot].slot
+ } else {
+ return adapter.slot
+ }
+ } else {
+ return location
+ }
}
-export function getLabwareInColumn4(
+export function getLabwareLatestSlot(
initialDeckSetup: InitialDeckSetup,
savedStepForms: SavedStepFormState,
- labwareId: string
-): boolean {
- const isStartingInColumn4 = COLUMN_4_SLOTS.includes(
- initialDeckSetup.labware[labwareId]?.slot
- )
+ labwareId: string,
+ robotType: RobotType
+): string | null {
+ const { modules, labware, additionalEquipmentOnDeck } = initialDeckSetup
+ const initialSlot = labware[labwareId]?.slot
+ const hasWasteChute = getHasWasteChute(additionalEquipmentOnDeck)
+
// latest moveLabware step related to labwareId
const moveLabwareStep = Object.values(savedStepForms)
.filter(
@@ -45,13 +60,25 @@ export function getLabwareInColumn4(
.reverse()[0]
if (
- moveLabwareStep?.newLocation != null &&
- COLUMN_4_SLOTS.includes(moveLabwareStep.newLocation as string)
+ hasWasteChute &&
+ (initialSlot === 'D3' || moveLabwareStep?.newLocation === 'D3')
) {
- return true
- } else if (moveLabwareStep == null && isStartingInColumn4) {
- return true
+ return WASTE_CHUTE_DISPLAY_NAME
+ }
+
+ if (moveLabwareStep?.newLocation != null) {
+ return resolveSlotLocation(
+ modules,
+ labware,
+ moveLabwareStep.newLocation as string,
+ robotType
+ )
+ } else if (moveLabwareStep == null) {
+ return resolveSlotLocation(modules, labware, initialSlot, robotType)
} else {
- return false
+ console.warn(
+ `Expected to find labware's location but could not with initial slot ${initialSlot}`
+ )
+ return null
}
}
diff --git a/protocol-designer/src/ui/modules/utils.ts b/protocol-designer/src/ui/modules/utils.ts
index e1d26bb840c..d347c1b5388 100644
--- a/protocol-designer/src/ui/modules/utils.ts
+++ b/protocol-designer/src/ui/modules/utils.ts
@@ -1,7 +1,13 @@
import values from 'lodash/values'
import {
- MAGNETIC_MODULE_V1,
+ ABSORBANCE_READER_TYPE,
getLabwareDefaultEngageHeight,
+ HEATERSHAKER_MODULE_TYPE,
+ MAGNETIC_BLOCK_TYPE,
+ MAGNETIC_MODULE_TYPE,
+ MAGNETIC_MODULE_V1,
+ TEMPERATURE_MODULE_TYPE,
+ THERMOCYCLER_MODULE_TYPE,
} from '@opentrons/shared-data'
import type { DropdownOption } from '@opentrons/components'
import type { ModuleType } from '@opentrons/shared-data'
@@ -76,17 +82,17 @@ export function getModuleUnderLabware(
export const getModuleShortNames = (type: ModuleType): string => {
switch (type) {
- case 'heaterShakerModuleType':
- return 'Heater-Shaker'
- case 'magneticBlockType':
+ case HEATERSHAKER_MODULE_TYPE:
+ return 'Heater-Shaker Module'
+ case MAGNETIC_BLOCK_TYPE:
return 'Magnetic Block'
- case 'magneticModuleType':
+ case MAGNETIC_MODULE_TYPE:
return 'Magnetic Module'
- case 'temperatureModuleType':
+ case TEMPERATURE_MODULE_TYPE:
return 'Temperature Module'
- case 'thermocyclerModuleType':
+ case THERMOCYCLER_MODULE_TYPE:
return 'Thermocycler'
- case 'absorbanceReaderType':
+ case ABSORBANCE_READER_TYPE:
return 'Absorbance Reader'
}
}
@@ -110,22 +116,25 @@ export function getModuleLabwareOptions(
)?.id
if (labwareOnAdapterId != null) {
return {
- name: `${nicknamesById[labwareOnAdapterId]} in ${
- nicknamesById[labware.id]
- } in ${module} in slot ${moduleOnDeck.slot}`,
+ name: `${nicknamesById[labware.id]} with ${
+ nicknamesById[labwareOnAdapterId]
+ }`,
+ deckLabel: moduleOnDeck.slot,
+ subtext: module,
value: moduleOnDeck.id,
}
} else {
return {
- name: `${nicknamesById[labware.id]} in ${module} in slot ${
- moduleOnDeck.slot
- }`,
+ name: nicknamesById[labware.id],
+ deckLabel: moduleOnDeck.slot,
+ subtext: module,
value: moduleOnDeck.id,
}
}
} else {
return {
- name: `No labware in ${module} in slot ${moduleOnDeck.slot}`,
+ name: module,
+ deckLabel: moduleOnDeck.slot,
value: moduleOnDeck.id,
}
}