Skip to content

Commit 396b013

Browse files
committed
Merge back 'chore_release-pd-8.5.0' into 'edge' (#18832)
2 parents 541de46 + cb4f90a commit 396b013

File tree

13 files changed

+514
-412
lines changed

13 files changed

+514
-412
lines changed

protocol-designer/src/assets/localization/en/modal.json

Lines changed: 262 additions & 257 deletions
Large diffs are not rendered by default.

protocol-designer/src/components/organisms/TipPositionModal/__tests__/TipPositionModal.test.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { TipPositionSideView } from '../TipPositionSideView'
1616

1717
import type { ComponentProps } from 'react'
18+
import type { MoveLiquidPrefixType } from '../../../../resources/types'
1819

1920
vi.mock('../TipPositionSideView')
2021
vi.mock('../../../../file-data/selectors')
@@ -51,7 +52,7 @@ describe('TipPositionModal', () => {
5152
specs: {
5253
z: {
5354
name: 'aspirate_mmFromBottom',
54-
value: null,
55+
value: 0,
5556
updateValue: mockUpdateZSpec,
5657
},
5758
y: {
@@ -95,6 +96,27 @@ describe('TipPositionModal', () => {
9596
'Tip position is close to the edge of the well and may cause collisions.'
9697
)
9798
})
99+
describe('submerge/retract in well warning', () => {
100+
const prefixes = [
101+
'aspirate_submerge',
102+
'dispense_submerge',
103+
'aspirate_retract',
104+
'dispense_retract',
105+
] as MoveLiquidPrefixType[]
106+
prefixes.forEach(prefix => {
107+
it(`renders the banner if the prefix is ${prefix} and the z value is inside the well`, () => {
108+
props.prefix = prefix
109+
props.specs.z.value = -1
110+
props.specs.y.value = 0
111+
props.specs.x.value = 0
112+
render(props)
113+
screen.getByText('The tip position may be inside the liquid')
114+
screen.getByText(
115+
'The tip must be above the ending height of the liquid for a valid transfer command'
116+
)
117+
})
118+
})
119+
})
98120
it('renders the captions, and visual', () => {
99121
render(props)
100122
screen.getByText('X position')
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
import type { MoveLiquidPrefixType } from '../../../resources/types'
2+
13
export const DECIMALS_ALLOWED = 1
24
export const TOO_MANY_DECIMALS: 'TOO_MANY_DECIMALS' = 'TOO_MANY_DECIMALS'
35
export const PERCENT_RANGE_TO_SHOW_WARNING = 0.9
6+
7+
export const MoveLiquidPrefixToAction: Record<MoveLiquidPrefixType, string> = {
8+
aspirate: 'aspirate',
9+
dispense: 'dispense',
10+
mix: 'mix',
11+
aspirate_retract: 'retract',
12+
dispense_retract: 'retract',
13+
aspirate_submerge: 'submerge',
14+
dispense_submerge: 'submerge',
15+
}

protocol-designer/src/components/organisms/TipPositionModal/hooks/usePositionReference.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface UsePositionReferenceResult {
2121

2222
export function usePositionReference(args: {
2323
zValue: number
24-
updateZValue: Dispatch<SetStateAction<string | null>>
24+
updateZValue: Dispatch<SetStateAction<string>>
2525
wellDepth: number
2626
initialReference?: unknown
2727
}): UsePositionReferenceResult {

protocol-designer/src/components/organisms/TipPositionModal/index.tsx

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ import {
2727
} from '@opentrons/shared-data'
2828

2929
import { getIsTouchTipField } from '../../../form-types'
30-
import { prefixMap } from '../../../resources/utils'
3130
import { LINK_BUTTON_STYLE } from '../../atoms'
3231
import { getMainPagePortalEl } from '../Portal'
33-
import { PERCENT_RANGE_TO_SHOW_WARNING, TOO_MANY_DECIMALS } from './constants'
32+
import {
33+
MoveLiquidPrefixToAction,
34+
PERCENT_RANGE_TO_SHOW_WARNING,
35+
TOO_MANY_DECIMALS,
36+
} from './constants'
3437
import { useDefaultPosition, usePositionReference } from './hooks'
3538
import { TipPositionSideView } from './TipPositionSideView'
3639
import { TipPositionTopView } from './TipPositionTopView'
40+
import { getIsTipInWell } from './utils'
3741
import * as utils from './utils'
3842

3943
import type { ChangeEvent, Dispatch, SetStateAction } from 'react'
@@ -45,8 +49,8 @@ import type { MoveLiquidPrefixType } from '../../../resources/types'
4549
type Offset = 'x' | 'y' | 'z'
4650
interface PositionSpec {
4751
name: StepFieldName
48-
value: number | null
49-
updateValue: (val?: number | null) => void
52+
value: number
53+
updateValue: (val: number) => void
5054
}
5155
export type PositionSpecs = Record<Offset, PositionSpec>
5256

@@ -99,7 +103,7 @@ export function TipPositionModal(
99103
})
100104
const defaultPosition = useDefaultPosition(formData, prefix)
101105

102-
const [zValue, setZValue] = useState<string | null>(
106+
const [zValue, setZValue] = useState<string>(
103107
zSpec?.value == null
104108
? String(
105109
referenceSpec?.value === POSITION_REFERENCE_BOTTOM
@@ -108,11 +112,11 @@ export function TipPositionModal(
108112
)
109113
: String(zSpec?.value)
110114
)
111-
const [yValue, setYValue] = useState<string | null>(
112-
ySpec?.value == null ? null : String(ySpec?.value)
115+
const [yValue, setYValue] = useState<string>(
116+
ySpec?.value == null ? '0' : String(ySpec?.value)
113117
)
114-
const [xValue, setXValue] = useState<string | null>(
115-
xSpec?.value == null ? null : String(xSpec?.value)
118+
const [xValue, setXValue] = useState<string>(
119+
xSpec?.value == null ? '0' : String(xSpec?.value)
116120
)
117121
const {
118122
positionReferenceDropdown,
@@ -125,6 +129,15 @@ export function TipPositionModal(
125129
wellDepth: wellDepthMm,
126130
})
127131

132+
// submerge/retract in well warning
133+
const isInWell =
134+
zValue != null && zValue !== ''
135+
? getIsTipInWell(Number(zValue), reference, wellDepthMm)
136+
: false
137+
const isSubmergeOrRetract =
138+
MoveLiquidPrefixToAction[prefix] === 'submerge' ||
139+
MoveLiquidPrefixToAction[prefix] === 'retract'
140+
128141
// in this modal, pristinity hides the OUT_OF_BOUNDS error only.
129142
const [isPristine, setPristine] = useState<boolean>(true)
130143
const getMinMaxMmFromBottom = (
@@ -212,9 +225,9 @@ export function TipPositionModal(
212225

213226
const handleDone = (): void => {
214227
if (!hasErrors) {
215-
zSpec?.updateValue(zValue === null ? null : Number(zValue))
216-
xSpec?.updateValue(xValue === null ? null : Number(xValue))
217-
ySpec?.updateValue(yValue === null ? null : Number(yValue))
228+
zSpec?.updateValue(Number(zValue))
229+
xSpec?.updateValue(Number(xValue))
230+
ySpec?.updateValue(Number(yValue))
218231
referenceSpec?.updateValue(reference)
219232
closeModal()
220233
}
@@ -226,7 +239,7 @@ export function TipPositionModal(
226239

227240
const handleChange = (
228241
newValueRaw: string,
229-
setValue: Dispatch<SetStateAction<string | null>>
242+
setValue: Dispatch<SetStateAction<string>>
230243
): void => {
231244
// if string, strip non-number characters from string and cast to number
232245
const newValue =
@@ -271,7 +284,9 @@ export function TipPositionModal(
271284
type="info"
272285
width="47rem"
273286
closeOnOutsideClick
274-
title={t('shared:tip_position', { prefix: prefixMap[prefix] })}
287+
title={t('shared:tip_position', {
288+
prefix: MoveLiquidPrefixToAction[prefix],
289+
})}
275290
onClose={handleCancel}
276291
footer={
277292
<Flex
@@ -297,10 +312,22 @@ export function TipPositionModal(
297312
{isXValueNearEdge || isYValueNearEdge || isZValueAtBottom ? (
298313
<Banner type="warning">
299314
<StyledText desktopStyle="bodyDefaultRegular">
300-
{t('tip_position.warning')}
315+
{t('tip_position.warning.close_to_edge')}
301316
</StyledText>
302317
</Banner>
303318
) : null}
319+
{isInWell && isSubmergeOrRetract ? (
320+
<Banner type="warning">
321+
<Flex flexDirection={DIRECTION_COLUMN}>
322+
<StyledText desktopStyle="bodyDefaultSemiBold">
323+
{t('tip_position.warning.submerge_retract_in_well.header')}
324+
</StyledText>
325+
<StyledText desktopStyle="bodyDefaultRegular">
326+
{t('tip_position.warning.submerge_retract_in_well.subtext')}
327+
</StyledText>
328+
</Flex>
329+
</Banner>
330+
) : null}
304331
<Flex gridGap={SPACING.spacing40}>
305332
<Flex
306333
flexDirection={DIRECTION_COLUMN}

protocol-designer/src/components/organisms/TipPositionModal/utils.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import floor from 'lodash/floor'
22
import round from 'lodash/round'
33

44
import {
5+
getMmFromBottom,
56
POSITION_REFERENCE_CENTER,
67
POSITION_REFERENCE_TOP,
78
} from '@opentrons/shared-data'
@@ -156,3 +157,15 @@ export const getIsZValueAtBottom = (
156157
}
157158
return round(parseFloat(zValue), 1) === round(minZValue, 1)
158159
}
160+
161+
export const getIsTipInWell = (
162+
z: number,
163+
positionReference: PositionReference,
164+
wellDepth: number | null
165+
): boolean => {
166+
if (wellDepth == null) {
167+
return false
168+
}
169+
const mmFromBottom = getMmFromBottom(z, positionReference, wellDepth)
170+
return mmFromBottom != null && mmFromBottom <= wellDepth
171+
}

protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PositionField.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import {
2121
TipPositionModal,
2222
ZTipPositionModal,
2323
} from '../../../../../components/organisms'
24+
import { MoveLiquidPrefixToAction } from '../../../../../components/organisms/TipPositionModal/constants'
2425
import { getDefaultMmFromEdge } from '../../../../../components/organisms/TipPositionModal/utils'
2526
import { getIsDelayPositionField } from '../../../../../form-types'
26-
import { prefixMap } from '../../../../../resources/utils'
2727
import { selectors as stepFormSelectors } from '../../../../../step-forms'
2828

2929
import type { PositionSpecs } from '../../../../../components/organisms'
@@ -159,12 +159,12 @@ export function PositionField(props: PositionFieldProps): JSX.Element {
159159
},
160160
x: {
161161
name: xName,
162-
value: rawXValue != null ? Number(rawXValue) : null,
162+
value: rawXValue != null ? Number(rawXValue) : 0,
163163
updateValue: xUpdateValue,
164164
},
165165
y: {
166166
name: yName,
167-
value: rawYValue != null ? Number(rawYValue) : null,
167+
value: rawYValue != null ? Number(rawYValue) : 0,
168168
updateValue: yUpdateValue,
169169
},
170170
}
@@ -206,7 +206,7 @@ export function PositionField(props: PositionFieldProps): JSX.Element {
206206
<StyledText desktopStyle="bodyDefaultRegular" color={COLORS.grey60}>
207207
{i18n.format(
208208
t('protocol_steps:tip_position', {
209-
prefix: prefixMap[prefix],
209+
prefix: MoveLiquidPrefixToAction[prefix],
210210
}),
211211
'capitalize'
212212
)}

protocol-designer/src/resources/utils/__tests__/index.test.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

protocol-designer/src/resources/utils/index.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)