Skip to content

Commit ab89bce

Browse files
committed
refactor(protocol-designer): allow for multiple lid selection
partially addresses AUTH-1945
1 parent f043cad commit ab89bce

File tree

13 files changed

+733
-409
lines changed

13 files changed

+733
-409
lines changed

components/src/molecules/CustomizeExpandButton/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memo } from 'react'
12
import styled, { css } from 'styled-components'
23

34
import { CheckboxField } from '../../atoms/CheckboxField'
@@ -8,7 +9,12 @@ import { Flex } from '../../primitives'
89
import { CURSOR_POINTER, DIRECTION_COLUMN } from '../../styles'
910
import { SPACING } from '../../ui-style-constants'
1011

11-
import type { ChangeEvent, ChangeEventHandler, MouseEvent } from 'react'
12+
import type {
13+
ChangeEvent,
14+
ChangeEventHandler,
15+
MemoExoticComponent,
16+
MouseEvent,
17+
} from 'react'
1218
import type { LabwareDefinition2 } from '@opentrons/shared-data'
1319
import type { StyleProps } from '../../primitives'
1420

@@ -38,7 +44,7 @@ interface CustomizeExpandButtonProps extends StyleProps {
3844
}
3945

4046
// used for helix and as a child button to ListButtonAccordion
41-
export function CustomizeExpandButton(
47+
export function CustomizeExpandButtonComponent(
4248
props: CustomizeExpandButtonProps
4349
): JSX.Element {
4450
const {
@@ -135,6 +141,10 @@ export function CustomizeExpandButton(
135141
)
136142
}
137143

144+
export const CustomizeExpandButton: MemoExoticComponent<
145+
typeof CustomizeExpandButtonComponent
146+
> = memo(CustomizeExpandButtonComponent)
147+
138148
const SettingButton = styled.input`
139149
display: none;
140150
`

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"labware_already_in_slot": "Labware already in this slot cannot be combined with other labware.",
5050
"labware_quantity": "Labware quantity",
5151
"labware": "Labware",
52+
"lid_compatible_labware": "Lid compatible labware",
5253
"lid": "Lids",
5354
"liquid": "Liquid",
5455
"liquids": "Liquids",

protocol-designer/src/components/organisms/LabwareCard/LabwareCard.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,5 @@ LabwareCardField.args = {
5050
pythonName: 'mockPythonName',
5151
stack: ['mockId', 'A1'],
5252
},
53-
lidDisplayName: 'Opentrons Flex Tip Rack Lid',
53+
lidId: 'mockLidId',
5454
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('LabwareCard', () => {
5555
labwareDefURI: 'mockuri',
5656
def: fixture96Plate as LabwareDefinition2,
5757
},
58-
lidDisplayName: 'mock lid',
58+
lidId: 'lidId',
5959
quantity: 1,
6060
}
6161
vi.mocked(EditLabwareQuantityModal).mockReturnValue(
@@ -84,6 +84,16 @@ describe('LabwareCard', () => {
8484
pythonName: 'mockPythonName',
8585
stack: ['labwareId', 'A1'],
8686
},
87+
lidId: {
88+
id: 'lidId',
89+
labwareDefURI: 'mockuri',
90+
def: ({
91+
...fixture96Plate,
92+
metadata: { displayName: 'mock lid' },
93+
} as any) as LabwareDefinition2,
94+
pythonName: 'mockPythonName',
95+
stack: ['labwareId', 'A1'],
96+
},
8797
},
8898
})
8999
})

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ import type { ThunkDispatch } from '../../../types'
3838
interface LabwareCardProps {
3939
labware: LabwareOnDeck
4040
quantity: number
41-
lidDisplayName?: string
41+
lidId?: string
4242
}
4343

