Skip to content

Commit

Permalink
add groups actions and wire up
Browse files Browse the repository at this point in the history
  • Loading branch information
jerader committed Jul 22, 2024
1 parent 4e8dce9 commit aabb1b7
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 27 deletions.
9 changes: 7 additions & 2 deletions app/src/organisms/ProtocolDetails/AnnotatedSteps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function AnnotatedSteps(props: AnnotatedStepsProps): JSX.Element {
const annotations = analysis.commandAnnotations ?? [
{
annotationType: 'secondOrderCommand',
machineReadableName: 'pips and mods',
machineReadableName: 'pipettes and module load commands',
params: {},
commandKeys: [
'a1b95079-5b17-428d-b40c-a8236a9890c5',
Expand Down Expand Up @@ -158,7 +158,12 @@ function AnnotatedGroup(props: AnnotatedGroupProps): JSX.Element {
const [isExpanded, setIsExpanded] = React.useState(false)
const backgroundColor = isHighlighted ? COLORS.blue30 : COLORS.grey20
return (
<Flex onClick={() => setIsExpanded(!isExpanded)} cursor="pointer">
<Flex
onClick={() => {
setIsExpanded(!isExpanded)
}}
cursor="pointer"
>
{isExpanded ? (
<Flex flexDirection={DIRECTION_COLUMN}>
<Flex
Expand Down
61 changes: 59 additions & 2 deletions protocol-designer/src/components/steplist/StepList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import * as React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { SidePanel } from '@opentrons/components'
import {
DIRECTION_COLUMN,
Flex,
Modal,
PrimaryButton,
SPACING,
SecondaryButton,
SidePanel,
} from '@opentrons/components'

import { END_TERMINAL_TITLE } from '../../constants'
import {
Expand All @@ -18,6 +26,12 @@ import { TerminalItem } from './TerminalItem'

import type { StepIdType } from '../../form-types'
import type { ThunkDispatch } from '../../types'
import { getUnsavedGroup } from '../../step-forms/selectors'
import {
addStepToGroup,
clearGroup,
createGroup,
} from '../../step-forms/actions/groups'

export interface StepListProps {
isMultiSelectMode?: boolean | null
Expand All @@ -30,6 +44,19 @@ export const StepList = (): JSX.Element => {
const orderedStepIds = useSelector(stepFormSelectors.getOrderedStepIds)
const isMultiSelectMode = useSelector(getIsMultiSelectMode)
const dispatch = useDispatch<ThunkDispatch<any>>()
const [group, setGroup] = React.useState<boolean>(false)

const [groupName, setGroupName] = React.useState<string>('')
const stepIds = useSelector(getUnsavedGroup)

const handleCreateGroup = (): void => {
if (groupName && stepIds.length > 0) {
dispatch(createGroup({ groupName }))
dispatch(addStepToGroup({ groupName, stepIds }))
dispatch(clearGroup())
setGroupName('')
}
}

const handleKeyDown: (e: KeyboardEvent) => void = e => {
const key = e.key
Expand Down Expand Up @@ -59,8 +86,38 @@ export const StepList = (): JSX.Element => {
}
}, [])

return (
return group ? (
<Modal>
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing16}>
<PrimaryButton
onClick={() => {
setGroup(false)
}}
>
close
</PrimaryButton>
<input
type="text"
value={groupName}
onChange={e => {
setGroupName(e.target.value)
}}
placeholder="Enter group name"
/>
<SecondaryButton onClick={handleCreateGroup}>
create group
</SecondaryButton>
</Flex>
</Modal>
) : (
<SidePanel title="Protocol Timeline">
<PrimaryButton
onClick={() => {
setGroup(true)
}}
>
make group
</PrimaryButton>
<MultiSelectToolbar isMultiSelectMode={Boolean(isMultiSelectMode)} />

<StartingDeckStateTerminalItem />
Expand Down
31 changes: 25 additions & 6 deletions protocol-designer/src/containers/ConnectedStepItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import uniq from 'lodash/uniq'
import UAParser from 'ua-parser-js'
import { useConditionalConfirm } from '@opentrons/components'
import { Box, Btn, Icon, useConditionalConfirm } from '@opentrons/components'

import { selectors as uiLabwareSelectors } from '../ui/labware'
import * as timelineWarningSelectors from '../top-selectors/timelineWarnings'
Expand Down Expand Up @@ -30,7 +30,9 @@ import {
import {
getAdditionalEquipmentEntities,
getInitialDeckSetup,
getUnsavedGroup,
} from '../step-forms/selectors'
import { selectStepForGroup } from '../step-forms/actions/groups'

import type { ThunkDispatch } from 'redux-thunk'
import type {
Expand Down Expand Up @@ -73,7 +75,7 @@ export const ConnectedStepItem = (
props: ConnectedStepItemProps
): JSX.Element => {
const { stepId, stepNumber } = props

const unsavedGroup = useSelector(getUnsavedGroup)
const step = useSelector(stepFormSelectors.getSavedStepForms)[stepId]
const argsAndErrors = useSelector(stepFormSelectors.getArgsAndErrorsByStepId)[
stepId
Expand Down Expand Up @@ -140,6 +142,10 @@ export const ConnectedStepItem = (
const unhighlightStep = (): HoverOnStepAction =>
dispatch(stepsActions.hoverOnStep(null))

const addStep = (stepId: string): void => {
dispatch(selectStepForGroup({ stepId }))
}

const handleStepItemSelection = (event: React.MouseEvent): void => {
const { isShiftKeyPressed, isMetaKeyPressed } = getMouseClickKeyInfo(event)
let stepsToSelect: StepIdType[] = []
Expand Down Expand Up @@ -230,7 +236,8 @@ export const ConnectedStepItem = (
highlightSubstep,
hoveredSubstep,
}

const name = unsavedGroup.includes(stepId) ? 'ot-checkbox' : 'minus-box'
console.log('unsavedGroup', unsavedGroup)
const getModalType = (): DeleteModalType => {
if (isMultiSelectMode) {
return CLOSE_BATCH_EDIT_FORM
Expand All @@ -249,9 +256,21 @@ export const ConnectedStepItem = (
onCancelClick={cancel}
/>
)}
<StepItem {...stepItemProps} onStepContextMenu={props.onStepContextMenu}>
<StepItemContents {...stepItemContentsProps} />
</StepItem>
<Box>
<Btn
onClick={() => {
addStep(stepId)
}}
>
<Icon name={name} width="2rem" height="2rem" />
</Btn>
<StepItem
{...stepItemProps}
onStepContextMenu={props.onStepContextMenu}
>
<StepItemContents {...stepItemContentsProps} />
</StepItem>
</Box>
</>
)
}
Expand Down
44 changes: 31 additions & 13 deletions protocol-designer/src/file-data/selectors/fileCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
DEFAULT_MM_TOUCH_TIP_OFFSET_FROM_TOP,
DEFAULT_MM_BLOWOUT_OFFSET_FROM_TOP,
} from '../../constants'
import { getStepGroups } from '../../step-forms/selectors'
import { getFileMetadata, getRobotType } from './fileFields'
import { getInitialRobotState, getRobotStateTimeline } from './commands'

Expand Down Expand Up @@ -57,7 +58,7 @@ import type {
import type { LabwareDefByDefURI } from '../../labware-defs'
import type { Selector } from '../../types'
import type { DesignerApplicationData } from '../../load-file/migration/utils/getLoadLiquidCommands'
import { SecondOrderCommandAnnotation } from '@opentrons/shared-data/commandAnnotation/types'
import type { SecondOrderCommandAnnotation } from '@opentrons/shared-data/commandAnnotation/types'

// TODO: BC: 2018-02-21 uncomment this assert, causes test failures
// console.assert(!isEmpty(process.env.OT_PD_VERSION), 'Could not find application version!')
Expand Down Expand Up @@ -112,6 +113,7 @@ export const createFile: Selector<ProtocolFile> = createSelector(
stepFormSelectors.getPipetteEntities,
uiLabwareSelectors.getLabwareNicknamesById,
labwareDefSelectors.getLabwareDefsByURI,
getStepGroups,
(
fileMetadata,
initialRobotState,
Expand All @@ -126,9 +128,11 @@ export const createFile: Selector<ProtocolFile> = createSelector(
moduleEntities,
pipetteEntities,
labwareNicknamesById,
labwareDefsByURI
labwareDefsByURI,
stepGroups
) => {
const { author, description, created } = fileMetadata

const name = fileMetadata.protocolName || 'untitled'
const lastModified = fileMetadata.lastModified
// TODO: Ian 2018-07-10 allow user to save steps in JSON file, even if those
Expand Down Expand Up @@ -382,19 +386,33 @@ export const createFile: Selector<ProtocolFile> = createSelector(
commands,
}

const annotationExample: SecondOrderCommandAnnotation = {
annotationType: 'secondOrderCommand',
machineReadableName: 'pips and mods',
params: {},
commandKeys: [
'a1b95079-5b17-428d-b40c-a8236a9890c5',
'6f1e3ad3-8f03-4583-8031-be6be2fcd903',
'4997a543-7788-434f-8eae-1c4aa3a2a805',
],
}
const commandAnnotations: SecondOrderCommandAnnotation[] = Object.entries(
stepGroups
).map(([name, groupStepIds]) => {
// map stepIds from group to orderedStepIds and return indices from orderedStepIds
const stepIndices = groupStepIds
.map(groupStepId => orderedStepIds.indexOf(groupStepId))
.filter(index => index !== -1)

// return commands assosciated with the indices
const commands = stepIndices.flatMap(
index => robotStateTimeline.timeline[index].commands
)
const commandKeys = commands.map(command => command.key ?? '')

const annotation: SecondOrderCommandAnnotation = {
annotationType: 'secondOrderCommand',
machineReadableName: name,
params: {},
commandKeys,
}

return annotation
})

const commandAnnotionaV1Mixin: CommandAnnotationV1Mixin = {
commandAnnotationSchemaId: 'opentronsCommandAnnotationSchemaV1',
commandAnnotations: [annotationExample],
commandAnnotations,
}

const protocolBase: ProtocolBase<DesignerApplicationData> = {
Expand Down
47 changes: 47 additions & 0 deletions protocol-designer/src/step-forms/actions/groups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
ADD_STEPS_TO_GROUP,
CLEAR_GROUP,
CREATE_GROUP,
SELECT_STEP_FOR_GROUP,
} from '../reducers'

export interface SaveGroupAction {
type: typeof CREATE_GROUP
payload: { groupName: string }
}
export interface AddStepToGroupAction {
type: typeof ADD_STEPS_TO_GROUP
payload: { groupName: string; stepIds: string[] }
}
export interface ClearGroupAction {
type: typeof CLEAR_GROUP
}
export interface SelectedStepForGroupAction {
type: typeof SELECT_STEP_FOR_GROUP
payload: { stepId: string }
}

export const addStepToGroup = (
args: AddStepToGroupAction['payload']
): AddStepToGroupAction => ({
type: ADD_STEPS_TO_GROUP,
payload: args,
})

export const createGroup = (
args: SaveGroupAction['payload']
): SaveGroupAction => ({
type: CREATE_GROUP,
payload: args,
})

export const selectStepForGroup = (
args: SelectedStepForGroupAction['payload']
): SelectedStepForGroupAction => ({
type: SELECT_STEP_FOR_GROUP,
payload: args,
})

export const clearGroup = (): ClearGroupAction => ({
type: CLEAR_GROUP,
})
52 changes: 52 additions & 0 deletions protocol-designer/src/step-forms/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1674,6 +1674,54 @@ export const additionalEquipmentInvariantProperties = handleActions<NormalizedAd
},
initialAdditionalEquipmentState
)
export const ADD_STEPS_TO_GROUP = 'ADD_STEPS_TO_GROUP'
export const CREATE_GROUP = 'CREATE_GROUP'
export type StepGroupsState = Record<string, StepIdType[]>
const initialStepGroupState = {}
const stepGroups: Reducer<StepGroupsState, any> = handleActions<
StepGroupsState,
any
>(
{
CREATE_GROUP: (state, action) => {
return {
...state,
[action.payload.groupName]: [],
}
},
ADD_STEPS_TO_GROUP: (state, action) => {
return {
...state,
[action.payload.groupName]: [
...state[action.payload.groupName],
...action.payload.stepIds,
],
}
},
},
initialStepGroupState
)
export type UnsavedGroupState = StepIdType[]
export const SELECT_STEP_FOR_GROUP = 'SELECT_STEP_FOR_GROUP'
export const CLEAR_GROUP = 'CLEAR_GROUP'
const initialUnsavedGroupState: StepIdType[] = []
const unsavedGroup: Reducer<UnsavedGroupState, any> = handleActions<
UnsavedGroupState,
any
>(
{
SELECT_STEP_FOR_GROUP: (state, action) => {
if (action.type === SELECT_STEP_FOR_GROUP) {
return [...state, action.payload.stepId]
}
return state
},
CLEAR_GROUP: () => {
return []
},
},
initialUnsavedGroupState
)

export type OrderedStepIdsState = StepIdType[]
const initialOrderedStepIdsState: string[] = []
Expand Down Expand Up @@ -1790,6 +1838,8 @@ export const presavedStepForm = (
}
}
export interface RootState {
unsavedGroup: UnsavedGroupState
stepGroups: StepGroupsState
orderedStepIds: OrderedStepIdsState
labwareDefs: LabwareDefsRootState
labwareInvariantProperties: NormalizedLabwareById
Expand All @@ -1806,6 +1856,8 @@ export interface RootState {
// TODO: Ian 2018-12-13 remove this 'action: any' type
export const rootReducer: Reducer<RootState, any> = nestedCombineReducers(
({ action, state, prevStateFallback }) => ({
unsavedGroup: unsavedGroup(prevStateFallback.unsavedGroup, action),
stepGroups: stepGroups(prevStateFallback.stepGroups, action),
orderedStepIds: orderedStepIds(prevStateFallback.orderedStepIds, action),
labwareInvariantProperties: labwareInvariantProperties(
prevStateFallback.labwareInvariantProperties,
Expand Down
Loading

0 comments on commit aabb1b7

Please sign in to comment.