diff --git a/protocol-designer/src/components/StepEditForm/fields/MoveLabwareField.tsx b/protocol-designer/src/components/StepEditForm/fields/MoveLabwareField.tsx new file mode 100644 index 00000000000..b0a6d51b463 --- /dev/null +++ b/protocol-designer/src/components/StepEditForm/fields/MoveLabwareField.tsx @@ -0,0 +1,10 @@ +import * as React from 'react' +import { useSelector } from 'react-redux' +import { getMoveLabwareOptions } from '../../../ui/labware/selectors' +import { StepFormDropdown } from './StepFormDropdownField' +import type { FieldProps } from '../types' + +export function MoveLabwareField(props: FieldProps): JSX.Element { + const options = useSelector(getMoveLabwareOptions) + return +} diff --git a/protocol-designer/src/components/StepEditForm/fields/index.ts b/protocol-designer/src/components/StepEditForm/fields/index.ts index b59231db01a..15d7f4bb21f 100644 --- a/protocol-designer/src/components/StepEditForm/fields/index.ts +++ b/protocol-designer/src/components/StepEditForm/fields/index.ts @@ -13,6 +13,7 @@ export { DisposalVolumeField } from './DisposalVolumeField' export { FlowRateField } from './FlowRateField' export { LabwareField } from './LabwareField' export { LabwareLocationField } from './LabwareLocationField' +export { MoveLabwareField } from './MoveLabwareField' export { PathField } from './PathField/PathField' export { PipetteField } from './PipetteField' export { ProfileItemRows } from './ProfileItemRows' diff --git a/protocol-designer/src/components/StepEditForm/forms/MoveLabwareForm/index.tsx b/protocol-designer/src/components/StepEditForm/forms/MoveLabwareForm/index.tsx index e7dde2d26fb..9de9709cbc0 100644 --- a/protocol-designer/src/components/StepEditForm/forms/MoveLabwareForm/index.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/MoveLabwareForm/index.tsx @@ -12,9 +12,9 @@ import { useHoverTooltip, } from '@opentrons/components' import { - LabwareField, LabwareLocationField, CheckboxRowField, + MoveLabwareField, } from '../../fields' import styles from '../../StepEditForm.module.css' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' @@ -50,7 +50,7 @@ export const MoveLabwareForm = (props: StepFormProps): JSX.Element => { label={t('form:step_edit_form.labwareLabel.movedLabware')} className={styles.large_field} > - + {robotType === FLEX_ROBOT_TYPE ? ( { ]) }) - it('should return labware options for move labware with tips and trash', () => { - const labwareEntities = { - ...tipracks, - ...trash, - ...otherLabware, - } - const initialDeckSetup = { - labware: labwareEntities, - modules: {}, - pipettes: {}, - } - - const presavedStepForm = { - stepType: 'moveLabware', - } - expect( - // @ts-expect-error(jr, 7/17/23): resultFunc doesn't exist on type Selector - getLabwareOptions.resultFunc( - labwareEntities, - names, - initialDeckSetup, - presavedStepForm, - {}, - {} - ) - ).toEqual([ - { name: 'Opentrons Tip Rack 10 µL', value: 'tiprack10Id' }, - { name: 'Opentrons Tip Rack 1000 µL', value: 'tiprack100Id' }, - { name: 'Source Plate', value: 'wellPlateId' }, - { name: 'Trash', value: mockTrash }, - ]) - }) - it('should return labware options with module prefixes when a labware is on module', () => { const labware = { wellPlateId: { @@ -345,7 +312,7 @@ describe('labware selectors', () => { ) ).toEqual([ { name: 'Trash', value: mockTrash }, - { name: 'Well Plate', value: 'wellPlateId' }, + { name: 'Well Plate in Magnetic Module', value: 'wellPlateId' }, ]) }) }) diff --git a/protocol-designer/src/ui/labware/selectors.ts b/protocol-designer/src/ui/labware/selectors.ts index 24790e7174f..61d5f5dab7a 100644 --- a/protocol-designer/src/ui/labware/selectors.ts +++ b/protocol-designer/src/ui/labware/selectors.ts @@ -11,6 +11,10 @@ import { getLabwareOffDeck, getLabwareInColumn4 } from './utils' import type { LabwareEntity } from '@opentrons/step-generation' import type { DropdownOption, Options } from '@opentrons/components' import type { Selector } from '../../types' +import type { + AllTemporalPropertiesForTimelineFrame, + SavedStepFormState, +} from '../../step-forms' const TRASH = 'Trash Bin' @@ -35,30 +39,63 @@ export const _sortLabwareDropdownOptions = (options: Options): Options => return a.name.localeCompare(b.name) }) -/** Returns options for labware dropdowns. +const getNickname = ( + nicknamesById: Record, + initialDeckSetup: AllTemporalPropertiesForTimelineFrame, + labwareId: string, + savedStepForms: SavedStepFormState +): string => { + const isOffDeck = getLabwareOffDeck( + initialDeckSetup, + savedStepForms ?? {}, + labwareId + ) + + const moduleOnDeck = getModuleUnderLabware( + initialDeckSetup, + savedStepForms ?? {}, + labwareId + ) + const module = + moduleOnDeck != null ? getModuleShortNames(moduleOnDeck.type) : null + + const isLabwareInColumn4 = getLabwareInColumn4( + initialDeckSetup, + savedStepForms ?? {}, + labwareId + ) + + let nickName: string = nicknamesById[labwareId] + if (module != null) { + nickName = `${nicknamesById[labwareId]} in ${module}` + } else if (isOffDeck) { + nickName = `${nicknamesById[labwareId]} off-deck` + } else if (isLabwareInColumn4) { + nickName = `${nicknamesById[labwareId]} in staging area slot` + } + return nickName +} + +/** Returns options for labware dropdowns for moveLabware. * Ordered by display name / nickname, but with trash at the bottom. */ -export const getLabwareOptions: Selector = createSelector( +export const getMoveLabwareOptions: Selector = createSelector( stepFormSelectors.getLabwareEntities, getLabwareNicknamesById, stepFormSelectors.getInitialDeckSetup, - stepFormSelectors.getPresavedStepForm, stepFormSelectors.getSavedStepForms, stepFormSelectors.getAdditionalEquipmentEntities, ( labwareEntities, nicknamesById, initialDeckSetup, - presavedStepForm, savedStepForms, additionalEquipmentEntities ) => { - const moveLabwarePresavedStep = presavedStepForm?.stepType === 'moveLabware' const wasteChuteLocation = Object.values(additionalEquipmentEntities).find( aE => aE.name === 'wasteChute' )?.location - - const labwareOptions = reduce( + const moveLabwareOptions = reduce( labwareEntities, ( acc: Options, @@ -72,67 +109,89 @@ export const getLabwareOptions: Selector = createSelector( form.newLocation === wasteChuteLocation ) - const isAdapter = labwareEntity.def.allowedRoles?.includes('adapter') - const isOffDeck = getLabwareOffDeck( + const isAdapter = + labwareEntity.def.allowedRoles?.includes('adapter') ?? false + const nickName = getNickname( + nicknamesById, initialDeckSetup, - savedStepForms ?? {}, - labwareId + labwareId, + savedStepForms ) - const moduleOnDeck = getModuleUnderLabware( - initialDeckSetup, - savedStepForms ?? {}, - labwareId + // filter out moving trash, adapters, and labware in + // waste chute for moveLabware + return isAdapter || isLabwareInWasteChute + ? acc + : [ + ...acc, + { + name: nickName, + value: labwareId, + }, + ] + }, + [] + ) + return _sortLabwareDropdownOptions(moveLabwareOptions) + } +) + +/** Returns options for labware dropdowns for moveLiquids. + * Ordered by display name / nickname, but with trash at the bottom. + */ +export const getLabwareOptions: Selector = createSelector( + stepFormSelectors.getLabwareEntities, + getLabwareNicknamesById, + stepFormSelectors.getInitialDeckSetup, + stepFormSelectors.getSavedStepForms, + stepFormSelectors.getAdditionalEquipmentEntities, + ( + labwareEntities, + nicknamesById, + initialDeckSetup, + savedStepForms, + additionalEquipmentEntities + ) => { + const wasteChuteLocation = Object.values(additionalEquipmentEntities).find( + aE => aE.name === 'wasteChute' + )?.location + const labwareOptions = reduce( + labwareEntities, + ( + acc: Options, + labwareEntity: LabwareEntity, + labwareId: string + ): Options => { + const isLabwareInWasteChute = Object.values(savedStepForms).find( + form => + form.stepType === 'moveLabware' && + form.labware === labwareId && + form.newLocation === wasteChuteLocation ) - const module = - moduleOnDeck != null ? getModuleShortNames(moduleOnDeck.type) : null - const isLabwareInColumn4 = getLabwareInColumn4( + const isAdapter = + labwareEntity.def.allowedRoles?.includes('adapter') ?? false + const nickName = getNickname( + nicknamesById, initialDeckSetup, - savedStepForms ?? {}, - labwareId + labwareId, + savedStepForms ) - let nickName = nicknamesById[labwareId] - if (module != null) { - nickName = `${nicknamesById[labwareId]} in ${module}` - } else if (isOffDeck) { - nickName = `${nicknamesById[labwareId]} off-deck` - } else if (isLabwareInColumn4) { - nickName = `${nicknamesById[labwareId]} in staging area slot` - } - - if (!moveLabwarePresavedStep) { - // filter out tip racks, adapters, and labware in waste chute - // for aspirating/dispensing/mixing into - return getIsTiprack(labwareEntity.def) || - isAdapter || - isLabwareInWasteChute - ? acc - : [ - ...acc, - { - name: nickName, - value: labwareId, - }, - ] - } else { - // filter out moving trash, adapters, and labware in - // waste chute for moveLabware - return isAdapter || isLabwareInWasteChute - ? acc - : [ - ...acc, - { - name: nickName, - value: labwareId, - }, - ] - } + return getIsTiprack(labwareEntity.def) || + isAdapter || + isLabwareInWasteChute + ? acc + : [ + ...acc, + { + name: nickName, + value: labwareId, + }, + ] }, [] ) - return _sortLabwareDropdownOptions(labwareOptions) } )