diff --git a/api/tests/opentrons/conftest.py b/api/tests/opentrons/conftest.py index 059ef76cc9c..b1d3c7fd28c 100755 --- a/api/tests/opentrons/conftest.py +++ b/api/tests/opentrons/conftest.py @@ -795,6 +795,7 @@ def minimal_liquid_class_def1() -> LiquidClassSchemaV1: return LiquidClassSchemaV1( liquidClassName="water1", displayName="water 1", + description="some water", schemaVersion=1, namespace="test-fixture-1", byPipette=[], @@ -806,6 +807,7 @@ def minimal_liquid_class_def2() -> LiquidClassSchemaV1: return LiquidClassSchemaV1( liquidClassName="water2", displayName="water 2", + description="some water", schemaVersion=1, namespace="test-fixture-2", byPipette=[ @@ -879,6 +881,7 @@ def maximal_liquid_class_def() -> LiquidClassSchemaV1: return LiquidClassSchemaV1( liquidClassName="test_water", displayName="Test Water", + description="some water", schemaVersion=1, namespace="opentrons", byPipette=[ diff --git a/api/tests/opentrons/protocol_api_integration/test_liquid_classes.py b/api/tests/opentrons/protocol_api_integration/test_liquid_classes.py index cf7791a271a..b372ba6362a 100644 --- a/api/tests/opentrons/protocol_api_integration/test_liquid_classes.py +++ b/api/tests/opentrons/protocol_api_integration/test_liquid_classes.py @@ -20,7 +20,7 @@ def test_liquid_class_creation_and_property_fetching( water = simulated_protocol_context.define_liquid_class("water") assert water.name == "water" - assert water.display_name == "Water" + assert water.display_name == "Aqueous" # TODO (spp, 2024-10-17): update this to fetch pipette load name from instrument context assert ( diff --git a/protocol-designer/src/assets/localization/en/liquids.json b/protocol-designer/src/assets/localization/en/liquids.json index 0ce974ee765..6c9f50a07dd 100644 --- a/protocol-designer/src/assets/localization/en/liquids.json +++ b/protocol-designer/src/assets/localization/en/liquids.json @@ -9,6 +9,7 @@ "delete_liquid": "Delete liquid", "description": "Description", "display_color": "Color", + "dont_use_liquid_class": "Don't use a liquid class", "liquid_required": "Liquid required", "liquid_volume": "Liquid volume by well", "liquid_volume_nonzero": "Liquid volume must be larger than zero", @@ -18,6 +19,7 @@ "title": "Liquid class", "tooltip": "Applies predefined pipetting settings to transfer and mix steps using this liquid" }, + "liquid_class_name_description": "{{displayName}} ({{description}})", "liquids_added": "Liquids added", "liquids": "Liquids", "microliters": "µL", diff --git a/protocol-designer/src/assets/localization/en/protocol_steps.json b/protocol-designer/src/assets/localization/en/protocol_steps.json index a2eec080f47..efb671b06ea 100644 --- a/protocol-designer/src/assets/localization/en/protocol_steps.json +++ b/protocol-designer/src/assets/localization/en/protocol_steps.json @@ -67,7 +67,10 @@ "distribute": "Distributingfrom{{sourceWells}} of {{source}}to{{destinationWells}} of {{destination}}", "transfer": "Transferringfrom{{sourceWells}} of {{source}}to{{destinationWells}} of {{destination}}", "consolidate_disposal": "Consolidatingfrom{{sourceWells}} of {{source}}to{{destination}}", - "transfer_disposal": "Transferringfrom{{sourceWells}} of {{source}}to{{destination}}" + "transfer_disposal": "Transferringfrom{{sourceWells}} of {{source}}to{{destination}}", + "substeps": { + "multi": "Multi" + } }, "multi_dispense_options": "Disposal volume", "multiAspirate": "Consolidate path", diff --git a/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx b/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx index 75ac02ca873..0d659a89da1 100644 --- a/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx +++ b/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx @@ -135,12 +135,25 @@ export function DefineLiquidsModal( } const liquidClassOptions = [ - { name: 'Choose an option', value: '' }, - ...Object.entries(liquidClassDefs).map( - ([liquidClassDefName, { displayName }]) => { - return { name: displayName, value: liquidClassDefName } - } - ), + { name: t('liquids:dont_use_liquid_class'), value: '' }, + ...Object.entries(liquidClassDefs) + .sort(([_0, a], [_1, b]) => { + if (a.displayName < b.displayName) { + return -1 + } else if (a.displayName > b.displayName) { + return 1 + } + return 0 + }) + .map(([liquidClassDefName, { displayName, description }]) => { + return { + value: liquidClassDefName, + name: t('liquids:liquid_class_name_description', { + displayName, + description, + }), + } + }), ] return ( diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PipetteField.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PipetteField.tsx index 1f9053b0478..d52d8260e06 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PipetteField.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PipetteField.tsx @@ -14,6 +14,7 @@ export const PipetteField = (props: FieldProps): JSX.Element => { options={pipetteOptions} value={value ? String(value) : null} title={t('pipette')} + width="100%" /> ) } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/MultichannelSubstep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/MultichannelSubstep.tsx index a57d340445d..b4a7328a539 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/MultichannelSubstep.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/MultichannelSubstep.tsx @@ -1,15 +1,16 @@ import { useState } from 'react' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' import { ALIGN_CENTER, DIRECTION_COLUMN, DeckInfoLabel, Flex, - JUSTIFY_SPACE_BETWEEN, ListButton, + NO_WRAP, SPACING, StyledText, Tag, + WRAP, } from '@opentrons/components' import { Substep } from './Substep' import { formatVolume } from './utils' @@ -18,6 +19,7 @@ import type { StepItemSourceDestRow, SubstepIdentifier, } from '../../../../steplist' +import { values } from 'lodash' interface MultichannelSubstepProps { trashName: AdditionalEquipmentName | null @@ -57,6 +59,43 @@ export function MultichannelSubstep( firstChannelDest ? firstChannelDest.well ?? 'Trash' : '' }:${lastChannelDest ? lastChannelDest.well : ''}` + interface MultiChannelSubstepButtonProps { + tagText: string + sources: JSX.Element | null + destinations: JSX.Element | null + } + + function MultiChannelSubstepButton( + props: MultiChannelSubstepButtonProps + ): JSX.Element { + const { tagText, sources, destinations } = props + const { t } = useTranslation('protocol_steps') + return ( + + + ), + tag: , + label1: sources ?? <>, + label2: destinations ?? <>, + }} + values={values} + /> + + ) + } + return ( {/* TODO: need to update this to match designs! */} - + - - Multi - {firstChannelSource != null ? ( - - ) : null} - - {firstChannelDest != null ? ( - - ) : null} - - - {!collapsed && - rowGroup.map((row, rowKey) => { + {/* replace below Flex with custom component */} + + ) : null + } + destinations={ + firstChannelDest != null ? ( + + ) : null + } + /> + {!collapsed ? ( + + {rowGroup.map((row, rowKey) => { return ( ) })} - + + ) : null} diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx index a7e272c3623..5bdda7791ec 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx @@ -1,7 +1,6 @@ import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { - FLEX_MAX_CONTENT, Flex, Icon, PrimaryButton, @@ -59,7 +58,7 @@ export function SubStepsToolbox( substeps.substepType === THERMOCYCLER_PROFILE ? ( } onCloseClick={handleClose} confirmButton={ diff --git a/shared-data/js/liquidClasses.ts b/shared-data/js/liquidClasses.ts index d97db8afa47..ac598312e42 100644 --- a/shared-data/js/liquidClasses.ts +++ b/shared-data/js/liquidClasses.ts @@ -1,8 +1,12 @@ +import ethanol80V1Uncasted from '../liquid-class/definitions/1/ethanol_80.json' +import glycerol50V1Uncasted from '../liquid-class/definitions/1/glycerol_50.json' import waterV1Uncasted from '../liquid-class/definitions/1/water.json' import type { LiquidClass } from '.' +const ethanol80V1 = ethanol80V1Uncasted as LiquidClass +const glycerol50V1 = glycerol50V1Uncasted as LiquidClass const waterV1 = waterV1Uncasted as LiquidClass -const defs = { waterV1 } +const defs = { ethanol80V1, glycerol50V1, waterV1 } export const getAllLiquidClassDefs = (): Record => defs diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index 3a4152dc442..9e2ad86299f 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -798,6 +798,7 @@ interface ByPipetteSetting { export interface LiquidClass { liquidClassName: string displayName: string + description: string schemaVersion: number namespace: string byPipette: ByPipetteSetting[] diff --git a/shared-data/liquid-class/definitions/1/ethanol_80.json b/shared-data/liquid-class/definitions/1/ethanol_80.json index 3b9870c59b1..b5b978bb637 100644 --- a/shared-data/liquid-class/definitions/1/ethanol_80.json +++ b/shared-data/liquid-class/definitions/1/ethanol_80.json @@ -1,6 +1,7 @@ { "liquidClassName": "ethanol_80", - "displayName": "Ethanol-80", + "displayName": "Volatile", + "description": "80% ethanol", "schemaVersion": 1, "namespace": "opentrons", "byPipette": [ diff --git a/shared-data/liquid-class/definitions/1/glycerol_50.json b/shared-data/liquid-class/definitions/1/glycerol_50.json index 52cf57ff184..a61d14952b7 100644 --- a/shared-data/liquid-class/definitions/1/glycerol_50.json +++ b/shared-data/liquid-class/definitions/1/glycerol_50.json @@ -1,6 +1,7 @@ { "liquidClassName": "glycerol_50", - "displayName": "Glycerol-50", + "displayName": "Viscous", + "description": "50% glycerol", "schemaVersion": 1, "namespace": "opentrons", "byPipette": [ diff --git a/shared-data/liquid-class/definitions/1/water.json b/shared-data/liquid-class/definitions/1/water.json index d7bcd1afdc3..aeea73e7b6f 100644 --- a/shared-data/liquid-class/definitions/1/water.json +++ b/shared-data/liquid-class/definitions/1/water.json @@ -1,6 +1,7 @@ { "liquidClassName": "water", - "displayName": "Water", + "displayName": "Aqueous", + "description": "Deionized water", "schemaVersion": 1, "namespace": "opentrons", "byPipette": [ diff --git a/shared-data/liquid-class/fixtures/1/fixture_glycerol50.json b/shared-data/liquid-class/fixtures/1/fixture_glycerol50.json index 9c24a40452d..1d8b9cef49f 100644 --- a/shared-data/liquid-class/fixtures/1/fixture_glycerol50.json +++ b/shared-data/liquid-class/fixtures/1/fixture_glycerol50.json @@ -1,6 +1,7 @@ { "liquidClassName": "fixture_glycerol50", - "displayName": "Glycerol 50%", + "displayName": "Viscous", + "description": "50% glycerol", "schemaVersion": 1, "namespace": "opentrons", "byPipette": [ diff --git a/shared-data/liquid-class/schemas/1.json b/shared-data/liquid-class/schemas/1.json index 633be549d4c..c965c10562d 100644 --- a/shared-data/liquid-class/schemas/1.json +++ b/shared-data/liquid-class/schemas/1.json @@ -448,6 +448,10 @@ "type": "string", "description": "User-readable name of the liquid class." }, + "description": { + "type": "string", + "description": "User-readable description of the liquid class" + }, "schemaVersion": { "description": "Which schema version a liquid class is using", "type": "number", @@ -500,6 +504,7 @@ "required": [ "liquidClassName", "displayName", + "description", "schemaVersion", "namespace", "byPipette" diff --git a/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py b/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py index aca371d43e6..31fddaad2a1 100644 --- a/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py +++ b/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py @@ -380,6 +380,9 @@ class LiquidClassSchemaV1(BaseModel): ..., description="The name of the liquid (e.g., water, ethanol, serum)." ) displayName: str = Field(..., description="User-readable name of the liquid class.") + description: str = Field( + ..., description="User-readable description of the liquid class" + ) schemaVersion: Literal[1] = Field( ..., description="Which schema version a liquid class is using" )