Skip to content

Commit

Permalink
feat(protocol-designer): update Magnetic Module step form (#16362)
Browse files Browse the repository at this point in the history
re AUTH-812

<!--
Thanks for taking the time to open a Pull Request (PR)! Please make sure
you've read the "Opening Pull Requests" section of our Contributing
Guide:


https://github.com/Opentrons/opentrons/blob/edge/CONTRIBUTING.md#opening-pull-requests

GitHub provides robust markdown to format your PR. Links, diagrams,
pictures, and videos along with text formatting make it possible to
create a rich and informative PR. For more information on GitHub
markdown, see:


https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax

To ensure your code is reviewed quickly and thoroughly, please fill out
the sections below to the best of your ability!
-->

# Overview

<!--
Describe your PR at a high level. State acceptance criteria and how this
PR fits into other work. Link issues, PRs, and other relevant resources.
-->

Updating the Magnetic module step form for single module to match the
[design](https://www.figma.com/design/WbkiUyU8VhtKz0JSuIFA45/Feature%3A-Protocol-Designer-Phase-1?node-id=5536-13337&node-type=canvas&t=PjpyUuPMkMl2ODS7-0)

## Test Plan and Hands on Testing

<!--
Describe your testing of the PR. Emphasize testing not reflected in the
code. Attach protocols, logs, screenshots and any other assets that
support your testing.
-->

- Create an OT-2 protocol and add a magnetic module GEN1 or GEN2 
- Go to the Protocol Steps tab and add a step for Magnet
- Text display for the min and max engage height for magnetic module
GEN1 and GEN2 (only show unit when it's a GEN2)
- Add labware to the Magnetic Module, and there might be a recommended
engage height depending on the labware

## Changelog

<!--
List changes introduced by this PR considering future developers and the
end user. Give careful thought and clear documentation to breaking
changes.
-->
- Added an optional `caption` prop to `ToggleExpandStepFormField` for
displaying the min, max, and recommended engage height text under the
input field
- Added `onToggleUpdateValue() `to `ToggleExpandStepFormField` to handle
both boolean and string values, toggling between 'engage' and
'disengage' action types
- Updated `Toolbox.tsx` and other step forms to add or remove the
borderBottom grey line
- Added test
## Review requests

<!--
- What do you need from reviewers to feel confident this PR is ready to
merge?
- Ask questions.
-->

## Risk assessment

<!--
- Indicate the level of attention this PR needs.
- Provide context to guide reviewers.
- Discuss trade-offs, coupling, and side effects.
- Look for the possibility, even if you think it's small, that your
change may affect some other part of the system.
- For instance, changing return tip behavior may also change the
behavior of labware calibration.
- How do your unit tests and on hands on testing mitigate this PR's
risks and the risk of future regressions?
- Especially in high risk PRs, explain how you know your testing is
enough.
-->

---------

Co-authored-by: shiyaochen <[email protected]>
Co-authored-by: shiyaochen <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2024
1 parent 8f81026 commit b60f8dd
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 13 deletions.
1 change: 1 addition & 0 deletions components/src/organisms/Toolbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export function Toolbox(props: ToolboxProps): JSX.Element {
</Flex>
</Flex>
</Flex>
<Box borderBottom={`1px solid ${COLORS.grey30}`} />
<Box
padding={childrenPadding}
flex="1 1 auto"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"delete": "Delete step",
"dispensed": "Dispensed",
"duplicate": "Duplicate step",
"engage_height": "Engage height",
"edit_step": "Edit step",
"final_deck_state": "Final deck state",
"from": "from",
Expand Down
39 changes: 29 additions & 10 deletions protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ALIGN_CENTER,
COLORS,
DIRECTION_COLUMN,
Flex,
JUSTIFY_SPACE_BETWEEN,
Expand All @@ -21,6 +22,7 @@ interface ToggleExpandStepFormFieldProps extends FieldProps {
offLabel: string
toggleUpdateValue: (value: unknown) => void
toggleValue: unknown
caption?: string
}
export function ToggleExpandStepFormField(
props: ToggleExpandStepFormFieldProps
Expand All @@ -34,9 +36,19 @@ export function ToggleExpandStepFormField(
units,
toggleUpdateValue,
toggleValue,
caption,
...restProps
} = props

const onToggleUpdateValue = (): void => {
if (typeof toggleValue === 'boolean') {
toggleUpdateValue(!toggleValue)
} else if (toggleValue === 'engage' || toggleValue === 'disengage') {
const newToggleValue = toggleValue === 'engage' ? 'disengage' : 'engage'
toggleUpdateValue(newToggleValue)
}
}

return (
<ListItem type="noActive">
<Flex
Expand All @@ -48,21 +60,28 @@ export function ToggleExpandStepFormField(
<StyledText desktopStyle="bodyDefaultRegular">{title}</StyledText>
<ToggleButton
onClick={() => {
toggleUpdateValue(!toggleValue)
onToggleUpdateValue()
}}
label={isSelected ? onLabel : offLabel}
toggledOn={isSelected}
/>
</Flex>
{isSelected ? (
<InputStepFormField
{...restProps}
padding="0"
showTooltip={false}
title={fieldTitle}
units={units}
/>
) : null}
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing10}>
{isSelected ? (
<InputStepFormField
{...restProps}
padding="0"
showTooltip={false}
title={fieldTitle}
units={units}
/>
) : null}
{isSelected && caption != null ? (
<StyledText desktopStyle="captionRegular" color={COLORS.grey60}>
{caption}
</StyledText>
) : null}
</Flex>
</Flex>
</ListItem>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,95 @@
export function MagnetTools(): JSX.Element {
return <div>TODO: wire this up</div>
import { useSelector } from 'react-redux'
import { useTranslation } from 'react-i18next'
import {
COLORS,
DIRECTION_COLUMN,
Divider,
Flex,
ListItem,
SPACING,
StyledText,
} from '@opentrons/components'
import { MAGNETIC_MODULE_V1 } from '@opentrons/shared-data'
import {
MAX_ENGAGE_HEIGHT_V1,
MAX_ENGAGE_HEIGHT_V2,
MIN_ENGAGE_HEIGHT_V1,
MIN_ENGAGE_HEIGHT_V2,
} from '../../../../../../constants'
import {
getMagnetLabwareEngageHeight,
getMagneticLabwareOptions,
} from '../../../../../../ui/modules/selectors'
import { ToggleExpandStepFormField } from '../../../../../../molecules'
import { getModuleEntities } from '../../../../../../step-forms/selectors'

import type { StepFormProps } from '../../types'

export function MagnetTools(props: StepFormProps): JSX.Element {
const { propsForFields, formData } = props
const { t } = useTranslation(['application', 'form', 'protocol_steps'])
const moduleLabwareOptions = useSelector(getMagneticLabwareOptions)
const moduleEntities = useSelector(getModuleEntities)
const defaultEngageHeight = useSelector(getMagnetLabwareEngageHeight)
const moduleModel = moduleEntities[formData.moduleId].model

const mmUnits = t('units.millimeter')
const isGen1 = moduleModel === MAGNETIC_MODULE_V1
const engageHeightMinMax = isGen1
? t('magnet_height_caption', {
low: MIN_ENGAGE_HEIGHT_V1,
high: MAX_ENGAGE_HEIGHT_V1,
})
: t('magnet_height_caption', {
low: `${MIN_ENGAGE_HEIGHT_V2} ${mmUnits}`,
high: `${MAX_ENGAGE_HEIGHT_V2} ${mmUnits}`,
})
const engageHeightDefault =
defaultEngageHeight != null
? isGen1
? t('magnet_recommended', { default: defaultEngageHeight })
: t('magnet_recommended', {
default: `${defaultEngageHeight} ${mmUnits}`,
})
: ''
const engageHeightCaption = `${engageHeightMinMax} ${engageHeightDefault}`

return (
<Flex flexDirection={DIRECTION_COLUMN}>
<Flex
flexDirection={DIRECTION_COLUMN}
padding={`${SPACING.spacing16} ${SPACING.spacing16} ${SPACING.spacing12}`}
gridGap={SPACING.spacing12}
width="100%"
>
<StyledText desktopStyle="bodyDefaultRegular" color={COLORS.grey60}>
{t('protocol_steps:module')}
</StyledText>
<ListItem type="noActive">
<Flex padding={SPACING.spacing12}>
<StyledText desktopStyle="bodyDefaultRegular">
{moduleLabwareOptions[0].name}
</StyledText>
</Flex>
</ListItem>
</Flex>
<Divider marginY="0" />
<Flex flexDirection={DIRECTION_COLUMN} padding={SPACING.spacing16}>
<ToggleExpandStepFormField
{...propsForFields.engageHeight}
toggleValue={propsForFields.magnetAction.value}
toggleUpdateValue={propsForFields.magnetAction.updateValue}
title={t('form:step_edit_form.field.magnetAction.label')}
fieldTitle={t('protocol_steps:engage_height')}
isSelected={formData.magnetAction === 'engage'}
units={mmUnits}
onLabel={t('form:step_edit_form.field.magnetAction.options.engage')}
offLabel={t(
'form:step_edit_form.field.magnetAction.options.disengage'
)}
caption={engageHeightCaption}
/>
</Flex>
</Flex>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export function MoveLabwareTools(props: StepFormProps): JSX.Element {
}
/>
) : null}
<Box borderBottom={`1px solid ${COLORS.grey30}`} />
<MoveLabwareField {...propsForFields.labware} />
<Box borderBottom={`1px solid ${COLORS.grey30}`} />
<LabwareLocationField
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { describe, it, vi, beforeEach, expect } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { renderWithProviders } from '../../../../../../__testing-utils__'
import { i18n } from '../../../../../../assets/localization'
import {
getMagneticLabwareOptions,
getMagnetLabwareEngageHeight,
} from '../../../../../../ui/modules/selectors'
import { getModuleEntities } from '../../../../../../step-forms/selectors'
import { MagnetTools } from '../MagnetTools'
import type { ComponentProps } from 'react'
import type * as ModulesSelectors from '../../../../../../ui/modules/selectors'

vi.mock('../../../../../../step-forms/selectors')

vi.mock('../../../../../../ui/modules/selectors', async importOriginal => {
const actualFields = await importOriginal<typeof ModulesSelectors>()
return {
...actualFields,
getMagnetLabwareEngageHeight: vi.fn(),
getMagneticLabwareOptions: vi.fn(),
}
})
const render = (props: ComponentProps<typeof MagnetTools>) => {
return renderWithProviders(<MagnetTools {...props} />, {
i18nInstance: i18n,
})[0]
}

describe('MagnetTools', () => {
let props: React.ComponentProps<typeof MagnetTools>

beforeEach(() => {
props = {
formData: {
id: 'magnet',
stepType: 'magnet',
moduleId: 'magnetId',
magnetAction: 'engage',
} as any,
focusHandlers: {
blur: vi.fn(),
focus: vi.fn(),
dirtyFields: [],
focusedField: null,
},
toolboxStep: 1,
propsForFields: {
magnetAction: {
onFieldFocus: vi.fn(),
onFieldBlur: vi.fn(),
errorToShow: null,
disabled: false,
name: 'magnetAction',
updateValue: vi.fn(),
value: 'engage',
},
engageHeight: {
onFieldFocus: vi.fn(),
onFieldBlur: vi.fn(),
errorToShow: null,
disabled: false,
name: 'engage height',
updateValue: vi.fn(),
value: 10,
},
},
}
vi.mocked(getMagneticLabwareOptions).mockReturnValue([
{ name: 'mock name', value: 'mockValue' },
])
vi.mocked(getModuleEntities).mockReturnValue({
magnetId: {
id: 'magnetId',
model: 'magneticModuleV2',
type: 'magneticModuleType',
},
})
vi.mocked(getMagnetLabwareEngageHeight).mockReturnValue(null)
})

it('renders the text and a switch button for v2', () => {
render(props)
screen.getByText('Module')
screen.getByText('mock name')
screen.getByText('Magnet action')
screen.getByLabelText('Engage')
const toggleButton = screen.getByRole('switch')
screen.getByText('Engage height')
screen.getByText('Must be between -2.5 mm to 25 mm.')

fireEvent.click(toggleButton)
expect(props.propsForFields.magnetAction.updateValue).toHaveBeenCalled()
})
it('renders the input text for v1', () => {
vi.mocked(getModuleEntities).mockReturnValue({
magnetId: {
id: 'magnetId',
model: 'magneticModuleV1',
type: 'magneticModuleType',
},
})
render(props)
screen.getByText('Must be between 0 to 45.')
})
})

0 comments on commit b60f8dd

Please sign in to comment.