Skip to content

Commit

Permalink
feat(protocol-designer): step edit form opens on edit button (#16378)
Browse files Browse the repository at this point in the history
closes Auth-879
  • Loading branch information
jerader authored Oct 1, 2024
1 parent d7ae1cd commit 88d7237
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"add_details": "Add step details",
"change_tips": "Change tips",
"default_tip_option": "Default - get next tip",
"delete": "Delete step",
"duplicate": "Duplicate step",
"edit_step": "Edit step",
"final_deck_state": "Final deck state",
"heater_shaker_settings": "Heater-shaker settings",
"module": "Module",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import { StepContainer } from './StepContainer'

import type { ThunkDispatch } from 'redux-thunk'
import type { HoverOnStepAction } from '../../../../ui/steps'
import type { DeleteModalType } from '../../../../components/modals/ConfirmDeleteModal'
import type { StepIdType } from '../../../../form-types'
import type { BaseState, ThunkAction } from '../../../../types'
import type { DeleteModalType } from '../../../../components/modals/ConfirmDeleteModal'

export interface ConnectedStepInfoProps {
stepId: StepIdType
Expand Down Expand Up @@ -63,27 +63,31 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
const selected: boolean = multiSelectItemIds?.length
? multiSelectItemIds.includes(stepId)
: selectedStepId === stepId

const currentFormIsPresaved = useSelector(
stepFormSelectors.getCurrentFormIsPresaved
)
const singleEditFormHasUnsavedChanges = useSelector(
stepFormSelectors.getCurrentFormHasUnsavedChanges
)

const selectStep = (): ThunkAction<any> =>
dispatch(stepsActions.resetSelectStep(stepId))
const selectStepOnDoubleClick = (): ThunkAction<any> =>
dispatch(stepsActions.selectStep(stepId))
const highlightStep = (): HoverOnStepAction =>
dispatch(stepsActions.hoverOnStep(stepId))
const unhighlightStep = (): HoverOnStepAction =>
dispatch(stepsActions.hoverOnStep(null))

const handleStepItemSelection = (): void => {
selectStep()
}

const {
confirm: confirmDoubleClick,
showConfirmation: showConfirmationDoubleClick,
cancel: cancelDoubleClick,
} = useConditionalConfirm(
selectStepOnDoubleClick,
currentFormIsPresaved || singleEditFormHasUnsavedChanges
)
const { confirm, showConfirmation, cancel } = useConditionalConfirm(
handleStepItemSelection,
selectStep,
currentFormIsPresaved || singleEditFormHasUnsavedChanges
)

Expand All @@ -94,10 +98,20 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
return CLOSE_STEP_FORM_WITH_CHANGES
}
}

const iconName = stepIconsByType[step.stepType]

