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 ${