Skip to content

Commit

Permalink
feat(protocol-designer): add export warning modal (#16627)
Browse files Browse the repository at this point in the history
If a user exports a protocol that doesn't use some hardware
(instruments, modules, staging areas, trash entities), we warn them on
export with an itemized list of any implicated hardware. Here, I update
the utility for getting content for this modal depending on the specific
unused hardware. I also wire up the modal using our `useBlockingHint`
hook in `ProtocolOverview`

Closes AUTH-818
  • Loading branch information
ncdiehl11 authored Oct 30, 2024
1 parent 31eb1a2 commit 4e56d7a
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 203 deletions.
44 changes: 41 additions & 3 deletions protocol-designer/src/assets/localization/en/alert.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"body2": "7.3.0 or higher",
"body3": ". Please ensure your robot is updated to the correct version."
},
"unused_hardware": {
"title": "Protocol has unused hardware"
},
"no_commands": {
"title": "Protocol has no steps"
},
"change_magnet_module_model": {
"title": "All existing engage heights will be cleared"
},
Expand Down Expand Up @@ -269,10 +275,42 @@
"no_commands": {
"heading": "Your protocol has no steps",
"body1": "This protocol has no steps in it- there's nothing for the robot to do! Before trying to run this on your robot add at least one step between your Starting Deck State and Final Deck State.",
"body2": "Learn more about building steps ",
"redesign": {
"body2": "Learn more about building steps "
},
"redesign": {
"unused_hardware": "Unused hardware:",
"unused_module": "{{module}} in slot {{slot}}",
"unused_pipette": "{{pipette}} on {{mount}} mount",
"unused_staging_area": "Staging area in slot {{slot}}",
"no_commands": {
"heading": "Protocol has no steps",
"body": "This protocol has no steps. Before trying to run this protocol on your robot, add at least one step."
"body1": "This protocol has no steps. Before trying to run this protocol on your robot, add at least one step."
},
"unused_gripper": {
"heading": "Protocol has unused gripper",
"body1": "The Flex Gripper is not used in any step. You won't be able to run this protocol unless the gripper is attached to your robot.",
"body2": "If you don’t intend to use the gripper, remove it from your protocol."
},
"unused_hardware_content": {
"heading": "Protocol has unused hardware",
"body1": "The hardware listed below is not used in any step. You won't be able to run this protocol unless the hardware is attached to your robot.",
"body2": "If you don’t intend to use this hardware, remove it from your protocol."
},
"unused_module_content": {
"heading": "Protocol has unused module",
"body1": "The {{module}} in slot {{slot}} is not used in any step. You won't be able to run this protocol unless the module is attached to your robot.",
"body2": "If you don’t intend to use the module, remove it from your protocol."
},
"unused_pipette_content": {
"heading": "Protocol has unused pipette",
"body1": "The {{pipette}} on {{mount}} mount is currently not used in any step. You won't be able to run this protocol unless this pipette is attached to your robot.",
"body1_96ch": "The Flex 8-Channel 1000 μL is currently not used in any step. You won't be able to run this protocol unless this pipette is attached to your robot.",
"body2": "If you don’t intend to use the pipette, remove it from your protocol."
},
"unused_staging_area_content": {
"heading": "Protocol has unused staging area",
"body1": "The staging area in {{slot}} is not used in any step. You won't be able to run this protocol unless it is in your robot's deck configuration.",
"body2": "If you don’t intend to use the staging area, remove it from your protocol."
}
},
"unused_pipette_and_module": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ describe('BlockingHintModal', () => {
render(props)
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }))
expect(props.handleCancel).toHaveBeenCalled()
expect(vi.mocked(removeHint)).toHaveBeenCalled()
fireEvent.click(screen.getByRole('button', { name: 'Continue' }))
expect(props.handleContinue).toHaveBeenCalled()
expect(vi.mocked(removeHint)).toHaveBeenCalled()
Expand Down
3 changes: 1 addition & 2 deletions protocol-designer/src/organisms/BlockingHintModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export function BlockingHintModal(props: HintProps): JSX.Element {
}, [])

const onCancelClick = (): void => {
dispatch(actions.removeHint(hintKey, rememberDismissal))
handleCancel()
}

Expand All @@ -56,7 +55,7 @@ export function BlockingHintModal(props: HintProps): JSX.Element {
<Flex
alignItems={ALIGN_CENTER}
justifyContent={JUSTIFY_SPACE_BETWEEN}
padding={SPACING.spacing24}
padding={`0 ${SPACING.spacing24} ${SPACING.spacing24}`}
>
<Flex
alignItems={ALIGN_CENTER}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ export interface HintProps {
* If the hint is enabled but has been dismissed, it will automatically call `handleContinue` when enabled.
* useBlockingHint expects the parent to disable the hint on cancel/continue */
enabled: boolean
hintKey: HintKey
hintKey: HintKey | null
content: React.ReactNode
handleCancel: () => void
handleContinue: () => void
}

export const useBlockingHint = (args: HintProps): JSX.Element | null => {
const { enabled, hintKey, handleCancel, handleContinue, content } = args
const isDismissed = useSelector(getDismissedHints).includes(hintKey)

const dismissedHints = useSelector(getDismissedHints)
const isDismissed = hintKey != null && dismissedHints.includes(hintKey)
if (isDismissed) {
if (enabled) {
handleContinue()
}
return null
}

if (!enabled) {
if (!enabled || hintKey == null) {
return null
}

Expand Down
Loading

0 comments on commit 4e56d7a

Please sign in to comment.