return (
<>
{/* TODO: update this modal */}
{showConfirmationDoubleClick && (
<ConfirmDeleteModal
modalType={getModalType()}
onContinueClick={confirmDoubleClick}
onCancelClick={cancelDoubleClick}
/>
)}
{/* TODO: update this modal */}
{showConfirmation && (
<ConfirmDeleteModal
modalType={getModalType()}
Expand All @@ -111,6 +125,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element {
stepId={stepId}
onMouseLeave={unhighlightStep}
selected={selected}
onDoubleClick={confirmDoubleClick}
onClick={confirm}
hovered={hoveredStep === stepId && !hoveredSubstep}
onMouseEnter={highlightStep}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react'
import { useSelector } from 'react-redux'
import { createPortal } from 'react-dom'
import {
ALIGN_CENTER,
Expand All @@ -11,14 +10,12 @@ import {
CURSOR_POINTER,
Flex,
Icon,
JUSTIFY_CENTER,
JUSTIFY_SPACE_BETWEEN,
JUSTIFY_START,
OverflowBtn,
SPACING,
StyledText,
} from '@opentrons/components'
import { getUnsavedForm } from '../../../../step-forms/selectors'
import { getTopPortalEl } from '../../../../components/portals/TopPortal'
import { StepOverflowMenu } from './StepOverflowMenu'
import { capitalizeFirstLetterAfterNumber } from './utils'
Expand All @@ -34,6 +31,7 @@ export interface StepContainerProps {
stepId?: string
iconColor?: string
onClick?: (event: React.MouseEvent) => void
onDoubleClick?: (event: React.MouseEvent) => void
onMouseEnter?: (event: React.MouseEvent) => void
onMouseLeave?: (event: React.MouseEvent) => void
selected?: boolean
Expand All @@ -46,6 +44,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
const {
stepId,
iconName,
onDoubleClick,
onMouseEnter,
onMouseLeave,
selected,
Expand All @@ -56,7 +55,6 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
hasError = false,
isStepAfterError = false,
} = props
const formData = useSelector(getUnsavedForm)
const [top, setTop] = React.useState<number>(0)
const menuRootRef = React.useRef<HTMLDivElement | null>(null)
const [stepOverflowMenu, setStepOverflowMenu] = React.useState<boolean>(false)
Expand Down Expand Up @@ -121,10 +119,11 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
}}
>
<Btn
onDoubleClick={onDoubleClick}
onClick={onClick}
padding={SPACING.spacing12}
borderRadius={BORDERS.borderRadius8}
width={formData != null ? '6rem' : '100%'}
width="100%"
backgroundColor={backgroundColor}
color={color}
opacity={isStepAfterError ? '50%' : '100%'}
Expand All @@ -138,19 +137,17 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
<Flex
alignItems={ALIGN_CENTER}
gridGap={SPACING.spacing8}
justifyContent={formData != null ? JUSTIFY_CENTER : JUSTIFY_START}
justifyContent={JUSTIFY_START}
width="100%"
>
{iconName && (
<Icon size="1rem" name={iconName} color={iconColor ?? color} />
)}
{formData != null ? null : (
<StyledText desktopStyle="bodyDefaultRegular">
{capitalizeFirstLetterAfterNumber(title)}
</StyledText>
)}
<StyledText desktopStyle="bodyDefaultRegular">
{capitalizeFirstLetterAfterNumber(title)}
</StyledText>
</Flex>
{selected && !isStartingOrEndingState && formData == null ? (
{selected && !isStartingOrEndingState ? (
<OverflowBtn
data-testid={`StepContainer_${stepId}`}
fillColor={COLORS.white}
Expand All @@ -168,6 +165,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element {
{stepOverflowMenu && stepId != null
? createPortal(
<StepOverflowMenu
setStepOverflowMenu={setStepOverflowMenu}
stepId={stepId}
menuRootRef={menuRootRef}
top={top}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type * as React from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { useDispatch } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'

import {
ALIGN_CENTER,
Expand All @@ -13,79 +12,141 @@ import {
NO_WRAP,
POSITION_ABSOLUTE,
SPACING,
useConditionalConfirm,
} from '@opentrons/components'
import { actions as steplistActions } from '../../../../steplist'
import { actions as stepsActions } from '../../../../ui/steps'

import { populateForm } from '../../../../ui/steps/actions/actions'
import {
CLOSE_STEP_FORM_WITH_CHANGES,
CLOSE_UNSAVED_STEP_FORM,
ConfirmDeleteModal,
DELETE_STEP_FORM,
} from '../../../../components/modals/ConfirmDeleteModal'
import {
getCurrentFormHasUnsavedChanges,
getCurrentFormIsPresaved,
getUnsavedForm,
} from '../../../../step-forms/selectors'
import type * as React from 'react'
import type { ThunkDispatch } from 'redux-thunk'
import type { BaseState } from '../../../../types'
import type { StepIdType } from '../../../../form-types'
import type { DeleteModalType } from '../../../../components/modals/ConfirmDeleteModal'

interface StepOverflowMenuProps {
stepId: string
menuRootRef: React.MutableRefObject<HTMLDivElement | null>
top: number
setStepOverflowMenu: React.Dispatch<React.SetStateAction<boolean>>
}

export function StepOverflowMenu(props: StepOverflowMenuProps): JSX.Element {
const { stepId, menuRootRef, top } = props
const { stepId, menuRootRef, top, setStepOverflowMenu } = props
const { t } = useTranslation('protocol_steps')
const dispatch = useDispatch<ThunkDispatch<BaseState, any, any>>()
const deleteStep = (stepId: StepIdType): void => {
dispatch(steplistActions.deleteStep(stepId))
}
const formData = useSelector(getUnsavedForm)
const currentFormIsPresaved = useSelector(getCurrentFormIsPresaved)
const singleEditFormHasUnsavedChanges = useSelector(
getCurrentFormHasUnsavedChanges
)
const duplicateStep = (
stepId: StepIdType
): ReturnType<typeof stepsActions.duplicateStep> =>
dispatch(stepsActions.duplicateStep(stepId))

const handleStepItemSelection = (): void => {
dispatch(populateForm(stepId))
setStepOverflowMenu(false)
}
const handleDelete = (): void => {
if (stepId != null) {
deleteStep(stepId)
} else {
console.warn(
'something went wrong, cannot delete a step without a step id'
)
}
}

const { confirm, showConfirmation, cancel } = useConditionalConfirm(
handleStepItemSelection,
currentFormIsPresaved || singleEditFormHasUnsavedChanges
)

const {
confirm: confirmDelete,
showConfirmation: showDeleteConfirmation,
cancel: cancelDelete,
} = useConditionalConfirm(handleDelete, true)

const getModalType = (): DeleteModalType => {
if (currentFormIsPresaved) {
return CLOSE_UNSAVED_STEP_FORM
} else {
return CLOSE_STEP_FORM_WITH_CHANGES
}
}

return (
<Flex
ref={menuRootRef}
zIndex={5}
top={top}
left="19.5rem"
position={POSITION_ABSOLUTE}
whiteSpace={NO_WRAP}
borderRadius={BORDERS.borderRadius8}
boxShadow="0px 1px 3px rgba(0, 0, 0, 0.2)"
backgroundColor={COLORS.white}
flexDirection={DIRECTION_COLUMN}
onClick={(e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
}}
>
<MenuButton
onClick={() => {
console.log('wire this up')
}}
>
{t('rename')}
</MenuButton>
<MenuButton
onClick={() => {
console.log('wire this up')
}}
>
{t('view_commands')}
</MenuButton>
<MenuButton
onClick={() => {
duplicateStep(stepId)
}}
>
{t('duplicate')}
</MenuButton>
<Divider marginY="0" />
<MenuButton
onClick={() => {
deleteStep(stepId)
<>
{/* TODO: update this modal */}
{showConfirmation && (
<ConfirmDeleteModal
modalType={getModalType()}
onContinueClick={confirm}
onCancelClick={cancel}
/>
)}
{/* TODO: update this modal */}
{showDeleteConfirmation && (
<ConfirmDeleteModal
modalType={DELETE_STEP_FORM}
onCancelClick={cancelDelete}
onContinueClick={confirmDelete}
/>
)}
<Flex
ref={menuRootRef}
zIndex={5}
top={top}
left="19.5rem"
position={POSITION_ABSOLUTE}
whiteSpace={NO_WRAP}
borderRadius={BORDERS.borderRadius8}
boxShadow="0px 1px 3px rgba(0, 0, 0, 0.2)"
backgroundColor={COLORS.white}
flexDirection={DIRECTION_COLUMN}
onClick={(e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
}}
>
{t('delete')}
</MenuButton>
</Flex>
{formData != null ? null : (
<MenuButton onClick={confirm}>{t('edit_step')}</MenuButton>
)}
<MenuButton
disabled={formData != null}
onClick={() => {
console.log('wire this up')
}}
>
{t('view_commands')}
</MenuButton>
<MenuButton
onClick={() => {
duplicateStep(stepId)
}}
>
{t('duplicate')}
</MenuButton>
<Divider marginY="0" />
<MenuButton onClick={confirmDelete}>{t('delete')}</MenuButton>
</Flex>
</>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const TimelineToolbox = (): JSX.Element => {

return (
<Toolbox
width={formData != null ? '8rem' : '19.5rem'}
width="19.5rem"
height={formData != null ? FLEX_MAX_CONTENT : 'calc(100vh - 78px)'}
side="left"
horizontalSide={formData != null ? 'top' : 'bottom'}
Expand Down
Loading

0 comments on commit 88d7237

Please sign in to comment.