4444
export function LabwareCard(props: LabwareCardProps): JSX.Element {
45-
const { labware, lidDisplayName, quantity } = props
45+
const { labware, lidId, quantity } = props
4646
const navigate = useNavigate()
4747
const dispatch = useDispatch<ThunkDispatch<any>>()
4848
const { t } = useTranslation('starting_deck_state')
@@ -106,6 +106,7 @@ export function LabwareCard(props: LabwareCardProps): JSX.Element {
106106
<LabwareCardOverflowMenu
107107
setShowOverflowMenu={setShowOverflowMenu}
108108
labwareIds={allLabwareIdsOnStack}
109+
lidId={lidId}
109110
/>
110111
) : null}
111112
<ListItem type="default" backgroundColor={COLORS.grey30}>
@@ -133,12 +134,14 @@ export function LabwareCard(props: LabwareCardProps): JSX.Element {
133134
{displayName}
134135
</StyledText>
135136
) : null}
136-
{lidDisplayName != null ? (
137+
{lidId != null && deckSetupLabware[lidId] != null ? (
137138
<StyledText
138139
desktopStyle="captionRegular"
139140
color={COLORS.grey60}
140141
>
141-
{t('with_lid', { name: lidDisplayName })}
142+
{t('with_lid', {
143+
name: deckSetupLabware[lidId].def.metadata.displayName,
144+
})}
142145
</StyledText>
143146
) : null}
144147

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ import type { ThunkDispatch } from '../../../types'
3838
interface LabwareCardOverflowMenuProps {
3939
labwareIds: string[]
4040
setShowOverflowMenu: Dispatch<SetStateAction<boolean>>
41+
lidId?: string
4142
}
4243
export function LabwareCardOverflowMenu(
4344
props: LabwareCardOverflowMenuProps
4445
): JSX.Element | null {
45-
const { labwareIds, setShowOverflowMenu } = props
46+
const { labwareIds, setShowOverflowMenu, lidId } = props
4647
const { t } = useTranslation('starting_deck_state')
4748
const savedSteps = useSelector(getSavedStepForms)
4849
const deckSetup = useSelector(getDeckSetupForActiveItem)
@@ -98,11 +99,13 @@ export function LabwareCardOverflowMenu(
9899
savedSteps,
99100
deckSetupLabware[topLabwareId]
100101
)
101-
102102
const handleClear = (): void => {
103103
labwareIds.forEach(labwareId => {
104104
dispatch(deleteContainer({ labwareId }))
105105
})
106+
if (lidId != null) {
107+
dispatch(deleteContainer({ labwareId: lidId }))
108+
}
106109
if (isAdapter) {
107110
dispatch(
108111
editSlotInfo({
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { useTranslation } from 'react-i18next'
2+
import { useDispatch, useSelector } from 'react-redux'
3+
4+
import {
5+
CustomizeExpandButton,
6+
ListButton,
7+
ListButtonAccordion,
8+
ListButtonAccordionContainer,
9+
} from '@opentrons/components'
10+
11+
import { getEnableStacking } from '../../../feature-flags/selectors'
12+
import { getCustomLabwareDefsByURI } from '../../../labware-defs/selectors'
13+
import { selectLid, selectTopLabware } from '../../../labware-ingred/actions'
14+
import { selectors } from '../../../labware-ingred/selectors'
15+
import { CUSTOM_CATEGORY } from '../../../pages/Designer/DeckSetup/constants'
16+
17+
import type { StackingProps } from '@opentrons/components'
18+
import type { LabwareDefinition2 } from '@opentrons/shared-data'
19+
import type { CategoryExpand } from '../../../pages/Designer/DeckSetup/DeckSetupToolbox'
20+
import type { ThunkDispatch } from '../../../types'
21+
import type { LabwareInfo } from './index'
22+
23+
interface SelectCustomLabwareProps {
24+
slot: string
25+
handleCategoryClick: (category: string, expand?: boolean) => void
26+
areCategoriesExpanded: CategoryExpand
27+
onFlexStacker: boolean
28+
filteredLabwareByCategory: Record<string, LabwareInfo[]>
29+
universalLid?: [string, LabwareDefinition2]
30+
}
31+
export function SelectCustomLabware(
32+
props: SelectCustomLabwareProps
33+
): JSX.Element | null {
34+
const {
35+
slot,
36+
handleCategoryClick,
37+
areCategoriesExpanded,
38+
onFlexStacker,
39+
filteredLabwareByCategory,
40+
universalLid,
41+
} = props
42+
const enableStacking = useSelector(getEnableStacking)
43+
const { t } = useTranslation(['starting_deck_state', 'shared'])
44+
const customLabwareDefs = useSelector(getCustomLabwareDefsByURI)
45+
const dispatch = useDispatch<ThunkDispatch<any>>()
46+
const zoomedInSlotInfo = useSelector(selectors.getZoomedInSlotInfo)
47+
const { selectedTopLabware, selectedLidLabware } = zoomedInSlotInfo
48+
49+
return filteredLabwareByCategory[CUSTOM_CATEGORY].length > 0 ? (
50+
<ListButton
51+
key={`ListButton_${CUSTOM_CATEGORY}`}
52+
type="noActive"
53+
onClick={() => {
54+
handleCategoryClick(CUSTOM_CATEGORY)
55+
}}
56+
>
57+
<ListButtonAccordionContainer id={`${CUSTOM_CATEGORY}_${slot}`}>
58+
<ListButtonAccordion
59+
mainHeadline={t(`${CUSTOM_CATEGORY}`)}
60+
isExpanded={areCategoriesExpanded[CUSTOM_CATEGORY]}
61+
>
62+
{filteredLabwareByCategory[CUSTOM_CATEGORY].map(({ uri }, index) => {
63+
const lidProps: StackingProps | null =
64+
slot !== 'offDeck' &&
65+
enableStacking &&
66+
universalLid != null &&
67+
customLabwareDefs[uri].metadata.displayCategory !== 'tubeRack'
68+
? {
69+
inputTitle: t('labware_quantity'),
70+
errorMessage: t('unsupported_range'),
71+
definition: universalLid[1],
72+
checkboxCaption: t('with_lid', {
73+
name: universalLid[1].metadata.displayName,
74+
}),
75+
inputFieldValue: 1,
76+
onInputFieldChange: () => {},
77+
inputCaption: '',
78+
checked: selectedLidLabware != null,
79+
onCheckboxChange: () => {
80+
dispatch(
81+
selectLid({
82+
labwareDefURI:
83+
selectedLidLabware === universalLid[0] ? null : uri,
84+
})
85+
)
86+
},
87+
}
88+
: null
89+
90+
return (
91+
<CustomizeExpandButton
92+
enableStackingFF={enableStacking}
93+
loadName={customLabwareDefs[uri].parameters.loadName}
94+
allowInputField={onFlexStacker}
95+
key={`${index}_${uri}`}
96+
id={`${index}_${uri}`}
97+
buttonText={customLabwareDefs[uri].metadata.displayName}
98+
buttonValue={uri}
99+
onChange={e => {
100+
e.stopPropagation()
101+
dispatch(selectTopLabware({ labwareDefURI: uri }))
102+
}}
103+
isSelected={uri === selectedTopLabware.labwareDefURI}
104+
stackingProps={lidProps ?? undefined}
105+
/>
106+
)
107+
})}
108+
</ListButtonAccordion>
109+
</ListButtonAccordionContainer>
110+
</ListButton>
111+
) : null
112+
}

0 commit comments

Comments
 (0)