From 748983d90868df2e08d359f0427e1d22e52837e4 Mon Sep 17 00:00:00 2001 From: Shlok Amin Date: Tue, 6 Jul 2021 17:49:56 -0400 Subject: [PATCH] refactor(protocol-designer): migrate from flow to TS (#7883) * Convert flow to TS Co-authored-by: Curt Elsasser Co-authored-by: Sarah Breen --- .madgerc | 8 +- Makefile | 2 +- .../src/system-info/network-interfaces.ts | 2 +- .../Robots/InstrumentSettings/PipetteInfo.tsx | 4 +- .../pages/Robots/InstrumentSettings/index.tsx | 2 + .../RobotSettings/AdvancedSettingsCard.tsx | 1 - .../__tests__/CalibrationCard.test.tsx | 1 + .../redux/protocol/__tests__/actions.test.ts | 1 + .../redux/robot-api/__tests__/http.test.ts | 2 + components/src/forms/SelectField.tsx | 2 + components/src/lists/ListItem.tsx | 2 +- components/src/lists/TitledList.tsx | 2 +- components/src/modals/Overlay.tsx | 2 +- components/src/structure/TitleBar.tsx | 1 + .../src/mdns-browser/interfaces.ts | 1 + .../labware-creator/components/RadioField.tsx | 2 +- package.json | 2 +- protocol-designer/.gitignore | 2 + .../benchmarks/timelineGeneration.js | 1 - .../docs/STEP_FORMS_AND_PROTOCOL_TIMELINE.md | 2 +- protocol-designer/package.json | 5 +- .../src/__tests__/persist.test.ts | 6 +- .../validateProtocolFixtures.test.ts | 9 +- .../__tests__/flattenNestedProperties.test.ts | 1 - .../reduxActionToAnalyticsEvent.test.ts | 31 +- protocol-designer/src/analytics/actions.ts | 31 +- protocol-designer/src/analytics/fullstory.ts | 11 +- protocol-designer/src/analytics/index.ts | 5 +- protocol-designer/src/analytics/middleware.ts | 26 +- protocol-designer/src/analytics/mixpanel.ts | 23 +- protocol-designer/src/analytics/reducers.ts | 25 +- protocol-designer/src/analytics/selectors.ts | 4 +- .../utils/flattenNestedProperties.ts | 10 +- protocol-designer/src/collision-types.ts | 40 +- protocol-designer/src/components/App.tsx | 3 +- .../components/BatchEditForm/BatchEditMix.tsx | 23 +- .../BatchEditForm/BatchEditMoveLiquid.tsx | 35 +- .../components/BatchEditForm/FormColumn.tsx | 11 +- .../NoBatchEditSharedSettings.tsx | 3 +- .../__tests__/BatchEditMoveLiquid.test.tsx | 15 +- .../__tests__/makeBatchEditFieldProps.test.ts | 1 - .../src/components/BatchEditForm/index.tsx | 15 +- .../BatchEditForm/makeBatchEditFieldProps.ts | 15 +- .../src/components/ComputingSpinner.tsx | 5 +- .../src/components/DeckSetup/DeckSetup.tsx | 113 +- .../components/DeckSetup/LabwareOnDeck.tsx | 44 +- .../DeckSetup/LabwareOverlays/BlockedSlot.tsx | 17 +- .../LabwareOverlays/BrowseLabware.tsx | 35 +- .../DeckSetup/LabwareOverlays/DragPreview.tsx | 31 +- .../DeckSetup/LabwareOverlays/EditLabware.tsx | 99 +- .../LabwareOverlays/LabwareControls.tsx | 26 +- .../LabwareOverlays/LabwareHighlight.tsx | 13 +- .../DeckSetup/LabwareOverlays/LabwareName.tsx | 30 +- .../LabwareOverlays/NameThisLabware.tsx | 49 +- .../LabwareOverlays/SlotControls.tsx | 94 +- .../__tests__/SlotControls.test.tsx | 21 +- .../DeckSetup/LabwareOverlays/index.ts | 1 - .../src/components/DeckSetup/ModuleTag.tsx | 48 +- .../src/components/DeckSetup/ModuleViz.tsx | 21 +- .../src/components/DeckSetup/SlotWarning.tsx | 21 +- .../DeckSetup/__tests__/DeckSetup.test.ts | 37 +- .../DeckSetup/__tests__/ModuleTag.test.tsx | 46 +- .../components/DeckSetup/getModuleVizDims.ts | 41 +- .../src/components/DeckSetup/index.ts | 47 +- .../src/components/DeckSetupManager.tsx | 3 +- .../src/components/EditModules.tsx | 31 +- .../src/components/EditableTextField.tsx | 23 +- protocol-designer/src/components/FilePage.tsx | 46 +- .../components/FileSidebar/FileSidebar.tsx | 63 +- .../__tests__/FileSidebar.test.tsx | 39 +- .../src/components/FileSidebar/index.ts | 42 +- .../utils/__tests__/getUnusedEntities.test.ts | 6 +- .../FileSidebar/utils/getUnusedEntities.ts | 12 +- .../src/components/FileSidebar/utils/index.ts | 2 - .../src/components/FormManager/index.tsx | 3 +- .../src/components/Hints/index.tsx | 47 +- .../src/components/Hints/useBlockingHint.tsx | 38 +- .../LabwareDetailsCard/LabwareDetailsCard.tsx | 11 +- .../LabwareDetailsCard/index.ts | 51 +- .../src/components/IngredientsList/index.tsx | 69 +- .../components/KnowledgeBaseLink/index.tsx | 17 +- .../LabwareSelectionModal/LabwareItem.tsx | 23 +- .../LabwareSelectionModal/LabwarePreview.tsx | 11 +- .../LabwareSelectionModal.tsx | 65 +- .../components/LabwareSelectionModal/index.ts | 59 +- .../LiquidPlacementForm.tsx | 60 +- .../components/LiquidPlacementForm/index.ts | 57 +- .../src/components/LiquidPlacementModal.tsx | 56 +- .../components/LiquidsPage/LiquidEditForm.tsx | 32 +- .../LiquidsPage/LiquidsPageInfo.tsx | 3 +- .../src/components/LiquidsPage/index.tsx | 41 +- .../src/components/LiquidsSidebar/index.tsx | 36 +- .../components/PrereleaseModeIndicator.tsx | 3 +- .../src/components/ProtocolEditor.tsx | 9 +- .../src/components/SelectionRect.tsx | 48 +- .../FeatureFlagCard/FeatureFlagCard.tsx | 30 +- .../SettingsPage/FeatureFlagCard/index.ts | 35 +- .../components/SettingsPage/SettingsApp.tsx | 35 +- .../SettingsPage/SettingsSidebar.tsx | 31 +- .../src/components/SettingsPage/index.tsx | 22 +- .../src/components/StepCreationButton.tsx | 51 +- .../StepEditForm/ButtonRow/index.tsx | 17 +- .../src/components/StepEditForm/FormAlerts.ts | 59 +- .../StepEditForm/StepEditFormComponent.tsx | 43 +- .../StepEditForm/__tests__/utils.test.ts | 10 +- .../fields/BlowoutLocationField.tsx | 18 +- .../getDisabledChangeTipOptions.ts | 28 +- .../fields/ChangeTipField/index.tsx | 22 +- .../StepEditForm/fields/CheckboxRowField.tsx | 22 +- .../StepEditForm/fields/DelayFields.tsx | 21 +- .../fields/DisposalVolumeField.tsx | 62 +- .../fields/FlowRateField/FlowRateInput.tsx | 60 +- .../fields/FlowRateField/index.tsx | 58 +- .../StepEditForm/fields/LabwareField.ts | 33 +- .../StepEditForm/fields/MixFields.tsx | 15 +- .../StepEditForm/fields/PathField/Path.tsx | 39 +- .../fields/PathField/getDisabledPathMap.ts | 42 +- .../StepEditForm/fields/PathField/index.ts | 21 +- .../StepEditForm/fields/PipetteField.tsx | 28 +- .../StepEditForm/fields/ProfileItemRows.tsx | 122 +- .../StepEditForm/fields/RadioGroupField.tsx | 20 +- .../fields/StepFormDropdownField.tsx | 23 +- .../StepEditForm/fields/TextField.tsx | 16 +- .../TipPositionField/TipPositionModal.tsx | 59 +- .../TipPositionField/TipPositionZAxisViz.tsx | 9 +- .../fields/TipPositionField/index.tsx | 59 +- .../fields/TipPositionField/utils.ts | 15 +- .../StepEditForm/fields/ToggleRowField.tsx | 18 +- .../StepEditForm/fields/VolumeField.tsx | 18 +- .../fields/WellOrderField/WellOrderModal.tsx | 76 +- .../fields/WellOrderField/WellOrderViz.tsx | 13 +- .../fields/WellOrderField/index.tsx | 38 +- .../WellSelectionField/WellSelectionInput.tsx | 61 +- .../WellSelectionField/WellSelectionModal.tsx | 81 +- .../fields/WellSelectionField/index.ts | 57 +- .../fields/__tests__/DelayFields.test.tsx | 99 +- .../fields/__tests__/WellOrderField.test.tsx | 13 +- .../makeSingleEditFieldProps.test.ts | 34 +- .../components/StepEditForm/fields/index.ts | 2 - .../fields/makeSingleEditFieldProps.ts | 45 +- .../StepEditForm/forms/AspDispSection.tsx | 16 +- .../StepEditForm/forms/MagnetForm.tsx | 7 +- .../components/StepEditForm/forms/MixForm.tsx | 5 +- .../forms/MoveLiquidForm/SourceDestFields.tsx | 26 +- .../MoveLiquidForm/SourceDestHeaders.tsx | 25 +- .../forms/MoveLiquidForm/index.tsx | 7 +- .../StepEditForm/forms/PauseForm.tsx | 9 +- .../StepEditForm/forms/TemperatureForm.tsx | 5 +- .../ThermocyclerForm/ProfileSettings.tsx | 9 +- .../forms/ThermocyclerForm/StateFields.tsx | 17 +- .../forms/ThermocyclerForm/index.tsx | 10 +- .../forms/__tests__/MagnetForm.test.tsx | 56 +- .../forms/__tests__/MixForm.test.tsx | 42 +- .../forms/__tests__/SourceDestFields.test.tsx | 104 +- .../components/StepEditForm/forms/index.ts | 2 - .../src/components/StepEditForm/index.tsx | 76 +- .../src/components/StepEditForm/types.ts | 54 +- .../src/components/StepEditForm/utils.ts | 95 +- .../StepSelectionBannerComponent.tsx | 35 +- .../__tests__/StepSelectionBanner.test.tsx | 12 +- .../components/StepSelectionBanner/index.tsx | 3 +- .../src/components/TitledListNotes.tsx | 9 +- .../components/WellSelectionInstructions.tsx | 4 +- .../components/__tests__/EditModules.test.tsx | 36 +- .../components/__tests__/FilePage.test.tsx | 23 +- .../__tests__/StepCreationButton.test.tsx | 5 +- .../src/components/alerts/Alerts.tsx | 21 +- .../src/components/alerts/ErrorContents.tsx | 14 +- .../src/components/alerts/PDAlert.tsx | 17 +- .../src/components/alerts/TimelineAlerts.tsx | 40 +- .../src/components/alerts/WarningContents.tsx | 13 +- .../src/components/alerts/types.ts | 12 +- .../components/labware/BrowsableLabware.tsx | 21 +- .../components/labware/BrowseLabwareModal.tsx | 40 +- .../components/labware/SelectableLabware.tsx | 44 +- .../src/components/labware/SingleLabware.tsx | 5 +- .../src/components/labware/WellTooltip.tsx | 37 +- .../src/components/labware/index.ts | 2 - .../src/components/labware/utils.ts | 11 +- .../src/components/lists/PDListItem.tsx | 12 +- .../src/components/lists/PDTitledList.tsx | 5 +- .../src/components/lists/TitledStepList.tsx | 48 +- .../lists/__tests__/TitledStepList.test.tsx | 3 +- .../src/components/lists/index.ts | 2 - .../__tests__/AnnouncementModal.test.tsx | 13 +- .../AnnouncementModal/announcements.tsx | 16 +- .../modals/AnnouncementModal/index.tsx | 6 +- .../modals/AutoAddPauseUntilTempStepModal.tsx | 13 +- .../components/modals/ConfirmDeleteModal.tsx | 14 +- .../EditModulesModal/ConnectedSlotMap.tsx | 11 +- .../MagneticModuleWarningModalContent.tsx | 3 +- .../modals/EditModulesModal/ModelDropdown.tsx | 21 +- .../modals/EditModulesModal/SlotDropdown.tsx | 23 +- .../__tests__/EditModulesModal.test.tsx | 37 +- .../__tests__/ModelDropdown.test.tsx | 19 +- .../__tests__/SlotDropdown.test.tsx | 19 +- .../__tests__/form-state.test.tsx | 35 +- .../modals/EditModulesModal/form-state.ts | 18 +- .../modals/EditModulesModal/index.tsx | 53 +- .../StepChangesConfirmModal.tsx | 8 +- .../modals/EditPipettesModal/index.ts | 92 +- .../modals/FilePipettesModal/ModuleFields.tsx | 62 +- .../FilePipettesModal/PipetteDiagram.tsx | 20 +- .../FilePipettesModal/PipetteFields.tsx | 57 +- .../__tests__/ModuleFields.test.tsx | 8 +- .../__tests__/PipetteFields.test.tsx | 42 +- .../__tests__/index.test.tsx | 12 +- .../modals/FilePipettesModal/index.tsx | 138 +- .../FileUploadMessageModal.tsx | 15 +- .../__tests__/modalContents.test.ts | 5 - .../modals/FileUploadMessageModal/index.ts | 34 +- .../FileUploadMessageModal/modalContents.tsx | 12 +- .../modals/FileUploadMessageModal/types.ts | 10 +- .../modals/GateModal/SignUpForm.tsx | 10 +- .../src/components/modals/GateModal/index.tsx | 48 +- .../LabwareUploadMessageModal.tsx | 31 +- .../modals/LabwareUploadMessageModal/index.ts | 34 +- .../components/modals/MoreOptionsModal.tsx | 51 +- .../components/modals/NewFileModal/index.ts | 100 +- .../src/components/modules/CrashInfoBox.tsx | 14 +- .../components/modules/EditModulesCard.tsx | 16 +- .../src/components/modules/ModuleDiagram.tsx | 19 +- .../src/components/modules/ModuleRow.tsx | 27 +- .../__tests__/EditModulesCard.test.tsx | 37 +- .../modules/__tests__/ModuleDiagram.test.tsx | 2 - .../modules/__tests__/ModuleRow.test.tsx | 12 +- .../modules/__tests__/utils.test.ts | 6 - .../src/components/modules/index.ts | 1 - .../src/components/modules/utils.ts | 6 +- .../portals/MainPageModalPortal.tsx | 9 +- .../src/components/portals/TopPortal.tsx | 9 +- .../portals/__mocks__/MainPageModalPortal.ts | 8 - .../portals/__mocks__/MainPageModalPortal.tsx | 7 + .../steplist/AspirateDispenseHeader.tsx | 11 +- .../src/components/steplist/ContextMenu.tsx | 45 +- .../steplist/DraggableStepItems.tsx | 154 +- .../src/components/steplist/IngredPill.tsx | 20 +- .../steplist/LabwareTooltipContents.tsx | 9 +- .../src/components/steplist/MixHeader.tsx | 13 +- .../components/steplist/ModuleStepItems.tsx | 37 +- .../steplist/MultiChannelSubstep.tsx | 27 +- .../steplist/MultiSelectToolbar/index.tsx | 57 +- .../components/steplist/PauseStepItems.tsx | 9 +- .../components/steplist/PresavedStepItem.tsx | 13 +- .../components/steplist/SourceDestSubstep.tsx | 25 +- .../StartingDeckStateTerminalItem.tsx | 22 +- .../src/components/steplist/StepItem.tsx | 144 +- .../src/components/steplist/StepList.tsx | 25 +- .../src/components/steplist/SubstepRow.tsx | 47 +- .../TerminalItem/TerminalItemLink.tsx | 34 +- .../steplist/TerminalItem/index.tsx | 28 +- .../__tests__/ModuleStepItems.test.tsx | 3 +- .../__tests__/MultiSelectToolbar.test.tsx | 23 +- .../__tests__/StepItemContents.test.tsx | 15 +- .../steplist/__tests__/StepList.test.tsx | 7 +- .../steplist/__tests__/TerminalItem.test.tsx | 36 +- .../src/components/steplist/index.ts | 8 +- .../src/components/steplist/utils.ts | 9 +- .../src/components/swatchColors.ts | 4 +- protocol-designer/src/configureStore.ts | 45 +- protocol-designer/src/constants.ts | 64 +- .../src/containers/ConnectedFilePage.ts | 47 +- .../src/containers/ConnectedMainPanel.tsx | 30 +- .../src/containers/ConnectedNav.tsx | 35 +- .../src/containers/ConnectedSidebar.tsx | 25 +- .../src/containers/ConnectedStepItem.tsx | 108 +- .../src/containers/ConnectedStepList.ts | 36 +- .../src/containers/ConnectedTitleBar.tsx | 55 +- .../src/containers/IngredientsList.ts | 37 +- .../__tests__/ConnectedStepItem.test.tsx | 198 +- .../src/dismiss/__tests__/reducers.test.ts | 5 +- protocol-designer/src/dismiss/actions.ts | 21 +- protocol-designer/src/dismiss/index.ts | 5 +- protocol-designer/src/dismiss/reducers.ts | 50 +- protocol-designer/src/dismiss/selectors.ts | 30 +- .../__tests__/getFlagsFromQueryParams.test.ts | 7 +- .../src/feature-flags/actions.ts | 13 +- protocol-designer/src/feature-flags/index.ts | 5 +- .../src/feature-flags/reducers.ts | 31 +- .../src/feature-flags/selectors.ts | 20 +- protocol-designer/src/feature-flags/types.ts | 17 +- protocol-designer/src/feature-flags/utils.ts | 12 +- .../__fixtures__/createFile/commonFields.ts | 43 +- .../__fixtures__/createFile/engageMagnet.ts | 24 +- .../__fixtures__/createFile/noModules.ts | 13 +- .../__fixtures__/createFile/v5Fixture.ts | 16 +- .../__tests__/commandsSelectors.test.ts | 13 +- .../file-data/__tests__/createFile.test.ts | 66 +- .../getExportedFileSchemaVersion.test.ts | 10 +- .../__tests__/getIsV4Protocol.test.ts | 19 +- ...melineWithoutAirGapDispenseCommand.test.ts | 1 + protocol-designer/src/file-data/actions.ts | 22 +- protocol-designer/src/file-data/index.ts | 5 +- .../src/file-data/reducers/index.ts | 59 +- .../src/file-data/selectors/commands.ts | 71 +- .../src/file-data/selectors/fileCreator.ts | 67 +- .../src/file-data/selectors/fileFields.ts | 13 +- .../src/file-data/selectors/index.ts | 1 - protocol-designer/src/file-data/types.ts | 17 +- protocol-designer/src/file-types.ts | 46 +- protocol-designer/src/form-types.ts | 348 +- protocol-designer/src/index.tsx | 2 +- protocol-designer/src/initialize.ts | 7 +- .../src/labware-defs/__mocks__/utils.ts | 25 +- protocol-designer/src/labware-defs/actions.ts | 110 +- protocol-designer/src/labware-defs/index.ts | 5 +- .../src/labware-defs/reducers.ts | 47 +- .../src/labware-defs/selectors.ts | 36 +- protocol-designer/src/labware-defs/types.ts | 53 +- protocol-designer/src/labware-defs/utils.ts | 22 +- .../labware-ingred/__tests__/actions.test.ts | 64 +- .../__tests__/containers.test.ts | 2 + .../__tests__/selectors.test.ts | 27 +- .../labware-ingred/__tests__/utils.test.ts | 2 - .../src/labware-ingred/actions/actions.ts | 284 +- .../src/labware-ingred/actions/index.ts | 1 - .../src/labware-ingred/actions/thunks.ts | 37 +- .../src/labware-ingred/reducers/index.ts | 156 +- .../src/labware-ingred/selectors.ts | 149 +- protocol-designer/src/labware-ingred/types.ts | 86 +- protocol-designer/src/labware-ingred/utils.ts | 21 +- .../src/load-file/__tests__/actions.test.ts | 33 +- .../src/load-file/__tests__/reducers.test.ts | 15 +- protocol-designer/src/load-file/actions.ts | 86 +- protocol-designer/src/load-file/index.ts | 5 +- .../src/load-file/migration/1_1_0.ts | 118 +- .../src/load-file/migration/3_0_0.ts | 34 +- .../src/load-file/migration/4_0_0.ts | 15 +- .../src/load-file/migration/5_0_0.ts | 13 +- .../src/load-file/migration/5_1_0.ts | 13 +- .../src/load-file/migration/5_2_0.ts | 12 +- .../migration/__tests__/1_1_0.test.ts | 28 +- .../migration/__tests__/3_0_0.test.ts | 3 +- ...{3_0_0.test.js.snap => 3_0_0.test.ts.snap} | 0 .../migration/__tests__/index.test.ts | 4 +- .../src/load-file/migration/index.ts | 25 +- .../utils/__mocks__/v1LabwareModelToV2Def.ts | 9 +- .../migration/utils/v1LabwareModelToV2Def.ts | 12 +- protocol-designer/src/load-file/reducers.ts | 33 +- protocol-designer/src/load-file/selectors.ts | 12 +- protocol-designer/src/load-file/types.ts | 38 +- protocol-designer/src/load-file/utils.ts | 6 +- .../src/localization/en/form.json | 2 +- .../src/localization/en/index.ts | 3 - protocol-designer/src/localization/index.ts | 13 +- protocol-designer/src/modules/moduleData.ts | 76 +- protocol-designer/src/navigation/actions.ts | 18 +- protocol-designer/src/navigation/index.ts | 5 +- .../src/navigation/reducers/index.ts | 32 +- protocol-designer/src/navigation/selectors.ts | 9 +- protocol-designer/src/navigation/types.ts | 1 - protocol-designer/src/networking/index.ts | 4 - .../src/networking/opentronsWebApi.ts | 61 +- protocol-designer/src/persist.ts | 52 +- protocol-designer/src/pipettes/pipetteData.ts | 34 +- .../src/step-forms/actions/index.ts | 44 +- .../src/step-forms/actions/modules.ts | 56 +- .../src/step-forms/actions/pipettes.ts | 48 +- protocol-designer/src/step-forms/index.ts | 7 +- .../src/step-forms/reducers/index.ts | 564 +- .../reducers/nestedCombineReducers.ts | 55 +- .../src/step-forms/selectors/index.ts | 462 +- .../src/step-forms/test/actions.test.ts | 21 +- .../test/createPresavedStepForm.test.ts | 69 +- .../test/getProfileItemsHaveErrors.test.ts | 20 +- .../test/nestedCombineReducers.test.ts | 83 +- .../src/step-forms/test/reducers.test.ts | 913 +- .../src/step-forms/test/selectors.test.ts | 79 +- .../src/step-forms/test/utils.test.ts | 2 - protocol-designer/src/step-forms/types.ts | 165 +- .../utils/createInitialProfileItems.ts | 11 +- .../utils/createPresavedStepForm.ts | 130 +- .../utils/getProfileItemsHaveErrors.ts | 14 +- .../src/step-forms/utils/index.ts | 41 +- .../src/steplist/actions/actions.ts | 182 +- .../src/steplist/actions/index.ts | 1 - .../src/steplist/actions/types.ts | 18 +- .../src/steplist/fieldLevel/errors.ts | 45 +- .../src/steplist/fieldLevel/index.ts | 66 +- .../src/steplist/fieldLevel/processing.ts | 32 +- .../steplist/fieldLevel/test/errors.test.ts | 10 +- .../fieldLevel/test/processing.test.ts | 2 - .../src/steplist/formLevel/createBlankForm.ts | 23 +- .../src/steplist/formLevel/errors.ts | 82 +- .../formLevel/getDefaultsForStepType.ts | 18 +- .../getDisabledFieldsMixForm.ts | 6 +- .../getDisabledFieldsMoveLiquidForm.ts | 7 +- .../formLevel/getDisabledFields/index.ts | 9 +- .../getNextDefautEngageHeight.test.ts | 16 +- .../getNextDefaultEngageHeight/index.ts | 12 +- .../getNextDefaultModuleAction.test.ts | 32 +- .../getNextDefaultMagnetAction/index.ts | 10 +- .../getNextDefaultTemperatureModuleId.test.ts | 38 +- ...getNextDefaultThermocyclerModuleId.test.ts | 29 +- .../getNextDefaultTemperatureModuleId.ts | 14 +- .../getNextDefaultThermocyclerModuleId.ts | 11 +- .../formLevel/getNextDefaultModuleId/index.ts | 1 - .../getNextDefaultPipetteId/index.ts | 16 +- .../test/getNextDefaultPipetteId.test.ts | 57 +- .../dependentFieldsUpdateMagnet.ts | 15 +- .../dependentFieldsUpdateMix.ts | 24 +- .../dependentFieldsUpdateMoveLiquid.ts | 222 +- .../dependentFieldsUpdatePause.ts | 10 +- .../dependentFieldsUpdateTemperature.ts | 15 +- .../dependentFieldsUpdateThermocycler.ts | 42 +- .../formLevel/handleFormChange/index.ts | 19 +- .../makeConditionalPatchUpdater.ts | 21 +- .../test/makeConditionalFieldUpdater.test.ts | 33 +- .../handleFormChange/test/mix.test.ts | 82 +- .../handleFormChange/test/moveLiquid.test.ts | 61 +- .../handleFormChange/test/utils.test.ts | 18 +- .../formLevel/handleFormChange/utils.ts | 87 +- .../src/steplist/formLevel/index.ts | 38 +- .../src/steplist/formLevel/profileErrors.ts | 33 +- .../formLevel/stepFormToArgs/getDelayData.ts | 24 +- .../formLevel/stepFormToArgs/index.ts | 18 +- .../stepFormToArgs/magnetFormToArgs.ts | 12 +- .../formLevel/stepFormToArgs/mixFormToArgs.ts | 29 +- .../stepFormToArgs/moveLiquidFormToArgs.ts | 55 +- .../stepFormToArgs/pauseFormToArgs.ts | 25 +- .../stepFormToArgs/temperatureFormToArgs.ts | 10 +- .../stepFormToArgs/test/getDelayData.test.ts | 8 +- .../stepFormToArgs/test/mixFormToArgs.test.ts | 18 +- .../test/moveLiquidFormToArgs.test.ts | 36 +- .../test/pauseFormToArgs.test.ts | 7 +- .../test/stepFormToArgs.test.ts | 16 +- .../test/thermocyclerFormToArgs.test.ts | 19 +- .../stepFormToArgs/thermocyclerFormToArgs.ts | 25 +- .../steplist/formLevel/test/errors.test.ts | 5 +- .../test/getDefaultsForStepType.test.ts | 2 +- .../steplist/formLevel/test/warnings.test.ts | 27 +- .../src/steplist/formLevel/warnings.tsx | 39 +- .../src/steplist/generateSubstepItem.ts | 109 +- protocol-designer/src/steplist/index.ts | 8 +- .../src/steplist/substepTimeline.ts | 96 +- ....js.snap => mergeSubstepsFns.test.ts.snap} | 0 .../src/steplist/test/actions.test.ts | 7 +- .../steplist/test/generateSubsteps.test.ts | 155 +- .../test/getNextNonTerminalItemStepId.test.ts | 2 - .../steplist/test/mergeSubstepsFns.test.ts | 29 +- .../src/steplist/test/mergeWhen.test.ts | 2 +- .../src/steplist/test/substeps.test.ts | 5 - protocol-designer/src/steplist/types.ts | 247 +- protocol-designer/src/steplist/utils/index.ts | 23 +- .../src/steplist/utils/mergeWhen.ts | 12 +- .../src/steplist/utils/orderWells.ts | 10 +- .../generateRobotStateTimeline.test.ts | 10 +- .../generateRobotStateTimeline.ts | 37 +- .../timelineMiddleware/generateSubsteps.ts | 34 +- .../makeTimelineMiddleware.ts | 35 +- .../src/timelineMiddleware/makeWorker.ts | 9 +- .../src/timelineMiddleware/types.ts | 70 +- .../src/timelineMiddleware/worker.ts | 2 +- .../__tests__/timelineFrames.test.ts | 82 +- .../src/top-selectors/substep-highlight.ts | 74 +- .../src/top-selectors/timelineFrames.ts | 37 +- .../top-selectors/timelineWarnings/index.ts | 18 +- .../src/top-selectors/tip-contents/index.ts | 16 +- .../getSelectedWellsCommonValues.test.ts | 18 +- .../getWellContentsAllLabware.test.ts | 32 +- .../getWellContentsAllLabware.ts | 35 +- .../src/top-selectors/well-contents/index.ts | 77 +- .../src/tutorial/__tests__/selectors.test.ts | 14 +- protocol-designer/src/tutorial/actions.ts | 43 +- protocol-designer/src/tutorial/index.ts | 10 +- protocol-designer/src/tutorial/reducers.ts | 47 +- protocol-designer/src/tutorial/selectors.ts | 21 +- protocol-designer/src/types.ts | 64 +- protocol-designer/src/ui/index.ts | 17 +- .../ui/labware/__tests__/selectors.test.ts | 54 +- protocol-designer/src/ui/labware/index.ts | 2 - protocol-designer/src/ui/labware/selectors.ts | 19 +- protocol-designer/src/ui/modules/index.ts | 2 - protocol-designer/src/ui/modules/selectors.ts | 11 +- protocol-designer/src/ui/modules/utils.ts | 26 +- .../src/ui/steps/__fixtures__/index.ts | 5 +- .../steps/actions/__tests__/actions.test.ts | 121 +- .../addAndSelectStepWithHints.test.ts | 95 +- .../steps/actions/__tests__/addStep.test.ts | 1 - .../src/ui/steps/actions/actions.ts | 84 +- .../src/ui/steps/actions/thunks/index.ts | 86 +- .../src/ui/steps/actions/types.ts | 189 +- protocol-designer/src/ui/steps/index.ts | 5 +- protocol-designer/src/ui/steps/reducers.ts | 150 +- protocol-designer/src/ui/steps/selectors.ts | 130 +- .../src/ui/steps/test/reducers.test.ts | 21 +- .../src/ui/steps/test/selectors.test.ts | 98 +- protocol-designer/src/ui/steps/utils.ts | 54 +- .../labwareModuleCompatibility.test.ts | 11 +- protocol-designer/src/utils/index.ts | 33 +- .../src/utils/labwareModuleCompatibility.ts | 20 +- .../src/well-selection/actions.ts | 44 +- .../src/well-selection/reducers.ts | 31 +- .../src/well-selection/selectors.ts | 12 +- protocol-designer/tsconfig-data.json | 12 + protocol-designer/tsconfig.json | 24 + protocol-designer/typings/css-modules.d.ts | 5 + protocol-designer/typings/fixtures.d.ts | 9 + protocol-designer/typings/global.d.ts | 15 + protocol-designer/typings/images.d.ts | 21 + .../typings/react-dnd-mouse-backend.d.ts | 5 + protocol-designer/typings/reselect.d.ts | 76 + .../typings/styled-components.d.ts | 1 + protocol-designer/typings/uuid.d.ts | 5 + protocol-designer/webpack.config.js | 2 +- scripts/setup-global-mocks.js | 4 +- shared-data/protocol/flowTypes/schemaV1.js | 126 - shared-data/protocol/flowTypes/schemaV3.js | 135 - shared-data/protocol/flowTypes/schemaV4.js | 115 - shared-data/protocol/flowTypes/schemaV5.js | 31 - shared-data/protocol/flowTypes/schemaV6.js | 19 - shared-data/protocol/types/schemaV3.ts | 3 +- shared-data/protocol/types/schemaV4.ts | 8 +- shared-data/protocol/types/schemaV5.ts | 8 +- step-generation/src/types.ts | 7 +- step-generation/tsconfig.json | 5 + tsconfig-base.json | 3 +- tsconfig-eslint.json | 4 +- tsconfig.json | 3 + yarn.lock | 13206 +++++++--------- 520 files changed, 14544 insertions(+), 17487 deletions(-) create mode 100644 protocol-designer/.gitignore delete mode 100644 protocol-designer/src/components/portals/__mocks__/MainPageModalPortal.ts create mode 100644 protocol-designer/src/components/portals/__mocks__/MainPageModalPortal.tsx rename protocol-designer/src/load-file/migration/__tests__/__snapshots__/{3_0_0.test.js.snap => 3_0_0.test.ts.snap} (100%) rename protocol-designer/src/steplist/test/__snapshots__/{mergeSubstepsFns.test.js.snap => mergeSubstepsFns.test.ts.snap} (100%) create mode 100644 protocol-designer/tsconfig-data.json create mode 100644 protocol-designer/tsconfig.json create mode 100644 protocol-designer/typings/css-modules.d.ts create mode 100644 protocol-designer/typings/fixtures.d.ts create mode 100644 protocol-designer/typings/global.d.ts create mode 100644 protocol-designer/typings/images.d.ts create mode 100644 protocol-designer/typings/react-dnd-mouse-backend.d.ts create mode 100644 protocol-designer/typings/reselect.d.ts create mode 100644 protocol-designer/typings/styled-components.d.ts create mode 100644 protocol-designer/typings/uuid.d.ts delete mode 100644 shared-data/protocol/flowTypes/schemaV1.js delete mode 100644 shared-data/protocol/flowTypes/schemaV3.js delete mode 100644 shared-data/protocol/flowTypes/schemaV4.js delete mode 100644 shared-data/protocol/flowTypes/schemaV5.js delete mode 100644 shared-data/protocol/flowTypes/schemaV6.js diff --git a/.madgerc b/.madgerc index 7724f1f38fd..fd5fdd3fe74 100644 --- a/.madgerc +++ b/.madgerc @@ -2,6 +2,12 @@ "detectiveOptions": { "es6": { "skipTypeImports": true - } + }, + "ts": { + "skipTypeImports": true + }, + "tsx": { + "skipTypeImports": true + } } } diff --git a/Makefile b/Makefile index 332d2ec2051..484b27e2967 100755 --- a/Makefile +++ b/Makefile @@ -223,7 +223,7 @@ clean-ts: # TODO: Ian 2019-12-17 gradually add components and shared-data .PHONY: circular-dependencies-js circular-dependencies-js: - madge $(and $(CI),--no-spinner --no-color) --circular protocol-designer/src/index.js + madge $(and $(CI),--no-spinner --no-color) --circular protocol-designer/src/index.tsx madge $(and $(CI),--no-spinner --no-color) --circular step-generation/src/index.ts madge $(and $(CI),--no-spinner --no-color) --circular labware-library/src/index.tsx madge $(and $(CI),--no-spinner --no-color) --circular app/src/index.tsx diff --git a/app-shell/src/system-info/network-interfaces.ts b/app-shell/src/system-info/network-interfaces.ts index f50520ba4cf..c18d638eb9f 100644 --- a/app-shell/src/system-info/network-interfaces.ts +++ b/app-shell/src/system-info/network-interfaces.ts @@ -18,7 +18,7 @@ export function getActiveInterfaces(): NetworkInterface[] { const ifaces = os.networkInterfaces() return Object.keys(ifaces).flatMap((name: string) => { - // $FlowFixMe(mc, 2020-05-27): Flow def of os.networkInterfaces return is incomplete + // @ts-expect-error(sa, 2021-6-28): this could be undefined because Object.keys returns a list of strings (too generic) return ifaces[name] .filter(iface => !iface.internal) .map(iface => ({ ...iface, name })) diff --git a/app/src/pages/Robots/InstrumentSettings/PipetteInfo.tsx b/app/src/pages/Robots/InstrumentSettings/PipetteInfo.tsx index 66e35f73621..b8b3f3120b6 100644 --- a/app/src/pages/Robots/InstrumentSettings/PipetteInfo.tsx +++ b/app/src/pages/Robots/InstrumentSettings/PipetteInfo.tsx @@ -115,8 +115,8 @@ export function PipetteInfo(props: PipetteInfoProps): JSX.Element { ( @@ -55,6 +56,7 @@ export function InstrumentSettings( render={routeProps => ( diff --git a/app/src/pages/Robots/RobotSettings/AdvancedSettingsCard.tsx b/app/src/pages/Robots/RobotSettings/AdvancedSettingsCard.tsx index dddb93e24e0..efb5a956a94 100644 --- a/app/src/pages/Robots/RobotSettings/AdvancedSettingsCard.tsx +++ b/app/src/pages/Robots/RobotSettings/AdvancedSettingsCard.tsx @@ -89,7 +89,6 @@ export function AdvancedSettingsCard( - {/* @ts-expect-error TODO: Link does not have a disable prop, is this prop achieving anything useful */} { const realBlob = global.Blob beforeAll(() => { + // @ts-expect-error(sa, 2021-6-28): not a valid blob interface global.Blob = function (content: any, options: any) { return { content, options } } diff --git a/app/src/redux/protocol/__tests__/actions.test.ts b/app/src/redux/protocol/__tests__/actions.test.ts index da7cf401567..0b2289e0805 100644 --- a/app/src/redux/protocol/__tests__/actions.test.ts +++ b/app/src/redux/protocol/__tests__/actions.test.ts @@ -23,6 +23,7 @@ describe('protocol actions', () => { mockReader = { readAsText: jest.fn(), readAsArrayBuffer: jest.fn() } store = mockStore({}) + // @ts-expect-error(sa, 2021-6-28): not a valid FileReader interface global.FileReader = jest.fn(() => mockReader) getFeatureFlags.mockReturnValue({ enableBundleUpload: false, diff --git a/app/src/redux/robot-api/__tests__/http.test.ts b/app/src/redux/robot-api/__tests__/http.test.ts index 72da5f60463..264b1e540d7 100644 --- a/app/src/redux/robot-api/__tests__/http.test.ts +++ b/app/src/redux/robot-api/__tests__/http.test.ts @@ -22,6 +22,7 @@ describe('robot-api http client', () => { let robot: RobotHost beforeAll(() => { + // @ts-expect-error(sa, 2021-6-28): global.fetch and node fetch have different interfaces global.fetch = fetch testApp = express() testApp.use((express as any).json()) @@ -53,6 +54,7 @@ describe('robot-api http client', () => { }) afterAll(() => { + // @ts-expect-error(sa, 2021-6-28): can't delete non optional properties delete global.fetch if (testServer) { diff --git a/components/src/forms/SelectField.tsx b/components/src/forms/SelectField.tsx index de6e461b269..bde843e4764 100644 --- a/components/src/forms/SelectField.tsx +++ b/components/src/forms/SelectField.tsx @@ -54,7 +54,9 @@ export function SelectField(props: SelectFieldProps): JSX.Element { const allOptions = options.flatMap(og => og.options || [og]) const value = find(allOptions, opt => opt.value === props.value) || null const caption = error || props.caption + // @ts-expect-error(sa, 2021-6-23): cast value to boolean const captionCx = cx(styles.select_caption, { [styles.error]: error }) + // @ts-expect-error(sa, 2021-6-23): cast value to boolean const fieldCx = cx(styles.select_field, { [styles.error]: error }, className) return ( diff --git a/components/src/lists/ListItem.tsx b/components/src/lists/ListItem.tsx index 7a2a69ffc55..a760e760351 100644 --- a/components/src/lists/ListItem.tsx +++ b/components/src/lists/ListItem.tsx @@ -45,7 +45,7 @@ export const ListItem = React.forwardRef( (props: ListItemProps, ref: React.ForwardedRef) => { const { url, isDisabled, iconName, activeClassName, exact } = props const onClick = props.onClick && !isDisabled ? props.onClick : undefined - + // @ts-expect-error(sa, 2021-6-23): cast value to boolean const className = classnames(props.className, styles.list_item, { [styles.disabled]: isDisabled, [styles.clickable]: onClick, diff --git a/components/src/lists/TitledList.tsx b/components/src/lists/TitledList.tsx index 4820993e156..cd089002ca2 100644 --- a/components/src/lists/TitledList.tsx +++ b/components/src/lists/TitledList.tsx @@ -87,7 +87,7 @@ export function TitledList(props: TitledListProps): JSX.Element { [styles.titled_list_selected]: !disabled && props.selected, [styles.hover_border]: !disabled && props.hovered, }) - + // @ts-expect-error(sa, 2021-6-23): cast value to boolean const titleBarClass = cx(styles.title_bar, { [styles.clickable]: props.onClick, }) diff --git a/components/src/modals/Overlay.tsx b/components/src/modals/Overlay.tsx index 4c0558d9a64..1444e04fc0c 100644 --- a/components/src/modals/Overlay.tsx +++ b/components/src/modals/Overlay.tsx @@ -16,7 +16,7 @@ export interface OverlayProps { */ export function Overlay(props: OverlayProps): JSX.Element { const { alertOverlay, onClick } = props - + // @ts-expect-error(sa, 2021-6-23): cast value to boolean const className = cx(styles.overlay, { [styles.clickable]: onClick, [styles.alert_modal_overlay]: alertOverlay, diff --git a/components/src/structure/TitleBar.tsx b/components/src/structure/TitleBar.tsx index 8cfb933b453..51e5eb9dafe 100644 --- a/components/src/structure/TitleBar.tsx +++ b/components/src/structure/TitleBar.tsx @@ -62,6 +62,7 @@ export function TitleBar(props: TitleBarProps): JSX.Element { {...back} /> )} + {/* @ts-expect-error(sa, 2021-6-23): cast value to boolean */}

{title}

{separator} {subheading} diff --git a/discovery-client/src/mdns-browser/interfaces.ts b/discovery-client/src/mdns-browser/interfaces.ts index d6975f1c786..9f6c0a5c612 100644 --- a/discovery-client/src/mdns-browser/interfaces.ts +++ b/discovery-client/src/mdns-browser/interfaces.ts @@ -18,6 +18,7 @@ export function getSystemInterfaces(): NetworkInterface[] { const interfaceMap = networkInterfaces() return Object.keys(interfaceMap).flatMap(ifaceName => { + // @ts-expect-error TS thinks interfaceMap[ifaceName] could be undefined because Object.keys returns a list of strings (too generic) return interfaceMap[ifaceName] .filter(iface => iface.family === 'IPv4' && !iface.internal) .map(iface => ({ name: ifaceName, address: iface.address })) diff --git a/labware-library/src/labware-creator/components/RadioField.tsx b/labware-library/src/labware-creator/components/RadioField.tsx index 4b767ccd5aa..339a518149a 100644 --- a/labware-library/src/labware-creator/components/RadioField.tsx +++ b/labware-library/src/labware-creator/components/RadioField.tsx @@ -11,7 +11,7 @@ import fieldStyles from './fieldStyles.css' interface Props { name: keyof LabwareFields options: RadioGroupProps['options'] - labelTextClassName?: string | null | undefined + labelTextClassName?: string | null } export const RadioField = (props: Props): JSX.Element => ( diff --git a/package.json b/package.json index 0ab653dafef..2171bc85c87 100755 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "react-docgen-typescript": "^1.21.0", "react-dom": "16.8.6", "react-snap": "^1.23.0", - "react-test-renderer": "^16.8.6", + "react-test-renderer": "16.8.6", "redux-mock-store": "^1.5.3", "rehype": "^9.0.0", "rehype-urls": "^1.0.0", diff --git a/protocol-designer/.gitignore b/protocol-designer/.gitignore new file mode 100644 index 00000000000..34100bf1852 --- /dev/null +++ b/protocol-designer/.gitignore @@ -0,0 +1,2 @@ +# type caches +lib/ \ No newline at end of file diff --git a/protocol-designer/benchmarks/timelineGeneration.js b/protocol-designer/benchmarks/timelineGeneration.js index 60b74398c63..74736da3806 100644 --- a/protocol-designer/benchmarks/timelineGeneration.js +++ b/protocol-designer/benchmarks/timelineGeneration.js @@ -1,4 +1,3 @@ -// @flow import assert from 'assert' import bench from 'nanobench' import { diff --git a/protocol-designer/docs/STEP_FORMS_AND_PROTOCOL_TIMELINE.md b/protocol-designer/docs/STEP_FORMS_AND_PROTOCOL_TIMELINE.md index 6be7733ae4e..be12bbe8d5f 100644 --- a/protocol-designer/docs/STEP_FORMS_AND_PROTOCOL_TIMELINE.md +++ b/protocol-designer/docs/STEP_FORMS_AND_PROTOCOL_TIMELINE.md @@ -20,7 +20,7 @@ Motivation: ultimately, `step-generation`'s main job is to build a timeline that In practice, these functions are never written directly, `CommandCreator` functions are always created by other functions. For a simple example, see `aspirate.js`: `const aspirate = (args: AspirateParams): CommandCreator => (...) => {...}`. We also use the phrase "command creator" to refer to a function that returns a `CommandCreator`. (This wording needs to be refactored because it's very confusing. Sorry!) -Additionally, some of these functions return `Array`; we call these "compound command creators". Compound command creators can be curried into non-compound command creators by use of `reduceCommandCreators` util. We plan to homogenize these to make it all simpler. Part of the complexity comes from the requirement that we want to control the granularity of commands and frames: a single "frame" can encompass one or more atomic commands. For example a Transfer Step in PD should be a single frame in the PD timeline from `getRobotStateTimeline`, but it may contain many pickUpTip/aspirate/dispense/touchTip/dropTip/etc atomic commands. The meaning of that granularity depends on how the consumer (eg PD) wants to use it. It's not useful to have hundreds of frames representing every intermediate robot state throughout a protocol, we only want to remember the intermediate states associated with key points in time (eg, a Step). +Additionally, some of these functions return `CommandCreator[]`; we call these "compound command creators". Compound command creators can be curried into non-compound command creators by use of `reduceCommandCreators` util. We plan to homogenize these to make it all simpler. Part of the complexity comes from the requirement that we want to control the granularity of commands and frames: a single "frame" can encompass one or more atomic commands. For example a Transfer Step in PD should be a single frame in the PD timeline from `getRobotStateTimeline`, but it may contain many pickUpTip/aspirate/dispense/touchTip/dropTip/etc atomic commands. The meaning of that granularity depends on how the consumer (eg PD) wants to use it. It's not useful to have hundreds of frames representing every intermediate robot state throughout a protocol, we only want to remember the intermediate states associated with key points in time (eg, a Step). ### Creating a timeline diff --git a/protocol-designer/package.json b/protocol-designer/package.json index 74710b20f78..60a19edbf7c 100755 --- a/protocol-designer/package.json +++ b/protocol-designer/package.json @@ -23,6 +23,9 @@ "@opentrons/components": "4.4.0", "@opentrons/step-generation": "4.4.0", "@typeform/embed": "0.16.0", + "@types/ua-parser-js": "0.7.36", + "@types/redux-actions": "2.6.1", + "@types/uuid": "8.3.0", "ajv": "6.10.2", "classnames": "2.2.5", "cookie": "0.3.1", @@ -42,7 +45,7 @@ "react-redux": "7.2.1", "redux": "4.0.5", "redux-actions": "2.2.1", - "redux-thunk": "2.2.0", + "redux-thunk": "2.3.0", "reselect": "4.0.0", "ua-parser-js": "^0.7.23", "uuid": "3.3.2", diff --git a/protocol-designer/src/__tests__/persist.test.ts b/protocol-designer/src/__tests__/persist.test.ts index cf10fd999c7..f774c825565 100644 --- a/protocol-designer/src/__tests__/persist.test.ts +++ b/protocol-designer/src/__tests__/persist.test.ts @@ -1,10 +1,8 @@ -// @flow - import * as persist from '../persist' describe('persist', () => { - let getItemSpy - let setItemSpy + let getItemSpy: jest.SpyInstance + let setItemSpy: jest.SpyInstance beforeEach(() => { const LocalStorageProto = Object.getPrototypeOf(global.localStorage) diff --git a/protocol-designer/src/__tests__/validateProtocolFixtures.test.ts b/protocol-designer/src/__tests__/validateProtocolFixtures.test.ts index c1961eb1ec1..2260660e8c8 100644 --- a/protocol-designer/src/__tests__/validateProtocolFixtures.test.ts +++ b/protocol-designer/src/__tests__/validateProtocolFixtures.test.ts @@ -1,4 +1,3 @@ -// @flow import Ajv from 'ajv' import glob from 'glob' import last from 'lodash/last' @@ -10,7 +9,7 @@ import protocolV5Schema from '@opentrons/shared-data/protocol/schemas/5.json' import labwareV2Schema from '@opentrons/shared-data/labware/schemas/2.json' // TODO: copied from createFile.test.js -const getAjvValidator = _protocolSchema => { +const getAjvValidator = (_protocolSchema: object) => { const ajv = new Ajv({ allErrors: true, jsonPointers: true, @@ -21,7 +20,11 @@ const getAjvValidator = _protocolSchema => { return validateProtocol } -const expectResultToMatchSchema = (testName, result, _protocolSchema): void => { +const expectResultToMatchSchema = ( + testName: string, + result: any, + _protocolSchema: object +): void => { const validate = getAjvValidator(_protocolSchema) const valid = validate(result) const validationErrors = validate.errors diff --git a/protocol-designer/src/analytics/__tests__/flattenNestedProperties.test.ts b/protocol-designer/src/analytics/__tests__/flattenNestedProperties.test.ts index 2ccb849da68..52ad2986c58 100644 --- a/protocol-designer/src/analytics/__tests__/flattenNestedProperties.test.ts +++ b/protocol-designer/src/analytics/__tests__/flattenNestedProperties.test.ts @@ -1,4 +1,3 @@ -// @flow import { flattenNestedProperties } from '../utils/flattenNestedProperties' describe('flattenNestedProperties', () => { diff --git a/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts b/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts index 7bac8d5f241..ccdcbf11529 100644 --- a/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts +++ b/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts @@ -1,4 +1,3 @@ -// @flow import { when, resetAllWhenMocks } from 'jest-when' import { reduxActionToAnalyticsEvent } from '../middleware' import { getFileMetadata } from '../../file-data/selectors' @@ -7,19 +6,23 @@ import { getPipetteEntities, getSavedStepForms, } from '../../step-forms/selectors' -import type { FileMetadataFields } from '../../file-data/types' -import type { SaveStepFormsMultiAction } from '../../step-forms/actions' +import { SaveStepFormsMultiAction } from '../../step-forms/actions' jest.mock('../../file-data/selectors') jest.mock('../../step-forms/selectors') -const getFileMetadataMock: JestMockFn = getFileMetadata -const getArgsAndErrorsByStepIdMock: JestMockFn< - any, - any -> = getArgsAndErrorsByStepId -const getPipetteEntitiesMock: JestMockFn = getPipetteEntities -const getSavedStepFormsMock: JestMockFn = getSavedStepForms +const getFileMetadataMock = getFileMetadata as jest.MockedFunction< + typeof getFileMetadata +> +const getArgsAndErrorsByStepIdMock = getArgsAndErrorsByStepId as jest.MockedFunction< + typeof getArgsAndErrorsByStepId +> +const getPipetteEntitiesMock = getPipetteEntities as jest.MockedFunction< + typeof getPipetteEntities +> +const getSavedStepFormsMock = getSavedStepForms as jest.MockedFunction< + typeof getSavedStepForms +> let fooState: any beforeEach(() => { fooState = {} @@ -52,6 +55,7 @@ describe('reduxActionToAnalyticsEvent', () => { getArgsAndErrorsByStepIdMock.mockReturnValue({ stepId: { stepArgs: { + // @ts-expect-error id is not on type CommandCreatorArgs id: 'stepId', pipette: 'pipetteId', otherField: 123, @@ -60,6 +64,7 @@ describe('reduxActionToAnalyticsEvent', () => { }, }) getPipetteEntitiesMock.mockReturnValue({ + // @ts-expect-error 'some_pipette_spec_name' isn't a valid pipette type pipetteId: { name: 'some_pipette_spec_name' }, }) @@ -110,7 +115,9 @@ describe('reduxActionToAnalyticsEvent', () => { when(getSavedStepFormsMock) .calledWith(expect.anything()) .mockReturnValue({ + // @ts-expect-error missing fields from test object id_1: { stepType: 'moveLiquid' }, + // @ts-expect-error missing fields from test object id_2: { stepType: 'moveLiquid' }, }) @@ -138,7 +145,9 @@ describe('reduxActionToAnalyticsEvent', () => { when(getSavedStepFormsMock) .calledWith(expect.anything()) .mockReturnValue({ + // @ts-expect-error missing fields from test object id_1: { stepType: 'mix' }, + // @ts-expect-error missing fields from test object id_2: { stepType: 'mix' }, }) @@ -166,7 +175,9 @@ describe('reduxActionToAnalyticsEvent', () => { when(getSavedStepFormsMock) .calledWith(expect.anything()) .mockReturnValue({ + // @ts-expect-error missing fields from test object id_1: { stepType: 'mix' }, + // @ts-expect-error missing fields from test object id_2: { stepType: 'moveLiquid' }, }) diff --git a/protocol-designer/src/analytics/actions.ts b/protocol-designer/src/analytics/actions.ts index 1daddafe68d..05a6466be06 100644 --- a/protocol-designer/src/analytics/actions.ts +++ b/protocol-designer/src/analytics/actions.ts @@ -1,14 +1,12 @@ -// @flow import { initializeFullstory, shutdownFullstory } from './fullstory' -import { setMixpanelTracking } from './mixpanel' -import type { AnalyticsEvent } from './mixpanel' +import { setMixpanelTracking, AnalyticsEvent } from './mixpanel' -export type SetOptIn = {| - type: 'SET_OPT_IN', - payload: boolean, -|} +export interface SetOptIn { + type: 'SET_OPT_IN' + payload: boolean +} -const _setOptIn = (payload: $PropertyType): SetOptIn => { +const _setOptIn = (payload: SetOptIn['payload']): SetOptIn => { // side effects if (payload) { initializeFullstory() @@ -26,13 +24,11 @@ const _setOptIn = (payload: $PropertyType): SetOptIn => { export const optIn = (): SetOptIn => _setOptIn(true) export const optOut = (): SetOptIn => _setOptIn(false) - -export type AnalyticsEventAction = {| - type: 'ANALYTICS_EVENT', - payload: AnalyticsEvent, - meta?: mixed, -|} - +export interface AnalyticsEventAction { + type: 'ANALYTICS_EVENT' + payload: AnalyticsEvent + meta?: unknown +} // NOTE: this action creator should only be used for special cases where you want to // report an analytics event but you do not have any Redux action that sensibly represents // that analytics event. @@ -45,4 +41,7 @@ export type AnalyticsEventAction = {| // we need to read opt-in status from the Redux state. export const analyticsEvent = ( payload: AnalyticsEvent -): AnalyticsEventAction => ({ type: 'ANALYTICS_EVENT', payload }) +): AnalyticsEventAction => ({ + type: 'ANALYTICS_EVENT', + payload, +}) diff --git a/protocol-designer/src/analytics/fullstory.ts b/protocol-designer/src/analytics/fullstory.ts index 52d3e8cb949..99e55f4e9d8 100644 --- a/protocol-designer/src/analytics/fullstory.ts +++ b/protocol-designer/src/analytics/fullstory.ts @@ -1,19 +1,20 @@ -// @flow +// @ts-nocheck import cookie from 'cookie' -export const shutdownFullstory = () => { +export const shutdownFullstory = (): void => { if (window[window['_fs_namespace']]) { window[window['_fs_namespace']].shutdown() } + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete window[window['_fs_namespace']] } -const _setAnalyticsTags = () => { +const _setAnalyticsTags = (): void => { const cookies = cookie.parse(global.document.cookie) const { ot_email: email, ot_name: displayName } = cookies const commit_str = process.env.OT_PD_COMMIT_HASH const version_str = process.env.OT_PD_VERSION - const buildDate_date = new Date((process.env.OT_PD_BUILD_DATE: any)) + const buildDate_date = new Date(process.env.OT_PD_BUILD_DATE as any) // NOTE: fullstory expects the keys 'displayName' and 'email' verbatim // though all other key names must be fit the schema described here @@ -36,7 +37,7 @@ window['_fs_host'] = 'fullstory.com' window['_fs_org'] = process.env.OT_PD_FULLSTORY_ORG window['_fs_namespace'] = 'FS' -export const initializeFullstory = () => { +export const initializeFullstory = (): void => { ;(function (m, n, e, t, l, o, g: any, y: any) { if (e in m) { if (m.console && m.console.log) { diff --git a/protocol-designer/src/analytics/index.ts b/protocol-designer/src/analytics/index.ts index 448de981797..5f980d62427 100644 --- a/protocol-designer/src/analytics/index.ts +++ b/protocol-designer/src/analytics/index.ts @@ -1,8 +1,5 @@ -// @flow import * as actions from './actions' import * as selectors from './selectors' -import { rootReducer, type RootState } from './reducers' - +import { RootState, rootReducer } from './reducers' export { actions, selectors, rootReducer } - export type { RootState } diff --git a/protocol-designer/src/analytics/middleware.ts b/protocol-designer/src/analytics/middleware.ts index a1832358424..d089f988261 100644 --- a/protocol-designer/src/analytics/middleware.ts +++ b/protocol-designer/src/analytics/middleware.ts @@ -1,4 +1,3 @@ -// @flow import uniq from 'lodash/uniq' import { getArgsAndErrorsByStepId, @@ -6,16 +5,15 @@ import { getSavedStepForms, } from '../step-forms/selectors' import { getFileMetadata } from '../file-data/selectors' -import { trackEvent } from './mixpanel' +import { trackEvent, AnalyticsEvent } from './mixpanel' import { getHasOptedIn } from './selectors' import { flattenNestedProperties } from './utils/flattenNestedProperties' -import type { Middleware } from 'redux' -import type { BaseState } from '../types' -import type { FormData, StepType } from '../form-types' -import type { StepArgsAndErrors } from '../steplist' -import type { SaveStepFormAction } from '../ui/steps/actions/thunks' -import type { AnalyticsEventAction } from './actions' -import type { AnalyticsEvent } from './mixpanel' +import { Middleware } from 'redux' +import { BaseState } from '../types' +import { FormData, StepIdType, StepType } from '../form-types' +import { StepArgsAndErrors } from '../steplist' +import { SaveStepFormAction } from '../ui/steps/actions/thunks' +import { AnalyticsEventAction } from './actions' // Converts Redux actions to analytics events (read: Mixpanel events). // Returns null if there is no analytics event associated with the action, @@ -50,10 +48,10 @@ export const reduxActionToAnalyticsEvent = ( : null additionalProperties.__protocolName = fileMetadata.protocolName - + // @ts-expect-error not a valid way to type narrow if (stepArgs.pipette) { additionalProperties.__pipetteName = - // $FlowFixMe(sa, 2021-05-10): stepArgs is unknown typed here for some reason + // @ts-expect-error not a valid way to type narrow pipetteEntities[stepArgs?.pipette].name } @@ -70,11 +68,11 @@ export const reduxActionToAnalyticsEvent = ( const { editedFields, stepIds } = action.payload const additionalProperties = flattenNestedProperties(editedFields) const savedStepForms = getSavedStepForms(state) - const batchEditedStepForms: Array = stepIds.map( - id => savedStepForms[id] + const batchEditedStepForms: FormData[] = stepIds.map( + (id: StepIdType) => savedStepForms[id] ) let stepType = null - const uniqueStepTypes: Array = uniq( + const uniqueStepTypes: StepType[] = uniq( batchEditedStepForms.map(form => form.stepType) ) if (uniqueStepTypes.length === 1) { diff --git a/protocol-designer/src/analytics/mixpanel.ts b/protocol-designer/src/analytics/mixpanel.ts index 15d864443f4..075c194318a 100644 --- a/protocol-designer/src/analytics/mixpanel.ts +++ b/protocol-designer/src/analytics/mixpanel.ts @@ -1,18 +1,17 @@ -// @flow // TODO(IL, 2020-09-09): reconcile with app/src/analytics/mixpanel.js, which this is derived from import mixpanel from 'mixpanel-browser' import { getIsProduction } from '../networking/opentronsWebApi' import { getHasOptedIn } from './selectors' -import type { BaseState } from '../types' +import { BaseState } from '../types' // TODO(IL, 2020-09-09): AnalyticsEvent type copied from app/src/analytics/types.js, consider merging export type AnalyticsEvent = - | {| - name: string, - properties: { ... }, - superProperties?: { ... }, - |} - | {| superProperties: { ... } |} + | { + name: string + properties: { [key: string]: unknown } + superProperties?: { [key: string]: unknown } + } + | { superProperties: { [key: string]: unknown } } // pulled in from environment at build time const MIXPANEL_ID = getIsProduction() @@ -24,7 +23,7 @@ const MIXPANEL_OPTS = { opt_out_tracking_by_default: true, } -export function initializeMixpanel(state: BaseState) { +export function initializeMixpanel(state: BaseState): void { const optedIn = getHasOptedIn(state) || false if (MIXPANEL_ID) { console.debug('Initializing Mixpanel', { optedIn }) @@ -38,19 +37,21 @@ export function initializeMixpanel(state: BaseState) { } // NOTE: Do not use directly. Used in analytics Redux middleware: trackEventMiddleware. -export function trackEvent(event: AnalyticsEvent, optedIn: boolean) { +export function trackEvent(event: AnalyticsEvent, optedIn: boolean): void { console.debug('Trackable event', { event, optedIn }) if (MIXPANEL_ID && optedIn) { if (event.superProperties) { mixpanel.register(event.superProperties) } + // @ts-expect-error not a valid way to type narrow if (event.name) { + // @ts-expect-error not a valid way to type narrow mixpanel.track(event.name, event.properties) } } } -export function setMixpanelTracking(optedIn: boolean) { +export function setMixpanelTracking(optedIn: boolean): void { if (MIXPANEL_ID) { if (optedIn) { console.debug('User has opted into analytics; tracking with Mixpanel') diff --git a/protocol-designer/src/analytics/reducers.ts b/protocol-designer/src/analytics/reducers.ts index 30290512fd3..c0beb172283 100644 --- a/protocol-designer/src/analytics/reducers.ts +++ b/protocol-designer/src/analytics/reducers.ts @@ -1,16 +1,12 @@ -// @flow -import { combineReducers } from 'redux' +import { combineReducers, Reducer } from 'redux' import { handleActions } from 'redux-actions' - -import type { Reducer } from 'redux' -import type { Action } from '../types' -import type { SetOptIn } from './actions' -import type { RehydratePersistedAction } from '../persist' - +import { Action } from '../types' +import { SetOptIn } from './actions' +import { RehydratePersistedAction } from '../persist' type OptInState = boolean | null const optInInitialState = null - -// NOTE(mc, 2020-06-04): `handleActions` cannot be strictly typed +// @ts-expect-error(sb, 2021-6-17): cannot use string literals as action type +// TODO IMMEDIATELY: refactor this to the old fashioned way if we cannot have type safety: https://github.com/redux-utilities/redux-actions/issues/282#issuecomment-595163081 const hasOptedIn: Reducer = handleActions( { SET_OPT_IN: (state: OptInState, action: SetOptIn): OptInState => @@ -25,15 +21,12 @@ const hasOptedIn: Reducer = handleActions( }, optInInitialState ) - const _allReducers = { hasOptedIn, } - -export type RootState = {| - hasOptedIn: OptInState, -|} - +export interface RootState { + hasOptedIn: OptInState +} export const rootReducer: Reducer = combineReducers( _allReducers ) diff --git a/protocol-designer/src/analytics/selectors.ts b/protocol-designer/src/analytics/selectors.ts index a5c411d2ea3..36268e7da5f 100644 --- a/protocol-designer/src/analytics/selectors.ts +++ b/protocol-designer/src/analytics/selectors.ts @@ -1,5 +1,3 @@ -// @flow -import type { BaseState } from '../types' - +import { BaseState } from '../types' export const getHasOptedIn = (state: BaseState): boolean | null => state.analytics.hasOptedIn diff --git a/protocol-designer/src/analytics/utils/flattenNestedProperties.ts b/protocol-designer/src/analytics/utils/flattenNestedProperties.ts index 52eb2b98e90..5208101c1aa 100644 --- a/protocol-designer/src/analytics/utils/flattenNestedProperties.ts +++ b/protocol-designer/src/analytics/utils/flattenNestedProperties.ts @@ -1,12 +1,11 @@ -// @flow import isPlainObject from 'lodash/isPlainObject' - const SEPARATOR = '__' -const _innerFnFlattenNested = (innerProperties: any, prefix: string) => { +const _innerFnFlattenNested = (innerProperties: any, prefix: string): any => { return Object.keys(innerProperties).reduce((acc, key) => { // if the key's value is an object, recurse into it const nestedValue = innerProperties[key] + if (isPlainObject(nestedValue)) { return { ...acc, @@ -19,10 +18,7 @@ const _innerFnFlattenNested = (innerProperties: any, prefix: string) => { if (prefix === '') { return acc } else { - return { - ...acc, - [`${prefix}${SEPARATOR}${key}`]: nestedValue, - } + return { ...acc, [`${prefix}${SEPARATOR}${key}`]: nestedValue } } }, {}) } diff --git a/protocol-designer/src/collision-types.ts b/protocol-designer/src/collision-types.ts index 84b87bbf2d3..6f8500b2194 100644 --- a/protocol-designer/src/collision-types.ts +++ b/protocol-designer/src/collision-types.ts @@ -1,24 +1,20 @@ -// @flow +export interface DragRect { + xStart: number + yStart: number + xDynamic: number + yDynamic: number +} -export type DragRect = {| - xStart: number, - yStart: number, - xDynamic: number, - yDynamic: number, -|} +export interface GenericRect { + x0: number + x1: number + y0: number + y1: number +} -export type GenericRect = {| - x0: number, - x1: number, - y0: number, - y1: number, -|} - -export type BoundingRect = {| - x: number, - y: number, - width: number, - height: number, -|} - -export type RectEvent = (MouseEvent, GenericRect) => mixed +export interface BoundingRect { + x: number + y: number + width: number + height: number +} diff --git a/protocol-designer/src/components/App.tsx b/protocol-designer/src/components/App.tsx index ff3e8bd19b0..a72cd1162fd 100644 --- a/protocol-designer/src/components/App.tsx +++ b/protocol-designer/src/components/App.tsx @@ -1,10 +1,9 @@ -// @flow import * as React from 'react' import { ProtocolEditor } from './ProtocolEditor' import '../css/reset.css' -export function App(): React.Node { +export function App(): JSX.Element { return (
diff --git a/protocol-designer/src/components/BatchEditForm/BatchEditMix.tsx b/protocol-designer/src/components/BatchEditForm/BatchEditMix.tsx index c27620a6fd4..7b8ad1efe08 100644 --- a/protocol-designer/src/components/BatchEditForm/BatchEditMix.tsx +++ b/protocol-designer/src/components/BatchEditForm/BatchEditMix.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { Box, @@ -23,20 +22,20 @@ import { getLabwareFieldForPositioningField, } from '../StepEditForm/utils' import { FormColumn } from './FormColumn' -import type { FieldPropsByName } from '../StepEditForm/types' -import type { WellOrderOption } from '../../form-types' +import { FieldPropsByName } from '../StepEditForm/types' +import { WellOrderOption } from '../../form-types' // TODO(IL, 2021-03-01): refactor these fragmented style rules (see #7402) import formStyles from '../forms/forms.css' import styles from '../StepEditForm/StepEditForm.css' import buttonStyles from '../StepEditForm/ButtonRow/styles.css' -type BatchEditMixProps = {| - batchEditFormHasChanges: boolean, - propsForFields: FieldPropsByName, - handleCancel: () => mixed, - handleSave: () => mixed, -|} -export const BatchEditMix = (props: BatchEditMixProps): React.Node => { +interface BatchEditMixProps { + batchEditFormHasChanges: boolean + propsForFields: FieldPropsByName + handleCancel: () => unknown + handleSave: () => unknown +} +export const BatchEditMix = (props: BatchEditMixProps): JSX.Element => { const { propsForFields, handleCancel, handleSave } = props const [cancelButtonTargetProps, cancelButtonTooltipProps] = useHoverTooltip({ placement: TOOLTIP_TOP, @@ -59,7 +58,9 @@ export const BatchEditMix = (props: BatchEditMixProps): React.Node => { return pipetteId ? String(pipetteId) : null } - const getWellOrderFieldValue = (name: string): ?WellOrderOption => { + const getWellOrderFieldValue = ( + name: string + ): WellOrderOption | null | undefined => { const val = propsForFields[name]?.value if (val === 'l2r' || val === 'r2l' || val === 't2b' || val === 'b2t') { return val diff --git a/protocol-designer/src/components/BatchEditForm/BatchEditMoveLiquid.tsx b/protocol-designer/src/components/BatchEditForm/BatchEditMoveLiquid.tsx index e897fed0301..ea2e9b322f6 100644 --- a/protocol-designer/src/components/BatchEditForm/BatchEditMoveLiquid.tsx +++ b/protocol-designer/src/components/BatchEditForm/BatchEditMoveLiquid.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { Box, @@ -24,19 +23,19 @@ import { getLabwareFieldForPositioningField, } from '../StepEditForm/utils' import { FormColumn } from './FormColumn' -import type { FieldPropsByName } from '../StepEditForm/types' -import type { WellOrderOption } from '../../form-types' +import { FieldPropsByName } from '../StepEditForm/types' +import { WellOrderOption } from '../../form-types' // TODO(IL, 2021-03-01): refactor these fragmented style rules (see #7402) import formStyles from '../forms/forms.css' import styles from '../StepEditForm/StepEditForm.css' import buttonStyles from '../StepEditForm/ButtonRow/styles.css' -const SourceDestBatchEditMoveLiquidFields = (props: {| - prefix: 'aspirate' | 'dispense', - propsForFields: FieldPropsByName, -|}): React.Node => { +const SourceDestBatchEditMoveLiquidFields = (props: { + prefix: 'aspirate' | 'dispense' + propsForFields: FieldPropsByName +}): JSX.Element => { const { prefix, propsForFields } = props - const addFieldNamePrefix = name => `${prefix}_${name}` + const addFieldNamePrefix = (name: string): string => `${prefix}_${name}` const getLabwareIdForPositioningField = (name: string): string | null => { const labwareField = getLabwareFieldForPositioningField(name) @@ -49,7 +48,9 @@ const SourceDestBatchEditMoveLiquidFields = (props: {| return pipetteId ? String(pipetteId) : null } - const getWellOrderFieldValue = (name: string): ?WellOrderOption => { + const getWellOrderFieldValue = ( + name: string + ): WellOrderOption | null | undefined => { const val = propsForFields[name]?.value if (val === 'l2r' || val === 'r2l' || val === 't2b' || val === 'b2t') { return val @@ -139,7 +140,7 @@ const SourceDestBatchEditMoveLiquidFields = (props: {| {...propsForFields['blowout_location']} className={styles.full_width} options={getBlowoutLocationOptionsForForm({ - path: (propsForFields['path'].value: any), + path: propsForFields['path'].value as any, stepType: 'moveLiquid', })} /> @@ -149,15 +150,15 @@ const SourceDestBatchEditMoveLiquidFields = (props: {| ) } -type BatchEditMoveLiquidProps = {| - batchEditFormHasChanges: boolean, - propsForFields: FieldPropsByName, - handleCancel: () => mixed, - handleSave: () => mixed, -|} +export interface BatchEditMoveLiquidProps { + batchEditFormHasChanges: boolean + propsForFields: FieldPropsByName + handleCancel: () => unknown + handleSave: () => unknown +} export const BatchEditMoveLiquid = ( props: BatchEditMoveLiquidProps -): React.Node => { +): JSX.Element => { const { propsForFields, handleCancel, handleSave } = props const [cancelButtonTargetProps, cancelButtonTooltipProps] = useHoverTooltip({ placement: TOOLTIP_TOP, diff --git a/protocol-designer/src/components/BatchEditForm/FormColumn.tsx b/protocol-designer/src/components/BatchEditForm/FormColumn.tsx index bfab2493d72..e32571d5ab0 100644 --- a/protocol-designer/src/components/BatchEditForm/FormColumn.tsx +++ b/protocol-designer/src/components/BatchEditForm/FormColumn.tsx @@ -1,14 +1,13 @@ -// @flow import * as React from 'react' import { Box } from '@opentrons/components' // TODO(IL, 2021-03-01): refactor these fragmented style rules (see #7402) import styles from '../StepEditForm/StepEditForm.css' -export type FormColumnProps = {| - children?: React.Node, - sectionHeader?: React.Node, -|} -export const FormColumn = (props: FormColumnProps): React.Node => { +export interface FormColumnProps { + children?: React.ReactNode + sectionHeader?: React.ReactNode +} +export const FormColumn = (props: FormColumnProps): JSX.Element => { return ( diff --git a/protocol-designer/src/components/BatchEditForm/NoBatchEditSharedSettings.tsx b/protocol-designer/src/components/BatchEditForm/NoBatchEditSharedSettings.tsx index f8507acf5df..03cd9464f2c 100644 --- a/protocol-designer/src/components/BatchEditForm/NoBatchEditSharedSettings.tsx +++ b/protocol-designer/src/components/BatchEditForm/NoBatchEditSharedSettings.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { Flex, @@ -10,7 +9,7 @@ import { } from '@opentrons/components' import { i18n } from '../../localization' -export const NoBatchEditSharedSettings = (): React.Node => { +export const NoBatchEditSharedSettings = (): JSX.Element => { return ( { const handleCancel = jest.fn() const handleSave = jest.fn() - let props + let props: BatchEditMoveLiquidProps beforeEach(() => { // just return the i18n text path itself, instead of the text context at that path + // @ts-expect-error (ce, 2021-06-21) return type issue localizationSpy.mockImplementation(path => path) props = { @@ -43,7 +46,8 @@ describe('BatchEditMoveLiquid', () => { expect(localizationSpy).toHaveBeenCalledWith(tooltipPath) expect(saveButtonTooltip.prop('children')).toBe(tooltipPath) - saveButton.invoke('onClick')() + // @ts-expect-error (ce, 2021-06-21) lacks constraining and param type is incorrect + saveButton.invoke('onClick')({}) expect(handleSave).toHaveBeenCalled() }) @@ -95,7 +99,8 @@ describe('BatchEditMoveLiquid', () => { expect(handleCancel).not.toHaveBeenCalled() - cancelButton.invoke('onClick')() + // @ts-expect-error (ce, 2021-06-21) lacks constraining and param type is incorrect + cancelButton.invoke('onClick')({}) expect(handleCancel).toHaveBeenCalled() }) }) diff --git a/protocol-designer/src/components/BatchEditForm/__tests__/makeBatchEditFieldProps.test.ts b/protocol-designer/src/components/BatchEditForm/__tests__/makeBatchEditFieldProps.test.ts index 9b68d93c147..a3fa436e663 100644 --- a/protocol-designer/src/components/BatchEditForm/__tests__/makeBatchEditFieldProps.test.ts +++ b/protocol-designer/src/components/BatchEditForm/__tests__/makeBatchEditFieldProps.test.ts @@ -1,4 +1,3 @@ -// @flow import noop from 'lodash/noop' import { makeBatchEditFieldProps } from '../makeBatchEditFieldProps' import * as stepEditFormUtils from '../../StepEditForm/utils' diff --git a/protocol-designer/src/components/BatchEditForm/index.tsx b/protocol-designer/src/components/BatchEditForm/index.tsx index 6ba4caf57e3..23d2e8892df 100644 --- a/protocol-designer/src/components/BatchEditForm/index.tsx +++ b/protocol-designer/src/components/BatchEditForm/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useDispatch, useSelector } from 'react-redux' import { makeBatchEditFieldProps } from './makeBatchEditFieldProps' @@ -15,13 +14,11 @@ import { resetBatchEditFieldChanges, saveStepFormsMulti, } from '../../step-forms/actions' -import { maskField } from '../../steplist/fieldLevel' +import { maskField, StepFieldName } from '../../steplist/fieldLevel' import { BatchEditMoveLiquid } from './BatchEditMoveLiquid' import { BatchEditMix } from './BatchEditMix' -export type BatchEditFormProps = {||} - -export const BatchEditForm = (props: BatchEditFormProps): React.Node => { +export const BatchEditForm = (): JSX.Element => { const dispatch = useDispatch() const fieldValues = useSelector(getMultiSelectFieldValues) const stepTypes = useSelector(getBatchEditSelectedStepTypes) @@ -29,16 +26,18 @@ export const BatchEditForm = (props: BatchEditFormProps): React.Node => { const selectedStepIds = useSelector(getMultiSelectItemIds) const batchEditFormHasChanges = useSelector(getBatchEditFormHasUnsavedChanges) - const handleChangeFormInput = (name, value) => { + const handleChangeFormInput = (name: StepFieldName, value: unknown): void => { const maskedValue = maskField(name, value) dispatch(changeBatchEditField({ [name]: maskedValue })) } - const handleSave = () => { + const handleSave = (): void => { dispatch(saveStepFormsMulti(selectedStepIds)) } - const handleCancel = () => dispatch(resetBatchEditFieldChanges()) + const handleCancel = (): void => { + dispatch(resetBatchEditFieldChanges()) + } const stepType = stepTypes.length === 1 ? stepTypes[0] : null diff --git a/protocol-designer/src/components/BatchEditForm/makeBatchEditFieldProps.ts b/protocol-designer/src/components/BatchEditForm/makeBatchEditFieldProps.ts index 9ba8e2b55dc..825c6ead185 100644 --- a/protocol-designer/src/components/BatchEditForm/makeBatchEditFieldProps.ts +++ b/protocol-designer/src/components/BatchEditForm/makeBatchEditFieldProps.ts @@ -1,6 +1,5 @@ -// @flow import noop from 'lodash/noop' -import type { +import { DisabledFields, MultiselectFieldValues, } from '../../ui/steps/selectors' @@ -8,24 +7,24 @@ import { getFieldDefaultTooltip, getFieldIndeterminateTooltip, } from '../StepEditForm/utils' -import type { FieldPropsByName } from '../StepEditForm/types' -import type { StepFieldName } from '../../form-types' - +import { FieldPropsByName } from '../StepEditForm/types' +import { StepFieldName } from '../../form-types' export const makeBatchEditFieldProps = ( fieldValues: MultiselectFieldValues, disabledFields: DisabledFields, - handleChangeFormInput: (name: string, value: mixed) => void + handleChangeFormInput: (name: string, value: unknown) => void ): FieldPropsByName => { - const fieldNames: Array = Object.keys(fieldValues) + const fieldNames: StepFieldName[] = Object.keys(fieldValues) return fieldNames.reduce((acc, name) => { const defaultTooltip = getFieldDefaultTooltip(name) const isIndeterminate = fieldValues[name].isIndeterminate const indeterminateTooltip = getFieldIndeterminateTooltip(name) - let tooltipContent = defaultTooltip // Default to the default content (or blank) + if (isIndeterminate && indeterminateTooltip) { tooltipContent = indeterminateTooltip } + if (name in disabledFields) { tooltipContent = disabledFields[name] // Use disabled content if field is disabled, override indeterminate tooltip if applicable } diff --git a/protocol-designer/src/components/ComputingSpinner.tsx b/protocol-designer/src/components/ComputingSpinner.tsx index e12fc4c4a98..1702fe15683 100644 --- a/protocol-designer/src/components/ComputingSpinner.tsx +++ b/protocol-designer/src/components/ComputingSpinner.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useSelector } from 'react-redux' import { css } from 'styled-components' @@ -9,9 +8,9 @@ const waitCursorStyle = css` cursor: wait; ` -export const ComputingSpinner = (): React.Node => { +export const ComputingSpinner = (): JSX.Element => { const showSpinner = useSelector(fileDataSelectors.getTimelineIsBeingComputed) - + // @ts-expect-error(sa, 2021-6-22): return null so it can be properly rendered return ( showSpinner && ( mixed, - drilledDown: boolean, - initialDeckSetup: InitialDeckSetup, -|} +export interface DeckSetupProps { + selectedTerminalItemId?: TerminalItemId | null + handleClickOutside?: () => unknown + drilledDown: boolean + initialDeckSetup: InitialDeckSetup +} -type ContentsProps = {| - ...RobotWorkSpaceRenderProps, - selectedTerminalItemId: ?TerminalItemId, - initialDeckSetup: InitialDeckSetup, - showGen1MultichannelCollisionWarnings: boolean, -|} +type ContentsProps = RobotWorkSpaceRenderProps & { + selectedTerminalItemId?: TerminalItemId | null + initialDeckSetup: InitialDeckSetup + showGen1MultichannelCollisionWarnings: boolean +} export const VIEWBOX_MIN_X = -64 export const VIEWBOX_MIN_Y = -10 @@ -110,18 +110,20 @@ const getSlotDefForModuleSlot = ( const getModuleSlotDefs = ( initialDeckSetup: InitialDeckSetup, deckSlots: { [slotId: string]: DeckDefSlot } -): Array => { +): DeckDefSlot[] => { return values(initialDeckSetup.modules).map((moduleOnDeck: ModuleOnDeck) => getSlotDefForModuleSlot(moduleOnDeck, deckSlots) ) } -export const getSwapBlocked = (args: { - hoveredLabware: ?LabwareOnDeckType, - draggedLabware: ?LabwareOnDeckType, - modulesById: $PropertyType, - customLabwareDefs: LabwareDefByDefURI, -}): boolean => { +export interface SwapBlockedArgs { + hoveredLabware?: LabwareOnDeckType | null + draggedLabware?: LabwareOnDeckType | null + modulesById: InitialDeckSetup['modules'] + customLabwareDefs: LabwareDefByDefURI +} + +export const getSwapBlocked = (args: SwapBlockedArgs): boolean => { const { hoveredLabware, draggedLabware, @@ -132,9 +134,9 @@ export const getSwapBlocked = (args: { return false } - const sourceModuleType: ?ModuleRealType = + const sourceModuleType: ModuleRealType | null = modulesById[draggedLabware.slot]?.type || null - const destModuleType: ?ModuleRealType = + const destModuleType: ModuleRealType | null = modulesById[hoveredLabware.slot]?.type || null const draggedLabwareIsCustom = getLabwareIsCustom( @@ -161,7 +163,7 @@ export const getSwapBlocked = (args: { // TODO IL 2020-01-12: to support dynamic labware/module movement during a protocol, // don't use initialDeckSetup here. Use some version of timelineFrameForActiveItem -export const DeckSetupContents = (props: ContentsProps): React.Node => { +export const DeckSetupContents = (props: ContentsProps): JSX.Element => { const { initialDeckSetup, deckSlotsById, @@ -176,14 +178,12 @@ export const DeckSetupContents = (props: ContentsProps): React.Node => { // hovered over**. The intrinsic state of `react-dnd` is not designed to handle that. // So we need to use our own state here to determine // whether swapping will be blocked due to labware<>module compat: - const [ - hoveredLabware, - setHoveredLabware, - ] = React.useState(null) - const [ - draggedLabware, - setDraggedLabware, - ] = React.useState(null) + const [hoveredLabware, setHoveredLabware] = React.useState< + LabwareOnDeckType | null | undefined + >(null) + const [draggedLabware, setDraggedLabware] = React.useState< + LabwareOnDeckType | null | undefined + >(null) const customLabwareDefs = useSelector( labwareDefSelectors.getCustomLabwareDefsByURI @@ -203,31 +203,32 @@ export const DeckSetupContents = (props: ContentsProps): React.Node => { const slotsBlockedBySpanning = getSlotsBlockedBySpanning( props.initialDeckSetup ) - const deckSlots: Array = values(deckSlotsById) + const deckSlots: DeckDefSlot[] = values(deckSlotsById) const moduleSlots = getModuleSlotDefs(initialDeckSetup, deckSlotsById) // NOTE: in these arrays of slots, order affects SVG render layering // labware can be in a module or on the deck - const labwareParentSlots: Array = [...deckSlots, ...moduleSlots] + const labwareParentSlots: DeckDefSlot[] = [...deckSlots, ...moduleSlots] // modules can be on the deck, including pseudo-slots (eg special 'spanning' slot for thermocycler position) const moduleParentSlots = [...deckSlots, ...values(PSEUDO_DECK_SLOTS)] - const allLabware: Array = Object.keys( + const allLabware: LabwareOnDeckType[] = Object.keys( initialDeckSetup.labware - ).reduce((acc, labwareId) => { + ).reduce((acc, labwareId) => { const labware = initialDeckSetup.labware[labwareId] return getLabwareHasQuirk(labware.def, 'fixedTrash') ? acc : [...acc, labware] }, []) - const allModules: Array = values(initialDeckSetup.modules) + const allModules: ModuleOnDeck[] = values(initialDeckSetup.modules) // NOTE: naively hard-coded to show warning north of slots 1 or 3 when occupied by any module - const multichannelWarningSlots: Array = showGen1MultichannelCollisionWarnings + const multichannelWarningSlots: DeckDefSlot[] = showGen1MultichannelCollisionWarnings ? compact([ (allModules.some( moduleOnDeck => moduleOnDeck.slot === '1' && + // @ts-expect-error(sa, 2021-6-21): ModuleModel is a super type of the elements in MODULES_WITH_COLLISION_ISSUES MODULES_WITH_COLLISION_ISSUES.includes(moduleOnDeck.model) ) && deckSlotsById?.['4']) || @@ -235,6 +236,7 @@ export const DeckSetupContents = (props: ContentsProps): React.Node => { (allModules.some( moduleOnDeck => moduleOnDeck.slot === '3' && + // @ts-expect-error(sa, 2021-6-21): ModuleModel is a super type of the elements in MODULES_WITH_COLLISION_ISSUES MODULES_WITH_COLLISION_ISSUES.includes(moduleOnDeck.model) ) && deckSlotsById?.['6']) || @@ -300,6 +302,7 @@ export const DeckSetupContents = (props: ContentsProps): React.Node => { ) .map(slot => { return ( + // @ts-expect-error (ce, 2021-06-21) once we upgrade to the react-dnd hooks api, and use react-redux hooks, typing this will be easier { } const getHasGen1MultiChannelPipette = ( - pipettes: $PropertyType -) => { + pipettes: InitialDeckSetup['pipettes'] +): boolean => { const pipetteIds = Object.keys(pipettes) return pipetteIds.some(pipetteId => GEN_ONE_MULTI_PIPETTES.includes(pipettes[pipetteId]?.name) ) } -export const DeckSetup = (props: Props): React.Node => { +export const DeckSetup = (props: DeckSetupProps): JSX.Element => { const _disableCollisionWarnings = useSelector( featureFlagSelectors.getDisableModuleRestrictions ) @@ -369,7 +372,7 @@ export const DeckSetup = (props: Props): React.Node => { !_disableCollisionWarnings && _hasGen1MultichannelPipette const deckDef = React.useMemo(() => getDeckDefinitions()['ot2_standard'], []) - const wrapperRef = useOnClickOutside({ + const wrapperRef: React.RefObject = useOnClickOutside({ onClickOutside: props.handleClickOutside, }) @@ -404,7 +407,7 @@ export const DeckSetup = (props: Props): React.Node => { ) } -export const NullDeckState = (): React.Node => { +export const NullDeckState = (): JSX.Element => { const deckDef = React.useMemo(() => getDeckDefinitions()['ot2_standard'], []) return ( diff --git a/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx b/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx index 16da2cf51aa..e9c8eae01ac 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOnDeck.tsx @@ -1,32 +1,31 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' -import { LabwareRender, type WellGroup } from '@opentrons/components' +import { LabwareRender, WellGroup } from '@opentrons/components' import * as wellContentsSelectors from '../../top-selectors/well-contents' import * as highlightSelectors from '../../top-selectors/substep-highlight' import * as tipContentsSelectors from '../../top-selectors/tip-contents' -import { type LabwareOnDeck as LabwareOnDeckType } from '../../step-forms' -import type { ContentsByWell } from '../../labware-ingred/types' -import type { BaseState } from '../../types' +import { LabwareOnDeck as LabwareOnDeckType } from '../../step-forms' +import { ContentsByWell } from '../../labware-ingred/types' +import { BaseState } from '../../types' import { wellFillFromWellContents } from '../labware/utils' -type OP = {| - className?: string, - labwareOnDeck: LabwareOnDeckType, - x: number, - y: number, -|} +interface OP { + className?: string + labwareOnDeck: LabwareOnDeckType + x: number + y: number +} -type SP = {| - wellContents: ContentsByWell, - missingTips: ?WellGroup, - highlightedWells: ?WellGroup, -|} +interface SP { + wellContents: ContentsByWell + missingTips?: WellGroup | null + highlightedWells?: WellGroup | null +} -type Props = { ...OP, ...SP } +type Props = OP & SP -const LabwareOnDeckComponent = (props: Props) => ( +const LabwareOnDeckComponent = (props: Props): JSX.Element => ( { } } -export const LabwareOnDeck: React.AbstractComponent = connect< - Props, - OP, - SP, - {||}, - _, - _ ->(mapStateToProps)(LabwareOnDeckComponent) +export const LabwareOnDeck = connect(mapStateToProps)(LabwareOnDeckComponent) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/BlockedSlot.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/BlockedSlot.tsx index 0d95e8fe5b7..798fb47fa57 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/BlockedSlot.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/BlockedSlot.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { RobotCoordsForeignDiv } from '@opentrons/components' import { i18n } from '../../../localization' @@ -8,15 +7,15 @@ type BlockedSlotMessage = | 'MODULE_INCOMPATIBLE_SINGLE_LABWARE' | 'MODULE_INCOMPATIBLE_LABWARE_SWAP' -type Props = {| - x: number, - y: number, - width: number, - height: number, - message: BlockedSlotMessage, -|} +interface Props { + x: number + y: number + width: number + height: number + message: BlockedSlotMessage +} -export const BlockedSlot = (props: Props): React.Node => { +export const BlockedSlot = (props: Props): JSX.Element => { const { x, y, width, height, message } = props return ( diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/BrowseLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/BrowseLabware.tsx index 9925ed5cce0..907f63a2a85 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/BrowseLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/BrowseLabware.tsx @@ -1,26 +1,25 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { connect } from 'react-redux' import { Icon } from '@opentrons/components' import { i18n } from '../../../localization' -import type { ThunkDispatch } from '../../../types' -import type { LabwareOnDeck } from '../../../step-forms' +import { ThunkDispatch } from '../../../types' +import { LabwareOnDeck } from '../../../step-forms' import { drillDownOnLabware } from '../../../labware-ingred/actions' import { resetScrollElements } from '../../../ui/steps/utils' import styles from './LabwareOverlays.css' -type OP = {| - labwareOnDeck: LabwareOnDeck, -|} +interface OP { + labwareOnDeck: LabwareOnDeck +} -type DP = {| - drillDown: () => mixed, -|} +interface DP { + drillDown: () => unknown +} -type Props = {| ...OP, ...DP |} +type Props = OP & DP -function BrowseLabwareOverlay(props: Props) { +function BrowseLabwareOverlay(props: Props): JSX.Element | null { if (props.labwareOnDeck.def.parameters.isTiprack) return null return (
@@ -32,21 +31,17 @@ function BrowseLabwareOverlay(props: Props) { ) } -const mapDispatchToProps = (dispatch: ThunkDispatch<*>, ownProps: OP): DP => ({ +const mapDispatchToProps = ( + dispatch: ThunkDispatch, + ownProps: OP +): DP => ({ drillDown: () => { resetScrollElements() dispatch(drillDownOnLabware(ownProps.labwareOnDeck.id)) }, }) -export const BrowseLabware: React.AbstractComponent = connect< - Props, - OP, - _, - DP, - _, - _ ->( +export const BrowseLabware = connect( null, mapDispatchToProps )(BrowseLabwareOverlay) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx index e8865a57367..700b683c946 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/DragPreview.tsx @@ -1,24 +1,20 @@ -// @flow import * as React from 'react' import { DragLayer } from 'react-dnd' import { LabwareOnDeck } from '../LabwareOnDeck' import { DND_TYPES } from '../../../constants' -import type { LabwareOnDeck as LabwareOnDeckType } from '../../../step-forms' -import type { RobotWorkSpaceRenderProps } from '@opentrons/components' +import { LabwareOnDeck as LabwareOnDeckType } from '../../../step-forms' +import { RobotWorkSpaceRenderProps } from '@opentrons/components' import styles from './DragPreview.css' -type DragPreviewProps = { - isDragging: boolean, - currentOffset?: { x: number, y: number }, - item: { labwareOnDeck: LabwareOnDeckType }, - itemType: string, - getRobotCoordsFromDOMCoords: $PropertyType< - RobotWorkSpaceRenderProps, - 'getRobotCoordsFromDOMCoords' - >, +interface DragPreviewProps { + isDragging: boolean + currentOffset?: { x: number; y: number } + item: { labwareOnDeck: LabwareOnDeckType } + itemType: string + getRobotCoordsFromDOMCoords: RobotWorkSpaceRenderProps['getRobotCoordsFromDOMCoords'] } -const LabwareDragPreview = (props: DragPreviewProps) => { +const LabwareDragPreview = (props: DragPreviewProps): JSX.Element | null => { const { item, itemType, @@ -42,12 +38,9 @@ const LabwareDragPreview = (props: DragPreviewProps) => { ) } -export const DragPreview: React.AbstractComponent< - $Diff< - DragPreviewProps, - {| currentOffset: mixed, isDragging: mixed, itemType: mixed, item: mixed |} - > -> = DragLayer(monitor => ({ +export const DragPreview = DragLayer< + Omit +>(monitor => ({ currentOffset: monitor.getSourceClientOffset(), isDragging: monitor.isDragging(), itemType: monitor.getItemType(), diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx index b7a3b2a5067..7bdf7570efb 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/EditLabware.tsx @@ -1,50 +1,57 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import cx from 'classnames' import { Icon } from '@opentrons/components' import { getLabwareDisplayName } from '@opentrons/shared-data' -import { DragSource, DropTarget } from 'react-dnd' +import { + DragSource, + DragSourceConnector, + DragSourceMonitor, + DropTarget, + DropTargetConnector, + DropTargetMonitor, + DropTargetSpec, +} from 'react-dnd' import { i18n } from '../../../localization' import { NameThisLabware } from './NameThisLabware' import { DND_TYPES } from '../../../constants' import { - openIngredientSelector, deleteContainer, duplicateLabware, moveDeckItem, + openIngredientSelector, } from '../../../labware-ingred/actions' import { selectors as labwareIngredSelectors } from '../../../labware-ingred/selectors' -import type { BaseState, ThunkDispatch, DeckSlot } from '../../../types' -import type { LabwareOnDeck } from '../../../step-forms' +import { BaseState, DeckSlot, ThunkDispatch } from '../../../types' +import { LabwareOnDeck } from '../../../step-forms' import styles from './LabwareOverlays.css' -type OP = {| - labwareOnDeck: LabwareOnDeck, - setHoveredLabware: (?LabwareOnDeck) => mixed, - setDraggedLabware: (?LabwareOnDeck) => mixed, - swapBlocked: boolean, -|} -type SP = {| - isYetUnnamed: boolean, -|} -type DP = {| - editLiquids: () => mixed, - duplicateLabware: () => mixed, - deleteLabware: () => mixed, - moveDeckItem: (DeckSlot, DeckSlot) => mixed, -|} +interface OP { + labwareOnDeck: LabwareOnDeck + setHoveredLabware: (val?: LabwareOnDeck | null) => unknown + setDraggedLabware: (val?: LabwareOnDeck | null) => unknown + swapBlocked: boolean +} +interface SP { + isYetUnnamed: boolean +} +interface DP { + editLiquids: () => unknown + duplicateLabware: () => unknown + deleteLabware: () => unknown + moveDeckItem: (item1: DeckSlot, item2: DeckSlot) => unknown +} -type DNDP = {| - draggedLabware: ?LabwareOnDeck, - isOver: boolean, - connectDragSource: React.Node => React.Node, - connectDropTarget: React.Node => React.Node, -|} +interface DNDP { + draggedLabware?: LabwareOnDeck | null + isOver: boolean + connectDragSource: (val: JSX.Element) => JSX.Element + connectDropTarget: (val: JSX.Element) => JSX.Element +} -type Props = {| ...OP, ...SP, ...DP, ...DNDP |} +type Props = OP & SP & DP & DNDP -const EditLabwareComponent = (props: Props) => { +const EditLabwareComponent = (props: Props): JSX.Element => { const { labwareOnDeck, isYetUnnamed, @@ -69,7 +76,7 @@ const EditLabwareComponent = (props: Props) => { } else { const isBeingDragged = draggedLabware?.slot === labwareOnDeck.slot - let contents: React.Node = null + let contents: React.ReactNode | null = null if (swapBlocked) { contents = null @@ -127,17 +134,20 @@ const EditLabwareComponent = (props: Props) => { } const labwareSource = { - beginDrag: (props, monitor, component) => { + beginDrag: (props: Props, monitor: DragSourceMonitor, component: any) => { const { labwareOnDeck } = props props.setDraggedLabware(labwareOnDeck) return { labwareOnDeck } }, - endDrag: (props, monitor, component) => { + endDrag: (props: Props, monitor: DragSourceMonitor, component: any) => { props.setHoveredLabware(null) props.setDraggedLabware(null) }, } -const collectLabwareSource = (connect, monitor) => ({ +const collectLabwareSource = ( + connect: DragSourceConnector, + monitor: DragSourceMonitor +): React.ReactNode => ({ connectDragSource: connect.dragSource(), isDragging: monitor.isDragging(), draggedItem: monitor.getItem(), @@ -149,19 +159,19 @@ const DragEditLabware = DragSource( )(EditLabwareComponent) const labwareDropTarget = { - canDrop: (props: { ...OP, ...SP, ...DP }, monitor) => { + canDrop: (props: Props, monitor: DropTargetMonitor) => { const draggedItem = monitor.getItem() const draggedLabware = draggedItem?.labwareOnDeck const isDifferentSlot = draggedLabware && draggedLabware.slot !== props.labwareOnDeck.slot return isDifferentSlot && !props.swapBlocked }, - hover: (props, monitor, component) => { + hover: (props: Props, monitor: DropTargetSpec, component: any) => { if (monitor.canDrop) { props.setHoveredLabware(component.props.labwareOnDeck) } }, - drop: (props, monitor) => { + drop: (props: Props, monitor: DropTargetMonitor) => { const draggedItem = monitor.getItem() if (draggedItem) { props.moveDeckItem( @@ -171,7 +181,10 @@ const labwareDropTarget = { } }, } -const collectLabwareDropTarget = (connect, monitor) => ({ +const collectLabwareDropTarget = ( + connect: DropTargetConnector, + monitor: DropTargetMonitor +): React.ReactNode => ({ connectDropTarget: connect.dropTarget(), isOver: monitor.isOver(), draggedLabware: monitor.getItem()?.labwareOnDeck || null, @@ -190,7 +203,10 @@ const mapStateToProps = (state: BaseState, ownProps: OP): SP => { } } -const mapDispatchToProps = (dispatch: ThunkDispatch<*>, ownProps: OP): DP => ({ +const mapDispatchToProps = ( + dispatch: ThunkDispatch, + ownProps: OP +): DP => ({ editLiquids: () => dispatch(openIngredientSelector(ownProps.labwareOnDeck.id)), duplicateLabware: () => dispatch(duplicateLabware(ownProps.labwareOnDeck.id)), @@ -205,14 +221,7 @@ const mapDispatchToProps = (dispatch: ThunkDispatch<*>, ownProps: OP): DP => ({ dispatch(moveDeckItem(sourceSlot, destSlot)), }) -export const EditLabware: React.AbstractComponent = connect< - {| ...OP, ...SP, ...DP |}, - OP, - SP, - DP, - BaseState, - ThunkDispatch<*> ->( +export const EditLabware = connect( mapStateToProps, mapDispatchToProps )(DragDropEditLabware) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx index 39fb988c0fd..e9d71aba1dd 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareControls.tsx @@ -1,11 +1,10 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { RobotCoordsForeignDiv } from '@opentrons/components' -import type { DeckSlot } from '@opentrons/shared-data' +import { DeckSlot } from '@opentrons/shared-data' -import { START_TERMINAL_ITEM_ID, type TerminalItemId } from '../../../steplist' -import type { LabwareOnDeck } from '../../../step-forms' +import { START_TERMINAL_ITEM_ID, TerminalItemId } from '../../../steplist' +import { LabwareOnDeck } from '../../../step-forms' import { BlockedSlot } from './BlockedSlot' import { BrowseLabware } from './BrowseLabware' import { EditLabware } from './EditLabware' @@ -13,16 +12,16 @@ import { LabwareName } from './LabwareName' import { LabwareHighlight } from './LabwareHighlight' import styles from './LabwareOverlays.css' -type LabwareControlsProps = {| - labwareOnDeck: LabwareOnDeck, - selectedTerminalItemId: ?TerminalItemId, - slot: DeckSlot, - setHoveredLabware: (?LabwareOnDeck) => mixed, - setDraggedLabware: (?LabwareOnDeck) => mixed, - swapBlocked: boolean, -|} +interface LabwareControlsProps { + labwareOnDeck: LabwareOnDeck + selectedTerminalItemId?: TerminalItemId | null + slot: DeckSlot + setHoveredLabware: (labware?: LabwareOnDeck | null) => unknown + setDraggedLabware: (labware?: LabwareOnDeck | null) => unknown + swapBlocked: boolean +} -export const LabwareControls = (props: LabwareControlsProps): React.Node => { +export const LabwareControls = (props: LabwareControlsProps): JSX.Element => { const { labwareOnDeck, slot, @@ -47,6 +46,7 @@ export const LabwareControls = (props: LabwareControlsProps): React.Node => { > {canEdit ? ( + // @ts-expect-error(sa, 2021-6-21): react dnd type mismatch { +export const LabwareHighlight = ( + props: LabwareHighlightProps +): JSX.Element | null => { const { labwareOnDeck } = props const highlighted = useSelector(getHoveredStepLabware).includes( labwareOnDeck.id diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareName.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareName.tsx index 258a3191239..65b5aeb64c1 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareName.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/LabwareName.tsx @@ -1,22 +1,21 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { LabwareNameOverlay } from '@opentrons/components' import { getLabwareDisplayName } from '@opentrons/shared-data' -import type { BaseState, ThunkDispatch } from '../../../types' +import { BaseState } from '../../../types' import { selectors as uiLabwareSelectors } from '../../../ui/labware' -import type { LabwareOnDeck } from '../../../step-forms' -type OP = {| - labwareOnDeck: LabwareOnDeck, -|} +import { LabwareOnDeck } from '../../../step-forms' +interface OP { + labwareOnDeck: LabwareOnDeck +} -type SP = {| - nickname: ?string, -|} +interface SP { + nickname?: string | null +} -type Props = { ...OP, ...SP } +type Props = OP & SP -const NameOverlay = (props: Props) => { +const NameOverlay = (props: Props): JSX.Element => { const { labwareOnDeck, nickname } = props const title = nickname || getLabwareDisplayName(labwareOnDeck.def) // TODO(mc, 2019-06-27): µL to uL replacement needed to handle CSS capitalization @@ -30,11 +29,4 @@ const mapStateToProps = (state: BaseState, ownProps: OP): SP => { } } -export const LabwareName: React.AbstractComponent = connect< - Props, - OP, - SP, - _, - BaseState, - ThunkDispatch<*> ->(mapStateToProps)(NameOverlay) +export const LabwareName = connect(mapStateToProps)(NameOverlay) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/NameThisLabware.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/NameThisLabware.tsx index fbf35706352..4a1be9cf21f 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/NameThisLabware.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/NameThisLabware.tsx @@ -1,45 +1,45 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import cx from 'classnames' import { Icon, useOnClickOutside } from '@opentrons/components' import { renameLabware } from '../../../labware-ingred/actions' -import type { BaseState, ThunkDispatch } from '../../../types' +import { ThunkDispatch } from '../../../types' import { i18n } from '../../../localization' -import type { LabwareOnDeck } from '../../../step-forms' +import { LabwareOnDeck } from '../../../step-forms' import styles from './LabwareOverlays.css' -type OP = {| - labwareOnDeck: LabwareOnDeck, - editLiquids: () => mixed, -|} +interface OP { + labwareOnDeck: LabwareOnDeck + editLiquids: () => unknown +} -type DP = {| +interface DP { // TODO Ian 2018-02-16 type these fns elsewhere and import the type - setLabwareName: (name: ?string) => mixed, -|} + setLabwareName: (name: string | null | undefined) => unknown +} -type Props = { ...OP, ...DP } +type Props = OP & DP -const NameThisLabwareComponent = (props: Props) => { +const NameThisLabwareComponent = (props: Props): JSX.Element => { const [inputValue, setInputValue] = React.useState('') - const saveNickname = () => { + const saveNickname = (): void => { props.setLabwareName(inputValue || null) } + const wrapperRef: React.RefObject = useOnClickOutside({ + onClickOutside: saveNickname, + }) - const wrapperRef = useOnClickOutside({ onClickOutside: saveNickname }) - - const handleChange = (e: SyntheticInputEvent) => { + const handleChange = (e: React.ChangeEvent): void => { setInputValue(e.target.value) } - const handleKeyUp = (e: SyntheticKeyboardEvent<*>) => { + const handleKeyUp = (e: React.KeyboardEvent): void => { if (e.key === 'Enter') { saveNickname() } } - const addLiquids = () => { + const addLiquids = (): void => { saveNickname() props.editLiquids() } @@ -67,22 +67,15 @@ const NameThisLabwareComponent = (props: Props) => { ) } -const mapDispatchToProps = (dispatch: ThunkDispatch<*>, ownProps: OP): DP => { +const mapDispatchToProps = (dispatch: ThunkDispatch, ownProps: OP): DP => { const { id } = ownProps.labwareOnDeck return { - setLabwareName: (name: ?string) => + setLabwareName: (name: string | null | undefined) => dispatch(renameLabware({ labwareId: id, name })), } } -export const NameThisLabware: React.AbstractComponent = connect< - Props, - OP, - _, - DP, - BaseState, - ThunkDispatch<*> ->( +export const NameThisLabware = connect( null, mapDispatchToProps )(NameThisLabwareComponent) diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx index b6a4fefc1d5..51eb482914a 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/SlotControls.tsx @@ -1,10 +1,9 @@ -// @flow import assert from 'assert' import * as React from 'react' import { Icon, RobotCoordsForeignDiv } from '@opentrons/components' +import { DropTarget, DropTargetConnector, DropTargetMonitor } from 'react-dnd' import cx from 'classnames' import { connect } from 'react-redux' -import { DropTarget } from 'react-dnd' import noop from 'lodash/noop' import { i18n } from '../../../localization' import { DND_TYPES } from '../../../constants' @@ -14,43 +13,51 @@ import { } from '../../../utils/labwareModuleCompatibility' import { BlockedSlot } from './BlockedSlot' import { - openAddLabwareModal, moveDeckItem, + openAddLabwareModal, } from '../../../labware-ingred/actions' -import { selectors as labwareDefSelectors } from '../../../labware-defs' -import { START_TERMINAL_ITEM_ID, type TerminalItemId } from '../../../steplist' +import { + LabwareDefByDefURI, + selectors as labwareDefSelectors, +} from '../../../labware-defs' +import { START_TERMINAL_ITEM_ID, TerminalItemId } from '../../../steplist' -import type { DeckSlot, ThunkDispatch, BaseState } from '../../../types' -import type { LabwareDefByDefURI } from '../../../labware-defs' -import type { LabwareOnDeck } from '../../../step-forms' -import type { +import { BaseState, DeckSlot, ThunkDispatch } from '../../../types' +import { LabwareOnDeck } from '../../../step-forms' +import { DeckSlot as DeckSlotDefinition, ModuleRealType, } from '@opentrons/shared-data' import styles from './LabwareOverlays.css' -type DNDP = {| - isOver: boolean, - connectDropTarget: React.Node => React.Node, - draggedItem: ?{ labwareOnDeck: LabwareOnDeck }, - itemType: string, -|} -type OP = {| - slot: {| ...DeckSlotDefinition, id: DeckSlot |}, // NOTE: Ian 2019-10-22 make slot `id` more restrictive when used in PD - moduleType: ModuleRealType | null, - selectedTerminalItemId: ?TerminalItemId, - handleDragHover?: () => mixed, -|} -type DP = {| - addLabware: (e: SyntheticEvent<*>) => mixed, - moveDeckItem: (DeckSlot, DeckSlot) => mixed, -|} -type SP = {| - customLabwareDefs: LabwareDefByDefURI, -|} -type Props = {| ...OP, ...DP, ...DNDP, ...SP |} - -export const SlotControlsComponent = (props: Props): React.Node => { +interface DNDP { + isOver: boolean + connectDropTarget: (val: React.ReactNode) => JSX.Element + draggedItem: { labwareOnDeck: LabwareOnDeck } | null + itemType: string +} + +interface OP { + slot: DeckSlotDefinition & { id: DeckSlot } // NOTE: Ian 2019-10-22 make slot `id` more restrictive when used in PD + moduleType: ModuleRealType | null + selectedTerminalItemId?: TerminalItemId | null + handleDragHover?: () => unknown +} + +interface DP { + addLabware: (e: React.MouseEvent) => unknown + moveDeckItem: (item1: DeckSlot, item2: DeckSlot) => unknown +} + +interface SP { + customLabwareDefs: LabwareDefByDefURI +} + +export type SlotControlsProps = OP & DP & DNDP & SP + +export const SlotControlsComponent = ( + props: SlotControlsProps +): JSX.Element | null => { const { slot, addLabware, @@ -125,25 +132,28 @@ const mapStateToProps = (state: BaseState): SP => { } } -const mapDispatchToProps = (dispatch: ThunkDispatch<*>, ownProps: OP): DP => ({ +const mapDispatchToProps = ( + dispatch: ThunkDispatch, + ownProps: OP +): DP => ({ addLabware: () => dispatch(openAddLabwareModal({ slot: ownProps.slot.id })), moveDeckItem: (sourceSlot, destSlot) => dispatch(moveDeckItem(sourceSlot, destSlot)), }) const slotTarget = { - drop: (props, monitor) => { + drop: (props: SlotControlsProps, monitor: DropTargetMonitor) => { const draggedItem = monitor.getItem() if (draggedItem) { props.moveDeckItem(draggedItem.labwareOnDeck.slot, props.slot.id) } }, - hover: (props, monitor) => { + hover: (props: SlotControlsProps) => { if (props.handleDragHover) { props.handleDragHover() } }, - canDrop: (props, monitor) => { + canDrop: (props: SlotControlsProps, monitor: DropTargetMonitor) => { const draggedItem = monitor.getItem() const draggedDef = draggedItem?.labwareOnDeck?.def const moduleType = props.moduleType @@ -161,21 +171,17 @@ const slotTarget = { return true }, } -const collectSlotTarget = (connect, monitor) => ({ +const collectSlotTarget = ( + connect: DropTargetConnector, + monitor: DropTargetMonitor +): React.ReactNode => ({ connectDropTarget: connect.dropTarget(), isOver: monitor.isOver(), draggedItem: monitor.getItem(), itemType: monitor.getItemType(), }) -export const SlotControls: React.AbstractComponent = connect< - {| ...OP, ...DP, ...SP |}, - OP, - _, - DP, - _, - _ ->( +export const SlotControls = connect( mapStateToProps, mapDispatchToProps )( diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/__tests__/SlotControls.test.tsx b/protocol-designer/src/components/DeckSetup/LabwareOverlays/__tests__/SlotControls.test.tsx index bc84e27fadd..1ca1dd77a82 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/__tests__/SlotControls.test.tsx +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/__tests__/SlotControls.test.tsx @@ -1,19 +1,24 @@ -// @flow - import React from 'react' import { shallow } from 'enzyme' import fixture_96_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' -import { MAGNETIC_MODULE_TYPE } from '@opentrons/shared-data' +import { + DeckSlot, + LabwareDefinition2, + MAGNETIC_MODULE_TYPE, +} from '@opentrons/shared-data' import { DND_TYPES } from '../../../../constants' import * as labwareModuleCompatibility from '../../../../utils/labwareModuleCompatibility' import { START_TERMINAL_ITEM_ID } from '../../../../steplist' -import { SlotControlsComponent } from '../SlotControls' +import { SlotControlsComponent, SlotControlsProps } from '../SlotControls' import { BlockedSlot } from '../BlockedSlot' describe('SlotControlsComponent', () => { - let props, getLabwareIsCompatibleSpy + let props: SlotControlsProps + let getLabwareIsCompatibleSpy: jest.SpiedFunction< + typeof labwareModuleCompatibility.getLabwareIsCompatible + > beforeEach(() => { - const slot = { + const slot: DeckSlot = { id: 'deckSlot1', position: [1, 2, 3], boundingBox: { @@ -29,7 +34,7 @@ describe('SlotControlsComponent', () => { labwareDefURI: 'fixture/fixture_96_plate', id: 'plate123', slot: '3', - def: fixture_96_plate, + def: fixture_96_plate as LabwareDefinition2, } props = { @@ -86,7 +91,7 @@ describe('SlotControlsComponent', () => { it('displays place here when dragged labware is custom and hovered over another labware on module slot', () => { props.customLabwareDefs = { - 'fixture/fixture_96_plate': fixture_96_plate, + 'fixture/fixture_96_plate': fixture_96_plate as LabwareDefinition2, } const wrapper = shallow() diff --git a/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts b/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts index 7a850736591..59d432b7928 100644 --- a/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts +++ b/protocol-designer/src/components/DeckSetup/LabwareOverlays/index.ts @@ -1,4 +1,3 @@ -// @flow export { SlotControls } from './SlotControls' export { LabwareControls } from './LabwareControls' export { DragPreview } from './DragPreview' diff --git a/protocol-designer/src/components/DeckSetup/ModuleTag.tsx b/protocol-designer/src/components/DeckSetup/ModuleTag.tsx index 68c404d5195..f486b8093f3 100644 --- a/protocol-designer/src/components/DeckSetup/ModuleTag.tsx +++ b/protocol-designer/src/components/DeckSetup/ModuleTag.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useSelector } from 'react-redux' import cx from 'classnames' @@ -7,7 +6,7 @@ import { MAGNETIC_MODULE_TYPE, TEMPERATURE_MODULE_TYPE, THERMOCYCLER_MODULE_TYPE, - type ModuleRealType, + ModuleRealType, } from '@opentrons/shared-data' import { TEMPERATURE_AT_TARGET, @@ -16,25 +15,25 @@ import { } from '@opentrons/step-generation' import { i18n } from '../../localization' import { timelineFrameBeforeActiveItem } from '../../top-selectors/timelineFrames' -import { selectors as stepFormSelectors } from '../../step-forms' +import { + selectors as stepFormSelectors, + ModuleTemporalProperties, + TemperatureModuleState, +} from '../../step-forms' import { STD_SLOT_X_DIM, STD_SLOT_Y_DIM } from '../../constants' import * as uiSelectors from '../../ui/steps' import { getLabwareOnModule } from '../../ui/modules/utils' import { makeTemperatureText } from '../../utils' import { getModuleVizDims } from './getModuleVizDims' import styles from './ModuleTag.css' -import type { ModuleOrientation } from '../../types' -import type { - ModuleTemporalProperties, - TemperatureModuleState, -} from '../../step-forms' +import { ModuleOrientation } from '../../types' -type Props = {| - x: number, - y: number, - orientation: ModuleOrientation, - id: string, -|} +export interface ModuleTagProps { + x: number + y: number + orientation: ModuleOrientation + id: string +} // eyeballed width/height to match designs const STANDARD_TAG_HEIGHT = 50 @@ -66,9 +65,9 @@ function getTempStatus(temperatureModuleState: TemperatureModuleState): string { export const ModuleStatus = ({ moduleState, -}: {| - moduleState: $PropertyType, -|}): React.Node => { +}: { + moduleState: ModuleTemporalProperties['moduleState'] +}): JSX.Element | null => { switch (moduleState.type) { case MAGNETIC_MODULE_TYPE: return ( @@ -113,20 +112,23 @@ export const ModuleStatus = ({ default: console.warn( + // @ts-expect-error (ce, 2021-07-21) doesn't think `type` exists on type never (clever TS) `ModuleStatus doesn't support module type ${moduleState.type}` ) return null } } -const ModuleTagComponent = (props: Props) => { +const ModuleTagComponent = (props: ModuleTagProps): JSX.Element | null => { const timelineFrame = useSelector(timelineFrameBeforeActiveItem) const moduleEntity = useSelector(stepFormSelectors.getModuleEntities)[ props.id ] - const moduleState: ?$PropertyType = - timelineFrame?.robotState.modules[props.id]?.moduleState - const moduleType: ?ModuleRealType = moduleEntity?.type + const moduleState: + | ModuleTemporalProperties['moduleState'] + | null + | undefined = timelineFrame?.robotState.modules[props.id]?.moduleState + const moduleType: ModuleRealType | null | undefined = moduleEntity?.type const hoveredLabwares = useSelector(uiSelectors.getHoveredStepLabware) const initialDeck = useSelector(stepFormSelectors.getInitialDeckSetup) @@ -188,6 +190,4 @@ const ModuleTagComponent = (props: Props) => { ) } -export const ModuleTag: React.AbstractComponent = React.memo( - ModuleTagComponent -) +export const ModuleTag = React.memo(ModuleTagComponent) diff --git a/protocol-designer/src/components/DeckSetup/ModuleViz.tsx b/protocol-designer/src/components/DeckSetup/ModuleViz.tsx index 9261b39737e..eb451d7fb11 100644 --- a/protocol-designer/src/components/DeckSetup/ModuleViz.tsx +++ b/protocol-designer/src/components/DeckSetup/ModuleViz.tsx @@ -1,19 +1,18 @@ -// @flow import * as React from 'react' import { getModuleVizDims } from './getModuleVizDims' import styles from './ModuleViz.css' -import type { ModuleOnDeck } from '../../step-forms' -import type { ModuleOrientation } from '../../types' +import { ModuleOnDeck } from '../../step-forms' +import { ModuleOrientation } from '../../types' -type Props = {| - x: number, - y: number, - orientation: ModuleOrientation, - module: ModuleOnDeck, - slotName: string, -|} +interface Props { + x: number + y: number + orientation: ModuleOrientation + module: ModuleOnDeck + slotName: string +} -export const ModuleViz = (props: Props): React.Node => { +export const ModuleViz = (props: Props): JSX.Element => { const moduleType = props.module.type const { xOffset, diff --git a/protocol-designer/src/components/DeckSetup/SlotWarning.tsx b/protocol-designer/src/components/DeckSetup/SlotWarning.tsx index 5d82ca8ed74..6e00cc20936 100644 --- a/protocol-designer/src/components/DeckSetup/SlotWarning.tsx +++ b/protocol-designer/src/components/DeckSetup/SlotWarning.tsx @@ -1,22 +1,21 @@ -// @flow import * as React from 'react' import { i18n } from '../../localization' import { RobotCoordsForeignDiv } from '@opentrons/components' import styles from './SlotWarning.css' -import type { ModuleOrientation } from '../../types' +import { ModuleOrientation } from '../../types' -type Props = {| - x: number, - y: number, - xDimension: number, - yDimension: number, - orientation: ModuleOrientation, - warningType: 'gen1multichannel', // NOTE: Ian 2019-10-31 if we want more on-deck warnings, expand the type here -|} +interface Props { + x: number + y: number + xDimension: number + yDimension: number + orientation: ModuleOrientation + warningType: 'gen1multichannel' // NOTE: Ian 2019-10-31 if we want more on-deck warnings, expand the type here +} const OVERHANG = 60 -export const SlotWarning = (props: Props): React.Node => { +export const SlotWarning = (props: Props): JSX.Element => { const { x, y, xDimension, yDimension, orientation, warningType } = props const rectXOffset = orientation === 'left' ? -OVERHANG : 0 const textXOffset = orientation === 'left' ? -1 * OVERHANG : xDimension diff --git a/protocol-designer/src/components/DeckSetup/__tests__/DeckSetup.test.ts b/protocol-designer/src/components/DeckSetup/__tests__/DeckSetup.test.ts index 6f9428da48a..576d1cf83c7 100644 --- a/protocol-designer/src/components/DeckSetup/__tests__/DeckSetup.test.ts +++ b/protocol-designer/src/components/DeckSetup/__tests__/DeckSetup.test.ts @@ -1,5 +1,3 @@ -// @flow - import fixture_96_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' import fixture_24_tuberack from '@opentrons/shared-data/labware/fixtures/2/fixture_24_tuberack.json' import { @@ -7,10 +5,11 @@ import { TEMPERATURE_MODULE_TYPE, MAGNETIC_MODULE_V1, TEMPERATURE_MODULE_V1, + LabwareDefinition2, } from '@opentrons/shared-data' import { TEMPERATURE_AT_TARGET } from '@opentrons/step-generation' import * as labwareModuleCompatibility from '../../../utils/labwareModuleCompatibility' -import { getSwapBlocked } from '../DeckSetup' +import { getSwapBlocked, SwapBlockedArgs } from '../DeckSetup' describe('DeckSetup', () => { describe('getSwapBlocked', () => { @@ -18,13 +17,13 @@ describe('DeckSetup', () => { labwareDefURI: 'fixture/fixture_96_plate', id: 'plate123', slot: '3', - def: fixture_96_plate, + def: fixture_96_plate as LabwareDefinition2, } const tuberackInSlot4 = { labwareDefURI: 'fixture/fixtures_24_tuberack', id: 'tuberack098', slot: '4', - def: fixture_24_tuberack, + def: fixture_24_tuberack as LabwareDefinition2, } const magneticModule = { @@ -49,7 +48,9 @@ describe('DeckSetup', () => { slot: '7', } - let getLabwareIsCompatibleSpy + let getLabwareIsCompatibleSpy: jest.SpiedFunction< + typeof labwareModuleCompatibility.getLabwareIsCompatible + > beforeEach(() => { getLabwareIsCompatibleSpy = jest.spyOn( labwareModuleCompatibility, @@ -62,7 +63,7 @@ describe('DeckSetup', () => { }) it('is not blocked when there is no labware in slot', () => { - const args = { + const args: SwapBlockedArgs = { hoveredLabware: null, draggedLabware: plateInSlot3, modulesById: {}, @@ -75,7 +76,7 @@ describe('DeckSetup', () => { }) it('is not blocked when no dragged labware', () => { - const args = { + const args: SwapBlockedArgs = { hoveredLabware: plateInSlot3, draggedLabware: null, modulesById: {}, @@ -90,18 +91,18 @@ describe('DeckSetup', () => { it('is not blocked when dragged labware to swap on module is custom', () => { tuberackInSlot4.slot = 'magnet123' getLabwareIsCompatibleSpy.mockReturnValue(false) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { magnet123: magneticModule, }, customLabwareDefs: { - 'fixture/fixture_96_plate': fixture_96_plate, + 'fixture/fixture_96_plate': fixture_96_plate as LabwareDefinition2, }, } - const isBlocked = getSwapBlocked(args) + const isBlocked = getSwapBlocked(args as SwapBlockedArgs) expect(isBlocked).toEqual(false) }) @@ -109,7 +110,7 @@ describe('DeckSetup', () => { it('is blocked when dragged labware on module to swap on another module is not custom and not compatible', () => { tuberackInSlot4.slot = 'magnet123' getLabwareIsCompatibleSpy.mockReturnValue(false) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { @@ -126,7 +127,7 @@ describe('DeckSetup', () => { it('is blocked when target labware on module to swap is incompatible with dragged labware', () => { tuberackInSlot4.slot = 'magnet123' getLabwareIsCompatibleSpy.mockReturnValue(false) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { @@ -144,7 +145,7 @@ describe('DeckSetup', () => { tuberackInSlot4.slot = 'magnet123' plateInSlot3.slot = 'temperature098' getLabwareIsCompatibleSpy.mockReturnValue(true) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { @@ -163,7 +164,7 @@ describe('DeckSetup', () => { tuberackInSlot4.slot = 'magnet123' plateInSlot3.slot = 'temperature098' getLabwareIsCompatibleSpy.mockReturnValue(false) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { @@ -181,7 +182,7 @@ describe('DeckSetup', () => { it('is not blocked when swapping labware from module with compatible labware on deck slot', () => { plateInSlot3.slot = 'temperature098' getLabwareIsCompatibleSpy.mockReturnValue(true) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { @@ -196,7 +197,7 @@ describe('DeckSetup', () => { }) it('is not blocked when swapping labware from one deck slot with another labware on deck slot', () => { - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: {}, @@ -211,7 +212,7 @@ describe('DeckSetup', () => { it('is not blocked when swapping compatible labware on deck slot with labware on module', () => { tuberackInSlot4.slot = 'magnet123' getLabwareIsCompatibleSpy.mockReturnValue(true) - const args = { + const args: SwapBlockedArgs = { hoveredLabware: tuberackInSlot4, draggedLabware: plateInSlot3, modulesById: { diff --git a/protocol-designer/src/components/DeckSetup/__tests__/ModuleTag.test.tsx b/protocol-designer/src/components/DeckSetup/__tests__/ModuleTag.test.tsx index edf95e98075..e28cccdf9be 100644 --- a/protocol-designer/src/components/DeckSetup/__tests__/ModuleTag.test.tsx +++ b/protocol-designer/src/components/DeckSetup/__tests__/ModuleTag.test.tsx @@ -1,5 +1,5 @@ -// @flow import { + LabwareDefinition2, MAGNETIC_MODULE_TYPE, TEMPERATURE_MODULE_TYPE, TEMPERATURE_MODULE_V1, @@ -13,33 +13,31 @@ import { TEMPERATURE_AT_TARGET, TEMPERATURE_DEACTIVATED, } from '@opentrons/step-generation' -import { ModuleStatus, ModuleTag } from '../ModuleTag' +import { ModuleStatus, ModuleTag, ModuleTagProps } from '../ModuleTag' import * as timelineFramesSelectors from '../../../top-selectors/timelineFrames' import { selectors as stepFormSelectors } from '../../../step-forms' import * as uiSelectors from '../../../ui/steps' -import type { CommandsAndRobotState } from '@opentrons/step-generation' -import type { BaseState } from '../../../types' -import type { ModuleEntities, InitialDeckSetup } from '../../../step-forms' - jest.mock('../../../ui/steps') jest.mock('../../../top-selectors/timelineFrames') jest.mock('../../../step-forms') -const timelineFrameBeforeActiveItemMock: JestMockFn< - [BaseState], - CommandsAndRobotState | null -> = timelineFramesSelectors.timelineFrameBeforeActiveItem +const timelineFrameBeforeActiveItemMock = timelineFramesSelectors.timelineFrameBeforeActiveItem as jest.MockedFunction< + typeof timelineFramesSelectors.timelineFrameBeforeActiveItem +> -const getModuleEntitiesMock: JestMockFn<[BaseState], ModuleEntities> = - stepFormSelectors.getModuleEntities +const getModuleEntitiesMock = stepFormSelectors.getModuleEntities as jest.MockedFunction< + typeof stepFormSelectors.getModuleEntities +> -const getHoveredStepLabwareMock: JestMockFn<[BaseState], Array> = - uiSelectors.getHoveredStepLabware +const getHoveredStepLabwareMock = uiSelectors.getHoveredStepLabware as jest.MockedFunction< + typeof uiSelectors.getHoveredStepLabware +> -const getInitialDeckSetup: JestMockFn<[BaseState], InitialDeckSetup> = - stepFormSelectors.getInitialDeckSetup +const getInitialDeckSetup = stepFormSelectors.getInitialDeckSetup as jest.MockedFunction< + typeof stepFormSelectors.getInitialDeckSetup +> describe('ModuleTag', () => { describe('ModuleStatus', () => { @@ -108,7 +106,8 @@ describe('ModuleTag', () => { describe('ModuleTagComponent', () => { const moduleId = 'abcdef' - let store, props + let store: any + let props: ModuleTagProps beforeEach(() => { props = { x: 1, @@ -162,7 +161,7 @@ describe('ModuleTag', () => { id: 'labwareId', slot: '3', labwareDefURI: 'url', - def: fixture_tiprack_10_ul, + def: fixture_tiprack_10_ul as LabwareDefinition2, }, }, pipettes: {}, @@ -188,7 +187,7 @@ describe('ModuleTag', () => { id: 'labwareId', slot: moduleId, labwareDefURI: 'url', - def: fixture_tiprack_10_ul, + def: fixture_tiprack_10_ul as LabwareDefinition2, }, }, pipettes: {}, @@ -198,7 +197,8 @@ describe('ModuleTag', () => { const wrapper = render() expect( - wrapper.find('RobotCoordsForeignDiv').prop('innerDivProps').className + wrapper.find('RobotCoordsForeignDiv').prop('innerDivProps') + .className ).toContain('highlighted_border_right_none') }) @@ -206,7 +206,8 @@ describe('ModuleTag', () => { const wrapper = render() expect( - wrapper.find('RobotCoordsForeignDiv').prop('innerDivProps').className + wrapper.find('RobotCoordsForeignDiv').prop('innerDivProps') + .className ).not.toContain('highlighted_border_right_none') }) @@ -216,7 +217,8 @@ describe('ModuleTag', () => { const wrapper = render() expect( - wrapper.find('RobotCoordsForeignDiv').prop('innerDivProps').className + wrapper.find('RobotCoordsForeignDiv').prop('innerDivProps') + .className ).not.toContain('highlighted_border_right_none') }) }) diff --git a/protocol-designer/src/components/DeckSetup/getModuleVizDims.ts b/protocol-designer/src/components/DeckSetup/getModuleVizDims.ts index dbaf1e9d5f4..8e14224a657 100644 --- a/protocol-designer/src/components/DeckSetup/getModuleVizDims.ts +++ b/protocol-designer/src/components/DeckSetup/getModuleVizDims.ts @@ -1,4 +1,3 @@ -// @flow // NOTE: Ian 2019-10-24 these are by-eye numbers that intentionally // ignore the real-life data to emphasize overhangs etc import { @@ -11,23 +10,21 @@ import { MAGNETIC_MODULE_TYPE, TEMPERATURE_MODULE_TYPE, THERMOCYCLER_MODULE_TYPE, + ModuleRealType, } from '@opentrons/shared-data' -import type { ModuleRealType } from '@opentrons/shared-data' -import type { DeckSlot, ModuleOrientation } from '../../types' - +import { DeckSlot, ModuleOrientation } from '../../types' // NOTE: all dims are in 'left' orientation. Rotate & transform to obtain 'right' orientation. -export type ModuleVizDims = {| - xOffset: number, - yOffset: number, - xDimension: number, - yDimension: number, - childXOffset: number, - childYOffset: number, - childXDimension: number, - childYDimension: number, -|} - -const MODULE_VIZ_DIMS: { [ModuleRealType]: ModuleVizDims } = { +export interface ModuleVizDims { + xOffset: number + yOffset: number + xDimension: number + yDimension: number + childXOffset: number + childYOffset: number + childXDimension: number + childYDimension: number +} +const MODULE_VIZ_DIMS: Record = { [MAGNETIC_MODULE_TYPE]: { xOffset: -1 * (SLOT_X * 0.2 + DIVIDER), yOffset: -1 * DIVIDER, @@ -59,7 +56,6 @@ const MODULE_VIZ_DIMS: { [ModuleRealType]: ModuleVizDims } = { childYDimension: SLOT_Y, }, } - export const getModuleVizDims = ( orientation: ModuleOrientation, moduleType: ModuleRealType @@ -75,15 +71,7 @@ export const getModuleVizDims = ( childYOffset: SLOT_Y - dims.childYOffset - dims.childYDimension, } } - -const LEFT_SIDE_SLOTS: Array = [ - '1', - '4', - '7', - '10', - SPAN7_8_10_11_SLOT, -] - +const LEFT_SIDE_SLOTS: DeckSlot[] = ['1', '4', '7', '10', SPAN7_8_10_11_SLOT] export const inferModuleOrientationFromSlot = ( slot: DeckSlot ): ModuleOrientation => { @@ -93,5 +81,6 @@ export const inferModuleOrientationFromSlot = ( if (!LEFT_SIDE_SLOTS.includes(slot)) { return 'right' } + return 'left' } diff --git a/protocol-designer/src/components/DeckSetup/index.ts b/protocol-designer/src/components/DeckSetup/index.ts index cc7881f0b9a..f6cc0c61304 100644 --- a/protocol-designer/src/components/DeckSetup/index.ts +++ b/protocol-designer/src/components/DeckSetup/index.ts @@ -1,26 +1,24 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' - import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' import * as labwareIngredActions from '../../labware-ingred/actions' import { getSelectedTerminalItemId } from '../../ui/steps' -import { selectors as stepFormSelectors } from '../../step-forms' -import type { InitialDeckSetup } from '../../step-forms' -import { DeckSetup as DeckSetupComponent } from './DeckSetup' - -import type { TerminalItemId } from '../../steplist' -import type { BaseState, ThunkDispatch } from '../../types' - -type Props = React.ElementProps - -type SP = {| - selectedTerminalItemId: ?TerminalItemId, - drilledDown: boolean, - initialDeckSetup: InitialDeckSetup, -|} - -type DP = {| drillUpFromLabware: () => mixed |} +import { + selectors as stepFormSelectors, + InitialDeckSetup, +} from '../../step-forms' +import { DeckSetup as DeckSetupComponent, DeckSetupProps } from './DeckSetup' +import { TerminalItemId } from '../../steplist' +import { BaseState, ThunkDispatch } from '../../types' + +type Props = DeckSetupProps +interface SP { + selectedTerminalItemId?: TerminalItemId | null + drilledDown: boolean + initialDeckSetup: InitialDeckSetup +} +interface DP { + drillUpFromLabware: () => unknown +} const mapStateToProps = (state: BaseState): SP => ({ selectedTerminalItemId: getSelectedTerminalItemId(state), @@ -28,7 +26,7 @@ const mapStateToProps = (state: BaseState): SP => ({ initialDeckSetup: stepFormSelectors.getInitialDeckSetup(state), }) -const mapDispatchToProps = (dispatch: ThunkDispatch<*>): DP => ({ +const mapDispatchToProps = (dispatch: ThunkDispatch): DP => ({ drillUpFromLabware: () => dispatch(labwareIngredActions.drillUpFromLabware()), }) @@ -41,14 +39,7 @@ const mergeProps = (stateProps: SP, dispatchProps: DP): Props => ({ }, }) -export const DeckSetup: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - DP, - _, - _ ->( +export const DeckSetup = connect( mapStateToProps, mapDispatchToProps, mergeProps diff --git a/protocol-designer/src/components/DeckSetupManager.tsx b/protocol-designer/src/components/DeckSetupManager.tsx index 77e5621105b..757ef75bf53 100644 --- a/protocol-designer/src/components/DeckSetupManager.tsx +++ b/protocol-designer/src/components/DeckSetupManager.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useSelector } from 'react-redux' import { @@ -8,7 +7,7 @@ import { import { DeckSetup } from './DeckSetup' import { NullDeckState } from './DeckSetup/DeckSetup' -export const DeckSetupManager = (): React.Node => { +export const DeckSetupManager = (): JSX.Element => { const batchEditSelectedStepTypes = useSelector(getBatchEditSelectedStepTypes) const hoveredItem = useSelector(getHoveredItem) diff --git a/protocol-designer/src/components/EditModules.tsx b/protocol-designer/src/components/EditModules.tsx index c98db76a343..19910e66adc 100644 --- a/protocol-designer/src/components/EditModules.tsx +++ b/protocol-designer/src/components/EditModules.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useSelector, useDispatch } from 'react-redux' import { @@ -9,22 +8,22 @@ import { moveDeckItem } from '../labware-ingred/actions/actions' import { useBlockingHint } from './Hints/useBlockingHint' import { MagneticModuleWarningModalContent } from './modals/EditModulesModal/MagneticModuleWarningModalContent' import { EditModulesModal } from './modals/EditModulesModal' -import type { ModuleModel, ModuleRealType } from '@opentrons/shared-data' +import { ModuleModel, ModuleRealType } from '@opentrons/shared-data' -type EditModulesProps = {| - moduleToEdit: {| - moduleId: ?string, - moduleType: ModuleRealType, - |}, - onCloseClick: () => mixed, -|} +export interface EditModulesProps { + moduleToEdit: { + moduleId?: string | null + moduleType: ModuleRealType + } + onCloseClick: () => unknown +} -export type ModelModuleInfo = {| - model: ModuleModel, - slot: string, -|} +export interface ModelModuleInfo { + model: ModuleModel + slot: string +} -export const EditModules = (props: EditModulesProps): React.Node => { +export const EditModules = (props: EditModulesProps): JSX.Element => { const { onCloseClick, moduleToEdit } = props const { moduleId, moduleType } = moduleToEdit const _initialDeckSetup = useSelector(stepFormSelectors.getInitialDeckSetup) @@ -36,7 +35,7 @@ export const EditModules = (props: EditModulesProps): React.Node => { ] = React.useState(null) const dispatch = useDispatch() - const editModuleModel = (selectedModel: ModuleModel) => { + const editModuleModel = (selectedModel: ModuleModel): void => { if (moduleOnDeck?.id != null) { dispatch( stepFormActions.editModule({ @@ -50,7 +49,7 @@ export const EditModules = (props: EditModulesProps): React.Node => { ) } } - const editModuleSlot = (selectedSlot: string) => { + const editModuleSlot = (selectedSlot: string): void => { if (selectedSlot && moduleOnDeck && moduleOnDeck.slot !== selectedSlot) { dispatch(moveDeckItem(moduleOnDeck.slot, selectedSlot)) } diff --git a/protocol-designer/src/components/EditableTextField.tsx b/protocol-designer/src/components/EditableTextField.tsx index 4a437a5acff..a43f61041dc 100644 --- a/protocol-designer/src/components/EditableTextField.tsx +++ b/protocol-designer/src/components/EditableTextField.tsx @@ -1,18 +1,17 @@ -// @flow // TODO: Ian 2018-10-30 if we like this, add it to components library import * as React from 'react' import { ClickOutside, Icon, InputField } from '@opentrons/components' import styles from './editableTextField.css' -type Props = { - className?: string, - value: ?string, - saveEdit: (newValue: string) => mixed, +interface Props { + className?: string + value?: string | null + saveEdit: (newValue: string) => unknown } -type State = { - editing: boolean, - transientValue: ?string, +interface State { + editing: boolean + transientValue?: string | null } export class EditableTextField extends React.Component { @@ -34,13 +33,13 @@ export class EditableTextField extends React.Component { }) } - handleKeyUp: (e: SyntheticKeyboardEvent<>) => void = e => { + handleKeyUp: (e: React.KeyboardEvent) => void = e => { if (e.key === 'Escape') { this.handleCancel() } } - handleFormSubmit: (e: SyntheticEvent<>) => void = e => { + handleFormSubmit: (e: React.FormEvent) => void = e => { e.preventDefault() // avoid 'form is not connected' warning this.handleSubmit() } @@ -51,11 +50,11 @@ export class EditableTextField extends React.Component { ) } - updateValue: (e: SyntheticInputEvent) => void = e => { + updateValue: (e: React.ChangeEvent) => void = e => { this.setState({ transientValue: e.currentTarget.value }) } - render(): React.Node { + render(): React.ReactNode { const { className, value } = this.props if (this.state.editing) { return ( diff --git a/protocol-designer/src/components/FilePage.tsx b/protocol-designer/src/components/FilePage.tsx index 04ff6fb539f..8303ca8dfc2 100644 --- a/protocol-designer/src/components/FilePage.tsx +++ b/protocol-designer/src/components/FilePage.tsx @@ -1,6 +1,5 @@ -// @flow import * as React from 'react' -import { Formik } from 'formik' +import { Formik, FormikProps } from 'formik' import { format } from 'date-fns' import { @@ -21,27 +20,26 @@ import { EditModules } from './EditModules' import styles from './FilePage.css' import modalStyles from '../components/modals/modal.css' import formStyles from '../components/forms/forms.css' -import type { FormikProps } from 'formik/@flow-typed' -import type { ModuleRealType } from '@opentrons/shared-data' -import type { FileMetadataFields } from '../file-data' -import type { ModulesForEditModulesCard } from '../step-forms' - -export type Props = {| - formValues: FileMetadataFields, - instruments: React.ElementProps, - goToNextPage: () => mixed, - saveFileMetadata: FileMetadataFields => mixed, - swapPipettes: () => mixed, - modules: ModulesForEditModulesCard, -|} - -type State = {| - isEditPipetteModalOpen: boolean, - moduleToEdit: {| - moduleType: ModuleRealType, - moduleId: ?string, - |} | null, -|} +import { ModuleRealType } from '@opentrons/shared-data' +import { FileMetadataFields } from '../file-data' +import { ModulesForEditModulesCard } from '../step-forms' + +export interface Props { + formValues: FileMetadataFields + instruments: React.ComponentProps + goToNextPage: () => unknown + saveFileMetadata: (fileMetaDataFields: FileMetadataFields) => void + swapPipettes: () => unknown + modules: ModulesForEditModulesCard +} + +interface State { + isEditPipetteModalOpen: boolean + moduleToEdit: { + moduleType: ModuleRealType + moduleId?: string | null + } | null +} // TODO(mc, 2020-02-28): explore l10n for these dates const DATE_ONLY_FORMAT = 'MMM dd, yyyy' @@ -78,7 +76,7 @@ export class FilePage extends React.Component { }) } - render(): React.Node { + render(): JSX.Element { const { formValues, instruments, diff --git a/protocol-designer/src/components/FileSidebar/FileSidebar.tsx b/protocol-designer/src/components/FileSidebar/FileSidebar.tsx index eca57eae574..7a820e0ba4f 100644 --- a/protocol-designer/src/components/FileSidebar/FileSidebar.tsx +++ b/protocol-designer/src/components/FileSidebar/FileSidebar.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { @@ -16,37 +15,37 @@ import { getUnusedEntities } from './utils' import modalStyles from '../modals/modal.css' import styles from './FileSidebar.css' -import type { HintKey } from '../../tutorial' -import type { PDProtocolFile } from '../../file-types' -import type { +import { HintKey } from '../../tutorial' +import { PDProtocolFile } from '../../file-types' +import { InitialDeckSetup, SavedStepFormState, ModuleOnDeck, PipetteOnDeck, } from '../../step-forms' -type Props = {| - loadFile: (event: SyntheticInputEvent) => mixed, - createNewFile?: () => mixed, - canDownload: boolean, - onDownload: () => mixed, - fileData: ?PDProtocolFile, - pipettesOnDeck: $PropertyType, - modulesOnDeck: $PropertyType, - savedStepForms: SavedStepFormState, - schemaVersion: number, -|} +export interface Props { + loadFile: (event: React.ChangeEvent) => unknown + createNewFile?: () => unknown + canDownload: boolean + onDownload: () => unknown + fileData?: PDProtocolFile | null + pipettesOnDeck: InitialDeckSetup['pipettes'] + modulesOnDeck: InitialDeckSetup['modules'] + savedStepForms: SavedStepFormState + schemaVersion: number +} -type WarningContent = {| - content: React.Node, - heading: string, -|} +interface WarningContent { + content: React.ReactNode + heading: string +} -type MissingContent = {| - noCommands: boolean, - pipettesWithoutStep: Array, - modulesWithoutStep: Array, -|} +interface MissingContent { + noCommands: boolean + pipettesWithoutStep: PipetteOnDeck[] + modulesWithoutStep: ModuleOnDeck[] +} function getWarningContent({ noCommands, @@ -134,7 +133,7 @@ function getWarningContent({ return null } -export const v4WarningContent: React.Node = ( +export const v4WarningContent: JSX.Element = (

{i18n.t(`alert.hint.export_v4_protocol_3_18.body1`)}{' '} @@ -144,7 +143,7 @@ export const v4WarningContent: React.Node = (

) -export const v5WarningContent: React.Node = ( +export const v5WarningContent: JSX.Element = (

{i18n.t(`alert.hint.export_v5_protocol_3_20.body1`)}{' '} @@ -154,7 +153,7 @@ export const v5WarningContent: React.Node = (

) -export function FileSidebar(props: Props): React.Node { +export function FileSidebar(props: Props): JSX.Element { const { canDownload, fileData, @@ -173,7 +172,7 @@ export function FileSidebar(props: Props): React.Node { const [showBlockingHint, setShowBlockingHint] = React.useState(false) - const cancelModal = () => setShowExportWarningModal(false) + const cancelModal = (): void => setShowExportWarningModal(false) const noCommands = fileData ? fileData.commands.length === 0 : true const pipettesWithoutStep = getUnusedEntities( @@ -198,10 +197,10 @@ export function FileSidebar(props: Props): React.Node { modulesWithoutStep, }) - const getExportHintContent = (): {| - hintKey: HintKey, - content: React.Node, - |} => { + const getExportHintContent = (): { + hintKey: HintKey + content: React.ReactNode + } => { return { hintKey: schemaVersion === 5 diff --git a/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx b/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx index 01337759a8d..8e22331643e 100644 --- a/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx +++ b/protocol-designer/src/components/FileSidebar/__tests__/FileSidebar.test.tsx @@ -1,8 +1,11 @@ -// @flow import * as React from 'react' import { shallow, mount } from 'enzyme' import { PrimaryButton, AlertModal, OutlineButton } from '@opentrons/components' -import { MAGNETIC_MODULE_TYPE } from '@opentrons/shared-data' +import { Command } from '@opentrons/shared-data/protocol/types/schemaV5' +import { + LabwareDefinition2, + MAGNETIC_MODULE_TYPE, +} from '@opentrons/shared-data' import { fixtureP10Single, fixtureP300Single, @@ -10,16 +13,21 @@ import { import fixture_tiprack_10_ul from '@opentrons/shared-data/labware/fixtures/2/fixture_tiprack_10_ul.json' import { FileSidebar, v4WarningContent, v5WarningContent } from '../FileSidebar' import { useBlockingHint } from '../../Hints/useBlockingHint' -import type { HintArgs } from '../../Hints/useBlockingHint' jest.mock('../../Hints/useBlockingHint') -const mockUseBlockingHint: JestMockFn<[HintArgs], ?React.Node> = useBlockingHint +const mockUseBlockingHint = useBlockingHint as jest.MockedFunction< + typeof useBlockingHint +> describe('FileSidebar', () => { const pipetteLeftId = 'pipetteLeftId' const pipetteRightId = 'pipetteRightId' - let props, commands, modulesOnDeck, pipettesOnDeck, savedStepForms + let props: React.ComponentProps + let commands: Command[] + let modulesOnDeck: React.ComponentProps['modulesOnDeck'] + let pipettesOnDeck: React.ComponentProps['pipettesOnDeck'] + let savedStepForms: React.ComponentProps['savedStepForms'] beforeEach(() => { props = { loadFile: jest.fn(), @@ -50,18 +58,18 @@ describe('FileSidebar', () => { pipettesOnDeck = { pipetteLeftId: { - name: 'string', + name: 'string' as any, id: pipetteLeftId, tiprackDefURI: 'test', - tiprackLabwareDef: fixture_tiprack_10_ul, + tiprackLabwareDef: fixture_tiprack_10_ul as LabwareDefinition2, spec: fixtureP10Single, mount: 'left', }, pipetteRightId: { - name: 'string', + name: 'string' as any, id: pipetteRightId, tiprackDefURI: 'test', - tiprackLabwareDef: fixture_tiprack_10_ul, + tiprackLabwareDef: fixture_tiprack_10_ul as LabwareDefinition2, spec: fixtureP300Single, mount: 'right', }, @@ -70,14 +78,14 @@ describe('FileSidebar', () => { modulesOnDeck = { magnet123: { type: MAGNETIC_MODULE_TYPE, - }, + } as any, } savedStepForms = { step123: { id: 'step123', pipette: pipetteLeftId, - }, + } as any, } }) afterEach(() => { @@ -112,6 +120,7 @@ describe('FileSidebar', () => { }) it('export button exports protocol when no errors', () => { + // @ts-expect-error(sa, 2021-6-22): props.fileData might be null props.fileData.commands = commands const wrapper = shallow() const downloadButton = wrapper.find(PrimaryButton).at(0) @@ -135,6 +144,7 @@ describe('FileSidebar', () => { }) it('warning modal is shown when export is clicked with unused pipette', () => { + // @ts-expect-error(sa, 2021-6-22): props.fileData might be null props.fileData.commands = commands props.pipettesOnDeck = pipettesOnDeck props.savedStepForms = savedStepForms @@ -162,6 +172,7 @@ describe('FileSidebar', () => { it('warning modal is shown when export is clicked with unused module', () => { props.modulesOnDeck = modulesOnDeck props.savedStepForms = savedStepForms + // @ts-expect-error(sa, 2021-6-22): props.fileData might be null props.fileData.commands = commands const wrapper = shallow() @@ -182,6 +193,7 @@ describe('FileSidebar', () => { props.modulesOnDeck = modulesOnDeck props.pipettesOnDeck = pipettesOnDeck props.savedStepForms = savedStepForms + // @ts-expect-error(sa, 2021-6-22): props.fileData might be null props.fileData.commands = commands const wrapper = shallow() @@ -206,13 +218,15 @@ describe('FileSidebar', () => { }) it('blocking hint is shown when protocol is v4', () => { + // @ts-expect-error(sa, 2021-6-22): props.fileData might be null props.fileData.commands = commands props.pipettesOnDeck = { pipetteLeftId: { + // @ts-expect-error(sa, 2021-6-22): not a valid pipette name name: 'string', id: pipetteLeftId, tiprackDefURI: 'test', - tiprackLabwareDef: fixture_tiprack_10_ul, + tiprackLabwareDef: fixture_tiprack_10_ul as LabwareDefinition2, spec: fixtureP10Single, mount: 'left', }, @@ -251,6 +265,7 @@ describe('FileSidebar', () => { }) it('blocking hint is shown when protocol is v5', () => { + // @ts-expect-error(sa, 2021-6-22): props.fileData might be null props.fileData.commands = commands props.savedStepForms = savedStepForms diff --git a/protocol-designer/src/components/FileSidebar/index.ts b/protocol-designer/src/components/FileSidebar/index.ts index f0ce88f3403..cfbb0a686a3 100644 --- a/protocol-designer/src/components/FileSidebar/index.ts +++ b/protocol-designer/src/components/FileSidebar/index.ts @@ -1,5 +1,3 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' import { i18n } from '../../localization' import { actions, selectors } from '../../navigation' @@ -9,32 +7,23 @@ import { actions as loadFileActions, selectors as loadFileSelectors, } from '../../load-file' -import { FileSidebar as FileSidebarComponent } from './FileSidebar' +import { FileSidebar as FileSidebarComponent, Props } from './FileSidebar' import type { BaseState, ThunkDispatch } from '../../types' import type { SavedStepFormState, InitialDeckSetup } from '../../step-forms' -type Props = React.ElementProps - -type SP = {| - canDownload: boolean, - fileData: $PropertyType, - _canCreateNew: ?boolean, - _hasUnsavedChanges: ?boolean, - pipettesOnDeck: $PropertyType, - modulesOnDeck: $PropertyType, - savedStepForms: SavedStepFormState, - schemaVersion: number, -|} - -export const FileSidebar: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - {||}, - _, - _ ->( +interface SP { + canDownload: boolean + fileData: Props['fileData'] + _canCreateNew?: boolean | null + _hasUnsavedChanges?: boolean | null + pipettesOnDeck: InitialDeckSetup['pipettes'] + modulesOnDeck: InitialDeckSetup['modules'] + savedStepForms: SavedStepFormState + schemaVersion: number +} +export const FileSidebar = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(FileSidebarComponent) @@ -43,7 +32,6 @@ function mapStateToProps(state: BaseState): SP { const fileData = fileDataSelectors.createFile(state) const canDownload = selectors.getCurrentPage(state) !== 'file-splash' const initialDeckSetup = stepFormSelectors.getInitialDeckSetup(state) - return { canDownload, fileData, @@ -59,7 +47,9 @@ function mapStateToProps(state: BaseState): SP { function mergeProps( stateProps: SP, - dispatchProps: { dispatch: ThunkDispatch<*> } + dispatchProps: { + dispatch: ThunkDispatch + } ): Props { const { _canCreateNew, diff --git a/protocol-designer/src/components/FileSidebar/utils/__tests__/getUnusedEntities.test.ts b/protocol-designer/src/components/FileSidebar/utils/__tests__/getUnusedEntities.test.ts index c8ad8f43fd5..97f9051650e 100644 --- a/protocol-designer/src/components/FileSidebar/utils/__tests__/getUnusedEntities.test.ts +++ b/protocol-designer/src/components/FileSidebar/utils/__tests__/getUnusedEntities.test.ts @@ -1,4 +1,3 @@ -// @flow import { fixtureP10Single, fixtureP300Single, @@ -11,11 +10,12 @@ import { TEMPERATURE_MODULE_V1, } from '@opentrons/shared-data' import { TEMPERATURE_DEACTIVATED } from '@opentrons/step-generation' +import { SavedStepFormState } from '../../../../step-forms' import { getUnusedEntities } from '../getUnusedEntities' describe('getUnusedEntities', () => { it('pipette entities not used in steps are returned', () => { - const stepForms = { + const stepForms: SavedStepFormState = { step123: { pipette: 'pipette123', id: 'step123', @@ -47,7 +47,7 @@ describe('getUnusedEntities', () => { }) it('module entities not used in steps are returned', () => { - const stepForms = { + const stepForms: SavedStepFormState = { step123: { moduleId: 'magnet123', id: 'step123', diff --git a/protocol-designer/src/components/FileSidebar/utils/getUnusedEntities.ts b/protocol-designer/src/components/FileSidebar/utils/getUnusedEntities.ts index 0e75ff67ef8..8e5a6867a2d 100644 --- a/protocol-designer/src/components/FileSidebar/utils/getUnusedEntities.ts +++ b/protocol-designer/src/components/FileSidebar/utils/getUnusedEntities.ts @@ -1,23 +1,23 @@ -// @flow import some from 'lodash/some' import reduce from 'lodash/reduce' import type { SavedStepFormState } from '../../../step-forms' /** Pull out all entities never specified by step forms. Assumes that all forms share the entityKey */ export function getUnusedEntities( - entities: { [entityId: string]: T }, + entities: Record, stepForms: SavedStepFormState, entityKey: 'pipette' | 'moduleId' -): Array { - return reduce( +): T[] { + const a = reduce( entities, - (acc, entity, entityId): Array => { + (acc, entity: T, entityId): T[] => { const stepContainsEntity = some( stepForms, form => form[entityKey] === entityId ) return stepContainsEntity ? acc : [...acc, entity] }, - [] + [] as T[] ) + return a } diff --git a/protocol-designer/src/components/FileSidebar/utils/index.ts b/protocol-designer/src/components/FileSidebar/utils/index.ts index d29ff0a9b86..1f9e4ed1b44 100644 --- a/protocol-designer/src/components/FileSidebar/utils/index.ts +++ b/protocol-designer/src/components/FileSidebar/utils/index.ts @@ -1,3 +1 @@ -// @flow - export * from './getUnusedEntities' diff --git a/protocol-designer/src/components/FormManager/index.tsx b/protocol-designer/src/components/FormManager/index.tsx index 154e9007e96..fa0e2b6a3f3 100644 --- a/protocol-designer/src/components/FormManager/index.tsx +++ b/protocol-designer/src/components/FormManager/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useSelector } from 'react-redux' import { Box, POSITION_STICKY, C_SELECTED_DARK } from '@opentrons/components' @@ -7,7 +6,7 @@ import { BatchEditForm } from '../BatchEditForm' import { StepSelectionBanner } from '../StepSelectionBanner' import { getIsMultiSelectMode } from '../../ui/steps/selectors' -export const FormManager = (): React.Node => { +export const FormManager = (): JSX.Element => { const isMultiSelectMode = useSelector(getIsMultiSelectMode) if (isMultiSelectMode) { diff --git a/protocol-designer/src/components/Hints/index.tsx b/protocol-designer/src/components/Hints/index.tsx index 5eb603c25e4..29e2a739d30 100644 --- a/protocol-designer/src/components/Hints/index.tsx +++ b/protocol-designer/src/components/Hints/index.tsx @@ -1,31 +1,33 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { AlertModal, CheckboxField, OutlineButton } from '@opentrons/components' import { i18n } from '../../localization' import { actions as stepsActions } from '../../ui/steps' -import type { TerminalItemId } from '../../steplist' -import { actions, selectors } from '../../tutorial' +import { TerminalItemId } from '../../steplist' +import { actions, selectors, HintKey } from '../../tutorial' import { Portal } from '../portals/MainPageModalPortal' import styles from './hints.css' import EXAMPLE_ADD_LIQUIDS_IMAGE from '../../images/example_add_liquids.png' import EXAMPLE_WATCH_LIQUIDS_MOVE_IMAGE from '../../images/example_watch_liquids_move.png' import EXAMPLE_BATCH_EDIT_IMAGE from '../../images/announcements/multi_select.gif' -import type { HintKey } from '../../tutorial' -import type { BaseState, ThunkDispatch } from '../../types' +import { BaseState, ThunkDispatch } from '../../types' -type SP = {| hintKey: ?HintKey |} -type DP = {| - removeHint: (HintKey, boolean) => mixed, - selectTerminalItem: TerminalItemId => mixed, -|} -type Props = {| ...SP, ...DP |} +interface SP { + hintKey?: HintKey | null +} +interface DP { + removeHint: (key: HintKey, rememberDismissal: boolean) => unknown + selectTerminalItem: (item: TerminalItemId) => unknown +} +type Props = SP & DP -type State = { rememberDismissal: boolean } +interface State { + rememberDismissal: boolean +} // List of hints that should have /!\ gray AlertModal header // (versus calmer non-alert header) -const HINT_IS_ALERT: Array = ['add_liquids_and_labware'] +const HINT_IS_ALERT: HintKey[] = ['add_liquids_and_labware'] class HintsComponent extends React.Component { constructor(props: Props) { @@ -33,16 +35,16 @@ class HintsComponent extends React.Component { this.state = { rememberDismissal: false } } - toggleRememberDismissal = () => { + toggleRememberDismissal = (): void => { this.setState({ rememberDismissal: !this.state.rememberDismissal }) } - makeHandleCloseClick = (hintKey: HintKey) => { + makeHandleCloseClick = (hintKey: HintKey): (() => void) => { const { rememberDismissal } = this.state return () => this.props.removeHint(hintKey, rememberDismissal) } - renderHintContents = (hintKey: HintKey) => { + renderHintContents = (hintKey: HintKey): JSX.Element | null => { // Only hints that have no outside effects should go here. // For hints that have an effect, use BlockingHint. switch (hintKey) { @@ -151,7 +153,7 @@ class HintsComponent extends React.Component { } } - render() { + render(): React.ReactNode { const { hintKey } = this.props if (!hintKey) return null @@ -189,21 +191,14 @@ class HintsComponent extends React.Component { const mapStateToProps = (state: BaseState): SP => ({ hintKey: selectors.getHint(state), }) -const mapDispatchToProps = (dispatch: ThunkDispatch<*>): DP => ({ +const mapDispatchToProps = (dispatch: ThunkDispatch): DP => ({ removeHint: (hintKey, rememberDismissal) => dispatch(actions.removeHint(hintKey, rememberDismissal)), selectTerminalItem: terminalId => dispatch(stepsActions.selectTerminalItem(terminalId)), }) -export const Hints: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - DP, - _, - _ ->( +export const Hints = connect( mapStateToProps, mapDispatchToProps )(HintsComponent) diff --git a/protocol-designer/src/components/Hints/useBlockingHint.tsx b/protocol-designer/src/components/Hints/useBlockingHint.tsx index b9ad67f690c..bfc972a5cbb 100644 --- a/protocol-designer/src/components/Hints/useBlockingHint.tsx +++ b/protocol-designer/src/components/Hints/useBlockingHint.tsx @@ -1,25 +1,23 @@ -// @flow // BlockingHint is an "are you sure" modal that can be dismissed. // Instances of BlockingHint need to be individually placed by whatever component // is controlling the flow that this modal will block, via useBlockingHint. import * as React from 'react' import { useDispatch, useSelector } from 'react-redux' -import { actions, selectors } from '../../tutorial' +import { actions, selectors, HintKey } from '../../tutorial' import { ContinueModal, CheckboxField } from '@opentrons/components' import { Portal } from '../portals/MainPageModalPortal' import { i18n } from '../../localization' import styles from './hints.css' -import type { HintKey } from '../../tutorial' -export type HintProps = {| - hintKey: HintKey, - handleCancel: () => mixed, - handleContinue: () => mixed, - content: React.Node, -|} +export interface HintProps { + hintKey: HintKey + handleCancel: () => unknown + handleContinue: () => unknown + content: React.ReactNode +} // This component handles the checkbox and dispatching `removeHint` action on continue/cancel -export const BlockingHint = (props: HintProps): React.Node => { +export const BlockingHint = (props: HintProps): JSX.Element => { const { hintKey, handleCancel, handleContinue } = props const dispatch = useDispatch() @@ -31,12 +29,12 @@ export const BlockingHint = (props: HintProps): React.Node => { setRememberDismissal(prevDismissal => !prevDismissal) }, []) - const onCancelClick = () => { + const onCancelClick = (): void => { dispatch(actions.removeHint(hintKey, rememberDismissal)) handleCancel() } - const onContinueClick = () => { + const onContinueClick = (): void => { dispatch(actions.removeHint(hintKey, rememberDismissal)) handleContinue() } @@ -63,18 +61,18 @@ export const BlockingHint = (props: HintProps): React.Node => { ) } -export type HintArgs = {| +export interface HintArgs { /** `enabled` should be a condition that the parent uses to toggle whether the hint should be active or not. * If the hint is enabled but has been dismissed, it will automatically call `handleContinue` when enabled. * useBlockingHint expects the parent to disable the hint on cancel/continue */ - enabled: boolean, - hintKey: HintKey, - content: React.Node, - handleCancel: () => mixed, - handleContinue: () => mixed, -|} + enabled: boolean + hintKey: HintKey + content: React.ReactNode + handleCancel: () => unknown + handleContinue: () => unknown +} -export const useBlockingHint = (args: HintArgs): React.Node => { +export const useBlockingHint = (args: HintArgs): JSX.Element | null => { const { enabled, hintKey, handleCancel, handleContinue, content } = args const isDismissed = useSelector(selectors.getDismissedHints).includes(hintKey) diff --git a/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/LabwareDetailsCard.tsx b/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/LabwareDetailsCard.tsx index dfff1288963..e6a473db3c4 100644 --- a/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/LabwareDetailsCard.tsx +++ b/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/LabwareDetailsCard.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { i18n } from '../../../localization' @@ -6,13 +5,13 @@ import { PDTitledList, PDListItem } from '../../lists' import { EditableTextField } from '../../EditableTextField' import styles from './labwareDetailsCard.css' -type Props = { - labwareDefDisplayName: string, - nickname: string, - renameLabware: (name: string) => mixed, +export interface Props { + labwareDefDisplayName: string + nickname: string + renameLabware: (name: string) => unknown } -export function LabwareDetailsCard(props: Props): React.Node { +export function LabwareDetailsCard(props: Props): JSX.Element { return ( diff --git a/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/index.ts b/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/index.ts index 6f7f98ab48e..8ac97d61f02 100644 --- a/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/index.ts +++ b/protocol-designer/src/components/IngredientsList/LabwareDetailsCard/index.ts @@ -1,22 +1,18 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' import assert from 'assert' import { getLabwareDisplayName } from '@opentrons/shared-data' -import { LabwareDetailsCard as LabwareDetailsCardComponent } from './LabwareDetailsCard' +import { + LabwareDetailsCard as LabwareDetailsCardComponent, + Props as LabwareDetailsCardProps, +} from './LabwareDetailsCard' import { selectors as stepFormSelectors } from '../../../step-forms' import { selectors as uiLabwareSelectors } from '../../../ui/labware' import { selectors as labwareIngredSelectors } from '../../../labware-ingred/selectors' import * as labwareIngredActions from '../../../labware-ingred/actions' -import type { ElementProps } from 'react' -import type { BaseState, ThunkDispatch } from '../../../types' - -type Props = ElementProps - -type SP = {| - ...$Diff<$Exact, {| renameLabware: * |}>, - _labwareId?: string, -|} +import { BaseState, ThunkDispatch } from '../../../types' +type SP = Omit & { + _labwareId?: string +} function mapStateToProps(state: BaseState): SP { const labwareNicknamesById = uiLabwareSelectors.getLabwareNicknamesById(state) @@ -26,11 +22,11 @@ function mapStateToProps(state: BaseState): SP { getLabwareDisplayName( stepFormSelectors.getLabwareEntities(state)[labwareId].def ) - assert( labwareId, 'Expected labware id to exist in connected labware details card' ) + if (!labwareId || !labwareDefDisplayName) { return { labwareDefDisplayName: '?', @@ -47,38 +43,35 @@ function mapStateToProps(state: BaseState): SP { function mergeProps( stateProps: SP, - dispatchProps: { dispatch: ThunkDispatch<*> } -): Props { + dispatchProps: { + dispatch: ThunkDispatch + } +): LabwareDetailsCardProps { const dispatch = dispatchProps.dispatch const { _labwareId, ...passThruProps } = stateProps - const renameLabware = (name: string) => { + const renameLabware = (name: string): void => { assert( _labwareId, 'renameLabware in LabwareDetailsCard expected a labwareId' ) + if (_labwareId) { dispatch( - labwareIngredActions.renameLabware({ labwareId: _labwareId, name }) + labwareIngredActions.renameLabware({ + labwareId: _labwareId, + name, + }) ) } } - return { - ...passThruProps, - renameLabware, - } + return { ...passThruProps, renameLabware } } -export const LabwareDetailsCard: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - {||}, - _, - _ ->( +export const LabwareDetailsCard = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(LabwareDetailsCardComponent) diff --git a/protocol-designer/src/components/IngredientsList/index.tsx b/protocol-designer/src/components/IngredientsList/index.tsx index 1c8fa9eda5f..61e30b02f35 100644 --- a/protocol-designer/src/components/IngredientsList/index.tsx +++ b/protocol-designer/src/components/IngredientsList/index.tsx @@ -1,4 +1,3 @@ -// @flow // TODO: Ian 2018-10-09 figure out what belongs in LiquidsSidebar vs IngredientsList after #2427 import * as React from 'react' @@ -10,28 +9,27 @@ import { TitledListNotes } from '../TitledListNotes' import { swatchColors } from '../swatchColors' import { LabwareDetailsCard } from './LabwareDetailsCard' import styles from './IngredientsList.css' -import type { LiquidGroupsById, LiquidGroup } from '../../labware-ingred/types' -import type { SingleLabwareLiquidState } from '@opentrons/step-generation' +import { LiquidGroupsById, LiquidGroup } from '../../labware-ingred/types' +import { SingleLabwareLiquidState } from '@opentrons/step-generation' -type RemoveWellsContents = (args: {| - liquidGroupId: string, - wells: Array, -|}) => mixed +type RemoveWellsContents = (args: { + liquidGroupId: string + wells: string[] +}) => unknown // Props used by both IngredientsList and LiquidGroupCard -type CommonProps = {| - removeWellsContents: RemoveWellsContents, - selected?: boolean, -|} - -type LiquidGroupCardProps = {| - groupId: string, - ingredGroup: LiquidGroup, - labwareWellContents: SingleLabwareLiquidState, - ...CommonProps, -|} - -const LiquidGroupCard = (props: LiquidGroupCardProps): React.Node => { +export interface CommonProps { + removeWellsContents: RemoveWellsContents + selected?: boolean +} + +type LiquidGroupCardProps = CommonProps & { + groupId: string + ingredGroup: LiquidGroup + labwareWellContents: SingleLabwareLiquidState +} + +const LiquidGroupCard = (props: LiquidGroupCardProps): JSX.Element | null => { const { ingredGroup, removeWellsContents, @@ -44,7 +42,7 @@ const LiquidGroupCard = (props: LiquidGroupCardProps): React.Node => { const [expanded, setExpanded] = React.useState(true) - const toggleAccordion = () => setExpanded(!expanded) + const toggleAccordion = (): void => setExpanded(!expanded) const wellsWithIngred = Object.keys(labwareWellContents) .sort(sortWells) @@ -100,17 +98,17 @@ const LiquidGroupCard = (props: LiquidGroupCardProps): React.Node => { ) } -type IndividProps = {| - name: ?string, - wellName: string, - volume: number, +interface IndividProps { + name?: string | null + wellName: string + volume: number // concentration?: string, - canDelete: boolean, - groupId: string, - removeWellsContents: RemoveWellsContents, -|} + canDelete: boolean + groupId: string + removeWellsContents: RemoveWellsContents +} -function IngredIndividual(props: IndividProps) { +function IngredIndividual(props: IndividProps): JSX.Element { const { name, wellName, @@ -144,14 +142,13 @@ function IngredIndividual(props: IndividProps) { ) } -type Props = { - ...CommonProps, - liquidGroupsById: LiquidGroupsById, - labwareWellContents: SingleLabwareLiquidState, - selectedIngredientGroupId: ?string, +type Props = CommonProps & { + liquidGroupsById: LiquidGroupsById + labwareWellContents: SingleLabwareLiquidState + selectedIngredientGroupId?: string | null } -export function IngredientsList(props: Props): React.Node { +export function IngredientsList(props: Props): JSX.Element { const { labwareWellContents, liquidGroupsById, diff --git a/protocol-designer/src/components/KnowledgeBaseLink/index.tsx b/protocol-designer/src/components/KnowledgeBaseLink/index.tsx index 415840d94ef..ebc1ae25b12 100644 --- a/protocol-designer/src/components/KnowledgeBaseLink/index.tsx +++ b/protocol-designer/src/components/KnowledgeBaseLink/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' export const KNOWLEDGEBASE_ROOT_URL = @@ -16,18 +15,16 @@ export const links = { betaReleases: `https://support.opentrons.com/en/articles/3854833-opentrons-beta-software-releases`, magneticModuleGenerations: 'http://support.opentrons.com/en/articles/1820112-magnetic-module', -} - -type Link = $Keys +} as const -type Props = {| - to: Link, - children: React.Node, - className?: ?string, -|} +interface Props { + to: keyof typeof links + children: React.ReactNode + className?: string +} /** Link which opens a page on the knowledge base to a new tab/window */ -export function KnowledgeBaseLink(props: Props): React.Node { +export function KnowledgeBaseLink(props: Props): JSX.Element { return ( any, - onMouseLeave: () => any, - selectLabware: (labwareLoadName: string) => mixed, -|} +interface Props { + disabled?: boolean | null + icon?: IconName | null + labwareDef: LabwareDefinition2 + onMouseEnter: () => any + onMouseLeave: () => any + selectLabware: (labwareLoadName: string) => unknown +} const LABWARE_LIBRARY_PAGE_PATH = 'https://labware.opentrons.com' -export function LabwareItem(props: Props): React.Node { +export function LabwareItem(props: Props): JSX.Element { const { disabled, icon, diff --git a/protocol-designer/src/components/LabwareSelectionModal/LabwarePreview.tsx b/protocol-designer/src/components/LabwareSelectionModal/LabwarePreview.tsx index 67ba0eb07c7..9b8c17f6aaf 100644 --- a/protocol-designer/src/components/LabwareSelectionModal/LabwarePreview.tsx +++ b/protocol-designer/src/components/LabwareSelectionModal/LabwarePreview.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import reduce from 'lodash/reduce' import { @@ -10,21 +9,21 @@ import { import { getLabwareDisplayName, getLabwareDefIsStandard, - type LabwareDefinition2, + LabwareDefinition2, } from '@opentrons/shared-data' import { i18n } from '../../localization' import styles from './styles.css' -type Props = { - labwareDef: ?LabwareDefinition2, +interface Props { + labwareDef?: LabwareDefinition2 | null moduleCompatibility?: | 'recommended' | 'potentiallyCompatible' | 'notCompatible' - | null, + | null } -export const LabwarePreview = (props: Props): React.Node => { +export const LabwarePreview = (props: Props): JSX.Element | null => { const { labwareDef, moduleCompatibility } = props if (!labwareDef) return null const maxVolumes = reduce( diff --git a/protocol-designer/src/components/LabwareSelectionModal/LabwareSelectionModal.tsx b/protocol-designer/src/components/LabwareSelectionModal/LabwareSelectionModal.tsx index 38ba8cc68ec..23a5bfe8a40 100644 --- a/protocol-designer/src/components/LabwareSelectionModal/LabwareSelectionModal.tsx +++ b/protocol-designer/src/components/LabwareSelectionModal/LabwareSelectionModal.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import startCase from 'lodash/startCase' import reduce from 'lodash/reduce' @@ -14,8 +13,8 @@ import { TEMPERATURE_MODULE_TYPE, MAGNETIC_MODULE_TYPE, THERMOCYCLER_MODULE_TYPE, - type LabwareDefinition2, - type ModuleRealType, + LabwareDefinition2, + ModuleRealType, } from '@opentrons/shared-data' import { i18n } from '../../localization' import { SPAN7_8_10_11_SLOT } from '../../constants' @@ -28,28 +27,28 @@ import { KnowledgeBaseLink } from '../KnowledgeBaseLink' import { LabwareItem } from './LabwareItem' import { LabwarePreview } from './LabwarePreview' import styles from './styles.css' -import type { DeckSlot } from '../../types' -import type { LabwareDefByDefURI } from '../../labware-defs' +import { DeckSlot } from '../../types' +import { LabwareDefByDefURI } from '../../labware-defs' -type Props = {| - onClose: (e?: any) => mixed, - onUploadLabware: (event: SyntheticInputEvent) => mixed, - selectLabware: (containerType: string) => mixed, - customLabwareDefs: LabwareDefByDefURI, +export interface Props { + onClose: (e?: any) => unknown + onUploadLabware: (event: React.ChangeEvent) => unknown + selectLabware: (containerType: string) => unknown + customLabwareDefs: LabwareDefByDefURI /** the slot you're literally adding labware to (may be a module slot) */ - slot: ?DeckSlot, + slot?: DeckSlot | null /** if adding to a module, the slot of the parent (for display) */ - parentSlot: ?DeckSlot, + parentSlot?: DeckSlot | null /** if adding to a module, the module's type */ - moduleType: ?ModuleRealType, + moduleType?: ModuleRealType | null /** tipracks that may be added to deck (depends on pipette<>tiprack assignment) */ - permittedTipracks: Array, -|} + permittedTipracks: string[] +} const LABWARE_CREATOR_URL = 'https://labware.opentrons.com/create' const CUSTOM_CATEGORY = 'custom' -const orderedCategories: Array = [ +const orderedCategories: string[] = [ 'tipRack', 'tubeRack', 'wellPlate', @@ -58,7 +57,7 @@ const orderedCategories: Array = [ // 'trash', // NOTE: trash intentionally hidden ] -const RECOMMENDED_LABWARE_BY_MODULE: { [ModuleRealType]: Array } = { +const RECOMMENDED_LABWARE_BY_MODULE: { [K in ModuleRealType]: string[] } = { [TEMPERATURE_MODULE_TYPE]: [ 'opentrons_24_aluminumblock_generic_2ml_screwcap', 'opentrons_96_aluminumblock_biorad_wellplate_200ul', @@ -76,7 +75,7 @@ const RECOMMENDED_LABWARE_BY_MODULE: { [ModuleRealType]: Array } = { export const getLabwareIsRecommended = ( def: LabwareDefinition2, - moduleType: ?ModuleRealType + moduleType?: ModuleRealType | null ): boolean => moduleType ? RECOMMENDED_LABWARE_BY_MODULE[moduleType].includes( @@ -84,7 +83,7 @@ export const getLabwareIsRecommended = ( ) : false -export const LabwareSelectionModal = (props: Props): React.Node => { +export const LabwareSelectionModal = (props: Props): JSX.Element | null => { const { customLabwareDefs, permittedTipracks, @@ -99,10 +98,9 @@ export const LabwareSelectionModal = (props: Props): React.Node => { const [selectedCategory, setSelectedCategory] = React.useState( null ) - const [ - previewedLabware, - setPreviewedLabware, - ] = React.useState(null) + const [previewedLabware, setPreviewedLabware] = React.useState< + LabwareDefinition2 | null | undefined + >(null) const [filterRecommended, setFilterRecommended] = React.useState( false ) @@ -163,7 +161,7 @@ export const LabwareSelectionModal = (props: Props): React.Node => { [filterRecommended, getLabwareCompatible, moduleType] ) - const customLabwareURIs: Array = React.useMemo( + const customLabwareURIs: string[] = React.useMemo( () => Object.keys(customLabwareDefs), [customLabwareDefs] ) @@ -172,10 +170,10 @@ export const LabwareSelectionModal = (props: Props): React.Node => { const defs = getOnlyLatestDefs() return reduce< LabwareDefByDefURI, - { [category: string]: Array } + { [category: string]: LabwareDefinition2[] } >( defs, - (acc, def: $Values) => { + (acc, def: typeof defs[keyof typeof defs]) => { const category: string = def.metadata.displayCategory // filter out non-permitted tipracks if ( @@ -211,7 +209,7 @@ export const LabwareSelectionModal = (props: Props): React.Node => { [labwareByCategory, getLabwareDisabled] ) - const wrapperRef = useOnClickOutside({ + const wrapperRef: React.RefObject = useOnClickOutside({ onClickOutside: () => { // don't close when clicking on the custom labware hint if (!enqueuedLabwareType) { @@ -233,7 +231,9 @@ export const LabwareSelectionModal = (props: Props): React.Node => {
setFilterRecommended(e.currentTarget.checked)} + onChange={(e: React.ChangeEvent) => + setFilterRecommended(e.currentTarget.checked) + } value={filterRecommended} /> @@ -248,10 +248,9 @@ export const LabwareSelectionModal = (props: Props): React.Node => {
) : null - let moduleCompatibility: $PropertyType< - React.ElementProps, - 'moduleCompatibility' - > = null + let moduleCompatibility: React.ComponentProps< + typeof LabwarePreview + >['moduleCompatibility'] = null if (previewedLabware && moduleType) { if (getLabwareIsRecommended(previewedLabware, moduleType)) { moduleCompatibility = 'recommended' @@ -296,6 +295,7 @@ export const LabwareSelectionModal = (props: Props): React.Node => { onMouseEnter={() => setPreviewedLabware(customLabwareDefs[labwareURI]) } + // @ts-expect-error(sa, 2021-6-22): need to pass in a nullsy value onMouseLeave={() => setPreviewedLabware()} /> ))} @@ -329,6 +329,7 @@ export const LabwareSelectionModal = (props: Props): React.Node => { labwareDef={labwareDef} selectLabware={selectLabware} onMouseEnter={() => setPreviewedLabware(labwareDef)} + // @ts-expect-error(sa, 2021-6-22): setPreviewedLabware expects an argument (even if nullsy) onMouseLeave={() => setPreviewedLabware()} /> ) diff --git a/protocol-designer/src/components/LabwareSelectionModal/index.ts b/protocol-designer/src/components/LabwareSelectionModal/index.ts index af9783151ca..70824aa4041 100644 --- a/protocol-designer/src/components/LabwareSelectionModal/index.ts +++ b/protocol-designer/src/components/LabwareSelectionModal/index.ts @@ -1,8 +1,8 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' -import { LabwareSelectionModal as LabwareSelectionModalComponent } from './LabwareSelectionModal' -// import { selectors as featureFlagSelectors } from '../../feature-flags' +import { + LabwareSelectionModal as LabwareSelectionModalComponent, + Props as LabwareSelectionModalProps, +} from './LabwareSelectionModal' import { closeLabwareSelector, createContainer, @@ -12,34 +12,27 @@ import { actions as labwareDefActions, selectors as labwareDefSelectors, } from '../../labware-defs' -import { - selectors as stepFormSelectors, - type ModuleOnDeck, -} from '../../step-forms' -import type { BaseState, ThunkDispatch } from '../../types' - -type Props = React.ElementProps - -type SP = {| - customLabwareDefs: $PropertyType, - slot: $PropertyType, - parentSlot: $PropertyType, - moduleType: $PropertyType, - permittedTipracks: $PropertyType, -|} +import { selectors as stepFormSelectors, ModuleOnDeck } from '../../step-forms' +import { BaseState, ThunkDispatch } from '../../types' +interface SP { + customLabwareDefs: LabwareSelectionModalProps['customLabwareDefs'] + slot: LabwareSelectionModalProps['slot'] + parentSlot: LabwareSelectionModalProps['parentSlot'] + moduleType: LabwareSelectionModalProps['moduleType'] + permittedTipracks: LabwareSelectionModalProps['permittedTipracks'] +} function mapStateToProps(state: BaseState): SP { const slot = labwareIngredSelectors.selectedAddLabwareSlot(state) || null // TODO: Ian 2019-10-29 needs revisit to support multiple manualIntervention steps const modulesById = stepFormSelectors.getInitialDeckSetup(state).modules - const initialModules: Array = Object.keys(modulesById).map( + const initialModules: ModuleOnDeck[] = Object.keys(modulesById).map( moduleId => modulesById[moduleId] ) const parentModule = (slot != null && initialModules.find(moduleOnDeck => moduleOnDeck.id === slot)) || null - return { customLabwareDefs: labwareDefSelectors.getCustomLabwareDefsByURI(state), slot, @@ -51,10 +44,11 @@ function mapStateToProps(state: BaseState): SP { function mergeProps( stateProps: SP, - dispatchProps: { dispatch: ThunkDispatch<*> } -): Props { + dispatchProps: { + dispatch: ThunkDispatch + } +): LabwareSelectionModalProps { const dispatch = dispatchProps.dispatch - return { ...stateProps, onClose: () => { @@ -64,21 +58,20 @@ function mergeProps( dispatch(labwareDefActions.createCustomLabwareDef(fileChangeEvent)), selectLabware: labwareDefURI => { if (stateProps.slot) { - dispatch(createContainer({ slot: stateProps.slot, labwareDefURI })) + dispatch( + createContainer({ + slot: stateProps.slot, + labwareDefURI, + }) + ) } }, } } -export const LabwareSelectionModal: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - {||}, - _, - _ ->( +export const LabwareSelectionModal = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(LabwareSelectionModalComponent) diff --git a/protocol-designer/src/components/LiquidPlacementForm/LiquidPlacementForm.tsx b/protocol-designer/src/components/LiquidPlacementForm/LiquidPlacementForm.tsx index e42675af2c2..db757e03a2c 100644 --- a/protocol-designer/src/components/LiquidPlacementForm/LiquidPlacementForm.tsx +++ b/protocol-designer/src/components/LiquidPlacementForm/LiquidPlacementForm.tsx @@ -1,6 +1,5 @@ -// @flow import * as React from 'react' -import { Formik } from 'formik' +import { Formik, FormikProps } from 'formik' import * as Yup from 'yup' // TODO: Ian 2018-10-19 move the processors out of steplist (chore) import * as fieldProcessors from '../../steplist/fieldLevel/processing' @@ -10,36 +9,34 @@ import { FormGroup, OutlineButton, PrimaryButton, + Options, } from '@opentrons/components' import { i18n } from '../../localization' import styles from './LiquidPlacementForm.css' import formStyles from '../forms/forms.css' import stepEditFormStyles from '../StepEditForm/StepEditForm.css' -import type { Options } from '@opentrons/components' -import type { FormikProps } from 'formik/@flow-typed' - -type ValidFormValues = {| - selectedLiquidId: string, - volume: string, -|} +interface ValidFormValues { + selectedLiquidId: string + volume: string +} -export type LiquidPlacementFormValues = {| - selectedLiquidId: ?string, - volume: ?string, -|} +export interface LiquidPlacementFormValues { + selectedLiquidId?: string | null + volume?: string | null +} -type Props = {| - commonSelectedLiquidId: ?string, - commonSelectedVolume: ?number, - liquidSelectionOptions: Options, - selectedWellsMaxVolume: number, - showForm: boolean, +export interface Props { + commonSelectedLiquidId?: string | null + commonSelectedVolume?: number | null + liquidSelectionOptions: Options + selectedWellsMaxVolume: number + showForm: boolean - cancelForm: () => mixed, - clearWells: ?() => mixed, - saveForm: LiquidPlacementFormValues => mixed, -|} + cancelForm: () => unknown + clearWells: (() => unknown | null) | null + saveForm: (liquidPlacementFormValues: LiquidPlacementFormValues) => unknown +} export class LiquidPlacementForm extends React.Component { getInitialValues: () => ValidFormValues = () => { @@ -51,10 +48,11 @@ export class LiquidPlacementForm extends React.Component { } getValidationSchema: () => Yup.Schema< - {| - selectedLiquidId: string, - volume: number, - |}, + | { + selectedLiquidId: string + volume: number + } + | undefined, any > = () => { const { selectedWellsMaxVolume } = this.props @@ -90,9 +88,9 @@ export class LiquidPlacementForm extends React.Component { } handleChangeVolume: ( - setFieldValue: (fieldName: string, value: mixed) => mixed - ) => (e: SyntheticInputEvent<*>) => void = setFieldValue => e => { - const value: ?string = e.currentTarget.value + setFieldValue: (fieldName: string, value: unknown) => unknown + ) => (e: React.ChangeEvent) => void = setFieldValue => e => { + const value: string | null | undefined = e.currentTarget.value const masked = fieldProcessors.composeMaskers( fieldProcessors.maskToFloat, fieldProcessors.onlyPositiveNumbers, @@ -105,7 +103,7 @@ export class LiquidPlacementForm extends React.Component { this.props.saveForm(values) } - render(): React.Node { + render(): React.ReactNode | null { const { liquidSelectionOptions, showForm } = this.props if (!showForm) return null return ( diff --git a/protocol-designer/src/components/LiquidPlacementForm/index.ts b/protocol-designer/src/components/LiquidPlacementForm/index.ts index a3d97497fad..9b140447f8e 100644 --- a/protocol-designer/src/components/LiquidPlacementForm/index.ts +++ b/protocol-designer/src/components/LiquidPlacementForm/index.ts @@ -1,5 +1,3 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' import assert from 'assert' import isEmpty from 'lodash/isEmpty' @@ -11,32 +9,29 @@ import { selectors as labwareIngredSelectors } from '../../labware-ingred/select import * as wellContentsSelectors from '../../top-selectors/well-contents' import { getSelectedWells } from '../../well-selection/selectors' import { deselectAllWells } from '../../well-selection/actions' -import { LiquidPlacementForm as LiquidPlacementFormComponent } from './LiquidPlacementForm' -import type { Dispatch } from 'redux' -import type { LiquidPlacementFormValues } from './LiquidPlacementForm' -import type { BaseState } from '../../types' - -type Props = React.ElementProps - -type SP = $Rest< - {| - ...$Exact, - _labwareId: ?string, - _selectedWells: ?Array, - _selectionHasLiquids: boolean, - |}, - {| - cancelForm: $PropertyType, - clearWells: $PropertyType, - saveForm: $PropertyType, - |} +import { + LiquidPlacementForm as LiquidPlacementFormComponent, + Props as LiquidPlacementFormProps, + LiquidPlacementFormValues, +} from './LiquidPlacementForm' +import { Dispatch } from 'redux' +import { BaseState } from '../../types' +type SP = Omit< + LiquidPlacementFormProps & { + _labwareId?: string | null + _selectedWells?: string[] | null + _selectionHasLiquids: boolean + }, + 'cancelForm' | 'clearWells' | 'saveForm' > function mapStateToProps(state: BaseState): SP { const selectedWells = getSelectedWells(state) const _labwareId = labwareIngredSelectors.getSelectedLabwareId(state) + const liquidLocations = labwareIngredSelectors.getLiquidsByLabwareId(state) + const _selectionHasLiquids = Boolean( _labwareId && liquidLocations[_labwareId] && @@ -57,7 +52,6 @@ function mapStateToProps(state: BaseState): SP { selectedWellsMaxVolume: wellContentsSelectors.getSelectedWellsMaxVolume( state ), - _labwareId, _selectedWells: Object.keys(selectedWells), _selectionHasLiquids, @@ -66,8 +60,10 @@ function mapStateToProps(state: BaseState): SP { function mergeProps( stateProps: SP, - dispatchProps: { dispatch: Dispatch<*> } -): Props { + dispatchProps: { + dispatch: Dispatch + } +): LiquidPlacementFormProps { const { _labwareId, _selectedWells, @@ -75,7 +71,6 @@ function mergeProps( ...passThruProps } = stateProps const { dispatch } = dispatchProps - const clearWells = _labwareId && _selectedWells && _selectionHasLiquids ? () => { @@ -94,7 +89,6 @@ function mergeProps( } } : null - return { ...passThruProps, cancelForm: () => dispatch(deselectAllWells()), @@ -102,7 +96,6 @@ function mergeProps( saveForm: (values: LiquidPlacementFormValues) => { const { selectedLiquidId } = values const volume = Number(values.volume) - assert( _labwareId != null, 'when saving liquid placement form, expected a selected labware ID' @@ -138,15 +131,9 @@ function mergeProps( } } -export const LiquidPlacementForm: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - {||}, - _, - _ ->( +export const LiquidPlacementForm = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(LiquidPlacementFormComponent) diff --git a/protocol-designer/src/components/LiquidPlacementModal.tsx b/protocol-designer/src/components/LiquidPlacementModal.tsx index 80feacbda10..01a86e76926 100644 --- a/protocol-designer/src/components/LiquidPlacementModal.tsx +++ b/protocol-designer/src/components/LiquidPlacementModal.tsx @@ -1,4 +1,3 @@ -// @flow import assert from 'assert' import * as React from 'react' import { connect } from 'react-redux' @@ -20,28 +19,30 @@ import { selectWells, deselectWells } from '../well-selection/actions' import styles from './LiquidPlacementModal.css' -import type { Dispatch } from 'redux' -import type { WellGroup } from '@opentrons/components' -import type { LabwareDefinition2 } from '@opentrons/shared-data' -import type { BaseState } from '../types' -import type { ContentsByWell } from '../labware-ingred/types' -import type { WellIngredientNames } from '../steplist' - -type SP = {| - selectedWells: WellGroup, - wellContents: ContentsByWell, - labwareDef: ?LabwareDefinition2, - liquidNamesById: WellIngredientNames, -|} +import { Dispatch } from 'redux' +import { WellGroup } from '@opentrons/components' +import { LabwareDefinition2 } from '@opentrons/shared-data' +import { BaseState } from '../types' +import { ContentsByWell } from '../labware-ingred/types' +import { WellIngredientNames } from '../steplist' + +interface SP { + selectedWells: WellGroup + wellContents: ContentsByWell + labwareDef?: LabwareDefinition2 | null + liquidNamesById: WellIngredientNames +} -type DP = {| - selectWells: WellGroup => mixed, - deselectWells: WellGroup => mixed, -|} +interface DP { + selectWells: (wellGroup: WellGroup) => unknown + deselectWells: (wellGroup: WellGroup) => unknown +} -type Props = { ...SP, ...DP } +type Props = SP & DP -type State = { highlightedWells: WellGroup } +interface State { + highlightedWells: WellGroup +} class LiquidPlacementModalComponent extends React.Component { state = { highlightedWells: {} } @@ -50,11 +51,11 @@ class LiquidPlacementModalComponent extends React.Component { this.state = { highlightedWells: {} } } - updateHighlightedWells = (wells: WellGroup) => { + updateHighlightedWells = (wells: WellGroup): void => { this.setState({ highlightedWells: wells }) } - render() { + render(): JSX.Element { const { labwareDef, selectedWells } = this.props return ( @@ -122,19 +123,12 @@ const mapStateToProps = (state: BaseState): SP => { } } -const mapDispatchToProps = (dispatch: Dispatch<*>): DP => ({ +const mapDispatchToProps = (dispatch: Dispatch): DP => ({ deselectWells: wells => dispatch(deselectWells(wells)), selectWells: wells => dispatch(selectWells(wells)), }) -export const LiquidPlacementModal: React.AbstractComponent<{||}> = connect< - Props, - {||}, - _, - _, - _, - _ ->( +export const LiquidPlacementModal = connect( mapStateToProps, mapDispatchToProps )(LiquidPlacementModalComponent) diff --git a/protocol-designer/src/components/LiquidsPage/LiquidEditForm.tsx b/protocol-designer/src/components/LiquidsPage/LiquidEditForm.tsx index ab32dd54230..e515dbc62b8 100644 --- a/protocol-designer/src/components/LiquidsPage/LiquidEditForm.tsx +++ b/protocol-designer/src/components/LiquidsPage/LiquidEditForm.tsx @@ -1,6 +1,5 @@ -// @flow import * as React from 'react' -import { Formik } from 'formik' +import { Formik, FormikProps } from 'formik' import * as Yup from 'yup' import { i18n } from '../../localization' import { @@ -14,26 +13,23 @@ import { import styles from './LiquidEditForm.css' import formStyles from '../forms/forms.css' -import type { FormikProps } from 'formik/@flow-typed' -import type { LiquidGroup } from '../../labware-ingred/types' +import { LiquidGroup } from '../../labware-ingred/types' -type Props = { - ...$Exact, - canDelete: boolean, - deleteLiquidGroup: () => mixed, - cancelForm: () => mixed, - saveForm: LiquidGroup => mixed, +type Props = LiquidGroup & { + canDelete: boolean + deleteLiquidGroup: () => unknown + cancelForm: () => unknown + saveForm: (liquidGroup: LiquidGroup) => unknown } -type LiquidEditFormValues = { - name: string, - description?: ?string, - serialize?: boolean, - ... +interface LiquidEditFormValues { + name: string + description?: string | null + serialize?: boolean + [key: string]: unknown } - export const liquidEditFormSchema: Yup.Schema< - {| name: string, description: string, serialize: boolean |}, + { name: string; description: string; serialize: boolean } | undefined, any > = Yup.object().shape({ name: Yup.string().required( @@ -45,7 +41,7 @@ export const liquidEditFormSchema: Yup.Schema< serialize: Yup.boolean(), }) -export function LiquidEditForm(props: Props): React.Node { +export function LiquidEditForm(props: Props): JSX.Element { const { deleteLiquidGroup, cancelForm, canDelete, saveForm } = props const initialValues: LiquidEditFormValues = { diff --git a/protocol-designer/src/components/LiquidsPage/LiquidsPageInfo.tsx b/protocol-designer/src/components/LiquidsPage/LiquidsPageInfo.tsx index e45e418a3ac..18919e6c23a 100644 --- a/protocol-designer/src/components/LiquidsPage/LiquidsPageInfo.tsx +++ b/protocol-designer/src/components/LiquidsPage/LiquidsPageInfo.tsx @@ -1,9 +1,8 @@ -// @flow import * as React from 'react' import { Icon } from '@opentrons/components' import styles from './LiquidsPageInfo.css' -export function LiquidsPageInfo(): React.Node { +export function LiquidsPageInfo(): JSX.Element { return (

Define your liquids

diff --git a/protocol-designer/src/components/LiquidsPage/index.tsx b/protocol-designer/src/components/LiquidsPage/index.tsx index 6c265602150..f6a5e4a20ef 100644 --- a/protocol-designer/src/components/LiquidsPage/index.tsx +++ b/protocol-designer/src/components/LiquidsPage/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import assert from 'assert' @@ -8,20 +7,23 @@ import { LiquidsPageInfo } from './LiquidsPageInfo' import * as labwareIngredActions from '../../labware-ingred/actions' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' -import type { LiquidGroup } from '../../labware-ingred/types' -import type { BaseState, ThunkDispatch } from '../../types' +import { LiquidGroup } from '../../labware-ingred/types' +import { BaseState, ThunkDispatch } from '../../types' -type Props = React.ElementProps -type WrapperProps = { showForm: boolean, formKey: string, formProps: Props } +type Props = React.ComponentProps +interface WrapperProps { + showForm: boolean + formKey: string + formProps: Props +} -type SP = {| - ...$Exact, - _liquidGroupId: ?string, - showForm: boolean, - canDelete: $ElementType, -|} +type SP = LiquidGroup & { + _liquidGroupId?: string | null + showForm: boolean + canDelete: Props['canDelete'] +} -function LiquidEditFormWrapper(props: WrapperProps) { +function LiquidEditFormWrapper(props: WrapperProps): JSX.Element { const { showForm, formKey, formProps } = props return showForm ? ( @@ -57,15 +59,18 @@ function mapStateToProps(state: BaseState): SP { _liquidGroupId, canDelete: _liquidGroupId != null, showForm, + // @ts-expect-error(sa, 2021-6-22): name might not exist name: selectedIngredFields.name, + // @ts-expect-error(sa, 2021-6-22): description might not exist description: selectedIngredFields.description, + // @ts-expect-error(sa, 2021-6-22): serialize might not exist serialize: selectedIngredFields.serialize, } } function mergeProps( stateProps: SP, - dispatchProps: { dispatch: ThunkDispatch<*> } + dispatchProps: { dispatch: ThunkDispatch } ): WrapperProps { const { dispatch } = dispatchProps const { showForm, _liquidGroupId, ...passThruFormProps } = stateProps @@ -90,15 +95,9 @@ function mergeProps( } } -export const LiquidsPage: React.AbstractComponent<{||}> = connect< - WrapperProps, - {||}, - SP, - {||}, - _, - _ ->( +export const LiquidsPage = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(LiquidEditFormWrapper) diff --git a/protocol-designer/src/components/LiquidsSidebar/index.tsx b/protocol-designer/src/components/LiquidsSidebar/index.tsx index c6ea9fc5cf8..83a5e459e5c 100644 --- a/protocol-designer/src/components/LiquidsSidebar/index.tsx +++ b/protocol-designer/src/components/LiquidsSidebar/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { i18n } from '../../localization' @@ -8,23 +7,23 @@ import { swatchColors } from '../swatchColors' import listButtonStyles from '../listButtons.css' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' -import type { OrderedLiquids } from '../../labware-ingred/types' +import { OrderedLiquids } from '../../labware-ingred/types' import * as labwareIngredActions from '../../labware-ingred/actions' -import type { BaseState, ThunkDispatch } from '../../types' +import { BaseState, ThunkDispatch } from '../../types' -type SP = {| - liquids: OrderedLiquids, - selectedLiquid: ?string, -|} +interface SP { + liquids: OrderedLiquids + selectedLiquid?: string | null +} -type DP = {| - createNewLiquid: () => mixed, - selectLiquid: (liquidId: string) => mixed, -|} +interface DP { + createNewLiquid: () => unknown + selectLiquid: (liquidId: string) => unknown +} -type Props = {| ...SP, ...DP |} +type Props = SP & DP -function LiquidsSidebarComponent(props: Props) { +function LiquidsSidebarComponent(props: Props): JSX.Element { const { liquids, selectedLiquid, createNewLiquid, selectLiquid } = props return ( @@ -57,7 +56,7 @@ function mapStateToProps(state: BaseState): SP { } } -function mapDispatchToProps(dispatch: ThunkDispatch<*>): DP { +function mapDispatchToProps(dispatch: ThunkDispatch): DP { return { selectLiquid: liquidGroupId => dispatch(labwareIngredActions.selectLiquidGroup(liquidGroupId)), @@ -66,14 +65,7 @@ function mapDispatchToProps(dispatch: ThunkDispatch<*>): DP { } } -export const LiquidsSidebar: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - DP, - _, - _ ->( +export const LiquidsSidebar = connect( mapStateToProps, mapDispatchToProps )(LiquidsSidebarComponent) diff --git a/protocol-designer/src/components/PrereleaseModeIndicator.tsx b/protocol-designer/src/components/PrereleaseModeIndicator.tsx index 4483fcfbda2..9c932dd0bbb 100644 --- a/protocol-designer/src/components/PrereleaseModeIndicator.tsx +++ b/protocol-designer/src/components/PrereleaseModeIndicator.tsx @@ -1,10 +1,9 @@ -// @flow import * as React from 'react' import { useSelector } from 'react-redux' import { Icon } from '@opentrons/components' import { selectors as featureFlagSelectors } from '../feature-flags' -export const PrereleaseModeIndicator = (): React.Node => { +export const PrereleaseModeIndicator = (): JSX.Element | null => { const prereleaseModeEnabled = useSelector( featureFlagSelectors.getEnabledPrereleaseMode ) diff --git a/protocol-designer/src/components/ProtocolEditor.tsx b/protocol-designer/src/components/ProtocolEditor.tsx index 06857e13220..aa811760aa5 100644 --- a/protocol-designer/src/components/ProtocolEditor.tsx +++ b/protocol-designer/src/components/ProtocolEditor.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { DragDropContext } from 'react-dnd' @@ -22,7 +21,7 @@ import styles from './ProtocolEditor.css' const showGateModal = process.env.NODE_ENV === 'production' || process.env.OT_PD_SHOW_GATE -function ProtocolEditorComponent() { +function ProtocolEditorComponent(): JSX.Element { return (
@@ -56,6 +55,6 @@ function ProtocolEditorComponent() { ) } -export const ProtocolEditor: React.AbstractComponent<{||}> = DragDropContext( - MouseBackEnd -)(ProtocolEditorComponent) +export const ProtocolEditor = DragDropContext(MouseBackEnd)( + ProtocolEditorComponent +) diff --git a/protocol-designer/src/components/SelectionRect.tsx b/protocol-designer/src/components/SelectionRect.tsx index 8de0f673423..251470e135b 100644 --- a/protocol-designer/src/components/SelectionRect.tsx +++ b/protocol-designer/src/components/SelectionRect.tsx @@ -1,33 +1,30 @@ -// @flow import * as React from 'react' import styles from './SelectionRect.css' -import type { DragRect, GenericRect } from '../collision-types' - -type Props = {| - onSelectionMove?: (e: MouseEvent, GenericRect) => mixed, - onSelectionDone?: (e: MouseEvent, GenericRect) => mixed, - svg?: boolean, // set true if this is an embedded SVG - children?: React.Node, - originXOffset?: number, - originYOffset?: number, -|} +import { DragRect, GenericRect } from '../collision-types' + +interface Props { + onSelectionMove?: (e: MouseEvent, arg: GenericRect) => unknown + onSelectionDone?: (e: MouseEvent, arg: GenericRect) => unknown + svg?: boolean // set true if this is an embedded SVG + children?: React.ReactNode + originXOffset?: number + originYOffset?: number +} -type State = {| - positions: DragRect | null, -|} +interface State { + positions: DragRect | null +} export class SelectionRect extends React.Component { - // TODO Ian 2018-02-22 No support in Flow for SVGElement yet: https://github.com/facebook/flow/issues/2332 - // this `parentRef` should be HTMLElement | SVGElement - parentRef: ?any + parentRef?: HTMLElement | SVGElement | null constructor(props: Props) { super(props) this.state = { positions: null } } - renderRect(args: DragRect): React.Node { + renderRect(args: DragRect): React.ReactNode { const { xStart, yStart, xDynamic, yDynamic } = args const left = Math.min(xStart, xDynamic) const top = Math.min(yStart, yDynamic) @@ -43,12 +40,13 @@ export class SelectionRect extends React.Component { } const clientRect: { - width: number, - height: number, - left: number, - top: number, + width: number + height: number + left: number + top: number } = parentRef.getBoundingClientRect() - const viewBox: { width: number, height: number } = parentRef.closest( + // @ts-expect-error(sa, 2021-7-1): parentRef.closest might return null + const viewBox: { width: number; height: number } = parentRef.closest( 'svg' ).viewBox.baseVal // WARNING: elem.closest() is experiemental @@ -91,7 +89,7 @@ export class SelectionRect extends React.Component { } } - handleMouseDown: (e: MouseEvent) => void = e => { + handleMouseDown: React.MouseEventHandler = e => { document.addEventListener('mousemove', this.handleDrag) document.addEventListener('mouseup', this.handleMouseUp) this.setState({ @@ -136,7 +134,7 @@ export class SelectionRect extends React.Component { this.props.onSelectionDone(e, finalRect) } - render(): React.Node { + render(): React.ReactNode { const { svg, children } = this.props return svg ? ( diff --git a/protocol-designer/src/components/SettingsPage/FeatureFlagCard/FeatureFlagCard.tsx b/protocol-designer/src/components/SettingsPage/FeatureFlagCard/FeatureFlagCard.tsx index 6e90fcb1917..79caa32684a 100644 --- a/protocol-designer/src/components/SettingsPage/FeatureFlagCard/FeatureFlagCard.tsx +++ b/protocol-designer/src/components/SettingsPage/FeatureFlagCard/FeatureFlagCard.tsx @@ -1,4 +1,3 @@ -// @flow import sortBy from 'lodash/sortBy' import * as React from 'react' import { ContinueModal, Card, ToggleButton } from '@opentrons/components' @@ -7,25 +6,22 @@ import { resetScrollElements } from '../../../ui/steps/utils' import { Portal } from '../../portals/MainPageModalPortal' import styles from '../SettingsPage.css' import modalStyles from '../../modals/modal.css' -import { - userFacingFlags, - type Flags, - type FlagTypes, -} from '../../../feature-flags' +import { userFacingFlags, Flags, FlagTypes } from '../../../feature-flags' -type Props = {| - flags: Flags, - setFeatureFlags: (flags: Flags) => mixed, -|} +export interface Props { + flags: Flags + setFeatureFlags: (flags: Flags) => unknown +} -export const FeatureFlagCard = (props: Props): React.Node => { +export const FeatureFlagCard = (props: Props): JSX.Element => { const [modalFlagName, setModalFlagName] = React.useState( null ) const prereleaseModeEnabled = props.flags.PRERELEASE_MODE === true - const allFlags = sortBy(Object.keys(props.flags)) + // @ts-expect-error(sa, 2021-6-21): Object.keys not smart enough to take keys from props.flags + const allFlags: FlagTypes[] = sortBy(Object.keys(props.flags)) const userFacingFlagNames = allFlags.filter(flagName => userFacingFlags.includes(flagName) @@ -35,8 +31,8 @@ export const FeatureFlagCard = (props: Props): React.Node => { flagName => !userFacingFlags.includes(flagName) ) - const getDescription = (flag: FlagTypes): React.Node => { - const RICH_DESCRIPTIONS: { [FlagTypes]: React.Node } = { + const getDescription = (flag: FlagTypes): JSX.Element => { + const RICH_DESCRIPTIONS: Partial> = { OT_PD_DISABLE_MODULE_RESTRICTIONS: ( <>

{i18n.t(`feature_flags.${flag}.description_1`)}

@@ -51,7 +47,7 @@ export const FeatureFlagCard = (props: Props): React.Node => { ) } - const toFlagRow = flagName => ( + const toFlagRow = (flagName: FlagTypes): JSX.Element => (

@@ -85,7 +81,7 @@ export const FeatureFlagCard = (props: Props): React.Node => { let flagSwitchDirection: string = 'on' if (modalFlagName) { - const isFlagOn: ?boolean = props.flags[modalFlagName] + const isFlagOn: boolean | null | undefined = props.flags[modalFlagName] flagSwitchDirection = isFlagOn ? 'off' : 'on' } return ( @@ -101,7 +97,7 @@ export const FeatureFlagCard = (props: Props): React.Node => { onCancelClick={() => setModalFlagName(null)} onContinueClick={() => { props.setFeatureFlags({ - [(modalFlagName: string)]: !props.flags[modalFlagName], + [modalFlagName as string]: !props.flags[modalFlagName], }) setModalFlagName(null) }} diff --git a/protocol-designer/src/components/SettingsPage/FeatureFlagCard/index.ts b/protocol-designer/src/components/SettingsPage/FeatureFlagCard/index.ts index 621864e9a94..034aba2e859 100644 --- a/protocol-designer/src/components/SettingsPage/FeatureFlagCard/index.ts +++ b/protocol-designer/src/components/SettingsPage/FeatureFlagCard/index.ts @@ -1,37 +1,30 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' -import { FeatureFlagCard as FeatureFlagCardComponent } from './FeatureFlagCard' +import { + FeatureFlagCard as FeatureFlagCardComponent, + Props as FeatureFlagCardProps, +} from './FeatureFlagCard' import { actions as featureFlagActions, selectors as featureFlagSelectors, } from '../../../feature-flags' - -import type { Dispatch } from 'redux' -import type { ElementProps } from 'react' -import type { BaseState } from '../../../types' - -type Props = ElementProps - -type SP = {| flags: $PropertyType |} -type DP = {| setFeatureFlags: $PropertyType |} +import { Dispatch } from 'redux' +import { BaseState } from '../../../types' +interface SP { + flags: FeatureFlagCardProps['flags'] +} +interface DP { + setFeatureFlags: FeatureFlagCardProps['setFeatureFlags'] +} const mapStateToProps = (state: BaseState): SP => ({ flags: featureFlagSelectors.getFeatureFlagData(state), }) -const mapDispatchToProps = (dispatch: Dispatch<*>): DP => ({ +const mapDispatchToProps = (dispatch: Dispatch): DP => ({ setFeatureFlags: flags => dispatch(featureFlagActions.setFeatureFlags(flags)), }) -export const FeatureFlagCard: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - DP, - BaseState, - _ ->( +export const FeatureFlagCard = connect( mapStateToProps, mapDispatchToProps )(FeatureFlagCardComponent) diff --git a/protocol-designer/src/components/SettingsPage/SettingsApp.tsx b/protocol-designer/src/components/SettingsPage/SettingsApp.tsx index ad567247133..a5868f02a16 100644 --- a/protocol-designer/src/components/SettingsPage/SettingsApp.tsx +++ b/protocol-designer/src/components/SettingsPage/SettingsApp.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { i18n } from '../../localization' @@ -19,21 +18,21 @@ import { import { OLDEST_MIGRATEABLE_VERSION } from '../../load-file/migration' import { FeatureFlagCard } from './FeatureFlagCard' import styles from './SettingsPage.css' -import type { BaseState, ThunkDispatch } from '../../types' +import { BaseState, ThunkDispatch } from '../../types' -type Props = { - canClearHintDismissals: boolean, - hasOptedIn: boolean | null, - restoreHints: () => mixed, - toggleOptedIn: () => mixed, +interface Props { + canClearHintDismissals: boolean + hasOptedIn: boolean | null + restoreHints: () => unknown + toggleOptedIn: () => unknown } -type SP = {| - canClearHintDismissals: $PropertyType, - hasOptedIn: $PropertyType, -|} +interface SP { + canClearHintDismissals: Props['canClearHintDismissals'] + hasOptedIn: Props['hasOptedIn'] +} -function SettingsAppComponent(props: Props) { +function SettingsAppComponent(props: Props): JSX.Element { const { canClearHintDismissals, hasOptedIn, @@ -115,7 +114,7 @@ function mapStateToProps(state: BaseState): SP { function mergeProps( stateProps: SP, - dispatchProps: { dispatch: ThunkDispatch<*> } + dispatchProps: { dispatch: ThunkDispatch } ): Props { const { dispatch } = dispatchProps const { hasOptedIn } = stateProps @@ -130,15 +129,9 @@ function mergeProps( } } -export const SettingsApp: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - {||}, - BaseState, - _ ->( +export const SettingsApp = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(SettingsAppComponent) diff --git a/protocol-designer/src/components/SettingsPage/SettingsSidebar.tsx b/protocol-designer/src/components/SettingsPage/SettingsSidebar.tsx index 6a59c6a3f6c..b901e0740f2 100644 --- a/protocol-designer/src/components/SettingsPage/SettingsSidebar.tsx +++ b/protocol-designer/src/components/SettingsPage/SettingsSidebar.tsx @@ -1,19 +1,22 @@ -// @flow import * as React from 'react' import { SidePanel } from '@opentrons/components' import { connect } from 'react-redux' -import type { BaseState, ThunkDispatch } from '../../types' -import { actions, selectors, type Page } from '../../navigation' +import { BaseState, ThunkDispatch } from '../../types' +import { actions, selectors, Page } from '../../navigation' import { i18n } from '../../localization' import { PDTitledList } from '../lists' import styles from './SettingsPage.css' -type SP = {| currentPage: Page |} -type DP = {| makeNavigateToPage: Page => () => mixed |} -type Props = { ...SP, ...DP } +interface SP { + currentPage: Page +} +interface DP { + makeNavigateToPage: (page: Page) => () => unknown +} +type Props = SP & DP -const SettingsSidebarComponent = (props: Props) => ( +const SettingsSidebarComponent = (props: Props): JSX.Element => ( ({ currentPage: selectors.getCurrentPage(state), }) -const DTP = (dispatch: ThunkDispatch<*>): DP => ({ +const DTP = (dispatch: ThunkDispatch): DP => ({ makeNavigateToPage: (pageName: Page) => () => dispatch(actions.navigateToPage(pageName)), }) -export const SettingsSidebar: React.AbstractComponent<{||}> = connect< - Props, - {||}, - SP, - DP, - _, - _ ->( - STP, - DTP -)(SettingsSidebarComponent) +export const SettingsSidebar = connect(STP, DTP)(SettingsSidebarComponent) diff --git a/protocol-designer/src/components/SettingsPage/index.tsx b/protocol-designer/src/components/SettingsPage/index.tsx index e27ef064f34..74a55b434fa 100644 --- a/protocol-designer/src/components/SettingsPage/index.tsx +++ b/protocol-designer/src/components/SettingsPage/index.tsx @@ -1,16 +1,17 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' -import type { BaseState } from '../../types' -import { selectors, type Page } from '../../navigation' +import { BaseState } from '../../types' +import { selectors, Page } from '../../navigation' import { SettingsApp } from './SettingsApp' export { SettingsSidebar } from './SettingsSidebar' -type Props = { currentPage: Page } +interface Props { + currentPage: Page +} -const SettingsPageComponent = (props: Props) => { +const SettingsPageComponent = (props: Props): JSX.Element => { switch (props.currentPage) { // TODO: Ian 2019-08-21 when we have other pages, put them here case 'settings-app': @@ -19,15 +20,8 @@ const SettingsPageComponent = (props: Props) => { } } -const STP = (state: BaseState): $Exact => ({ +const STP = (state: BaseState): Props => ({ currentPage: selectors.getCurrentPage(state), }) -export const SettingsPage: React.AbstractComponent<{||}> = connect< - Props, - {||}, - $Exact, - _, - _, - _ ->(STP)(SettingsPageComponent) +export const SettingsPage = connect(STP)(SettingsPageComponent) diff --git a/protocol-designer/src/components/StepCreationButton.tsx b/protocol-designer/src/components/StepCreationButton.tsx index 8e8cd356db1..910adc5d5d0 100644 --- a/protocol-designer/src/components/StepCreationButton.tsx +++ b/protocol-designer/src/components/StepCreationButton.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useDispatch, useSelector } from 'react-redux' import cx from 'classnames' @@ -26,29 +25,24 @@ import { CLOSE_UNSAVED_STEP_FORM, } from './modals/ConfirmDeleteModal' import { Portal } from './portals/MainPageModalPortal' -import { stepIconsByType, type StepType } from '../form-types' +import { stepIconsByType, StepType } from '../form-types' import styles from './listButtons.css' -type StepButtonComponentProps = {| - children: React.Node, - expanded: boolean, - disabled: boolean, - setExpanded: boolean => mixed, -|} +interface StepButtonComponentProps { + children: React.ReactNode + expanded: boolean + disabled: boolean + setExpanded: (expanded: boolean) => unknown +} // TODO: Ian 2019-01-17 move out to centralized step info file - see #2926 -const getSupportedSteps = () => [ - 'moveLiquid', - 'mix', - 'pause', - 'magnet', - 'temperature', - 'thermocycler', -] +const getSupportedSteps = (): Array< + Exclude +> => ['moveLiquid', 'mix', 'pause', 'magnet', 'temperature', 'thermocycler'] export const StepCreationButtonComponent = ( props: StepButtonComponentProps -): React.Node => { +): JSX.Element => { const { children, expanded, setExpanded, disabled } = props const [targetProps, tooltipProps] = useHoverTooltip({ placement: TOOLTIP_TOP, @@ -78,13 +72,13 @@ export const StepCreationButtonComponent = ( ) } -export type StepButtonItemProps = {| - onClick: () => mixed, - disabled: boolean, - stepType: StepType, -|} +export interface StepButtonItemProps { + onClick: () => unknown + disabled: boolean + stepType: StepType +} -export function StepButtonItem(props: StepButtonItemProps): React.Node { +export function StepButtonItem(props: StepButtonItemProps): JSX.Element { const { onClick, disabled, stepType } = props const [targetProps, tooltipProps] = useHoverTooltip({ placement: TOOLTIP_RIGHT, @@ -110,7 +104,7 @@ export function StepButtonItem(props: StepButtonItemProps): React.Node { ) } -export const StepCreationButton = (): React.Node => { +export const StepCreationButton = (): JSX.Element => { const currentFormIsPresaved = useSelector( stepFormSelectors.getCurrentFormIsPresaved ) @@ -119,7 +113,10 @@ export const StepCreationButton = (): React.Node => { ) const isStepCreationDisabled = useSelector(getIsMultiSelectMode) const modules = useSelector(stepFormSelectors.getInitialDeckSetup).modules - const isStepTypeEnabled = { + const isStepTypeEnabled: Record< + Exclude, + boolean + > = { moveLiquid: true, mix: true, pause: true, @@ -135,7 +132,9 @@ export const StepCreationButton = (): React.Node => { ] = React.useState(null) const dispatch = useDispatch() - const addStep = (stepType: StepType) => + const addStep = ( + stepType: StepType + ): ReturnType => dispatch(stepsActions.addAndSelectStepWithHints({ stepType })) const items = getSupportedSteps().map(stepType => ( diff --git a/protocol-designer/src/components/StepEditForm/ButtonRow/index.tsx b/protocol-designer/src/components/StepEditForm/ButtonRow/index.tsx index 194e83b9358..b0bab7b8831 100644 --- a/protocol-designer/src/components/StepEditForm/ButtonRow/index.tsx +++ b/protocol-designer/src/components/StepEditForm/ButtonRow/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { OutlineButton, PrimaryButton } from '@opentrons/components' @@ -7,15 +6,15 @@ import { i18n } from '../../../localization' import modalStyles from '../../modals/modal.css' import styles from './styles.css' -type ButtonRowProps = {| - handleClickMoreOptions: () => mixed, - handleClose: () => mixed, - handleSave: () => mixed, - handleDelete: () => mixed, - canSave: boolean, -|} +interface ButtonRowProps { + handleClickMoreOptions: () => unknown + handleClose: () => unknown + handleSave: () => unknown + handleDelete: () => unknown + canSave: boolean +} -export const ButtonRow = (props: ButtonRowProps): React.Node => { +export const ButtonRow = (props: ButtonRowProps): JSX.Element => { const { handleDelete, handleClickMoreOptions, diff --git a/protocol-designer/src/components/StepEditForm/FormAlerts.ts b/protocol-designer/src/components/StepEditForm/FormAlerts.ts index c619f4d326a..0c073b090b5 100644 --- a/protocol-designer/src/components/StepEditForm/FormAlerts.ts +++ b/protocol-designer/src/components/StepEditForm/FormAlerts.ts @@ -1,7 +1,5 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' -import { Alerts, type Props } from '../alerts/Alerts' +import { Props, Alerts } from '../alerts/Alerts' import { actions as dismissActions, selectors as dismissSelectors, @@ -13,25 +11,24 @@ import { getVisibleFormWarnings, getVisibleProfileFormLevelErrors, } from './utils' -import type { Dispatch } from 'redux' -import type { StepIdType } from '../../form-types' -import type { StepFieldName } from '../../steplist/fieldLevel' -import type { BaseState } from '../../types' +import { Dispatch } from 'redux' +import { StepIdType } from '../../form-types' +import { StepFieldName } from '../../steplist/fieldLevel' +import { BaseState } from '../../types' +import { ProfileFormError } from '../../steplist/formLevel/profileErrors' /* TODO: BC 2018-09-13 move to src/components/alerts and adapt and use src/components/alerts/Alerts * see #1814 for reference */ - -type SP = {| - errors: $PropertyType, - warnings: $PropertyType, - stepId?: ?StepIdType, -|} - -type OP = {| - focusedField: ?StepFieldName, - dirtyFields: Array, -|} +interface SP { + errors: Props['errors'] + warnings: Props['warnings'] + stepId?: StepIdType | null | undefined +} +interface OP { + focusedField: StepFieldName | null + dirtyFields: StepFieldName[] +} const mapStateToProps = (state: BaseState, ownProps: OP): SP => { const { focusedField, dirtyFields } = ownProps @@ -40,7 +37,6 @@ const mapStateToProps = (state: BaseState, ownProps: OP): SP => { dirtyFields, errors: dismissSelectors.getFormWarningsForSelectedStep(state), }) - const formLevelErrors = stepFormSelectors.getFormLevelErrorsForUnsavedForm( state ) @@ -49,10 +45,10 @@ const mapStateToProps = (state: BaseState, ownProps: OP): SP => { dirtyFields, errors: formLevelErrors, }) - // deal with special-case dynamic field form-level errors const { profileItemsById } = stepFormSelectors.getHydratedUnsavedForm(state) - let visibleDynamicFieldFormErrors = [] + let visibleDynamicFieldFormErrors: ProfileFormError[] = [] + if (profileItemsById != null) { const dynamicFieldFormErrors = stepFormSelectors.getDynamicFieldFormErrorsForUnsavedForm( state @@ -87,7 +83,9 @@ const mapStateToProps = (state: BaseState, ownProps: OP): SP => { const mergeProps = ( stateProps: SP, - dispatchProps: { dispatch: Dispatch<*> } + dispatchProps: { + dispatch: Dispatch + } ): Props => { const { stepId } = stateProps const { dispatch } = dispatchProps @@ -95,20 +93,19 @@ const mergeProps = ( ...stateProps, dismissWarning: (dismissId: string) => { if (stepId) - dispatch(dismissActions.dismissFormWarning({ type: dismissId, stepId })) + dispatch( + dismissActions.dismissFormWarning({ + type: dismissId, + stepId, + }) + ) }, } } -export const FormAlerts: React.AbstractComponent = connect< - Props, - OP, - SP, - {||}, - _, - _ ->( +export const FormAlerts = connect( mapStateToProps, + // @ts-expect-error(sa, 2021-6-21): TODO: refactor to use hooks api null, mergeProps )(Alerts) diff --git a/protocol-designer/src/components/StepEditForm/StepEditFormComponent.tsx b/protocol-designer/src/components/StepEditForm/StepEditFormComponent.tsx index c9d3bfb5af7..3efb5416a2f 100644 --- a/protocol-designer/src/components/StepEditForm/StepEditFormComponent.tsx +++ b/protocol-designer/src/components/StepEditForm/StepEditFormComponent.tsx @@ -1,7 +1,7 @@ -// @flow import * as React from 'react' import cx from 'classnames' import get from 'lodash/get' +import { StepFieldName } from '../../steplist/fieldLevel' import { MoreOptionsModal } from '../modals/MoreOptionsModal' import { MixForm, @@ -15,10 +15,14 @@ import { FormAlerts } from './FormAlerts' import { ButtonRow } from './ButtonRow' import formStyles from '../forms/forms.css' import styles from './StepEditForm.css' -import type { FormData, StepType } from '../../form-types' -import type { FieldPropsByName, FocusHandlers, StepFormProps } from './types' +import { FormData, StepType } from '../../form-types' +import { FieldPropsByName, FocusHandlers, StepFormProps } from './types' -const STEP_FORM_MAP: { [StepType]: ?React.ComponentType } = { +type StepFormMap = { + [K in StepType]?: React.ComponentType | null +} + +const STEP_FORM_MAP: StepFormMap = { mix: MixForm, pause: PauseForm, moveLiquid: MoveLiquidForm, @@ -27,21 +31,21 @@ const STEP_FORM_MAP: { [StepType]: ?React.ComponentType } = { thermocycler: ThermocyclerForm, } -type Props = {| - canSave: boolean, - dirtyFields: Array, - focusHandlers: FocusHandlers, - focusedField: string | null, - formData: FormData, - propsForFields: FieldPropsByName, - handleClose: () => mixed, - handleDelete: () => mixed, - handleSave: () => mixed, - showMoreOptionsModal: boolean, - toggleMoreOptionsModal: () => mixed, -|} +interface Props { + canSave: boolean + dirtyFields: string[] + focusHandlers: FocusHandlers + focusedField: StepFieldName | null + formData: FormData + propsForFields: FieldPropsByName + handleClose: () => unknown + handleDelete: () => unknown + handleSave: () => unknown + showMoreOptionsModal: boolean + toggleMoreOptionsModal: () => unknown +} -export const StepEditFormComponent = (props: Props): React.Node => { +export const StepEditFormComponent = (props: Props): JSX.Element => { const { formData, focusHandlers, @@ -56,7 +60,7 @@ export const StepEditFormComponent = (props: Props): React.Node => { focusedField, } = props - const FormComponent: $Values = get( + const FormComponent: typeof STEP_FORM_MAP[keyof typeof STEP_FORM_MAP] = get( STEP_FORM_MAP, formData.stepType ) @@ -74,6 +78,7 @@ export const StepEditFormComponent = (props: Props): React.Node => { {showMoreOptionsModal && ( )} + {/* @ts-expect-error(ce, 2021-06-22) getting into the weeds of `connect` and props and not sure what is going on */}

diff --git a/protocol-designer/src/components/StepEditForm/__tests__/utils.test.ts b/protocol-designer/src/components/StepEditForm/__tests__/utils.test.ts index 0ebf6e108de..f6d84763ce2 100644 --- a/protocol-designer/src/components/StepEditForm/__tests__/utils.test.ts +++ b/protocol-designer/src/components/StepEditForm/__tests__/utils.test.ts @@ -1,15 +1,15 @@ -// @flow import { SOURCE_WELL_BLOWOUT_DESTINATION, DEST_WELL_BLOWOUT_DESTINATION, } from '@opentrons/step-generation' +import { DropdownOption } from '@opentrons/components' import { getBlowoutLocationOptionsForForm } from '../utils' describe('getBlowoutLocationOptionsForForm', () => { - let destOption - let sourceOption - let disabledSourceOption - let disabledDestOption + let destOption: DropdownOption + let sourceOption: DropdownOption + let disabledSourceOption: DropdownOption + let disabledDestOption: DropdownOption const sourceName = 'Source Well' const destName = 'Destination Well' diff --git a/protocol-designer/src/components/StepEditForm/fields/BlowoutLocationField.tsx b/protocol-designer/src/components/StepEditForm/fields/BlowoutLocationField.tsx index 6bddfb7aa81..a37615eee50 100644 --- a/protocol-designer/src/components/StepEditForm/fields/BlowoutLocationField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/BlowoutLocationField.tsx @@ -1,21 +1,19 @@ -// @flow import * as React from 'react' import { useSelector } from 'react-redux' -import { DropdownField, type Options } from '@opentrons/components' +import { DropdownField, Options } from '@opentrons/components' import cx from 'classnames' import { selectors as uiLabwareSelectors } from '../../../ui/labware' import styles from '../StepEditForm.css' -import type { FieldProps } from '../types' +import { FieldProps } from '../types' -type BlowoutLocationDropdownProps = {| - ...FieldProps, - className?: string, - options: Options, -|} +type BlowoutLocationDropdownProps = FieldProps & { + className?: string + options: Options +} export const BlowoutLocationField = ( props: BlowoutLocationDropdownProps -): React.Node => { +): JSX.Element => { const { className, disabled, @@ -39,7 +37,7 @@ export const BlowoutLocationField = ( onBlur={onFieldBlur} onFocus={onFieldFocus} value={value ? String(value) : null} - onChange={(e: SyntheticEvent) => { + onChange={(e: React.ChangeEvent) => { updateValue(e.currentTarget.value) }} /> diff --git a/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/getDisabledChangeTipOptions.ts b/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/getDisabledChangeTipOptions.ts index bb2df032388..1dca83bb557 100644 --- a/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/getDisabledChangeTipOptions.ts +++ b/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/getDisabledChangeTipOptions.ts @@ -1,36 +1,38 @@ -// @flow import { getWellRatio } from '../../../../steplist/utils' - -import type { PathOption, StepType } from '../../../../form-types' -import type { ChangeTipOptions } from '@opentrons/step-generation' - -export type DisabledChangeTipArgs = {| - aspirateWells?: Array, - dispenseWells?: Array, - stepType?: StepType, - path?: ?PathOption, -|} - +import { PathOption, StepType } from '../../../../form-types' +import { ChangeTipOptions } from '@opentrons/step-generation' +export interface DisabledChangeTipArgs { + aspirateWells?: string[] + dispenseWells?: string[] + stepType?: StepType + path?: PathOption | null | undefined +} export const getDisabledChangeTipOptions = ( args: DisabledChangeTipArgs -): ?Set => { +): Set | null | undefined => { const { path, aspirateWells, dispenseWells, stepType } = args + switch (stepType) { case 'moveLiquid': { const wellRatio = getWellRatio(aspirateWells, dispenseWells) + // form with no wells selected treated as 'single' if (!wellRatio || !path || path === 'single') { if (wellRatio === '1:many') { return new Set(['perSource']) } + return new Set(['perDest']) } + // path is multi return new Set(['perSource', 'perDest']) } + case 'mix': { return new Set(['perSource', 'perDest']) } + default: { console.warn( `getChangeTipOptions for stepType ${String( diff --git a/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/index.tsx b/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/index.tsx index 20953aaca8a..758f6838a4d 100644 --- a/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/index.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/ChangeTipField/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { FormGroup, @@ -10,25 +9,22 @@ import { import { i18n } from '../../../../localization' import { getDisabledChangeTipOptions, - type DisabledChangeTipArgs, + DisabledChangeTipArgs, } from './getDisabledChangeTipOptions' -import type { ChangeTipOptions } from '@opentrons/step-generation' -import type { FieldProps } from '../../types' +import { ChangeTipOptions } from '@opentrons/step-generation' +import { FieldProps } from '../../types' import styles from '../../StepEditForm.css' -const ALL_CHANGE_TIP_VALUES: Array = [ +const ALL_CHANGE_TIP_VALUES: ChangeTipOptions[] = [ 'always', 'once', 'perSource', 'perDest', 'never', ] -type Props = {| - ...FieldProps, - ...DisabledChangeTipArgs, -|} +type Props = FieldProps & DisabledChangeTipArgs -export const ChangeTipField = (props: Props): React.Node => { +export const ChangeTipField = (props: Props): JSX.Element => { const { aspirateWells, dispenseWells, @@ -69,11 +65,11 @@ export const ChangeTipField = (props: Props): React.Node => { ) } -type LabelProps = { - value: string, +interface LabelProps { + value: string } -const ChangeTipOptionLabel = (props: LabelProps) => { +const ChangeTipOptionLabel = (props: LabelProps): JSX.Element => { const { value } = props const [targetProps, tooltipProps] = useHoverTooltip({ placement: 'bottom-start', diff --git a/protocol-designer/src/components/StepEditForm/fields/CheckboxRowField.tsx b/protocol-designer/src/components/StepEditForm/fields/CheckboxRowField.tsx index 5a8d0d1732f..c5a92b0c228 100644 --- a/protocol-designer/src/components/StepEditForm/fields/CheckboxRowField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/CheckboxRowField.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { CheckboxField, @@ -8,17 +7,16 @@ import { } from '@opentrons/components' import cx from 'classnames' import styles from '../StepEditForm.css' -import type { FieldProps } from '../types' +import { FieldProps } from '../types' -type CheckboxRowProps = {| - ...FieldProps, - children?: React.Node, - className?: string, - label?: string, - tooltipContent?: React.Node, -|} +type CheckboxRowProps = FieldProps & { + children?: React.ReactNode + className?: string + label?: string + tooltipContent?: React.ReactNode +} -export const CheckboxRowField = (props: CheckboxRowProps): React.Node => { +export const CheckboxRowField = (props: CheckboxRowProps): JSX.Element => { const { children, className, @@ -48,7 +46,9 @@ export const CheckboxRowField = (props: CheckboxRowProps): React.Node => { label={label} labelProps={targetProps} name={name} - onChange={(e: SyntheticInputEvent<*>) => updateValue(!value)} + onChange={(e: React.ChangeEvent) => + updateValue(!value) + } value={Boolean(value)} /> {value && !disabled && !isIndeterminate ? children : null} diff --git a/protocol-designer/src/components/StepEditForm/fields/DelayFields.tsx b/protocol-designer/src/components/StepEditForm/fields/DelayFields.tsx index cbea323a9fd..8a82c779662 100644 --- a/protocol-designer/src/components/StepEditForm/fields/DelayFields.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/DelayFields.tsx @@ -1,22 +1,21 @@ -// @flow import * as React from 'react' import { i18n } from '../../../localization' import { TextField } from './TextField' import { CheckboxRowField } from './CheckboxRowField' import { TipPositionField } from './TipPositionField' import styles from '../StepEditForm.css' -import type { FieldPropsByName } from '../types' -import type { StepFieldName } from '../../../form-types' +import { FieldPropsByName } from '../types' +import { StepFieldName } from '../../../form-types' -export type DelayFieldProps = {| - checkboxFieldName: StepFieldName, // TODO(IL, 2021-03-03): strictly, could be DelayCheckboxFields! - labwareId: ?string, - propsForFields: FieldPropsByName, - secondsFieldName: StepFieldName, // TODO(IL, 2021-03-03): strictly, could be DelaySecondFields! - tipPositionFieldName?: StepFieldName, // TODO(IL, 2021-03-03): strictly, could be TipOffsetFields! -|} +export interface DelayFieldProps { + checkboxFieldName: StepFieldName // TODO(IL, 2021-03-03): strictly, could be DelayCheckboxFields! + labwareId?: string | null + propsForFields: FieldPropsByName + secondsFieldName: StepFieldName // TODO(IL, 2021-03-03): strictly, could be DelaySecondFields! + tipPositionFieldName?: StepFieldName // TODO(IL, 2021-03-03): strictly, could be TipOffsetFields! +} -export const DelayFields = (props: DelayFieldProps): React.Node => { +export const DelayFields = (props: DelayFieldProps): JSX.Element => { const { checkboxFieldName, secondsFieldName, diff --git a/protocol-designer/src/components/StepEditForm/fields/DisposalVolumeField.tsx b/protocol-designer/src/components/StepEditForm/fields/DisposalVolumeField.tsx index 55d3476865f..cf785600fe9 100644 --- a/protocol-designer/src/components/StepEditForm/fields/DisposalVolumeField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/DisposalVolumeField.tsx @@ -1,10 +1,9 @@ -// @flow import * as React from 'react' import { FormGroup, CheckboxField, DropdownField, - type Options, + Options, } from '@opentrons/components' import { connect } from 'react-redux' import cx from 'classnames' @@ -16,18 +15,17 @@ import { selectors as uiLabwareSelectors } from '../../../ui/labware' import { getBlowoutLocationOptionsForForm } from '../utils' import { TextField } from './TextField' -import type { FieldProps, FieldPropsByName } from '../types' -import type { PathOption, StepType } from '../../../form-types' -import type { BaseState } from '../../../types' +import { FieldProps, FieldPropsByName } from '../types' +import { PathOption, StepType } from '../../../form-types' +import { BaseState } from '../../../types' import styles from '../StepEditForm.css' -type DropdownFormFieldProps = {| - ...FieldProps, - className?: string, - options: Options, -|} -const DropdownFormField = (props: DropdownFormFieldProps) => { +interface DropdownFormFieldProps extends FieldProps { + className?: string + options: Options +} +const DropdownFormField = (props: DropdownFormFieldProps): JSX.Element => { return ( { ) } -type SP = {| - disposalDestinationOptions: Options, - maxDisposalVolume: ?number, -|} -type OP = {| - aspirate_airGap_checkbox?: boolean | null, - aspirate_airGap_volume?: string | null, - path: PathOption, - pipette: string | null, - propsForFields: FieldPropsByName, - stepType: StepType, - volume: string | null, -|} -type Props = { ...SP, ...OP } +interface SP { + disposalDestinationOptions: Options + maxDisposalVolume?: number | null +} +interface OP { + aspirate_airGap_checkbox?: boolean | null + aspirate_airGap_volume?: string | null + path: PathOption + pipette: string | null + propsForFields: FieldPropsByName + stepType: StepType + volume: string | null +} +type Props = SP & OP -const DisposalVolumeFieldComponent = (props: Props) => { +const DisposalVolumeFieldComponent = (props: Props): JSX.Element => { const { propsForFields } = props const { maxDisposalVolume } = props @@ -81,6 +79,7 @@ const DisposalVolumeFieldComponent = (props: Props) => { <>
{ label="Disposal Volume" value={Boolean(value)} className={cx(styles.checkbox_field, styles.large_field)} - onChange={(e: SyntheticInputEvent<*>) => updateValue(!value)} + onChange={(e: React.ChangeEvent) => updateValue(!value)} /> {value ? volumeField : null}
@@ -146,11 +145,4 @@ const mapSTP = (state: BaseState, ownProps: OP): SP => { } } -export const DisposalVolumeField: React.AbstractComponent = connect< - Props, - OP, - SP, - _, - _, - _ ->(mapSTP)(DisposalVolumeFieldComponent) +export const DisposalVolumeField = connect(mapSTP)(DisposalVolumeFieldComponent) diff --git a/protocol-designer/src/components/StepEditForm/fields/FlowRateField/FlowRateInput.tsx b/protocol-designer/src/components/StepEditForm/fields/FlowRateField/FlowRateInput.tsx index 47c27a5f31a..07ea4d10e8c 100644 --- a/protocol-designer/src/components/StepEditForm/fields/FlowRateField/FlowRateInput.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/FlowRateField/FlowRateInput.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import round from 'lodash/round' import { @@ -12,31 +11,30 @@ import { Portal } from '../../../portals/MainPageModalPortal' import modalStyles from '../../../modals/modal.css' import stepFormStyles from '../../StepEditForm.css' import styles from './FlowRateInput.css' -import type { FieldProps } from '../../types' +import { FieldProps } from '../../types' const DEFAULT_LABEL = i18n.t('form.step_edit_form.field.flow_rate.label') const DECIMALS_ALLOWED = 1 /** When flow rate is falsey (including 0), it means 'use default' */ -export type FlowRateInputProps = {| - ...FieldProps, - defaultFlowRate: ?number, - flowRateType: 'aspirate' | 'dispense', - label: ?string, - minFlowRate: number, - maxFlowRate: number, - pipetteDisplayName: ?string, - className?: string, -|} - -type State = {| - isPristine: boolean, - modalFlowRate: ?string, - modalUseDefault: boolean, - showModal: boolean, -|} - -export const FlowRateInput = (props: FlowRateInputProps): React.Node => { +export interface FlowRateInputProps extends FieldProps { + defaultFlowRate?: number | null + flowRateType: 'aspirate' | 'dispense' + label?: string | null + minFlowRate: number + maxFlowRate: number + pipetteDisplayName?: string | null + className?: string +} + +interface State { + isPristine: boolean + modalFlowRate?: string | null + modalUseDefault: boolean + showModal: boolean +} + +export const FlowRateInput = (props: FlowRateInputProps): JSX.Element => { const { className, defaultFlowRate, @@ -57,21 +55,21 @@ export const FlowRateInput = (props: FlowRateInputProps): React.Node => { showModal: false, } - const [isPristine, setIsPristine] = React.useState< - $PropertyType - >(initialState.isPristine) + const [isPristine, setIsPristine] = React.useState( + initialState.isPristine + ) const [modalFlowRate, setModalFlowRate] = React.useState< - $PropertyType + State['modalFlowRate'] >(initialState.modalFlowRate) const [modalUseDefault, setModalUseDefault] = React.useState< - $PropertyType + State['modalUseDefault'] >(initialState.modalUseDefault) - const [showModal, setShowModal] = React.useState< - $PropertyType - >(initialState.showModal) + const [showModal, setShowModal] = React.useState( + initialState.showModal + ) const resetModalState = (): void => { setShowModal(initialState.showModal) @@ -96,11 +94,11 @@ export const FlowRateInput = (props: FlowRateInputProps): React.Node => { } } - const handleChangeRadio = (e: SyntheticInputEvent<>): void => { + const handleChangeRadio = (e: React.ChangeEvent): void => { setModalUseDefault(e.target.value !== 'custom') } - const handleChangeNumber = (e: SyntheticInputEvent<>) => { + const handleChangeNumber = (e: React.ChangeEvent): void => { const value = e.target.value if (value === '' || value === '.' || !Number.isNaN(Number(value))) { setModalFlowRate(value) diff --git a/protocol-designer/src/components/StepEditForm/fields/FlowRateField/index.tsx b/protocol-designer/src/components/StepEditForm/fields/FlowRateField/index.tsx index fd73e9c9aa0..4a3080c01cb 100644 --- a/protocol-designer/src/components/StepEditForm/fields/FlowRateField/index.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/FlowRateField/index.tsx @@ -1,34 +1,31 @@ -// @flow import * as React from 'react' -import { FlowRateInput, type FlowRateInputProps } from './FlowRateInput' +import { FlowRateInput, FlowRateInputProps } from './FlowRateInput' import { connect } from 'react-redux' import { selectors as stepFormSelectors } from '../../../../step-forms' -import type { FieldProps } from '../../types' -import type { BaseState } from '../../../../types' +import { FieldProps } from '../../types' +import { BaseState } from '../../../../types' -type OP = {| - ...FieldProps, - pipetteId: ?string, - className?: $PropertyType, - flowRateType: $PropertyType, - label?: $PropertyType, -|} +interface OP extends FieldProps { + pipetteId?: string | null + className?: FlowRateInputProps['className'] + flowRateType: FlowRateInputProps['flowRateType'] + label?: FlowRateInputProps['label'] +} -type SP = {| - innerKey: string, - defaultFlowRate: ?number, - minFlowRate: number, - maxFlowRate: number, - pipetteDisplayName: string, -|} +interface SP { + innerKey: string + defaultFlowRate?: number | null + minFlowRate: number + maxFlowRate: number + pipetteDisplayName: string +} -type Props = {| - ...FlowRateInputProps, - innerKey: string, -|} +interface Props extends FlowRateInputProps { + innerKey: string +} // Add a key to force re-constructing component when values change -function FlowRateInputWithKey(props: Props) { +function FlowRateInputWithKey(props: Props): JSX.Element { const { innerKey, ...otherProps } = props return } @@ -64,19 +61,16 @@ function mapStateToProps(state: BaseState, ownProps: OP): SP { } } -const mergeProps = (stateProps: SP, dispatchProps, ownProps: OP): Props => { +const mergeProps = ( + stateProps: SP, + _dispatchProps: null, + ownProps: OP +): Props => { const { pipetteId, ...passThruProps } = ownProps return { ...stateProps, ...passThruProps } } -export const FlowRateField: React.AbstractComponent = connect< - Props, - OP, - SP, - {||}, - _, - _ ->( +export const FlowRateField = connect( mapStateToProps, null, mergeProps diff --git a/protocol-designer/src/components/StepEditForm/fields/LabwareField.ts b/protocol-designer/src/components/StepEditForm/fields/LabwareField.ts index 0521c974ed7..d008a054d4f 100644 --- a/protocol-designer/src/components/StepEditForm/fields/LabwareField.ts +++ b/protocol-designer/src/components/StepEditForm/fields/LabwareField.ts @@ -1,34 +1,15 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' - import { selectors as uiLabwareSelectors } from '../../../ui/labware' -import { - StepFormDropdown, - type StepFormDropdownProps, -} from './StepFormDropdownField' -import type { Options } from '@opentrons/components' -import type { StepFieldName } from '../../../steplist/fieldLevel' -import type { BaseState } from '../../../types' -import type { FieldProps } from '../types' - -type OP = {| - ...FieldProps, - name: StepFieldName, - className?: string, -|} +import { StepFormDropdown } from './StepFormDropdownField' +import { Options } from '@opentrons/components' +import { BaseState } from '../../../types' -type SP = {| options: Options |} +interface SP { + options: Options +} const mapSTP = (state: BaseState): SP => ({ options: uiLabwareSelectors.getLabwareOptions(state), }) -export const LabwareField: React.AbstractComponent = connect< - StepFormDropdownProps, - OP, - SP, - _, - _, - _ ->(mapSTP)(StepFormDropdown) +export const LabwareField = connect(mapSTP)(StepFormDropdown) diff --git a/protocol-designer/src/components/StepEditForm/fields/MixFields.tsx b/protocol-designer/src/components/StepEditForm/fields/MixFields.tsx index f150939a3d9..1ebfcd8b8d8 100644 --- a/protocol-designer/src/components/StepEditForm/fields/MixFields.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/MixFields.tsx @@ -1,16 +1,15 @@ -// @flow import * as React from 'react' import { i18n } from '../../../localization' import { CheckboxRowField, TextField } from './' -import type { FieldPropsByName } from '../types' +import { FieldPropsByName } from '../types' import styles from '../StepEditForm.css' -export const MixFields = (props: {| - propsForFields: FieldPropsByName, - checkboxFieldName: string, - volumeFieldName: string, - timesFieldName: string, -|}): React.Node => { +export const MixFields = (props: { + propsForFields: FieldPropsByName + checkboxFieldName: string + volumeFieldName: string + timesFieldName: string +}): JSX.Element => { const { propsForFields, checkboxFieldName, diff --git a/protocol-designer/src/components/StepEditForm/fields/PathField/Path.tsx b/protocol-designer/src/components/StepEditForm/fields/PathField/Path.tsx index d04d6e21fbe..d8108997074 100644 --- a/protocol-designer/src/components/StepEditForm/fields/PathField/Path.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/PathField/Path.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { FormGroup, Tooltip, useHoverTooltip } from '@opentrons/components' @@ -6,9 +5,9 @@ import { i18n } from '../../../../localization' import SINGLE_IMAGE from '../../../../images/path_single_transfers.svg' import MULTI_DISPENSE_IMAGE from '../../../../images/path_multi_dispense.svg' import MULTI_ASPIRATE_IMAGE from '../../../../images/path_multi_aspirate.svg' -import type { PathOption } from '../../../../form-types' -import type { FieldProps } from '../../types' -import type { DisabledPathMap, ValuesForPath } from './getDisabledPathMap' +import { PathOption } from '../../../../form-types' +import { FieldProps } from '../../types' +import { DisabledPathMap, ValuesForPath } from './getDisabledPathMap' import styles from '../../StepEditForm.css' const PATH_ANIMATION_IMAGES = { @@ -17,7 +16,7 @@ const PATH_ANIMATION_IMAGES = { multiDispense: require('../../../../images/path_multiDispense.gif'), } -const ALL_PATH_OPTIONS = [ +const ALL_PATH_OPTIONS: Array<{ name: PathOption; image: string }> = [ { name: 'single', image: SINGLE_IMAGE, @@ -32,23 +31,22 @@ const ALL_PATH_OPTIONS = [ }, ] -type PathFieldProps = {| - ...FieldProps, - ...ValuesForPath, - disabledPathMap: DisabledPathMap, -|} +type PathFieldProps = FieldProps & + ValuesForPath & { + disabledPathMap: DisabledPathMap + } -type ButtonProps = { - children?: React.Node, - disabled: boolean, - id?: string, - selected: boolean, - subtitle: string, - onClick: (e: SyntheticMouseEvent<*>) => mixed, - path: PathOption, +interface ButtonProps { + children?: React.ReactNode + disabled: boolean + id?: string + selected: boolean + subtitle: string + onClick: (e: React.MouseEvent) => unknown + path: PathOption } -const PathButton = (buttonProps: ButtonProps) => { +const PathButton = (buttonProps: ButtonProps): JSX.Element => { const { children, disabled, @@ -86,6 +84,7 @@ const PathButton = (buttonProps: ButtonProps) => { [styles.selected]: selected, [styles.disabled]: disabled, })} + // @ts-expect-error(sa, 2021-6-22): null is not a valid onClick handler onClick={disabled ? null : onClick} id={id} data-test={pathButtonData} @@ -104,7 +103,7 @@ const getSubtitle = ( return reasonForDisabled || '' } -export const Path = (props: PathFieldProps): React.Node => { +export const Path = (props: PathFieldProps): JSX.Element => { const { disabledPathMap, value, updateValue } = props return ( diff --git a/protocol-designer/src/components/StepEditForm/fields/PathField/getDisabledPathMap.ts b/protocol-designer/src/components/StepEditForm/fields/PathField/getDisabledPathMap.ts index aa6b87ba305..a07c072d39d 100644 --- a/protocol-designer/src/components/StepEditForm/fields/PathField/getDisabledPathMap.ts +++ b/protocol-designer/src/components/StepEditForm/fields/PathField/getDisabledPathMap.ts @@ -1,4 +1,3 @@ -// @flow import { i18n } from '../../../../localization' import { getWellRatio } from '../../../../steplist/utils' import { getPipetteCapacity } from '../../../../pipettes/pipetteData' @@ -6,25 +5,18 @@ import { volumeInCapacityForMultiDispense, volumeInCapacityForMultiAspirate, } from '../../../../steplist/formLevel/handleFormChange/utils' - -import type { - ChangeTipOptions, - PipetteEntities, -} from '@opentrons/step-generation' -import type { PathOption } from '../../../../form-types' - -export type DisabledPathMap = { [PathOption]: string } | null - -export type ValuesForPath = {| - aspirate_airGap_checkbox: ?boolean, - aspirate_airGap_volume: ?string, - aspirate_wells: ?Array, - changeTip: ChangeTipOptions, - dispense_wells: ?Array, - pipette: ?string, - volume: ?string, -|} - +import { ChangeTipOptions, PipetteEntities } from '@opentrons/step-generation' +import { PathOption } from '../../../../form-types' +export type DisabledPathMap = Partial> | null +export interface ValuesForPath { + aspirate_airGap_checkbox?: boolean | null + aspirate_airGap_volume?: string | null + aspirate_wells?: string[] | null + changeTip: ChangeTipOptions + dispense_wells?: string[] | null + pipette?: string | null + volume?: string | null +} export function getDisabledPathMap( values: ValuesForPath, pipetteEntities: PipetteEntities @@ -36,12 +28,9 @@ export function getDisabledPathMap( dispense_wells, pipette, } = values - if (!pipette) return null - const wellRatio = getWellRatio(aspirate_wells, dispense_wells) - - let disabledPathMap: { multiAspirate: string, multiAspirate: string } = {} + let disabledPathMap: Partial> = {} // changeTip is lowest priority disable reasoning if (changeTip === 'perDest') { @@ -63,18 +52,15 @@ export function getDisabledPathMap( // transfer volume overwrites change tip disable reasoning const pipetteEntity = pipetteEntities[pipette] const pipetteCapacity = pipetteEntity && getPipetteCapacity(pipetteEntity) - const volume = Number(values.volume) const airGapChecked = aspirate_airGap_checkbox let airGapVolume = airGapChecked ? Number(values.aspirate_airGap_volume) : 0 airGapVolume = Number.isFinite(airGapVolume) ? airGapVolume : 0 - const withinCapacityForMultiDispense = volumeInCapacityForMultiDispense({ volume, pipetteCapacity, airGapVolume, }) - const withinCapacityForMultiAspirate = volumeInCapacityForMultiAspirate({ volume, pipetteCapacity, @@ -89,6 +75,7 @@ export function getDisabledPathMap( ), } } + if (!withinCapacityForMultiAspirate) { disabledPathMap = { ...disabledPathMap, @@ -124,6 +111,5 @@ export function getDisabledPathMap( ), } } - return disabledPathMap } diff --git a/protocol-designer/src/components/StepEditForm/fields/PathField/index.ts b/protocol-designer/src/components/StepEditForm/fields/PathField/index.ts index 3c7f110516d..8f22db5cbd3 100644 --- a/protocol-designer/src/components/StepEditForm/fields/PathField/index.ts +++ b/protocol-designer/src/components/StepEditForm/fields/PathField/index.ts @@ -1,14 +1,14 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { Path } from './Path' import { selectors as stepFormSelectors } from '../../../../step-forms' import { getDisabledPathMap } from './getDisabledPathMap' -import type { BaseState } from '../../../../types' - -type Props = React.ElementProps -type SP = {| disabledPathMap: $PropertyType |} -type OP = $Diff<$Exact, SP> +import { BaseState } from '../../../../types' +type Props = React.ComponentProps +interface SP { + disabledPathMap: Props['disabledPathMap'] +} +type OP = Omit function mapSTP(state: BaseState, ownProps: OP): SP { const { @@ -38,11 +38,4 @@ function mapSTP(state: BaseState, ownProps: OP): SP { } } -export const PathField: React.AbstractComponent = connect< - Props, - OP, - SP, - _, - _, - _ ->(mapSTP, () => ({}))(Path) +export const PathField = connect(mapSTP, () => ({}))(Path) diff --git a/protocol-designer/src/components/StepEditForm/fields/PipetteField.tsx b/protocol-designer/src/components/StepEditForm/fields/PipetteField.tsx index c88528668f2..9308cc1baaa 100644 --- a/protocol-designer/src/components/StepEditForm/fields/PipetteField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/PipetteField.tsx @@ -1,33 +1,25 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' -import { FormGroup, DropdownField, type Options } from '@opentrons/components' +import { FormGroup, DropdownField, Options } from '@opentrons/components' import { i18n } from '../../../localization' import { selectors as stepFormSelectors } from '../../../step-forms' -import type { BaseState } from '../../../types' +import { BaseState } from '../../../types' import styles from '../StepEditForm.css' -import type { FieldProps } from '../types' +import { FieldProps } from '../types' -type OP = {| - ...FieldProps, -|} +type OP = FieldProps -type SP = {| pipetteOptions: Options |} +interface SP { + pipetteOptions: Options +} -type Props = { ...OP, ...SP } +type Props = OP & SP const PipetteFieldSTP = (state: BaseState, ownProps: OP): SP => ({ pipetteOptions: stepFormSelectors.getEquippedPipetteOptions(state), }) -export const PipetteField: React.AbstractComponent = connect< - Props, - OP, - SP, - _, - _, - _ ->(PipetteFieldSTP)((props: Props) => { +export const PipetteField = connect(PipetteFieldSTP)((props: Props) => { const { onFieldBlur, onFieldFocus, updateValue, value } = props return ( @@ -41,7 +33,7 @@ export const PipetteField: React.AbstractComponent = connect< value={value ? String(value) : null} onBlur={onFieldBlur} onFocus={onFieldFocus} - onChange={(e: SyntheticEvent) => { + onChange={(e: React.ChangeEvent) => { updateValue(e.currentTarget.value) }} /> diff --git a/protocol-designer/src/components/StepEditForm/fields/ProfileItemRows.tsx b/protocol-designer/src/components/StepEditForm/fields/ProfileItemRows.tsx index 2d47ea133b2..86098c63b26 100644 --- a/protocol-designer/src/components/StepEditForm/fields/ProfileItemRows.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/ProfileItemRows.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { useDispatch } from 'react-redux' import cx from 'classnames' @@ -14,7 +13,12 @@ import { } from '@opentrons/components' import { i18n } from '../../../localization' import * as steplistActions from '../../../steplist/actions' -import { PROFILE_CYCLE } from '../../../form-types' +import { + PROFILE_CYCLE, + ProfileStepItem, + ProfileItem, + ProfileCycleItem, +} from '../../../form-types' import { getProfileFieldErrors, maskProfileField, @@ -25,39 +29,35 @@ import { } from '../../modals/ConfirmDeleteModal' import { getDynamicFieldFocusHandlerId } from '../utils' import styles from '../StepEditForm.css' -import type { - ProfileStepItem, - ProfileItem, - ProfileCycleItem, -} from '../../../form-types' -import type { FocusHandlers } from '../types' + +import { FocusHandlers } from '../types' export const showProfileFieldErrors = ({ fieldId, focusedField, dirtyFields, -}: {| - fieldId: string, - focusedField: ?string, - dirtyFields: Array, -|}): boolean => +}: { + fieldId: string + focusedField?: string | null + dirtyFields: string[] +}): boolean => !(fieldId === focusedField) && dirtyFields && dirtyFields.includes(fieldId) -type ProfileCycleRowProps = {| - cycleItem: ProfileCycleItem, - focusHandlers: FocusHandlers, - stepOffset: number, -|} -export const ProfileCycleRow = (props: ProfileCycleRowProps): React.Node => { +interface ProfileCycleRowProps { + cycleItem: ProfileCycleItem + focusHandlers: FocusHandlers + stepOffset: number +} +export const ProfileCycleRow = (props: ProfileCycleRowProps): JSX.Element => { const { cycleItem, focusHandlers, stepOffset } = props const dispatch = useDispatch() - const addStepToCycle = () => { + const addStepToCycle = (): void => { dispatch(steplistActions.addProfileStep({ cycleId: cycleItem.id })) } // TODO IMMEDIATELY make conditional - const deleteProfileCycle = () => + const deleteProfileCycle = (): steplistActions.DeleteProfileCycleAction => dispatch(steplistActions.deleteProfileCycle({ id: cycleItem.id })) const [ @@ -139,21 +139,24 @@ export const ProfileCycleRow = (props: ProfileCycleRowProps): React.Node => { ) } -export type ProfileItemRowsProps = {| - focusHandlers: FocusHandlers, - orderedProfileItems: Array, +export interface ProfileItemRowsProps { + focusHandlers: FocusHandlers + orderedProfileItems: string[] profileItemsById: { - [string]: ProfileItem, - ... - }, -|} + [key: string]: ProfileItem + } +} -export const ProfileItemRows = (props: ProfileItemRowsProps): React.Node => { +export const ProfileItemRows = (props: ProfileItemRowsProps): JSX.Element => { const { orderedProfileItems, profileItemsById } = props const dispatch = useDispatch() - const addProfileCycle = () => dispatch(steplistActions.addProfileCycle(null)) - const addProfileStep = () => dispatch(steplistActions.addProfileStep(null)) + const addProfileCycle = (): void => { + dispatch(steplistActions.addProfileCycle(null)) + } + const addProfileStep = (): void => { + dispatch(steplistActions.addProfileStep(null)) + } const [addCycleTargetProps, addCycleTooltipProps] = useHoverTooltip({ placement: TOOLTIP_TOP, @@ -230,15 +233,15 @@ export const ProfileItemRows = (props: ProfileItemRowsProps): React.Node => { ) } -type ProfileFieldProps = {| - name: string, - focusHandlers: FocusHandlers, - profileItem: ProfileItem, - units?: React.Node, - className?: string, - updateValue: (name: string, value: mixed) => mixed, -|} -const ProfileField = (props: ProfileFieldProps) => { +interface ProfileFieldProps { + name: string + focusHandlers: FocusHandlers + profileItem: ProfileItem + units?: React.ReactNode + className?: string + updateValue: (name: string, value: unknown) => unknown +} +const ProfileField = (props: ProfileFieldProps): JSX.Element => { const { focusHandlers, name, @@ -247,13 +250,13 @@ const ProfileField = (props: ProfileFieldProps) => { className, updateValue, } = props - const value = profileItem[name] + const value = profileItem[name as keyof ProfileItem] // this is not very safe but I don't know how else to tell TS that name should be keyof ProfileItem without being a discriminated union const fieldId = getDynamicFieldFocusHandlerId({ id: profileItem.id, name, }) - const onChange = (e: SyntheticEvent<*>) => { + const onChange = (e: React.ChangeEvent): void => { const value = e.currentTarget.value const maskedValue = maskProfileField(name, value) updateValue(name, maskedValue) @@ -267,14 +270,10 @@ const ProfileField = (props: ProfileFieldProps) => { const errors = getProfileFieldErrors(name, value) const errorToShow = showErrors && errors.length > 0 ? errors.join(', ') : null - // TODO: tooltips for profile fields - // const tooltipComponent = - // props.tooltipComponent || getTooltipForField(stepType, name, disabled) - - const onBlur = () => { + const onBlur = (): void => { focusHandlers.blur(fieldId) } - const onFocus = () => { + const onFocus = (): void => { focusHandlers.focus(fieldId) } return ( @@ -289,18 +288,18 @@ const ProfileField = (props: ProfileFieldProps) => { ) } -type ProfileStepRowProps = {| - focusHandlers: FocusHandlers, - profileStepItem: ProfileStepItem, - stepNumber: number, - isCycle?: ?boolean, -|} +interface ProfileStepRowProps { + focusHandlers: FocusHandlers + profileStepItem: ProfileStepItem + stepNumber: number + isCycle?: boolean | null +} -const ProfileStepRow = (props: ProfileStepRowProps) => { +const ProfileStepRow = (props: ProfileStepRowProps): JSX.Element => { const { focusHandlers, profileStepItem, isCycle } = props const dispatch = useDispatch() - const updateStepFieldValue = (name: string, value: mixed) => { + const updateStepFieldValue = (name: string, value: unknown): void => { dispatch( steplistActions.editProfileStep({ id: profileStepItem.id, @@ -309,11 +308,16 @@ const ProfileStepRow = (props: ProfileStepRowProps) => { ) } - const deleteProfileStep = () => { + const deleteProfileStep = (): void => { dispatch(steplistActions.deleteProfileStep({ id: profileStepItem.id })) } - const names = ['title', 'temperature', 'durationMinutes', 'durationSeconds'] - const units = { + const names = [ + 'title', + 'temperature', + 'durationMinutes', + 'durationSeconds', + ] as const + const units: Record = { title: null, temperature: i18n.t('application.units.degrees'), durationMinutes: i18n.t('application.units.minutes'), diff --git a/protocol-designer/src/components/StepEditForm/fields/RadioGroupField.tsx b/protocol-designer/src/components/StepEditForm/fields/RadioGroupField.tsx index 4bfe7e4529a..8a889e9f333 100644 --- a/protocol-designer/src/components/StepEditForm/fields/RadioGroupField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/RadioGroupField.tsx @@ -1,17 +1,15 @@ -// @flow import * as React from 'react' import { RadioGroup } from '@opentrons/components' -import type { StepFieldName } from '../../../steplist/fieldLevel' -import type { FieldProps } from '../types' +import { StepFieldName } from '../../../steplist/fieldLevel' +import { FieldProps } from '../types' -type RadioGroupFieldProps = {| - ...FieldProps, - name: StepFieldName, - options: $PropertyType, 'options'>, - className?: string, -|} +interface RadioGroupFieldProps extends FieldProps { + name: StepFieldName + options: React.ComponentProps['options'] + className?: string +} -export const RadioGroupField = (props: RadioGroupFieldProps): React.Node => { +export const RadioGroupField = (props: RadioGroupFieldProps): JSX.Element => { const { className, disabled, // NOTE: not used @@ -31,7 +29,7 @@ export const RadioGroupField = (props: RadioGroupFieldProps): React.Node => { className={className} value={value ? String(value) : ''} error={errorToShow} - onChange={(e: SyntheticEvent<*>) => { + onChange={(e: React.ChangeEvent) => { updateValue(e.currentTarget.value) // NOTE(IL, 2020-01-29): to allow the intented pristinity UX, this component "blurs" onchange if (onFieldBlur) { diff --git a/protocol-designer/src/components/StepEditForm/fields/StepFormDropdownField.tsx b/protocol-designer/src/components/StepEditForm/fields/StepFormDropdownField.tsx index f0e0f6a055c..6f3a1a3fac9 100644 --- a/protocol-designer/src/components/StepEditForm/fields/StepFormDropdownField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/StepFormDropdownField.tsx @@ -1,19 +1,17 @@ -// @flow import * as React from 'react' -import { DropdownField, type Options } from '@opentrons/components' +import { DropdownField, Options } from '@opentrons/components' import cx from 'classnames' import styles from '../StepEditForm.css' -import type { StepFieldName } from '../../../steplist/fieldLevel' -import type { FieldProps } from '../types' +import { StepFieldName } from '../../../steplist/fieldLevel' +import { FieldProps } from '../types' -export type StepFormDropdownProps = { - ...FieldProps, - options: Options, - name: StepFieldName, - className?: string, +export interface StepFormDropdownProps extends FieldProps { + options: Options + name: StepFieldName + className?: string } -export const StepFormDropdown = (props: StepFormDropdownProps): React.Node => { +export const StepFormDropdown = (props: StepFormDropdownProps): JSX.Element => { const { options, name, @@ -24,9 +22,10 @@ export const StepFormDropdown = (props: StepFormDropdownProps): React.Node => { updateValue, errorToShow, } = props - // TODO: BC abstract e.currentTarget.value inside onChange with fn like onChangeValue of type (value: mixed) => {} + // TODO: BC abstract e.currentTarget.value inside onChange with fn like onChangeValue of type (value: unknown) => {} // blank out the dropdown if labware id does not exist const availableOptionIds = options.map(opt => opt.value) + // @ts-expect-error (ce, 2021-06-21) unknown not assignable to string const fieldValue = availableOptionIds.includes(value) ? String(value) : null return ( @@ -38,7 +37,7 @@ export const StepFormDropdown = (props: StepFormDropdownProps): React.Node => { onBlur={onFieldBlur} onFocus={onFieldFocus} value={fieldValue} - onChange={(e: SyntheticEvent) => { + onChange={(e: React.ChangeEvent) => { updateValue(e.currentTarget.value) }} /> diff --git a/protocol-designer/src/components/StepEditForm/fields/TextField.tsx b/protocol-designer/src/components/StepEditForm/fields/TextField.tsx index abedd91d6da..ba6677990bd 100644 --- a/protocol-designer/src/components/StepEditForm/fields/TextField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/TextField.tsx @@ -1,16 +1,14 @@ -// @flow import * as React from 'react' import { InputField } from '@opentrons/components' -import type { FieldProps } from '../types' +import { FieldProps } from '../types' -type TextFieldProps = {| - ...FieldProps, - className?: string, - caption?: ?string, - units?: ?string, -|} +type TextFieldProps = FieldProps & { + className?: string + caption?: string | null + units?: string | null +} -export const TextField = (props: TextFieldProps): React.Node => { +export const TextField = (props: TextFieldProps): JSX.Element => { const { errorToShow, onFieldBlur, diff --git a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionModal.tsx b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionModal.tsx index ad28f9459c4..78e51347010 100644 --- a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionModal.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionModal.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import round from 'lodash/round' @@ -18,20 +17,20 @@ import { TipPositionZAxisViz } from './TipPositionZAxisViz' import styles from './TipPositionInput.css' import * as utils from './utils' -import { getIsTouchTipField, type StepFieldName } from '../../../../form-types' +import { getIsTouchTipField, StepFieldName } from '../../../../form-types' const SMALL_STEP_MM = 1 const LARGE_STEP_MM = 10 const DECIMALS_ALLOWED = 1 -type Props = {| - closeModal: () => mixed, - isIndeterminate?: boolean, - mmFromBottom: number | null, - name: StepFieldName, - updateValue: (?number) => mixed, - wellDepthMm: number, -|} +interface Props { + closeModal: () => unknown + isIndeterminate?: boolean + mmFromBottom: number | null + name: StepFieldName + updateValue: (val: number | null | undefined) => unknown + wellDepthMm: number +} const roundValue = (value: number | string | null): number => { return round(Number(value), DECIMALS_ALLOWED) @@ -41,12 +40,12 @@ const TOO_MANY_DECIMALS: 'TOO_MANY_DECIMALS' = 'TOO_MANY_DECIMALS' const OUT_OF_BOUNDS: 'OUT_OF_BOUNDS' = 'OUT_OF_BOUNDS' type Error = typeof TOO_MANY_DECIMALS | typeof OUT_OF_BOUNDS -const getErrorText = (args: {| - errors: Array, - maxMmFromBottom: number, - minMmFromBottom: number, - isPristine: boolean, -|}): string | null => { +const getErrorText = (args: { + errors: Error[] + maxMmFromBottom: number + minMmFromBottom: number + isPristine: boolean +}): string | null => { const { errors, minMmFromBottom, maxMmFromBottom, isPristine } = args if (errors.includes(TOO_MANY_DECIMALS)) { @@ -61,14 +60,14 @@ const getErrorText = (args: {| } } -const getErrors = (args: {| - isDefault: boolean, - value: string | null, - maxMmFromBottom: number, - minMmFromBottom: number, -|}): Array => { +const getErrors = (args: { + isDefault: boolean + value: string | null + maxMmFromBottom: number + minMmFromBottom: number +}): Error[] => { const { isDefault, value, maxMmFromBottom, minMmFromBottom } = args - const errors = [] + const errors: Error[] = [] if (isDefault) return errors const v = Number(value) @@ -88,7 +87,7 @@ const getErrors = (args: {| return errors } -export const TipPositionModal = (props: Props): React.Node => { +export const TipPositionModal = (props: Props): JSX.Element => { const { isIndeterminate, name, wellDepthMm } = props const defaultMmFromBottom = utils.getDefaultMmFromBottom({ @@ -105,10 +104,10 @@ export const TipPositionModal = (props: Props): React.Node => { // in this modal, pristinity hides the OUT_OF_BOUNDS error only. const [isPristine, setPristine] = React.useState(true) - const getMinMaxMmFromBottom = (): {| - maxMmFromBottom: number, - minMmFromBottom: number, - |} => { + const getMinMaxMmFromBottom = (): { + maxMmFromBottom: number + minMmFromBottom: number + } => { if (getIsTouchTipField(name)) { return { maxMmFromBottom: roundValue(wellDepthMm), @@ -170,7 +169,7 @@ export const TipPositionModal = (props: Props): React.Node => { } const handleInputFieldChange = ( - e: SyntheticEvent + e: React.ChangeEvent ): void => { handleChange(e.currentTarget.value) } @@ -255,7 +254,7 @@ export const TipPositionModal = (props: Props): React.Node => {
{ + onChange={(e: React.ChangeEvent) => { setIsDefault(e.currentTarget.value === 'default') }} options={[ diff --git a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionZAxisViz.tsx b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionZAxisViz.tsx index 5601dfa640f..64beadbb2ef 100644 --- a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionZAxisViz.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/TipPositionZAxisViz.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import round from 'lodash/round' @@ -9,12 +8,12 @@ import styles from './TipPositionInput.css' const WELL_HEIGHT_PIXELS = 48 const PIXEL_DECIMALS = 2 -type Props = { - mmFromBottom: number, - wellDepthMm: number, +interface Props { + mmFromBottom: number + wellDepthMm: number } -export const TipPositionZAxisViz = (props: Props): React.Node => { +export const TipPositionZAxisViz = (props: Props): JSX.Element => { const fractionOfWellHeight = props.mmFromBottom / props.wellDepthMm const pixelsFromBottom = Number(fractionOfWellHeight) * WELL_HEIGHT_PIXELS - WELL_HEIGHT_PIXELS diff --git a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/index.tsx b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/index.tsx index 02689282756..d99ee2a184f 100644 --- a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/index.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { @@ -6,7 +5,7 @@ import { InputField, Tooltip, useHoverTooltip, - type UseHoverTooltipResult, + UseHoverTooltipTargetProps, } from '@opentrons/components' import { getWellsDepth } from '@opentrons/shared-data' import { @@ -20,31 +19,30 @@ import styles from './TipPositionInput.css' import { TipPositionModal } from './TipPositionModal' import { getDefaultMmFromBottom } from './utils' -import type { BaseState } from '../../../../types' -import type { FieldProps } from '../../types' +import { BaseState } from '../../../../types' +import { FieldProps } from '../../types' -type OP = {| - ...FieldProps, - labwareId: ?string, - className?: string, -|} +interface OP extends FieldProps { + labwareId?: string | null + className?: string +} -type SP = {| - mmFromBottom: number | null, - wellDepthMm: number, -|} +interface SP { + mmFromBottom: number | null + wellDepthMm: number +} -type Props = {| ...OP, ...SP |} +type Props = OP & SP -function TipPositionInput(props: Props) { +function TipPositionInput(props: Props): JSX.Element { const [isModalOpen, setModalOpen] = React.useState(false) - const handleOpen = () => { + const handleOpen = (): void => { if (props.wellDepthMm) { setModalOpen(true) } } - const handleClose = () => { + const handleClose = (): void => { setModalOpen(false) } @@ -60,7 +58,7 @@ function TipPositionInput(props: Props) { const isTouchTipField = getIsTouchTipField(name) const isDelayPositionField = getIsDelayPositionField(name) - let value = '' + let value: number | string = '' if (wellDepthMm !== null) { // show default value for field in parens if no mmFromBottom value is selected value = @@ -105,15 +103,15 @@ function TipPositionInput(props: Props) { ) } -type WrapperProps = {| - isTouchTipField: boolean, - isDelayPositionField: boolean, - children: React.Node, - disabled: boolean, - targetProps: $ElementType, -|} +interface WrapperProps { + isTouchTipField: boolean + isDelayPositionField: boolean + children: React.ReactNode + disabled: boolean + targetProps: UseHoverTooltipTargetProps +} -const Wrapper = (props: WrapperProps) => +const Wrapper = (props: WrapperProps): JSX.Element => props.isTouchTipField || props.isDelayPositionField ? (
{props.children}
) : ( @@ -147,11 +145,4 @@ const mapSTP = (state: BaseState, ownProps: OP): SP => { } } -export const TipPositionField: React.AbstractComponent = connect< - Props, - OP, - SP, - _, - _, - _ ->(mapSTP, () => ({}))(TipPositionInput) +export const TipPositionField = connect(mapSTP, () => ({}))(TipPositionInput) diff --git a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/utils.ts b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/utils.ts index 5da8ba202cc..cc833151f87 100644 --- a/protocol-designer/src/components/StepEditForm/fields/TipPositionField/utils.ts +++ b/protocol-designer/src/components/StepEditForm/fields/TipPositionField/utils.ts @@ -1,35 +1,38 @@ -// @flow import assert from 'assert' import { DEFAULT_MM_FROM_BOTTOM_ASPIRATE, DEFAULT_MM_FROM_BOTTOM_DISPENSE, DEFAULT_MM_TOUCH_TIP_OFFSET_FROM_TOP, } from '../../../../constants' - -import { getIsTouchTipField, type StepFieldName } from '../../../../form-types' - +import { StepFieldName, getIsTouchTipField } from '../../../../form-types' // TODO: Ian + Brian 2019-02-13 this should switch on stepType, not use field // name to infer step type! // // TODO(IL, 2021-03-10): after resolving #7470, use this util instead // of directly using these constants, wherever these constants are used. See also #7469 export function getDefaultMmFromBottom(args: { - name: StepFieldName, - wellDepthMm: number, + name: StepFieldName + wellDepthMm: number }): number { const { name, wellDepthMm } = args + switch (name) { case 'aspirate_mmFromBottom': return DEFAULT_MM_FROM_BOTTOM_ASPIRATE + case 'aspirate_delay_mmFromBottom': return DEFAULT_MM_FROM_BOTTOM_ASPIRATE + case 'dispense_mmFromBottom': return DEFAULT_MM_FROM_BOTTOM_DISPENSE + case 'dispense_delay_mmFromBottom': return DEFAULT_MM_FROM_BOTTOM_DISPENSE + case 'mix_mmFromBottom': // TODO: Ian 2018-11-131 figure out what offset makes most sense for mix return DEFAULT_MM_FROM_BOTTOM_DISPENSE + default: // touch tip fields assert( diff --git a/protocol-designer/src/components/StepEditForm/fields/ToggleRowField.tsx b/protocol-designer/src/components/StepEditForm/fields/ToggleRowField.tsx index a6b4bdaeae2..f77277bb293 100644 --- a/protocol-designer/src/components/StepEditForm/fields/ToggleRowField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/ToggleRowField.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' @@ -6,15 +5,14 @@ import { ToggleField } from '@opentrons/components' import styles from '../StepEditForm.css' -import type { FieldProps } from '../types' +import { FieldProps } from '../types' -type ToggleRowProps = {| - ...FieldProps, - offLabel?: string, - onLabel?: string, - className?: string, -|} -export const ToggleRowField = (props: ToggleRowProps): React.Node => { +type ToggleRowProps = FieldProps & { + offLabel?: string + onLabel?: string + className?: string +} +export const ToggleRowField = (props: ToggleRowProps): JSX.Element => { const { updateValue, value, @@ -31,7 +29,7 @@ export const ToggleRowField = (props: ToggleRowProps): React.Node => { onLabel={onLabel} className={cx(styles.toggle_field, className)} value={Boolean(value)} - onChange={(e: SyntheticInputEvent<*>) => updateValue(!value)} + onChange={() => updateValue(!value)} disabled={disabled} /> ) diff --git a/protocol-designer/src/components/StepEditForm/fields/VolumeField.tsx b/protocol-designer/src/components/StepEditForm/fields/VolumeField.tsx index 2d08c5845a7..0d1ba73efa2 100644 --- a/protocol-designer/src/components/StepEditForm/fields/VolumeField.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/VolumeField.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { FormGroup, @@ -10,17 +9,16 @@ import { import { i18n } from '../../../localization' import { getFieldDefaultTooltip } from '../utils' import { TextField } from './TextField' -import type { StepType } from '../../../form-types' -import type { FieldProps } from '../types' +import { StepType } from '../../../form-types' +import { FieldProps } from '../types' import styles from '../StepEditForm.css' -type Props = {| - ...FieldProps, - stepType: StepType, - label: string, - className: string, -|} -export const VolumeField = (props: Props): React.Node => { +type Props = FieldProps & { + stepType: StepType + label: string + className: string +} +export const VolumeField = (props: Props): JSX.Element => { const { stepType, label, className, ...propsForVolumeField } = props const [targetProps, tooltipProps] = useHoverTooltip({ placement: TOOLTIP_TOP, diff --git a/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderModal.tsx b/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderModal.tsx index 61ccbde5942..3b3721d336d 100644 --- a/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderModal.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderModal.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { i18n } from '../../../../localization' @@ -11,7 +10,7 @@ import { DropdownField, } from '@opentrons/components' import modalStyles from '../../../modals/modal.css' -import type { WellOrderOption } from '../../../../form-types' +import { WellOrderOption } from '../../../../form-types' import { WellOrderViz } from './WellOrderViz' import styles from './WellOrderInput.css' @@ -19,38 +18,39 @@ import stepEditStyles from '../../StepEditForm.css' const DEFAULT_FIRST: WellOrderOption = 't2b' const DEFAULT_SECOND: WellOrderOption = 'l2r' -const VERTICAL_VALUES: Array = ['t2b', 'b2t'] -const HORIZONTAL_VALUES: Array = ['l2r', 'r2l'] -const WELL_ORDER_VALUES: Array = [ +const VERTICAL_VALUES: WellOrderOption[] = ['t2b', 'b2t'] +const HORIZONTAL_VALUES: WellOrderOption[] = ['l2r', 'r2l'] +const WELL_ORDER_VALUES: WellOrderOption[] = [ ...VERTICAL_VALUES, ...HORIZONTAL_VALUES, ] -type Props = {| - isOpen: boolean, - closeModal: () => mixed, - prefix: 'aspirate' | 'dispense' | 'mix', - firstValue: ?WellOrderOption, - secondValue: ?WellOrderOption, - firstName: string, - secondName: string, + +export interface WellOrderModalProps { + isOpen: boolean + closeModal: () => unknown + prefix: 'aspirate' | 'dispense' | 'mix' + firstValue?: WellOrderOption | null + secondValue?: WellOrderOption | null + firstName: string + secondName: string updateValues: ( - firstValue: ?WellOrderOption, - secondValue: ?WellOrderOption - ) => void, -|} + firstValue?: WellOrderOption | null, + secondValue?: WellOrderOption | null + ) => void +} -type State = {| - firstValue: WellOrderOption, - secondValue: WellOrderOption, -|} +interface State { + firstValue: WellOrderOption + secondValue: WellOrderOption +} -export const ResetButton = (props: {| onClick: () => void |}): React.Node => ( +export const ResetButton = (props: { onClick: () => void }): JSX.Element => ( {i18n.t('button.reset')} ) -export const CancelButton = (props: {| onClick: () => void |}): React.Node => ( +export const CancelButton = (props: { onClick: () => void }): JSX.Element => ( void |}): React.Node => ( ) -export const DoneButton = (props: {| onClick: () => void |}): React.Node => ( +export const DoneButton = (props: { onClick: () => void }): JSX.Element => ( {i18n.t('button.done')} ) -export class WellOrderModal extends React.Component { - constructor(props: Props) { +export class WellOrderModal extends React.Component< + WellOrderModalProps, + State +> { + constructor(props: WellOrderModalProps) { super(props) const { initialFirstValue, @@ -78,10 +81,10 @@ export class WellOrderModal extends React.Component { } } - getInitialFirstValues: () => {| - initialFirstValue: WellOrderOption, - initialSecondValue: WellOrderOption, - |} = () => { + getInitialFirstValues: () => { + initialFirstValue: WellOrderOption + initialSecondValue: WellOrderOption + } = () => { const { firstValue, secondValue } = this.props if (firstValue == null || secondValue == null) { return { @@ -127,18 +130,19 @@ export class WellOrderModal extends React.Component { makeOnChange: ( ordinality: 'first' | 'second' ) => ( - event: SyntheticEvent + event: React.ChangeEvent ) => void = ordinality => event => { const { value } = event.currentTarget - let nextState = { [`${ordinality}Value`]: value } + // @ts-expect-error (ce, 2021-06-22) missing one prop or the other + let nextState: State = { [`${ordinality}Value`]: value } if (ordinality === 'first') { if ( - VERTICAL_VALUES.includes(value) && + VERTICAL_VALUES.includes(value as WellOrderOption) && VERTICAL_VALUES.includes(this.state.secondValue) ) { nextState = { ...nextState, secondValue: HORIZONTAL_VALUES[0] } } else if ( - HORIZONTAL_VALUES.includes(value) && + HORIZONTAL_VALUES.includes(value as WellOrderOption) && HORIZONTAL_VALUES.includes(this.state.secondValue) ) { nextState = { ...nextState, secondValue: VERTICAL_VALUES[0] } @@ -147,7 +151,7 @@ export class WellOrderModal extends React.Component { this.setState(nextState) } - isSecondOptionDisabled: WellOrderOption => boolean = ( + isSecondOptionDisabled: (wellOrderOption: WellOrderOption) => boolean = ( value: WellOrderOption ) => { if (VERTICAL_VALUES.includes(this.state.firstValue)) { @@ -159,7 +163,7 @@ export class WellOrderModal extends React.Component { } } - render(): React.Node { + render(): React.ReactNode | null { if (!this.props.isOpen) return null const { firstValue, secondValue } = this.state diff --git a/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderViz.tsx b/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderViz.tsx index 3e8003f8d6e..fdc00920773 100644 --- a/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderViz.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/WellOrderField/WellOrderViz.tsx @@ -1,20 +1,19 @@ -// @flow import * as React from 'react' import cx from 'classnames' import WELLS_IMAGE from '../../../../images/well_order_wells.svg' import PATH_IMAGE from '../../../../images/well_order_path.svg' -import type { WellOrderOption } from '../../../../form-types' +import { WellOrderOption } from '../../../../form-types' import styles from './WellOrderInput.css' -type Props = {| - firstValue: WellOrderOption, - secondValue: WellOrderOption, -|} +interface Props { + firstValue: WellOrderOption + secondValue: WellOrderOption +} -export const WellOrderViz = (props: Props): React.Node => { +export const WellOrderViz = (props: Props): JSX.Element => { const { firstValue, secondValue } = props return ( diff --git a/protocol-designer/src/components/StepEditForm/fields/WellOrderField/index.tsx b/protocol-designer/src/components/StepEditForm/fields/WellOrderField/index.tsx index 39dc0ba13d1..62e6587177f 100644 --- a/protocol-designer/src/components/StepEditForm/fields/WellOrderField/index.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/WellOrderField/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { css } from 'styled-components' import { @@ -16,22 +15,22 @@ import ZIG_ZAG_IMAGE from '../../../../images/zig_zag_icon.svg' import { WellOrderModal } from './WellOrderModal' import stepEditStyles from '../../StepEditForm.css' import styles from './WellOrderInput.css' -import type { FieldProps } from '../../types' -import type { WellOrderOption } from '../../../../form-types' +import { FieldProps } from '../../types' +import { WellOrderOption } from '../../../../form-types' -type Props = {| - className?: ?string, - label?: string, - prefix: 'aspirate' | 'dispense' | 'mix', - firstValue: ?WellOrderOption, - secondValue: ?WellOrderOption, - firstName: string, - secondName: string, - updateFirstWellOrder: $PropertyType, - updateSecondWellOrder: $PropertyType, -|} +export interface WellOrderFieldProps { + className?: string | null + label?: string + prefix: 'aspirate' | 'dispense' | 'mix' + firstValue?: WellOrderOption | null + secondValue?: WellOrderOption | null + firstName: string + secondName: string + updateFirstWellOrder: FieldProps['updateValue'] + updateSecondWellOrder: FieldProps['updateValue'] +} -export const WellOrderField = (props: Props): React.Node => { +export const WellOrderField = (props: WellOrderFieldProps): JSX.Element => { const { firstValue, secondValue, @@ -42,19 +41,19 @@ export const WellOrderField = (props: Props): React.Node => { } = props const [isModalOpen, setModalOpen] = React.useState(false) - const handleOpen = () => { + const handleOpen = (): void => { setModalOpen(true) } - const handleClose = () => { + const handleClose = (): void => { setModalOpen(false) } - const updateValues = (firstValue, secondValue) => { + const updateValues = (firstValue: unknown, secondValue: unknown): void => { updateFirstWellOrder(firstValue) updateSecondWellOrder(secondValue) } - const getIconClassNames = () => { + const getIconClassNames = (): string[] => { const iconClassNames = [] if (firstValue) { iconClassNames.push(styles[`${firstValue}_first`]) @@ -106,6 +105,7 @@ export const WellOrderField = (props: Props): React.Node => { src={ZIG_ZAG_IMAGE} className={cx( styles.well_order_icon, + // @ts-expect-error(sa, 2021-6-22): I think props.label needs to be casted to a boolean first { [styles.icon_with_label]: props.label }, getIconClassNames() )} diff --git a/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionInput.tsx b/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionInput.tsx index ed8f9567cf7..6d2bc74c57f 100644 --- a/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionInput.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionInput.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { connect } from 'react-redux' import { FormGroup, InputField } from '@opentrons/components' @@ -12,33 +11,32 @@ import { } from '../../../../ui/steps' import styles from '../../StepEditForm.css' -import type { Dispatch } from 'redux' -import type { StepIdType } from '../../../../form-types' -import type { BaseState } from '../../../../types' -import type { FieldProps } from '../../types' +import { Dispatch } from 'redux' +import { StepIdType } from '../../../../form-types' +import { BaseState } from '../../../../types' +import { FieldProps } from '../../types' -type SP = {| - stepId: ?StepIdType, - wellSelectionLabwareKey: ?string, -|} +export interface SP { + stepId?: StepIdType | null + wellSelectionLabwareKey?: string | null +} -type DP = {| - onOpen: string => mixed, - onClose: () => mixed, -|} +export interface DP { + onOpen: (val: string) => unknown + onClose: () => unknown +} -type OP = {| - ...FieldProps, - primaryWellCount?: number, - isMulti: ?boolean, - pipetteId: ?string, - labwareId: ?string, -|} +export type OP = FieldProps & { + primaryWellCount?: number + isMulti?: boolean | null + pipetteId?: string | null + labwareId?: string | null +} -type Props = {| ...OP, ...SP, ...DP |} +export type Props = OP & SP & DP -class WellSelectionInputComponent extends React.Component { - handleOpen = () => { +export class WellSelectionInputComponent extends React.Component { + handleOpen = (): void => { const { labwareId, pipetteId, onFieldFocus } = this.props if (onFieldFocus) { @@ -49,7 +47,7 @@ class WellSelectionInputComponent extends React.Component { } } - handleClose = () => { + handleClose = (): void => { const { onFieldBlur, onClose } = this.props if (onFieldBlur) { onFieldBlur() @@ -57,14 +55,14 @@ class WellSelectionInputComponent extends React.Component { onClose() } - getModalKey = () => { + getModalKey = (): string => { const { name, pipetteId, labwareId, stepId } = this.props return `${String(stepId)}${name}${pipetteId || 'noPipette'}${ labwareId || 'noLabware' }` } - render() { + render(): JSX.Element { const modalKey = this.getModalKey() const label = this.props.isMulti ? i18n.t('form.step_edit_form.wellSelectionLabel.columns') @@ -107,19 +105,12 @@ const mapStateToProps = (state: BaseState): SP => ({ stepId: getSelectedStepId(state), wellSelectionLabwareKey: getWellSelectionLabwareKey(state), }) -const mapDispatchToProps = (dispatch: Dispatch<*>): DP => ({ +const mapDispatchToProps = (dispatch: Dispatch): DP => ({ onOpen: key => dispatch(stepsActions.setWellSelectionLabwareKey(key)), onClose: () => dispatch(stepsActions.clearWellSelectionLabwareKey()), }) -export const WellSelectionInput: React.AbstractComponent = connect< - Props, - OP, - SP, - DP, - _, - _ ->( +export const WellSelectionInput = connect( mapStateToProps, mapDispatchToProps )(WellSelectionInputComponent) diff --git a/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionModal.tsx b/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionModal.tsx index 6c42f4b9bb4..07e57387ce4 100644 --- a/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionModal.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/WellSelectionModal.tsx @@ -1,11 +1,19 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { useSelector } from 'react-redux' import omit from 'lodash/omit' -import { Modal, OutlineButton, LabeledValue } from '@opentrons/components' -import { sortWells } from '@opentrons/shared-data' +import { + Modal, + OutlineButton, + LabeledValue, + WellGroup, +} from '@opentrons/components' +import { + sortWells, + LabwareDefinition2, + PipetteNameSpecs, +} from '@opentrons/shared-data' import { arrayToWellGroup } from '../../../../utils' import { WellSelectionInstructions } from '../../../WellSelectionInstructions' @@ -14,45 +22,40 @@ import { SelectableLabware, wellFillFromWellContents } from '../../../labware' import * as wellContentsSelectors from '../../../../top-selectors/well-contents' import { selectors } from '../../../../labware-ingred/selectors' import { selectors as stepFormSelectors } from '../../../../step-forms' -import type { WellGroup } from '@opentrons/components' -import type { - LabwareDefinition2, - PipetteNameSpecs, -} from '@opentrons/shared-data' -import type { ContentsByWell } from '../../../../labware-ingred/types' -import type { WellIngredientNames } from '../../../../steplist/types' -import type { StepFieldName } from '../../../../form-types' +import { ContentsByWell } from '../../../../labware-ingred/types' +import { WellIngredientNames } from '../../../../steplist/types' +import { StepFieldName } from '../../../../form-types' import styles from './WellSelectionModal.css' import modalStyles from '../../../modals/modal.css' -type WellSelectionModalProps = {| - isOpen: boolean, - labwareId: ?string, - name: StepFieldName, - onCloseClick: (e: ?SyntheticEvent<*>) => mixed, - pipetteId: ?string, - value: mixed, - updateValue: (?mixed) => void, -|} - -type WellSelectionModalComponentProps = {| - deselectWells: WellGroup => mixed, - handleSave: () => mixed, - highlightedWells: WellGroup, - ingredNames: WellIngredientNames, - labwareDef: ?LabwareDefinition2, - onCloseClick: (e: ?SyntheticEvent<*>) => mixed, - pipetteSpec: ?PipetteNameSpecs, - selectedPrimaryWells: WellGroup, - selectWells: WellGroup => mixed, - updateHighlightedWells: WellGroup => mixed, - wellContents: ContentsByWell, -|} +interface WellSelectionModalProps { + isOpen: boolean + labwareId?: string | null + name: StepFieldName + onCloseClick: (e?: React.MouseEvent) => unknown + pipetteId?: string | null + value: unknown + updateValue: (val: unknown | null | undefined) => void +} + +interface WellSelectionModalComponentProps { + deselectWells: (wellGroup: WellGroup) => unknown + handleSave: () => unknown + highlightedWells: WellGroup + ingredNames: WellIngredientNames + labwareDef?: LabwareDefinition2 | null + onCloseClick: (e?: React.MouseEvent) => unknown + pipetteSpec?: PipetteNameSpecs | null + selectedPrimaryWells: WellGroup + selectWells: (wellGroup: WellGroup) => unknown + updateHighlightedWells: (wellGroup: WellGroup) => unknown + wellContents: ContentsByWell +} const WellSelectionModalComponent = ( props: WellSelectionModalComponentProps -) => { +): JSX.Element => { const { deselectWells, handleSave, @@ -112,7 +115,7 @@ const WellSelectionModalComponent = ( export const WellSelectionModal = ( props: WellSelectionModalProps -): React.Node => { +): JSX.Element | null => { const { isOpen, labwareId, onCloseClick, pipetteId } = props const wellFieldData = props.value @@ -142,17 +145,17 @@ export const WellSelectionModal = ( const [highlightedWells, setHighlightedWells] = React.useState({}) // actions - const selectWells = (wells: WellGroup) => { + const selectWells = (wells: WellGroup): void => { setSelectedPrimaryWells(prev => ({ ...prev, ...wells })) setHighlightedWells({}) } - const deselectWells = (deselectedWells: WellGroup) => { + const deselectWells = (deselectedWells: WellGroup): void => { setSelectedPrimaryWells(prev => omit(prev, Object.keys(deselectedWells))) setHighlightedWells({}) } - const handleSave = () => { + const handleSave = (): void => { const sortedWells = Object.keys(selectedPrimaryWells).sort(sortWells) props.updateValue(sortedWells) onCloseClick() diff --git a/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/index.ts b/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/index.ts index 3711bb8b4a1..134b30aae81 100644 --- a/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/index.ts +++ b/protocol-designer/src/components/StepEditForm/fields/WellSelectionField/index.ts @@ -1,32 +1,35 @@ -// @flow -import * as React from 'react' import { connect } from 'react-redux' -import { WellSelectionInput } from './WellSelectionInput' +import { + WellSelectionInput, + Props as WellSelectionInputProps, + DP, +} from './WellSelectionInput' import { selectors as stepFormSelectors } from '../../../../step-forms' -import type { BaseState, ThunkDispatch } from '../../../../types' -import type { FieldProps } from '../../types' +import { BaseState } from '../../../../types' +import { FieldProps } from '../../types' -type Props = React.ElementConfig - -type OP = {| - ...FieldProps, - labwareId: ?string, - pipetteId: ?string, -|} - -type SP = {| - isMulti: $PropertyType, - primaryWellCount: $PropertyType, -|} +type Props = Omit< + JSX.LibraryManagedAttributes< + typeof WellSelectionInput, + WellSelectionInputProps + >, + keyof DP +> +type OP = FieldProps & { + labwareId?: string | null + pipetteId?: string | null +} +interface SP { + isMulti: Props['isMulti'] + primaryWellCount: Props['primaryWellCount'] +} const mapStateToProps = (state: BaseState, ownProps: OP): SP => { const { pipetteId } = ownProps const selectedWells = ownProps.value - const pipette = pipetteId && stepFormSelectors.getPipetteEntities(state)[pipetteId] const isMulti = pipette ? pipette.spec.channels > 1 : false - return { primaryWellCount: Array.isArray(selectedWells) ? selectedWells.length @@ -35,11 +38,7 @@ const mapStateToProps = (state: BaseState, ownProps: OP): SP => { } } -function mergeProps( - stateProps: SP, - dispatchProps: { dispatch: ThunkDispatch<*> }, - ownProps: OP -): Props { +function mergeProps(stateProps: SP, _dispatchProps: null, ownProps: OP): Props { const { disabled, errorToShow, @@ -51,7 +50,6 @@ function mergeProps( updateValue, value, } = ownProps - return { disabled, errorToShow, @@ -67,14 +65,7 @@ function mergeProps( } } -export const WellSelectionField: React.AbstractComponent = connect< - Props, - OP, - SP, - {||}, - _, - _ ->( +export const WellSelectionField = connect( mapStateToProps, null, mergeProps diff --git a/protocol-designer/src/components/StepEditForm/fields/__tests__/DelayFields.test.tsx b/protocol-designer/src/components/StepEditForm/fields/__tests__/DelayFields.test.tsx index 0044e699451..02d3ab6b7d5 100644 --- a/protocol-designer/src/components/StepEditForm/fields/__tests__/DelayFields.test.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/__tests__/DelayFields.test.tsx @@ -1,22 +1,24 @@ -// @flow -import fixture_96_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' +import _fixture_96_plate from '@opentrons/shared-data/labware/fixtures/2/fixture_96_plate.json' +import { LabwareDefinition2 } from '@opentrons/shared-data' import React from 'react' import { Provider } from 'react-redux' import { mount } from 'enzyme' -import ApplicationText from '../../../../localization/en/application' +import ApplicationText from '../../../../localization/en/application.json' import * as stepFormSelectors from '../../../../step-forms/selectors' import { CheckboxRowField, TextField, TipPositionField } from '../../fields' -import { DelayFields, type DelayFieldProps } from '../DelayFields' -import type { BaseState } from '../../../../types' -import type { FormData } from '../../../../form-types' +import { DelayFields, DelayFieldProps } from '../DelayFields' jest.mock('../../../../step-forms/selectors') -const getUnsavedFormMock: JestMockFn<[BaseState], ?FormData> = - stepFormSelectors.getUnsavedForm +const fixture96Plate = _fixture_96_plate as LabwareDefinition2 -const getLabwareEntitiesMock: JestMockFn<[BaseState], any> = - stepFormSelectors.getLabwareEntities +const getUnsavedFormMock = stepFormSelectors.getUnsavedForm as jest.MockedFunction< + typeof stepFormSelectors.getUnsavedForm +> + +const getLabwareEntitiesMock = stepFormSelectors.getLabwareEntities as jest.MockedFunction< + typeof stepFormSelectors.getLabwareEntities +> const mockStore = { dispatch: jest.fn(), @@ -33,18 +35,21 @@ beforeEach(() => { getLabwareEntitiesMock.mockReturnValue({ labware123asp: { id: 'labware123asp', - labwareDefURI: fixture_96_plate.labwareDefURI, - def: fixture_96_plate, + // @ts-expect-error (ce, 2021-06-21) need to stub labwareDefURI. see createFile.test.ts for an example + labwareDefURI: fixture96Plate.labwareDefURI, + def: fixture96Plate, }, labware123disp: { id: 'labware123disp', - labwareDefURI: fixture_96_plate.labwareDefURI, - def: fixture_96_plate, + // @ts-expect-error (ce, 2021-06-21) need to stub labwareDefURI. see createFile.test.ts for an example + labwareDefURI: fixture96Plate.labwareDefURI, + def: fixture96Plate, }, labware123: { id: 'labware123', - labwareDefURI: fixture_96_plate.labwareDefURI, - def: fixture_96_plate, + // @ts-expect-error (ce, 2021-06-21) need to stub labwareDefURI. see createFile.test.ts for an example + labwareDefURI: fixture96Plate.labwareDefURI, + def: fixture96Plate, }, }) }) @@ -69,49 +74,49 @@ describe('DelayFields', () => { labwareId: 'labware123asp', propsForFields: { aspirate_delay_checkbox: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'aspirate_delay_checkbox', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, tooltipContent: 'tooltip for aspirate_delay_checkbox', }, aspirate_delay_seconds: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'aspirate_delay_seconds', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: '1', }, preWetTip: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'preWetTip', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, }, aspirate_mmFromBottom: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'aspirate_mmFromBottom', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, }, aspirate_delay_mmFromBottom: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'aspirate_delay_mmFromBottom', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, }, }, @@ -161,7 +166,7 @@ describe('DelayFields', () => { }) describe('Dispense Delay', () => { - let props + let props: DelayFieldProps beforeEach(() => { props = { checkboxFieldName: 'dispense_delay_checkbox', @@ -169,49 +174,49 @@ describe('DelayFields', () => { labwareId: 'labware123disp', propsForFields: { dispense_delay_checkbox: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'dispense_delay_checkbox', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, tooltipContent: 'tooltip for dispense_delay_checkbox', }, dispense_delay_seconds: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'dispense_delay_seconds', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: '1', }, preWetTip: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'preWetTip', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, }, dispense_mmFromBottom: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'dispense_mmFromBottom', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, }, dispense_delay_mmFromBottom: { - onFieldFocus: (jest.fn(): any), - onFieldBlur: (jest.fn(): any), + onFieldFocus: jest.fn() as any, + onFieldBlur: jest.fn() as any, errorToShow: null, disabled: false, name: 'dispense_delay_mmFromBottom', - updateValue: (jest.fn(): any), + updateValue: jest.fn() as any, value: true, }, }, diff --git a/protocol-designer/src/components/StepEditForm/fields/__tests__/WellOrderField.test.tsx b/protocol-designer/src/components/StepEditForm/fields/__tests__/WellOrderField.test.tsx index 9c75d650511..ccdc171d18b 100644 --- a/protocol-designer/src/components/StepEditForm/fields/__tests__/WellOrderField.test.tsx +++ b/protocol-designer/src/components/StepEditForm/fields/__tests__/WellOrderField.test.tsx @@ -1,18 +1,19 @@ -// @flow import React from 'react' import { mount } from 'enzyme' import { act } from 'react-dom/test-utils' -import { WellOrderField } from '../WellOrderField' +import { WellOrderField, WellOrderFieldProps } from '../WellOrderField' import { WellOrderModal, CancelButton, ResetButton, + WellOrderModalProps, } from '../WellOrderField/WellOrderModal' describe('WellOrderField', () => { - const render = _props => mount() + const render = (_props: WellOrderFieldProps) => + mount() - let props + let props: WellOrderFieldProps beforeEach(() => { props = { prefix: 'aspirate', @@ -36,7 +37,7 @@ describe('WellOrderField', () => { expect(props.updateSecondWellOrder).toHaveBeenCalledWith('t2b') }) it('should NOT update on cancel', () => { - const wellOrderModalProps = { + const wellOrderModalProps: WellOrderModalProps = { prefix: 'aspirate', closeModal: jest.fn(), isOpen: true, @@ -55,7 +56,7 @@ describe('WellOrderField', () => { expect(wellOrderModalProps.updateValues).not.toHaveBeenCalled() }) it('should update to default values on reset', () => { - const wellOrderModalProps = { + const wellOrderModalProps: WellOrderModalProps = { prefix: 'aspirate', closeModal: jest.fn(), isOpen: true, diff --git a/protocol-designer/src/components/StepEditForm/fields/__tests__/makeSingleEditFieldProps.test.ts b/protocol-designer/src/components/StepEditForm/fields/__tests__/makeSingleEditFieldProps.test.ts index 4c081de4b2f..bb998523130 100644 --- a/protocol-designer/src/components/StepEditForm/fields/__tests__/makeSingleEditFieldProps.test.ts +++ b/protocol-designer/src/components/StepEditForm/fields/__tests__/makeSingleEditFieldProps.test.ts @@ -1,4 +1,3 @@ -// @flow import { makeSingleEditFieldProps } from '../makeSingleEditFieldProps' import { getDisabledFields, @@ -6,6 +5,7 @@ import { } from '../../../../steplist/formLevel' import { getFieldErrors } from '../../../../steplist/fieldLevel' import * as stepEditFormUtils from '../../utils' +import { FormData } from '../../../../form-types' jest.mock('../../../../steplist/formLevel') jest.mock('../../../../steplist/fieldLevel') @@ -19,15 +19,15 @@ const getSingleSelectDisabledTooltipSpy = jest.spyOn( 'getSingleSelectDisabledTooltip' ) -const getDisabledFieldsMock: JestMockFn> = getDisabledFields -const getDefaultsForStepTypeMock: JestMockFn< - [any], - any -> = getDefaultsForStepType -const getFieldErrorsMock: JestMockFn< - [string, mixed], - Array -> = getFieldErrors +const getDisabledFieldsMock = getDisabledFields as jest.MockedFunction< + typeof getDisabledFields +> +const getDefaultsForStepTypeMock = getDefaultsForStepType as jest.MockedFunction< + typeof getDefaultsForStepType +> +const getFieldErrorsMock = getFieldErrors as jest.MockedFunction< + typeof getFieldErrors +> beforeEach(() => { getFieldDefaultTooltipSpy.mockImplementation(name => `tooltip for ${name}`) @@ -58,12 +58,14 @@ describe('makeSingleEditFieldProps', () => { focused_error_field: '', } - getDisabledFieldsMock.mockImplementation(form => { - expect(form).toBe(formData) - const disabled = new Set() - disabled.add('disabled_field') - return disabled - }) + getDisabledFieldsMock.mockImplementation( + (form: FormData): Set => { + expect(form).toBe(formData) + const disabled = new Set() + disabled.add('disabled_field') + return disabled + } + ) getDefaultsForStepTypeMock.mockImplementation(stepType => { expect(stepType).toEqual('fakeStepType') diff --git a/protocol-designer/src/components/StepEditForm/fields/index.ts b/protocol-designer/src/components/StepEditForm/fields/index.ts index e129a2b0d89..cb9af06b39f 100644 --- a/protocol-designer/src/components/StepEditForm/fields/index.ts +++ b/protocol-designer/src/components/StepEditForm/fields/index.ts @@ -1,5 +1,3 @@ -// @flow - /* Generic Fields */ export { CheckboxRowField } from './CheckboxRowField' diff --git a/protocol-designer/src/components/StepEditForm/fields/makeSingleEditFieldProps.ts b/protocol-designer/src/components/StepEditForm/fields/makeSingleEditFieldProps.ts index f72c72020cd..7daf2a4abca 100644 --- a/protocol-designer/src/components/StepEditForm/fields/makeSingleEditFieldProps.ts +++ b/protocol-designer/src/components/StepEditForm/fields/makeSingleEditFieldProps.ts @@ -1,4 +1,3 @@ -// @flow import { getFieldErrors } from '../../../steplist/fieldLevel' import { getDisabledFields, @@ -8,60 +7,57 @@ import { getFieldDefaultTooltip, getSingleSelectDisabledTooltip, } from '../utils' -import type { StepFieldName, FormData } from '../../../form-types' -import type { FieldProps, FieldPropsByName, FocusHandlers } from '../types' - -type ShowFieldErrorParams = {| - name: StepFieldName, - focusedField: StepFieldName | null, - dirtyFields?: Array, -|} +import { StepFieldName, FormData } from '../../../form-types' +import { FieldProps, FieldPropsByName, FocusHandlers } from '../types' +interface ShowFieldErrorParams { + name: StepFieldName + focusedField: StepFieldName | null + dirtyFields?: StepFieldName[] +} export const showFieldErrors = ({ name, focusedField, dirtyFields, -}: ShowFieldErrorParams): boolean | void | Array => +}: ShowFieldErrorParams): boolean | undefined | StepFieldName[] => !(name === focusedField) && dirtyFields && dirtyFields.includes(name) - export const makeSingleEditFieldProps = ( focusHandlers: FocusHandlers, formData: FormData, - handleChangeFormInput: (name: string, value: mixed) => void + handleChangeFormInput: (name: string, value: unknown) => void ): FieldPropsByName => { const { dirtyFields, blur, focusedField, focus } = focusHandlers - - const fieldNames: Array = Object.keys( + const fieldNames: string[] = Object.keys( getDefaultsForStepType(formData.stepType) ) - return fieldNames.reduce((acc, name) => { const disabled = formData ? getDisabledFields(formData).has(name) : false const value = formData ? formData[name] : null - - const showErrors = showFieldErrors({ name, focusedField, dirtyFields }) + const showErrors = showFieldErrors({ + name, + focusedField, + dirtyFields, + }) const errors = getFieldErrors(name, value) const errorToShow = showErrors && errors.length > 0 ? errors.join(', ') : null - const updateValue = value => { + const updateValue = (value: unknown): void => { handleChangeFormInput(name, value) } - const onFieldBlur = () => { + const onFieldBlur = (): void => { blur(name) } - const onFieldFocus = () => { + const onFieldFocus = (): void => { focus(name) } const defaultTooltip = getFieldDefaultTooltip(name) - const disabledTooltip = getSingleSelectDisabledTooltip( name, formData.stepType ) - const fieldProps: FieldProps = { disabled, errorToShow, @@ -72,9 +68,6 @@ export const makeSingleEditFieldProps = ( onFieldFocus, tooltipContent: disabled ? disabledTooltip : defaultTooltip, } - return { - ...acc, - [name]: fieldProps, - } + return { ...acc, [name]: fieldProps } }, {}) } diff --git a/protocol-designer/src/components/StepEditForm/forms/AspDispSection.tsx b/protocol-designer/src/components/StepEditForm/forms/AspDispSection.tsx index 182b7131b6f..299fb987d5a 100644 --- a/protocol-designer/src/components/StepEditForm/forms/AspDispSection.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/AspDispSection.tsx @@ -1,21 +1,21 @@ -// @flow import * as React from 'react' import { IconButton, Tooltip, useHoverTooltip } from '@opentrons/components' import { i18n } from '../../../localization' import styles from '../StepEditForm.css' -type Props = { - className?: ?string, - collapsed?: ?boolean, - toggleCollapsed: () => void, - prefix: 'aspirate' | 'dispense', - children?: React.Node, +interface Props { + className?: string | null + collapsed?: boolean | null + toggleCollapsed: () => void + prefix: 'aspirate' | 'dispense' + children?: React.ReactNode } -export const AspDispSection = (props: Props): React.Node => { +export const AspDispSection = (props: Props): JSX.Element => { const { children, className, collapsed, toggleCollapsed, prefix } = props const [targetProps, tooltipProps] = useHoverTooltip() return ( + // @ts-expect-error(sa, 2021-7-2): className might be null
{prefix} diff --git a/protocol-designer/src/components/StepEditForm/forms/MagnetForm.tsx b/protocol-designer/src/components/StepEditForm/forms/MagnetForm.tsx index b9acdb0a9ed..94e1ba9c42d 100644 --- a/protocol-designer/src/components/StepEditForm/forms/MagnetForm.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/MagnetForm.tsx @@ -1,4 +1,3 @@ -// @flow import cx from 'classnames' import * as React from 'react' import { useSelector } from 'react-redux' @@ -11,9 +10,9 @@ import { maskField } from '../../../steplist/fieldLevel' import { TextField, RadioGroupField } from '../fields' import styles from '../StepEditForm.css' -import type { StepFormProps } from '../types' +import { StepFormProps } from '../types' -export const MagnetForm = (props: StepFormProps): React.Node => { +export const MagnetForm = (props: StepFormProps): JSX.Element => { const moduleLabwareOptions = useSelector( uiModuleSelectors.getMagneticLabwareOptions ) @@ -22,7 +21,7 @@ export const MagnetForm = (props: StepFormProps): React.Node => { const { magnetAction, moduleId } = props.formData const moduleModel = moduleId ? moduleEntities[moduleId]?.model : null - const moduleOption: ?string = moduleLabwareOptions[0] + const moduleOption: string | null | undefined = moduleLabwareOptions[0] ? moduleLabwareOptions[0].name : 'No magnetic module' diff --git a/protocol-designer/src/components/StepEditForm/forms/MixForm.tsx b/protocol-designer/src/components/StepEditForm/forms/MixForm.tsx index f976c19c129..91ace178476 100644 --- a/protocol-designer/src/components/StepEditForm/forms/MixForm.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/MixForm.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { FormGroup } from '@opentrons/components' @@ -23,11 +22,11 @@ import { } from '../utils' import { AspDispSection } from './AspDispSection' -import type { StepFormProps } from '../types' +import { StepFormProps } from '../types' import styles from '../StepEditForm.css' -export const MixForm = (props: StepFormProps): React.Node => { +export const MixForm = (props: StepFormProps): JSX.Element => { const [collapsed, setCollapsed] = React.useState(true) const { propsForFields, formData } = props diff --git a/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/SourceDestFields.tsx b/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/SourceDestFields.tsx index 2e11b3689cf..93dea75c6b0 100644 --- a/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/SourceDestFields.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/SourceDestFields.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import { i18n } from '../../../../localization' @@ -17,28 +16,28 @@ import { getLabwareFieldForPositioningField, } from '../../utils' -import type { FormData } from '../../../../form-types' -import type { StepFieldName } from '../../../../steplist/fieldLevel' -import type { FieldPropsByName } from '../../types' +import { FormData } from '../../../../form-types' +import { StepFieldName } from '../../../../steplist/fieldLevel' +import { FieldPropsByName } from '../../types' import styles from '../../StepEditForm.css' -type Props = {| - className?: ?string, - prefix: 'aspirate' | 'dispense', - propsForFields: FieldPropsByName, - formData: FormData, -|} +interface SourceDestFieldsProps { + className?: string | null + prefix: 'aspirate' | 'dispense' + propsForFields: FieldPropsByName + formData: FormData +} const makeAddFieldNamePrefix = (prefix: string) => ( fieldName: string ): StepFieldName => `${prefix}_${fieldName}` -export const SourceDestFields = (props: Props): React.Node => { +export const SourceDestFields = (props: SourceDestFieldsProps): JSX.Element => { const { className, formData, prefix, propsForFields } = props const addFieldNamePrefix = makeAddFieldNamePrefix(prefix) - const getDelayFields = () => ( + const getDelayFields = (): JSX.Element => ( { /> ) - const getMixFields = () => ( + const getMixFields = (): JSX.Element => ( { ) return ( + // @ts-expect-error(sa, 2021-7-2): className might be null
void, -|} +interface Props { + className?: string | null + collapsed?: boolean | null + formData: FormData + prefix: 'aspirate' | 'dispense' + propsForFields: FieldPropsByName + toggleCollapsed: () => void +} const makeAddFieldNamePrefix = (prefix: string) => ( fieldName: string ): StepFieldName => `${prefix}_${fieldName}` -export const SourceDestHeaders = (props: Props): React.Node => { +export const SourceDestHeaders = (props: Props): JSX.Element => { const { className, collapsed, diff --git a/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/index.tsx b/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/index.tsx index 577815faa2d..0afcd2ebc5e 100644 --- a/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/index.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/MoveLiquidForm/index.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { i18n } from '../../../../localization' @@ -10,17 +9,17 @@ import { PathField, } from '../../fields' import styles from '../../StepEditForm.css' -import type { StepFormProps } from '../../types' +import { StepFormProps } from '../../types' import { SourceDestFields } from './SourceDestFields' import { SourceDestHeaders } from './SourceDestHeaders' // TODO: BC 2019-01-25 instead of passing path from here, put it in connect fields where needed // or question if it even needs path -export const MoveLiquidForm = (props: StepFormProps): React.Node => { +export const MoveLiquidForm = (props: StepFormProps): JSX.Element => { const [collapsed, _setCollapsed] = React.useState(true) - const toggleCollapsed = () => _setCollapsed(!collapsed) + const toggleCollapsed = (): void => _setCollapsed(!collapsed) const { propsForFields, formData } = props const { stepType, path } = formData diff --git a/protocol-designer/src/components/StepEditForm/forms/PauseForm.tsx b/protocol-designer/src/components/StepEditForm/forms/PauseForm.tsx index 862c2149e64..538af43835a 100644 --- a/protocol-designer/src/components/StepEditForm/forms/PauseForm.tsx +++ b/protocol-designer/src/components/StepEditForm/forms/PauseForm.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react' import cx from 'classnames' import { useSelector } from 'react-redux' @@ -21,9 +20,9 @@ import { TextField, RadioGroupField, StepFormDropdown } from '../fields' import { getSingleSelectDisabledTooltip } from '../utils' import styles from '../StepEditForm.css' -import type { StepFormProps } from '../types' +import { StepFormProps } from '../types' -export const PauseForm = (props: StepFormProps): React.Node => { +export const PauseForm = (props: StepFormProps): JSX.Element => { const moduleLabwareOptions = useSelector( uiModuleSelectors.getTemperatureLabwareOptions ) @@ -154,8 +153,8 @@ export const PauseForm = (props: StepFormProps): React.Node => { >