= {
temperature: 'ot-temperature-v2',
thermocycler: 'ot-thermocycler',
heaterShaker: 'ot-heater-shaker',
+ plateReader: 'ot-absorbance',
}
// ===== Unprocessed form types =====
export interface AnnotationFields {
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx
index d8ec3458985..307e9e4aeac 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx
@@ -45,6 +45,7 @@ import {
MoveLabwareTools,
MoveLiquidTools,
PauseTools,
+ PlateReaderTools,
TemperatureTools,
ThermocyclerTools,
} from './StepTools'
@@ -84,6 +85,7 @@ const STEP_FORM_MAP: StepFormMap = {
thermocycler: ThermocyclerTools,
heaterShaker: HeaterShakerTools,
comment: CommentTools,
+ plateReader: PlateReaderTools,
}
interface StepFormToolboxProps {
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PlateReaderTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PlateReaderTools/index.tsx
new file mode 100644
index 00000000000..e2c0478bf9e
--- /dev/null
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PlateReaderTools/index.tsx
@@ -0,0 +1,5 @@
+import type { StepFormProps } from '../../types'
+
+export function PlateReaderTools(props: StepFormProps): JSX.Element {
+ return TODO: ADD PLATE READER TOOLS
+}
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts
index 7f0eff60340..f62b0127c36 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts
@@ -4,6 +4,7 @@ export { MagnetTools } from './MagnetTools'
export { MixTools } from './MixTools'
export { MoveLabwareTools } from './MoveLabwareTools'
export { MoveLiquidTools } from './MoveLiquidTools'
+export { PlateReaderTools } from './PlateReaderTools'
export { PauseTools } from './PauseTools'
export { TemperatureTools } from './TemperatureTools'
export { ThermocyclerTools } from './ThermocyclerTools'
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx
index 7ce06184378..f9c2aa9d395 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx
@@ -17,6 +17,7 @@ import {
SecondaryButton,
} from '@opentrons/components'
import {
+ ABSORBANCE_READER_TYPE,
HEATERSHAKER_MODULE_TYPE,
MAGNETIC_MODULE_TYPE,
TEMPERATURE_MODULE_TYPE,
@@ -35,7 +36,10 @@ import {
ConfirmDeleteModal,
getMainPagePortalEl,
} from '../../../../organisms'
-import { getEnableComment } from '../../../../feature-flags/selectors'
+import {
+ getEnableAbsorbanceReader,
+ getEnableComment,
+} from '../../../../feature-flags/selectors'
import { AddStepOverflowButton } from './AddStepOverflowButton'
@@ -71,32 +75,22 @@ export function AddStepButton(): JSX.Element {
const [enqueuedStepType, setEnqueuedStepType] = useState(
null
)
+ const enableAbsorbanceReader = useSelector(getEnableAbsorbanceReader)
const getSupportedSteps = (): Array<
Exclude
- > =>
- enableComment
- ? [
- 'comment',
- 'moveLabware',
- 'moveLiquid',
- 'mix',
- 'pause',
- 'heaterShaker',
- 'magnet',
- 'temperature',
- 'thermocycler',
- ]
- : [
- 'moveLabware',
- 'moveLiquid',
- 'mix',
- 'pause',
- 'heaterShaker',
- 'magnet',
- 'temperature',
- 'thermocycler',
- ]
+ > => [
+ 'comment',
+ 'moveLabware',
+ 'moveLiquid',
+ 'mix',
+ 'pause',
+ 'heaterShaker',
+ 'magnet',
+ 'temperature',
+ 'thermocycler',
+ 'plateReader',
+ ]
const isStepTypeEnabled: Record<
Exclude,
boolean
@@ -110,6 +104,9 @@ export function AddStepButton(): JSX.Element {
temperature: getIsModuleOnDeck(modules, TEMPERATURE_MODULE_TYPE),
thermocycler: getIsModuleOnDeck(modules, THERMOCYCLER_MODULE_TYPE),
heaterShaker: getIsModuleOnDeck(modules, HEATERSHAKER_MODULE_TYPE),
+ plateReader:
+ getIsModuleOnDeck(modules, ABSORBANCE_READER_TYPE) &&
+ enableAbsorbanceReader,
}
const addStep = (stepType: StepType): ReturnType =>
diff --git a/protocol-designer/src/step-forms/selectors/index.ts b/protocol-designer/src/step-forms/selectors/index.ts
index 2fa3a69f122..2fe25dc790f 100644
--- a/protocol-designer/src/step-forms/selectors/index.ts
+++ b/protocol-designer/src/step-forms/selectors/index.ts
@@ -209,6 +209,7 @@ const MAGNETIC_BLOCK_INITIAL_STATE: MagneticBlockState = {
}
const ABSORBANCE_READER_INITIAL_STATE: AbsorbanceReaderState = {
type: ABSORBANCE_READER_TYPE,
+ lidOpen: null,
}
const _getInitialDeckSetup = (
diff --git a/protocol-designer/src/step-forms/types.ts b/protocol-designer/src/step-forms/types.ts
index b170a128843..cec0a6ee22c 100644
--- a/protocol-designer/src/step-forms/types.ts
+++ b/protocol-designer/src/step-forms/types.ts
@@ -64,6 +64,7 @@ export interface MagneticBlockState {
}
export interface AbsorbanceReaderState {
type: typeof ABSORBANCE_READER_TYPE
+ lidOpen: boolean | null
}
export interface ModuleTemporalProperties {
slot: DeckSlot
diff --git a/step-generation/src/constants.ts b/step-generation/src/constants.ts
index 30c44782e8e..cc215de8c99 100644
--- a/step-generation/src/constants.ts
+++ b/step-generation/src/constants.ts
@@ -58,6 +58,7 @@ export const HEATERSHAKER_MODULE_INITIAL_STATE: HeaterShakerModuleState = {
const ABSORBANCE_READER_INITIAL_STATE: AbsorbanceReaderState = {
type: 'absorbanceReaderType',
+ lidOpen: null,
}
const MAGNETIC_BLOCK_INITIAL_STATE: MagneticBlockState = {
type: 'magneticBlockType',
diff --git a/step-generation/src/types.ts b/step-generation/src/types.ts
index 021b6cfa515..c1716fa62c3 100644
--- a/step-generation/src/types.ts
+++ b/step-generation/src/types.ts
@@ -77,6 +77,7 @@ export interface MagneticBlockState {
export interface AbsorbanceReaderState {
type: typeof ABSORBANCE_READER_TYPE
+ lidOpen: boolean | null
}
export type ModuleState =
From 3db0e4fff2d00dbd2630427f76e4feafe1953347 Mon Sep 17 00:00:00 2001
From: Ryan Howard
Date: Fri, 20 Dec 2024 10:53:16 -0500
Subject: [PATCH 05/18] feat(shared-data): Add support for PEEK pipettes
(#17036) (#17160)
This Adds support for the new oem pipette by doing the following:
- Add support for the flashing and factory testing pipettes with the new
serial number prefix P1KP
- Add the shared data definitions with updated max flowrates
- Add ability to change the max speed when the pipette definition has
the new "highSpeed" quirk
- Disable support for the pressure sensor
- Don't monitor for over pressure
- Throw errors if trying to enable liquid presence detection on a
pipette
- Throw errors if trying to explicitly use LLD
- Support UI differences for the new pipette name
---------
# Overview
Mergeback from some of the things I fixed on the chore_release-8.3
branch for the OEM pipettes.
## Test Plan and Hands on Testing
## Changelog
## Review requests
## Risk assessment
---------
Co-authored-by: Caila Marashaj <98041399+caila-marashaj@users.noreply.github.com>
Co-authored-by: Jethary
---
.../backends/ot3controller.py | 1 +
.../hardware_control/backends/ot3simulator.py | 3 +
.../instruments/ot2/pipette.py | 7 +-
.../instruments/ot3/pipette.py | 6 +
api/src/opentrons/protocol_api/validation.py | 1 +
.../resources/pipette_data_provider.py | 5 +
.../instruments/test_nozzle_manager.py | 46 ++-
.../hardware_control/test_ot3_api.py | 155 ++++++++-
.../hardware_control/test_pipette.py | 5 +-
api/tests/opentrons/test_execute.py | 6 +
.../service/legacy/routers/test_settings.py | 1 +
shared-data/command/schemas/10.json | 1 +
shared-data/command/schemas/11.json | 2 +-
shared-data/js/pipettes.ts | 18 +-
.../definitions/1/pipetteModelSpecs.json | 195 +++++++++++
.../definitions/1/pipetteNameSpecs.json | 48 +++
.../2/general/eight_channel_em/p1000/1_0.json | 320 ++++++++++++++++++
.../geometry/eight_channel_em/p1000/1_0.json | 55 +++
.../eight_channel_em/p1000/default/1_0.json | 236 +++++++++++++
.../pipette/load_data.py | 29 +-
.../pipette/model_constants.py | 1 -
.../pipette/mutable_configurations.py | 3 +
.../pipette/pipette_definition.py | 16 +-
.../pipette/pipette_load_name_conversions.py | 19 +-
.../opentrons_shared_data/pipette/types.py | 22 +-
.../python/tests/pipette/test_load_data.py | 8 +-
.../pipette/test_max_flow_rates_per_volume.py | 1 +
.../pipette/test_mutable_configurations.py | 6 +-
.../test_pipette_load_name_conversions.py | 28 +-
.../tests/pipette/test_validate_schema.py | 6 +
30 files changed, 1185 insertions(+), 65 deletions(-)
create mode 100644 shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json
create mode 100644 shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json
create mode 100644 shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json
diff --git a/api/src/opentrons/hardware_control/backends/ot3controller.py b/api/src/opentrons/hardware_control/backends/ot3controller.py
index cdbd8818111..84ffbffd8da 100644
--- a/api/src/opentrons/hardware_control/backends/ot3controller.py
+++ b/api/src/opentrons/hardware_control/backends/ot3controller.py
@@ -1072,6 +1072,7 @@ def _build_attached_pip(
converted_name.pipette_type,
converted_name.pipette_channels,
converted_name.pipette_version,
+ converted_name.oem_type,
),
"id": OT3Controller._combine_serial_number(attached),
}
diff --git a/api/src/opentrons/hardware_control/backends/ot3simulator.py b/api/src/opentrons/hardware_control/backends/ot3simulator.py
index 10f4ebcfe2a..b7466386be6 100644
--- a/api/src/opentrons/hardware_control/backends/ot3simulator.py
+++ b/api/src/opentrons/hardware_control/backends/ot3simulator.py
@@ -511,6 +511,7 @@ def _attached_pipette_to_mount(
converted_name.pipette_type,
converted_name.pipette_channels,
converted_name.pipette_version,
+ converted_name.oem_type,
),
"id": None,
}
@@ -533,6 +534,7 @@ def _attached_pipette_to_mount(
converted_name.pipette_type,
converted_name.pipette_channels,
converted_name.pipette_version,
+ converted_name.oem_type,
),
"id": init_instr["id"],
}
@@ -544,6 +546,7 @@ def _attached_pipette_to_mount(
converted_name.pipette_type,
converted_name.pipette_channels,
converted_name.pipette_version,
+ converted_name.oem_type,
),
"id": None,
}
diff --git a/api/src/opentrons/hardware_control/instruments/ot2/pipette.py b/api/src/opentrons/hardware_control/instruments/ot2/pipette.py
index 0881999d435..2205da161f1 100644
--- a/api/src/opentrons/hardware_control/instruments/ot2/pipette.py
+++ b/api/src/opentrons/hardware_control/instruments/ot2/pipette.py
@@ -56,6 +56,7 @@
UlPerMmAction,
PipetteName,
PipetteModel,
+ PipetteOEMType,
)
from opentrons.hardware_control.dev_types import InstrumentHardwareConfigs
@@ -112,17 +113,20 @@ def __init__(
pipette_type=config.pipette_type,
pipette_channels=config.channels,
pipette_generation=config.display_category,
+ oem_type=PipetteOEMType.OT,
)
self._acting_as = self._pipette_name
self._pipette_model = PipetteModelVersionType(
pipette_type=config.pipette_type,
pipette_channels=config.channels,
pipette_version=config.version,
+ oem_type=PipetteOEMType.OT,
)
self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
self._pipette_model.pipette_type,
self._pipette_model.pipette_channels,
self._pipette_model.pipette_version,
+ PipetteOEMType.OT,
)
self._nozzle_offset = self._config.nozzle_offset
self._nozzle_manager = (
@@ -189,7 +193,7 @@ def act_as(self, name: PipetteNameType) -> None:
], f"{self.name} is not back-compatible with {name}"
liquid_model = load_pipette_data.load_liquid_model(
- name.pipette_type, name.pipette_channels, name.get_version()
+ name.pipette_type, name.pipette_channels, name.get_version(), name.oem_type
)
# TODO need to grab name config here to deal with act as test
self._liquid_class.max_volume = liquid_model["default"].max_volume
@@ -280,6 +284,7 @@ def reload_configurations(self) -> None:
self._pipette_model.pipette_type,
self._pipette_model.pipette_channels,
self._pipette_model.pipette_version,
+ self._pipette_model.oem_type,
)
self._config_as_dict = self._config.model_dump()
diff --git a/api/src/opentrons/hardware_control/instruments/ot3/pipette.py b/api/src/opentrons/hardware_control/instruments/ot3/pipette.py
index 6098b88b964..bd7671d745d 100644
--- a/api/src/opentrons/hardware_control/instruments/ot3/pipette.py
+++ b/api/src/opentrons/hardware_control/instruments/ot3/pipette.py
@@ -42,6 +42,7 @@
PipetteName,
PipetteModel,
Quirks,
+ PipetteOEMType,
)
from opentrons_shared_data.pipette import (
load_data as load_pipette_data,
@@ -93,22 +94,26 @@ def __init__(
self._liquid_class_name = pip_types.LiquidClasses.default
self._liquid_class = self._config.liquid_properties[self._liquid_class_name]
+ oem = PipetteOEMType.get_oem_from_quirks(config.quirks)
# TODO (lc 12-05-2022) figure out how we can safely deprecate "name" and "model"
self._pipette_name = PipetteNameType(
pipette_type=config.pipette_type,
pipette_channels=config.channels,
pipette_generation=config.display_category,
+ oem_type=oem,
)
self._acting_as = self._pipette_name
self._pipette_model = PipetteModelVersionType(
pipette_type=config.pipette_type,
pipette_channels=config.channels,
pipette_version=config.version,
+ oem_type=oem,
)
self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
self._pipette_model.pipette_type,
self._pipette_model.pipette_channels,
self._pipette_model.pipette_version,
+ self._pipette_model.oem_type,
)
self._nozzle_offset = self._config.nozzle_offset
self._nozzle_manager = (
@@ -250,6 +255,7 @@ def reload_configurations(self) -> None:
self._pipette_model.pipette_type,
self._pipette_model.pipette_channels,
self._pipette_model.pipette_version,
+ self._pipette_model.oem_type,
)
self._config_as_dict = self._config.model_dump()
diff --git a/api/src/opentrons/protocol_api/validation.py b/api/src/opentrons/protocol_api/validation.py
index f0db8a71e5e..cd1e5112718 100644
--- a/api/src/opentrons/protocol_api/validation.py
+++ b/api/src/opentrons/protocol_api/validation.py
@@ -72,6 +72,7 @@
"flex_8channel_50": PipetteNameType.P50_MULTI_FLEX,
"flex_1channel_1000": PipetteNameType.P1000_SINGLE_FLEX,
"flex_8channel_1000": PipetteNameType.P1000_MULTI_FLEX,
+ "flex_8channel_1000_em": PipetteNameType.P1000_MULTI_EM,
"flex_96channel_1000": PipetteNameType.P1000_96,
"flex_96channel_200": PipetteNameType.P200_96,
}
diff --git a/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py b/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py
index 4df6b0d4d77..ee721c88f2c 100644
--- a/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py
+++ b/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py
@@ -98,6 +98,7 @@ def configure_virtual_pipette_nozzle_layout(
config.pipette_type,
config.channels,
config.version,
+ pip_types.PipetteOEMType.OT,
)
new_nozzle_manager = NozzleConfigurationManager.build_from_config(
config, valid_nozzle_maps
@@ -130,6 +131,7 @@ def configure_virtual_pipette_for_volume(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
liquid_class = pipette_definition.liquid_class_for_volume_between_default_and_defaultlowvolume(
@@ -163,6 +165,7 @@ def _get_virtual_pipette_full_config_by_model_string(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
def _get_virtual_pipette_static_config_by_model( # noqa: C901
@@ -179,6 +182,7 @@ def _get_virtual_pipette_static_config_by_model( # noqa: C901
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
try:
tip_type = pip_types.PipetteTipType(
@@ -195,6 +199,7 @@ def _get_virtual_pipette_static_config_by_model( # noqa: C901
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
if pipette_id not in self._nozzle_manager_layout_by_id:
nozzle_manager = NozzleConfigurationManager.build_from_config(
diff --git a/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py b/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py
index ba1f10aaaef..066cdbc3caf 100644
--- a/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py
+++ b/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py
@@ -10,6 +10,7 @@
PipetteModelType,
PipetteChannelType,
PipetteVersionType,
+ PipetteOEMType,
)
from opentrons_shared_data.pipette.pipette_definition import (
PipetteConfigurations,
@@ -261,7 +262,10 @@ def test_single_pipettes_always_full(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.SINGLE_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.SINGLE_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config, ValidNozzleMaps(maps=A1)
@@ -289,7 +293,10 @@ def test_single_pipette_map_entries(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.SINGLE_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.SINGLE_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config, ValidNozzleMaps(maps=A1)
@@ -326,7 +333,10 @@ def test_single_pipette_map_geometry(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.SINGLE_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.SINGLE_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config, ValidNozzleMaps(maps=A1)
@@ -359,7 +369,10 @@ def test_multi_config_identification(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.EIGHT_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.EIGHT_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config,
@@ -419,7 +432,10 @@ def test_multi_config_map_entries(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.EIGHT_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.EIGHT_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config,
@@ -485,7 +501,10 @@ def test_multi_config_geometry(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.EIGHT_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.EIGHT_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config,
@@ -536,7 +555,10 @@ def test_96_config_identification(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.NINETY_SIX_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.NINETY_SIX_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config,
@@ -651,7 +673,10 @@ def test_96_config_map_entries(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.NINETY_SIX_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.NINETY_SIX_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config,
@@ -988,7 +1013,10 @@ def test_96_config_geometry(
pipette_details: Tuple[PipetteModelType, PipetteVersionType],
) -> None:
config = load_definition(
- pipette_details[0], PipetteChannelType.NINETY_SIX_CHANNEL, pipette_details[1]
+ pipette_details[0],
+ PipetteChannelType.NINETY_SIX_CHANNEL,
+ pipette_details[1],
+ PipetteOEMType.OT,
)
subject = nozzle_manager.NozzleConfigurationManager.build_from_config(
config,
diff --git a/api/tests/opentrons/hardware_control/test_ot3_api.py b/api/tests/opentrons/hardware_control/test_ot3_api.py
index a0775d48e19..200549108db 100644
--- a/api/tests/opentrons/hardware_control/test_ot3_api.py
+++ b/api/tests/opentrons/hardware_control/test_ot3_api.py
@@ -82,6 +82,7 @@
PipetteChannelType,
PipetteVersionType,
LiquidClasses,
+ PipetteOEMType,
)
from opentrons_shared_data.pipette import (
load_data as load_pipette_data,
@@ -381,6 +382,7 @@ class PipetteLoadConfig(TypedDict):
channels: Literal[1, 8, 96]
version: Tuple[Literal[1, 2, 3], Literal[0, 1, 2, 3, 4, 5, 6]]
model: PipetteModel
+ oem_type: PipetteOEMType
class GripperLoadConfig(TypedDict):
@@ -402,8 +404,24 @@ class GripperLoadConfig(TypedDict):
(
(
[
- (OT3Mount.RIGHT, {"channels": 8, "version": (3, 3), "model": "p50"}),
- (OT3Mount.LEFT, {"channels": 1, "version": (3, 3), "model": "p1000"}),
+ (
+ OT3Mount.RIGHT,
+ {
+ "channels": 8,
+ "version": (3, 3),
+ "model": "p50",
+ "oem_type": PipetteOEMType.OT,
+ },
+ ),
+ (
+ OT3Mount.LEFT,
+ {
+ "channels": 1,
+ "version": (3, 3),
+ "model": "p1000",
+ "oem_type": PipetteOEMType.OT,
+ },
+ ),
],
GantryLoad.LOW_THROUGHPUT,
),
@@ -413,34 +431,88 @@ class GripperLoadConfig(TypedDict):
GantryLoad.LOW_THROUGHPUT,
),
(
- [(OT3Mount.LEFT, {"channels": 8, "version": (3, 3), "model": "p1000"})],
+ [
+ (
+ OT3Mount.LEFT,
+ {
+ "channels": 8,
+ "version": (3, 3),
+ "model": "p1000",
+ "oem_type": "ot",
+ },
+ )
+ ],
GantryLoad.LOW_THROUGHPUT,
),
(
- [(OT3Mount.RIGHT, {"channels": 8, "version": (3, 3), "model": "p1000"})],
+ [
+ (
+ OT3Mount.RIGHT,
+ {
+ "channels": 8,
+ "version": (3, 3),
+ "model": "p1000",
+ "oem_type": "ot",
+ },
+ )
+ ],
GantryLoad.LOW_THROUGHPUT,
),
(
- [(OT3Mount.LEFT, {"channels": 96, "model": "p1000", "version": (3, 3)})],
+ [
+ (
+ OT3Mount.LEFT,
+ {
+ "channels": 96,
+ "model": "p1000",
+ "version": (3, 3),
+ "oem_type": "ot",
+ },
+ )
+ ],
GantryLoad.HIGH_THROUGHPUT,
),
(
[
- (OT3Mount.LEFT, {"channels": 1, "version": (3, 3), "model": "p1000"}),
+ (
+ OT3Mount.LEFT,
+ {
+ "channels": 1,
+ "version": (3, 3),
+ "model": "p1000",
+ "oem_type": "ot",
+ },
+ ),
(OT3Mount.GRIPPER, {"model": GripperModel.v1, "id": "g12345"}),
],
GantryLoad.LOW_THROUGHPUT,
),
(
[
- (OT3Mount.RIGHT, {"channels": 8, "version": (3, 3), "model": "p1000"}),
+ (
+ OT3Mount.RIGHT,
+ {
+ "channels": 8,
+ "version": (3, 3),
+ "model": "p1000",
+ "oem_type": "ot",
+ },
+ ),
(OT3Mount.GRIPPER, {"model": GripperModel.v1, "id": "g12345"}),
],
GantryLoad.LOW_THROUGHPUT,
),
(
[
- (OT3Mount.LEFT, {"channels": 96, "model": "p1000", "version": (3, 3)}),
+ (
+ OT3Mount.LEFT,
+ {
+ "channels": 96,
+ "model": "p1000",
+ "version": (3, 3),
+ "oem_type": "ot",
+ },
+ ),
(OT3Mount.GRIPPER, {"model": GripperModel.v1, "id": "g12345"}),
],
GantryLoad.HIGH_THROUGHPUT,
@@ -463,6 +535,7 @@ async def test_gantry_load_transform(
PipetteModelType(pair[1]["model"]),
PipetteChannelType(pair[1]["channels"]),
PipetteVersionType(*pair[1]["version"]),
+ PipetteOEMType(pair[1]["oem_type"]),
)
instr_data = AttachedPipette(config=pipette_config, id="fakepip")
await ot3_hardware.cache_pipette(pair[0], instr_data, None)
@@ -557,9 +630,30 @@ def mock_verify_tip_presence(
load_pipette_configs = [
- {OT3Mount.LEFT: {"channels": 1, "version": (3, 3), "model": "p1000"}},
- {OT3Mount.RIGHT: {"channels": 8, "version": (3, 3), "model": "p50"}},
- {OT3Mount.LEFT: {"channels": 96, "model": "p1000", "version": (3, 3)}},
+ {
+ OT3Mount.LEFT: {
+ "channels": 1,
+ "version": (3, 3),
+ "model": "p1000",
+ "oem_type": PipetteOEMType.OT,
+ }
+ },
+ {
+ OT3Mount.RIGHT: {
+ "channels": 8,
+ "version": (3, 3),
+ "model": "p50",
+ "oem_type": PipetteOEMType.OT,
+ }
+ },
+ {
+ OT3Mount.LEFT: {
+ "channels": 96,
+ "model": "p1000",
+ "version": (3, 3),
+ "oem_type": PipetteOEMType.OT,
+ }
+ },
]
@@ -573,6 +667,7 @@ async def prepare_for_mock_blowout(
PipetteModelType(configs["model"]),
PipetteChannelType(configs["channels"]),
PipetteVersionType(*configs["version"]),
+ PipetteOEMType(configs["oem_type"]),
)
instr_data = AttachedPipette(config=pipette_config, id="fakepip")
await ot3_hardware.cache_pipette(mount, instr_data, None)
@@ -804,7 +899,10 @@ async def test_liquid_probe(
) -> None:
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -895,7 +993,10 @@ async def test_liquid_probe_plunger_moves(
# when approaching its max z distance
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1002,7 +1103,10 @@ async def test_liquid_probe_mount_moves(
"""Verify move targets for one singular liquid pass probe."""
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1064,7 +1168,10 @@ async def test_multi_liquid_probe(
) -> None:
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1131,7 +1238,10 @@ async def test_liquid_not_found(
) -> None:
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1599,7 +1709,10 @@ async def test_home_plunger(
mount = OT3Mount.LEFT
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1621,6 +1734,7 @@ async def test_prepare_for_aspirate(
PipetteModelType("p1000"),
PipetteChannelType(1),
PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1655,6 +1769,7 @@ async def test_plunger_ready_to_aspirate_after_dispense(
PipetteModelType("p1000"),
PipetteChannelType(1),
PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -1680,7 +1795,10 @@ async def test_move_to_plunger_bottom(
mount = OT3Mount.LEFT
instr_data = AttachedPipette(
config=load_pipette_data.load_definition(
- PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4)
+ PipetteModelType("p1000"),
+ PipetteChannelType(1),
+ PipetteVersionType(3, 4),
+ PipetteOEMType.OT,
),
id="fakepip",
)
@@ -2130,6 +2248,7 @@ async def test_home_axis(
PipetteModelType("p1000"),
PipetteChannelType(1),
PipetteVersionType(3, 3),
+ PipetteOEMType.OT,
)
instr_data = AttachedPipette(config=pipette_config, id="fakepip")
await ot3_hardware.cache_pipette(Axis.to_ot3_mount(axis), instr_data, None)
diff --git a/api/tests/opentrons/hardware_control/test_pipette.py b/api/tests/opentrons/hardware_control/test_pipette.py
index 25ac7b0298a..610fcc2a022 100644
--- a/api/tests/opentrons/hardware_control/test_pipette.py
+++ b/api/tests/opentrons/hardware_control/test_pipette.py
@@ -73,7 +73,10 @@ def _create_pipette(
) -> ot3_pipette.Pipette:
return ot3_pipette.Pipette(
load_pipette_data.load_definition(
- model.pipette_type, model.pipette_channels, model.pipette_version
+ model.pipette_type,
+ model.pipette_channels,
+ model.pipette_version,
+ model.oem_type,
),
calibration,
id,
diff --git a/api/tests/opentrons/test_execute.py b/api/tests/opentrons/test_execute.py
index b9f791f4253..1e72a3757bf 100644
--- a/api/tests/opentrons/test_execute.py
+++ b/api/tests/opentrons/test_execute.py
@@ -131,6 +131,7 @@ def test_execute_function_apiv2(
converted_model_v15.pipette_type,
converted_model_v15.pipette_channels,
converted_model_v15.pipette_version,
+ converted_model_v15.oem_type,
),
"id": "testid",
}
@@ -139,6 +140,7 @@ def test_execute_function_apiv2(
converted_model_v1.pipette_type,
converted_model_v1.pipette_channels,
converted_model_v1.pipette_version,
+ converted_model_v1.oem_type,
),
"id": "testid2",
}
@@ -177,6 +179,7 @@ def emit_runlog(entry: Any) -> None:
converted_model_v15.pipette_type,
converted_model_v15.pipette_channels,
converted_model_v15.pipette_version,
+ converted_model_v15.oem_type,
),
"id": "testid",
}
@@ -215,6 +218,7 @@ def emit_runlog(entry: Any) -> None:
converted_model_v15.pipette_type,
converted_model_v15.pipette_channels,
converted_model_v15.pipette_version,
+ converted_model_v15.oem_type,
),
"id": "testid",
}
@@ -253,6 +257,7 @@ def emit_runlog(entry: Any) -> None:
converted_model_v15.pipette_type,
converted_model_v15.pipette_channels,
converted_model_v15.pipette_version,
+ converted_model_v15.oem_type,
),
"id": "testid",
}
@@ -292,6 +297,7 @@ def emit_runlog(entry: Any) -> None:
converted_model_v15.pipette_type,
converted_model_v15.pipette_channels,
converted_model_v15.pipette_version,
+ converted_model_v15.oem_type,
),
"id": "testid",
}
diff --git a/robot-server/tests/service/legacy/routers/test_settings.py b/robot-server/tests/service/legacy/routers/test_settings.py
index 9c52256b82d..4e8ee53b138 100644
--- a/robot-server/tests/service/legacy/routers/test_settings.py
+++ b/robot-server/tests/service/legacy/routers/test_settings.py
@@ -146,6 +146,7 @@ def test_receive_attached_pipette_settings(
pip_types.PipetteModelType.p20,
pip_types.PipetteChannelType.EIGHT_CHANNEL,
pip_types.PipetteVersionType(3, 5),
+ pip_types.PipetteOEMType.OT,
),
pipette_serial_number="P12345",
pipette_override_path="nope",
diff --git a/shared-data/command/schemas/10.json b/shared-data/command/schemas/10.json
index aced561bdff..fe43bffd92f 100644
--- a/shared-data/command/schemas/10.json
+++ b/shared-data/command/schemas/10.json
@@ -1774,6 +1774,7 @@
"p1000_single_gen2",
"p1000_single_flex",
"p1000_multi_flex",
+ "p1000_multi_em_flex",
"p1000_96",
"p200_96"
],
diff --git a/shared-data/command/schemas/11.json b/shared-data/command/schemas/11.json
index ac5dd17eeb3..ec0d854e5f9 100644
--- a/shared-data/command/schemas/11.json
+++ b/shared-data/command/schemas/11.json
@@ -3348,7 +3348,7 @@
"p1000_single_gen2",
"p1000_single_flex",
"p1000_multi_flex",
- "p1000_multi_em",
+ "p1000_multi_em_flex",
"p1000_96",
"p200_96"
],
diff --git a/shared-data/js/pipettes.ts b/shared-data/js/pipettes.ts
index 2921696b820..134f4efbd82 100644
--- a/shared-data/js/pipettes.ts
+++ b/shared-data/js/pipettes.ts
@@ -186,9 +186,9 @@ const getHighestVersion = (
}
const V2_DEFINITION_TYPES = ['general', 'geometry']
-/* takes in pipetteName such as 'p300_single' or 'p300_single_gen1'
+/* takes in pipetteName such as 'p300_single' or 'p300_single_gen1'
or PipetteModel such as 'p300_single_v1.3' and converts it to channels,
-model, and version in order to return the correct pipette schema v2 json files.
+model, and version in order to return the correct pipette schema v2 json files.
**/
export const getPipetteSpecsV2 = (
name?: PipetteName | PipetteModel
@@ -200,7 +200,15 @@ export const getPipetteSpecsV2 = (
const nameSplit = name.split('_')
const pipetteModel = nameSplit[0] // ex: p300
const channels = getChannelsFromString(nameSplit[1] as PipChannelString) // ex: single -> single_channel
- const pipetteGen = getVersionFromGen(nameSplit[2] as Gen)
+ let version_index: number
+ let oemString: string = ''
+ if (nameSplit.length === 4) {
+ version_index = 3
+ oemString = `_${nameSplit[2]}`
+ } else {
+ version_index = 2
+ }
+ const pipetteGen = getVersionFromGen(nameSplit[version_index] as Gen)
let version: string = ''
let majorVersion: number
// the first 2 conditions are to accommodate version from the pipetteName
@@ -215,7 +223,7 @@ export const getPipetteSpecsV2 = (
majorVersion = pipetteGen // ex: gen1 -> 1
// the 'else' is to accommodate the exact version if PipetteModel was added
} else {
- const versionNumber = nameSplit[2].split('v')[1]
+ const versionNumber = nameSplit[version_index].split('v')[1]
if (versionNumber.includes('.')) {
version = versionNumber.replace('.', '_') // ex: 1.0 -> 1_0
} else {
@@ -236,7 +244,7 @@ export const getPipetteSpecsV2 = (
)
V2_DEFINITION_TYPES.forEach(type => {
if (
- `../pipette/definitions/2/${type}/${channels}/${pipetteModel}/${
+ `../pipette/definitions/2/${type}/${channels}${oemString}/${pipetteModel}/${
version === '' ? highestVersion : version
}.json` === path
) {
diff --git a/shared-data/pipette/definitions/1/pipetteModelSpecs.json b/shared-data/pipette/definitions/1/pipetteModelSpecs.json
index a66312eb522..6a1eea16bd4 100644
--- a/shared-data/pipette/definitions/1/pipetteModelSpecs.json
+++ b/shared-data/pipette/definitions/1/pipetteModelSpecs.json
@@ -8788,6 +8788,201 @@
"returnTipHeight": 0.71,
"idleCurrent": 0.3
},
+ "p1000_multi_em_v3.0": {
+ "name": "p1000_multi_em_flex",
+ "backCompatNames": [],
+ "top": {
+ "value": 0.5,
+ "min": 0,
+ "max": 45,
+ "units": "mm",
+ "type": "float"
+ },
+ "bottom": {
+ "value": 71.5,
+ "min": 55,
+ "max": 80,
+ "type": "float",
+ "units": "mm"
+ },
+ "blowout": {
+ "value": 76.5,
+ "min": 60,
+ "max": 85,
+ "units": "mm",
+ "type": "float"
+ },
+ "dropTip": {
+ "value": 92.5,
+ "min": 78,
+ "max": 110,
+ "units": "mm",
+ "type": "float"
+ },
+ "pickUpCurrent": {
+ "value": 0.5,
+ "min": 0.05,
+ "max": 2.0,
+ "units": "amps",
+ "type": "float"
+ },
+ "pickUpDistance": {
+ "value": 13,
+ "min": 1,
+ "max": 30,
+ "units": "mm",
+ "type": "float"
+ },
+ "pickUpIncrement": {
+ "value": 0.0,
+ "min": 0.0,
+ "max": 10.0,
+ "units": "mm",
+ "type": "float"
+ },
+ "pickUpPresses": {
+ "value": 1,
+ "min": 0,
+ "max": 10,
+ "units": "presses",
+ "type": "int"
+ },
+ "pickUpSpeed": {
+ "value": 10,
+ "min": 1,
+ "max": 30,
+ "units": "mm/s",
+ "type": "float"
+ },
+ "nozzleOffset": [-8.0, -16.0, -259.15],
+ "modelOffset": [0.0, 0.0, 25.14],
+ "ulPerMm": [
+ {
+ "aspirate": [
+ [0.7511, 3.9556, 6.455],
+ [1.3075, 2.1664, 5.8839],
+ [1.8737, 1.1513, 7.2111],
+ [3.177, 0.9374, 7.612],
+ [4.5368, 0.5531, 8.8328],
+ [7.31, 0.3035, 9.9651],
+ [10.0825, 0.1513, 11.0781],
+ [12.9776, 0.1293, 11.2991],
+ [15.9173, 0.0976, 11.7115],
+ [18.8243, 0.06244, 12.2706],
+ [21.8529, 0.07004, 12.1275],
+ [24.8068, 0.04182, 12.7442],
+ [27.7744, 0.0356, 12.8984],
+ [35.2873, 0.03031, 13.04544],
+ [42.799, 0.02015, 13.4038],
+ [50.4562, 0.01956, 13.4293],
+ [58.1081, 0.0145, 13.6843],
+ [65.7267, 0.01036, 13.9252],
+ [73.2857, 0.006776, 14.1606],
+ [81.00159, 0.009126, 13.9883],
+ [88.6617, 0.006448, 14.2052],
+ [103.9829, 0.005074, 14.3271],
+ [119.4408, 0.004878, 14.3476],
+ [134.889, 0.003727, 14.485],
+ [150.273, 0.00258, 14.6402],
+ [181.2798, 0.002559, 14.6427],
+ [212.4724, 0.002242, 14.7002],
+ [243.577, 0.00151, 14.856],
+ [274.7216, 0.001244, 14.9205],
+ [305.8132, 0.0009118, 15.0118],
+ [368.06968, 0.0007321, 15.06677],
+ [430.2513, 0.0004805, 15.1594],
+ [492.3487, 0.0003186, 15.2291],
+ [554.5713, 0.0003031, 15.237],
+ [616.6825, 0.0001981, 15.2948],
+ [694.4168, 0.0001855, 15.3027],
+ [772.0327, 0.0001181, 15.3494],
+ [849.617, 0.00008929, 15.3717],
+ [927.2556, 0.00008601, 15.3745],
+ [1004.87, 0.00006801, 15.3912],
+ [1051.4648, 0.00006824, 15.391]
+ ],
+ "dispense": [
+ [0.7511, 3.9556, 6.455],
+ [1.3075, 2.1664, 5.8839],
+ [1.8737, 1.1513, 7.2111],
+ [3.177, 0.9374, 7.612],
+ [4.5368, 0.5531, 8.8328],
+ [7.31, 0.3035, 9.9651],
+ [10.0825, 0.1513, 11.0781],
+ [12.9776, 0.1293, 11.2991],
+ [15.9173, 0.0976, 11.7115],
+ [18.8243, 0.06244, 12.2706],
+ [21.8529, 0.07004, 12.1275],
+ [24.8068, 0.04182, 12.7442],
+ [27.7744, 0.0356, 12.8984],
+ [35.2873, 0.03031, 13.04544],
+ [42.799, 0.02015, 13.4038],
+ [50.4562, 0.01956, 13.4293],
+ [58.1081, 0.0145, 13.6843],
+ [65.7267, 0.01036, 13.9252],
+ [73.2857, 0.006776, 14.1606],
+ [81.00159, 0.009126, 13.9883],
+ [88.6617, 0.006448, 14.2052],
+ [103.9829, 0.005074, 14.3271],
+ [119.4408, 0.004878, 14.3476],
+ [134.889, 0.003727, 14.485],
+ [150.273, 0.00258, 14.6402],
+ [181.2798, 0.002559, 14.6427],
+ [212.4724, 0.002242, 14.7002],
+ [243.577, 0.00151, 14.856],
+ [274.7216, 0.001244, 14.9205],
+ [305.8132, 0.0009118, 15.0118],
+ [368.06968, 0.0007321, 15.06677],
+ [430.2513, 0.0004805, 15.1594],
+ [492.3487, 0.0003186, 15.2291],
+ [554.5713, 0.0003031, 15.237],
+ [616.6825, 0.0001981, 15.2948],
+ [694.4168, 0.0001855, 15.3027],
+ [772.0327, 0.0001181, 15.3494],
+ [849.617, 0.00008929, 15.3717],
+ [927.2556, 0.00008601, 15.3745],
+ [1004.87, 0.00006801, 15.3912],
+ [1051.4648, 0.00006824, 15.391]
+ ]
+ }
+ ],
+ "plungerCurrent": {
+ "value": 1,
+ "min": 0.1,
+ "max": 1.5,
+ "units": "amps",
+ "type": "float"
+ },
+ "dropTipCurrent": {
+ "value": 1,
+ "min": 0.1,
+ "max": 1.25,
+ "units": "amps",
+ "type": "float"
+ },
+ "dropTipSpeed": {
+ "value": 10,
+ "min": 0.001,
+ "max": 30,
+ "units": "mm/sec",
+ "type": "float"
+ },
+ "tipOverlap": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5
+ },
+ "tipLength": {
+ "value": 78.3,
+ "units": "mm",
+ "type": "float",
+ "min": 0,
+ "max": 100
+ },
+ "quirks": ["highSpeed"],
+ "returnTipHeight": 0.71,
+ "idleCurrent": 0.3
+ },
"p50_multi_v3.0": {
"name": "p50_multi_flex",
"backCompatNames": [],
diff --git a/shared-data/pipette/definitions/1/pipetteNameSpecs.json b/shared-data/pipette/definitions/1/pipetteNameSpecs.json
index 61cd56d32f6..7beeb92aabc 100644
--- a/shared-data/pipette/definitions/1/pipetteNameSpecs.json
+++ b/shared-data/pipette/definitions/1/pipetteNameSpecs.json
@@ -599,6 +599,54 @@
"opentrons/opentrons_flex_96_filtertiprack_50ul/1"
]
},
+ "p1000_multi_em_flex": {
+ "displayName": "Flex 8-Channel EM 1000 μL",
+ "displayCategory": "FLEX",
+ "defaultAspirateFlowRate": {
+ "value": 478,
+ "min": 3,
+ "max": 812,
+ "valuesByApiLevel": {
+ "2.0": 159.04,
+ "2.6": 159.04,
+ "2.14": 478
+ }
+ },
+ "defaultDispenseFlowRate": {
+ "value": 478,
+ "min": 3,
+ "max": 812,
+ "valuesByApiLevel": {
+ "2.0": 159.04,
+ "2.14": 478
+ }
+ },
+ "defaultBlowOutFlowRate": {
+ "value": 478,
+ "min": 3,
+ "max": 812,
+ "valuesByApiLevel": {
+ "2.0": 78.52,
+ "2.14": 478
+ }
+ },
+ "channels": 8,
+ "minVolume": 5,
+ "maxVolume": 1000,
+ "smoothieConfigs": {
+ "stepsPerMM": 2133.33,
+ "homePosition": 230.15,
+ "travelDistance": 80
+ },
+ "defaultTipracks": [
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1",
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1",
+ "opentrons/opentrons_flex_96_tiprack_200ul/1",
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1",
+ "opentrons/opentrons_flex_96_tiprack_50ul/1",
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1"
+ ]
+ },
"p50_multi_flex": {
"displayName": "Flex 8-Channel 50 μL",
"displayCategory": "FLEX",
diff --git a/shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json b/shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json
new file mode 100644
index 00000000000..c267504b404
--- /dev/null
+++ b/shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json
@@ -0,0 +1,320 @@
+{
+ "$otSharedSchema": "#/pipette/schemas/2/pipettePropertiesSchema.json",
+ "displayName": "FLEX 8-Channel EM 1000 μL",
+ "model": "p1000",
+ "displayCategory": "FLEX",
+ "validNozzleMaps": {
+ "maps": {
+ "SingleA1": ["A1"],
+ "SingleH1": ["H1"],
+ "H1toG1": ["G1", "H1"],
+ "H1toF1": ["F1", "G1", "H1"],
+ "H1toE1": ["E1", "F1", "G1", "H1"],
+ "H1toD1": ["D1", "E1", "F1", "G1", "H1"],
+ "H1toC1": ["C1", "D1", "E1", "F1", "G1", "H1"],
+ "H1toB1": ["B1", "C1", "D1", "E1", "F1", "G1", "H1"],
+ "Full": ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"]
+ }
+ },
+ "pickUpTipConfigurations": {
+ "pressFit": {
+ "presses": 1,
+ "increment": 0.0,
+ "configurationsByNozzleMap": {
+ "SingleA1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 11.0,
+ "current": 0.15,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 10.08,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.08,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.26,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.08,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.26
+ }
+ }
+ }
+ },
+ "SingleH1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 11.0,
+ "current": 0.15,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.52,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.71,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.52,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.2,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.71,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.52,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.2
+ }
+ }
+ }
+ },
+ "H1toG1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.2,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.24,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.52,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.24,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.73,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.52,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.24,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.73
+ }
+ }
+ }
+ },
+ "H1toF1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.2,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8
+ }
+ }
+ }
+ },
+ "H1toE1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.35,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8
+ }
+ }
+ }
+ },
+ "H1toD1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.4,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8
+ }
+ }
+ }
+ },
+ "H1toC1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.4,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8
+ }
+ }
+ }
+ },
+ "H1toB1": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.5,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 9.13,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.23,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.13,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.9,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.23,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.13,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.9
+ }
+ }
+ }
+ },
+ "Full": {
+ "default": {
+ "speed": 10.0,
+ "distance": 13.0,
+ "current": 0.55,
+ "tipOverlaps": {
+ "v0": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17
+ },
+ "v1": {
+ "default": 10.5,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.42,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.27,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.67,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.42,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.27,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.67
+ },
+ "v3": {
+ "default": 9.28,
+ "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.37,
+ "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.28,
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.67,
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.37,
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.28,
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.67
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "dropTipConfigurations": {
+ "plungerEject": {
+ "current": 1.0,
+ "speed": 10
+ }
+ },
+ "plungerMotorConfigurations": {
+ "idle": 0.3,
+ "run": 1.0
+ },
+ "plungerPositionsConfigurations": {
+ "default": {
+ "top": 0.0,
+ "bottom": 71.5,
+ "blowout": 76.5,
+ "drop": 91.5
+ }
+ },
+ "availableSensors": {
+ "sensors": ["capacitive", "environment"],
+ "capacitive": {
+ "count": 2
+ },
+ "environment": {
+ "count": 1
+ }
+ },
+ "partialTipConfigurations": {
+ "partialTipSupported": true,
+ "availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8]
+ },
+ "backCompatNames": [],
+ "channels": 8,
+ "shaftDiameter": 4.5,
+ "shaftULperMM": 15.904,
+ "backlashDistance": 0.1,
+ "quirks": ["highSpeed"],
+ "plungerHomingConfigurations": {
+ "current": 1.0,
+ "speed": 30
+ }
+}
diff --git a/shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json b/shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json
new file mode 100644
index 00000000000..b92e7415fe3
--- /dev/null
+++ b/shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json
@@ -0,0 +1,55 @@
+{
+ "$otSharedSchema": "#/pipette/schemas/2/pipetteGeometrySchema.json",
+ "pathTo3D": "pipette/definitions/2/geometry/eight_channel_em/p1000/placeholder.gltf",
+ "nozzleOffset": [-8.0, -16.0, -259.15],
+ "pipetteBoundingBoxOffsets": {
+ "backLeftCorner": [-38.5, 0.0, -259.15],
+ "frontRightCorner": [11.5, -95.0, -259.15]
+ },
+ "orderedRows": [
+ {
+ "key": "A",
+ "orderedNozzles": ["A1"]
+ },
+ {
+ "key": "B",
+ "orderedNozzles": ["B1"]
+ },
+ { "key": "C", "orderedNozzles": ["C1"] },
+ { "key": "D", "orderedNozzles": ["D1"] },
+ { "key": "E", "orderedNozzles": ["E1"] },
+ { "key": "F", "orderedNozzles": ["F1"] },
+ { "key": "G", "orderedNozzles": ["G1"] },
+ { "key": "H", "orderedNozzles": ["H1"] }
+ ],
+ "orderedColumns": [
+ {
+ "key": "1",
+ "orderedNozzles": ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"]
+ }
+ ],
+ "nozzleMap": {
+ "A1": [-8.0, -16.0, -259.15],
+ "B1": [-8.0, -25.0, -259.15],
+ "C1": [-8.0, -34.0, -259.15],
+ "D1": [-8.0, -43.0, -259.15],
+ "E1": [-8.0, -52.0, -259.15],
+ "F1": [-8.0, -61.0, -259.15],
+ "G1": [-8.0, -70.0, -259.15],
+ "H1": [-8.0, -79.0, -259.15]
+ },
+ "lldSettings": {
+ "t50": {
+ "minHeight": 1.0,
+ "minVolume": 0
+ },
+ "t200": {
+ "minHeight": 1.0,
+ "minVolume": 0
+ },
+ "t1000": {
+ "minHeight": 1.5,
+ "minVolume": 0
+ }
+ }
+}
diff --git a/shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json b/shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json
new file mode 100644
index 00000000000..52c7b58171d
--- /dev/null
+++ b/shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json
@@ -0,0 +1,236 @@
+{
+ "$otSharedSchema": "#/pipette/schemas/2/pipetteLiquidPropertiesSchema.json",
+ "supportedTips": {
+ "t50": {
+ "uiMaxFlowRate": 1431.0,
+ "defaultAspirateFlowRate": {
+ "default": 478,
+ "valuesByApiLevel": { "2.14": 478 }
+ },
+ "defaultDispenseFlowRate": {
+ "default": 478,
+ "valuesByApiLevel": { "2.14": 478 }
+ },
+ "defaultBlowOutFlowRate": {
+ "default": 478,
+ "valuesByApiLevel": { "2.14": 478 }
+ },
+ "defaultFlowAcceleration": 24000.0,
+ "defaultTipLength": 57.9,
+ "defaultReturnTipHeight": 0.71,
+ "aspirate": {
+ "default": {
+ "1": [
+ [0.12, -57.973785, 8.495981],
+ [0.11, 40.31047, -3.298129],
+ [0.09, 19.330223, -0.990302],
+ [0.375, 6.200306, 0.19139],
+ [1.17, 4.795927, 0.718032],
+ [1.92, 2.746428, 3.115947],
+ [2.145, 1.592373, 5.331732],
+ [2.4, 1.336497, 5.880586],
+ [2.66, 1.043996, 6.582588],
+ [2.84, 0.280189, 8.614315],
+ [2.985, -0.698973, 11.395134],
+ [3.085, -5.627462, 26.106674],
+ [3.625, 1.899561, 2.885808],
+ [4.43, 1.977851, 2.602006],
+ [5.155, 0.596916, 8.71955],
+ [6.71, 0.366092, 9.909446],
+ [8.62, 0.233878, 10.796602],
+ [11.015, 0.158281, 11.448248],
+ [13.97, 0.101002, 12.079177],
+ [17.545, 0.047056, 12.832813],
+ [22.075, 0.043416, 12.896662],
+ [27.955, 0.049456, 12.763333],
+ [34.695, 0.00096, 14.119053],
+ [43.535, 0.018347, 13.515795],
+ [54.08, 0.001949, 14.229706]
+ ]
+ }
+ },
+ "dispense": {
+ "default": {
+ "1": [
+ [0.12, -57.973785, 8.495981],
+ [0.11, 40.31047, -3.298129],
+ [0.09, 19.330223, -0.990302],
+ [0.375, 6.200306, 0.19139],
+ [1.17, 4.795927, 0.718032],
+ [1.92, 2.746428, 3.115947],
+ [2.145, 1.592373, 5.331732],
+ [2.4, 1.336497, 5.880586],
+ [2.66, 1.043996, 6.582588],
+ [2.84, 0.280189, 8.614315],
+ [2.985, -0.698973, 11.395134],
+ [3.085, -5.627462, 26.106674],
+ [3.625, 1.899561, 2.885808],
+ [4.43, 1.977851, 2.602006],
+ [5.155, 0.596916, 8.71955],
+ [6.71, 0.366092, 9.909446],
+ [8.62, 0.233878, 10.796602],
+ [11.015, 0.158281, 11.448248],
+ [13.97, 0.101002, 12.079177],
+ [17.545, 0.047056, 12.832813],
+ [22.075, 0.043416, 12.896662],
+ [27.955, 0.049456, 12.763333],
+ [34.695, 0.00096, 14.119053],
+ [43.535, 0.018347, 13.515795],
+ [54.08, 0.001949, 14.229706]
+ ]
+ }
+ },
+ "defaultPushOutVolume": 7
+ },
+ "t200": {
+ "uiMaxFlowRate": 1431.0,
+ "defaultAspirateFlowRate": {
+ "default": 716,
+ "valuesByApiLevel": { "2.14": 716 }
+ },
+ "defaultDispenseFlowRate": {
+ "default": 716,
+ "valuesByApiLevel": { "2.14": 716 }
+ },
+ "defaultBlowOutFlowRate": {
+ "default": 716,
+ "valuesByApiLevel": { "2.14": 716 }
+ },
+ "defaultFlowAcceleration": 24000.0,
+ "defaultTipLength": 58.35,
+ "defaultReturnTipHeight": 0.71,
+ "aspirate": {
+ "default": {
+ "1": [
+ [0.28375, -141.180627, 42.499381],
+ [0.26125, 27.065799, -5.240543],
+ [0.715, 4.916546, 0.54595],
+ [1.685, 3.844391, 1.31254],
+ [2.6025, 2.148973, 4.169319],
+ [3.75875, 1.461751, 5.957816],
+ [4.9975, 0.733738, 8.694235],
+ [6.41375, 0.377599, 10.474036],
+ [8.1225, 0.214926, 11.517382],
+ [10.2425, 0.152451, 12.024835],
+ [12.80125, 0.081051, 12.75615],
+ [15.9875, 0.062849, 12.989161],
+ [19.9625, 0.051585, 13.169235],
+ [24.83625, 0.030593, 13.588301],
+ [30.89125, 0.024593, 13.737307],
+ [38.42625, 0.020128, 13.875257],
+ [47.71875, 0.014091, 14.107204],
+ [59.28375, 0.011625, 14.224918],
+ [73.41375, 0.00635, 14.537608],
+ [90.84375, 0.004458, 14.676515],
+ [112.32, 0.003084, 14.801312],
+ [138.7675, 0.002045, 14.917998],
+ [171.29875, 0.001319, 15.018758],
+ [211.27375, 0.000719, 15.121662]
+ ]
+ }
+ },
+ "dispense": {
+ "default": {
+ "1": [
+ [0.28375, -141.180627, 42.499381],
+ [0.26125, 27.065799, -5.240543],
+ [0.715, 4.916546, 0.54595],
+ [1.685, 3.844391, 1.31254],
+ [2.6025, 2.148973, 4.169319],
+ [3.75875, 1.461751, 5.957816],
+ [4.9975, 0.733738, 8.694235],
+ [6.41375, 0.377599, 10.474036],
+ [8.1225, 0.214926, 11.517382],
+ [10.2425, 0.152451, 12.024835],
+ [12.80125, 0.081051, 12.75615],
+ [15.9875, 0.062849, 12.989161],
+ [19.9625, 0.051585, 13.169235],
+ [24.83625, 0.030593, 13.588301],
+ [30.89125, 0.024593, 13.737307],
+ [38.42625, 0.020128, 13.875257],
+ [47.71875, 0.014091, 14.107204],
+ [59.28375, 0.011625, 14.224918],
+ [73.41375, 0.00635, 14.537608],
+ [90.84375, 0.004458, 14.676515],
+ [112.32, 0.003084, 14.801312],
+ [138.7675, 0.002045, 14.917998],
+ [171.29875, 0.001319, 15.018758],
+ [211.27375, 0.000719, 15.121662]
+ ]
+ }
+ },
+ "defaultPushOutVolume": 5
+ },
+ "t1000": {
+ "uiMaxFlowRate": 1431.0,
+ "defaultAspirateFlowRate": {
+ "default": 716,
+ "valuesByApiLevel": { "2.14": 716 }
+ },
+ "defaultDispenseFlowRate": {
+ "default": 716,
+ "valuesByApiLevel": { "2.14": 716 }
+ },
+ "defaultBlowOutFlowRate": {
+ "default": 716,
+ "valuesByApiLevel": { "2.14": 716 }
+ },
+ "defaultFlowAcceleration": 24000.0,
+ "defaultTipLength": 95.6,
+ "defaultReturnTipHeight": 0.82,
+ "aspirate": {
+ "default": {
+ "1": [
+ [2.1443, 1.9858, 4.2677],
+ [3.0286, 1.2526, 5.84],
+ [4.9557, 0.6268, 7.7351],
+ [9.7943, 0.2745, 9.4811],
+ [12.1514, 0.1715, 10.4901],
+ [14.9414, 0.0897, 11.4833],
+ [51.46, 0.0424, 12.1913],
+ [92.68, 0.0095, 13.881],
+ [112.4886, 0.0049, 14.3053],
+ [243.5986, 0.0028, 14.5507],
+ [356.5686, 0.0009, 15.0019],
+ [430.99, 0.0005, 15.1492],
+ [628.7886, 0.0003, 15.2496],
+ [1001.15, 0.0001, 15.3472],
+ [1106.0857, 0.0001, 15.3551]
+ ]
+ }
+ },
+ "dispense": {
+ "default": {
+ "1": [
+ [2.1443, 1.9858, 4.2677],
+ [3.0286, 1.2526, 5.84],
+ [4.9557, 0.6268, 7.7351],
+ [9.7943, 0.2745, 9.4811],
+ [12.1514, 0.1715, 10.4901],
+ [14.9414, 0.0897, 11.4833],
+ [51.46, 0.0424, 12.1913],
+ [92.68, 0.0095, 13.881],
+ [112.4886, 0.0049, 14.3053],
+ [243.5986, 0.0028, 14.5507],
+ [356.5686, 0.0009, 15.0019],
+ [430.99, 0.0005, 15.1492],
+ [628.7886, 0.0003, 15.2496],
+ [1001.15, 0.0001, 15.3472],
+ [1106.0857, 0.0001, 15.3551]
+ ]
+ }
+ },
+ "defaultPushOutVolume": 20
+ }
+ },
+ "maxVolume": 1000,
+ "minVolume": 5,
+ "defaultTipracks": [
+ "opentrons/opentrons_flex_96_tiprack_1000ul/1",
+ "opentrons/opentrons_flex_96_tiprack_200ul/1",
+ "opentrons/opentrons_flex_96_tiprack_50ul/1",
+ "opentrons/opentrons_flex_96_filtertiprack_1000ul/1",
+ "opentrons/opentrons_flex_96_filtertiprack_200ul/1",
+ "opentrons/opentrons_flex_96_filtertiprack_50ul/1"
+ ]
+}
diff --git a/shared-data/python/opentrons_shared_data/pipette/load_data.py b/shared-data/python/opentrons_shared_data/pipette/load_data.py
index 66ba690356a..f495713b3d8 100644
--- a/shared-data/python/opentrons_shared_data/pipette/load_data.py
+++ b/shared-data/python/opentrons_shared_data/pipette/load_data.py
@@ -22,6 +22,7 @@
PipetteModelMajorVersion,
PipetteModelMinorVersion,
LiquidClasses,
+ PipetteOEMType,
)
@@ -35,8 +36,10 @@ def _get_configuration_dictionary(
channels: PipetteChannelType,
model: PipetteModelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
liquid_class: Optional[LiquidClasses] = None,
) -> LoadedConfiguration:
+ oem_extension = f"_{oem.value}" if oem != PipetteOEMType.OT else ""
if liquid_class:
config_path = (
get_shared_data_root()
@@ -44,7 +47,7 @@ def _get_configuration_dictionary(
/ "definitions"
/ "2"
/ config_type
- / channels.name.lower()
+ / f"{channels.name.lower()}{oem_extension}"
/ model.value
/ liquid_class.name
/ f"{version.major}_{version.minor}.json"
@@ -56,7 +59,7 @@ def _get_configuration_dictionary(
/ "definitions"
/ "2"
/ config_type
- / channels.name.lower()
+ / f"{channels.name.lower()}{oem_extension}"
/ model.value
/ f"{version.major}_{version.minor}.json"
)
@@ -68,8 +71,9 @@ def _geometry(
channels: PipetteChannelType,
model: PipetteModelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
) -> LoadedConfiguration:
- return _get_configuration_dictionary("geometry", channels, model, version)
+ return _get_configuration_dictionary("geometry", channels, model, version, oem)
@lru_cache(maxsize=None)
@@ -77,12 +81,13 @@ def _liquid(
channels: PipetteChannelType,
model: PipetteModelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
) -> Dict[str, LoadedConfiguration]:
liquid_dict = {}
for liquid_class in LiquidClasses:
try:
liquid_dict[liquid_class.name] = _get_configuration_dictionary(
- "liquid", channels, model, version, liquid_class
+ "liquid", channels, model, version, oem, liquid_class
)
except FileNotFoundError:
continue
@@ -95,8 +100,9 @@ def _physical(
channels: PipetteChannelType,
model: PipetteModelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
) -> LoadedConfiguration:
- return _get_configuration_dictionary("general", channels, model, version)
+ return _get_configuration_dictionary("general", channels, model, version, oem)
def _dirs_in(path: Path) -> Iterator[Path]:
@@ -152,8 +158,9 @@ def load_liquid_model(
model: PipetteModelType,
channels: PipetteChannelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
) -> Dict[str, PipetteLiquidPropertiesDefinition]:
- liquid_dict = _liquid(channels, model, version)
+ liquid_dict = _liquid(channels, model, version, oem)
return {
k: PipetteLiquidPropertiesDefinition.model_validate(v)
for k, v in liquid_dict.items()
@@ -252,6 +259,7 @@ def load_definition(
model: PipetteModelType,
channels: PipetteChannelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
) -> PipetteConfigurations:
if (
version.major not in PipetteModelMajorVersion
@@ -259,9 +267,9 @@ def load_definition(
):
raise KeyError("Pipette version not found.")
- geometry_dict = _geometry(channels, model, version)
- physical_dict = _physical(channels, model, version)
- liquid_dict = _liquid(channels, model, version)
+ geometry_dict = _geometry(channels, model, version, oem)
+ physical_dict = _physical(channels, model, version, oem)
+ liquid_dict = _liquid(channels, model, version, oem)
generation = PipetteGenerationType(physical_dict["displayCategory"])
mount_configs = MOUNT_CONFIG_LOOKUP_TABLE[generation][channels]
@@ -281,6 +289,7 @@ def load_valid_nozzle_maps(
model: PipetteModelType,
channels: PipetteChannelType,
version: PipetteVersionType,
+ oem: PipetteOEMType,
) -> ValidNozzleMaps:
if (
version.major not in PipetteModelMajorVersion
@@ -288,5 +297,5 @@ def load_valid_nozzle_maps(
):
raise KeyError("Pipette version not found.")
- physical_dict = _physical(channels, model, version)
+ physical_dict = _physical(channels, model, version, oem)
return ValidNozzleMaps.model_validate(physical_dict["validNozzleMaps"])
diff --git a/shared-data/python/opentrons_shared_data/pipette/model_constants.py b/shared-data/python/opentrons_shared_data/pipette/model_constants.py
index daf9b233e52..7d34e0e5f6a 100644
--- a/shared-data/python/opentrons_shared_data/pipette/model_constants.py
+++ b/shared-data/python/opentrons_shared_data/pipette/model_constants.py
@@ -20,7 +20,6 @@
PipetteGenerationType.FLEX: {
PipetteChannelType.SINGLE_CHANNEL: RobotMountConfigs(2133.33, 230.15, 80),
PipetteChannelType.EIGHT_CHANNEL: RobotMountConfigs(2133.33, 230.15, 80),
- PipetteChannelType.EIGHT_CHANNEL_EM: RobotMountConfigs(2133.33, 230.15, 80),
PipetteChannelType.NINETY_SIX_CHANNEL: RobotMountConfigs(2133.33, 230.15, 80),
},
}
diff --git a/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py b/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py
index 23943ef9082..7214adf2483 100644
--- a/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py
+++ b/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py
@@ -238,6 +238,7 @@ def _load_full_mutable_configs(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
base_configs_dict = base_configs.model_dump(by_alias=True)
full_mutable_configs = _list_all_mutable_configs(overrides, base_configs_dict)
@@ -334,6 +335,7 @@ def load_with_mutable_configurations(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
# Load overrides if we have a pipette id
if pipette_serial_number:
@@ -431,6 +433,7 @@ def save_overrides(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
base_configs_dict = base_configs.model_dump(by_alias=True)
try:
diff --git a/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py b/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py
index 2e10ede5093..9f52eaf85f2 100644
--- a/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py
+++ b/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py
@@ -41,9 +41,15 @@ class PipetteNameType:
pipette_type: pip_types.PipetteModelType
pipette_channels: pip_types.PipetteChannelType
pipette_generation: pip_types.PipetteGenerationType
+ oem_type: pip_types.PipetteOEMType
def __repr__(self) -> str:
- base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}"
+ oem_name = (
+ f"_{self.oem_type.value}"
+ if self.oem_type != pip_types.PipetteOEMType.OT
+ else ""
+ )
+ base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}{oem_name}"
if self.pipette_generation == pip_types.PipetteGenerationType.GEN1:
return base_name
elif self.pipette_channels == pip_types.PipetteChannelType.NINETY_SIX_CHANNEL:
@@ -65,9 +71,15 @@ class PipetteModelVersionType:
pipette_type: pip_types.PipetteModelType
pipette_channels: pip_types.PipetteChannelType
pipette_version: pip_types.PipetteVersionType
+ oem_type: pip_types.PipetteOEMType
def __repr__(self) -> str:
- base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}"
+ oem_name = (
+ f"_{self.oem_type.value}"
+ if self.oem_type != pip_types.PipetteOEMType.OT
+ else ""
+ )
+ base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}{oem_name}"
return f"{base_name}_v{self.pipette_version}"
diff --git a/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py b/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py
index 865862bfec2..2c7dd0dc57c 100644
--- a/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py
+++ b/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py
@@ -11,6 +11,7 @@
PipetteGenerationType,
PipetteModelMajorVersionType,
PipetteModelMinorVersionType,
+ PipetteOEMType,
)
from .pipette_definition import (
PipetteNameType,
@@ -21,6 +22,7 @@
DEFAULT_MODEL = PipetteModelType.p1000
DEFAULT_CHANNELS = PipetteChannelType.SINGLE_CHANNEL
DEFAULT_MODEL_VERSION = PipetteVersionType(major=1, minor=0)
+DEFAULT_OEM = PipetteOEMType.OT
PIPETTE_AVAILABLE_TYPES = [m.name for m in PipetteModelType]
PIPETTE_CHANNELS_INTS = [c.value for c in PipetteChannelType]
@@ -81,8 +83,6 @@ def channels_from_string(channels: str) -> PipetteChannelType:
if channels == "96":
return PipetteChannelType.NINETY_SIX_CHANNEL
elif "multi" in channels:
- if "em" in channels:
- return PipetteChannelType.EIGHT_CHANNEL_EM
return PipetteChannelType.EIGHT_CHANNEL
elif channels == "single":
return PipetteChannelType.SINGLE_CHANNEL
@@ -229,8 +229,8 @@ def convert_to_pipette_name_type(
channels = channels_from_string(split_pipette_model_or_name[1])
generation = generation_from_string(split_pipette_model_or_name)
pipette_type = PipetteModelType[split_pipette_model_or_name[0]]
-
- return PipetteNameType(pipette_type, channels, generation)
+ oem = PipetteOEMType.get_oem_from_model_str(model_or_name)
+ return PipetteNameType(pipette_type, channels, generation, oem)
def convert_pipette_name(
@@ -266,8 +266,8 @@ def convert_pipette_name(
version = version_from_generation(pipette_name_tuple)
pipette_type = PipetteModelType[split_pipette_name[0]]
-
- return PipetteModelVersionType(pipette_type, channels, version)
+ oem = PipetteOEMType.get_oem_from_model_str(name)
+ return PipetteModelVersionType(pipette_type, channels, version, oem)
def convert_pipette_model(
@@ -304,12 +304,17 @@ def convert_pipette_model(
pipette_type, parsed_channels, parsed_oem, parsed_version = exploded
channels = channels_from_string(f"{parsed_channels}_{parsed_oem}")
version = version_from_string(parsed_version)
+ oem = PipetteOEMType.get_oem_from_model_str(str(model))
elif model and provided_version:
pipette_type, parsed_channels = model.split("_")
channels = channels_from_string(parsed_channels)
version = version_from_string(provided_version)
+ oem = PipetteOEMType.get_oem_from_model_str(str(model))
else:
pipette_type = DEFAULT_MODEL.value
channels = DEFAULT_CHANNELS
version = DEFAULT_MODEL_VERSION
- return PipetteModelVersionType(PipetteModelType[pipette_type], channels, version)
+ oem = DEFAULT_OEM
+ return PipetteModelVersionType(
+ PipetteModelType[pipette_type], channels, version, oem
+ )
diff --git a/shared-data/python/opentrons_shared_data/pipette/types.py b/shared-data/python/opentrons_shared_data/pipette/types.py
index 685dae89957..93bfe2c0593 100644
--- a/shared-data/python/opentrons_shared_data/pipette/types.py
+++ b/shared-data/python/opentrons_shared_data/pipette/types.py
@@ -49,7 +49,6 @@ def check_and_return_type(
class PipetteChannelType(int, enum.Enum):
SINGLE_CHANNEL = 1
EIGHT_CHANNEL = 8
- EIGHT_CHANNEL_EM = 82
NINETY_SIX_CHANNEL = 96
def __str__(self) -> str:
@@ -57,8 +56,6 @@ def __str__(self) -> str:
return "96"
elif self.value == 8:
return "multi"
- elif self.value == 82:
- return "multi_em"
else:
return "single"
@@ -115,6 +112,21 @@ class Quirks(enum.Enum):
highSpeed = "highSpeed"
+class PipetteOEMType(enum.Enum):
+ OT = "ot" # opentrons type
+ EM = "em" # Emulsifying Pipette
+
+ @classmethod
+ def get_oem_from_quirks(cls, quirks: List[Quirks]) -> "PipetteOEMType":
+ """Return an oem type if true based on the quirks."""
+ return cls.EM if Quirks.highSpeed in quirks else cls.OT
+
+ @classmethod
+ def get_oem_from_model_str(cls, model_str: str) -> "PipetteOEMType":
+ """Return an oem type if true based on the model string."""
+ return cls.EM if "multi_em" in model_str else cls.OT
+
+
class AvailableUnits(enum.Enum):
mm = "mm"
amps = "amps"
@@ -220,7 +232,7 @@ def dict_for_encode(self) -> bool:
"p1000_single_gen2",
"p1000_single_flex",
"p1000_multi_flex",
- "p1000_multi_em",
+ "p1000_multi_em_flex",
"p1000_96",
"p200_96",
]
@@ -247,7 +259,7 @@ class PipetteNameType(str, enum.Enum):
P1000_SINGLE_GEN2 = "p1000_single_gen2"
P1000_SINGLE_FLEX = "p1000_single_flex"
P1000_MULTI_FLEX = "p1000_multi_flex"
- P1000_MULTI_EM = "p1000_multi_em"
+ P1000_MULTI_EM = "p1000_multi_em_flex"
P1000_96 = "p1000_96"
P200_96 = "p200_96"
diff --git a/shared-data/python/tests/pipette/test_load_data.py b/shared-data/python/tests/pipette/test_load_data.py
index 386af05de5b..78b7ac74c5b 100644
--- a/shared-data/python/tests/pipette/test_load_data.py
+++ b/shared-data/python/tests/pipette/test_load_data.py
@@ -9,6 +9,7 @@
PipetteChannelType,
PipetteModelType,
PipetteVersionType,
+ PipetteOEMType,
PipetteTipType,
Quirks,
LiquidClasses,
@@ -20,6 +21,7 @@ def test_load_pipette_definition() -> None:
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteVersionType(major=3, minor=3),
+ PipetteOEMType.OT,
)
assert pipette_config_one.channels == 1
@@ -38,6 +40,7 @@ def test_load_pipette_definition() -> None:
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteVersionType(major=1, minor=0),
+ PipetteOEMType.OT,
)
assert pipette_config_two.channels == 1
@@ -83,7 +86,10 @@ def test_update_pipette_configuration(
cast(types.PipetteModel, pipette_model)
)
base_configurations = load_data.load_definition(
- model_name.pipette_type, model_name.pipette_channels, model_name.pipette_version
+ model_name.pipette_type,
+ model_name.pipette_channels,
+ model_name.pipette_version,
+ model_name.oem_type,
)
updated_configurations = load_data.update_pipette_configuration(
diff --git a/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py b/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py
index aae0c1a4e1b..e0797ddec08 100644
--- a/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py
+++ b/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py
@@ -75,6 +75,7 @@ def test_max_flow_rates_per_volume(pipette: PipetteModel, action: str) -> None:
pipette_model_version.pipette_type,
pipette_model_version.pipette_channels,
pipette_model_version.pipette_version,
+ pipette_model_version.oem_type,
)
pipette_model_version_str = f"{pipette_model_version}"
diff --git a/shared-data/python/tests/pipette/test_mutable_configurations.py b/shared-data/python/tests/pipette/test_mutable_configurations.py
index d7a6c8ed1db..322682360c8 100644
--- a/shared-data/python/tests/pipette/test_mutable_configurations.py
+++ b/shared-data/python/tests/pipette/test_mutable_configurations.py
@@ -76,6 +76,7 @@ def test_load_old_overrides_regression(
pipette_type=types.PipetteModelType.p20,
pipette_channels=types.PipetteChannelType.SINGLE_CHANNEL,
pipette_version=types.PipetteVersionType(2, 2),
+ oem_type=types.PipetteOEMType.OT,
),
override_configuration_path,
"P20SV222021040709",
@@ -269,6 +270,7 @@ def test_load_with_overrides(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
+ pipette_model.oem_type,
)
if serial_number == TEST_SERIAL_NUMBER:
@@ -454,7 +456,9 @@ def test_loading_does_not_log_warnings(
load_with_mutable_configurations() suppresses and logs internal exceptions to
protect its caller, but those are still bugs, and we still want tests to catch them.
"""
- model = pipette_definition.PipetteModelVersionType(type, channels, version)
+ model = pipette_definition.PipetteModelVersionType(
+ type, channels, version, types.PipetteOEMType.OT
+ )
(override_configuration_path / filename).write_text(file_contents)
with caplog.at_level(logging.WARNING):
mutable_configurations.load_with_mutable_configurations(
diff --git a/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py b/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py
index 6e792560e9c..fdd9ec434cd 100644
--- a/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py
+++ b/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py
@@ -6,6 +6,7 @@
PipetteModelType,
PipetteVersionType,
PipetteGenerationType,
+ PipetteOEMType,
)
from opentrons_shared_data.pipette.types import PipetteModel, PipetteName
from opentrons_shared_data.pipette import (
@@ -23,6 +24,7 @@
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteVersionType(2, 0),
+ PipetteOEMType.OT,
),
],
[
@@ -31,6 +33,7 @@
PipetteModelType.p1000,
PipetteChannelType.EIGHT_CHANNEL,
PipetteVersionType(1, 0),
+ PipetteOEMType.OT,
),
],
[
@@ -39,6 +42,7 @@
PipetteModelType.p1000,
PipetteChannelType.NINETY_SIX_CHANNEL,
PipetteVersionType(1, 0),
+ PipetteOEMType.OT,
),
],
],
@@ -59,6 +63,7 @@ def test_convert_pipette_model(
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteVersionType(2, 0),
+ PipetteOEMType.OT,
),
],
[
@@ -68,6 +73,7 @@ def test_convert_pipette_model(
PipetteModelType.p1000,
PipetteChannelType.EIGHT_CHANNEL,
PipetteVersionType(3, 3),
+ PipetteOEMType.OT,
),
],
[
@@ -77,6 +83,7 @@ def test_convert_pipette_model(
PipetteModelType.p1000,
PipetteChannelType.NINETY_SIX_CHANNEL,
PipetteVersionType(1, 1),
+ PipetteOEMType.OT,
),
],
],
@@ -96,6 +103,7 @@ def test_convert_pipette_model_provided_version(
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteVersionType(2, 0),
+ PipetteOEMType.OT,
),
],
[
@@ -104,6 +112,7 @@ def test_convert_pipette_model_provided_version(
PipetteModelType.p1000,
PipetteChannelType.EIGHT_CHANNEL,
PipetteVersionType(3, 5),
+ PipetteOEMType.OT,
),
],
[
@@ -112,6 +121,7 @@ def test_convert_pipette_model_provided_version(
PipetteModelType.p1000,
PipetteChannelType.NINETY_SIX_CHANNEL,
PipetteVersionType(3, 6),
+ PipetteOEMType.OT,
),
],
],
@@ -123,19 +133,21 @@ def test_convert_pipette_name(
@pytest.mark.parametrize(
- argnames=["model_type", "channels", "generation", "output"],
+ argnames=["model_type", "channels", "generation", "output", "oem"],
argvalues=[
[
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteGenerationType.GEN2,
"p50_single_gen2",
+ PipetteOEMType.OT,
],
[
PipetteModelType.p1000,
PipetteChannelType.EIGHT_CHANNEL,
PipetteGenerationType.GEN2,
"p1000_multi_gen2",
+ PipetteOEMType.OT,
],
[
# 96 channel has a unique "name" right now
@@ -143,6 +155,7 @@ def test_convert_pipette_name(
PipetteChannelType.NINETY_SIX_CHANNEL,
PipetteGenerationType.FLEX,
"p1000_96",
+ PipetteOEMType.OT,
],
],
)
@@ -151,35 +164,40 @@ def test_model_version_type_string_version(
channels: PipetteChannelType,
generation: PipetteGenerationType,
output: PipetteName,
+ oem: PipetteOEMType,
) -> None:
data = pc.PipetteNameType(
pipette_type=model_type,
pipette_channels=channels,
pipette_generation=generation,
+ oem_type=oem,
)
assert output == str(data)
@pytest.mark.parametrize(
- argnames=["model_type", "channels", "version", "output"],
+ argnames=["model_type", "channels", "version", "output", "oem"],
argvalues=[
[
PipetteModelType.p50,
PipetteChannelType.SINGLE_CHANNEL,
PipetteVersionType(1, 0),
"p50_single_v1",
+ PipetteOEMType.OT,
],
[
PipetteModelType.p1000,
PipetteChannelType.EIGHT_CHANNEL,
PipetteVersionType(2, 1),
"p1000_multi_v2.1",
+ PipetteOEMType.OT,
],
[
PipetteModelType.p1000,
PipetteChannelType.NINETY_SIX_CHANNEL,
PipetteVersionType(3, 3),
"p1000_96_v3.3",
+ PipetteOEMType.OT,
],
],
)
@@ -188,9 +206,13 @@ def test_name_type_string_generation(
channels: PipetteChannelType,
version: PipetteVersionType,
output: PipetteModel,
+ oem: PipetteOEMType,
) -> None:
data = pc.PipetteModelVersionType(
- pipette_type=model_type, pipette_channels=channels, pipette_version=version
+ pipette_type=model_type,
+ pipette_channels=channels,
+ pipette_version=version,
+ oem_type=oem,
)
assert output == str(data)
diff --git a/shared-data/python/tests/pipette/test_validate_schema.py b/shared-data/python/tests/pipette/test_validate_schema.py
index a002c38cfb2..cf1d35288cb 100644
--- a/shared-data/python/tests/pipette/test_validate_schema.py
+++ b/shared-data/python/tests/pipette/test_validate_schema.py
@@ -45,6 +45,7 @@ def test_check_all_models_are_valid() -> None:
model_version.pipette_type,
model_version.pipette_channels,
model_version.pipette_version,
+ model_version.oem_type,
)
except json.JSONDecodeError:
print(
@@ -81,11 +82,13 @@ def test_pick_up_configs_configuration_by_nozzle_map_keys() -> None:
model_version.pipette_type,
model_version.pipette_channels,
model_version.pipette_version,
+ model_version.oem_type,
)
valid_nozzle_maps = load_valid_nozzle_maps(
model_version.pipette_type,
model_version.pipette_channels,
model_version.pipette_version,
+ model_version.oem_type,
)
pipette_maps = list(
@@ -123,11 +126,13 @@ def test_pick_up_configs_configuration_ordered_from_smallest_to_largest() -> Non
model_version.pipette_type,
model_version.pipette_channels,
model_version.pipette_version,
+ model_version.oem_type,
)
valid_nozzle_maps = load_valid_nozzle_maps(
model_version.pipette_type,
model_version.pipette_channels,
model_version.pipette_version,
+ model_version.oem_type,
)
map_keys = list(valid_nozzle_maps.maps.keys())
@@ -154,6 +159,7 @@ def test_serializer() -> None:
types.PipetteModelType.p1000,
types.PipetteChannelType.NINETY_SIX_CHANNEL,
types.PipetteVersionType(3, 3),
+ types.PipetteOEMType.OT,
)
quirk_0 = types.Quirks.pickupTipShake
quirk_1 = types.Quirks.dropTipShake
From a80aa0052c83cb46e9ba626e9df15a9449c924e5 Mon Sep 17 00:00:00 2001
From: Ryan Howard
Date: Fri, 20 Dec 2024 11:40:04 -0500
Subject: [PATCH 06/18] fix(api): missed one link in the chain of loading
config values (#17154)
# Overview
Data was loading in shared data but I didn't realize that the
pipette_handler was truncating the settings.
## Test Plan and Hands on Testing
## Changelog
## Review requests
## Risk assessment
---
.../hardware_control/instruments/ot3/pipette_handler.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py b/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py
index 8e4975b3b5b..ef081b95a62 100644
--- a/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py
+++ b/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py
@@ -237,6 +237,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
"back_compat_names",
"supported_tips",
"lld_settings",
+ "available_sensors",
]
instr_dict = instr.as_dict()
@@ -289,6 +290,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
"drop_tip": instr.plunger_positions.drop_tip,
}
result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm
+ result["available_sensors"] = instr.config.available_sensors
return cast(PipetteDict, result)
@property
From 04dea6c803bcef7424e6bc59bea8d509e1f59e0b Mon Sep 17 00:00:00 2001
From: koji
Date: Fri, 20 Dec 2024 11:41:43 -0500
Subject: [PATCH 07/18] chore: update vitest and vitest-when (#17149)
* chore: update vitest and vitest-when
---
.../RobotUpdateProgressModal.test.tsx | 5 +-
package.json | 6 +-
.../src/__tests__/persist.test.ts | 8 +-
setup-vitest.ts => setup-vitest.mts | 0
vitest.config.ts => vitest.config.mts | 7 +-
yarn.lock | 463 +++++++++---------
6 files changed, 237 insertions(+), 252 deletions(-)
rename setup-vitest.ts => setup-vitest.mts (100%)
rename vitest.config.ts => vitest.config.mts (90%)
diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx
index a2139f636bd..176388a5527 100644
--- a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx
+++ b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx
@@ -52,6 +52,7 @@ describe('DownloadUpdateModal', () => {
let props: React.ComponentProps
const mockCreateLiveCommand = vi.fn()
+ const mockDispatchStartRobotUpdate = vi.fn()
beforeEach(() => {
mockCreateLiveCommand.mockResolvedValue(null)
@@ -68,7 +69,9 @@ describe('DownloadUpdateModal', () => {
progressPercent: 50,
})
vi.mocked(getRobotSessionIsManualFile).mockReturnValue(false)
- vi.mocked(useDispatchStartRobotUpdate).mockReturnValue(vi.fn)
+ vi.mocked(useDispatchStartRobotUpdate).mockReturnValue(
+ mockDispatchStartRobotUpdate
+ )
vi.mocked(getRobotUpdateDownloadError).mockReturnValue(null)
})
diff --git a/package.json b/package.json
index 17ee30cc3e4..654f12137fc 100755
--- a/package.json
+++ b/package.json
@@ -69,7 +69,7 @@
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"@vitejs/plugin-react": "4.2.0",
- "@vitest/coverage-v8": "1.3.0",
+ "@vitest/coverage-v8": "2.1.8",
"ajv": "6.12.3",
"aws-sdk": "^2.493.0",
"babel-loader": "^8.2.2",
@@ -151,8 +151,8 @@
"typescript": "5.3.3",
"url-loader": "^2.1.0",
"vite": "5.3.2",
- "vitest": "1.2.2",
- "vitest-when": "0.3.1",
+ "vitest": "2.1.8",
+ "vitest-when": "0.5.0",
"wait-on": "^4.0.2",
"webpack": "^4.41.6",
"webpack-bundle-analyzer": "^3.6.0",
diff --git a/protocol-designer/src/__tests__/persist.test.ts b/protocol-designer/src/__tests__/persist.test.ts
index b6683979c6f..4b396924b8b 100644
--- a/protocol-designer/src/__tests__/persist.test.ts
+++ b/protocol-designer/src/__tests__/persist.test.ts
@@ -4,12 +4,14 @@ import type { MockInstance } from 'vitest'
import * as persist from '../persist'
describe('persist', () => {
- let getItemSpy: MockInstance
- let setItemSpy: MockInstance
+ let getItemSpy: MockInstance<(key: string) => string | null>
+ let setItemSpy: MockInstance<(key: string, value: string) => void>
beforeEach(() => {
const LocalStorageProto = Object.getPrototypeOf(global.localStorage)
- getItemSpy = vi.spyOn(LocalStorageProto, 'getItem')
+ getItemSpy = vi.spyOn(LocalStorageProto, 'getItem') as MockInstance<
+ (key: string) => string | null
+ >
setItemSpy = vi.spyOn(LocalStorageProto, 'setItem')
})
diff --git a/setup-vitest.ts b/setup-vitest.mts
similarity index 100%
rename from setup-vitest.ts
rename to setup-vitest.mts
diff --git a/vitest.config.ts b/vitest.config.mts
similarity index 90%
rename from vitest.config.ts
rename to vitest.config.mts
index a485e7536bd..1412fdcee4f 100644
--- a/vitest.config.ts
+++ b/vitest.config.mts
@@ -13,7 +13,12 @@ export default mergeConfig(
environment: 'jsdom',
allowOnly: true,
exclude: [...configDefaults.exclude, '**/node_modules/**', '**/dist/**'],
- setupFiles: ['./setup-vitest.ts'],
+ setupFiles: ['./setup-vitest.mts'],
+ coverage: {
+ exclude: ['**/node_modules/**', '**/dist/**', '**/__tests__/**'],
+ provider: 'v8',
+ reporter: ['text', 'json', 'html'],
+ },
},
resolve: {
alias: {
diff --git a/yarn.lock b/yarn.lock
index ee61c887c8e..56ebcbf301a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -22,7 +22,7 @@
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff"
integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==
-"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.2.1":
+"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
@@ -1122,7 +1122,7 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88"
integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==
-"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3":
+"@babel/parser@^7.25.4", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234"
integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==
@@ -1936,7 +1936,7 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
-"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3":
+"@babel/types@^7.25.4", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0"
integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==
@@ -3293,7 +3293,12 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
-"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+"@jridgewell/sourcemap-codec@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+ integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
version "0.3.25"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
@@ -5690,7 +5695,7 @@
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
-"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
@@ -6285,68 +6290,82 @@
magic-string "^0.27.0"
react-refresh "^0.14.0"
-"@vitest/coverage-v8@1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.3.0.tgz#31f98b1bad1d5e9db733a4c1ae8d46dec549cd3c"
- integrity sha512-e5Y5uK5NNoQMQaNitGQQjo9FoA5ZNcu7Bn6pH+dxUf48u6po1cX38kFBYUHZ9GNVkF4JLbncE0WeWwTw+nLrxg==
+"@vitest/coverage-v8@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz#738527e6e79cef5004248452527e272e0df12284"
+ integrity sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==
dependencies:
- "@ampproject/remapping" "^2.2.1"
+ "@ampproject/remapping" "^2.3.0"
"@bcoe/v8-coverage" "^0.2.3"
- debug "^4.3.4"
+ debug "^4.3.7"
istanbul-lib-coverage "^3.2.2"
istanbul-lib-report "^3.0.1"
- istanbul-lib-source-maps "^4.0.1"
- istanbul-reports "^3.1.6"
- magic-string "^0.30.5"
- magicast "^0.3.3"
- picocolors "^1.0.0"
- std-env "^3.5.0"
- test-exclude "^6.0.0"
- v8-to-istanbul "^9.2.0"
+ istanbul-lib-source-maps "^5.0.6"
+ istanbul-reports "^3.1.7"
+ magic-string "^0.30.12"
+ magicast "^0.3.5"
+ std-env "^3.8.0"
+ test-exclude "^7.0.1"
+ tinyrainbow "^1.2.0"
+
+"@vitest/expect@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.8.tgz#13fad0e8d5a0bf0feb675dcf1d1f1a36a1773bc1"
+ integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==
+ dependencies:
+ "@vitest/spy" "2.1.8"
+ "@vitest/utils" "2.1.8"
+ chai "^5.1.2"
+ tinyrainbow "^1.2.0"
-"@vitest/expect@1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.2.2.tgz#39ea22e849bbf404b7e5272786551aa99e2663d0"
- integrity sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg==
+"@vitest/mocker@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.8.tgz#51dec42ac244e949d20009249e033e274e323f73"
+ integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==
dependencies:
- "@vitest/spy" "1.2.2"
- "@vitest/utils" "1.2.2"
- chai "^4.3.10"
+ "@vitest/spy" "2.1.8"
+ estree-walker "^3.0.3"
+ magic-string "^0.30.12"
-"@vitest/runner@1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.2.2.tgz#8b060a56ecf8b3d607b044d79f5f50d3cd9fee2f"
- integrity sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg==
+"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.8.tgz#88f47726e5d0cf4ba873d50c135b02e4395e2bca"
+ integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==
dependencies:
- "@vitest/utils" "1.2.2"
- p-limit "^5.0.0"
- pathe "^1.1.1"
+ tinyrainbow "^1.2.0"
-"@vitest/snapshot@1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.2.2.tgz#f56fd575569774968f3eeba9382a166c26201042"
- integrity sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==
+"@vitest/runner@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.8.tgz#b0e2dd29ca49c25e9323ea2a45a5125d8729759f"
+ integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==
dependencies:
- magic-string "^0.30.5"
- pathe "^1.1.1"
- pretty-format "^29.7.0"
+ "@vitest/utils" "2.1.8"
+ pathe "^1.1.2"
-"@vitest/spy@1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.2.2.tgz#8fc2aeccb96cecbbdd192c643729bd5f97a01c86"
- integrity sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==
+"@vitest/snapshot@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.8.tgz#d5dc204f4b95dc8b5e468b455dfc99000047d2de"
+ integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==
dependencies:
- tinyspy "^2.2.0"
+ "@vitest/pretty-format" "2.1.8"
+ magic-string "^0.30.12"
+ pathe "^1.1.2"
-"@vitest/utils@1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.2.2.tgz#94b5a1bd8745ac28cf220a99a8719efea1bcfc83"
- integrity sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==
+"@vitest/spy@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.8.tgz#bc41af3e1e6a41ae3b67e51f09724136b88fa447"
+ integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==
dependencies:
- diff-sequences "^29.6.3"
- estree-walker "^3.0.3"
- loupe "^2.3.7"
- pretty-format "^29.7.0"
+ tinyspy "^3.0.2"
+
+"@vitest/utils@2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.8.tgz#f8ef85525f3362ebd37fd25d268745108d6ae388"
+ integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==
+ dependencies:
+ "@vitest/pretty-format" "2.1.8"
+ loupe "^3.1.2"
+ tinyrainbow "^1.2.0"
"@vituum/vite-plugin-postcss@1.1.0":
version "1.1.0"
@@ -6595,11 +6614,6 @@ acorn-walk@^7.1.1, acorn-walk@^7.2.0:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
-acorn-walk@^8.3.2:
- version "8.3.2"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa"
- integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==
-
acorn@^6.4.1:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
@@ -6610,7 +6624,7 @@ acorn@^7.1.1, acorn@^7.4.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-acorn@^8.10.0, acorn@^8.11.3, acorn@^8.2.4, acorn@^8.8.2, acorn@^8.9.0:
+acorn@^8.11.3, acorn@^8.2.4, acorn@^8.8.2, acorn@^8.9.0:
version "8.11.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
@@ -7138,10 +7152,10 @@ assert@^2.1.0:
object.assign "^4.1.4"
util "^0.12.5"
-assertion-error@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
- integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
+assertion-error@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
+ integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
assign-symbols@^1.0.0:
version "1.0.0"
@@ -8200,18 +8214,16 @@ ccount@^2.0.0:
resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
-chai@^4.3.10:
- version "4.4.1"
- resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1"
- integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==
+chai@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
+ integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
dependencies:
- assertion-error "^1.1.0"
- check-error "^1.0.3"
- deep-eql "^4.1.3"
- get-func-name "^2.0.2"
- loupe "^2.3.6"
- pathval "^1.1.1"
- type-detect "^4.0.8"
+ assertion-error "^2.0.1"
+ check-error "^2.1.1"
+ deep-eql "^5.0.1"
+ loupe "^3.1.0"
+ pathval "^2.0.0"
chalk@^1.1.3:
version "1.1.3"
@@ -8289,12 +8301,10 @@ character-reference-invalid@^2.0.0:
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
-check-error@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
- integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
- dependencies:
- get-func-name "^2.0.2"
+check-error@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
+ integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
check-more-types@^2.24.0:
version "2.24.0"
@@ -8765,11 +8775,6 @@ conf@^6.2.1:
semver "^6.2.0"
write-file-atomic "^3.0.0"
-confbox@^0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579"
- integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==
-
config-file-ts@0.2.8-rc1:
version "0.2.8-rc1"
resolved "https://registry.yarnpkg.com/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz#fb7fc6ccb2e313f69dbeb78f1db0b00038049de0"
@@ -9626,6 +9631,13 @@ debug@^3.1.0, debug@^3.2.7:
dependencies:
ms "^2.1.1"
+debug@^4.3.7:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
+ integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+ dependencies:
+ ms "^2.1.3"
+
decamelize-keys@^1.0.0, decamelize-keys@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8"
@@ -9742,12 +9754,10 @@ dedent@^0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
-deep-eql@^4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
- integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
- dependencies:
- type-detect "^4.0.0"
+deep-eql@^5.0.1:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
+ integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
deep-equal@^1.0.1:
version "1.1.2"
@@ -10099,11 +10109,6 @@ dicer@0.2.5:
readable-stream "1.1.x"
streamsearch "0.1.2"
-diff-sequences@^29.6.3:
- version "29.6.3"
- resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
- integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
-
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -10826,6 +10831,11 @@ es-module-lexer@^0.9.3:
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
+es-module-lexer@^1.5.4:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
+ integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
+
es-object-atoms@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
@@ -11524,6 +11534,11 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
+expect-type@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75"
+ integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==
+
exponential-backoff@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
@@ -12379,11 +12394,6 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
-get-func-name@^2.0.1, get-func-name@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
- integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
-
get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
@@ -12629,7 +12639,7 @@ glob@^10.0.0:
minipass "^7.0.4"
path-scurry "^1.10.2"
-glob@^10.3.12:
+glob@^10.3.12, glob@^10.4.1:
version "10.4.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
@@ -14463,16 +14473,16 @@ istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
make-dir "^4.0.0"
supports-color "^7.1.0"
-istanbul-lib-source-maps@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
- integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
+istanbul-lib-source-maps@^5.0.6:
+ version "5.0.6"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441"
+ integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==
dependencies:
+ "@jridgewell/trace-mapping" "^0.3.23"
debug "^4.1.1"
istanbul-lib-coverage "^3.0.0"
- source-map "^0.6.1"
-istanbul-reports@^3.1.6:
+istanbul-reports@^3.1.7:
version "3.1.7"
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b"
integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==
@@ -15050,14 +15060,6 @@ loader-utils@^2.0.0:
emojis-list "^3.0.0"
json5 "^2.1.2"
-local-pkg@^0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c"
- integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==
- dependencies:
- mlly "^1.4.2"
- pkg-types "^1.0.3"
-
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -15240,12 +15242,10 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
-loupe@^2.3.6, loupe@^2.3.7:
- version "2.3.7"
- resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
- integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==
- dependencies:
- get-func-name "^2.0.1"
+loupe@^3.1.0, loupe@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
+ integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
lower-case@^1.1.1:
version "1.1.4"
@@ -15338,20 +15338,27 @@ magic-string@^0.27.0:
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13"
-magic-string@^0.30.0, magic-string@^0.30.5:
+magic-string@^0.30.0:
version "0.30.10"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e"
integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
-magicast@^0.3.3:
- version "0.3.4"
- resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.4.tgz#bbda1791d03190a24b00ff3dd18151e7fd381d19"
- integrity sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==
+magic-string@^0.30.12:
+ version "0.30.17"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
+ integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
dependencies:
- "@babel/parser" "^7.24.4"
- "@babel/types" "^7.24.0"
+ "@jridgewell/sourcemap-codec" "^1.5.0"
+
+magicast@^0.3.5:
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739"
+ integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==
+ dependencies:
+ "@babel/parser" "^7.25.4"
+ "@babel/types" "^7.25.4"
source-map-js "^1.2.0"
make-dir@^1.0.0:
@@ -16263,16 +16270,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-mlly@^1.4.2, mlly@^1.6.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.6.1.tgz#0983067dc3366d6314fc5e12712884e6978d028f"
- integrity sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==
- dependencies:
- acorn "^8.11.3"
- pathe "^1.1.2"
- pkg-types "^1.0.3"
- ufo "^1.3.2"
-
mnemonist@0.38.3:
version "0.38.3"
resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d"
@@ -16369,7 +16366,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-ms@2.1.3, ms@^2.0.0, ms@^2.1.1:
+ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -17098,13 +17095,6 @@ p-limit@^3.0.2, "p-limit@^3.1.0 ":
dependencies:
yocto-queue "^0.1.0"
-p-limit@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985"
- integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==
- dependencies:
- yocto-queue "^1.0.0"
-
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -17418,15 +17408,15 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-pathe@^1.1.1, pathe@^1.1.2:
+pathe@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
-pathval@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
- integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
+pathval@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
+ integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
pbkdf2@^3.0.3, pbkdf2@^3.1.2:
version "3.1.2"
@@ -17541,15 +17531,6 @@ pkg-dir@^5.0.0:
dependencies:
find-up "^5.0.0"
-pkg-types@^1.0.3:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.1.0.tgz#3ec1bf33379030fd0a34c227b6c650e8ea7ca271"
- integrity sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==
- dependencies:
- confbox "^0.1.7"
- mlly "^1.6.1"
- pathe "^1.1.2"
-
pkg-up@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
@@ -20790,10 +20771,10 @@ statuses@~1.4.0:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
-std-env@^3.5.0:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
- integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
+std-env@^3.8.0:
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
+ integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
stdopt@^2.0.0:
version "2.2.0"
@@ -21106,13 +21087,6 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
-strip-literal@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.3.0.tgz#db3942c2ec1699e6836ad230090b84bb458e3a07"
- integrity sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==
- dependencies:
- acorn "^8.10.0"
-
strip-outer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631"
@@ -21517,6 +21491,15 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
+test-exclude@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2"
+ integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==
+ dependencies:
+ "@istanbuljs/schema" "^0.1.2"
+ glob "^10.4.1"
+ minimatch "^9.0.4"
+
text-extensions@^1.0.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
@@ -21599,25 +21582,35 @@ tiny-warning@^1.0.2:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
-tinybench@^2.5.1:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.8.0.tgz#30e19ae3a27508ee18273ffed9ac7018949acd7b"
- integrity sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==
+tinybench@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
+ integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
tinycolor2@^1.4.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
-tinypool@^0.8.2:
- version "0.8.4"
- resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8"
- integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==
+tinyexec@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
+ integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
-tinyspy@^2.2.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1"
- integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==
+tinypool@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2"
+ integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==
+
+tinyrainbow@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
+ integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==
+
+tinyspy@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
+ integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
tmp-promise@^3.0.2:
version "3.0.3"
@@ -21878,11 +21871,6 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
-type-detect@^4.0.0, type-detect@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
- integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
-
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
@@ -22022,7 +22010,7 @@ ua-parser-js@^0.7.23, ua-parser-js@^0.7.30:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832"
integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==
-ufo@^1.3.2, ufo@^1.4.0:
+ufo@^1.4.0:
version "1.5.3"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344"
integrity sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==
@@ -22613,15 +22601,6 @@ v8-compile-cache@^2.1.0, v8-compile-cache@^2.1.1:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128"
integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==
-v8-to-istanbul@^9.2.0:
- version "9.2.0"
- resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad"
- integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==
- dependencies:
- "@jridgewell/trace-mapping" "^0.3.12"
- "@types/istanbul-lib-coverage" "^2.0.1"
- convert-source-map "^2.0.0"
-
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -22720,15 +22699,15 @@ vfile@^6.0.0:
unist-util-stringify-position "^4.0.0"
vfile-message "^4.0.0"
-vite-node@1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.2.2.tgz#f6d329b06f9032130ae6eac1dc773f3663903c25"
- integrity sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg==
+vite-node@2.1.8:
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5"
+ integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==
dependencies:
cac "^6.7.14"
- debug "^4.3.4"
- pathe "^1.1.1"
- picocolors "^1.0.0"
+ debug "^4.3.7"
+ es-module-lexer "^1.5.4"
+ pathe "^1.1.2"
vite "^5.0.0"
vite@5.3.2:
@@ -22753,37 +22732,38 @@ vite@^5.0, vite@^5.0.0:
optionalDependencies:
fsevents "~2.3.3"
-vitest-when@0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/vitest-when/-/vitest-when-0.3.1.tgz#72db1c0a8e76fae81f8fc21c6da3c769f8e7f8bb"
- integrity sha512-qZt4VmuvGtkLEqUpq5AJHQtdfhU8wJH+eXHk+WBo8kFT5zdfVV06+vFgYzvuSOq73srlCEsJ4VJqX7uBtOwWLg==
+vitest-when@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/vitest-when/-/vitest-when-0.5.0.tgz#1cef4713a71c30af741964df780e50485177eaf0"
+ integrity sha512-BYDfzSawgKsV5GX3bU9ZbURuljjBCqi5KPtE2hBn/DsCRThU0z4qH0PAhJGemyKNnR01ADObXkmm1UPDHGzVUw==
+ dependencies:
+ pretty-format "^29.7.0"
-vitest@1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.2.2.tgz#9e29ad2a74a5df553c30c5798c57a062d58ce299"
- integrity sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==
- dependencies:
- "@vitest/expect" "1.2.2"
- "@vitest/runner" "1.2.2"
- "@vitest/snapshot" "1.2.2"
- "@vitest/spy" "1.2.2"
- "@vitest/utils" "1.2.2"
- acorn-walk "^8.3.2"
- cac "^6.7.14"
- chai "^4.3.10"
- debug "^4.3.4"
- execa "^8.0.1"
- local-pkg "^0.5.0"
- magic-string "^0.30.5"
- pathe "^1.1.1"
- picocolors "^1.0.0"
- std-env "^3.5.0"
- strip-literal "^1.3.0"
- tinybench "^2.5.1"
- tinypool "^0.8.2"
+vitest@^2.1.8:
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa"
+ integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==
+ dependencies:
+ "@vitest/expect" "2.1.8"
+ "@vitest/mocker" "2.1.8"
+ "@vitest/pretty-format" "^2.1.8"
+ "@vitest/runner" "2.1.8"
+ "@vitest/snapshot" "2.1.8"
+ "@vitest/spy" "2.1.8"
+ "@vitest/utils" "2.1.8"
+ chai "^5.1.2"
+ debug "^4.3.7"
+ expect-type "^1.1.0"
+ magic-string "^0.30.12"
+ pathe "^1.1.2"
+ std-env "^3.8.0"
+ tinybench "^2.9.0"
+ tinyexec "^0.3.1"
+ tinypool "^1.0.1"
+ tinyrainbow "^1.2.0"
vite "^5.0.0"
- vite-node "1.2.2"
- why-is-node-running "^2.2.2"
+ vite-node "2.1.8"
+ why-is-node-running "^2.3.0"
vituum@^1.1:
version "1.1.0"
@@ -23217,10 +23197,10 @@ which@^2.0.1, which@^2.0.2:
dependencies:
isexe "^2.0.0"
-why-is-node-running@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e"
- integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==
+why-is-node-running@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
+ integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
dependencies:
siginfo "^2.0.0"
stackback "0.0.2"
@@ -23559,11 +23539,6 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
-yocto-queue@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
- integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
-
yup@0.32.9:
version "0.32.9"
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.9.tgz#9367bec6b1b0e39211ecbca598702e106019d872"
From e458d067f346eced69181e851e43f6142f670d9d Mon Sep 17 00:00:00 2001
From: Anthony Ngumah <68346382+AnthonyNASC20@users.noreply.github.com>
Date: Fri, 20 Dec 2024 13:23:58 -0500
Subject: [PATCH 08/18] feat(abr-testing): add module tests (#17127)
# Overview
Module testing script for running predefined tests on modules.
(Currently only has heater shaker test)
---------
Co-authored-by: rclarke0
---
.../abr_testing/tools/module_control.py | 138 ++++++++++++++++
abr-testing/abr_testing/tools/test_modules.py | 155 ++++++++++++++++++
2 files changed, 293 insertions(+)
create mode 100644 abr-testing/abr_testing/tools/module_control.py
create mode 100644 abr-testing/abr_testing/tools/test_modules.py
diff --git a/abr-testing/abr_testing/tools/module_control.py b/abr-testing/abr_testing/tools/module_control.py
new file mode 100644
index 00000000000..5bc1f5cfb1d
--- /dev/null
+++ b/abr-testing/abr_testing/tools/module_control.py
@@ -0,0 +1,138 @@
+"""Interface with opentrons modules!"""
+from serial import Serial # type: ignore[import-untyped]
+import asyncio
+import subprocess
+from typing import Any
+
+# Generic
+_READ_ALL = "readall"
+_READ_LINE = "read"
+_DONE = "done"
+
+# TC commands
+_MOVE_SEAL = "ms"
+_MOVE_LID = "ml"
+tc_gcode_shortcuts = {
+ "status": "M119",
+ _MOVE_SEAL: "M241.D", # move seal motor
+ _MOVE_LID: "M240.D", # move lid stepper motor
+ "ol": "M126", # open lid
+ "cl": "M127", # close lid
+ "sw": "M901.D", # status of all switches
+ "lt": "M141.D", # get lid temperature
+ "pt": "M105.D", # get plate temperature
+}
+
+# HS Commands
+hs_gcode_shortcuts = {
+ "srpm": "M3 S{rpm}", # Set RPM
+ "grpm": "M123", # Get RPM
+ "home": "G28", # Home
+ "deactivate": "M106", # Deactivate
+}
+
+gcode_shortcuts = tc_gcode_shortcuts | hs_gcode_shortcuts
+
+
+async def message_read(dev: Serial) -> Any:
+ """Read message."""
+ response = dev.readline().decode()
+ while not response:
+ await asyncio.sleep(1)
+ response = dev.readline().decode()
+ return response
+
+
+async def message_return(dev: Serial) -> Any:
+ """Wait until message becomes available."""
+ try:
+ response = await asyncio.wait_for(message_read(dev), timeout=30)
+ return response
+ except asyncio.exceptions.TimeoutError:
+ print("response timed out.")
+ return ""
+
+
+async def handle_module_gcode_shortcut(
+ dev: Serial, command: str, in_commands: bool, output: str = ""
+) -> None:
+ """Handle debugging commands that require followup."""
+ if in_commands:
+ if command == _MOVE_SEAL:
+ distance = input("enter distance in steps => ")
+ dev.write(
+ f"{gcode_shortcuts[command]} {distance}\n".encode()
+ ) # (+) -> retract, (-) -> engage
+ # print(await message_return(dev))
+ elif command == _MOVE_LID:
+ distance = input(
+ "enter angular distance in degrees => "
+ ) # (+) -> open, (-) -> close
+ dev.write(f"{gcode_shortcuts[command]} {distance}\n".encode())
+ # print(await message_return(dev))
+ # everything else
+ else:
+ dev.write(f"{gcode_shortcuts[command]}\n".encode())
+ else:
+ dev.write(f"{command}\n".encode())
+ try:
+ mr = await message_return(dev)
+ print(mr)
+ except TypeError:
+ print("Invalid input")
+ return
+
+ if output:
+ try:
+ with open(output, "a") as result_file:
+ if "OK" in mr:
+ status = command + ": SUCCESS"
+ else:
+ status = command + ": FAILURE"
+ result_file.write(status)
+ result_file.write(f" {mr}")
+ result_file.close()
+ except FileNotFoundError:
+ print(f"cannot open file: {output}")
+
+
+async def comms_loop(dev: Serial, commands: list, output: str = "") -> bool:
+ """Loop for commands."""
+ _exit = False
+ try:
+ command = commands.pop(0)
+ except IndexError:
+ command = input("\n>>> ")
+ if command == _READ_ALL:
+ print(dev.readlines())
+ elif command == _READ_LINE:
+ print(dev.readline())
+ elif command == _DONE:
+ _exit = True
+ elif command in gcode_shortcuts:
+ await handle_module_gcode_shortcut(dev, command, True, output)
+ else:
+ await handle_module_gcode_shortcut(dev, command, False, output)
+ return _exit
+
+
+async def _main(module: str, commands: list = [], output: str = "") -> bool:
+ """Main process."""
+ module_name = (
+ subprocess.check_output(["find", "/dev/", "-name", f"*{module}*"])
+ .decode()
+ .strip()
+ )
+ if not module_name:
+ print(f"{module} not found. Exiting.")
+ return False
+ dev = Serial(f"{module_name}", 9600, timeout=2)
+ _exit = False
+ while not _exit:
+ _exit = await comms_loop(dev, commands, output)
+ dev.close()
+ return True
+
+
+if __name__ == "__main__":
+ asyncio.run(_main("heatershaker"))
diff --git a/abr-testing/abr_testing/tools/test_modules.py b/abr-testing/abr_testing/tools/test_modules.py
new file mode 100644
index 00000000000..8c372fbff53
--- /dev/null
+++ b/abr-testing/abr_testing/tools/test_modules.py
@@ -0,0 +1,155 @@
+"""Modules Tests Script!"""
+import asyncio
+import time
+from datetime import datetime
+import os
+import module_control # type: ignore
+from typing import Any, Tuple, Dict
+import traceback
+
+# To run:
+# SSH into robot
+# cd /opt/opentrons-robot-server/abr-testing/tools
+# python3 test_modules.py
+
+
+async def tc_test_1(module: str, path_to_file: str) -> None:
+ """Thermocycler Test 1 Open and Close Lid."""
+ duration = int(input("How long to run this test for? (in seconds): "))
+ start = time.time()
+ while time.time() - start < duration:
+ try:
+ await (tc_open_lid(module, path_to_file))
+ except asyncio.TimeoutError:
+ return
+ time.sleep(5)
+ try:
+ await (tc_close_lid(module, path_to_file))
+ except asyncio.TimeoutError:
+ return
+ time.sleep(5)
+
+
+async def hs_test_1(module: str, path_to_file: str) -> None:
+ """Heater Shaker Test 1. (Home and Shake)."""
+ duration = int(input("How long to run this test for? (in seconds): "))
+ rpm = input("Target RPM (200-3000): ")
+ start = time.time()
+ while time.time() - start < duration:
+ try:
+ await (hs_test_home(module, path_to_file))
+ except asyncio.TimeoutError:
+ return
+ time.sleep(5)
+ try:
+ await (hs_test_set_shake(module, rpm, path_to_file))
+ except asyncio.TimeoutError:
+ return
+ time.sleep(10)
+ try:
+ await (hs_test_set_shake(module, "0", path_to_file))
+ except asyncio.TimeoutError:
+ return
+ time.sleep(10)
+
+
+async def input_codes(module: str, path_to_file: str) -> None:
+ """Opens serial for manual code input."""
+ await module_control._main(module, output=path_to_file)
+
+
+hs_tests: Dict[str, Tuple[Any, str]] = {
+ "Test 1": (hs_test_1, "Repeatedly home heater shaker then set shake speed"),
+ "Input GCodes": (input_codes, "Input g codes"),
+}
+
+tc_tests: Dict[str, Tuple[Any, str]] = {
+ "Test 1": (tc_test_1, "Repeatedly open and close TC lid"),
+ "Input GCodes": (input_codes, "Input g codes"),
+}
+
+global modules
+
+modules = {
+ "heatershaker": hs_tests,
+ "thermocycler": tc_tests,
+}
+
+
+async def main(module: str) -> None:
+ """Select test to be run."""
+ # Select test to run
+ # Set directory for tests
+ BASE_DIRECTORY = "/userfs/data/testing_data/"
+ if not os.path.exists(BASE_DIRECTORY):
+ os.makedirs(BASE_DIRECTORY)
+ tests = modules[module]
+ for i, test in enumerate(tests.keys()):
+ function, description = tests[test]
+ print(f"{i}) {test} : {description}")
+ selected_test = int(input("Please select a test: "))
+ try:
+ function, description = tests[list(tests.keys())[selected_test]]
+ test_dir = BASE_DIRECTORY + f"{module}/test/{list(tests.keys())[selected_test]}"
+ print(f"{i}, {description}")
+ print(f"TEST DIR: {test_dir}")
+ date = datetime.now()
+ filename = f"results_{datetime.strftime(date, '%Y-%m-%d_%H:%M:%S')}.txt"
+ output_file = os.path.join(test_dir, filename)
+ try:
+ if not os.path.exists(test_dir):
+ os.makedirs(test_dir)
+ open(output_file, "a").close()
+ except Exception:
+ traceback.print_exc()
+ print(f"PATH: {output_file} ")
+ await (function(module, output_file))
+ except Exception:
+ print("Failed to run test")
+ traceback.print_exc()
+
+
+# HS Test Functions
+async def hs_test_home(module: str, path_to_file: str) -> None:
+ """Home heater shaker."""
+ hs_gcodes = module_control.hs_gcode_shortcuts
+ home_gcode = hs_gcodes["home"]
+ await (module_control._main(module, [home_gcode, "done"], path_to_file))
+
+
+async def hs_test_set_shake(module: str, rpm: str, path_to_file: str) -> None:
+ """Shake heater shaker at specified speed."""
+ hs_gcodes = module_control.hs_gcode_shortcuts
+ set_shake_gcode = hs_gcodes["srpm"].format(rpm=rpm)
+ await (module_control._main(module, [set_shake_gcode, "done"], path_to_file))
+
+
+async def hs_deactivate(module: str, path_to_file: str) -> None:
+ """Deactivate Heater Shaker."""
+ hs_gcodes = module_control.hs_gcode_shortcuts
+ deactivate_gcode = hs_gcodes["deactivate"]
+ await (module_control._main(module, [deactivate_gcode, "done"], path_to_file))
+
+
+# TC Test Functions
+async def tc_open_lid(module: str, path_to_file: str) -> None:
+ """Open thermocycler lid."""
+ tc_gcodes = module_control.tc_gcode_shortcuts
+ open_lid_gcode = tc_gcodes["ol"]
+ await (module_control._main(module, [open_lid_gcode, "done"], path_to_file))
+
+
+async def tc_close_lid(module: str, path_to_file: str) -> None:
+ """Open thermocycler lid."""
+ tc_gcodes = module_control.tc_gcode_shortcuts
+ close_lid_gcode = tc_gcodes["cl"]
+ await (module_control._main(module, [close_lid_gcode, "done"], path_to_file))
+
+
+if __name__ == "__main__":
+ print("Modules:")
+ for i, module in enumerate(modules):
+ print(f"{i}) {module}")
+ module_int = int(input("Please select a module: "))
+ module = list(modules.keys())[module_int]
+ asyncio.run(main(module))
From 3162131ab33f004859c1a51eaf578d5f174596e4 Mon Sep 17 00:00:00 2001
From: Sarah Breen
Date: Fri, 20 Dec 2024 14:01:51 -0500
Subject: [PATCH 09/18] feat(app): add analytics for localization feature
(#17130)
fix EXEC-1028
---
.../SystemLanguagePreferenceModal.test.tsx | 47 +++++++++++++++++++
.../SystemLanguagePreferenceModal/index.tsx | 16 ++++++-
.../LanguageSetting.tsx | 20 +++++++-
.../__tests__/LanguageSetting.test.tsx | 14 ++++++
.../Desktop/AppSettings/GeneralSettings.tsx | 17 ++++++-
.../__test__/GeneralSettings.test.tsx | 14 ++++++
.../__tests__/ChooseLanguage.test.tsx | 21 ++++++++-
app/src/pages/ODD/ChooseLanguage/index.tsx | 9 ++++
.../useTrackProtocolRunEvent.test.tsx | 9 +++-
.../hooks/useTrackProtocolRunEvent.ts | 5 +-
app/src/redux/analytics/constants.ts | 12 +++++
11 files changed, 176 insertions(+), 8 deletions(-)
diff --git a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx
index 104085fcb15..af4c4feb5d0 100644
--- a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx
+++ b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx
@@ -5,6 +5,10 @@ import { describe, it, vi, afterEach, beforeEach, expect } from 'vitest'
import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
+import {
+ ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ useTrackEvent,
+} from '/app/redux/analytics'
import {
getAppLanguage,
getStoredSystemLanguage,
@@ -16,6 +20,7 @@ import { SystemLanguagePreferenceModal } from '..'
vi.mock('react-router-dom')
vi.mock('/app/redux/config')
vi.mock('/app/redux/shell')
+vi.mock('/app/redux/analytics')
const render = () => {
return renderWithProviders(, {
@@ -24,6 +29,7 @@ const render = () => {
}
const mockNavigate = vi.fn()
+const mockTrackEvent = vi.fn()
const MOCK_DEFAULT_LANGUAGE = 'en-US'
@@ -33,6 +39,7 @@ describe('SystemLanguagePreferenceModal', () => {
vi.mocked(getSystemLanguage).mockReturnValue(MOCK_DEFAULT_LANGUAGE)
vi.mocked(getStoredSystemLanguage).mockReturnValue(MOCK_DEFAULT_LANGUAGE)
vi.mocked(useNavigate).mockReturnValue(mockNavigate)
+ vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent)
})
afterEach(() => {
vi.resetAllMocks()
@@ -68,6 +75,14 @@ describe('SystemLanguagePreferenceModal', () => {
'language.systemLanguage',
MOCK_DEFAULT_LANGUAGE
)
+ expect(mockTrackEvent).toBeCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ properties: {
+ language: MOCK_DEFAULT_LANGUAGE,
+ systemLanguage: MOCK_DEFAULT_LANGUAGE,
+ modalType: 'appBootModal',
+ },
+ })
})
it('should default to English (US) if system language is unsupported', () => {
@@ -90,6 +105,14 @@ describe('SystemLanguagePreferenceModal', () => {
MOCK_DEFAULT_LANGUAGE
)
expect(updateConfigValue).toBeCalledWith('language.systemLanguage', 'es-MX')
+ expect(mockTrackEvent).toBeCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ properties: {
+ language: MOCK_DEFAULT_LANGUAGE,
+ systemLanguage: 'es-MX',
+ modalType: 'appBootModal',
+ },
+ })
})
it('should set a supported app language when system language is an unsupported locale of the same language', () => {
@@ -112,6 +135,14 @@ describe('SystemLanguagePreferenceModal', () => {
MOCK_DEFAULT_LANGUAGE
)
expect(updateConfigValue).toBeCalledWith('language.systemLanguage', 'en-GB')
+ expect(mockTrackEvent).toBeCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ properties: {
+ language: MOCK_DEFAULT_LANGUAGE,
+ systemLanguage: 'en-GB',
+ modalType: 'appBootModal',
+ },
+ })
})
it('should render the correct header, description, and buttons when system language changes', () => {
@@ -139,6 +170,14 @@ describe('SystemLanguagePreferenceModal', () => {
'language.systemLanguage',
'zh-CN'
)
+ expect(mockTrackEvent).toBeCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ properties: {
+ language: 'zh-CN',
+ systemLanguage: 'zh-CN',
+ modalType: 'systemLanguageUpdateModal',
+ },
+ })
fireEvent.click(secondaryButton)
expect(updateConfigValue).toHaveBeenNthCalledWith(
3,
@@ -168,6 +207,14 @@ describe('SystemLanguagePreferenceModal', () => {
'language.systemLanguage',
'zh-Hant'
)
+ expect(mockTrackEvent).toBeCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ properties: {
+ language: 'zh-CN',
+ systemLanguage: 'zh-Hant',
+ modalType: 'systemLanguageUpdateModal',
+ },
+ })
fireEvent.click(secondaryButton)
expect(updateConfigValue).toHaveBeenNthCalledWith(
3,
diff --git a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx
index d3b04f19061..f135c0fe10a 100644
--- a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx
+++ b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx
@@ -16,6 +16,10 @@ import {
} from '@opentrons/components'
import { LANGUAGES } from '/app/i18n'
+import {
+ ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ useTrackEvent,
+} from '/app/redux/analytics'
import {
getAppLanguage,
getStoredSystemLanguage,
@@ -32,7 +36,7 @@ type ArrayElement<
export function SystemLanguagePreferenceModal(): JSX.Element | null {
const { i18n, t } = useTranslation(['app_settings', 'shared', 'branded'])
-
+ const trackEvent = useTrackEvent()
const [currentOption, setCurrentOption] = useState(
LANGUAGES[0]
)
@@ -66,6 +70,16 @@ export function SystemLanguagePreferenceModal(): JSX.Element | null {
const handlePrimaryClick = (): void => {
dispatch(updateConfigValue('language.appLanguage', currentOption.value))
dispatch(updateConfigValue('language.systemLanguage', systemLanguage))
+ trackEvent({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
+ properties: {
+ language: currentOption.value,
+ systemLanguage,
+ modalType: showUpdateModal
+ ? 'systemLanguageUpdateModal'
+ : 'appBootModal',
+ },
+ })
}
const handleDropdownClick = (value: string): void => {
diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx
index 49f58e26993..50af850a44f 100644
--- a/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx
+++ b/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx
@@ -1,7 +1,8 @@
-import { Fragment } from 'react'
+import { Fragment, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import styled from 'styled-components'
+import uuidv1 from 'uuid/v4'
import {
BORDERS,
@@ -14,6 +15,8 @@ import {
} from '@opentrons/components'
import { LANGUAGES } from '/app/i18n'
+import { ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS } from '/app/redux/analytics'
+import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
@@ -42,16 +45,31 @@ interface LanguageSettingProps {
setCurrentOption: SetSettingOption
}
+const uuid: () => string = uuidv1
+
export function LanguageSetting({
setCurrentOption,
}: LanguageSettingProps): JSX.Element {
const { t } = useTranslation('app_settings')
const dispatch = useDispatch()
+ const { trackEventWithRobotSerial } = useTrackEventWithRobotSerial()
+
+ let transactionId = ''
+ useEffect(() => {
+ transactionId = uuid()
+ }, [])
const appLanguage = useSelector(getAppLanguage)
const handleChange = (event: ChangeEvent): void => {
dispatch(updateConfigValue('language.appLanguage', event.target.value))
+ trackEventWithRobotSerial({
+ name: ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS,
+ properties: {
+ language: event.target.value,
+ transactionId,
+ },
+ })
}
return (
diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx
index 80d35ebea15..fe90eb2e1cb 100644
--- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx
+++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx
@@ -10,14 +10,18 @@ import {
SIMPLIFIED_CHINESE_DISPLAY_NAME,
SIMPLIFIED_CHINESE,
} from '/app/i18n'
+import { ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS } from '/app/redux/analytics'
+import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
import { renderWithProviders } from '/app/__testing-utils__'
import { LanguageSetting } from '../LanguageSetting'
vi.mock('/app/redux/config')
+vi.mock('/app/redux-resources/analytics')
const mockSetCurrentOption = vi.fn()
+const mockTrackEvent = vi.fn()
const render = (props: React.ComponentProps) => {
return renderWithProviders(, {
@@ -32,6 +36,9 @@ describe('LanguageSetting', () => {
setCurrentOption: mockSetCurrentOption,
}
vi.mocked(getAppLanguage).mockReturnValue(US_ENGLISH)
+ vi.mocked(useTrackEventWithRobotSerial).mockReturnValue({
+ trackEventWithRobotSerial: mockTrackEvent,
+ })
})
it('should render text and buttons', () => {
@@ -49,6 +56,13 @@ describe('LanguageSetting', () => {
'language.appLanguage',
SIMPLIFIED_CHINESE
)
+ expect(mockTrackEvent).toHaveBeenCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS,
+ properties: {
+ language: SIMPLIFIED_CHINESE,
+ transactionId: expect.anything(),
+ },
+ })
})
it('should call mock function when tapping back button', () => {
diff --git a/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx b/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx
index 85b24816da6..eed4f5be96a 100644
--- a/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx
+++ b/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx
@@ -1,8 +1,9 @@
// app info card with version and updated
-import { useState } from 'react'
+import { useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import { useSelector, useDispatch } from 'react-redux'
+import uuidv1 from 'uuid/v4'
import {
ALIGN_CENTER,
@@ -41,6 +42,7 @@ import {
import {
useTrackEvent,
ANALYTICS_APP_UPDATE_NOTIFICATIONS_TOGGLED,
+ ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
} from '/app/redux/analytics'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
import { UpdateAppModal } from '/app/organisms/Desktop/UpdateAppModal'
@@ -55,6 +57,7 @@ const GITHUB_LINK =
'https://github.com/Opentrons/opentrons/blob/edge/app-shell/build/release-notes.md'
const ENABLE_APP_UPDATE_NOTIFICATIONS = 'Enable app update notifications'
+const uuid: () => string = uuidv1
export function GeneralSettings(): JSX.Element {
const { t } = useTranslation(['app_settings', 'shared', 'branded'])
@@ -68,9 +71,19 @@ export function GeneralSettings(): JSX.Element {
const appLanguage = useSelector(getAppLanguage)
const currentLanguageOption = LANGUAGES.find(lng => lng.value === appLanguage)
-
+ let transactionId = ''
+ useEffect(() => {
+ transactionId = uuid()
+ }, [])
const handleDropdownClick = (value: string): void => {
dispatch(updateConfigValue('language.appLanguage', value))
+ trackEvent({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
+ properties: {
+ language: value,
+ transactionId,
+ },
+ })
}
const [showUpdateBanner, setShowUpdateBanner] = useState(
diff --git a/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx b/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx
index 43d17a4d2cb..a06f4204bd7 100644
--- a/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx
+++ b/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx
@@ -12,6 +12,10 @@ import {
US_ENGLISH_DISPLAY_NAME,
} from '/app/i18n'
import { getAlertIsPermanentlyIgnored } from '/app/redux/alerts'
+import {
+ ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
+ useTrackEvent,
+} from '/app/redux/analytics'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
import * as Shell from '/app/redux/shell'
import { GeneralSettings } from '../GeneralSettings'
@@ -32,11 +36,14 @@ const render = (): ReturnType => {
)
}
+const mockTrackEvent = vi.fn()
+
describe('GeneralSettings', () => {
beforeEach(() => {
vi.mocked(Shell.getAvailableShellUpdate).mockReturnValue(null)
vi.mocked(getAlertIsPermanentlyIgnored).mockReturnValue(false)
vi.mocked(getAppLanguage).mockReturnValue(US_ENGLISH)
+ vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent)
})
afterEach(() => {
vi.resetAllMocks()
@@ -118,5 +125,12 @@ describe('GeneralSettings', () => {
'language.appLanguage',
SIMPLIFIED_CHINESE
)
+ expect(mockTrackEvent).toHaveBeenCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
+ properties: {
+ language: SIMPLIFIED_CHINESE,
+ transactionId: expect.anything(),
+ },
+ })
})
})
diff --git a/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx b/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx
index 8508a7b4d08..fa5c793e2d2 100644
--- a/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx
+++ b/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx
@@ -1,10 +1,12 @@
-import { vi, it, describe, expect } from 'vitest'
+import { vi, it, describe, expect, beforeEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
-import { updateConfigValue } from '/app/redux/config'
+import { ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW } from '/app/redux/analytics'
+import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
+import { updateConfigValue, getAppLanguage } from '/app/redux/config'
import { ChooseLanguage } from '..'
import type { NavigateFunction } from 'react-router-dom'
@@ -18,6 +20,9 @@ vi.mock('react-router-dom', async importOriginal => {
}
})
vi.mock('/app/redux/config')
+vi.mock('/app/redux-resources/analytics')
+
+const mockTrackEvent = vi.fn()
const render = () => {
return renderWithProviders(
@@ -31,6 +36,12 @@ const render = () => {
}
describe('ChooseLanguage', () => {
+ beforeEach(() => {
+ vi.mocked(useTrackEventWithRobotSerial).mockReturnValue({
+ trackEventWithRobotSerial: mockTrackEvent,
+ })
+ vi.mocked(getAppLanguage).mockReturnValue('en-US')
+ })
it('should render text, language options, and continue button', () => {
render()
screen.getByText('Choose your language')
@@ -54,6 +65,12 @@ describe('ChooseLanguage', () => {
it('should call mockNavigate when tapping continue', () => {
render()
fireEvent.click(screen.getByRole('button', { name: 'Continue' }))
+ expect(mockTrackEvent).toHaveBeenCalledWith({
+ name: ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW,
+ properties: {
+ language: 'en-US',
+ },
+ })
expect(mockNavigate).toHaveBeenCalledWith('/welcome')
})
})
diff --git a/app/src/pages/ODD/ChooseLanguage/index.tsx b/app/src/pages/ODD/ChooseLanguage/index.tsx
index d0110e68591..8ecb87451f7 100644
--- a/app/src/pages/ODD/ChooseLanguage/index.tsx
+++ b/app/src/pages/ODD/ChooseLanguage/index.tsx
@@ -13,6 +13,8 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
+import { ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW } from '/app/redux/analytics'
+import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
import { MediumButton } from '/app/atoms/buttons'
import { LANGUAGES, US_ENGLISH } from '/app/i18n'
import { RobotSetupHeader } from '/app/organisms/ODD/RobotSetupHeader'
@@ -24,6 +26,7 @@ export function ChooseLanguage(): JSX.Element {
const { i18n, t } = useTranslation(['app_settings', 'shared'])
const navigate = useNavigate()
const dispatch = useDispatch()
+ const { trackEventWithRobotSerial } = useTrackEventWithRobotSerial()
const appLanguage = useSelector(getAppLanguage)
@@ -69,6 +72,12 @@ export function ChooseLanguage(): JSX.Element {
{
+ trackEventWithRobotSerial({
+ name: ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW,
+ properties: {
+ language: appLanguage,
+ },
+ })
navigate('/welcome')
}}
width="100%"
diff --git a/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx b/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx
index f769dc005c4..c12bcd5dbeb 100644
--- a/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx
+++ b/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx
@@ -12,6 +12,7 @@ import {
useTrackEvent,
ANALYTICS_PROTOCOL_RUN_ACTION,
} from '/app/redux/analytics'
+import { getAppLanguage } from '/app/redux/config'
import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__'
import { useRobot } from '/app/redux-resources/robots'
@@ -23,6 +24,7 @@ vi.mock('../useProtocolRunAnalyticsData')
vi.mock('/app/redux/discovery')
vi.mock('/app/redux/pipettes')
vi.mock('/app/redux/analytics')
+vi.mock('/app/redux/config')
vi.mock('/app/redux/robot-settings')
const RUN_ID = 'runId'
@@ -55,6 +57,7 @@ describe('useTrackProtocolRunEvent hook', () => {
)
vi.mocked(useRobot).mockReturnValue(mockConnectableRobot)
vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent)
+ vi.mocked(getAppLanguage).mockReturnValue('en-US')
when(vi.mocked(useProtocolRunAnalyticsData))
.calledWith(RUN_ID, mockConnectableRobot)
@@ -88,7 +91,11 @@ describe('useTrackProtocolRunEvent hook', () => {
)
expect(mockTrackEvent).toHaveBeenCalledWith({
name: ANALYTICS_PROTOCOL_RUN_ACTION.START,
- properties: { ...PROTOCOL_PROPERTIES, transactionId: RUN_ID },
+ properties: {
+ ...PROTOCOL_PROPERTIES,
+ transactionId: RUN_ID,
+ appLanguage: 'en-US',
+ },
})
})
diff --git a/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts b/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts
index 05c3ce16746..0603994d4b4 100644
--- a/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts
+++ b/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts
@@ -1,5 +1,7 @@
+import { useSelector } from 'react-redux'
import { useTrackEvent } from '/app/redux/analytics'
import { useProtocolRunAnalyticsData } from './useProtocolRunAnalyticsData'
+import { getAppLanguage } from '/app/redux/config'
import { useRobot } from '/app/redux-resources/robots'
interface ProtocolRunAnalyticsEvent {
@@ -21,7 +23,7 @@ export function useTrackProtocolRunEvent(
runId,
robot
)
-
+ const appLanguage = useSelector(getAppLanguage)
const trackProtocolRunEvent: TrackProtocolRunEvent = ({
name,
properties = {},
@@ -37,6 +39,7 @@ export function useTrackProtocolRunEvent(
// It's sometimes unavoidable (namely on the desktop app) to prevent sending an event multiple times.
// In these circumstances, we need an idempotency key to accurately filter events in Mixpanel.
transactionId: runId,
+ appLanguage,
},
})
})
diff --git a/app/src/redux/analytics/constants.ts b/app/src/redux/analytics/constants.ts
index cde9b0a1d59..aadeb7c6696 100644
--- a/app/src/redux/analytics/constants.ts
+++ b/app/src/redux/analytics/constants.ts
@@ -103,3 +103,15 @@ export const ANALYTICS_QUICK_TRANSFER_RERUN = 'quickTransferReRunFromSummary'
*/
export const ANALYTICS_RESOURCE_MONITOR_REPORT: 'analytics:RESOURCE_MONITOR_REPORT' =
'analytics:RESOURCE_MONITOR_REPORT'
+
+/**
+ * Internationalization Analytics
+ */
+export const ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW: 'languageUpdatedOddUnboxingFlow' =
+ 'languageUpdatedOddUnboxingFlow'
+export const ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS: 'languageUpdatedOddSettings' =
+ 'languageUpdatedOddSettings'
+export const ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL: 'languageUpdatedDesktopAppModal' =
+ 'languageUpdatedDesktopAppModal'
+export const ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS: 'languageUpdatedDesktopAppSettings' =
+ 'languageUpdatedDesktopAppSettings'
From 99da2524454ba535e59cb2dd3e54d40d4a451489 Mon Sep 17 00:00:00 2001
From: Jethary Alcid <66035149+jerader@users.noreply.github.com>
Date: Fri, 20 Dec 2024 14:06:40 -0500
Subject: [PATCH 10/18] fix(protocol-designer): timeline errors/warnings show
up in mixpanel (#17164)
closes RQA-3778
---
.../reduxActionToAnalyticsEvent.test.ts | 7 ++++---
protocol-designer/src/analytics/middleware.ts | 17 ++++++++++++-----
protocol-designer/src/configureStore.ts | 4 ++--
3 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts b/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts
index 5f5c9aa4012..cecf0755c0a 100644
--- a/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts
+++ b/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts
@@ -154,9 +154,10 @@ describe('reduxActionToAnalyticsEvent', () => {
id: 'stepId',
pipette: 'pipetteId',
otherField: 123,
- aspirateFlowRate: undefined,
- dispenseFlowRate: undefined,
- aspirateAirGap: undefined,
+ aspirateFlowRate: 'default',
+ dispenseFlowRate: 'default',
+ aspirateAirGap: 'default',
+ dispenseAirGap: 'default',
nested: { inner: true },
// de-nested fields
__nested__inner: true,
diff --git a/protocol-designer/src/analytics/middleware.ts b/protocol-designer/src/analytics/middleware.ts
index 29cc66f8a11..661bb2a65ff 100644
--- a/protocol-designer/src/analytics/middleware.ts
+++ b/protocol-designer/src/analytics/middleware.ts
@@ -158,9 +158,14 @@ export const reduxActionToAnalyticsEvent = (
name: `${modifiedStepName}Step`,
properties: {
...stepArgModified,
- aspirateAirGap: stepArgModified.aspirateAirGapVolume,
- aspirateFlowRate: stepArgModified.aspirateFlowRateUlSec,
- dispenseFlowRate: stepArgModified.dispenseFlowRateUlSec,
+ aspirateAirGap:
+ stepArgModified.aspirateAirGapVolume ?? DEFAULT_VALUE,
+ aspirateFlowRate:
+ stepArgModified.aspirateFlowRateUlSec ?? DEFAULT_VALUE,
+ dispenseFlowRate:
+ stepArgModified.dispenseFlowRateUlSec ?? DEFAULT_VALUE,
+ dispenseAirGap:
+ stepArgModified.dispenseAirGapVolume ?? DEFAULT_VALUE,
blowoutFlowRate: stepArgModified.blowoutFlowRateUlSec,
aspirateOffsetFromBottomMm:
stepArgModified.aspirateOffsetFromBottomMm ===
@@ -202,8 +207,10 @@ export const reduxActionToAnalyticsEvent = (
name: `mixStep`,
properties: {
...stepArgModified,
- aspirateFlowRate: stepArgModified.aspirateFlowRateUlSec,
- dispenseFlowRate: stepArgModified.dispenseFlowRateUlSec,
+ aspirateFlowRate:
+ stepArgModified.aspirateFlowRateUlSec ?? DEFAULT_VALUE,
+ dispenseFlowRate:
+ stepArgModified.dispenseFlowRateUlSec ?? DEFAULT_VALUE,
blowoutFlowRate: stepArgModified.blowoutFlowRateUlSec,
aspirateOffsetFromBottomMm:
stepArgModified.aspirateOffsetFromBottomMm ===
diff --git a/protocol-designer/src/configureStore.ts b/protocol-designer/src/configureStore.ts
index f7c31f00810..9f049628c0a 100644
--- a/protocol-designer/src/configureStore.ts
+++ b/protocol-designer/src/configureStore.ts
@@ -89,9 +89,9 @@ export function configureStore(): StoreType {
/* preloadedState, */
composeEnhancers(
applyMiddleware(
- trackEventMiddleware as Middleware, any>,
+ thunk,
timelineMiddleware as Middleware, any>,
- thunk
+ trackEventMiddleware as Middleware, any>
)
) as StoreEnhancer
)
From 20a23ef452544bb161e5e4e4316ceb99e393d98a Mon Sep 17 00:00:00 2001
From: Alise Au <20424172+ahiuchingau@users.noreply.github.com>
Date: Fri, 20 Dec 2024 14:24:54 -0500
Subject: [PATCH 11/18] feat(api): add hardware controller driver and simulator
for the Flex Stacker (#17120)
---
.../communication/serial_connection.py | 13 +-
api/src/opentrons/drivers/command_builder.py | 4 +-
.../drivers/flex_stacker/__init__.py | 9 +
.../drivers/flex_stacker/abstract.py | 89 ++++++
.../opentrons/drivers/flex_stacker/driver.py | 260 ++++++++++++++++++
.../drivers/flex_stacker/simulator.py | 109 ++++++++
.../opentrons/drivers/flex_stacker/types.py | 138 ++++++++++
.../drivers/flex_stacker/__init__.py | 0
.../drivers/flex_stacker/test_driver.py | 257 +++++++++++++++++
9 files changed, 875 insertions(+), 4 deletions(-)
create mode 100644 api/src/opentrons/drivers/flex_stacker/__init__.py
create mode 100644 api/src/opentrons/drivers/flex_stacker/abstract.py
create mode 100644 api/src/opentrons/drivers/flex_stacker/driver.py
create mode 100644 api/src/opentrons/drivers/flex_stacker/simulator.py
create mode 100644 api/src/opentrons/drivers/flex_stacker/types.py
create mode 100644 api/tests/opentrons/drivers/flex_stacker/__init__.py
create mode 100644 api/tests/opentrons/drivers/flex_stacker/test_driver.py
diff --git a/api/src/opentrons/drivers/asyncio/communication/serial_connection.py b/api/src/opentrons/drivers/asyncio/communication/serial_connection.py
index 294e5779a7b..f925cfe8680 100644
--- a/api/src/opentrons/drivers/asyncio/communication/serial_connection.py
+++ b/api/src/opentrons/drivers/asyncio/communication/serial_connection.py
@@ -298,6 +298,7 @@ async def create(
alarm_keyword: Optional[str] = None,
reset_buffer_before_write: bool = False,
async_error_ack: Optional[str] = None,
+ number_of_retries: int = 0,
) -> AsyncResponseSerialConnection:
"""
Create a connection.
@@ -340,6 +341,7 @@ async def create(
error_keyword=error_keyword or "err",
alarm_keyword=alarm_keyword or "alarm",
async_error_ack=async_error_ack or "async",
+ number_of_retries=number_of_retries,
)
def __init__(
@@ -352,6 +354,7 @@ def __init__(
error_keyword: str,
alarm_keyword: str,
async_error_ack: str,
+ number_of_retries: int = 0,
) -> None:
"""
Constructor
@@ -383,6 +386,7 @@ def __init__(
self._name = name
self._ack = ack.encode()
self._retry_wait_time_seconds = retry_wait_time_seconds
+ self._number_of_retries = number_of_retries
self._error_keyword = error_keyword.lower()
self._alarm_keyword = alarm_keyword.lower()
self._async_error_ack = async_error_ack.lower()
@@ -403,7 +407,9 @@ async def send_command(
Raises: SerialException
"""
return await self.send_data(
- data=command.build(), retries=retries, timeout=timeout
+ data=command.build(),
+ retries=retries or self._number_of_retries,
+ timeout=timeout,
)
async def send_data(
@@ -424,7 +430,9 @@ async def send_data(
async with super().send_data_lock, self._serial.timeout_override(
"timeout", timeout
):
- return await self._send_data(data=data, retries=retries)
+ return await self._send_data(
+ data=data, retries=retries or self._number_of_retries
+ )
async def _send_data(self, data: str, retries: int = 0) -> str:
"""
@@ -439,6 +447,7 @@ async def _send_data(self, data: str, retries: int = 0) -> str:
Raises: SerialException
"""
data_encode = data.encode()
+ retries = retries or self._number_of_retries
for retry in range(retries + 1):
log.debug(f"{self._name}: Write -> {data_encode!r}")
diff --git a/api/src/opentrons/drivers/command_builder.py b/api/src/opentrons/drivers/command_builder.py
index 99ac5c7890c..ea90a12b946 100644
--- a/api/src/opentrons/drivers/command_builder.py
+++ b/api/src/opentrons/drivers/command_builder.py
@@ -6,7 +6,7 @@
class CommandBuilder:
"""Class used to build GCODE commands."""
- def __init__(self, terminator: str) -> None:
+ def __init__(self, terminator: str = "\n") -> None:
"""
Construct a command builder.
@@ -17,7 +17,7 @@ def __init__(self, terminator: str) -> None:
self._elements: List[str] = []
def add_float(
- self, prefix: str, value: float, precision: Optional[int]
+ self, prefix: str, value: float, precision: Optional[int] = None
) -> CommandBuilder:
"""
Add a float value.
diff --git a/api/src/opentrons/drivers/flex_stacker/__init__.py b/api/src/opentrons/drivers/flex_stacker/__init__.py
new file mode 100644
index 00000000000..cd4866c179a
--- /dev/null
+++ b/api/src/opentrons/drivers/flex_stacker/__init__.py
@@ -0,0 +1,9 @@
+from .abstract import AbstractStackerDriver
+from .driver import FlexStackerDriver
+from .simulator import SimulatingDriver
+
+__all__ = [
+ "AbstractStackerDriver",
+ "FlexStackerDriver",
+ "SimulatingDriver",
+]
diff --git a/api/src/opentrons/drivers/flex_stacker/abstract.py b/api/src/opentrons/drivers/flex_stacker/abstract.py
new file mode 100644
index 00000000000..5ba3cdcb026
--- /dev/null
+++ b/api/src/opentrons/drivers/flex_stacker/abstract.py
@@ -0,0 +1,89 @@
+from typing import Protocol
+
+from .types import (
+ StackerAxis,
+ PlatformStatus,
+ Direction,
+ MoveParams,
+ StackerInfo,
+ LEDColor,
+)
+
+
+class AbstractStackerDriver(Protocol):
+ """Protocol for the Stacker driver."""
+
+ async def connect(self) -> None:
+ """Connect to stacker."""
+ ...
+
+ async def disconnect(self) -> None:
+ """Disconnect from stacker."""
+ ...
+
+ async def is_connected(self) -> bool:
+ """Check connection to stacker."""
+ ...
+
+ async def update_firmware(self, firmware_file_path: str) -> None:
+ """Updates the firmware on the device."""
+ ...
+
+ async def get_device_info(self) -> StackerInfo:
+ """Get Device Info."""
+ ...
+
+ async def set_serial_number(self, sn: str) -> bool:
+ """Set Serial Number."""
+ ...
+
+ async def stop_motors(self) -> bool:
+ """Stop all motor movement."""
+ ...
+
+ async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
+ """Get limit switch status.
+
+ :return: True if limit switch is triggered, False otherwise
+ """
+ ...
+
+ async def get_platform_sensor(self, direction: Direction) -> bool:
+ """Get platform sensor status.
+
+ :return: True if platform is present, False otherwise
+ """
+ ...
+
+ async def get_platform_status(self) -> PlatformStatus:
+ """Get platform status."""
+ ...
+
+ async def get_hopper_door_closed(self) -> bool:
+ """Get whether or not door is closed.
+
+ :return: True if door is closed, False otherwise
+ """
+ ...
+
+ async def move_in_mm(
+ self, axis: StackerAxis, distance: float, params: MoveParams | None = None
+ ) -> bool:
+ """Move axis."""
+ ...
+
+ async def move_to_limit_switch(
+ self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
+ ) -> bool:
+ """Move until limit switch is triggered."""
+ ...
+
+ async def home_axis(self, axis: StackerAxis, direction: Direction) -> bool:
+ """Home axis."""
+ ...
+
+ async def set_led(
+ self, power: float, color: LEDColor | None = None, external: bool | None = None
+ ) -> bool:
+ """Set LED color of status bar."""
+ ...
diff --git a/api/src/opentrons/drivers/flex_stacker/driver.py b/api/src/opentrons/drivers/flex_stacker/driver.py
new file mode 100644
index 00000000000..83671023772
--- /dev/null
+++ b/api/src/opentrons/drivers/flex_stacker/driver.py
@@ -0,0 +1,260 @@
+import asyncio
+import re
+from typing import Optional
+
+from opentrons.drivers.command_builder import CommandBuilder
+from opentrons.drivers.asyncio.communication import AsyncResponseSerialConnection
+
+from .abstract import AbstractStackerDriver
+from .types import (
+ GCODE,
+ StackerAxis,
+ PlatformStatus,
+ Direction,
+ StackerInfo,
+ HardwareRevision,
+ MoveParams,
+ LimitSwitchStatus,
+ LEDColor,
+)
+
+
+FS_BAUDRATE = 115200
+DEFAULT_FS_TIMEOUT = 40
+FS_ACK = "OK\n"
+FS_ERROR_KEYWORD = "err"
+FS_ASYNC_ERROR_ACK = "async"
+DEFAULT_COMMAND_RETRIES = 0
+GCODE_ROUNDING_PRECISION = 2
+
+
+class FlexStackerDriver(AbstractStackerDriver):
+ """FLEX Stacker driver."""
+
+ @classmethod
+ def parse_device_info(cls, response: str) -> StackerInfo:
+ """Parse stacker info."""
+ # TODO: Validate serial number format once established
+ _RE = re.compile(
+ f"^{GCODE.DEVICE_INFO} FW:(?P\\S+) HW:Opentrons-flex-stacker-(?P\\S+) SerialNo:(?P\\S+)$"
+ )
+ m = _RE.match(response)
+ if not m:
+ raise ValueError(f"Incorrect Response for device info: {response}")
+ return StackerInfo(
+ m.group("fw"), HardwareRevision(m.group("hw")), m.group("sn")
+ )
+
+ @classmethod
+ def parse_limit_switch_status(cls, response: str) -> LimitSwitchStatus:
+ """Parse limit switch statuses."""
+ field_names = LimitSwitchStatus.get_fields()
+ pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names])
+ _RE = re.compile(f"^{GCODE.GET_LIMIT_SWITCH} {pattern}$")
+ m = _RE.match(response)
+ if not m:
+ raise ValueError(f"Incorrect Response for limit switch status: {response}")
+ return LimitSwitchStatus(*(bool(int(m.group(name))) for name in field_names))
+
+ @classmethod
+ def parse_platform_sensor_status(cls, response: str) -> PlatformStatus:
+ """Parse platform statuses."""
+ field_names = PlatformStatus.get_fields()
+ pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names])
+ _RE = re.compile(f"^{GCODE.GET_PLATFORM_SENSOR} {pattern}$")
+ m = _RE.match(response)
+ if not m:
+ raise ValueError(f"Incorrect Response for platform status: {response}")
+ return PlatformStatus(*(bool(int(m.group(name))) for name in field_names))
+
+ @classmethod
+ def parse_door_closed(cls, response: str) -> bool:
+ """Parse door closed."""
+ _RE = re.compile(r"^M122 D:(\d)$")
+ match = _RE.match(response)
+ if not match:
+ raise ValueError(f"Incorrect Response for door closed: {response}")
+ return bool(int(match.group(1)))
+
+ @classmethod
+ def append_move_params(
+ cls, command: CommandBuilder, params: MoveParams | None
+ ) -> CommandBuilder:
+ """Append move params."""
+ if params is not None:
+ if params.max_speed is not None:
+ command.add_float("V", params.max_speed, GCODE_ROUNDING_PRECISION)
+ if params.acceleration is not None:
+ command.add_float("A", params.acceleration, GCODE_ROUNDING_PRECISION)
+ if params.max_speed_discont is not None:
+ command.add_float(
+ "D", params.max_speed_discont, GCODE_ROUNDING_PRECISION
+ )
+ return command
+
+ @classmethod
+ async def create(
+ cls, port: str, loop: Optional[asyncio.AbstractEventLoop]
+ ) -> "FlexStackerDriver":
+ """Create a FLEX Stacker driver."""
+ connection = await AsyncResponseSerialConnection.create(
+ port=port,
+ baud_rate=FS_BAUDRATE,
+ timeout=DEFAULT_FS_TIMEOUT,
+ number_of_retries=DEFAULT_COMMAND_RETRIES,
+ ack=FS_ACK,
+ loop=loop,
+ error_keyword=FS_ERROR_KEYWORD,
+ async_error_ack=FS_ASYNC_ERROR_ACK,
+ )
+ return cls(connection)
+
+ def __init__(self, connection: AsyncResponseSerialConnection) -> None:
+ """
+ Constructor
+
+ Args:
+ connection: Connection to the FLEX Stacker
+ """
+ self._connection = connection
+
+ async def connect(self) -> None:
+ """Connect to stacker."""
+ await self._connection.open()
+
+ async def disconnect(self) -> None:
+ """Disconnect from stacker."""
+ await self._connection.close()
+
+ async def is_connected(self) -> bool:
+ """Check connection to stacker."""
+ return await self._connection.is_open()
+
+ async def get_device_info(self) -> StackerInfo:
+ """Get Device Info."""
+ response = await self._connection.send_command(
+ GCODE.DEVICE_INFO.build_command()
+ )
+ await self._connection.send_command(GCODE.GET_RESET_REASON.build_command())
+ return self.parse_device_info(response)
+
+ async def set_serial_number(self, sn: str) -> bool:
+ """Set Serial Number."""
+ # TODO: validate the serial number format
+ resp = await self._connection.send_command(
+ GCODE.SET_SERIAL_NUMBER.build_command().add_element(sn)
+ )
+ if not re.match(rf"^{GCODE.SET_SERIAL_NUMBER}$", resp):
+ raise ValueError(f"Incorrect Response for set serial number: {resp}")
+ return True
+
+ async def stop_motors(self) -> bool:
+ """Stop all motor movement."""
+ resp = await self._connection.send_command(GCODE.STOP_MOTORS.build_command())
+ if not re.match(rf"^{GCODE.STOP_MOTORS}$", resp):
+ raise ValueError(f"Incorrect Response for stop motors: {resp}")
+ return True
+
+ async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
+ """Get limit switch status.
+
+ :return: True if limit switch is triggered, False otherwise
+ """
+ response = await self.get_limit_switches_status()
+ return response.get(axis, direction)
+
+ async def get_limit_switches_status(self) -> LimitSwitchStatus:
+ """Get limit switch statuses for all axes."""
+ response = await self._connection.send_command(
+ GCODE.GET_LIMIT_SWITCH.build_command()
+ )
+ return self.parse_limit_switch_status(response)
+
+ async def get_platform_sensor(self, direction: Direction) -> bool:
+ """Get platform sensor at one direction."""
+ response = await self.get_platform_status()
+ return response.get(direction)
+
+ async def get_platform_status(self) -> PlatformStatus:
+ """Get platform sensor status.
+
+ :return: True if platform is detected, False otherwise
+ """
+ response = await self._connection.send_command(
+ GCODE.GET_PLATFORM_SENSOR.build_command()
+ )
+ return self.parse_platform_sensor_status(response)
+
+ async def get_hopper_door_closed(self) -> bool:
+ """Get whether or not door is closed.
+
+ :return: True if door is closed, False otherwise
+ """
+ response = await self._connection.send_command(
+ GCODE.GET_DOOR_SWITCH.build_command()
+ )
+ return self.parse_door_closed(response)
+
+ async def move_in_mm(
+ self, axis: StackerAxis, distance: float, params: MoveParams | None = None
+ ) -> bool:
+ """Move axis."""
+ command = self.append_move_params(
+ GCODE.MOVE_TO.build_command().add_float(
+ axis.name, distance, GCODE_ROUNDING_PRECISION
+ ),
+ params,
+ )
+ resp = await self._connection.send_command(command)
+ if not re.match(rf"^{GCODE.MOVE_TO}$", resp):
+ raise ValueError(f"Incorrect Response for move to: {resp}")
+ return True
+
+ async def move_to_limit_switch(
+ self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
+ ) -> bool:
+ """Move until limit switch is triggered."""
+ command = self.append_move_params(
+ GCODE.MOVE_TO_SWITCH.build_command().add_int(axis.name, direction.value),
+ params,
+ )
+ resp = await self._connection.send_command(command)
+ if not re.match(rf"^{GCODE.MOVE_TO_SWITCH}$", resp):
+ raise ValueError(f"Incorrect Response for move to switch: {resp}")
+ return True
+
+ async def home_axis(self, axis: StackerAxis, direction: Direction) -> bool:
+ """Home axis."""
+ resp = await self._connection.send_command(
+ GCODE.HOME_AXIS.build_command().add_int(axis.name, direction.value)
+ )
+ if not re.match(rf"^{GCODE.HOME_AXIS}$", resp):
+ raise ValueError(f"Incorrect Response for home axis: {resp}")
+ return True
+
+ async def set_led(
+ self, power: float, color: LEDColor | None = None, external: bool | None = None
+ ) -> bool:
+ """Set LED color.
+
+ :param power: Power of the LED (0-1.0), 0 is off, 1 is full power
+ :param color: Color of the LED
+ :param external: True if external LED, False if internal LED
+ """
+ power = max(0, min(power, 1.0))
+ command = GCODE.SET_LED.build_command().add_float(
+ "P", power, GCODE_ROUNDING_PRECISION
+ )
+ if color is not None:
+ command.add_int("C", color.value)
+ if external is not None:
+ command.add_int("E", external)
+ resp = await self._connection.send_command(command)
+ if not re.match(rf"^{GCODE.SET_LED}$", resp):
+ raise ValueError(f"Incorrect Response for set led: {resp}")
+ return True
+
+ async def update_firmware(self, firmware_file_path: str) -> None:
+ """Updates the firmware on the device."""
+ # TODO: Implement firmware update
+ pass
diff --git a/api/src/opentrons/drivers/flex_stacker/simulator.py b/api/src/opentrons/drivers/flex_stacker/simulator.py
new file mode 100644
index 00000000000..1e0b59b19de
--- /dev/null
+++ b/api/src/opentrons/drivers/flex_stacker/simulator.py
@@ -0,0 +1,109 @@
+from typing import Optional
+
+from opentrons.util.async_helpers import ensure_yield
+
+from .abstract import AbstractStackerDriver
+from .types import (
+ StackerAxis,
+ PlatformStatus,
+ Direction,
+ StackerInfo,
+ HardwareRevision,
+ MoveParams,
+ LimitSwitchStatus,
+)
+
+
+class SimulatingDriver(AbstractStackerDriver):
+ """FLEX Stacker driver simulator."""
+
+ def __init__(self, serial_number: Optional[str] = None) -> None:
+ self._sn = serial_number or "dummySerialFS"
+ self._limit_switch_status = LimitSwitchStatus(False, False, False, False, False)
+ self._platform_sensor_status = PlatformStatus(False, False)
+ self._door_closed = True
+
+ def set_limit_switch(self, status: LimitSwitchStatus) -> bool:
+ self._limit_switch_status = status
+ return True
+
+ def set_platform_sensor(self, status: PlatformStatus) -> bool:
+ self._platform_sensor_status = status
+ return True
+
+ def set_door_closed(self, door_closed: bool) -> bool:
+ self._door_closed = door_closed
+ return True
+
+ @ensure_yield
+ async def connect(self) -> None:
+ """Connect to stacker."""
+ pass
+
+ @ensure_yield
+ async def disconnect(self) -> None:
+ """Disconnect from stacker."""
+ pass
+
+ @ensure_yield
+ async def is_connected(self) -> bool:
+ """Check connection to stacker."""
+ return True
+
+ @ensure_yield
+ async def get_device_info(self) -> StackerInfo:
+ """Get Device Info."""
+ return StackerInfo(fw="stacker-fw", hw=HardwareRevision.EVT, sn=self._sn)
+
+ @ensure_yield
+ async def set_serial_number(self, sn: str) -> bool:
+ """Set Serial Number."""
+ return True
+
+ @ensure_yield
+ async def stop_motor(self) -> bool:
+ """Stop motor movement."""
+ return True
+
+ @ensure_yield
+ async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
+ """Get limit switch status.
+
+ :return: True if limit switch is triggered, False otherwise
+ """
+ return self._limit_switch_status.get(axis, direction)
+
+ @ensure_yield
+ async def get_limit_switches_status(self) -> LimitSwitchStatus:
+ """Get limit switch statuses for all axes."""
+ return self._limit_switch_status
+
+ @ensure_yield
+ async def get_platform_sensor_status(self) -> PlatformStatus:
+ """Get platform sensor status.
+
+ :return: True if platform is detected, False otherwise
+ """
+ return self._platform_sensor_status
+
+ @ensure_yield
+ async def get_hopper_door_closed(self) -> bool:
+ """Get whether or not door is closed.
+
+ :return: True if door is closed, False otherwise
+ """
+ return self._door_closed
+
+ @ensure_yield
+ async def move_in_mm(
+ self, axis: StackerAxis, distance: float, params: MoveParams | None = None
+ ) -> bool:
+ """Move axis."""
+ return True
+
+ @ensure_yield
+ async def move_to_limit_switch(
+ self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
+ ) -> bool:
+ """Move until limit switch is triggered."""
+ return True
diff --git a/api/src/opentrons/drivers/flex_stacker/types.py b/api/src/opentrons/drivers/flex_stacker/types.py
new file mode 100644
index 00000000000..4035aaaa755
--- /dev/null
+++ b/api/src/opentrons/drivers/flex_stacker/types.py
@@ -0,0 +1,138 @@
+from enum import Enum
+from dataclasses import dataclass, fields
+from typing import List
+
+from opentrons.drivers.command_builder import CommandBuilder
+
+
+class GCODE(str, Enum):
+
+ MOVE_TO = "G0"
+ MOVE_TO_SWITCH = "G5"
+ HOME_AXIS = "G28"
+ STOP_MOTORS = "M0"
+ GET_RESET_REASON = "M114"
+ DEVICE_INFO = "M115"
+ GET_LIMIT_SWITCH = "M119"
+ SET_LED = "M200"
+ GET_PLATFORM_SENSOR = "M121"
+ GET_DOOR_SWITCH = "M122"
+ SET_SERIAL_NUMBER = "M996"
+ ENTER_BOOTLOADER = "dfu"
+
+ def build_command(self) -> CommandBuilder:
+ """Build command."""
+ return CommandBuilder().add_gcode(self)
+
+
+STACKER_VID = 0x483
+STACKER_PID = 0xEF24
+STACKER_FREQ = 115200
+
+
+class HardwareRevision(Enum):
+ """Hardware Revision."""
+
+ NFF = "nff"
+ EVT = "a1"
+
+
+@dataclass
+class StackerInfo:
+ """Stacker Info."""
+
+ fw: str
+ hw: HardwareRevision
+ sn: str
+
+
+class StackerAxis(Enum):
+ """Stacker Axis."""
+
+ X = "X"
+ Z = "Z"
+ L = "L"
+
+ def __str__(self) -> str:
+ """Name."""
+ return self.name
+
+
+class LEDColor(Enum):
+ """Stacker LED Color."""
+
+ WHITE = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 3
+
+
+class Direction(Enum):
+ """Direction."""
+
+ RETRACT = 0 # negative
+ EXTENT = 1 # positive
+
+ def __str__(self) -> str:
+ """Convert to tag for clear logging."""
+ return "negative" if self == Direction.RETRACT else "positive"
+
+ def opposite(self) -> "Direction":
+ """Get opposite direction."""
+ return Direction.EXTENT if self == Direction.RETRACT else Direction.RETRACT
+
+ def distance(self, distance: float) -> float:
+ """Get signed distance, where retract direction is negative."""
+ return distance * -1 if self == Direction.RETRACT else distance
+
+
+@dataclass
+class LimitSwitchStatus:
+ """Stacker Limit Switch Statuses."""
+
+ XE: bool
+ XR: bool
+ ZE: bool
+ ZR: bool
+ LR: bool
+
+ @classmethod
+ def get_fields(cls) -> List[str]:
+ """Get fields."""
+ return [f.name for f in fields(cls)]
+
+ def get(self, axis: StackerAxis, direction: Direction) -> bool:
+ """Get limit switch status."""
+ if axis == StackerAxis.X:
+ return self.XE if direction == Direction.EXTENT else self.XR
+ if axis == StackerAxis.Z:
+ return self.ZE if direction == Direction.EXTENT else self.ZR
+ if direction == Direction.EXTENT:
+ raise ValueError("Latch does not have extent limit switch")
+ return self.LR
+
+
+@dataclass
+class PlatformStatus:
+ """Stacker Platform Statuses."""
+
+ E: bool
+ R: bool
+
+ @classmethod
+ def get_fields(cls) -> List[str]:
+ """Get fields."""
+ return [f.name for f in fields(cls)]
+
+ def get(self, direction: Direction) -> bool:
+ """Get platform status."""
+ return self.E if direction == Direction.EXTENT else self.R
+
+
+@dataclass
+class MoveParams:
+ """Move Parameters."""
+
+ max_speed: float | None = None
+ acceleration: float | None = None
+ max_speed_discont: float | None = None
diff --git a/api/tests/opentrons/drivers/flex_stacker/__init__.py b/api/tests/opentrons/drivers/flex_stacker/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/api/tests/opentrons/drivers/flex_stacker/test_driver.py b/api/tests/opentrons/drivers/flex_stacker/test_driver.py
new file mode 100644
index 00000000000..aea2492cf9e
--- /dev/null
+++ b/api/tests/opentrons/drivers/flex_stacker/test_driver.py
@@ -0,0 +1,257 @@
+import pytest
+from mock import AsyncMock
+from opentrons.drivers.asyncio.communication.serial_connection import (
+ AsyncResponseSerialConnection,
+)
+from opentrons.drivers.flex_stacker.driver import FlexStackerDriver
+from opentrons.drivers.flex_stacker import types
+
+
+@pytest.fixture
+def connection() -> AsyncMock:
+ return AsyncMock(spec=AsyncResponseSerialConnection)
+
+
+@pytest.fixture
+def subject(connection: AsyncMock) -> FlexStackerDriver:
+ connection.send_command.return_value = ""
+ return FlexStackerDriver(connection)
+
+
+async def test_get_device_info(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a get device info command"""
+ connection.send_command.return_value = (
+ "M115 FW:0.0.1 HW:Opentrons-flex-stacker-a1 SerialNo:STCA120230605001"
+ )
+ response = await subject.get_device_info()
+ assert response == types.StackerInfo(
+ fw="0.0.1",
+ hw=types.HardwareRevision.EVT,
+ sn="STCA120230605001",
+ )
+
+ device_info = types.GCODE.DEVICE_INFO.build_command()
+ reset_reason = types.GCODE.GET_RESET_REASON.build_command()
+ connection.send_command.assert_any_call(device_info)
+ connection.send_command.assert_called_with(reset_reason)
+ connection.reset_mock()
+
+ # Test invalid response
+ connection.send_command.return_value = "M115 FW:0.0.1 SerialNo:STCA120230605001"
+
+ # This should raise ValueError
+ with pytest.raises(ValueError):
+ response = await subject.get_device_info()
+
+ device_info = types.GCODE.DEVICE_INFO.build_command()
+ reset_reason = types.GCODE.GET_RESET_REASON.build_command()
+ connection.send_command.assert_any_call(device_info)
+ connection.send_command.assert_called_with(reset_reason)
+
+
+async def test_stop_motors(subject: FlexStackerDriver, connection: AsyncMock) -> None:
+ """It should send a stop motors command"""
+ connection.send_command.return_value = "M0"
+ response = await subject.stop_motors()
+ assert response
+
+ stop_motors = types.GCODE.STOP_MOTORS.build_command()
+ connection.send_command.assert_any_call(stop_motors)
+ connection.reset_mock()
+
+ # This should raise ValueError
+ with pytest.raises(ValueError):
+ await subject.get_device_info()
+
+
+async def test_set_serial_number(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a set serial number command"""
+ connection.send_command.return_value = "M996"
+
+ serial_number = "Something"
+ response = await subject.set_serial_number(serial_number)
+ assert response
+
+ set_serial_number = types.GCODE.SET_SERIAL_NUMBER.build_command().add_element(
+ serial_number
+ )
+ connection.send_command.assert_any_call(set_serial_number)
+ connection.reset_mock()
+
+ # Test invalid response
+ connection.send_command.return_value = "M9nn"
+ with pytest.raises(ValueError):
+ response = await subject.set_serial_number(serial_number)
+
+ set_serial_number = types.GCODE.SET_SERIAL_NUMBER.build_command().add_element(
+ serial_number
+ )
+ connection.send_command.assert_any_call(set_serial_number)
+ connection.reset_mock()
+
+
+async def test_get_limit_switch(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a get limit switch command and return the boolean of one."""
+ connection.send_command.return_value = "M119 XE:1 XR:0 ZE:0 ZR:1 LR:1"
+ response = await subject.get_limit_switch(
+ types.StackerAxis.X, types.Direction.EXTENT
+ )
+ assert response
+
+ limit_switch_status = types.GCODE.GET_LIMIT_SWITCH.build_command()
+ connection.send_command.assert_any_call(limit_switch_status)
+ connection.reset_mock()
+
+
+async def test_get_limit_switches_status(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a get limit switch status and return LimitSwitchStatus."""
+ connection.send_command.return_value = "M119 XE:1 XR:0 ZE:0 ZR:1 LR:1"
+ response = await subject.get_limit_switches_status()
+ assert response == types.LimitSwitchStatus(
+ XE=True,
+ XR=False,
+ ZE=False,
+ ZR=True,
+ LR=True,
+ )
+
+ limit_switch_status = types.GCODE.GET_LIMIT_SWITCH.build_command()
+ connection.send_command.assert_any_call(limit_switch_status)
+ connection.reset_mock()
+
+ # Test invalid response
+ connection.send_command.return_value = "M119 XE:b XR:0 ZE:a ZR:1 LR:n"
+ with pytest.raises(ValueError):
+ response = await subject.get_limit_switches_status()
+
+ limit_switch_status = types.GCODE.GET_LIMIT_SWITCH.build_command()
+ connection.send_command.assert_any_call(limit_switch_status)
+
+
+async def test_get_platform_sensor(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a get platform sensor command return status of specified sensor."""
+ connection.send_command.return_value = "M121 E:1 R:1"
+ response = await subject.get_platform_sensor(types.Direction.EXTENT)
+ assert response
+
+ platform_sensor = types.GCODE.GET_PLATFORM_SENSOR.build_command()
+ connection.send_command.assert_any_call(platform_sensor)
+ connection.reset_mock()
+
+
+async def test_get_platform_status(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """it should send a get platform sensors status."""
+ connection.send_command.return_value = "M121 E:0 R:1"
+ response = await subject.get_platform_status()
+ assert response == types.PlatformStatus(
+ E=False,
+ R=True,
+ )
+
+ platform_status = types.GCODE.GET_PLATFORM_SENSOR.build_command()
+ connection.send_command.assert_any_call(platform_status)
+ connection.reset_mock()
+
+ # Test invalid response
+ connection.send_command.return_value = "M121 E:0 R:1 something"
+ with pytest.raises(ValueError):
+ response = await subject.get_platform_status()
+
+ platform_status = types.GCODE.GET_PLATFORM_SENSOR.build_command()
+ connection.send_command.assert_any_call(platform_status)
+
+
+async def test_get_hopper_door_closed(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a get door closed command."""
+ connection.send_command.return_value = "M122 D:1"
+ response = await subject.get_hopper_door_closed()
+ assert response
+
+ door_closed = types.GCODE.GET_DOOR_SWITCH.build_command()
+ connection.send_command.assert_any_call(door_closed)
+ connection.reset_mock()
+
+ # Test door open
+ connection.send_command.return_value = "M122 D:0"
+ response = await subject.get_hopper_door_closed()
+ assert not response
+
+ door_closed = types.GCODE.GET_DOOR_SWITCH.build_command()
+ connection.send_command.assert_any_call(door_closed)
+ connection.reset_mock()
+
+ # Test invalid response
+ connection.send_command.return_value = "M122 78gybhjk"
+
+ with pytest.raises(ValueError):
+ response = await subject.get_hopper_door_closed()
+
+ door_closed = types.GCODE.GET_DOOR_SWITCH.build_command()
+ connection.send_command.assert_any_call(door_closed)
+ connection.reset_mock()
+
+
+async def test_move_in_mm(subject: FlexStackerDriver, connection: AsyncMock) -> None:
+ """It should send a move to command"""
+ connection.send_command.return_value = "G0"
+ response = await subject.move_in_mm(types.StackerAxis.X, 10)
+ assert response
+
+ move_to = types.GCODE.MOVE_TO.build_command().add_float("X", 10)
+ connection.send_command.assert_any_call(move_to)
+ connection.reset_mock()
+
+
+async def test_move_to_switch(
+ subject: FlexStackerDriver, connection: AsyncMock
+) -> None:
+ """It should send a move to switch command"""
+ connection.send_command.return_value = "G5"
+ axis = types.StackerAxis.X
+ direction = types.Direction.EXTENT
+ response = await subject.move_to_limit_switch(axis, direction)
+ assert response
+
+ move_to = types.GCODE.MOVE_TO_SWITCH.build_command().add_int(
+ axis.name, direction.value
+ )
+ connection.send_command.assert_any_call(move_to)
+ connection.reset_mock()
+
+
+async def test_home_axis(subject: FlexStackerDriver, connection: AsyncMock) -> None:
+ """It should send a home axis command"""
+ connection.send_command.return_value = "G28"
+ axis = types.StackerAxis.X
+ direction = types.Direction.EXTENT
+ response = await subject.home_axis(axis, direction)
+ assert response
+
+ move_to = types.GCODE.HOME_AXIS.build_command().add_int(axis.name, direction.value)
+ connection.send_command.assert_any_call(move_to)
+ connection.reset_mock()
+
+
+async def test_set_led(subject: FlexStackerDriver, connection: AsyncMock) -> None:
+ """It should send a set led command"""
+ connection.send_command.return_value = "M200"
+ response = await subject.set_led(1, types.LEDColor.RED)
+ assert response
+
+ set_led = types.GCODE.SET_LED.build_command().add_float("P", 1).add_int("C", 1)
+ connection.send_command.assert_any_call(set_led)
+ connection.reset_mock()
From d36b284c294e753696399131eb6c68562f19f9dc Mon Sep 17 00:00:00 2001
From: koji
Date: Fri, 20 Dec 2024 14:31:59 -0500
Subject: [PATCH 12/18] fix(protocol-designer): replace text link style
(#17163)
* fix(protocol-designer): replace text link style
---
protocol-designer/src/atoms/constants.ts | 7 -------
protocol-designer/src/organisms/Alerts/ErrorContents.tsx | 6 +++---
.../src/organisms/EditInstrumentsModal/index.tsx | 6 +++---
protocol-designer/src/organisms/PipetteInfoItem/index.tsx | 6 +++---
.../src/organisms/TipPositionModal/ZTipPositionModal.tsx | 4 ++--
.../src/organisms/TipPositionModal/index.tsx | 6 +++---
protocol-designer/src/organisms/WellOrderModal/index.tsx | 4 ++--
.../src/pages/CreateNewProtocolWizard/SelectPipettes.tsx | 4 ++--
.../src/pages/CreateNewProtocolWizard/WizardBody.tsx | 4 ++--
.../src/pages/Designer/DeckSetup/DeckSetupTools.tsx | 4 ++--
.../src/pages/Designer/DeckSetup/LabwareTools.tsx | 4 ++--
.../Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx | 4 ++--
.../StepTools/ThermocyclerTools/ThermocyclerCycle.tsx | 8 ++++----
.../StepTools/ThermocyclerTools/ThermocyclerStep.tsx | 6 +++---
protocol-designer/src/pages/Landing/index.tsx | 4 ++--
.../src/pages/ProtocolOverview/InstrumentsInfo.tsx | 4 ++--
.../src/pages/ProtocolOverview/ProtocolMetadata.tsx | 4 ++--
.../src/pages/ProtocolOverview/StartingDeck.tsx | 4 ++--
protocol-designer/src/pages/Settings/index.tsx | 6 +++---
yarn.lock | 2 +-
20 files changed, 45 insertions(+), 52 deletions(-)
diff --git a/protocol-designer/src/atoms/constants.ts b/protocol-designer/src/atoms/constants.ts
index 67393baafd9..31fd22da638 100644
--- a/protocol-designer/src/atoms/constants.ts
+++ b/protocol-designer/src/atoms/constants.ts
@@ -6,13 +6,6 @@ import {
} from '@opentrons/components'
import type { FlattenSimpleInterpolation } from 'styled-components'
-export const BUTTON_LINK_STYLE = css`
- color: ${COLORS.grey60};
- &:hover {
- color: ${COLORS.grey40};
- }
-`
-
export const LINK_BUTTON_STYLE = css`
color: ${COLORS.black90};
diff --git a/protocol-designer/src/organisms/Alerts/ErrorContents.tsx b/protocol-designer/src/organisms/Alerts/ErrorContents.tsx
index 73dfa60f995..8320ae8ec1b 100644
--- a/protocol-designer/src/organisms/Alerts/ErrorContents.tsx
+++ b/protocol-designer/src/organisms/Alerts/ErrorContents.tsx
@@ -7,7 +7,7 @@ import {
SPACING,
TYPOGRAPHY,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { selectDesignerTab } from '../../file-data/actions'
import type { AlertLevel } from './types'
@@ -38,7 +38,7 @@ export const ErrorContents = (
{
dispatch(selectDesignerTab({ tab: 'startingDeck' }))
}}
@@ -58,7 +58,7 @@ export const ErrorContents = (
{
dispatch(selectDesignerTab({ tab: 'startingDeck' }))
}}
diff --git a/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx b/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx
index 0140abf8e70..5de4843883c 100644
--- a/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx
+++ b/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx
@@ -64,7 +64,7 @@ import { setFeatureFlags } from '../../feature-flags/actions'
import { createCustomTiprackDef } from '../../labware-defs/actions'
import { deleteContainer } from '../../labware-ingred/actions'
import { selectors as stepFormSelectors } from '../../step-forms'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { getMainPagePortalEl } from '../Portal'
import {
getSectionsFromPipetteName,
@@ -236,7 +236,7 @@ export function EditInstrumentsModal(
{has96Channel ||
(leftPipette == null && rightPipette == null) ? null : (
dispatch(
changeSavedStepForm({
@@ -354,7 +354,7 @@ export function EditInstrumentsModal(
@@ -80,7 +80,7 @@ export function PipetteInfoItem(props: PipetteInfoItemProps): JSX.Element {
cleanForm()
}}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
padding={SPACING.spacing4}
>
diff --git a/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx b/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx
index 7809a976313..81fb4ab3c2d 100644
--- a/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx
+++ b/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx
@@ -17,7 +17,7 @@ import {
} from '@opentrons/components'
import { DEFAULT_MM_BLOWOUT_OFFSET_FROM_TOP } from '../../constants'
import { getIsTouchTipField } from '../../form-types'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { getMainPagePortalEl } from '../Portal'
import * as utils from './utils'
import { TOO_MANY_DECIMALS } from './constants'
@@ -156,7 +156,7 @@ export function ZTipPositionModal(props: ZTipPositionModalProps): JSX.Element {
onClick={() => {
setValue(utils.roundValue(defaultMm, 'up').toString())
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{t('shared:reset_to_default')}
diff --git a/protocol-designer/src/organisms/TipPositionModal/index.tsx b/protocol-designer/src/organisms/TipPositionModal/index.tsx
index cdef7a6855b..cb06f4c2fc8 100644
--- a/protocol-designer/src/organisms/TipPositionModal/index.tsx
+++ b/protocol-designer/src/organisms/TipPositionModal/index.tsx
@@ -18,7 +18,7 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
import { getIsTouchTipField } from '../../form-types'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { getMainPagePortalEl } from '../Portal'
import { TOO_MANY_DECIMALS, PERCENT_RANGE_TO_SHOW_WARNING } from './constants'
import * as utils from './utils'
@@ -257,7 +257,7 @@ export function TipPositionModal(
setYValue('0')
setZValue('1')
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{t('shared:reset_to_default')}
@@ -331,7 +331,7 @@ export function TipPositionModal(
{
setView(view === 'side' ? 'top' : 'side')
}}
diff --git a/protocol-designer/src/organisms/WellOrderModal/index.tsx b/protocol-designer/src/organisms/WellOrderModal/index.tsx
index b7b181f6a72..54b4ad437ef 100644
--- a/protocol-designer/src/organisms/WellOrderModal/index.tsx
+++ b/protocol-designer/src/organisms/WellOrderModal/index.tsx
@@ -14,7 +14,7 @@ import {
DropdownMenu,
ALIGN_CENTER,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { getMainPagePortalEl } from '../Portal'
import { WellOrderVisualization } from './WellOrderVisualization'
import type { WellOrderOption } from '../../form-types'
@@ -164,7 +164,7 @@ export function WellOrderModal(props: WellOrderModalProps): JSX.Element | null {
padding={`0 ${SPACING.spacing24} ${SPACING.spacing24}`}
alignItems={ALIGN_CENTER}
>
-
+
{t('shared:reset_to_default')}
diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx
index bae902d4153..ff96f699267 100644
--- a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx
+++ b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx
@@ -38,7 +38,7 @@ import { setFeatureFlags } from '../../feature-flags/actions'
import { createCustomTiprackDef } from '../../labware-defs/actions'
import { useKitchen } from '../../organisms/Kitchen/hooks'
import { IncompatibleTipsModal, PipetteInfoItem } from '../../organisms'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { WizardBody } from './WizardBody'
import { PIPETTE_GENS, PIPETTE_TYPES, PIPETTE_VOLUMES } from './constants'
import { getTiprackOptions } from './utils'
@@ -450,7 +450,7 @@ export function SelectPipettes(props: WizardTileProps): JSX.Element | null {
(pipettesByMount.left.tiprackDefURI == null &&
pipettesByMount.right.tiprackDefURI == null) ? null : (
{
const leftPipetteName = pipettesByMount.left.pipetteName
const rightPipetteName = pipettesByMount.right.pipetteName
diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx
index d0c9c57cb37..54a629120a9 100644
--- a/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx
+++ b/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx
@@ -25,7 +25,7 @@ import three from '../../assets/images/onboarding_animation_3.webm'
import four from '../../assets/images/onboarding_animation_4.webm'
import five from '../../assets/images/onboarding_animation_5.webm'
import six from '../../assets/images/onboarding_animation_6.webm'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import type { RobotType } from '@opentrons/shared-data'
interface WizardBodyProps {
@@ -144,7 +144,7 @@ export function WizardBody(props: WizardBodyProps): JSX.Element {
alignItems={ALIGN_CENTER}
>
{goBack != null ? (
-
+
{t('go_back')}
diff --git a/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx b/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx
index c498a71534c..f5b1fe252cd 100644
--- a/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx
+++ b/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupTools.tsx
@@ -55,7 +55,7 @@ import { selectors } from '../../../labware-ingred/selectors'
import { useKitchen } from '../../../organisms/Kitchen/hooks'
import { getDismissedHints } from '../../../tutorial/selectors'
import { createContainerAboveModule } from '../../../step-forms/actions/thunks'
-import { BUTTON_LINK_STYLE, NAV_BAR_HEIGHT_REM } from '../../../atoms'
+import { LINK_BUTTON_STYLE, NAV_BAR_HEIGHT_REM } from '../../../atoms'
import { ConfirmDeleteStagingAreaModal } from '../../../organisms'
import { getSlotInformation } from '../utils'
import { ALL_ORDERED_CATEGORIES, FIXTURES, MOAM_MODELS } from './constants'
@@ -438,7 +438,7 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null {
handleResetToolbox()
}
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
>
diff --git a/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx b/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx
index ca8feac72d1..d41f2057502 100644
--- a/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx
+++ b/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx
@@ -32,7 +32,7 @@ import {
getModuleType,
} from '@opentrons/shared-data'
-import { BUTTON_LINK_STYLE } from '../../../atoms'
+import { LINK_BUTTON_STYLE } from '../../../atoms'
import { selectors as stepFormSelectors } from '../../../step-forms'
import { getOnlyLatestDefs } from '../../../labware-defs'
import {
@@ -491,7 +491,7 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element {
alignItems={ALIGN_CENTER}
justifyContent={JUSTIFY_CENTER}
>
-
+
{t('upload_custom_labware')}
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx
index 307e9e4aeac..475340bf2d4 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx
@@ -18,7 +18,7 @@ import {
} from '@opentrons/components'
import { stepIconsByType } from '../../../../form-types'
import {
- BUTTON_LINK_STYLE,
+ LINK_BUTTON_STYLE,
LINE_CLAMP_TEXT_STYLE,
NAV_BAR_HEIGHT_REM,
} from '../../../../atoms'
@@ -299,7 +299,7 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element {
onClick={() => {
setIsRename(true)
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
>
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx
index 9c0e356c158..96d0007e096 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx
@@ -20,7 +20,7 @@ import {
StyledText,
TYPOGRAPHY,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE } from '../../../../../../atoms'
+import { LINK_BUTTON_STYLE } from '../../../../../../atoms'
import {
isTimeFormatMinutesSeconds,
temperatureRangeFieldValue,
@@ -262,7 +262,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element {
whiteSpace={NO_WRAP}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
padding={SPACING.spacing4}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{i18n.format(
@@ -329,7 +329,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element {
}}
padding={SPACING.spacing4}
css={[
- BUTTON_LINK_STYLE,
+ LINK_BUTTON_STYLE,
css`
visibility: ${hover ? 'visible' : 'hidden'};
opacity: ${hover ? 1 : 0};
@@ -527,7 +527,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element {
whiteSpace={NO_WRAP}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
padding={SPACING.spacing4}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{i18n.format(
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx
index 22dda2fc368..3e45704c94d 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx
@@ -18,7 +18,7 @@ import {
StyledText,
TYPOGRAPHY,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE } from '../../../../../../atoms'
+import { LINK_BUTTON_STYLE } from '../../../../../../atoms'
import {
temperatureRangeFieldValue,
isTimeFormatMinutesSeconds,
@@ -160,7 +160,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element {
whiteSpace={NO_WRAP}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
padding={SPACING.spacing4}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{i18n.format(
@@ -221,7 +221,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element {
}}
padding={SPACING.spacing4}
css={[
- BUTTON_LINK_STYLE,
+ LINK_BUTTON_STYLE,
css`
visibility: ${hover ? 'visible' : 'hidden'};
opacity: ${hover ? 1 : 0};
diff --git a/protocol-designer/src/pages/Landing/index.tsx b/protocol-designer/src/pages/Landing/index.tsx
index a1ca26b867c..48d2eb27ead 100644
--- a/protocol-designer/src/pages/Landing/index.tsx
+++ b/protocol-designer/src/pages/Landing/index.tsx
@@ -17,7 +17,7 @@ import {
StyledText,
TYPOGRAPHY,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { AnnouncementModal } from '../../organisms'
import { actions as loadFileActions } from '../../load-file'
import { getFileMetadata } from '../../file-data/selectors'
@@ -137,7 +137,7 @@ export function Landing(): JSX.Element {
/>
-
+
{t('edit_existing')}
diff --git a/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx b/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx
index 63ce567a805..ee40f77aef4 100644
--- a/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx
+++ b/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx
@@ -14,7 +14,7 @@ import {
} from '@opentrons/components'
import { getPipetteSpecsV2, FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import type { PipetteName, RobotType } from '@opentrons/shared-data'
import type { AdditionalEquipmentEntities } from '@opentrons/step-generation'
@@ -87,7 +87,7 @@ export function InstrumentsInfo({
onClick={() => {
setShowEditInstrumentsModal(true)
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{t('edit')}
diff --git a/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx b/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx
index d750edaaad5..564c99d9f89 100644
--- a/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx
+++ b/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx
@@ -12,7 +12,7 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE, LINE_CLAMP_TEXT_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE, LINE_CLAMP_TEXT_STYLE } from '../../atoms'
const REQUIRED_APP_VERSION = '8.2.0'
@@ -46,7 +46,7 @@ export function ProtocolMetadata({
onClick={() => {
setShowEditMetadataModal(true)
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
data-testid="ProtocolOverview_MetadataEditButton"
>
diff --git a/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx b/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx
index 8dac5003b6f..48e3cb178b4 100644
--- a/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx
+++ b/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx
@@ -15,7 +15,7 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { SlotDetailsContainer } from '../../organisms'
import { getInitialDeckSetup } from '../../step-forms/selectors'
import { DeckThumbnail } from './DeckThumbnail'
@@ -72,7 +72,7 @@ function StartingDeckHeader(props: StartingDeckHeaderProps): JSX.Element {
onClick={() => {
setShowMaterialsListModal(true)
}}
- css={BUTTON_LINK_STYLE}
+ css={LINK_BUTTON_STYLE}
>
{t('protocol_overview:materials_list')}
diff --git a/protocol-designer/src/pages/Settings/index.tsx b/protocol-designer/src/pages/Settings/index.tsx
index 63519226a92..fa364525f99 100644
--- a/protocol-designer/src/pages/Settings/index.tsx
+++ b/protocol-designer/src/pages/Settings/index.tsx
@@ -27,7 +27,7 @@ import {
selectors as tutorialSelectors,
} from '../../tutorial'
import { ToggleButton } from '../../atoms/ToggleButton'
-import { BUTTON_LINK_STYLE } from '../../atoms'
+import { LINK_BUTTON_STYLE } from '../../atoms'
import { actions as featureFlagActions } from '../../feature-flags'
import { getFeatureFlagData } from '../../feature-flags/selectors'
import type { FlagTypes } from '../../feature-flags'
@@ -142,7 +142,7 @@ export function Settings(): JSX.Element {
{
setShowAnnouncementModal(true)
diff --git a/yarn.lock b/yarn.lock
index 56ebcbf301a..d58c1ef61ac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -22739,7 +22739,7 @@ vitest-when@0.5.0:
dependencies:
pretty-format "^29.7.0"
-vitest@^2.1.8:
+vitest@2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa"
integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==
From 35422b60c5feeae7258396de46e6bd6e0d4cb828 Mon Sep 17 00:00:00 2001
From: koji
Date: Fri, 20 Dec 2024 15:01:02 -0500
Subject: [PATCH 13/18] feat(protocol-designer): make timeline responsive
(#17109)
* feat(protocol-designer): make timeline responsive
---
.../src/assets/localization/en/button.json | 2 +-
.../ProtocolSteps/DraggableSidebar.tsx | 119 ++++++++++++++++++
.../ProtocolSteps/Timeline/AddStepButton.tsx | 59 ++++++---
.../Timeline/ConnectedStepInfo.tsx | 3 +
.../ProtocolSteps/Timeline/DraggableSteps.tsx | 18 ++-
.../ProtocolSteps/Timeline/PresavedStep.tsx | 9 +-
.../ProtocolSteps/Timeline/StepContainer.tsx | 32 +++--
...ubstepsToolbox.tsx => SubStepsToolbox.tsx} | 6 +-
.../Timeline/TerminalItemStep.tsx | 10 +-
.../Timeline/TimelineToolbox.tsx | 36 ++++--
.../Timeline/__tests__/AddStepButton.test.tsx | 24 +++-
.../Timeline/__tests__/StepContainer.test.tsx | 1 +
.../__tests__/TimelineToolbox.test.tsx | 16 ++-
.../Designer/ProtocolSteps/Timeline/index.ts | 2 +-
.../Designer/ProtocolSteps/Timeline/utils.ts | 1 +
.../__tests__/DraggableSidebar.test.tsx | 41 ++++++
.../__tests__/ProtocolSteps.test.tsx | 15 ++-
.../pages/Designer/ProtocolSteps/index.tsx | 20 +--
18 files changed, 343 insertions(+), 71 deletions(-)
create mode 100644 protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx
rename protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/{SubstepsToolbox.tsx => SubStepsToolbox.tsx} (96%)
create mode 100644 protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx
diff --git a/protocol-designer/src/assets/localization/en/button.json b/protocol-designer/src/assets/localization/en/button.json
index bedb95b28c1..af643b9b815 100644
--- a/protocol-designer/src/assets/localization/en/button.json
+++ b/protocol-designer/src/assets/localization/en/button.json
@@ -1,5 +1,5 @@
{
- "add_step": "+ Add Step",
+ "add_step": "Add Step",
"add_off_deck": "+ Off-deck labware",
"cancel": "cancel",
"close": "close",
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx
new file mode 100644
index 00000000000..210abdec6ba
--- /dev/null
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx
@@ -0,0 +1,119 @@
+import { useState, useRef, useCallback, useEffect } from 'react'
+import styled from 'styled-components'
+import {
+ Box,
+ COLORS,
+ DIRECTION_COLUMN,
+ DISPLAY_FLEX,
+ Flex,
+ JUSTIFY_SPACE_BETWEEN,
+} from '@opentrons/components'
+import { TimelineToolbox } from './Timeline/TimelineToolbox'
+
+const INITIAL_SIDEBAR_WIDTH = 276
+const MIN_SIDEBAR_WIDTH = 80
+const MAX_SIDEBAR_WIDTH = 350
+
+interface DraggableSidebarProps {
+ setTargetWidth: (width: number) => void
+}
+
+// Note (kk:2024/12/20 the designer will revisit responsive sidebar design in 2025
+// we will need to update the details to align with the updated design
+export function DraggableSidebar({
+ setTargetWidth,
+}: DraggableSidebarProps): JSX.Element {
+ const sidebarRef = useRef(null)
+ const [isResizing, setIsResizing] = useState(false)
+ const [sidebarWidth, setSidebarWidth] = useState(INITIAL_SIDEBAR_WIDTH)
+
+ const startResizing = useCallback(() => {
+ setIsResizing(true)
+ }, [])
+
+ const stopResizing = useCallback(() => {
+ setIsResizing(false)
+ }, [])
+
+ const resize = useCallback(
+ (mouseMoveEvent: MouseEvent) => {
+ if (isResizing && sidebarRef.current != null) {
+ const newWidth =
+ mouseMoveEvent.clientX -
+ sidebarRef.current.getBoundingClientRect().left
+
+ if (newWidth >= MIN_SIDEBAR_WIDTH && newWidth <= MAX_SIDEBAR_WIDTH) {
+ setSidebarWidth(newWidth)
+ setTargetWidth(newWidth)
+ }
+ }
+ },
+ [isResizing, setTargetWidth]
+ )
+
+ useEffect(() => {
+ window.addEventListener('mousemove', resize)
+ window.addEventListener('mouseup', stopResizing)
+
+ return () => {
+ window.removeEventListener('mousemove', resize)
+ window.removeEventListener('mouseup', stopResizing)
+ }
+ }, [resize, stopResizing])
+
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+const SidebarContainer = styled(Box)`
+ display: ${DISPLAY_FLEX};
+ flex-direction: ${DIRECTION_COLUMN};
+ border-right: 1px solid #ccc;
+ position: relative;
+ /* overflow: hidden; */
+ height: 100%;
+`
+
+const SidebarContent = styled(Flex)`
+ flex: 1;
+`
+
+interface SidebarResizerProps {
+ dragging: boolean
+}
+
+const SidebarResizer = styled(Flex)`
+ user-select: none;
+ width: 2px;
+ cursor: ew-resize;
+ background-color: #ddd;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ margin: 0;
+ padding: 0;
+ transition: background-color 0.2s ease;
+
+ &:hover {
+ background-color: ${COLORS.blue50}; /* Hover state */
+ }
+
+ ${props =>
+ props.dragging === true &&
+ `
+ background-color: ${COLORS.blue55}; /* Dragging state */
+ `}
+`
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx
index f9c2aa9d395..e9494eb6e44 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx
@@ -2,19 +2,27 @@ import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
+import { css } from 'styled-components'
+
import {
- useHoverTooltip,
- TOOLTIP_TOP,
- TOOLTIP_FIXED,
- Tooltip,
+ ALIGN_CENTER,
+ BORDERS,
COLORS,
DIRECTION_COLUMN,
+ DISPLAY_FLEX,
Flex,
- POSITION_ABSOLUTE,
- BORDERS,
+ Icon,
+ JUSTIFY_CENTER,
NO_WRAP,
- useOnClickOutside,
+ POSITION_ABSOLUTE,
SecondaryButton,
+ SPACING,
+ StyledText,
+ TOOLTIP_FIXED,
+ TOOLTIP_TOP,
+ Tooltip,
+ useHoverTooltip,
+ useOnClickOutside,
} from '@opentrons/components'
import {
ABSORBANCE_READER_TYPE,
@@ -23,6 +31,7 @@ import {
TEMPERATURE_MODULE_TYPE,
THERMOCYCLER_MODULE_TYPE,
} from '@opentrons/shared-data'
+
import {
actions as stepsActions,
getIsMultiSelectMode,
@@ -40,7 +49,6 @@ import {
getEnableAbsorbanceReader,
getEnableComment,
} from '../../../../feature-flags/selectors'
-
import { AddStepOverflowButton } from './AddStepOverflowButton'
import type { MouseEvent } from 'react'
@@ -48,7 +56,11 @@ import type { ThunkDispatch } from 'redux-thunk'
import type { BaseState } from '../../../../types'
import type { StepType } from '../../../../form-types'
-export function AddStepButton(): JSX.Element {
+interface AddStepButtonProps {
+ hasText: boolean
+}
+
+export function AddStepButton({ hasText }: AddStepButtonProps): JSX.Element {
const { t } = useTranslation(['tooltip', 'button'])
const enableComment = useSelector(getEnableComment)
const dispatch = useDispatch>()
@@ -151,16 +163,8 @@ export function AddStepButton(): JSX.Element {
{showStepOverflowMenu ? (
{
e.preventDefault()
e.stopPropagation()
@@ -176,6 +180,10 @@ export function AddStepButton(): JSX.Element {
)}
- {t('button:add_step')}
+
+ {hasText ? {t('button:add_step')} : null}
>
)
}
+
+const STEP_OVERFLOW_MENU_STYLE = css`
+ position: ${POSITION_ABSOLUTE};
+ z-index: 5;
+ right: -7.75rem;
+ white-space: ${NO_WRAP};
+ bottom: 4.2rem;
+ border-radius: ${BORDERS.borderRadius8};
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2);
+ background-color: ${COLORS.white};
+ flex-direction: ${DIRECTION_COLUMN};
+`
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx
index 70cdf70a984..1a9ac29f33d 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx
@@ -49,6 +49,7 @@ export interface ConnectedStepInfoProps {
dragHovered?: boolean
openedOverflowMenuId?: string | null
setOpenedOverflowMenuId?: Dispatch>
+ sidebarWidth: number
}
export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
@@ -58,6 +59,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
dragHovered = false,
openedOverflowMenuId,
setOpenedOverflowMenuId,
+ sidebarWidth,
} = props
const { t } = useTranslation('application')
const dispatch = useDispatch>()
@@ -227,6 +229,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
step.stepName || t(`stepType.${step.stepType}`)
}`}
dragHovered={dragHovered}
+ sidebarWidth={sidebarWidth}
/>
>
)
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx
index 02e1ad772fa..592cdec02f3 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx
@@ -31,6 +31,7 @@ interface DragDropStepProps extends ConnectedStepItemProps {
orderedStepIds: string[]
openedOverflowMenuId?: string | null
setOpenedOverflowMenuId?: Dispatch>
+ sidebarWidth: number
}
interface DropType {
@@ -46,6 +47,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element {
stepNumber,
openedOverflowMenuId,
setOpenedOverflowMenuId,
+ sidebarWidth,
} = props
const stepRef = useRef(null)
@@ -94,6 +96,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element {
stepNumber={stepNumber}
stepId={stepId}
dragHovered={hovered}
+ sidebarWidth={sidebarWidth}
/>
)
@@ -102,9 +105,10 @@ function DragDropStep(props: DragDropStepProps): JSX.Element {
interface DraggableStepsProps {
orderedStepIds: StepIdType[]
reorderSteps: (steps: StepIdType[]) => void
+ sidebarWidth: number
}
export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null {
- const { orderedStepIds, reorderSteps } = props
+ const { orderedStepIds, reorderSteps, sidebarWidth } = props
const { t } = useTranslation('shared')
const [openedOverflowMenuId, setOpenedOverflowMenuId] = useState<
string | null
@@ -146,14 +150,21 @@ export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null {
orderedStepIds={orderedStepIds}
openedOverflowMenuId={openedOverflowMenuId}
setOpenedOverflowMenuId={setOpenedOverflowMenuId}
+ sidebarWidth={sidebarWidth}
/>
))}
-
+
)
}
-function StepDragPreview(): JSX.Element | null {
+interface StepDragPreviewProps {
+ sidebarWidth: number
+}
+
+function StepDragPreview({
+ sidebarWidth,
+}: StepDragPreviewProps): JSX.Element | null {
const [{ isDragging, itemType, item, currentOffset }] = useDrag(() => ({
type: DND_TYPES.STEP_ITEM,
collect: (monitor: DragLayerMonitor) => ({
@@ -182,6 +193,7 @@ function StepDragPreview(): JSX.Element | null {
)
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx
index 9d0289ddb9c..346c296855f 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx
@@ -10,7 +10,13 @@ import {
} from '../../../../ui/steps'
import { StepContainer } from './StepContainer'
-export function PresavedStep(): JSX.Element | null {
+interface PresavedStepProps {
+ sidebarWidth: number
+}
+
+export function PresavedStep({
+ sidebarWidth,
+}: PresavedStepProps): JSX.Element | null {
const { t } = useTranslation('application')
const presavedStepForm = useSelector(stepFormSelectors.getPresavedStepForm)
const stepNumber = useSelector(stepFormSelectors.getOrderedStepIds).length + 1
@@ -39,6 +45,7 @@ export function PresavedStep(): JSX.Element | null {
hovered={hovered}
iconName={stepIconsByType[stepType]}
title={`${stepNumber}. ${t(`stepType.${stepType}`)}`}
+ sidebarWidth={sidebarWidth}
/>
)
}
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx
index 3db0802fbd5..d0534b234cf 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx
@@ -12,6 +12,7 @@ import {
Divider,
Flex,
Icon,
+ JUSTIFY_CENTER,
JUSTIFY_SPACE_BETWEEN,
JUSTIFY_START,
OverflowBtn,
@@ -48,9 +49,12 @@ import type { BaseState } from '../../../../types'
const STARTING_DECK_STATE = 'Starting deck'
const FINAL_DECK_STATE = 'Ending deck'
const PX_HEIGHT_TO_TOP_OF_CONTAINER = 32
+const PX_SIDEBAR_MIN_WIDTH_FOR_ICON = 179
+
export interface StepContainerProps {
title: string
iconName: IconName
+ sidebarWidth: number
openedOverflowMenuId?: string | null
setOpenedOverflowMenuId?: Dispatch>
stepId?: string
@@ -83,6 +87,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
dragHovered = false,
setOpenedOverflowMenuId,
openedOverflowMenuId,
+ sidebarWidth,
} = props
const [top, setTop] = useState(0)
const menuRootRef = useRef(null)
@@ -91,6 +96,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
const dispatch = useDispatch>()
const multiSelectItemIds = useSelector(getMultiSelectItemIds)
+ const hasText = sidebarWidth > PX_SIDEBAR_MIN_WIDTH_FOR_ICON
let backgroundColor = isStartingOrEndingState ? COLORS.blue20 : COLORS.grey20
let color = COLORS.black90
if (selected) {
@@ -183,14 +189,14 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
return (
<>
- {showDeleteConfirmation && (
+ {showDeleteConfirmation === true && (
)}
- {showMultiDeleteConfirmation && (
+ {showMultiDeleteConfirmation === true && (
- {iconName && (
+ {iconName != null && (
)}
-
- {capitalizeFirstLetterAfterNumber(title)}
-
+ {hasText ? (
+
+ {capitalizeFirstLetterAfterNumber(title)}
+
+ ) : null}
{selected && !isStartingOrEndingState ? (
>
)
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx
index 4f99cda8f54..7e7b0d83b6c 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import {
DIRECTION_COLUMN,
Flex,
+ OVERFLOW_WRAP_ANYWHERE,
POSITION_RELATIVE,
SPACING,
StyledText,
@@ -27,7 +28,14 @@ import { DraggableSteps } from './DraggableSteps'
import type { StepIdType } from '../../../../form-types'
import type { ThunkDispatch } from '../../../../types'
-export const TimelineToolbox = (): JSX.Element => {
+const SIDEBAR_MIN_WIDTH_FOR_ICON = 179
+interface TimelineToolboxProps {
+ sidebarWidth: number
+}
+
+export const TimelineToolbox = ({
+ sidebarWidth,
+}: TimelineToolboxProps): JSX.Element => {
const { t } = useTranslation('protocol_steps')
const orderedStepIds = useSelector(stepFormSelectors.getOrderedStepIds)
const formData = useSelector(getUnsavedForm)
@@ -64,30 +72,44 @@ export const TimelineToolbox = (): JSX.Element => {
position={POSITION_RELATIVE}
height="100%"
maxHeight={`calc(100vh - ${NAV_BAR_HEIGHT_REM}rem - 2 * ${SPACING.spacing12})`}
- width="19.5rem"
+ width={`${sidebarWidth / 16}rem`}
title={
-
+
{t('timeline')}
}
titlePadding={SPACING.spacing12}
childrenPadding={SPACING.spacing12}
- confirmButton={formData != null ? undefined : }
+ confirmButton={
+ formData != null ? undefined : (
+ SIDEBAR_MIN_WIDTH_FOR_ICON} />
+ )
+ }
>
-
+
{
dispatch(steplistActions.reorderSteps(stepIds))
}}
+ sidebarWidth={sidebarWidth}
+ />
+
+
-
-
)
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx
index 4b7b716b0c7..a2fcea0a7e2 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx
@@ -1,4 +1,4 @@
-import { describe, it, vi, beforeEach } from 'vitest'
+import { describe, it, vi, beforeEach, expect } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import {
HEATERSHAKER_MODULE_TYPE,
@@ -20,18 +20,25 @@ import {
} from '../../../../../step-forms/selectors'
import { getIsMultiSelectMode } from '../../../../../ui/steps'
+import type { ComponentProps } from 'react'
+
vi.mock('../../../../../feature-flags/selectors')
vi.mock('../../../../../ui/steps')
vi.mock('../../../../../step-forms/selectors')
-const render = () => {
- return renderWithProviders(, {
+const render = (props: ComponentProps) => {
+ return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('AddStepButton', () => {
+ let props: ComponentProps
+
beforeEach(() => {
+ props = {
+ hasText: true,
+ }
vi.mocked(getEnableComment).mockReturnValue(true)
vi.mocked(getCurrentFormIsPresaved).mockReturnValue(false)
vi.mocked(getIsMultiSelectMode).mockReturnValue(false)
@@ -73,8 +80,8 @@ describe('AddStepButton', () => {
})
it('renders add step button and clicking on it renders the overflow menu with all modules', () => {
- render()
- fireEvent.click(screen.getByText('+ Add Step'))
+ render(props)
+ fireEvent.click(screen.getByText('Add Step'))
screen.getByText('Comment')
screen.getByText('Transfer')
screen.getByText('Mix')
@@ -84,4 +91,11 @@ describe('AddStepButton', () => {
screen.getByText('Temperature')
screen.getByText('Magnet')
})
+
+ it('should not render texts if hasText is false', () => {
+ props.hasText = false
+ render(props)
+ const text = screen.queryByText('Add Step')
+ expect(text).toBeNull()
+ })
})
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx
index 32df562e61a..4933dffe3ed 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx
@@ -32,6 +32,7 @@ describe('StepContainer', () => {
stepId: 'mockStepId',
hasError: false,
isStepAfterError: false,
+ sidebarWidth: 350,
}
vi.mocked(StepOverflowMenu).mockReturnValue(
mock StepOverflowMenu
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx
index c4ae078c572..f6b11ce36a5 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx
@@ -6,25 +6,32 @@ import {
getOrderedStepIds,
getUnsavedForm,
} from '../../../../../step-forms/selectors'
-import { TimelineToolbox } from '../TimelineToolbox'
import { TerminalItemStep } from '../TerminalItemStep'
import { DraggableSteps } from '../DraggableSteps'
import { PresavedStep } from '../PresavedStep'
import { AddStepButton } from '../AddStepButton'
+import { TimelineToolbox } from '../TimelineToolbox'
+
+import type { ComponentProps } from 'react'
vi.mock('../AddStepButton')
vi.mock('../DraggableSteps')
vi.mock('../PresavedStep')
vi.mock('../TerminalItemStep')
vi.mock('../../../../../step-forms/selectors')
-const render = () => {
- return renderWithProviders(, {
+const render = (props: ComponentProps) => {
+ return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('TimelineToolbox', () => {
+ let props: ComponentProps
+
beforeEach(() => {
+ props = {
+ sidebarWidth: 350,
+ }
vi.mocked(getOrderedStepIds).mockReturnValue(['mock1Step'])
vi.mocked(getUnsavedForm).mockReturnValue(null)
vi.mocked(TerminalItemStep).mockReturnValue(
@@ -34,8 +41,9 @@ describe('TimelineToolbox', () => {
vi.mocked(PresavedStep).mockReturnValue(mock PresavedStep
)
vi.mocked(AddStepButton).mockReturnValue(mock AddStepButton
)
})
+
it('renders 2 terminal item steps, a draggable step and presaved step with toolbox title', () => {
- render()
+ render(props)
screen.getByText('Timeline')
screen.getByText('mock AddStepButton')
screen.getByText('mock PresavedStep')
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts
index 2b4945b756b..14a0f0058af 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts
@@ -1,2 +1,2 @@
-export * from './SubstepsToolbox'
+export * from './SubStepsToolbox'
export * from './TimelineToolbox'
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts
index 2d918b7790f..b12d5598ca1 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts
@@ -1,6 +1,7 @@
import round from 'lodash/round'
import uniq from 'lodash/uniq'
import { UAParser } from 'ua-parser-js'
+
import type { StepIdType } from '../../../../form-types'
export const capitalizeFirstLetterAfterNumber = (title: string): string =>
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx
new file mode 100644
index 00000000000..4ea6b03d2ab
--- /dev/null
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx
@@ -0,0 +1,41 @@
+import { describe, it, vi, beforeEach } from 'vitest'
+import { screen } from '@testing-library/react'
+
+import { i18n } from '../../../../assets/localization'
+import { renderWithProviders } from '../../../../__testing-utils__'
+import { DraggableSidebar } from '../DraggableSidebar'
+
+import type { ComponentProps } from 'react'
+
+vi.mock('../../../../step-forms/selectors')
+vi.mock('../../../../ui/steps/selectors')
+vi.mock('../../../../feature-flags/selectors')
+vi.mock('../Timeline/DraggableSteps')
+vi.mock('../Timeline/PresavedStep')
+vi.mock('../Timeline/AddStepButton')
+
+const mockSetTargetWidth = vi.fn()
+
+const render = (props: ComponentProps) => {
+ return renderWithProviders(, {
+ i18nInstance: i18n,
+ })
+}
+
+describe('DraggableSidebar', () => {
+ let props: ComponentProps
+ beforeEach(() => {
+ props = {
+ setTargetWidth: mockSetTargetWidth,
+ }
+ })
+
+ it('renders initial timeline toolbox', () => {
+ render(props)
+ screen.getByText('Timeline')
+ screen.getByText('Starting deck')
+ screen.getByText('Ending deck')
+ })
+
+ // ToDo (kk: 2024/12/12): Add more tests
+})
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx
index 9ff98460fc6..31c1c93eafc 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx
@@ -19,8 +19,10 @@ import {
import { getEnableHotKeysDisplay } from '../../../../feature-flags/selectors'
import { DeckSetupContainer } from '../../DeckSetup'
import { OffDeck } from '../../Offdeck'
+import { SubStepsToolbox } from '../Timeline'
+import { DraggableSidebar } from '../DraggableSidebar'
import { ProtocolSteps } from '..'
-import { SubstepsToolbox, TimelineToolbox } from '../Timeline'
+
import type { SavedStepFormState } from '../../../../step-forms'
vi.mock('../../Offdeck')
@@ -31,6 +33,7 @@ vi.mock('../StepForm')
vi.mock('../../DeckSetup')
vi.mock('../StepSummary.tsx')
vi.mock('../Timeline')
+vi.mock('../DraggableSidebar')
vi.mock('../../../../feature-flags/selectors')
vi.mock('../../../../file-data/selectors')
vi.mock('../../../../organisms/Alerts')
@@ -64,7 +67,9 @@ describe('ProtocolSteps', () => {
timeline: [],
errors: [],
})
- vi.mocked(TimelineToolbox).mockReturnValue(mock TimelineToolbox
)
+ vi.mocked(DraggableSidebar).mockReturnValue(
+ mock DraggableSidebar
+ )
vi.mocked(DeckSetupContainer).mockReturnValue(
mock DeckSetupContainer
)
@@ -72,7 +77,7 @@ describe('ProtocolSteps', () => {
vi.mocked(OffDeck).mockReturnValue(mock OffDeck
)
vi.mocked(getUnsavedForm).mockReturnValue(null)
vi.mocked(getSelectedSubstep).mockReturnValue(null)
- vi.mocked(SubstepsToolbox).mockReturnValue(mock SubstepsToolbox
)
+ vi.mocked(SubStepsToolbox).mockReturnValue(mock SubStepsToolbox
)
vi.mocked(getEnableHotKeysDisplay).mockReturnValue(true)
vi.mocked(getSavedStepForms).mockReturnValue(
MOCK_STEP_FORMS as SavedStepFormState
@@ -84,7 +89,7 @@ describe('ProtocolSteps', () => {
it('renders each component in ProtocolSteps', () => {
render()
- screen.getByText('mock TimelineToolbox')
+ screen.getByText('mock DraggableSidebar')
screen.getByText('mock DeckSetupContainer')
})
@@ -98,7 +103,7 @@ describe('ProtocolSteps', () => {
it('renders the substepToolbox when selectedSubstep is not null', () => {
vi.mocked(getSelectedSubstep).mockReturnValue('mockId')
render()
- screen.getByText('mock SubstepsToolbox')
+ screen.getByText('mock SubStepsToolbox')
})
it('renders the hot keys display', () => {
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx
index 97f337c2dcd..38f4979b0cf 100644
--- a/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx
+++ b/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx
@@ -1,6 +1,7 @@
import { useState } from 'react'
import { useSelector } from 'react-redux'
import { useTranslation } from 'react-i18next'
+
import {
ALIGN_CENTER,
COLORS,
@@ -10,6 +11,7 @@ import {
JUSTIFY_CENTER,
JUSTIFY_SPACE_BETWEEN,
POSITION_FIXED,
+ POSITION_RELATIVE,
SPACING,
StyledText,
Tag,
@@ -30,7 +32,7 @@ import {
} from '../../../ui/steps/selectors'
import { DeckSetupContainer } from '../DeckSetup'
import { OffDeck } from '../Offdeck'
-import { TimelineToolbox, SubstepsToolbox } from './Timeline'
+import { SubStepsToolbox } from './Timeline'
import { StepForm } from './StepForm'
import { StepSummary } from './StepSummary'
import { BatchEditToolbox } from './BatchEditToolbox'
@@ -39,6 +41,7 @@ import {
getRobotStateTimeline,
} from '../../../file-data/selectors'
import { TimelineAlerts } from '../../../organisms'
+import { DraggableSidebar } from './DraggableSidebar'
const CONTENT_MAX_WIDTH = '44.6704375rem'
@@ -56,6 +59,7 @@ export function ProtocolSteps(): JSX.Element {
const [deckView, setDeckView] = useState<
typeof leftString | typeof rightString
>(leftString)
+ const [targetWidth, setTargetWidth] = useState(350)
const currentHoveredStepId = useSelector(getHoveredStepId)
const currentSelectedStepId = useSelector(getSelectedStepId)
@@ -82,15 +86,17 @@ export function ProtocolSteps(): JSX.Element {
width="100%"
padding={SPACING.spacing12}
gridGap={SPACING.spacing16}
- justifyContent={JUSTIFY_SPACE_BETWEEN}
>
-
+
+
+
{formData == null && selectedSubstep ? (
-
+
) : null}
{isMultiSelectMode ? : null}
From 03fbfc894b111a62b3faa0ac62676270ab0e9b9c Mon Sep 17 00:00:00 2001
From: koji
Date: Mon, 23 Dec 2024 16:44:39 -0500
Subject: [PATCH 14/18] chore(app): update import statements for react (#17170)
* chore(app): update import statements for react
---
app/src/App/Navbar.tsx | 3 +-
app/src/App/__mocks__/portal.tsx | 4 +--
app/src/App/__tests__/hooks.test.tsx | 4 +--
app/src/App/index.tsx | 5 ++--
app/src/App/types.ts | 4 +--
app/src/LocalizationProvider.tsx | 4 +--
.../__testing-utils__/renderWithProviders.tsx | 13 +++++---
.../__tests__/InlineNotification.test.tsx | 7 +++--
app/src/atoms/InlineNotification/index.tsx | 6 ++--
.../__tests__/InstrumentContainer.test.tsx | 7 +++--
.../Link/__tests__/ExternalLink.test.tsx | 7 +++--
.../__tests__/ProgressBar.test.tsx | 7 +++--
app/src/atoms/ProgressBar/index.tsx | 4 +--
app/src/atoms/SelectField/index.tsx | 6 ++--
.../Skeleton/__tests__/Skeleton.test.tsx | 5 ++--
.../Slideout/__tests__/Slideout.test.tsx | 7 +++--
app/src/atoms/Slideout/index.tsx | 8 +++--
.../AlphanumericKeyboard/index.tsx | 4 ++-
.../SoftwareKeyboard/IndividualKey/index.tsx | 6 ++--
.../NumericalKeyboard/index.tsx | 5 ++--
.../__tests__/StatusLabel.test.tsx | 7 +++--
.../StepMeter/__tests__/StepMeter.test.tsx | 7 +++--
app/src/atoms/buttons/BackButton.tsx | 5 ++--
.../atoms/buttons/FloatingActionButton.tsx | 4 +--
app/src/atoms/buttons/IconButton.tsx | 7 +++--
app/src/atoms/buttons/MediumButton.tsx | 6 ++--
app/src/atoms/buttons/SmallButton.tsx | 7 +++--
app/src/atoms/buttons/SubmitPrimaryButton.tsx | 5 ++--
app/src/atoms/buttons/TextOnlyButton.tsx | 7 +++--
app/src/atoms/buttons/ToggleButton.tsx | 10 +++----
.../buttons/__tests__/BackButton.test.tsx | 5 ++--
.../__tests__/FloatingActionButton.test.tsx | 7 +++--
.../buttons/__tests__/MediumButton.test.tsx | 7 +++--
.../__tests__/QuaternaryButton.test.tsx | 7 +++--
.../buttons/__tests__/SmallButton.test.tsx | 7 +++--
.../__tests__/SubmitPrimaryButton.test.tsx | 6 ++--
.../buttons/__tests__/TertiaryButton.test.tsx | 7 +++--
.../buttons/__tests__/ToggleButton.test.tsx | 7 +++--
app/src/atoms/structure/Divider.tsx | 4 +--
app/src/atoms/structure/Line.tsx | 4 +--
.../structure/__tests__/Divider.test.tsx | 7 +++--
.../atoms/structure/__tests__/Line.test.tsx | 7 +++--
.../__tests__/BackgroundOverlay.test.tsx | 7 +++--
app/src/molecules/BackgroundOverlay/index.tsx | 8 ++---
.../CardButton/__tests__/CardButton.test.tsx | 7 +++--
app/src/molecules/Command/CommandText.tsx | 8 ++---
.../FileUpload/__tests__/FileUpload.test.tsx | 7 +++--
.../__tests__/GenericWizardTile.test.tsx | 7 +++--
app/src/molecules/GenericWizardTile/index.tsx | 11 +++----
.../InProgressModal/InProgressModal.tsx | 5 ++--
.../__tests__/InProgressModal.test.tsx | 7 +++--
.../__tests__/InfoMessage.test.tsx | 7 +++--
app/src/molecules/InstrumentCard/index.tsx | 5 ++--
.../InterventionContent/index.tsx | 9 +++---
.../molecules/InterventionModal/OneColumn.tsx | 6 ++--
.../OneColumnOrTwoColumn.tsx | 8 ++---
.../molecules/InterventionModal/TwoColumn.tsx | 6 ++--
.../__tests__/InterventionModal.test.tsx | 6 ++--
.../ModalContentOneColSimpleButtons.test.tsx | 5 ++--
app/src/molecules/InterventionModal/index.tsx | 10 +++----
.../InterventionModal/story-utils/StandIn.tsx | 4 +--
.../JogControls/ControlContainer.tsx | 5 ++--
.../molecules/JogControls/StepSizeControl.tsx | 6 ++--
.../MiniCard/__tests__/MiniCard.test.tsx | 7 +++--
app/src/molecules/MiniCard/index.tsx | 4 +--
.../ModuleIcon/__tests__/ModuleIcon.test.tsx | 6 ++--
.../ModuleInfo/__tests__/ModuleInfo.test.tsx | 7 +++--
.../NavTab/__tests__/NavTab.test.tsx | 7 +++--
app/src/molecules/NavTab/index.tsx | 4 ++-
.../__tests__/ODDBackButton.test.tsx | 7 +++--
app/src/molecules/ODDBackButton/index.tsx | 6 ++--
.../NeedHelpLink.tsx | 5 ++--
app/src/molecules/OddModal/OddModal.tsx | 8 ++---
.../OddModal/__tests__/OddModal.test.tsx | 7 +++--
.../__tests__/OddModalHeader.test.tsx | 7 +++--
app/src/molecules/OddModal/types.ts | 3 +-
.../__tests__/OffsetVector.test.tsx | 7 +++--
app/src/molecules/OffsetVector/index.tsx | 4 +--
.../SimpleWizardBodyContent.tsx | 5 ++--
.../SimpleWizardInProgressBody.tsx | 7 +++--
.../__tests__/SimpleWizardBody.test.tsx | 7 +++--
app/src/molecules/SimpleWizardBody/index.tsx | 9 +++---
.../__tests__/useToggleGroup.test.tsx | 8 ++---
app/src/molecules/UnorderedList/index.tsx | 5 ++--
.../__tests__/UpdateBanner.test.tsx | 7 +++--
.../__tests__/WizardHeader.test.tsx | 7 +++--
.../WizardRequiredEquipmentList/index.tsx | 4 +--
app/src/molecules/modals/BottomButtonBar.tsx | 4 +--
.../molecules/modals/ScrollableAlertModal.tsx | 5 ++--
.../AdvancedSettings/OT2AdvancedSettings.tsx | 4 +--
.../AdvancedSettings/OverridePathToPython.tsx | 4 +--
.../AdvancedSettings/UpdatedChannel.tsx | 4 +--
.../__tests__/ConnectRobotSlideout.test.tsx | 9 +++---
.../__tests__/PreviousVersionModal.test.tsx | 7 +++--
.../__tests__/CalibrateDeck.test.tsx | 6 ++--
.../__tests__/CalibratePipetteOffset.test.tsx | 15 +++++-----
.../TipLengthCalibrationInfoBox.tsx | 5 ++--
.../AskForCalibrationBlockModal.test.tsx | 7 +++--
.../__tests__/CalibrateTipLength.test.tsx | 6 ++--
.../__tests__/CalibrationError.test.tsx | 2 +-
.../CompleteConfirmation.tsx | 7 +++--
.../Introduction/__tests__/Body.test.tsx | 5 ++--
.../__tests__/Introduction.test.tsx | 7 ++---
.../__tests__/ChooseTipRack.test.tsx | 6 ++--
.../__tests__/ChosenTipRackRender.test.tsx | 7 +++--
.../__tests__/CompleteConfirmation.test.tsx | 5 ++--
.../__tests__/ConfirmCrashRecovery.test.tsx | 5 ++--
.../__tests__/ConfirmExit.test.tsx | 7 ++---
.../__tests__/DeckSetup.test.tsx | 6 ++--
.../__tests__/MeasureNozzle.test.tsx | 5 ++--
.../__tests__/MeasureTip.test.tsx | 7 ++---
.../__tests__/SaveXYPoint.test.tsx | 7 ++---
.../__tests__/SaveZPoint.test.tsx | 7 ++---
.../__tests__/TipConfirmation.test.tsx | 5 ++--
.../__tests__/TipPickUp.test.tsx | 7 ++---
.../__tests__/CalibrationStatusCard.test.tsx | 7 +++--
.../CalibrationHealthCheckResults.test.tsx | 7 +++--
.../__tests__/CalibrationResult.test.tsx | 7 +++--
.../__tests__/RenderMountInformation.test.tsx | 7 +++--
.../__tests__/RenderResult.test.tsx | 7 +++--
.../__tests__/CheckCalibration.test.tsx | 7 +++--
.../__tests__/ChooseProtocolSlideout.test.tsx | 5 ++--
.../__tests__/ChooseRobotSlideout.test.tsx | 6 ++--
.../__tests__/FileCard.test.tsx | 4 +--
.../ChooseRobotToRunProtocolSlideout.test.tsx | 4 +--
.../Devices/ChangePipette/ConfirmPipette.tsx | 8 ++---
.../Devices/ChangePipette/InstructionStep.tsx | 5 ++--
.../ChangePipette/PipetteSelection.tsx | 5 ++--
.../__tests__/ChangePipette.test.tsx | 6 ++--
.../__tests__/CheckPipettesButton.test.tsx | 7 +++--
.../__tests__/ClearDeckModal.test.tsx | 7 +++--
.../__tests__/ConfirmPipette.test.tsx | 6 ++--
.../__tests__/ExitModal.test.tsx | 7 +++--
.../__tests__/InstructionStep.test.tsx | 7 +++--
.../__tests__/Instructions.test.tsx | 7 +++--
.../__tests__/LevelPipette.test.tsx | 7 +++--
.../__tests__/PipetteSelection.test.tsx | 7 +++--
.../ConfigurePipette/ConfigFormGroup.tsx | 6 ++--
.../__tests__/ConfigFormResetButton.test.tsx | 7 +++--
.../__tests__/ConfigFormSubmitButton.test.tsx | 7 +++--
.../__tests__/ConfigurePipette.test.tsx | 8 ++---
.../__tests__/ErrorRecoveryBanner.test.tsx | 5 ++--
.../__tests__/AboutGripperSlideout.test.tsx | 7 +++--
.../__tests__/GripperCard.test.tsx | 7 +++--
.../HistoricalProtocolRunOverflowMenu.tsx | 12 ++++----
.../__tests__/AboutPipetteSlideout.test.tsx | 7 +++--
.../__tests__/FlexPipetteCard.test.tsx | 8 ++---
.../__tests__/PipetteCard.test.tsx | 6 ++--
.../__tests__/PipetteOverflowMenu.test.tsx | 8 ++---
.../PipetteSettingsSlideout.test.tsx | 8 ++---
.../Devices/ProtocolRun/BackToTopButton.tsx | 5 ++--
.../Devices/ProtocolRun/EmptySetupStep.tsx | 7 +++--
.../ProtocolAnalysisErrorBanner.test.tsx | 9 +++---
.../RunHeaderContent/ActionButton/index.tsx | 5 ++--
.../RunHeaderContent/LabeledValue.tsx | 6 ++--
.../RunHeaderContent/index.tsx | 7 ++---
.../HeaterShakerIsRunningModal.test.tsx | 8 ++---
.../__tests__/HeaterShakerModuleCard.test.tsx | 7 +++--
.../__tests__/hooks.test.tsx | 6 ++--
.../__tests__/ConfirmCancelModal.test.tsx | 6 ++--
.../ProtocolAnalysisErrorModal.test.tsx | 9 +++---
.../__tests__/ProtocolDropTipModal.test.tsx | 6 ++--
.../modals/__tests__/RunFailedModal.test.tsx | 9 +++---
.../__tests__/ProtocolRunHeader.test.tsx | 7 +++--
.../__tests__/LabwareListItem.test.tsx | 5 ++--
.../__tests__/OffDeckLabwareList.test.tsx | 5 ++--
.../__tests__/SecureLabwareModal.test.tsx | 7 +++--
.../__tests__/SetupLabwareList.test.tsx | 5 ++--
.../__tests__/SetupLabwareMap.test.tsx | 4 +--
.../__tests__/CurrentOffsetsTable.test.tsx | 6 ++--
.../__tests__/HowLPCWorksModal.test.tsx | 7 +++--
.../__tests__/SetupLiquids.test.tsx | 7 +++--
.../__tests__/SetupLiquidsList.test.tsx | 6 ++--
.../__tests__/SetupLiquidsMap.test.tsx | 6 ++--
.../__tests__/NotConfiguredModal.test.tsx | 6 ++--
.../__tests__/SetupFixtureList.test.tsx | 6 ++--
.../__tests__/SetupModulesAndDeck.test.tsx | 7 +++--
.../__tests__/SetupModulesList.test.tsx | 6 ++--
.../__tests__/SetupModulesMap.test.tsx | 8 ++---
.../Desktop/Devices/ProtocolRun/SetupStep.tsx | 9 +++---
.../__tests__/EmptySetupStep.test.tsx | 7 +++--
.../__tests__/LabwareInfoOverlay.test.tsx | 7 +++--
.../ProtocolRunModuleControls.test.tsx | 7 ++---
.../ProtocolRunRuntimeParameters.test.tsx | 8 ++---
.../__tests__/SetupCalibrationItem.test.tsx | 5 ++--
.../SetupFlexPipetteCalibrationItem.test.tsx | 7 ++---
.../SetupPipetteCalibrationItem.test.tsx | 9 +++---
.../__tests__/SetupRobotCalibration.test.tsx | 5 ++--
.../ProtocolRun/__tests__/SetupStep.test.tsx | 4 +--
.../SetupTipLengthCalibrationButton.test.tsx | 6 ++--
.../__tests__/DeviceResetModal.test.tsx | 4 +--
.../RobotSettings/AdvancedTab/DeviceReset.tsx | 5 ++--
.../AdvancedTab/DisplayRobotName.tsx | 5 ++--
.../RobotSettings/AdvancedTab/FactoryMode.tsx | 5 ++--
.../AdvancedTab/GantryHoming.tsx | 4 +--
.../AdvancedTab/LegacySettings.tsx | 4 +--
.../AdvancedTab/ShortTrashBin.tsx | 4 +--
.../AdvancedTab/UsageSettings.tsx | 4 +--
.../AdvancedTab/UseOlderAspirateBehavior.tsx | 4 +--
.../EnableErrorRecoveryMode.test.tsx | 8 ++---
.../__tests__/EnableStatusLight.test.tsx | 7 +++--
.../__tests__/OpenJupyterControl.test.tsx | 7 +++--
.../__tests__/Troubleshooting.test.tsx | 6 ++--
.../ConnectNetwork/ConnectModal/FormRow.tsx | 5 ++--
.../ConnectNetwork/SelectSsid/index.tsx | 5 ++--
.../RobotSettings/ConnectNetwork/types.ts | 5 ++--
.../Devices/RobotSettings/SettingToggle.tsx | 5 ++--
.../RobotUpdateProgressModal.test.tsx | 8 ++---
.../__tests__/UpdateRobotModal.test.tsx | 6 ++--
.../__tests__/useRobotUpdateInfo.test.tsx | 4 +--
.../Desktop/Devices/RobotStatusHeader.tsx | 4 +--
.../Desktop/Devices/RunPreview/index.tsx | 6 ++--
.../CalibrationStatusBanner.test.tsx | 9 +++---
.../ConnectionTroubleshootingModal.test.tsx | 7 +++--
.../Devices/__tests__/EstopBanner.test.tsx | 7 +++--
.../__tests__/HistoricalProtocolRun.test.tsx | 6 ++--
...HistoricalProtocolRunOverflowMenu.test.tsx | 6 ++--
.../Devices/__tests__/RobotCard.test.tsx | 6 ++--
.../__tests__/RobotOverflowMenu.test.tsx | 7 +++--
.../Devices/__tests__/RobotOverview.test.tsx | 8 ++---
.../RobotOverviewOverflowMenu.test.tsx | 9 +++---
.../__tests__/RobotStatusHeader.test.tsx | 6 ++--
.../__tests__/useCalibrationTaskList.test.tsx | 4 +--
.../__tests__/useDeckCalibrationData.test.tsx | 9 +++---
.../usePipetteOffsetCalibration.test.tsx | 4 +--
.../usePipetteOffsetCalibrations.test.tsx | 5 ++--
.../__tests__/useSyncRobotClock.test.tsx | 5 ++--
.../useTipLengthCalibrations.test.tsx | 5 ++--
.../useTrackCreateProtocolRunEvent.test.tsx | 4 +--
.../HowCalibrationWorksModal.test.tsx | 9 +++---
.../AddCustomLabwareSlideout.test.tsx | 9 +++---
.../CustomLabwareOverflowMenu.test.tsx | 6 ++--
.../__tests__/LabwareCard.test.tsx | 6 ++--
.../__tests__/ExpandingTitle.test.tsx | 7 +++--
.../__tests__/LabeledValue.test.tsx | 7 +++--
.../__tests__/Dimensions.test.tsx | 7 +++--
.../LabwareDetails/__tests__/Gallery.test.tsx | 7 +++--
.../__tests__/LabwareDetails.test.tsx | 7 +++--
.../__tests__/ManufacturerDetails.test.tsx | 7 +++--
.../__tests__/WellCount.test.tsx | 7 +++--
.../__tests__/WellDimensions.test.tsx | 7 +++--
.../__tests__/WellProperties.test.tsx | 7 +++--
.../__tests__/WellSpacing.test.tsx | 7 +++--
.../ProtocolAnalysisStale.tsx | 7 +++--
.../ProtocolAnalysisFailure.test.tsx | 5 ++--
.../__tests__/ProtocolParameters.test.tsx | 6 ++--
.../__tests__/ProtocolDetails.test.tsx | 4 +--
.../__tests__/ProtocolLabwareDetails.test.tsx | 6 ++--
.../__tests__/ProtocolLiquidsDetails.test.tsx | 6 ++--
.../RobotConfigurationDetails.test.tsx | 9 +++---
.../ConfirmDeleteProtocolModal.tsx | 7 +++--
.../ProtocolsLanding/ProtocolOverflowMenu.tsx | 18 +++++------
.../ConfirmDeleteProtocolModal.test.tsx | 9 +++---
.../__tests__/ProtocolList.test.tsx | 7 +++--
.../ProtocolsLanding/__tests__/hooks.test.tsx | 14 ++++-----
.../CalibrationDataDownload.tsx | 5 ++--
.../__tests__/ModuleCalibrationItems.test.tsx | 8 ++---
.../ModuleCalibrationOverflowMenu.test.tsx | 6 ++--
.../__tests__/OverflowMenu.test.tsx | 6 ++--
.../PipetteOffsetCalibrationItems.test.tsx | 6 ++--
.../TipLengthCalibrationItems.test.tsx | 7 +++--
.../__tests__/CalibrationHealthCheck.test.tsx | 6 ++--
.../RobotSettingsDeckCalibration.test.tsx | 4 +--
.../RobotSettingsGripperCalibration.test.tsx | 6 ++--
.../RobotSettingsModuleCalibration.test.tsx | 7 +++--
...tSettingsPipetteOffsetCalibration.test.tsx | 6 ++--
.../__tests__/InterventionTicks.test.tsx | 7 +++--
.../__tests__/RunProgressMeter.test.tsx | 6 ++--
.../hooks/useRunProgressCopy.tsx | 4 +--
.../Desktop/RunProgressMeter/index.tsx | 5 ++--
.../SendProtocolToFlexSlideout.test.tsx | 6 ++--
.../__tests__/UpdateAppModal.test.tsx | 8 ++---
.../__tests__/UpdateRobotBanner.test.tsx | 7 +++--
.../Desktop/UpdateRobotBanner/index.tsx | 4 +--
.../__tests__/AddFixtureModal.test.tsx | 8 ++---
...kConfigurationDiscardChangesModal.test.tsx | 7 +++--
...DeckFixtureSetupInstructionsModal.test.tsx | 9 +++---
.../DeviceDetailsDeckConfiguration.test.tsx | 6 ++--
.../__tests__/DropTipWizard.test.tsx | 11 +++----
.../__tests__/DropTipWizardHeader.test.tsx | 6 ++--
.../__tests__/EstopMissingModal.test.tsx | 9 +++---
.../__tests__/EstopPressedModal.test.tsx | 9 +++---
.../__tests__/EstopTakeover.test.tsx | 7 +++--
.../RecoveryOptions/IgnoreErrorSkipStep.tsx | 3 +-
.../__tests__/CancelRun.test.tsx | 7 +++--
.../__tests__/FillWellAndSkip.test.tsx | 16 +++++-----
.../__tests__/HomeAndRetry.test.tsx | 7 +++--
.../__tests__/IgnoreErrorSkipStep.test.tsx | 12 ++++----
.../__tests__/ManageTips.test.tsx | 6 ++--
.../__tests__/ManualMoveLwAndSkip.test.tsx | 6 ++--
.../ManualReplaceLwAndRetry.test.tsx | 8 ++---
.../__tests__/RetryNewTips.test.tsx | 10 +++----
.../__tests__/RetrySameTips.test.tsx | 10 +++----
.../__tests__/RetryStep.test.tsx | 7 +++--
.../__tests__/SelectRecoveryOptions.test.tsx | 10 +++----
.../__tests__/SkipStepNewTips.test.tsx | 6 ++--
.../__tests__/SkipStepSameTips.test.tsx | 7 +++--
.../__tests__/ErrorRecoveryFlows.test.tsx | 6 ++--
.../__tests__/ErrorRecoveryWizard.test.tsx | 11 +++----
.../__tests__/RecoveryDoorOpen.test.tsx | 6 ++--
.../__tests__/RecoveryError.test.tsx | 6 ++--
.../__tests__/RecoveryInProgress.test.tsx | 7 +++--
.../__tests__/RecoverySplash.test.tsx | 8 ++---
.../__tests__/RecoveryTakeover.test.tsx | 8 ++---
.../__tests__/useRecoveryOptionCopy.test.tsx | 6 ++--
.../__tests__/useRecoveryToasts.test.tsx | 4 +--
.../shared/LeftColumnLabwareInfo.tsx | 6 ++--
.../shared/RecoveryContentWrapper.tsx | 15 +++++-----
.../shared/RecoveryInterventionModal.tsx | 4 +--
.../ErrorRecoveryFlows/shared/StepInfo.tsx | 7 ++---
.../shared/TwoColLwInfoAndDeck.tsx | 10 +++----
.../__tests__/ErrorDetailsModal.test.tsx | 7 +++--
.../GripperIsHoldingLabware.test.tsx | 9 +++---
.../__tests__/GripperReleaseLabware.test.tsx | 5 ++--
.../__tests__/LeftColumnLabwareInfo.test.tsx | 7 +++--
.../RecoveryDoorOpenSpecial.test.tsx | 8 ++---
.../__tests__/RecoveryFooterButtons.test.tsx | 6 ++--
.../shared/__tests__/RetryStepInfo.test.tsx | 5 ++--
.../shared/__tests__/SelectTips.test.tsx | 6 ++--
.../shared/__tests__/SkipStepInfo.test.tsx | 5 ++--
.../shared/__tests__/StepInfo.test.tsx | 7 +++--
.../shared/__tests__/TipSelection.test.tsx | 7 +++--
.../__tests__/TipSelectionModal.test.tsx | 7 +++--
.../__tests__/TwoColLwInfoAndDeck.test.tsx | 6 ++--
.../__tests__/FirmwareUpdateModal.test.tsx | 7 +++--
.../__tests__/UpdateInProgressModal.test.tsx | 7 +++--
.../__tests__/UpdateNeededModal.test.tsx | 6 ++--
.../__tests__/UpdateResultsModal.test.tsx | 7 +++--
.../organisms/GripperWizardFlows/MovePin.tsx | 8 ++---
.../__tests__/BeforeBeginning.test.tsx | 7 +++--
.../__tests__/ExitConfirmation.test.tsx | 5 ++--
.../__tests__/MountGripper.test.tsx | 7 ++---
.../__tests__/MovePin.test.tsx | 7 ++---
.../__tests__/Success.test.tsx | 7 ++---
.../__tests__/UnmountGripper.test.tsx | 5 ++--
...ncompatibleModuleDesktopModalBody.test.tsx | 9 +++---
.../IncompatibleModuleODDModalBody.test.tsx | 7 +++--
.../IncompatibleModuleTakeover.test.tsx | 7 +++--
.../useIncompatibleModulesAttached.test.tsx | 8 ++---
.../InterventionCommandMesage.test.tsx | 9 +++---
.../InterventionCommandMessage.test.tsx | 9 +++---
.../__tests__/InterventionModal.test.tsx | 6 ++--
.../__tests__/ApplyHistoricOffsets.test.tsx | 8 ++---
.../__tests__/LabwareOffsetTable.test.tsx | 4 ++-
.../__tests__/useHistoricRunDetails.test.tsx | 6 ++--
.../useOffsetCandidatesForAnalysis.test.tsx | 8 ++---
.../PrepareSpace.tsx | 6 ++--
.../TwoUpTileLayout.tsx | 9 +++---
.../__tests__/CheckItem.test.tsx | 6 ++--
.../__tests__/ExitConfirmation.test.tsx | 7 +++--
.../__tests__/PickUpTip.test.tsx | 9 +++---
.../__tests__/ResultsSummary.test.tsx | 7 +++--
.../__tests__/ReturnTip.test.tsx | 7 +++--
.../__tests__/TipConfirmation.test.tsx | 7 +++--
.../__tests__/useLaunchLPC.test.tsx | 4 +--
.../LiquidDetailCard.tsx | 5 ++--
.../__tests__/LiquidDetailCard.test.tsx | 7 +++--
.../LiquidsLabwareDetailsModal.test.tsx | 8 ++---
.../__tests__/LocationConflictModal.test.tsx | 6 ++--
app/src/organisms/ModuleCard/Collapsible.tsx | 7 +++--
.../__tests__/AboutModuleSlideout.test.tsx | 7 +++--
.../ModuleCard/__tests__/Collapsible.test.tsx | 7 +++--
.../__tests__/ConfirmAttachmentModal.test.tsx | 7 +++--
.../ModuleCard/__tests__/ErrorInfo.test.tsx | 7 +++--
.../FirmwareUpdateFailedModal.test.tsx | 9 +++---
.../__tests__/HeaterShakerModuleData.test.tsx | 7 +++--
.../__tests__/HeaterShakerSlideout.test.tsx | 7 +++--
.../__tests__/MagneticModuleData.test.tsx | 7 +++--
.../__tests__/MagneticModuleSlideout.test.tsx | 7 +++--
.../ModuleCard/__tests__/ModuleCard.test.tsx | 8 ++---
.../__tests__/ModuleOverflowMenu.test.tsx | 6 ++--
.../__tests__/ModuleSetupModal.test.tsx | 7 +++--
.../__tests__/TemperatureModuleData.test.tsx | 7 +++--
.../TemperatureModuleSlideout.test.tsx | 9 +++---
.../__tests__/TestShakeSlideout.test.tsx | 7 +++--
.../__tests__/ThermocyclerModuleData.test.tsx | 7 ++---
.../ThermocyclerModuleSlideout.test.tsx | 9 +++---
.../ModuleCard/__tests__/hooks.test.tsx | 30 +++++++++----------
.../__tests__/ChildNavigation.test.tsx | 9 +++---
.../organisms/ODD/ChildNavigation/index.tsx | 10 +++----
.../__tests__/InstrumentInfo.test.tsx | 10 +++----
.../ODD/InstrumentMountItem/LabeledMount.tsx | 5 ++--
.../ProtocolInstrumentMountItem.test.tsx | 9 +++---
.../__tests__/ConfirmRobotName.test.tsx | 6 ++--
.../Navigation/__tests__/Navigation.test.tsx | 7 +++--
.../__tests__/NavigationMenu.test.tsx | 6 ++--
.../RestartRobotConfirmationModal.test.tsx | 7 +++--
.../AlternativeSecurityTypeModal.test.tsx | 8 ++---
.../__tests__/ConnectingNetwork.test.tsx | 7 +++--
.../__tests__/DisplayWifiList.test.tsx | 6 ++--
.../__tests__/FailedToConnect.test.tsx | 6 ++--
.../SelectAuthenticationType.test.tsx | 8 ++---
.../__tests__/SetWifiCred.test.tsx | 7 +++--
.../__tests__/SetWifiSsid.test.tsx | 7 +++--
.../__tests__/WifiConnectionDetails.test.tsx | 6 ++--
.../ProtocolSetupDeckConfiguration.test.tsx | 6 ++--
.../ProtocolSetupInstruments.tsx | 6 ++--
.../__tests__/LabwareMapView.test.tsx | 4 +--
.../__tests__/LiquidDetails.test.tsx | 7 +++--
.../__tests__/ProtocolSetupLiquids.test.tsx | 6 ++--
.../__tests__/FixtureTable.test.tsx | 7 +++--
.../__tests__/ModulesAndDeckMapView.test.tsx | 7 +++--
.../__tests__/SetupInstructionsModal.test.tsx | 7 +++--
.../ProtocolSetupOffsets/index.tsx | 5 ++--
.../ViewOnlyParameters.tsx | 4 +--
.../__tests__/AnalysisFailedModal.test.tsx | 7 +++--
.../__tests__/ChooseCsvFile.test.tsx | 6 ++--
.../__tests__/ChooseEnum.test.tsx | 7 +++--
.../__tests__/ChooseNumber.test.tsx | 6 ++--
.../ProtocolSetupParameters.test.tsx | 8 ++---
.../__tests__/ResetValuesModal.test.tsx | 7 +++--
.../__tests__/ViewOnlyParameters.test.tsx | 7 +++--
.../QuickTransferFlow/CreateNewTransfer.tsx | 5 ++--
.../__tests__/ConfirmExitModal.test.tsx | 7 +++--
.../__tests__/CreateNewTransfer.test.tsx | 6 ++--
.../__tests__/NameQuickTransfer.test.tsx | 7 +++--
.../__tests__/Overview.test.tsx | 7 +++--
.../AirGap.test.tsx | 7 +++--
.../BlowOut.test.tsx | 7 +++--
.../Delay.test.tsx | 7 +++--
.../FlowRate.test.tsx | 7 +++--
.../Mix.test.tsx | 7 +++--
.../PipettePath.test.tsx | 7 +++--
.../QuickTransferAdvancedSettings.test.tsx | 7 +++--
.../TipPosition.test.tsx | 7 +++--
.../TouchTip.test.tsx | 7 +++--
.../__tests__/SelectDestLabware.test.tsx | 7 +++--
.../__tests__/SelectPipette.test.tsx | 7 +++--
.../__tests__/SelectSourceLabware.test.tsx | 7 +++--
.../__tests__/SelectTipRack.test.tsx | 7 +++--
.../__tests__/SummaryAndSettings.test.tsx | 7 +++--
.../TipManagement/ChangeTip.test.tsx | 7 +++--
.../TipManagement/TipDropLocation.test.tsx | 7 +++--
.../TipManagement/TipManagement.test.tsx | 7 +++--
.../__tests__/VolumeEntry.test.tsx | 7 +++--
.../__tests__/RecentRunProtocolCard.test.tsx | 6 ++--
.../RecentRunProtocolCarousel.test.tsx | 8 ++---
.../__tests__/useHardwareStatusText.test.tsx | 5 ++--
.../RobotSettingsSelectAuthenticationType.tsx | 4 +--
.../RobotSettingsSetWifiCred.tsx | 4 +--
.../NetworkSettings/RobotSettingsWifi.tsx | 4 +--
.../EthernetConnectionDetails.test.tsx | 9 +++---
.../__tests__/NetworkDetailsModal.test.tsx | 7 +++--
.../__tests__/NetworkSettings.test.tsx | 6 ++--
.../__tests__/WifiConnectionDetails.test.tsx | 7 +++--
.../NetworkSettings/index.tsx | 4 +--
.../RobotSettingButton.tsx | 6 ++--
.../__tests__/DeviceReset.test.tsx | 6 ++--
.../__tests__/LanguageSetting.test.tsx | 7 +++--
.../__tests__/Privacy.test.tsx | 7 +++--
.../__tests__/RobotSystemVersion.test.tsx | 7 +++--
.../RobotSystemVersionModal.test.tsx | 9 +++---
.../__tests__/TextSize.test.tsx | 7 +++--
.../__tests__/TouchScreenSleep.test.tsx | 7 +++--
.../__tests__/TouchscreenBrightness.test.tsx | 7 +++--
.../__tests__/UpdateChannel.test.tsx | 7 +++--
.../organisms/ODD/RobotSetupHeader/index.tsx | 9 +++---
.../__tests__/ConfirmCancelRunModal.test.tsx | 6 ++--
.../CurrentRunningProtocolCommand.test.tsx | 7 +++--
.../__tests__/RunFailedModal.test.tsx | 7 +++--
.../RunningProtocolCommandList.test.tsx | 9 +++---
.../RunningProtocolSkeleton.test.tsx | 9 +++---
.../PipetteWizardFlows/CheckPipetteButton.tsx | 5 ++--
.../PipetteWizardFlows/MountPipette.tsx | 7 +++--
.../__tests__/AttachProbe.test.tsx | 7 +++--
.../__tests__/BeforeBeginning.test.tsx | 7 +++--
.../__tests__/Carriage.test.tsx | 7 +++--
.../__tests__/CheckPipetteButton.test.tsx | 7 +++--
.../__tests__/ChoosePipette.test.tsx | 7 +++--
.../__tests__/DetachPipette.test.tsx | 7 +++--
.../__tests__/DetachProbe.test.tsx | 7 +++--
.../__tests__/ExitModal.test.tsx | 7 +++--
.../__tests__/MountPipette.test.tsx | 7 +++--
.../__tests__/MountingPlate.test.tsx | 7 +++--
.../__tests__/Results.test.tsx | 6 ++--
.../__tests__/UnskippableModal.test.tsx | 7 +++--
.../__tests__/hooks.test.tsx | 5 ++--
app/src/organisms/PipetteWizardFlows/types.ts | 5 ++--
.../organisms/TakeoverModal/TakeoverModal.tsx | 4 +--
.../__tests__/MaintenanceRunTakeover.test.tsx | 6 ++--
.../__tests__/TakeoverModal.test.tsx | 7 +++--
.../ErrorUpdateSoftware.tsx | 5 ++--
.../__tests__/CompleteUpdateSoftware.test.tsx | 7 +++--
.../__tests__/ErrorUpdateSoftware.test.tsx | 7 +++--
.../__tests__/UpdateSoftware.test.tsx | 7 +++--
.../Desktop/Labware/__tests__/hooks.test.tsx | 22 +++++++-------
.../DisplayConnectionStatus.test.tsx | 8 ++---
.../__tests__/TitleHeader.test.tsx | 6 ++--
.../pages/ODD/ConnectViaWifi/SetWifiCred.tsx | 4 +--
.../pages/ODD/InitialLoadingScreen/index.tsx | 5 ++--
.../pages/ODD/InstrumentsDashboard/index.tsx | 5 ++--
.../PinnedProtocolCarousel.tsx | 6 ++--
.../ODD/ProtocolDashboard/ProtocolCard.tsx | 3 +-
.../DeleteProtocolConfirmationModal.test.tsx | 7 +++--
.../__tests__/PinnedProtocol.test.tsx | 8 ++---
.../__tests__/ProtocolCard.test.tsx | 6 ++--
.../ProtocolDetails/__tests__/Deck.test.tsx | 6 ++--
.../__tests__/EmptySection.test.tsx | 7 +++--
.../__tests__/Hardware.test.tsx | 7 +++--
.../__tests__/Labware.test.tsx | 17 ++++++-----
.../__tests__/Liquids.test.tsx | 6 ++--
.../__tests__/Parameters.test.tsx | 7 +++--
.../__tests__/ConfirmAttachedModal.test.tsx | 7 +++--
.../PinnedTransferCarousel.tsx | 4 +--
.../DeleteTransferConfirmationModal.test.tsx | 6 ++--
.../__tests__/Deck.test.tsx | 6 ++--
.../__tests__/Hardware.test.tsx | 7 +++--
.../__tests__/Labware.test.tsx | 17 +++++------
.../__tests__/WelcomeModal.test.tsx | 6 ++--
.../useProtocolRunAnalyticsData.test.tsx | 5 ++--
.../__tests__/useRobotAnalyticsData.test.tsx | 4 +--
.../useTrackProtocolRunEvent.test.tsx | 4 +--
.../useIsUnboxingFlowOngoing.test.tsx | 4 +--
.../robots/hooks/__tests__/useIsFlex.test.tsx | 5 ++--
.../__tests__/useIsRobotViewable.test.tsx | 5 ++--
.../robots/hooks/__tests__/useRobot.test.tsx | 4 +--
.../robot-update/__tests__/hooks.test.tsx | 4 +--
.../useStoredProtocolAnalysis.test.tsx | 4 +--
.../useDeckCalibrationStatus.test.tsx | 5 ++--
.../hooks/__tests__/useLights.test.tsx | 4 +--
.../useAttachedPipetteCalibrations.test.tsx | 5 ++--
.../__tests__/useAttachedPipettes.test.tsx | 5 ++--
...tachedPipettesFromInstrumentsQuery.test.ts | 4 +--
.../__tests__/useAttachedModules.test.tsx | 3 +-
.../hooks/__tests__/useCanDisconnect.test.tsx | 12 ++++----
.../__tests__/useNetworkConnection.test.tsx | 6 ++--
.../__tests__/useProtocolMetadata.test.tsx | 4 +--
.../runs/__tests__/useCloneRun.test.tsx | 8 ++---
.../__tests__/useLPCDisabledReason.test.tsx | 6 ++--
.../useModuleCalibrationStatus.test.tsx | 9 +++---
.../useRunCalibrationStatus.test.tsx | 10 +++----
app/src/resources/runs/utils.ts | 8 ++---
.../useMissingProtocolHardware.test.tsx | 16 ++++++----
.../__testing-utils__/renderWithProviders.tsx | 13 +++++---
.../src/atoms/ToggleButton/index.tsx | 4 +--
protocol-designer/src/labware-defs/actions.ts | 8 +++--
protocol-designer/src/load-file/actions.ts | 4 ++-
.../CheckboxExpandStepFormField/index.tsx | 4 ++-
.../molecules/CheckboxStepFormField/index.tsx | 7 +++--
.../Alerts/__tests__/FormAlerts.test.tsx | 7 +++--
.../src/organisms/Alerts/types.ts | 4 ++-
.../AssignLiquidsModal/LiquidToolbox.tsx | 6 ++--
.../BlockingHintModal/useBlockingHint.tsx | 4 ++-
.../organisms/ConfirmDeleteModal/index.tsx | 4 +--
.../__tests__/EditNickNameModal.test.tsx | 7 +++--
.../EditProtocolMetadataModal.test.tsx | 9 +++---
.../FileUploadMessagesModal/utils.tsx | 4 +--
.../__tests__/IncompatibleTipsModal.test.tsx | 7 +++--
.../organisms/Labware/SelectableLabware.tsx | 7 ++---
.../src/organisms/Labware/SelectionRect.tsx | 16 +++++-----
.../src/organisms/Labware/SingleLabware.tsx | 4 +--
.../src/organisms/Labware/WellTooltip.tsx | 18 +++++------
.../__tests__/MaterialsListModal.test.tsx | 6 ++--
.../src/organisms/Navigation/index.tsx | 6 ++--
.../__tests__/PipetteInfoItem.test.tsx | 7 +++--
.../__tests__/RenameStepModal.test.tsx | 6 ++--
.../__tests__/SelectWellsModal.test.tsx | 6 ++--
.../__tests__/SlotInformation.test.tsx | 6 ++--
.../TipPositionModal/ZTipPositionModal.tsx | 5 ++--
.../__tests__/TipPositionModal.test.tsx | 6 ++--
.../__tests__/ZTipPositionModal.test.tsx | 7 +++--
.../src/organisms/TipPositionModal/index.tsx | 13 +++-----
.../__tests__/WellOrderModal.test.tsx | 6 ++--
.../CreateNewProtocolWizard/WizardBody.tsx | 4 ++-
.../__tests__/AddMetadata.test.tsx | 6 ++--
.../__tests__/SelectFixtures.test.tsx | 7 +++--
.../__tests__/SelectGripper.test.tsx | 6 ++--
.../__tests__/SelectModules.test.tsx | 7 +++--
.../__tests__/SelectPipettes.test.tsx | 6 ++--
.../__tests__/SelectRobot.test.tsx | 7 +++--
.../__tests__/WizardBody.test.tsx | 7 +++--
.../pages/CreateNewProtocolWizard/index.tsx | 4 ++-
.../Designer/DeckSetup/DeckItemHover.tsx | 6 ++--
.../pages/Designer/DeckSetup/LabwareTools.tsx | 7 +++--
.../__tests__/DeckSetupTools.test.tsx | 6 ++--
.../DeckSetup/__tests__/HoveredItems.test.tsx | 7 +++--
.../DeckSetup/__tests__/LabwareTools.test.tsx | 7 +++--
.../__tests__/SelectedHoveredItems.test.tsx | 7 +++--
.../__tests__/SlotOverflowMenu.test.tsx | 6 ++--
.../src/pages/Designer/DeckSetup/utils.ts | 3 +-
.../pages/Designer/LiquidsOverflowMenu.tsx | 4 +--
.../Offdeck/__tests__/OffDeckDetails.test.tsx | 6 ++--
.../StepForm/PipetteFields/PathField.tsx | 6 ++--
.../StepForm/StepFormToolbox.tsx | 4 ++-
.../ThermocyclerTools/ThermocyclerCycle.tsx | 13 ++++----
.../ThermocyclerProfileModal.tsx | 3 +-
.../ThermocyclerTools/ThermocyclerStep.tsx | 13 ++++----
.../StepTools/__tests__/MagnetTools.test.tsx | 2 +-
.../__tests__/TemperatureTools.test.tsx | 6 ++--
.../Designer/ProtocolSteps/StepForm/types.ts | 5 ++--
.../Timeline/ConnectedStepInfo.tsx | 5 ++--
.../Timeline/StepOverflowMenu.tsx | 8 +++--
.../Timeline/__tests__/StepContainer.test.tsx | 7 +++--
.../__tests__/StepOverflowMenu.test.tsx | 7 +++--
.../ThermocyclerProfileSubsteps.test.tsx | 8 ++---
.../Designer/ProtocolSteps/Timeline/utils.ts | 3 +-
protocol-designer/src/pages/Landing/index.tsx | 7 ++---
.../ProtocolOverview/UnusedModalContent.tsx | 3 +-
.../__tests__/DeckThumbnail.test.tsx | 7 +++--
.../__tests__/OffdeckThumbnail.test.tsx | 6 ++--
.../src/step-forms/selectors/index.ts | 3 +-
.../src/steplist/formLevel/errors.ts | 5 ++--
.../src/steplist/formLevel/profileErrors.ts | 4 ++-
protocol-designer/src/types.ts | 3 +-
604 files changed, 2145 insertions(+), 1883 deletions(-)
diff --git a/app/src/App/Navbar.tsx b/app/src/App/Navbar.tsx
index 1d7711563ae..90e62b608ae 100644
--- a/app/src/App/Navbar.tsx
+++ b/app/src/App/Navbar.tsx
@@ -25,6 +25,7 @@ import logoSvgThree from '/app/assets/images/logo_nav_three.svg'
import { NAV_BAR_WIDTH } from './constants'
+import type { MouseEvent } from 'react'
import type { RouteProps } from './types'
const SALESFORCE_HELP_LINK = 'https://support.opentrons.com/s/'
@@ -161,7 +162,7 @@ export function Navbar({ routes }: { routes: RouteProps[] }): JSX.Element {
) => {
+ onClick={(e: MouseEvent) => {
e.preventDefault()
debouncedNavigate('/app-settings')
}}
diff --git a/app/src/App/__mocks__/portal.tsx b/app/src/App/__mocks__/portal.tsx
index f80b1deb44e..498f3220f65 100644
--- a/app/src/App/__mocks__/portal.tsx
+++ b/app/src/App/__mocks__/portal.tsx
@@ -1,8 +1,8 @@
// mock portal for enzyme tests
-import type * as React from 'react'
+import type { ReactNode } from 'react'
interface Props {
- children: React.ReactNode
+ children: ReactNode
}
// replace Portal with a pass-through React.Fragment
diff --git a/app/src/App/__tests__/hooks.test.tsx b/app/src/App/__tests__/hooks.test.tsx
index 5b3f315049b..2423414c748 100644
--- a/app/src/App/__tests__/hooks.test.tsx
+++ b/app/src/App/__tests__/hooks.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { vi, describe, beforeEach, afterEach, expect, it } from 'vitest'
import { renderHook } from '@testing-library/react'
import { createStore } from 'redux'
@@ -9,11 +8,12 @@ import { i18n } from '/app/i18n'
import { checkShellUpdate } from '/app/redux/shell'
import { useSoftwareUpdatePoll } from '../hooks'
+import type { FunctionComponent, ReactNode } from 'react'
import type { Store } from 'redux'
import type { State } from '/app/redux/types'
describe('useSoftwareUpdatePoll', () => {
- let wrapper: React.FunctionComponent<{ children: React.ReactNode }>
+ let wrapper: FunctionComponent<{ children: ReactNode }>
let store: Store
beforeEach(() => {
vi.useFakeTimers()
diff --git a/app/src/App/index.tsx b/app/src/App/index.tsx
index f0ba1de0304..0ffcf0dd751 100644
--- a/app/src/App/index.tsx
+++ b/app/src/App/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useSelector } from 'react-redux'
import { Flex, POSITION_FIXED, DIRECTION_ROW } from '@opentrons/components'
@@ -9,7 +8,9 @@ import { DesktopApp } from './DesktopApp'
import { OnDeviceDisplayApp } from './OnDeviceDisplayApp'
import { TopPortalRoot } from './portal'
-const stopEvent = (event: React.MouseEvent): void => {
+import type { MouseEvent } from 'react'
+
+const stopEvent = (event: MouseEvent): void => {
event.preventDefault()
}
diff --git a/app/src/App/types.ts b/app/src/App/types.ts
index 87d8f77d4a1..c6a0822260d 100644
--- a/app/src/App/types.ts
+++ b/app/src/App/types.ts
@@ -1,11 +1,11 @@
-import type * as React from 'react'
+import type { FC } from 'react'
export interface RouteProps {
/**
* the component rendered by a route match
* drop developed components into slots held by placeholder div components
*/
- Component: React.FC
+ Component: FC
/**
* a route/page name to render in the nav bar
*/
diff --git a/app/src/LocalizationProvider.tsx b/app/src/LocalizationProvider.tsx
index df2bbc8bc40..3c8fcf9feab 100644
--- a/app/src/LocalizationProvider.tsx
+++ b/app/src/LocalizationProvider.tsx
@@ -7,10 +7,10 @@ import { i18n, i18nCb, i18nConfig } from '/app/i18n'
import { getAppLanguage } from '/app/redux/config'
import { useIsOEMMode } from '/app/resources/robot-settings/hooks'
-import type * as React from 'react'
+import type { ReactNode } from 'react'
export interface LocalizationProviderProps {
- children?: React.ReactNode
+ children?: ReactNode
}
export const BRANDED_RESOURCE = 'branded'
diff --git a/app/src/__testing-utils__/renderWithProviders.tsx b/app/src/__testing-utils__/renderWithProviders.tsx
index 11e3ba16d9b..4c3115281f5 100644
--- a/app/src/__testing-utils__/renderWithProviders.tsx
+++ b/app/src/__testing-utils__/renderWithProviders.tsx
@@ -1,6 +1,5 @@
// render using targetted component using @testing-library/react
// with wrapping providers for i18next and redux
-import type * as React from 'react'
import { QueryClient, QueryClientProvider } from 'react-query'
import { I18nextProvider } from 'react-i18next'
import { Provider } from 'react-redux'
@@ -8,16 +7,22 @@ import { vi } from 'vitest'
import { render } from '@testing-library/react'
import { createStore } from 'redux'
+import type {
+ ComponentProps,
+ ComponentType,
+ PropsWithChildren,
+ ReactElement,
+} from 'react'
import type { PreloadedState, Store } from 'redux'
import type { RenderOptions, RenderResult } from '@testing-library/react'
export interface RenderWithProvidersOptions extends RenderOptions {
initialState?: State
- i18nInstance: React.ComponentProps['i18n']
+ i18nInstance: ComponentProps['i18n']
}
export function renderWithProviders(
- Component: React.ReactElement,
+ Component: ReactElement,
options?: RenderWithProvidersOptions
): [RenderResult, Store] {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -32,7 +37,7 @@ export function renderWithProviders(
const queryClient = new QueryClient()
- const ProviderWrapper: React.ComponentType> = ({
+ const ProviderWrapper: ComponentType> = ({
children,
}) => {
const BaseWrapper = (
diff --git a/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx b/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx
index 0e91433e7b2..bb5ba0669b9 100644
--- a/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx
+++ b/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx
@@ -1,18 +1,19 @@
-import type * as React from 'react'
import { describe, it, vi, beforeEach, expect } from 'vitest'
import { screen, fireEvent } from '@testing-library/react'
import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
import { InlineNotification } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('InlineNotification', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/InlineNotification/index.tsx b/app/src/atoms/InlineNotification/index.tsx
index 5b5bf21aafa..cf413b652cc 100644
--- a/app/src/atoms/InlineNotification/index.tsx
+++ b/app/src/atoms/InlineNotification/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
ALIGN_CENTER,
@@ -19,6 +18,7 @@ import {
Link,
} from '@opentrons/components'
+import type { MouseEventHandler } from 'react'
import type { IconProps, StyleProps } from '@opentrons/components'
type InlineNotificationType = 'alert' | 'error' | 'neutral' | 'success'
@@ -32,9 +32,9 @@ export interface InlineNotificationProps extends StyleProps {
/** Optional dynamic width based on contents */
hug?: boolean
/** optional handler to show close button/clear alert */
- onCloseClick?: (() => void) | React.MouseEventHandler
+ onCloseClick?: (() => void) | MouseEventHandler
linkText?: string
- onLinkClick?: (() => void) | React.MouseEventHandler
+ onLinkClick?: (() => void) | MouseEventHandler
}
const INLINE_NOTIFICATION_PROPS_BY_TYPE: Record<
diff --git a/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx b/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx
index e5fa872ab54..318175558fe 100644
--- a/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx
+++ b/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx
@@ -1,15 +1,16 @@
-import type * as React from 'react'
import { describe, it } from 'vitest'
import { screen } from '@testing-library/react'
import { renderWithProviders } from '/app/__testing-utils__'
import { InstrumentContainer } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('InstrumentContainer', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
it('renders an instrument display name', () => {
props = {
diff --git a/app/src/atoms/Link/__tests__/ExternalLink.test.tsx b/app/src/atoms/Link/__tests__/ExternalLink.test.tsx
index f58ad595169..863a10e886e 100644
--- a/app/src/atoms/Link/__tests__/ExternalLink.test.tsx
+++ b/app/src/atoms/Link/__tests__/ExternalLink.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, beforeEach } from 'vitest'
import { screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
@@ -6,14 +5,16 @@ import { COLORS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { ExternalLink } from '../ExternalLink'
+import type { ComponentProps } from 'react'
+
const TEST_URL = 'https://opentrons.com'
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('ExternalLink', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx b/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx
index 6d5b3d3fa40..557a234e969 100644
--- a/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx
+++ b/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen } from '@testing-library/react'
@@ -7,12 +6,14 @@ import { COLORS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { ProgressBar } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()
}
describe('ProgressBar', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/ProgressBar/index.tsx b/app/src/atoms/ProgressBar/index.tsx
index 5852dadf2b5..bd598c0105b 100644
--- a/app/src/atoms/ProgressBar/index.tsx
+++ b/app/src/atoms/ProgressBar/index.tsx
@@ -1,7 +1,7 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import { COLORS, Box } from '@opentrons/components'
+import type { ReactNode } from 'react'
import type { FlattenSimpleInterpolation } from 'styled-components'
interface ProgressBarProps {
@@ -12,7 +12,7 @@ interface ProgressBarProps {
/** extra styles to be filled progress element */
innerStyles?: FlattenSimpleInterpolation
/** extra elements to be rendered within container */
- children?: React.ReactNode
+ children?: ReactNode
}
export function ProgressBar({
diff --git a/app/src/atoms/SelectField/index.tsx b/app/src/atoms/SelectField/index.tsx
index 50deed28266..a51ed7a9ecd 100644
--- a/app/src/atoms/SelectField/index.tsx
+++ b/app/src/atoms/SelectField/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import find from 'lodash/find'
import { Select } from './Select'
import {
@@ -11,6 +10,7 @@ import {
} from '@opentrons/components'
import { css } from 'styled-components'
+import type { ReactNode } from 'react'
import type { SelectProps, SelectOption } from './Select'
import type { ActionMeta, MultiValue, SingleValue } from 'react-select'
@@ -32,9 +32,9 @@ export interface SelectFieldProps {
/** render function for the option label passed to react-select */
formatOptionLabel?: SelectProps['formatOptionLabel']
/** optional title */
- title?: React.ReactNode
+ title?: ReactNode
/** optional caption. hidden when `error` is given */
- caption?: React.ReactNode
+ caption?: ReactNode
/** if included, use error style and display error instead of caption */
error?: string | null
/** change handler called with (name, value, actionMeta) */
diff --git a/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx b/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx
index b03bd72e98d..471506eb864 100644
--- a/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx
+++ b/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen } from '@testing-library/react'
@@ -6,7 +5,9 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
import { Skeleton } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
diff --git a/app/src/atoms/Slideout/__tests__/Slideout.test.tsx b/app/src/atoms/Slideout/__tests__/Slideout.test.tsx
index 8e3301ad374..cca0cb02929 100644
--- a/app/src/atoms/Slideout/__tests__/Slideout.test.tsx
+++ b/app/src/atoms/Slideout/__tests__/Slideout.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen, fireEvent } from '@testing-library/react'
@@ -6,14 +5,16 @@ import { i18n } from '/app/i18n'
import { renderWithProviders } from '/app/__testing-utils__'
import { Slideout } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('Slideout', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
const mockOnClick = vi.fn()
beforeEach(() => {
props = {
diff --git a/app/src/atoms/Slideout/index.tsx b/app/src/atoms/Slideout/index.tsx
index 53a6888efa0..7260da7c342 100644
--- a/app/src/atoms/Slideout/index.tsx
+++ b/app/src/atoms/Slideout/index.tsx
@@ -22,17 +22,19 @@ import {
import { Divider } from '../structure'
+import type { ReactElement, ReactNode } from 'react'
+
export interface MultiSlideoutSpecs {
currentStep: number
maxSteps: number
}
export interface SlideoutProps {
- title: string | React.ReactElement
- children: React.ReactNode
+ title: string | ReactElement
+ children: ReactNode
onCloseClick: () => void
// isExpanded is for collapse and expand animation
isExpanded?: boolean
- footer?: React.ReactNode
+ footer?: ReactNode
multiSlideoutSpecs?: MultiSlideoutSpecs
}
diff --git a/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx b/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx
index 3b3bb3ab48c..d536b18b30c 100644
--- a/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx
+++ b/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx
@@ -7,6 +7,8 @@ import {
layoutCandidates,
customDisplay,
} from '../constants'
+
+import type { MutableRefObject } from 'react'
import type { KeyboardReactInterface } from 'react-simple-keyboard'
import '../index.css'
@@ -15,7 +17,7 @@ import './index.css'
// TODO (kk:04/05/2024) add debug to make debugging easy
interface AlphanumericKeyboardProps {
onChange: (input: string) => void
- keyboardRef: React.MutableRefObject
+ keyboardRef: MutableRefObject
debug?: boolean
}
diff --git a/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx b/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx
index 1cc2668d788..23ed71f9851 100644
--- a/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx
+++ b/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx
@@ -1,6 +1,8 @@
-import type * as React from 'react'
import { KeyboardReact as Keyboard } from 'react-simple-keyboard'
+
+import type { MutableRefObject } from 'react'
import type { KeyboardReactInterface } from 'react-simple-keyboard'
+
import '../index.css'
import './index.css'
@@ -11,7 +13,7 @@ const customDisplay = {
// TODO (kk:04/05/2024) add debug to make debugging easy
interface IndividualKeyProps {
onChange: (input: string) => void
- keyboardRef: React.MutableRefObject
+ keyboardRef: MutableRefObject
keyText: string
debug?: boolean
}
diff --git a/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx b/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx
index 4d3959eb9c3..16180ecbbdb 100644
--- a/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx
+++ b/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx
@@ -1,15 +1,16 @@
-import type * as React from 'react'
import { KeyboardReact as Keyboard } from 'react-simple-keyboard'
import { numericalKeyboardLayout, numericalCustom } from '../constants'
+import type { MutableRefObject } from 'react'
import type { KeyboardReactInterface } from 'react-simple-keyboard'
+
import '../index.css'
import './index.css'
// Note (kk:04/05/2024) add debug to make debugging easy
interface NumericalKeyboardProps {
onChange: (input: string) => void
- keyboardRef: React.MutableRefObject
+ keyboardRef: MutableRefObject
isDecimal?: boolean
hasHyphen?: boolean
debug?: boolean
diff --git a/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx b/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx
index 568326f0065..48a7753cab5 100644
--- a/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx
+++ b/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen } from '@testing-library/react'
@@ -6,12 +5,14 @@ import { C_SKY_BLUE, COLORS } from '@opentrons/components'
import { StatusLabel } from '..'
import { renderWithProviders } from '/app/__testing-utils__'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('StatusLabel', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
it('renders an engaged status label with a blue background and text', () => {
props = {
diff --git a/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx b/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx
index 90f027b0f7a..dcd98b06159 100644
--- a/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx
+++ b/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen } from '@testing-library/react'
@@ -6,14 +5,16 @@ import { i18n } from '/app/i18n'
import { renderWithProviders } from '/app/__testing-utils__'
import { StepMeter } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('StepMeter', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/buttons/BackButton.tsx b/app/src/atoms/buttons/BackButton.tsx
index 29657e1f1b2..2819c0eb48e 100644
--- a/app/src/atoms/buttons/BackButton.tsx
+++ b/app/src/atoms/buttons/BackButton.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
@@ -11,11 +10,13 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
+import type { HTMLProps } from 'react'
+
// TODO(bh, 2022-12-7): finish styling when designs finalized
export function BackButton({
onClick,
children,
-}: React.HTMLProps): JSX.Element {
+}: HTMLProps): JSX.Element {
const navigate = useNavigate()
const { t } = useTranslation('shared')
diff --git a/app/src/atoms/buttons/FloatingActionButton.tsx b/app/src/atoms/buttons/FloatingActionButton.tsx
index b7e870eab16..6088a5675b4 100644
--- a/app/src/atoms/buttons/FloatingActionButton.tsx
+++ b/app/src/atoms/buttons/FloatingActionButton.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
@@ -15,9 +14,10 @@ import {
StyledText,
} from '@opentrons/components'
+import type { ComponentProps } from 'react'
import type { IconName } from '@opentrons/components'
-interface FloatingActionButtonProps extends React.ComponentProps {
+interface FloatingActionButtonProps extends ComponentProps {
buttonText: string
disabled?: boolean
iconName?: IconName
diff --git a/app/src/atoms/buttons/IconButton.tsx b/app/src/atoms/buttons/IconButton.tsx
index ee754472ff1..43c935d462f 100644
--- a/app/src/atoms/buttons/IconButton.tsx
+++ b/app/src/atoms/buttons/IconButton.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
BORDERS,
@@ -10,8 +9,10 @@ import {
} from '@opentrons/components'
import { ODD_FOCUS_VISIBLE } from './constants'
-interface IconButtonProps extends React.ComponentProps {
- iconName: React.ComponentProps['name']
+import type { ComponentProps } from 'react'
+
+interface IconButtonProps extends ComponentProps {
+ iconName: ComponentProps['name']
hasBackground?: boolean
}
diff --git a/app/src/atoms/buttons/MediumButton.tsx b/app/src/atoms/buttons/MediumButton.tsx
index d784029696a..7439cb39a46 100644
--- a/app/src/atoms/buttons/MediumButton.tsx
+++ b/app/src/atoms/buttons/MediumButton.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
ALIGN_CENTER,
@@ -16,6 +15,7 @@ import {
} from '@opentrons/components'
import { ODD_FOCUS_VISIBLE } from './constants'
+import type { MouseEventHandler, ReactNode } from 'react'
import type { IconName, StyleProps } from '@opentrons/components'
import type { ButtonCategory } from './SmallButton'
@@ -28,12 +28,12 @@ type MediumButtonTypes =
| 'tertiaryLowLight'
interface MediumButtonProps extends StyleProps {
- buttonText: React.ReactNode
+ buttonText: ReactNode
buttonType?: MediumButtonTypes
disabled?: boolean
iconName?: IconName
buttonCategory?: ButtonCategory
- onClick: React.MouseEventHandler
+ onClick: MouseEventHandler
}
export function MediumButton(props: MediumButtonProps): JSX.Element {
diff --git a/app/src/atoms/buttons/SmallButton.tsx b/app/src/atoms/buttons/SmallButton.tsx
index e659d52fd58..7ad8c3d1a99 100644
--- a/app/src/atoms/buttons/SmallButton.tsx
+++ b/app/src/atoms/buttons/SmallButton.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
ALIGN_CENTER,
@@ -15,6 +14,8 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
import { ODD_FOCUS_VISIBLE } from './constants'
+
+import type { MouseEventHandler, ReactNode } from 'react'
import type { IconName, StyleProps } from '@opentrons/components'
export type SmallButtonTypes =
@@ -28,9 +29,9 @@ export type ButtonCategory = 'default' | 'rounded'
export type IconPlacement = 'startIcon' | 'endIcon'
interface SmallButtonProps extends StyleProps {
- onClick: React.MouseEventHandler
+ onClick: MouseEventHandler
buttonType?: SmallButtonTypes
- buttonText: React.ReactNode
+ buttonText: ReactNode
iconPlacement?: IconPlacement | null
iconName?: IconName | null
buttonCategory?: ButtonCategory // if not specified, it will be 'default'
diff --git a/app/src/atoms/buttons/SubmitPrimaryButton.tsx b/app/src/atoms/buttons/SubmitPrimaryButton.tsx
index cdbf3442a65..cc53717bab0 100644
--- a/app/src/atoms/buttons/SubmitPrimaryButton.tsx
+++ b/app/src/atoms/buttons/SubmitPrimaryButton.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
SPACING,
@@ -8,10 +7,12 @@ import {
styleProps,
} from '@opentrons/components'
+import type { MouseEvent } from 'react'
+
interface SubmitPrimaryButtonProps {
form: string
value: string
- onClick?: (event: React.MouseEvent) => unknown
+ onClick?: (event: MouseEvent) => unknown
disabled?: boolean
}
export const SubmitPrimaryButton = (
diff --git a/app/src/atoms/buttons/TextOnlyButton.tsx b/app/src/atoms/buttons/TextOnlyButton.tsx
index de3bbc969ab..0acdaf058ed 100644
--- a/app/src/atoms/buttons/TextOnlyButton.tsx
+++ b/app/src/atoms/buttons/TextOnlyButton.tsx
@@ -1,7 +1,8 @@
-import type * as React from 'react'
+import { css } from 'styled-components'
import { Btn, StyledText, COLORS, RESPONSIVENESS } from '@opentrons/components'
+
+import type { ReactNode } from 'react'
import type { StyleProps } from '@opentrons/components'
-import { css } from 'styled-components'
const GO_BACK_BUTTON_STYLE = css`
color: ${COLORS.grey50};
@@ -26,7 +27,7 @@ const GO_BACK_BUTTON_DISABLED_STYLE = css`
interface TextOnlyButtonProps extends StyleProps {
onClick: () => void
- buttonText: React.ReactNode
+ buttonText: ReactNode
disabled?: boolean
}
diff --git a/app/src/atoms/buttons/ToggleButton.tsx b/app/src/atoms/buttons/ToggleButton.tsx
index b814f45da1d..42efbde32fb 100644
--- a/app/src/atoms/buttons/ToggleButton.tsx
+++ b/app/src/atoms/buttons/ToggleButton.tsx
@@ -1,8 +1,8 @@
-import type * as React from 'react'
import { css } from 'styled-components'
-import { Btn, Icon, COLORS, SIZE_1, SIZE_2 } from '@opentrons/components'
+import { Btn, COLORS, Icon } from '@opentrons/components'
+import type { MouseEvent } from 'react'
import type { StyleProps } from '@opentrons/components'
const TOGGLE_DISABLED_STYLES = css`
@@ -42,7 +42,7 @@ interface ToggleButtonProps extends StyleProps {
toggledOn: boolean
disabled?: boolean | null
id?: string
- onClick?: (e: React.MouseEvent) => unknown
+ onClick?: (e: MouseEvent) => unknown
}
export const ToggleButton = (props: ToggleButtonProps): JSX.Element => {
@@ -55,12 +55,12 @@ export const ToggleButton = (props: ToggleButtonProps): JSX.Element => {
role="switch"
aria-label={label}
aria-checked={toggledOn}
- size={size ?? SIZE_2}
+ size={size ?? '2rem'}
css={props.toggledOn ? TOGGLE_ENABLED_STYLES : TOGGLE_DISABLED_STYLES}
{...buttonProps}
>
{/* TODO(bh, 2022-10-05): implement small and large sizes from design system */}
-
+
)
}
diff --git a/app/src/atoms/buttons/__tests__/BackButton.test.tsx b/app/src/atoms/buttons/__tests__/BackButton.test.tsx
index 510abd0ee7d..67a65d107da 100644
--- a/app/src/atoms/buttons/__tests__/BackButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/BackButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import '@testing-library/jest-dom/vitest'
@@ -9,7 +8,9 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
import { BackButton } from '..'
-const render = (props?: React.HTMLProps) => {
+import type { HTMLProps } from 'react'
+
+const render = (props?: HTMLProps) => {
return renderWithProviders(
) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('FloatingActionButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/buttons/__tests__/MediumButton.test.tsx b/app/src/atoms/buttons/__tests__/MediumButton.test.tsx
index 988248413d9..1ce59db9217 100644
--- a/app/src/atoms/buttons/__tests__/MediumButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/MediumButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -7,12 +6,14 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { MediumButton } from '../MediumButton'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('MediumButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
onClick: vi.fn(),
diff --git a/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx b/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx
index 4e10831c73c..7fd27eb874c 100644
--- a/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { screen } from '@testing-library/react'
@@ -7,6 +6,8 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { QuaternaryButton } from '..'
+import type { ComponentProps } from 'react'
+
vi.mock('styled-components', async () => {
const actual = await vi.importActual(
'styled-components/dist/styled-components.browser.esm.js'
@@ -14,12 +15,12 @@ vi.mock('styled-components', async () => {
return actual
})
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('QuaternaryButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/buttons/__tests__/SmallButton.test.tsx b/app/src/atoms/buttons/__tests__/SmallButton.test.tsx
index 283f21daf4e..84341d3b39b 100644
--- a/app/src/atoms/buttons/__tests__/SmallButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/SmallButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -7,12 +6,14 @@ import { COLORS, BORDERS } from '@opentrons/components'
import { SmallButton } from '../SmallButton'
import { renderWithProviders } from '/app/__testing-utils__'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('SmallButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx b/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx
index a62ba9c4a95..6af258d9d3f 100644
--- a/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -6,15 +5,16 @@ import { COLORS, SPACING, TYPOGRAPHY, BORDERS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { SubmitPrimaryButton } from '..'
+import type { ComponentProps } from 'react'
const mockOnClick = vi.fn()
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('SubmitPrimaryButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx b/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx
index 4a3f95369c8..30b15c5bea2 100644
--- a/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, beforeEach } from 'vitest'
import { screen } from '@testing-library/react'
import { renderWithProviders } from '/app/__testing-utils__'
@@ -7,12 +6,14 @@ import { COLORS, SPACING, TYPOGRAPHY, BORDERS } from '@opentrons/components'
import { TertiaryButton } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('TertiaryButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx b/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx
index 3f61d43c5d0..29991d4ae0f 100644
--- a/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx
+++ b/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { screen, fireEvent } from '@testing-library/react'
import { COLORS, SIZE_2 } from '@opentrons/components'
@@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { ToggleButton } from '..'
+import type { ComponentProps } from 'react'
+
const mockOnClick = vi.fn()
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('ToggleButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/structure/Divider.tsx b/app/src/atoms/structure/Divider.tsx
index db55ad84f44..a253f27dce4 100644
--- a/app/src/atoms/structure/Divider.tsx
+++ b/app/src/atoms/structure/Divider.tsx
@@ -1,7 +1,7 @@
-import type * as React from 'react'
import { Box, COLORS, SPACING } from '@opentrons/components'
+import type { ComponentProps } from 'react'
-type Props = React.ComponentProps
+type Props = ComponentProps
export function Divider(props: Props): JSX.Element {
const { marginY } = props
diff --git a/app/src/atoms/structure/Line.tsx b/app/src/atoms/structure/Line.tsx
index ecbbecc24cd..8eb456233f6 100644
--- a/app/src/atoms/structure/Line.tsx
+++ b/app/src/atoms/structure/Line.tsx
@@ -1,7 +1,7 @@
-import type * as React from 'react'
import { Box, BORDERS } from '@opentrons/components'
+import type { ComponentProps } from 'react'
-type Props = React.ComponentProps
+type Props = ComponentProps
export function Line(props: Props): JSX.Element {
return
diff --git a/app/src/atoms/structure/__tests__/Divider.test.tsx b/app/src/atoms/structure/__tests__/Divider.test.tsx
index 27460be938d..0aa40edb9d4 100644
--- a/app/src/atoms/structure/__tests__/Divider.test.tsx
+++ b/app/src/atoms/structure/__tests__/Divider.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen } from '@testing-library/react'
@@ -6,12 +5,14 @@ import { SPACING, COLORS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { Divider } from '../index'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('Divider', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/atoms/structure/__tests__/Line.test.tsx b/app/src/atoms/structure/__tests__/Line.test.tsx
index d9a9caefba2..c4e3e267565 100644
--- a/app/src/atoms/structure/__tests__/Line.test.tsx
+++ b/app/src/atoms/structure/__tests__/Line.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { screen } from '@testing-library/react'
@@ -6,12 +5,14 @@ import { SPACING, COLORS } from '@opentrons/components'
import { Line } from '../index'
import { renderWithProviders } from '/app/__testing-utils__'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('Line', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx b/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx
index e09b3c11765..882c4a644b4 100644
--- a/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx
+++ b/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx
@@ -1,15 +1,16 @@
-import type * as React from 'react'
import { describe, it, expect, vi } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { renderWithProviders } from '/app/__testing-utils__'
import { BackgroundOverlay } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('BackgroundOverlay', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
it('renders background overlay', () => {
props = { onClick: vi.fn() }
render(props)
diff --git a/app/src/molecules/BackgroundOverlay/index.tsx b/app/src/molecules/BackgroundOverlay/index.tsx
index 3d6c6d976c6..c0a711ffa91 100644
--- a/app/src/molecules/BackgroundOverlay/index.tsx
+++ b/app/src/molecules/BackgroundOverlay/index.tsx
@@ -1,8 +1,9 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import { COLORS, Flex, POSITION_FIXED } from '@opentrons/components'
+import type { ComponentProps, MouseEventHandler } from 'react'
+
const BACKGROUND_OVERLAY_STYLE = css`
position: ${POSITION_FIXED};
inset: 0;
@@ -10,10 +11,9 @@ const BACKGROUND_OVERLAY_STYLE = css`
background-color: ${COLORS.black90}${COLORS.opacity60HexCode};
`
-export interface BackgroundOverlayProps
- extends React.ComponentProps {
+export interface BackgroundOverlayProps extends ComponentProps {
// onClick handler so when you click anywhere in the overlay, the modal/menu closes
- onClick: React.MouseEventHandler
+ onClick: MouseEventHandler
}
export function BackgroundOverlay(props: BackgroundOverlayProps): JSX.Element {
diff --git a/app/src/molecules/CardButton/__tests__/CardButton.test.tsx b/app/src/molecules/CardButton/__tests__/CardButton.test.tsx
index 820dfbdc4e9..177d61dfd2d 100644
--- a/app/src/molecules/CardButton/__tests__/CardButton.test.tsx
+++ b/app/src/molecules/CardButton/__tests__/CardButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
@@ -7,6 +6,8 @@ import { COLORS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
import { CardButton } from '..'
+
+import type { ComponentProps } from 'react'
import type { NavigateFunction } from 'react-router-dom'
const mockNavigate = vi.fn()
@@ -19,7 +20,7 @@ vi.mock('react-router-dom', async importOriginal => {
}
})
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(
@@ -31,7 +32,7 @@ const render = (props: React.ComponentProps) => {
}
describe('CardButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/Command/CommandText.tsx b/app/src/molecules/Command/CommandText.tsx
index 9c43afb4b02..a2d872af5a8 100644
--- a/app/src/molecules/Command/CommandText.tsx
+++ b/app/src/molecules/Command/CommandText.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { pick } from 'lodash'
import { css } from 'styled-components'
@@ -14,6 +13,7 @@ import {
import { useCommandTextString } from '/app/local-resources/commands'
+import type { ComponentProps } from 'react'
import type {
LabwareDefinition2,
RobotType,
@@ -27,13 +27,13 @@ import type {
} from '/app/local-resources/commands'
interface LegacySTProps {
- as?: React.ComponentProps['as']
+ as?: ComponentProps['as']
modernStyledTextDefaults?: false
}
interface ModernSTProps {
- desktopStyle?: React.ComponentProps['desktopStyle']
- oddStyle?: React.ComponentProps['oddStyle']
+ desktopStyle?: ComponentProps['desktopStyle']
+ oddStyle?: ComponentProps['oddStyle']
modernStyledTextDefaults: true
}
diff --git a/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx b/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx
index cd5b5adfd82..9f3c22db269 100644
--- a/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx
+++ b/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { screen } from '@testing-library/react'
@@ -7,7 +6,9 @@ import { i18n } from '/app/i18n'
import { FileUpload } from '..'
import testFile from './test-file.png'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
@@ -16,7 +17,7 @@ const render = (props: React.ComponentProps) => {
const handleClick = vi.fn()
describe('FileUpload', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
const file = new File([testFile], 'a-file-to-test.png')
diff --git a/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx b/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx
index 1f53800ff6d..ee1d680d4d2 100644
--- a/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx
+++ b/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -7,16 +6,18 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { getIsOnDevice } from '/app/redux/config'
import { GenericWizardTile } from '..'
+import type { ComponentProps } from 'react'
+
vi.mock('/app/redux/config')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('GenericWizardTile', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/GenericWizardTile/index.tsx b/app/src/molecules/GenericWizardTile/index.tsx
index 24883a6ffea..3948daa292a 100644
--- a/app/src/molecules/GenericWizardTile/index.tsx
+++ b/app/src/molecules/GenericWizardTile/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useSelector } from 'react-redux'
import styled, { css } from 'styled-components'
import { useTranslation } from 'react-i18next'
@@ -26,6 +25,8 @@ import { getIsOnDevice } from '/app/redux/config'
import { NeedHelpLink } from '/app/molecules/OT2CalibrationNeedHelpLink'
import { SmallButton, TextOnlyButton } from '/app/atoms/buttons'
+import type { ReactNode } from 'react'
+
const ALIGN_BUTTONS = css`
align-items: ${ALIGN_FLEX_END};
@@ -59,13 +60,13 @@ const TILE_CONTAINER_STYLE = css`
}
`
export interface GenericWizardTileProps {
- rightHandBody: React.ReactNode
- bodyText: React.ReactNode
- header: string | React.ReactNode
+ rightHandBody: ReactNode
+ bodyText: ReactNode
+ header: string | ReactNode
getHelp?: string
back?: () => void
proceed?: () => void
- proceedButtonText?: React.ReactNode
+ proceedButtonText?: ReactNode
proceedIsDisabled?: boolean
proceedButton?: JSX.Element
backIsDisabled?: boolean
diff --git a/app/src/molecules/InProgressModal/InProgressModal.tsx b/app/src/molecules/InProgressModal/InProgressModal.tsx
index c6fefe761a2..82693b34429 100644
--- a/app/src/molecules/InProgressModal/InProgressModal.tsx
+++ b/app/src/molecules/InProgressModal/InProgressModal.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
ALIGN_CENTER,
@@ -13,9 +12,11 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
+import type { ReactNode } from 'react'
+
interface Props {
// optional override of the spinner
- alternativeSpinner?: React.ReactNode
+ alternativeSpinner?: ReactNode
description?: string
body?: string
children?: JSX.Element
diff --git a/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx b/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx
index f670fa221c3..db7ac2294cf 100644
--- a/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx
+++ b/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { screen } from '@testing-library/react'
import { describe, it, beforeEach, vi } from 'vitest'
import { i18n } from '/app/i18n'
@@ -6,15 +5,17 @@ import { getIsOnDevice } from '/app/redux/config'
import { renderWithProviders } from '/app/__testing-utils__'
import { InProgressModal } from '../InProgressModal'
+import type { ComponentProps } from 'react'
+
vi.mock('/app/redux/config')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('InProgressModal', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
vi.mocked(getIsOnDevice).mockReturnValue(false)
})
diff --git a/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx b/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx
index 5ff6948976f..378e1eff503 100644
--- a/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx
+++ b/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx
@@ -1,18 +1,19 @@
-import type * as React from 'react'
import { describe, it, beforeEach } from 'vitest'
import { renderWithProviders } from '/app/__testing-utils__'
import { screen } from '@testing-library/react'
import { i18n } from '/app/i18n'
import { InfoMessage } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('InfoMessage', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/InstrumentCard/index.tsx b/app/src/molecules/InstrumentCard/index.tsx
index edcee546c32..2cd838a08bd 100644
--- a/app/src/molecules/InstrumentCard/index.tsx
+++ b/app/src/molecules/InstrumentCard/index.tsx
@@ -1,5 +1,3 @@
-import type * as React from 'react'
-
import {
ALIGN_CENTER,
ALIGN_FLEX_START,
@@ -22,6 +20,7 @@ import flexGripper from '/app/assets/images/flex_gripper.png'
import { MenuOverlay } from './MenuOverlay'
+import type { ReactNode } from 'react'
import type { InstrumentDiagramProps, StyleProps } from '@opentrons/components'
import type { MenuOverlayItemProps } from './MenuOverlay'
@@ -33,7 +32,7 @@ interface InstrumentCardProps extends StyleProps {
instrumentDiagramProps?: InstrumentDiagramProps
// special casing the gripper at least for now
isGripperAttached?: boolean
- banner?: React.ReactNode
+ banner?: ReactNode
isEstopNotDisengaged: boolean
}
diff --git a/app/src/molecules/InterventionModal/InterventionContent/index.tsx b/app/src/molecules/InterventionModal/InterventionContent/index.tsx
index 8d2bbba9c8c..e9b156cf0e6 100644
--- a/app/src/molecules/InterventionModal/InterventionContent/index.tsx
+++ b/app/src/molecules/InterventionModal/InterventionContent/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import {
Flex,
StyledText,
@@ -7,15 +6,17 @@ import {
RESPONSIVENESS,
} from '@opentrons/components'
import { InlineNotification } from '/app/atoms/InlineNotification'
-
import { InterventionInfo } from './InterventionInfo'
+
+import type { ComponentProps } from 'react'
+
export type { InterventionInfoProps } from './InterventionInfo'
export { InterventionInfo }
export interface InterventionContentProps {
headline: string
- infoProps: React.ComponentProps
- notificationProps?: React.ComponentProps
+ infoProps: ComponentProps
+ notificationProps?: ComponentProps
}
export function InterventionContent({
diff --git a/app/src/molecules/InterventionModal/OneColumn.tsx b/app/src/molecules/InterventionModal/OneColumn.tsx
index 35a37dd10f9..8fca9f26ff3 100644
--- a/app/src/molecules/InterventionModal/OneColumn.tsx
+++ b/app/src/molecules/InterventionModal/OneColumn.tsx
@@ -1,14 +1,14 @@
-import type * as React from 'react'
-
import {
Flex,
DIRECTION_COLUMN,
JUSTIFY_SPACE_BETWEEN,
} from '@opentrons/components'
+
+import type { ReactNode } from 'react'
import type { StyleProps } from '@opentrons/components'
export interface OneColumnProps extends StyleProps {
- children: React.ReactNode
+ children: ReactNode
}
export function OneColumn({
diff --git a/app/src/molecules/InterventionModal/OneColumnOrTwoColumn.tsx b/app/src/molecules/InterventionModal/OneColumnOrTwoColumn.tsx
index db25d343be5..0a02f3397ac 100644
--- a/app/src/molecules/InterventionModal/OneColumnOrTwoColumn.tsx
+++ b/app/src/molecules/InterventionModal/OneColumnOrTwoColumn.tsx
@@ -1,5 +1,3 @@
-import type * as React from 'react'
-
import { css } from 'styled-components'
import {
Flex,
@@ -9,11 +7,13 @@ import {
WRAP,
RESPONSIVENESS,
} from '@opentrons/components'
-import type { StyleProps } from '@opentrons/components'
import { TWO_COLUMN_ELEMENT_MIN_WIDTH } from './constants'
+import type { ReactNode } from 'react'
+import type { StyleProps } from '@opentrons/components'
+
export interface OneColumnOrTwoColumnProps extends StyleProps {
- children: [React.ReactNode, React.ReactNode]
+ children: [ReactNode, ReactNode]
}
export function OneColumnOrTwoColumn({
diff --git a/app/src/molecules/InterventionModal/TwoColumn.tsx b/app/src/molecules/InterventionModal/TwoColumn.tsx
index fc9072232be..600386ec60f 100644
--- a/app/src/molecules/InterventionModal/TwoColumn.tsx
+++ b/app/src/molecules/InterventionModal/TwoColumn.tsx
@@ -1,11 +1,11 @@
-import type * as React from 'react'
-
import { Flex, Box, DIRECTION_ROW, SPACING, WRAP } from '@opentrons/components'
import type { StyleProps } from '@opentrons/components'
import { TWO_COLUMN_ELEMENT_MIN_WIDTH } from './constants'
+import type { ReactNode } from 'react'
+
export interface TwoColumnProps extends StyleProps {
- children: [React.ReactNode, React.ReactNode]
+ children: [ReactNode, ReactNode]
}
export function TwoColumn({
diff --git a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx
index a063bee13bc..bdbe4c95e70 100644
--- a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx
+++ b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { vi, describe, it, expect, beforeEach } from 'vitest'
import { when } from 'vitest-when'
import '@testing-library/jest-dom/vitest'
@@ -12,6 +11,7 @@ import { getIsOnDevice } from '/app/redux/config'
import { InterventionModal } from '../'
+import type { ComponentProps } from 'react'
import type { ModalType } from '../'
import type { State } from '/app/redux/types'
@@ -23,7 +23,7 @@ const MOCK_STATE: State = {
},
} as any
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
initialState: MOCK_STATE,
@@ -31,7 +31,7 @@ const render = (props: React.ComponentProps) => {
}
describe('InterventionModal', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx b/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx
index b68e8a379a0..bb25d211619 100644
--- a/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx
+++ b/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx
@@ -1,9 +1,10 @@
-import type * as React from 'react'
import { vi, describe, it, expect } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { ModalContentOneColSimpleButtons } from '../ModalContentOneColSimpleButtons'
+import type { ChangeEventHandler } from 'react'
+
/* eslint-disable testing-library/no-node-access */
const inputElForButtonFromButtonText = (text: string): HTMLInputElement =>
((screen.getByText(text)?.parentElement?.parentElement
@@ -88,7 +89,7 @@ describe('InterventionModal', () => {
firstButton={{
label: 'first button',
value: 'first',
- onChange: onChange as React.ChangeEventHandler,
+ onChange: onChange as ChangeEventHandler,
}}
secondButton={{ label: 'second button', value: 'second' }}
furtherButtons={[{ label: 'third button', value: 'third' }]}
diff --git a/app/src/molecules/InterventionModal/index.tsx b/app/src/molecules/InterventionModal/index.tsx
index 679d3b3d1f8..3e4801d4922 100644
--- a/app/src/molecules/InterventionModal/index.tsx
+++ b/app/src/molecules/InterventionModal/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useSelector } from 'react-redux'
import { css } from 'styled-components'
@@ -32,6 +31,7 @@ import { DescriptionContent } from './DescriptionContent'
import { DeckMapContent } from './DeckMapContent'
import { CategorizedStepContent } from './CategorizedStepContent'
+import type { MouseEvent, ReactNode } from 'react'
import type { FlattenSimpleInterpolation } from 'styled-components'
import type { IconName } from '@opentrons/components'
@@ -116,9 +116,9 @@ const ERROR_COLOR = COLORS.red50
export interface InterventionModalProps {
/** Optional modal title heading. Aligned to the left. */
- titleHeading?: React.ReactNode
+ titleHeading?: ReactNode
/** Optional modal heading right of the icon. Aligned right if titleHeading is supplied, otherwise aligned left. **/
- iconHeading?: React.ReactNode
+ iconHeading?: ReactNode
/** Optional onClick for the icon heading and icon. */
iconHeadingOnClick?: () => void
/** overall style hint */
@@ -128,7 +128,7 @@ export interface InterventionModalProps {
/* Optional icon size override. */
iconSize?: string
/** modal contents */
- children: React.ReactNode
+ children: ReactNode
}
export function InterventionModal({
@@ -160,7 +160,7 @@ export function InterventionModal({
{...modalStyle}
flexDirection={DIRECTION_COLUMN}
border={border}
- onClick={(e: React.MouseEvent) => {
+ onClick={(e: MouseEvent) => {
e.stopPropagation()
}}
>
diff --git a/app/src/molecules/InterventionModal/story-utils/StandIn.tsx b/app/src/molecules/InterventionModal/story-utils/StandIn.tsx
index 04aa5690e7a..56153551df3 100644
--- a/app/src/molecules/InterventionModal/story-utils/StandIn.tsx
+++ b/app/src/molecules/InterventionModal/story-utils/StandIn.tsx
@@ -1,10 +1,10 @@
-import type * as React from 'react'
import { Box, BORDERS } from '@opentrons/components'
+import type { ReactNode } from 'react'
export function StandInContent({
children,
}: {
- children?: React.ReactNode
+ children?: ReactNode
}): JSX.Element {
return (
0) setCurrentStepSize(stepSizes[i - 1])
}
- const handleStepSelect = (
- event: React.MouseEvent
- ): void => {
+ const handleStepSelect = (event: MouseEvent): void => {
setCurrentStepSize(Number(event.currentTarget.value) as StepSize)
event.currentTarget.blur()
}
diff --git a/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx b/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx
index fcc76f38505..0e26b805f16 100644
--- a/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx
+++ b/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -6,12 +5,14 @@ import { COLORS, SPACING, BORDERS, CURSOR_POINTER } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { MiniCard } from '../'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('MiniCard', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/MiniCard/index.tsx b/app/src/molecules/MiniCard/index.tsx
index b65ccbbb59d..d95c42e149f 100644
--- a/app/src/molecules/MiniCard/index.tsx
+++ b/app/src/molecules/MiniCard/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import {
BORDERS,
@@ -8,12 +7,13 @@ import {
SPACING,
} from '@opentrons/components'
+import type { ReactNode } from 'react'
import type { StyleProps } from '@opentrons/components'
interface MiniCardProps extends StyleProps {
onClick: () => void
isSelected: boolean
- children: React.ReactNode
+ children: ReactNode
isError?: boolean
isWarning?: boolean
}
diff --git a/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx b/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx
index 73c44639e51..6e20f8997fa 100644
--- a/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx
+++ b/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { COLORS, SPACING } from '@opentrons/components'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { screen } from '@testing-library/react'
@@ -6,6 +5,7 @@ import '@testing-library/jest-dom/vitest'
import { renderWithProviders } from '/app/__testing-utils__'
import { ModuleIcon } from '../'
+import type { ComponentProps } from 'react'
import type { AttachedModule } from '/app/redux/modules/types'
import type * as OpentronsComponents from '@opentrons/components'
@@ -17,7 +17,7 @@ vi.mock('@opentrons/components', async importOriginal => {
}
})
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
@@ -46,7 +46,7 @@ const mockHeaterShakerModule = {
} as AttachedModule
describe('ModuleIcon', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx b/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx
index 1d732faeb18..de5eef75577 100644
--- a/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx
+++ b/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { screen } from '@testing-library/react'
import { describe, it, vi, beforeEach, expect } from 'vitest'
import '@testing-library/jest-dom/vitest'
@@ -7,11 +6,13 @@ import { when } from 'vitest-when'
import { i18n } from '/app/i18n'
import { ModuleInfo } from '../ModuleInfo'
import { useRunHasStarted } from '/app/resources/runs'
+
+import type { ComponentProps } from 'react'
import type { ModuleModel, ModuleType } from '@opentrons/shared-data'
vi.mock('/app/resources/runs')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
@@ -26,7 +27,7 @@ const mockTCModule = {
const MOCK_RUN_ID = '1'
describe('ModuleInfo', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
moduleModel: mockTCModule.model,
diff --git a/app/src/molecules/NavTab/__tests__/NavTab.test.tsx b/app/src/molecules/NavTab/__tests__/NavTab.test.tsx
index 176c76b60cc..e6a2c9a504f 100644
--- a/app/src/molecules/NavTab/__tests__/NavTab.test.tsx
+++ b/app/src/molecules/NavTab/__tests__/NavTab.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, beforeEach } from 'vitest'
@@ -7,7 +6,9 @@ import { SPACING, COLORS, TYPOGRAPHY, BORDERS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { NavTab } from '..'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(
@@ -16,7 +17,7 @@ const render = (props: React.ComponentProps) => {
}
describe('NavTab', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/NavTab/index.tsx b/app/src/molecules/NavTab/index.tsx
index e170da56e43..6cdf6cb0e6b 100644
--- a/app/src/molecules/NavTab/index.tsx
+++ b/app/src/molecules/NavTab/index.tsx
@@ -3,6 +3,8 @@ import { NavLink } from 'react-router-dom'
import { BORDERS, COLORS, SPACING, TYPOGRAPHY } from '@opentrons/components'
+import type { ComponentProps } from 'react'
+
export const TAB_BORDER_STYLE = css`
border-bottom-style: ${BORDERS.styleSolid};
border-bottom-width: 2px;
@@ -15,7 +17,7 @@ interface NavTabProps {
disabled?: boolean
}
-const StyledNavLink = styled(NavLink)>`
+const StyledNavLink = styled(NavLink)>`
padding: 0 ${SPACING.spacing4} ${SPACING.spacing8};
${TYPOGRAPHY.labelSemiBold}
color: ${COLORS.grey50};
diff --git a/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx b/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx
index 2b520279cf8..5b487b2e7dc 100644
--- a/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx
+++ b/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -6,12 +5,14 @@ import { COLORS } from '@opentrons/components'
import { ODDBackButton } from '..'
import { renderWithProviders } from '/app/__testing-utils__'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('ODDBackButton', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/ODDBackButton/index.tsx b/app/src/molecules/ODDBackButton/index.tsx
index 396feaa5e19..0390dc21284 100644
--- a/app/src/molecules/ODDBackButton/index.tsx
+++ b/app/src/molecules/ODDBackButton/index.tsx
@@ -1,5 +1,3 @@
-import type * as React from 'react'
-
import {
ALIGN_CENTER,
Btn,
@@ -11,8 +9,10 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
+import type { HTMLProps } from 'react'
+
export function ODDBackButton(
- props: React.HTMLProps
+ props: HTMLProps
): JSX.Element {
const { onClick, label } = props
diff --git a/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx b/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx
index a87a7808827..6dd9a5f8d86 100644
--- a/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx
+++ b/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useTranslation } from 'react-i18next'
import {
Flex,
@@ -11,9 +10,11 @@ import {
SPACING,
} from '@opentrons/components'
+import type { ComponentProps } from 'react'
+
const SUPPORT_PAGE_URL = 'https://support.opentrons.com/s/ot2-calibration'
-interface NeedHelpLinkProps extends React.ComponentProps {
+interface NeedHelpLinkProps extends ComponentProps {
href?: string
}
diff --git a/app/src/molecules/OddModal/OddModal.tsx b/app/src/molecules/OddModal/OddModal.tsx
index 9b32974000c..e95aaa1d9a4 100644
--- a/app/src/molecules/OddModal/OddModal.tsx
+++ b/app/src/molecules/OddModal/OddModal.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import {
ALIGN_CENTER,
BORDERS,
@@ -11,14 +10,15 @@ import {
import { BackgroundOverlay } from '../BackgroundOverlay'
import { OddModalHeader } from './OddModalHeader'
+import type { MouseEvent, MouseEventHandler, ReactNode } from 'react'
import type { StyleProps } from '@opentrons/components'
import type { OddModalHeaderBaseProps, ModalSize } from './types'
interface OddModalProps extends StyleProps {
/** clicking anywhere outside of the modal closes it */
- onOutsideClick?: React.MouseEventHandler
+ onOutsideClick?: MouseEventHandler
/** modal content */
- children: React.ReactNode
+ children: ReactNode
/** for small, medium, or large modal sizes, medium by default */
modalSize?: ModalSize
/** see OddModalHeader component for more details */
@@ -66,7 +66,7 @@ export function OddModal(props: OddModalProps): JSX.Element {
margin={SPACING.spacing32}
flexDirection={DIRECTION_COLUMN}
aria-label={`modal_${modalSize}`}
- onClick={(e: React.MouseEvent) => {
+ onClick={(e: MouseEvent) => {
e.stopPropagation()
}}
>
diff --git a/app/src/molecules/OddModal/__tests__/OddModal.test.tsx b/app/src/molecules/OddModal/__tests__/OddModal.test.tsx
index d8c129a8b01..9d9097b4729 100644
--- a/app/src/molecules/OddModal/__tests__/OddModal.test.tsx
+++ b/app/src/molecules/OddModal/__tests__/OddModal.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
@@ -7,14 +6,16 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { OddModalHeader } from '../OddModalHeader'
import { OddModal } from '../OddModal'
+import type { ComponentProps } from 'react'
+
vi.mock('../OddModalHeader')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('OddModal', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
onOutsideClick: vi.fn(),
diff --git a/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx b/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx
index e824e49648c..94499d35abf 100644
--- a/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx
+++ b/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -6,12 +5,14 @@ import { COLORS } from '@opentrons/components'
import { renderWithProviders } from '/app/__testing-utils__'
import { OddModalHeader } from '../OddModalHeader'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('OddModalHeader', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
title: 'title',
diff --git a/app/src/molecules/OddModal/types.ts b/app/src/molecules/OddModal/types.ts
index b0fa6d103ae..e4f07cc52ad 100644
--- a/app/src/molecules/OddModal/types.ts
+++ b/app/src/molecules/OddModal/types.ts
@@ -1,10 +1,11 @@
+import type { MouseEventHandler } from 'react'
import type { IconName, StyleProps } from '@opentrons/components'
export type ModalSize = 'small' | 'medium' | 'large'
export interface OddModalHeaderBaseProps extends StyleProps {
title: string | JSX.Element
- onClick?: React.MouseEventHandler
+ onClick?: MouseEventHandler
hasExitIcon?: boolean
iconName?: IconName
iconColor?: string
diff --git a/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx b/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx
index bb247c0175a..e3b4c87da6c 100644
--- a/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx
+++ b/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, beforeEach } from 'vitest'
@@ -7,12 +6,14 @@ import { renderWithProviders } from '/app/__testing-utils__'
import { OffsetVector } from '../'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('OffsetVector', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/OffsetVector/index.tsx b/app/src/molecules/OffsetVector/index.tsx
index 155019e8074..af4d8c5dc68 100644
--- a/app/src/molecules/OffsetVector/index.tsx
+++ b/app/src/molecules/OffsetVector/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import {
Flex,
SPACING,
@@ -6,13 +5,14 @@ import {
LegacyStyledText,
} from '@opentrons/components'
+import type { ComponentProps } from 'react'
import type { StyleProps } from '@opentrons/components'
interface OffsetVectorProps extends StyleProps {
x: number
y: number
z: number
- as?: React.ComponentProps['as']
+ as?: ComponentProps['as']
}
export function OffsetVector(props: OffsetVectorProps): JSX.Element {
diff --git a/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx b/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx
index 16113e28c9a..1a179f1b987 100644
--- a/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx
+++ b/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useSelector } from 'react-redux'
import { css } from 'styled-components'
import {
@@ -21,13 +20,15 @@ import SuccessIcon from '/app/assets/images/icon_success.png'
import { getIsOnDevice } from '/app/redux/config'
import { Skeleton } from '/app/atoms/Skeleton'
+
+import type { ReactNode } from 'react'
import type { RobotType } from '@opentrons/shared-data'
interface Props {
iconColor: string
header: string
isSuccess: boolean
- children?: React.ReactNode
+ children?: ReactNode
subHeader?: string | JSX.Element
isPending?: boolean
robotType?: RobotType
diff --git a/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx b/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx
index 10882025dfc..92f65aa2cf2 100644
--- a/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx
+++ b/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx
@@ -1,9 +1,10 @@
-import type * as React from 'react'
-import type { StyleProps } from '@opentrons/components'
import { InProgressModal } from '../InProgressModal/InProgressModal'
import { SimpleWizardBodyContainer } from './SimpleWizardBodyContainer'
-export type SimpleWizardInProgressBodyProps = React.ComponentProps<
+import type { ComponentProps } from 'react'
+import type { StyleProps } from '@opentrons/components'
+
+export type SimpleWizardInProgressBodyProps = ComponentProps<
typeof InProgressModal
> &
StyleProps
diff --git a/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx b/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx
index 9849e8fa03c..f21e14f0580 100644
--- a/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx
+++ b/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { screen } from '@testing-library/react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { COLORS } from '@opentrons/components'
@@ -7,14 +6,16 @@ import { Skeleton } from '/app/atoms/Skeleton'
import { getIsOnDevice } from '/app/redux/config'
import { SimpleWizardBody } from '..'
+import type { ComponentProps } from 'react'
+
vi.mock('/app/atoms/Skeleton')
vi.mock('/app/redux/config')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders()[0]
}
describe('SimpleWizardBody', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
iconColor: COLORS.red60,
diff --git a/app/src/molecules/SimpleWizardBody/index.tsx b/app/src/molecules/SimpleWizardBody/index.tsx
index b554b71b90e..c49c49be2a8 100644
--- a/app/src/molecules/SimpleWizardBody/index.tsx
+++ b/app/src/molecules/SimpleWizardBody/index.tsx
@@ -1,8 +1,9 @@
-import type * as React from 'react'
-
import { SimpleWizardBodyContainer } from './SimpleWizardBodyContainer'
import { SimpleWizardBodyContent } from './SimpleWizardBodyContent'
import { SimpleWizardInProgressBody } from './SimpleWizardInProgressBody'
+
+import type { ComponentProps } from 'react'
+
export {
SimpleWizardBodyContainer,
SimpleWizardBodyContent,
@@ -10,8 +11,8 @@ export {
}
export function SimpleWizardBody(
- props: React.ComponentProps &
- React.ComponentProps
+ props: ComponentProps &
+ ComponentProps
): JSX.Element {
const { children, ...rest } = props
return (
diff --git a/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx b/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx
index c4829b79615..b90da347489 100644
--- a/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx
+++ b/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import '@testing-library/jest-dom/vitest'
@@ -7,6 +6,7 @@ import { renderHook, render, fireEvent, screen } from '@testing-library/react'
import { useTrackEvent } from '/app/redux/analytics'
import { useToggleGroup } from '../useToggleGroup'
+import type { FunctionComponent, ReactNode } from 'react'
import type { Store } from 'redux'
import type { State } from '/app/redux/types'
@@ -23,7 +23,7 @@ describe('useToggleGroup', () => {
})
it('should return default selectedValue and toggle buttons', () => {
- const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({
+ const wrapper: FunctionComponent<{ children: ReactNode }> = ({
children,
}) => {children}
@@ -35,7 +35,7 @@ describe('useToggleGroup', () => {
expect(result.current[0]).toBe('List View')
})
it('should record an analytics event for list view', async () => {
- const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({
+ const wrapper: FunctionComponent<{ children: ReactNode }> = ({
children,
}) => {children}
@@ -53,7 +53,7 @@ describe('useToggleGroup', () => {
})
})
it('should record an analytics event for map view', () => {
- const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({
+ const wrapper: FunctionComponent<{ children: ReactNode }> = ({
children,
}) => {children}
diff --git a/app/src/molecules/UnorderedList/index.tsx b/app/src/molecules/UnorderedList/index.tsx
index cf9937266a8..f48c3e685ba 100644
--- a/app/src/molecules/UnorderedList/index.tsx
+++ b/app/src/molecules/UnorderedList/index.tsx
@@ -1,9 +1,10 @@
-import type * as React from 'react'
import { css } from 'styled-components'
import { SPACING, LegacyStyledText } from '@opentrons/components'
+import type { ReactNode } from 'react'
+
interface UnorderedListProps {
- items: React.ReactNode[]
+ items: ReactNode[]
}
export function UnorderedList(props: UnorderedListProps): JSX.Element {
const { items } = props
diff --git a/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx b/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx
index 52b074b37d8..1f8470efcd0 100644
--- a/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx
+++ b/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { when } from 'vitest-when'
@@ -9,10 +8,12 @@ import { useIsEstopNotDisengaged } from '/app/resources/devices/hooks/useIsEstop
import { UpdateBanner } from '..'
import { renderWithProviders } from '/app/__testing-utils__'
+import type { ComponentProps } from 'react'
+
vi.mock('/app/redux-resources/robots')
vi.mock('/app/resources/devices/hooks/useIsEstopNotDisengaged')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
initialState: { robotsByName: 'test' },
@@ -20,7 +21,7 @@ const render = (props: React.ComponentProps) => {
}
describe('Module Update Banner', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx b/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx
index 0e11447cac2..436e594dd59 100644
--- a/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx
+++ b/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import '@testing-library/jest-dom/vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -8,17 +7,19 @@ import { StepMeter } from '/app/atoms/StepMeter'
import { WizardHeader } from '..'
import { renderWithProviders } from '/app/__testing-utils__'
+import type { ComponentProps } from 'react'
+
vi.mock('/app/atoms/StepMeter')
vi.mock('/app/redux/config')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('WizardHeader', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/molecules/WizardRequiredEquipmentList/index.tsx b/app/src/molecules/WizardRequiredEquipmentList/index.tsx
index f6f7457a71a..3a39d904639 100644
--- a/app/src/molecules/WizardRequiredEquipmentList/index.tsx
+++ b/app/src/molecules/WizardRequiredEquipmentList/index.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useSelector } from 'react-redux'
import { useTranslation } from 'react-i18next'
import { css } from 'styled-components'
@@ -23,9 +22,10 @@ import { Divider } from '/app/atoms/structure'
import { labwareImages } from '/app/local-resources/labware'
import { equipmentImages } from './equipmentImages'
+import type { ComponentProps } from 'react'
import type { StyleProps } from '@opentrons/components'
interface WizardRequiredEquipmentListProps extends StyleProps {
- equipmentList: Array>
+ equipmentList: Array>
footer?: string
}
export function WizardRequiredEquipmentList(
diff --git a/app/src/molecules/modals/BottomButtonBar.tsx b/app/src/molecules/modals/BottomButtonBar.tsx
index d5c202e943c..f7f78f067e4 100644
--- a/app/src/molecules/modals/BottomButtonBar.tsx
+++ b/app/src/molecules/modals/BottomButtonBar.tsx
@@ -1,18 +1,18 @@
// bottom button bar for modals
// TODO(mc, 2018-08-18): maybe make this the default AlertModal behavior
-import type * as React from 'react'
import cx from 'classnames'
import { OutlineButton } from '@opentrons/components'
import styles from './styles.module.css'
+import type { ReactNode } from 'react'
import type { ButtonProps } from '@opentrons/components'
type MaybeButtonProps = ButtonProps | null | undefined
interface Props {
buttons: MaybeButtonProps[]
className?: string | null
- description?: React.ReactNode | null
+ description?: ReactNode | null
}
export function BottomButtonBar(props: Props): JSX.Element {
diff --git a/app/src/molecules/modals/ScrollableAlertModal.tsx b/app/src/molecules/modals/ScrollableAlertModal.tsx
index c98846899b8..d8ebdc18543 100644
--- a/app/src/molecules/modals/ScrollableAlertModal.tsx
+++ b/app/src/molecules/modals/ScrollableAlertModal.tsx
@@ -1,12 +1,13 @@
// AlertModal with vertical scrolling
-import type * as React from 'react'
import omit from 'lodash/omit'
import { AlertModal } from '@opentrons/components'
import { BottomButtonBar } from './BottomButtonBar'
import styles from './styles.module.css'
-type Props = React.ComponentProps
+import type { ComponentProps } from 'react'
+
+type Props = ComponentProps
export function ScrollableAlertModal(props: Props): JSX.Element {
return (
diff --git a/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx b/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx
index 7ab3ebb0bd0..3ff3313a840 100644
--- a/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx
+++ b/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useTranslation } from 'react-i18next'
import { css } from 'styled-components'
@@ -17,6 +16,7 @@ import {
} from '/app/redux/calibration'
import { getUseTrashSurfaceForTipCal } from '/app/redux/config'
+import type { ChangeEvent } from 'react'
import type { Dispatch, State } from '/app/redux/types'
const ALWAYS_BLOCK: 'always-block' = 'always-block'
@@ -74,7 +74,7 @@ export function OT2AdvancedSettings(): JSX.Element {
? ALWAYS_BLOCK
: ALWAYS_PROMPT
}
- onChange={(event: React.ChangeEvent) => {
+ onChange={(event: ChangeEvent) => {
// you know this is a limited-selection field whose values are only
// the elements of BlockSelection; i know this is a limited-selection
// field whose values are only the elements of BlockSelection; but sadly,
diff --git a/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx b/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx
index d0e5b7d6d93..a87d739172f 100644
--- a/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx
+++ b/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector, useDispatch } from 'react-redux'
@@ -27,6 +26,7 @@ import {
ANALYTICS_CHANGE_PATH_TO_PYTHON_DIRECTORY,
} from '/app/redux/analytics'
+import type { MouseEventHandler } from 'react'
import type { Dispatch } from '/app/redux/types'
export function OverridePathToPython(): JSX.Element {
@@ -35,7 +35,7 @@ export function OverridePathToPython(): JSX.Element {
const dispatch = useDispatch()
const trackEvent = useTrackEvent()
- const handleClickPythonDirectoryChange: React.MouseEventHandler = _event => {
+ const handleClickPythonDirectoryChange: MouseEventHandler = _event => {
dispatch(changePythonPathOverrideConfig())
trackEvent({
name: ANALYTICS_CHANGE_PATH_TO_PYTHON_DIRECTORY,
diff --git a/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx b/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx
index aca0348cb7b..ec261c75083 100644
--- a/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx
+++ b/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
@@ -19,6 +18,7 @@ import {
updateConfigValue,
} from '/app/redux/config'
+import type { ComponentProps } from 'react'
import type { SelectOption } from '/app/atoms/SelectField/Select'
import type { Dispatch } from '/app/redux/types'
@@ -31,7 +31,7 @@ export function UpdatedChannel(): JSX.Element {
dispatch(updateConfigValue('update.channel', value))
}
- const formatOptionLabel: React.ComponentProps<
+ const formatOptionLabel: ComponentProps<
typeof SelectField
>['formatOptionLabel'] = (option, index): JSX.Element => {
const { label, value } = option
diff --git a/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx b/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx
index c92e1f495a0..4cddd3e1763 100644
--- a/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx
+++ b/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
@@ -8,17 +7,19 @@ import { getConfig } from '/app/redux/config'
import { renderWithProviders } from '/app/__testing-utils__'
import { ConnectRobotSlideout } from '../ConnectRobotSlideout'
+import type { ComponentProps } from 'react'
+
vi.mock('/app/redux/discovery')
vi.mock('/app/redux/config')
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('ConnectRobotSlideout', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
vi.mocked(getScanning).mockReturnValue(true)
@@ -54,7 +55,7 @@ describe('ConnectRobotSlideout', () => {
checkIpAndHostname: vi.fn(),
isExpanded: true,
onCloseClick: vi.fn(),
- } as React.ComponentProps
+ } as ComponentProps
})
it('renders correct title, body, and footer for ConnectRobotSlideout', () => {
diff --git a/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx b/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx
index b4449623c05..59eff47aa2c 100644
--- a/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx
+++ b/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import '@testing-library/jest-dom/vitest'
@@ -10,12 +9,14 @@ import {
PREVIOUS_RELEASES_URL,
} from '../PreviousVersionModal'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})
}
-const props: React.ComponentProps = {
+const props: ComponentProps = {
closeModal: vi.fn(),
}
diff --git a/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx b/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx
index 3f90064c03c..13415c8e47c 100644
--- a/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx
+++ b/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { vi, describe, beforeEach, expect, it } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -14,6 +13,7 @@ import {
CalibrationError,
} from '/app/organisms/Desktop/CalibrationError'
+import type { ComponentProps, ComponentType } from 'react'
import type { DeckCalibrationStep } from '/app/redux/sessions/types'
import type { DispatchRequestsType } from '/app/redux/robot-api'
@@ -41,14 +41,14 @@ describe('CalibrateDeck', () => {
...mockDeckCalibrationSessionAttributes,
}
const render = (
- props: Partial> = {}
+ props: Partial> = {}
) => {
const {
showSpinner = false,
isJogging = false,
session = mockDeckCalSession,
} = props
- return renderWithProviders>(
+ return renderWithProviders>(
{
const actual = await importOriginal()
@@ -35,16 +36,14 @@ interface CalibratePipetteOffsetSpec {
describe('CalibratePipetteOffset', () => {
let dispatchRequests: DispatchRequestsType
const render = (
- props: Partial> = {}
+ props: Partial> = {}
) => {
const {
showSpinner = false,
isJogging = false,
session = mockPipOffsetCalSession,
} = props
- return renderWithProviders<
- React.ComponentType
- >(
+ return renderWithProviders>(
{
const onResponse = vi.fn()
const render = () => {
return renderWithProviders<
- React.ComponentProps
+ ComponentProps
>(
{
@@ -41,14 +41,14 @@ describe('CalibrateTipLength', () => {
...mockTipLengthCalibrationSessionAttributes,
}
const render = (
- props: Partial> = {}
+ props: Partial> = {}
) => {
const {
showSpinner = false,
isJogging = false,
session = mockTipLengthSession,
} = props
- return renderWithProviders>(
+ return renderWithProviders>(
{
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
diff --git a/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx b/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx
index cbd0e214cd1..3f6de1fed33 100644
--- a/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx
+++ b/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { useTranslation } from 'react-i18next'
import {
ALIGN_CENTER,
@@ -16,11 +15,13 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
+import type { MouseEventHandler, ReactNode } from 'react'
+
interface CompleteConfirmationProps {
- proceed: React.MouseEventHandler
+ proceed: MouseEventHandler
flowName?: string
body?: string
- visualAid?: React.ReactNode
+ visualAid?: ReactNode
}
export function CompleteConfirmation(
diff --git a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx
index c1300fb3218..26682dff0cc 100644
--- a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx
+++ b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { it, describe } from 'vitest'
import { screen } from '@testing-library/react'
@@ -8,7 +7,9 @@ import * as Sessions from '/app/redux/sessions'
import { i18n } from '/app/i18n'
import { Body } from '../Body'
-const render = (props: React.ComponentProps) => {
+import type { ComponentProps } from 'react'
+
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
diff --git a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx
index a34460571ed..4005f6c996c 100644
--- a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx
+++ b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
@@ -9,6 +8,8 @@ import { i18n } from '/app/i18n'
import { Introduction } from '../'
import { ChooseTipRack } from '../../ChooseTipRack'
+import type { ComponentProps } from 'react'
+
vi.mock('../../ChooseTipRack')
const mockCalInvalidationHandler = vi.fn()
@@ -17,9 +18,7 @@ describe('Introduction', () => {
const mockSendCommands = vi.fn()
const mockCleanUpAndExit = vi.fn()
- const render = (
- props: Partial> = {}
- ) => {
+ const render = (props: Partial> = {}) => {
return renderWithProviders(
) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
}
describe('ChooseTipRack', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
vi.mocked(Select).mockReturnValue(mock select
)
diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx
index 5eb7fa1db8b..27bc9a7d233 100644
--- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx
+++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx
@@ -1,13 +1,14 @@
-import type * as React from 'react'
import { it, describe, beforeEach } from 'vitest'
import { screen } from '@testing-library/react'
import { i18n } from '/app/i18n'
import { renderWithProviders } from '/app/__testing-utils__'
import { ChosenTipRackRender } from '../ChosenTipRackRender'
+
+import type { ComponentProps } from 'react'
import type { SelectOption } from '/app/atoms/SelectField/Select'
-const render = (props: React.ComponentProps) => {
+const render = (props: ComponentProps) => {
return renderWithProviders(, {
i18nInstance: i18n,
})[0]
@@ -19,7 +20,7 @@ const mockSelectValue = {
} as SelectOption
describe('ChosenTipRackRender', () => {
- let props: React.ComponentProps
+ let props: ComponentProps
beforeEach(() => {
props = {
selectedValue: mockSelectValue,
diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx
index 99dc73310d1..8dd9575a2c3 100644
--- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx
+++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx
@@ -1,4 +1,3 @@
-import type * as React from 'react'
import { fireEvent, screen } from '@testing-library/react'
import { vi, it, describe, expect } from 'vitest'
@@ -7,10 +6,12 @@ import { i18n } from '/app/i18n'
import { CompleteConfirmation } from '../CompleteConfirmation'
+import type { ComponentProps } from 'react'
+
describe('CompleteConfirmation', () => {
const mockCleanUpAndExit = vi.fn()
const render = (
- props: Partial