Skip to content

Commit 4461ca7

Browse files
authored
fix(protocol-designer): change BlockedSlot overlay for waste chute (#17799)
closes RQA-4003
1 parent 9d41c60 commit 4461ca7

File tree

3 files changed

+134
-24
lines changed

3 files changed

+134
-24
lines changed

components/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,24 @@ interface WasteChuteProps {
8989
backgroundColor: string
9090
showHighlight?: boolean
9191
tagInfo?: DeckLabelProps[]
92+
// optional opacity and overlay to change the overlay container over the WasteChute container
93+
// currently used in PD's BlockedSlot for drag/drop
94+
overlay?: JSX.Element
95+
opacity?: string
9296
}
9397

9498
/**
9599
* a deck map foreign object representing the physical location of the waste chute connected to the deck
96100
*/
97101
export function WasteChute(props: WasteChuteProps): JSX.Element {
98-
const { wasteIconColor, backgroundColor, showHighlight, tagInfo } = props
102+
const {
103+
wasteIconColor,
104+
backgroundColor,
105+
showHighlight,
106+
tagInfo,
107+
overlay,
108+
opacity,
109+
} = props
99110

100111
return (
101112
<>
@@ -105,29 +116,33 @@ export function WasteChute(props: WasteChuteProps): JSX.Element {
105116
x={WASTE_CHUTE_X}
106117
y={-51}
107118
flexProps={{ flex: '1' }}
108-
foreignObjectProps={{ flex: '1' }}
119+
foreignObjectProps={{ opacity: opacity ?? '1.0', flex: '1' }}
109120
>
110-
<Flex
111-
alignItems={ALIGN_CENTER}
112-
backgroundColor={backgroundColor}
113-
borderRadius="6px"
114-
color={wasteIconColor}
115-
flexDirection={DIRECTION_COLUMN}
116-
gridGap={SPACING.spacing4}
117-
justifyContent={JUSTIFY_CENTER}
118-
padding={SPACING.spacing8}
119-
width="100%"
120-
border={showHighlight ? `3px solid ${COLORS.blue50}` : 'none'}
121-
>
122-
<Icon name="trash" color={wasteIconColor} height="2rem" />
123-
<Text
124-
color={COLORS.white}
125-
textAlign={TEXT_ALIGN_CENTER}
126-
css={TYPOGRAPHY.bodyTextSemiBold}
121+
{overlay != null ? (
122+
overlay
123+
) : (
124+
<Flex
125+
alignItems={ALIGN_CENTER}
126+
backgroundColor={backgroundColor}
127+
borderRadius="6px"
128+
color={wasteIconColor}
129+
flexDirection={DIRECTION_COLUMN}
130+
gridGap={SPACING.spacing4}
131+
justifyContent={JUSTIFY_CENTER}
132+
padding={SPACING.spacing8}
133+
width="100%"
134+
border={showHighlight ? `3px solid ${COLORS.blue50}` : 'none'}
127135
>
128-
Waste chute
129-
</Text>
130-
</Flex>
136+
<Icon name="trash" color={wasteIconColor} height="2rem" />
137+
<Text
138+
color={COLORS.white}
139+
textAlign={TEXT_ALIGN_CENTER}
140+
css={TYPOGRAPHY.bodyTextSemiBold}
141+
>
142+
Waste chute
143+
</Text>
144+
</Flex>
145+
)}
131146
</RobotCoordsForeignObject>
132147
{tagInfo != null && tagInfo.length > 0 ? (
133148
<DeckLabelSet

protocol-designer/src/pages/Designer/DeckSetup/Overlays/BlockedSlot.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
1-
import { COLORS, Icon } from '@opentrons/components'
1+
import { useSelector } from 'react-redux'
2+
import {
3+
ALIGN_CENTER,
4+
COLORS,
5+
Flex,
6+
Icon,
7+
JUSTIFY_CENTER,
8+
SPACING,
9+
WasteChute,
10+
} from '@opentrons/components'
11+
import {
12+
getCutoutIdFromAddressableArea,
13+
getDeckDefFromRobotType,
14+
FLEX_ROBOT_TYPE,
15+
WASTE_CHUTE_CUTOUT,
16+
} from '@opentrons/shared-data'
17+
import { getHasWasteChute } from '@opentrons/step-generation'
18+
import { getAdditionalEquipmentEntities } from '../../../../step-forms/selectors'
219
import { SlotOverlay } from './SlotOverlay'
320
import type { CoordinateTuple, DeckSlotId } from '@opentrons/shared-data'
421

@@ -9,7 +26,33 @@ interface BlockedSlotProps {
926

1027
export function BlockedSlot(props: BlockedSlotProps): JSX.Element | null {
1128
const { slotId, slotPosition } = props
12-
return (
29+
const deckDef = getDeckDefFromRobotType(FLEX_ROBOT_TYPE)
30+
const cutoutId = getCutoutIdFromAddressableArea(slotId, deckDef)
31+
const additionalEquipmentEntities = useSelector(
32+
getAdditionalEquipmentEntities
33+
)
34+
const hasWasteChute = getHasWasteChute(additionalEquipmentEntities)
35+
36+
return cutoutId === WASTE_CHUTE_CUTOUT && hasWasteChute ? (
37+
<WasteChute
38+
backgroundColor={COLORS.white}
39+
wasteIconColor={COLORS.red50}
40+
opacity="0.8"
41+
overlay={
42+
<Flex
43+
// NOTE: this border radius matches WasteChuteFixture's border radius
44+
borderRadius="6px"
45+
alignItems={ALIGN_CENTER}
46+
backgroundColor={COLORS.white}
47+
gridGap={SPACING.spacing8}
48+
justifyContent={JUSTIFY_CENTER}
49+
width="100%"
50+
>
51+
<Icon size="2.25rem" name="no-icon" color={COLORS.red50} />
52+
</Flex>
53+
}
54+
/>
55+
) : (
1356
<SlotOverlay
1457
slotId={slotId}
1558
slotPosition={slotPosition}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, it, vi, beforeEach } from 'vitest'
2+
import '@testing-library/jest-dom/vitest'
3+
import { WasteChute } from '@opentrons/components'
4+
import { getHasWasteChute } from '@opentrons/step-generation'
5+
import { screen } from '@testing-library/react'
6+
import { renderWithProviders } from '../../../../__testing-utils__'
7+
import { getAdditionalEquipmentEntities } from '../../../../step-forms/selectors'
8+
import { BlockedSlot } from '../Overlays/BlockedSlot'
9+
import { SlotOverlay } from '../Overlays/SlotOverlay'
10+
11+
import type { ComponentProps } from 'react'
12+
import type * as OpentronsComponents from '@opentrons/components'
13+
14+
vi.mock('../Overlays/SlotOverlay')
15+
vi.mock('@opentrons/step-generation')
16+
vi.mock('../../../../step-forms/selectors')
17+
vi.mock('@opentrons/components', async importOriginal => {
18+
const actual = await importOriginal<typeof OpentronsComponents>()
19+
return {
20+
...actual,
21+
WasteChute: vi.fn(),
22+
}
23+
})
24+
25+
const render = (props: ComponentProps<typeof BlockedSlot>) => {
26+
return renderWithProviders(<BlockedSlot {...props} />)[0]
27+
}
28+
29+
describe('BlockedSlot', () => {
30+
let props: ComponentProps<typeof BlockedSlot>
31+
32+
beforeEach(() => {
33+
props = {
34+
slotId: 'D3',
35+
slotPosition: [0, 0, 0],
36+
}
37+
vi.mocked(getAdditionalEquipmentEntities).mockReturnValue({})
38+
vi.mocked(SlotOverlay).mockReturnValue(<div>mock SlotOverlay</div>)
39+
vi.mocked(WasteChute).mockReturnValue(<div>mock WasteChute</div>)
40+
})
41+
it('renders a waste chute overlay', () => {
42+
vi.mocked(getHasWasteChute).mockReturnValue(true)
43+
render(props)
44+
screen.getByText('mock WasteChute')
45+
})
46+
it('renders a slot overlay', () => {
47+
vi.mocked(getHasWasteChute).mockReturnValue(false)
48+
49+
render(props)
50+
screen.getByText('mock SlotOverlay')
51+
})
52+
})

0 commit comments

Comments
 (0)