Skip to content

Commit d403380

Browse files
authored
fix(protocol-designer): update labware entities when modifying stacker fill step (#20519)
Handles labware deletion and creation when editing an already-saved stacker fill step. If the new quantity exceeds the previously saved quantity, we preserve the old labware IDs and create as many new labwares as the difference of (newQuantity - oldQuantity). If the new quantity is less than the previously saved quantity, we delete the labwares that are no longer called in the fill.
1 parent 3f2888e commit d403380

File tree

4 files changed

+66
-15
lines changed

4 files changed

+66
-15
lines changed

protocol-designer/src/components/organisms/StepSummary/FlexStackerSummary.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export function FlexStackerSummary(
3030
const { fillLabwareIds, flexStackerFormType, moduleId } = currentStep
3131
const { moduleState: stackerModuleState } = moduleRobotState[moduleId] ?? {}
3232
const nicknamesById = useSelector(getLabwareNicknamesById)
33-
console.log(nicknamesById)
3433
if (
3534
stackerModuleState == null ||
3635
stackerModuleState.type !== FLEX_STACKER_MODULE_TYPE

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/FlexStackerTools/RefillSettings.tsx

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,70 @@ import {
1111
import { getIsTiprack } from '@opentrons/shared-data'
1212

1313
import { InputStepFormField } from '/protocol-designer/components/molecules'
14-
import { getLabwareEntities } from '/protocol-designer/step-forms/selectors'
14+
import {
15+
getLabwareEntities,
16+
getSavedStepForms,
17+
} from '/protocol-designer/step-forms/selectors'
1518
import { uuid } from '/protocol-designer/utils'
1619

1720
import styles from './flexstackertools.module.css'
1821
import { MessageField } from './MessageField'
1922
import { StackerContentItem } from './StackerContentItem'
2023

2124
import type { FlexStackerModuleState } from '@opentrons/step-generation'
25+
import type { FormData } from '/protocol-designer/form-types'
2226
import type { FieldPropsByName } from '../../types'
2327

2428
interface RefillSettingsProps {
29+
formData: FormData
2530
propsForFields: FieldPropsByName
2631
moduleState: FlexStackerModuleState | null
2732
maxPoolCount: number
2833
}
2934

3035
export function RefillSettings(props: RefillSettingsProps): JSX.Element {
31-
const { propsForFields, moduleState, maxPoolCount } = props
36+
const { formData, propsForFields, moduleState, maxPoolCount } = props
3237
const { t } = useTranslation('form')
3338
const { storedLabwareDetails } = moduleState ?? {}
3439
const labwareEntities = useSelector(getLabwareEntities)
40+
const savedStepForms = useSelector(getSavedStepForms)
41+
const initialLabwareIds =
42+
(savedStepForms[formData.id]?.fillLabwareIds as string[]) ?? []
43+
const oldFillQuantity = initialLabwareIds.length
3544
const [fillQuantityLocalState, setFillQuantityState] = useState<
3645
string | null
37-
>(null)
46+
// initialize if saved step form exists
47+
>(initialLabwareIds.length > 0 ? String(initialLabwareIds.length) : null)
3848
const storedEntity = Object.values(labwareEntities).find(
3949
({ labwareDefURI }) => {
4050
return labwareDefURI === storedLabwareDetails?.primaryLabwareURI
4151
}
4252
)
53+
4354
const storedEntityName = storedEntity?.def.metadata.displayName
4455
const isTiprack = storedEntity != null && getIsTiprack(storedEntity.def)
4556
// TODO: figure out a way to not need this use Effect. its hard because
4657
// you can't rely on generating the uuid in the hydrated form
4758
useEffect(() => {
4859
const quantity = Number(fillQuantityLocalState) ?? 1
49-
const newFill = Array.from(
50-
{ length: quantity },
51-
() => `${uuid()}:${storedEntity?.labwareDefURI}`
52-
)
53-
propsForFields.fillLabwareIds.updateValue(newFill)
60+
const difference = quantity - oldFillQuantity
61+
if (difference > 0) {
62+
const additionalIds = Array.from(
63+
{ length: difference },
64+
() => `${uuid()}:${storedEntity?.labwareDefURI}`
65+
)
66+
// ensure we preserve the existing labware IDs, even if a user extensively modifies the quantity up/down
67+
propsForFields.fillLabwareIds.updateValue([
68+
...initialLabwareIds,
69+
...additionalIds,
70+
])
71+
} else if (difference < 0) {
72+
propsForFields.fillLabwareIds.updateValue(
73+
initialLabwareIds.slice(0, quantity)
74+
)
75+
}
5476
}, [fillQuantityLocalState, storedEntity?.labwareDefURI])
77+
5578
return (
5679
<div className={styles.refill_settings_container}>
5780
<StyledText desktopStyle="bodyDefaultRegular" color={COLORS.grey60}>

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/FlexStackerTools/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export function FlexStackerTools(props: StepFormProps): JSX.Element {
200200
) : null}
201201
{formData.flexStackerFormType === FLEX_STACKER_FILL ? (
202202
<RefillSettings
203+
formData={formData}
203204
propsForFields={propsForFields}
204205
moduleState={moduleState}
205206
maxPoolCount={maxPoolCount}

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/index.tsx

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from 'react'
2-
import { connect } from 'react-redux'
2+
import { connect, useSelector } from 'react-redux'
33

44
import { useConditionalConfirm } from '@opentrons/components'
55
import { getModuleDisplayName } from '@opentrons/shared-data'
@@ -12,12 +12,16 @@ import {
1212
} from '/protocol-designer/components/organisms'
1313
import { getEnableConcurrentModuleActions } from '/protocol-designer/feature-flags/selectors'
1414
import { selectors as labwareDefSelectors } from '/protocol-designer/labware-defs'
15+
import { deleteContainer } from '/protocol-designer/labware-ingred/actions'
1516
import {
1617
getHydratedForm,
1718
selectors as stepFormSelectors,
1819
} from '/protocol-designer/step-forms'
1920
import { createLabwareAndQueueForHopper } from '/protocol-designer/step-forms/actions/thunks'
20-
import { getInvariantContext } from '/protocol-designer/step-forms/selectors'
21+
import {
22+
getInvariantContext,
23+
getSavedStepForms,
24+
} from '/protocol-designer/step-forms/selectors'
2125
import { actions } from '/protocol-designer/steplist'
2226
import { actions as stepsActions } from '/protocol-designer/ui/steps'
2327

@@ -45,6 +49,7 @@ interface DispatchProps {
4549
cancelStepForm: () => void
4650
saveStepForm: (options?: { userWantsBonusStep?: boolean }) => void
4751
createdLabwareForQueue: (moduleId: string, fillLabwareIds: string[]) => void
52+
deleteLabwares: (labwareIds: string[]) => void
4853
}
4954
type StepFormManagerProps = StateProps & DispatchProps
5055

@@ -60,11 +65,14 @@ function StepFormManager(props: StepFormManagerProps): JSX.Element | null {
6065
invariantContext,
6166
allLabwareDefs,
6267
createdLabwareForQueue,
68+
deleteLabwares,
6369
} = props
6470
const [focusedField, setFocusedField] = useState<string | null>(null)
6571
const [dirtyFields, setDirtyFields] = useState<StepFieldName[]>(
6672
getDirtyFields(isNewStep, formData)
6773
)
74+
const savedStepForms = useSelector(getSavedStepForms)
75+
const savedStepForm = formData != null ? savedStepForms[formData.id] : null
6876

6977
const handleBlur = (fieldName: StepFieldName): void => {
7078
if (fieldName === focusedField) {
@@ -111,10 +119,23 @@ function StepFormManager(props: StepFormManagerProps): JSX.Element | null {
111119
hydratedForm.stepType === 'flexStacker' &&
112120
hydratedForm.flexStackerFormType === 'fill'
113121
) {
114-
createdLabwareForQueue(
115-
hydratedForm.moduleId,
116-
hydratedForm.fillLabwareIds
117-
)
122+
const initialLabwareIds =
123+
(savedStepForm?.fillLabwareIds as string[]) ?? null
124+
const oldFillQuantity = initialLabwareIds.length
125+
126+
// delete extraneous labware if fill quantity is decreased
127+
if (oldFillQuantity > hydratedForm.fillLabwareIds.length) {
128+
const extraneousLabwareIds = initialLabwareIds.slice(
129+
hydratedForm.fillLabwareIds.length,
130+
oldFillQuantity
131+
)
132+
deleteLabwares(extraneousLabwareIds)
133+
} else {
134+
createdLabwareForQueue(
135+
hydratedForm.moduleId,
136+
hydratedForm.fillLabwareIds
137+
)
138+
}
118139
}
119140
} else {
120141
// There's a dialog we have to show before saving the step.
@@ -233,10 +254,17 @@ const mapDispatchToProps = (dispatch: ThunkDispatch<any>): DispatchProps => {
233254
)
234255
}
235256

257+
const deleteLabwares = (labwareIds: string[]): void => {
258+
for (const labwareId of labwareIds) {
259+
dispatch(deleteContainer({ labwareId }))
260+
}
261+
}
262+
236263
return {
237264
cancelStepForm,
238265
saveStepForm,
239266
createdLabwareForQueue,
267+
deleteLabwares,
240268
}
241269
}
242270

0 commit comments

Comments
 (0)