From 5af8b1cfa428c2eb8bd419b27185ead9135b1272 Mon Sep 17 00:00:00 2001
From: Jethary Alcid <66035149+jerader@users.noreply.github.com>
Date: Thu, 26 Sep 2024 08:21:11 -0400
Subject: [PATCH] feat(protocol-designer): heater-shaker step form tools
(#16342)
closes AUTH-811
---
components/src/atoms/Checkbox/index.tsx | 8 +-
.../localization/en/protocol_steps.json | 5 +
protocol-designer/src/atoms/Toggle/index.tsx | 70 +++++++++
protocol-designer/src/atoms/index.ts | 1 +
.../CheckboxExpandStepFormField/index.tsx | 52 +++++++
.../molecules/InputStepFormField/index.tsx | 81 ++++++++++
.../ToggleExpandStepFormField/index.tsx | 68 +++++++++
.../molecules/ToggleStepFormField/index.tsx | 72 +++++++++
protocol-designer/src/molecules/index.ts | 4 +
.../StepTools/HeaterShakerTools/index.tsx | 138 +++++++++++++++++-
protocol-designer/src/ui/modules/utils.ts | 5 +-
11 files changed, 497 insertions(+), 7 deletions(-)
create mode 100644 protocol-designer/src/atoms/Toggle/index.tsx
create mode 100644 protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx
create mode 100644 protocol-designer/src/molecules/InputStepFormField/index.tsx
create mode 100644 protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx
create mode 100644 protocol-designer/src/molecules/ToggleStepFormField/index.tsx
diff --git a/components/src/atoms/Checkbox/index.tsx b/components/src/atoms/Checkbox/index.tsx
index 36547743821..02fa36da6d4 100644
--- a/components/src/atoms/Checkbox/index.tsx
+++ b/components/src/atoms/Checkbox/index.tsx
@@ -98,11 +98,13 @@ export function Checkbox(props: CheckboxProps): JSX.Element {
interface CheckProps {
isChecked: boolean
+ color?: string
}
-function Check(props: CheckProps): JSX.Element {
- return props.isChecked ? (
+export function Check(props: CheckProps): JSX.Element {
+ const { isChecked, color = COLORS.white } = props
+ return isChecked ? (
-
+
) : (
void
+ label: string
+ disabled?: boolean
+}
+export function Toggle(props: ToggleProps): JSX.Element {
+ const { isSelected, onClick, label, disabled = false } = props
+ return (
+
+
+ {label}
+
+
+
+
+
+ )
+}
+
+const TOGGLE_DISABLED_STYLES = css`
+ color: ${COLORS.grey50};
+
+ &:hover {
+ color: ${COLORS.grey55};
+ }
+
+ &:focus-visible {
+ box-shadow: 0 0 0 3px ${COLORS.yellow50};
+ }
+
+ &:disabled {
+ color: ${COLORS.grey30};
+ }
+`
+
+const TOGGLE_ENABLED_STYLES = css`
+ color: ${COLORS.blue50};
+
+ &:hover {
+ color: ${COLORS.blue55};
+ }
+
+ &:focus-visible {
+ box-shadow: 0 0 0 3px ${COLORS.yellow50};
+ }
+
+ &:disabled {
+ color: ${COLORS.grey30};
+ }
+`
diff --git a/protocol-designer/src/atoms/index.ts b/protocol-designer/src/atoms/index.ts
index f87cf0102a1..b56d9bcd741 100644
--- a/protocol-designer/src/atoms/index.ts
+++ b/protocol-designer/src/atoms/index.ts
@@ -1 +1,2 @@
export * from './constants'
+export * from './Toggle'
diff --git a/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx b/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx
new file mode 100644
index 00000000000..06db7033790
--- /dev/null
+++ b/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx
@@ -0,0 +1,52 @@
+import {
+ ALIGN_CENTER,
+ Btn,
+ COLORS,
+ Check,
+ DIRECTION_COLUMN,
+ Flex,
+ JUSTIFY_SPACE_BETWEEN,
+ ListItem,
+ SPACING,
+ StyledText,
+} from '@opentrons/components'
+
+interface CheckboxExpandStepFormFieldProps {
+ title: string
+ checkboxUpdateValue: (value: unknown) => void
+ checkboxValue: unknown
+ isChecked: boolean
+ children: React.ReactNode
+}
+export function CheckboxExpandStepFormField(
+ props: CheckboxExpandStepFormFieldProps
+): JSX.Element {
+ const {
+ checkboxUpdateValue,
+ checkboxValue,
+ children,
+ isChecked,
+ title,
+ } = props
+ return (
+
+
+
+ {title}
+ {
+ checkboxUpdateValue(!checkboxValue)
+ }}
+ >
+
+
+
+ {children}
+
+
+ )
+}
diff --git a/protocol-designer/src/molecules/InputStepFormField/index.tsx b/protocol-designer/src/molecules/InputStepFormField/index.tsx
new file mode 100644
index 00000000000..60e76eda7bf
--- /dev/null
+++ b/protocol-designer/src/molecules/InputStepFormField/index.tsx
@@ -0,0 +1,81 @@
+import { useTranslation } from 'react-i18next'
+import {
+ COLORS,
+ DIRECTION_COLUMN,
+ Flex,
+ Icon,
+ InputField,
+ SPACING,
+ StyledText,
+ Tooltip,
+ useHoverTooltip,
+} from '@opentrons/components'
+import { getFieldDefaultTooltip } from '../../components/StepEditForm/utils'
+
+import type { FieldProps } from '../../components/StepEditForm/types'
+
+interface InputStepFormFieldProps extends FieldProps {
+ title: string
+ units: string
+ padding?: string
+ showTooltip?: boolean
+}
+
+export function InputStepFormField(
+ props: InputStepFormFieldProps
+): JSX.Element {
+ const {
+ errorToShow,
+ onFieldBlur,
+ onFieldFocus,
+ updateValue,
+ value,
+ name,
+ title,
+ units,
+ showTooltip = true,
+ padding = SPACING.spacing16,
+ ...otherProps
+ } = props
+ const { t } = useTranslation(['tooltip', 'application'])
+ const [targetProps, tooltipProps] = useHoverTooltip()
+
+ return (
+
+ {title !== null ? (
+
+
+ {title}
+
+ {showTooltip ? (
+ <>
+
+
+
+
+ {getFieldDefaultTooltip(name, t)}
+
+ >
+ ) : null}
+
+ ) : null}
+ {
+ updateValue(e.currentTarget.value)
+ }}
+ value={value ? String(value) : null}
+ units={units}
+ />
+
+ )
+}
diff --git a/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx b/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx
new file mode 100644
index 00000000000..c7ba55c4447
--- /dev/null
+++ b/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx
@@ -0,0 +1,68 @@
+import {
+ ALIGN_CENTER,
+ DIRECTION_COLUMN,
+ Flex,
+ JUSTIFY_SPACE_BETWEEN,
+ ListItem,
+ SPACING,
+ StyledText,
+} from '@opentrons/components'
+import { Toggle } from '../../atoms'
+import { InputStepFormField } from '../InputStepFormField'
+import type { FieldProps } from '../../pages/Designer/ProtocolSteps/StepForm/types'
+
+interface ToggleExpandStepFormFieldProps extends FieldProps {
+ title: string
+ fieldTitle: string
+ isSelected: boolean
+ units: string
+ onLabel: string
+ offLabel: string
+ toggleUpdateValue: (value: unknown) => void
+ toggleValue: unknown
+}
+export function ToggleExpandStepFormField(
+ props: ToggleExpandStepFormFieldProps
+): JSX.Element {
+ const {
+ title,
+ isSelected,
+ onLabel,
+ offLabel,
+ fieldTitle,
+ units,
+ toggleUpdateValue,
+ toggleValue,
+ ...restProps
+ } = props
+
+ return (
+
+
+
+ {title}
+ {
+ toggleUpdateValue(!toggleValue)
+ }}
+ label={isSelected ? onLabel : offLabel}
+ isSelected={isSelected}
+ />
+
+ {isSelected ? (
+
+ ) : null}
+
+
+ )
+}
diff --git a/protocol-designer/src/molecules/ToggleStepFormField/index.tsx b/protocol-designer/src/molecules/ToggleStepFormField/index.tsx
new file mode 100644
index 00000000000..05404294629
--- /dev/null
+++ b/protocol-designer/src/molecules/ToggleStepFormField/index.tsx
@@ -0,0 +1,72 @@
+import {
+ ALIGN_CENTER,
+ DIRECTION_COLUMN,
+ Flex,
+ JUSTIFY_SPACE_BETWEEN,
+ ListItem,
+ SPACING,
+ StyledText,
+ TOOLTIP_BOTTOM,
+ Tooltip,
+ useHoverTooltip,
+} from '@opentrons/components'
+import { Toggle } from '../../atoms'
+
+interface ToggleStepFormFieldProps {
+ title: string
+ isSelected: boolean
+ onLabel: string
+ offLabel: string
+ toggleUpdateValue: (value: unknown) => void
+ toggleValue: unknown
+ tooltipContent: string | null
+ isDisabled: boolean
+}
+export function ToggleStepFormField(
+ props: ToggleStepFormFieldProps
+): JSX.Element {
+ const {
+ title,
+ isSelected,
+ onLabel,
+ offLabel,
+ toggleUpdateValue,
+ toggleValue,
+ tooltipContent,
+ isDisabled,
+ } = props
+ const [targetProps, tooltipProps] = useHoverTooltip({
+ placement: TOOLTIP_BOTTOM,
+ })
+
+ return (
+ <>
+
+
+
+ {title}
+ {
+ toggleUpdateValue(!toggleValue)
+ }}
+ label={isSelected ? onLabel : offLabel}
+ isSelected={isSelected}
+ />
+
+
+
+ {tooltipContent != null ? (
+ {tooltipContent}
+ ) : null}
+ >
+ )
+}
diff --git a/protocol-designer/src/molecules/index.ts b/protocol-designer/src/molecules/index.ts
index b12df75dc80..1c0f2bbb67f 100644
--- a/protocol-designer/src/molecules/index.ts
+++ b/protocol-designer/src/molecules/index.ts
@@ -1,3 +1,7 @@
+export * from './CheckboxExpandStepFormField'
export * from './CheckboxStepFormField'
export * from './DropdownStepFormField'
+export * from './InputStepFormField'
export * from './SettingsIcon'
+export * from './ToggleExpandStepFormField'
+export * from './ToggleStepFormField'
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx
index 686b7a2cdf6..eefc9d36717 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx
@@ -1,3 +1,137 @@
-export function HeaterShakerTools(): JSX.Element {
- return TODO: wire this up
+import { useEffect } from 'react'
+import { useSelector } from 'react-redux'
+import { useTranslation } from 'react-i18next'
+import {
+ Box,
+ COLORS,
+ DIRECTION_COLUMN,
+ Flex,
+ ListItem,
+ SPACING,
+ StyledText,
+} from '@opentrons/components'
+import { getHeaterShakerLabwareOptions } from '../../../../../../ui/modules/selectors'
+import {
+ CheckboxExpandStepFormField,
+ DropdownStepFormField,
+ InputStepFormField,
+ ToggleExpandStepFormField,
+ ToggleStepFormField,
+} from '../../../../../../molecules'
+import type { StepFormProps } from '../../types'
+
+export function HeaterShakerTools(props: StepFormProps): JSX.Element {
+ const { propsForFields, formData } = props
+ const { t } = useTranslation(['application', 'form', 'protocol_steps'])
+ const moduleLabwareOptions = useSelector(getHeaterShakerLabwareOptions)
+
+ useEffect(() => {
+ if (moduleLabwareOptions.length === 1) {
+ propsForFields.moduleId.updateValue(moduleLabwareOptions[0].value)
+ }
+ }, [])
+
+ return (
+
+ {moduleLabwareOptions.length > 1 ? (
+
+ ) : (
+
+
+ {t('protocol_steps:module')}
+
+
+
+
+ {moduleLabwareOptions[0].name}
+
+
+
+
+ )}
+
+
+
+ {t('protocol_steps:heater_shaker_settings')}
+
+
+
+
+
+ {/* TODO: wire up the new timer with the combined field */}
+ {formData.heaterShakerSetTimer === true ? (
+
+ ) : null}
+
+
+
+ )
}
diff --git a/protocol-designer/src/ui/modules/utils.ts b/protocol-designer/src/ui/modules/utils.ts
index 80560fab6ec..ec2a20a7474 100644
--- a/protocol-designer/src/ui/modules/utils.ts
+++ b/protocol-designer/src/ui/modules/utils.ts
@@ -105,8 +105,9 @@ export function getModuleLabwareOptions(
options = modulesOnDeck.map(moduleOnDeck => {
const labware = getLabwareOnModule(initialDeckSetup, moduleOnDeck.id)
if (labware) {
- const labwareOnAdapterId =
- labwares[labware.id] != null ? labwares[labware.id].id : null
+ const labwareOnAdapterId = Object.values(labwares).find(
+ lw => lw.slot === labware.id
+ )?.id
if (labwareOnAdapterId != null) {
return {
name: `${nicknamesById[labwareOnAdapterId]} in ${