diff --git a/protocol-designer/src/assets/localization/en/form.json b/protocol-designer/src/assets/localization/en/form.json index 98a1e328809..f6dcb7d5e37 100644 --- a/protocol-designer/src/assets/localization/en/form.json +++ b/protocol-designer/src/assets/localization/en/form.json @@ -23,7 +23,11 @@ "liquid_placement": { "liquid": "Liquid", "volume": "Volume", - "volume_exceeded": "Warning: exceeded max volume per well: {{volume}}µL" + "errors": { + "liquid_required": "Define a liquid", + "volume_exceeded": "Warning: exceeded max volume per well: {{volume}}µL", + "volume_required": "Define a volume" + } }, "pipette_mount_label": { "left": "(L)", diff --git a/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx b/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx index 4f33b968db4..215e1575060 100644 --- a/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx +++ b/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx @@ -12,7 +12,7 @@ import { Flex, Icon, LiquidIcon, - ListItem, + ListButton, SPACING, StyledText, TEXT_DECORATION_UNDERLINE, @@ -98,7 +98,15 @@ export function LiquidCard(props: LiquidCardProps): JSX.Element { } return ( - + { + setIsExpanded(prev => !prev) + }} + padding="0" + > - { - setIsExpanded(prev => !prev) - }} - > + {isExpanded ? ( - - - - {t('well')} - - - - {t('microliters')} + <> + + + + + {t('well')} + + + {t('microliters')} + + - - - {info.liquidIndex != null - ? fullWellsByLiquid[info.liquidIndex] - .sort((a, b) => - orderedWells.indexOf(b) > orderedWells.indexOf(a) ? -1 : 1 - ) - .map((wellName, wellliquidIndex) => { - const volume = - wellContents != null - ? wellContents[wellName].ingreds[liquidIndex].volume - : 0 - return ( - <> - - {wellliquidIndex < - fullWellsByLiquid[liquidIndex].length - 1 ? ( - - ) : null} - + + {info.liquidIndex != null + ? fullWellsByLiquid[info.liquidIndex] + .sort((a, b) => + orderedWells.indexOf(b) > orderedWells.indexOf(a) ? -1 : 1 ) - }) - : null} - + .map((wellName, wellliquidIndex) => { + const volume = + wellContents != null + ? wellContents[wellName].ingreds[liquidIndex].volume + : 0 + return ( + <> + + {wellliquidIndex < + fullWellsByLiquid[liquidIndex].length - 1 ? ( + + ) : null} + + ) + }) + : null} + + ) : null} - + ) } diff --git a/protocol-designer/src/organisms/AssignLiquidsModal/LiquidToolbox.tsx b/protocol-designer/src/organisms/AssignLiquidsModal/LiquidToolbox.tsx index 269d78d5c05..5ec1618e72c 100644 --- a/protocol-designer/src/organisms/AssignLiquidsModal/LiquidToolbox.tsx +++ b/protocol-designer/src/organisms/AssignLiquidsModal/LiquidToolbox.tsx @@ -106,11 +106,12 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { control, setValue, reset, - formState: { touchedFields }, + formState, } = useForm({ defaultValues: getInitialValues(), }) + const { errors } = formState const selectedLiquidId = watch('selectedLiquidId') const volume = watch('volume') @@ -184,15 +185,22 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { reset() } - let volumeErrors: string | null = null - if (Boolean(touchedFields.volume)) { - if (volume == null || volume === '0') { - volumeErrors = t('generic.error.more_than_zero') - } else if (parseInt(volume) > selectedWellsMaxVolume) { - volumeErrors = t('form:liquid_placement.volume_exceeded', { + const validateVolume = ( + volume: string | null | undefined + ): string | undefined => { + if (volume == null || volume === '') { + return t('form:liquid_placement.errors.volume_required') + } + const volumeNumber = parseFloat(volume) + if (volumeNumber === 0) { + return t('form:generic.error.more_than_zero') + } + if (volumeNumber > selectedWellsMaxVolume) { + return t('form:liquid_placement.errors.volume_exceeded', { volume: selectedWellsMaxVolume, }) } + return undefined } let wellContents: ContentsByWell | null = null @@ -301,7 +309,12 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { name="selectedLiquidId" control={control} rules={{ - required: true, + required: { + value: true, + message: t( + 'form:liquid_placement.errors.liquid_required' + ), + }, }} render={({ field }) => { const fullOptions: DropdownOption[] = liquidSelectionOptions.map( @@ -337,6 +350,7 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { }} onClick={field.onChange} menuPlacement="bottom" + error={errors.selectedLiquidId?.message} /> ) }} @@ -354,14 +368,14 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { name="volume" control={control} rules={{ - required: true, + validate: validateVolume, }} render={({ field }) => ( @@ -377,18 +391,7 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { {t('shared:cancel')} - - {t('save')} - + {t('save')}