Skip to content

Commit

Permalink
feat(protocol-designer): update error style and render logic (#16589)
Browse files Browse the repository at this point in the history
This PR fixes some styling bugs for both form-level and timeline errors.
Also, I update the logic of the step form 'save' button to always be
enabled, and conditionally render form errors and warnings should any
exist. To consolidate components that are providing essentially the same
function in `ToggleExpandStepFormField` and
`CheckboxExpandStepFormField`, I refactor `ToggleExpandStepFormField` to
render either a toggle button or a checkbox. Lastly, I update copy for
thermocycler step form toggle input field menus.
  • Loading branch information
ncdiehl11 authored Oct 24, 2024
1 parent 02236b3 commit 545d0f7
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 63 deletions.
12 changes: 6 additions & 6 deletions protocol-designer/src/assets/localization/en/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -209,20 +209,20 @@
},
"thermocyclerState": {
"block": {
"engage": "Engage block temperature",
"engage": "Block temperature",
"label": "Thermocycler block",
"temperature": "Block temperature",
"toggleOff": "Deactivated",
"toggleOn": "Active",
"toggleOff": "Deactivate",
"toggleOn": "Activate",
"valid_range": "Valid range between 4 and 96ºC"
},
"ending_hold": "Ending hold",
"lid": {
"engage": "Engage lid temperature",
"engage": "Lid temperature",
"label": "Lid",
"temperature": "Lid temperature",
"toggleOff": "Deactivated",
"toggleOn": "Active",
"toggleOff": "Deactivate",
"toggleOn": "Activate",
"valid_range": "Valid range between 37 and 110ºC"
},
"lidPosition": {
Expand Down
34 changes: 24 additions & 10 deletions protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
ALIGN_CENTER,
Btn,
COLORS,
Check,
DIRECTION_COLUMN,
Flex,
JUSTIFY_SPACE_BETWEEN,
Expand All @@ -23,6 +25,7 @@ interface ToggleExpandStepFormFieldProps extends FieldProps {
onLabel?: string
offLabel?: string
caption?: string
toggleElement?: 'toggle' | 'checkbox'
}
export function ToggleExpandStepFormField(
props: ToggleExpandStepFormFieldProps
Expand All @@ -37,6 +40,7 @@ export function ToggleExpandStepFormField(
toggleUpdateValue,
toggleValue,
caption,
toggleElement = 'toggle',
...restProps
} = props

Expand All @@ -58,6 +62,7 @@ export function ToggleExpandStepFormField(
}
}

const label = isSelected ? onLabel : offLabel ?? null
return (
<ListItem type="noActive">
<Flex
Expand All @@ -68,16 +73,25 @@ export function ToggleExpandStepFormField(
<Flex justifyContent={JUSTIFY_SPACE_BETWEEN} alignItems={ALIGN_CENTER}>
<StyledText desktopStyle="bodyDefaultRegular">{title}</StyledText>
<Flex alignItems={ALIGN_CENTER} gridGap={SPACING.spacing8}>
<StyledText desktopStyle="bodyDefaultRegular" color={COLORS.grey60}>
{isSelected ? onLabel : offLabel ?? null}
</StyledText>
<ToggleButton
onClick={() => {
onToggleUpdateValue()
}}
label={isSelected ? onLabel : offLabel}
toggledOn={isSelected}
/>
{label != null ? (
<StyledText
desktopStyle="bodyDefaultRegular"
color={COLORS.grey60}
>
{isSelected ? onLabel : offLabel ?? null}
</StyledText>
) : null}
{toggleElement === 'toggle' ? (
<ToggleButton
onClick={onToggleUpdateValue}
label={isSelected ? onLabel : offLabel}
toggledOn={isSelected}
/>
) : (
<Btn onClick={onToggleUpdateValue}>
<Check color={COLORS.blue50} isChecked={isSelected} />
</Btn>
)}
</Flex>
</Flex>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing10}>
Expand Down
23 changes: 15 additions & 8 deletions protocol-designer/src/organisms/Alerts/FormAlerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface FormAlertsProps {
dirtyFields?: StepFieldName[]
}

function FormAlertsComponent(props: FormAlertsProps): JSX.Element {
function FormAlertsComponent(props: FormAlertsProps): JSX.Element | null {
const { focusedField, dirtyFields } = props
const { t } = useTranslation('alert')
const dispatch = useDispatch()
Expand Down Expand Up @@ -95,7 +95,7 @@ function FormAlertsComponent(props: FormAlertsProps): JSX.Element {
}

const makeAlert: MakeAlert = (alertType, data, key) => (
<Flex padding={`${SPACING.spacing16} ${SPACING.spacing16} 0`}>
<Flex>
<Banner
type={alertType === 'error' ? 'error' : 'warning'}
key={`${alertType}:${key}`}
Expand All @@ -104,14 +104,17 @@ function FormAlertsComponent(props: FormAlertsProps): JSX.Element {
? makeHandleCloseWarning(data.dismissId)
: undefined
}
width="100%"
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing4}>
<StyledText desktopStyle="bodyDefaultSemiBold">
{data.title}
</StyledText>
<StyledText desktopStyle="bodyDefaultRegular">
{data.description}
</StyledText>
{data.description != null ? (
<StyledText desktopStyle="bodyDefaultRegular">
{data.description}
</StyledText>
) : null}
</Flex>
</Banner>
</Flex>
Expand Down Expand Up @@ -151,15 +154,19 @@ function FormAlertsComponent(props: FormAlertsProps): JSX.Element {
)
}
}
return (
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
return [...formErrors, ...formWarnings, ...timelineWarnings].length > 0 ? (
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing4}
padding={`${SPACING.spacing16} ${SPACING.spacing16} 0`}
>
{formErrors.map((error, key) => makeAlert('error', error, key))}
{formWarnings.map((warning, key) => makeAlert('warning', warning, key))}
{timelineWarnings.map((warning, key) =>
makeAlert('warning', warning, key)
)}
</Flex>
)
) : null
}

export const FormAlerts = memo(FormAlertsComponent)
12 changes: 10 additions & 2 deletions protocol-designer/src/organisms/Alerts/TimelineAlerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import {
} from '@opentrons/components'
import { getRobotStateTimeline } from '../../file-data/selectors'
import { ErrorContents } from './ErrorContents'

import type { StyleProps } from '@opentrons/components'
import type { CommandCreatorError } from '@opentrons/step-generation'
import type { MakeAlert } from './types'

function TimelineAlertsComponent(): JSX.Element {
function TimelineAlertsComponent(props: StyleProps): JSX.Element | null {
const { t } = useTranslation('alert')

const timeline = useSelector(getRobotStateTimeline)
Expand All @@ -26,6 +28,10 @@ function TimelineAlertsComponent(): JSX.Element {
})
)

if (timelineErrors.length === 0) {
return null
}

const makeAlert: MakeAlert = (alertType, data, key) => (
<Banner
type={alertType === 'error' ? 'error' : 'warning'}
Expand All @@ -42,7 +48,9 @@ function TimelineAlertsComponent(): JSX.Element {
)

return (
<>{timelineErrors.map((error, key) => makeAlert('error', error, key))}</>
<Flex {...props}>
{timelineErrors.map((error, key) => makeAlert('error', error, key))}
</Flex>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element {
? 1
: 0
)
const [
showFormErrorsAndWarnings,
setShowFormErrorsAndWarnings,
] = useState<boolean>(false)
const [isRename, setIsRename] = useState<boolean>(false)
const icon = stepIconsByType[formData.stepType]

Expand Down Expand Up @@ -126,18 +130,22 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element {
const numErrors = timeline.errors?.length ?? 0

const handleSaveClick = (): void => {
handleSave()
makeSnackbar(
getSaveStepSnackbarText({
numWarnings,
numErrors,
stepTypeDisplayName: i18n.format(
t(`stepType.${formData.stepType}`),
'capitalize'
),
t,
}) as string
)
if (canSave) {
handleSave()
makeSnackbar(
getSaveStepSnackbarText({
numWarnings,
numErrors,
stepTypeDisplayName: i18n.format(
t(`stepType.${formData.stepType}`),
'capitalize'
),
t,
}) as string
)
} else {
setShowFormErrorsAndWarnings(true)
}
}

return (
Expand Down Expand Up @@ -195,9 +203,6 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element {
}
: handleSaveClick
}
disabled={
isMultiStepToolbox && toolboxStep === 0 ? false : !canSave
}
width="100%"
>
{isMultiStepToolbox && toolboxStep === 0
Expand All @@ -215,7 +220,9 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element {
</Flex>
}
>
<FormAlerts focusedField={focusedField} dirtyFields={dirtyFields} />
{showFormErrorsAndWarnings ? (
<FormAlerts focusedField={focusedField} dirtyFields={dirtyFields} />
) : null}
<ToolsComponent
{...{
formData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export function HeaterShakerTools(props: StepFormProps): JSX.Element {
fieldTitle={t('form:step_edit_form.field.heaterShaker.duration')}
isSelected={formData.heaterShakerSetTimer === true}
units={t('application:units.time')}
toggleElement="checkbox"
/>
</Flex>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,9 @@ export function ThermocyclerState(props: ThermocyclerStateProps): JSX.Element {
fieldTitle={i18n.format(t('stepType.temperature'), 'capitalize')}
units={t('units.degrees')}
isSelected={formData[lidFieldActive] === true}
onLabel={t(
'form:step_edit_form.field.thermocyclerState.lidPosition.toggleOn'
)}
onLabel={t('form:step_edit_form.field.thermocyclerState.lid.toggleOn')}
offLabel={t(
'form:step_edit_form.field.thermocyclerState.lidPosition.toggleOff'
'form:step_edit_form.field.thermocyclerState.lid.toggleOff'
)}
/>
<ToggleStepFormField
Expand Down
23 changes: 9 additions & 14 deletions protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import { BatchEditToolbox } from './BatchEditToolbox'
import { getDesignerTab } from '../../../file-data/selectors'
import { TimelineAlerts } from '../../../organisms'

const CONTENT_MAX_WIDTH = '46.9375rem'

export function ProtocolSteps(): JSX.Element {
const { i18n, t } = useTranslation('starting_deck_state')
const formData = useSelector(getUnsavedForm)
Expand Down Expand Up @@ -79,16 +81,13 @@ export function ProtocolSteps(): JSX.Element {
width="100%"
justifyContent={JUSTIFY_FLEX_START}
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing16}
maxWidth={CONTENT_MAX_WIDTH}
>
{tab === 'protocolSteps' ? (
<Flex
justifyContent={JUSTIFY_CENTER}
width="100%"
paddingTop="2.34375rem"
paddingBottom="1.34375rem"
>
<TimelineAlerts />
</Flex>
<TimelineAlerts justifyContent={JUSTIFY_CENTER} width="100%" />
) : null}
<Flex
justifyContent={
Expand All @@ -112,11 +111,7 @@ export function ProtocolSteps(): JSX.Element {
}}
/>
</Flex>
<Flex
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing16}
maxWidth="46.9375rem"
>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
{deckView === leftString ? (
<DeckSetupContainer tab="protocolSteps" />
) : (
Expand Down
6 changes: 3 additions & 3 deletions protocol-designer/src/steplist/fieldLevel/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,20 @@ export const minimumWellCount = (minimum: number): ErrorChecker => (
export const minFieldValue = (minimum: number): ErrorChecker => (
value: unknown
): string | null =>
value === null || Number(value) >= minimum
!value || Number(value) >= minimum
? null
: `${FIELD_ERRORS.UNDER_RANGE_MINIMUM} ${minimum}`
export const maxFieldValue = (maximum: number): ErrorChecker => (
value: unknown
): string | null =>
value === null || Number(value) <= maximum
!value || Number(value) <= maximum
? null
: `${FIELD_ERRORS.OVER_RANGE_MAXIMUM} ${maximum}`
export const temperatureRangeFieldValue = (
minimum: number,
maximum: number
): ErrorChecker => (value: unknown): string | null =>
value === null || (Number(value) <= maximum && Number(value) >= minimum)
!value || (Number(value) <= maximum && Number(value) >= minimum)
? null
: `${FIELD_ERRORS.OUTSIDE_OF_RANGE} ${minimum} and ${maximum} °C`
export const realNumber: ErrorChecker = (value: unknown) =>
Expand Down

0 comments on commit 545d0f7

Please sign in to comment.