diff --git a/protocol-designer/src/assets/localization/en/button.json b/protocol-designer/src/assets/localization/en/button.json index bedb95b28c1..af643b9b815 100644 --- a/protocol-designer/src/assets/localization/en/button.json +++ b/protocol-designer/src/assets/localization/en/button.json @@ -1,5 +1,5 @@ { - "add_step": "+ Add Step", + "add_step": "Add Step", "add_off_deck": "+ Off-deck labware", "cancel": "cancel", "close": "close", diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx new file mode 100644 index 00000000000..210abdec6ba --- /dev/null +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx @@ -0,0 +1,119 @@ +import { useState, useRef, useCallback, useEffect } from 'react' +import styled from 'styled-components' +import { + Box, + COLORS, + DIRECTION_COLUMN, + DISPLAY_FLEX, + Flex, + JUSTIFY_SPACE_BETWEEN, +} from '@opentrons/components' +import { TimelineToolbox } from './Timeline/TimelineToolbox' + +const INITIAL_SIDEBAR_WIDTH = 276 +const MIN_SIDEBAR_WIDTH = 80 +const MAX_SIDEBAR_WIDTH = 350 + +interface DraggableSidebarProps { + setTargetWidth: (width: number) => void +} + +// Note (kk:2024/12/20 the designer will revisit responsive sidebar design in 2025 +// we will need to update the details to align with the updated design +export function DraggableSidebar({ + setTargetWidth, +}: DraggableSidebarProps): JSX.Element { + const sidebarRef = useRef(null) + const [isResizing, setIsResizing] = useState(false) + const [sidebarWidth, setSidebarWidth] = useState(INITIAL_SIDEBAR_WIDTH) + + const startResizing = useCallback(() => { + setIsResizing(true) + }, []) + + const stopResizing = useCallback(() => { + setIsResizing(false) + }, []) + + const resize = useCallback( + (mouseMoveEvent: MouseEvent) => { + if (isResizing && sidebarRef.current != null) { + const newWidth = + mouseMoveEvent.clientX - + sidebarRef.current.getBoundingClientRect().left + + if (newWidth >= MIN_SIDEBAR_WIDTH && newWidth <= MAX_SIDEBAR_WIDTH) { + setSidebarWidth(newWidth) + setTargetWidth(newWidth) + } + } + }, + [isResizing, setTargetWidth] + ) + + useEffect(() => { + window.addEventListener('mousemove', resize) + window.addEventListener('mouseup', stopResizing) + + return () => { + window.removeEventListener('mousemove', resize) + window.removeEventListener('mouseup', stopResizing) + } + }, [resize, stopResizing]) + + return ( + + + + + + + + + ) +} + +const SidebarContainer = styled(Box)` + display: ${DISPLAY_FLEX}; + flex-direction: ${DIRECTION_COLUMN}; + border-right: 1px solid #ccc; + position: relative; + /* overflow: hidden; */ + height: 100%; +` + +const SidebarContent = styled(Flex)` + flex: 1; +` + +interface SidebarResizerProps { + dragging: boolean +} + +const SidebarResizer = styled(Flex)` + user-select: none; + width: 2px; + cursor: ew-resize; + background-color: #ddd; + position: absolute; + top: 0; + right: 0; + bottom: 0; + margin: 0; + padding: 0; + transition: background-color 0.2s ease; + + &:hover { + background-color: ${COLORS.blue50}; /* Hover state */ + } + + ${props => + props.dragging === true && + ` + background-color: ${COLORS.blue55}; /* Dragging state */ + `} +` diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx index f9c2aa9d395..e9494eb6e44 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx @@ -2,19 +2,27 @@ import { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' +import { css } from 'styled-components' + import { - useHoverTooltip, - TOOLTIP_TOP, - TOOLTIP_FIXED, - Tooltip, + ALIGN_CENTER, + BORDERS, COLORS, DIRECTION_COLUMN, + DISPLAY_FLEX, Flex, - POSITION_ABSOLUTE, - BORDERS, + Icon, + JUSTIFY_CENTER, NO_WRAP, - useOnClickOutside, + POSITION_ABSOLUTE, SecondaryButton, + SPACING, + StyledText, + TOOLTIP_FIXED, + TOOLTIP_TOP, + Tooltip, + useHoverTooltip, + useOnClickOutside, } from '@opentrons/components' import { ABSORBANCE_READER_TYPE, @@ -23,6 +31,7 @@ import { TEMPERATURE_MODULE_TYPE, THERMOCYCLER_MODULE_TYPE, } from '@opentrons/shared-data' + import { actions as stepsActions, getIsMultiSelectMode, @@ -40,7 +49,6 @@ import { getEnableAbsorbanceReader, getEnableComment, } from '../../../../feature-flags/selectors' - import { AddStepOverflowButton } from './AddStepOverflowButton' import type { MouseEvent } from 'react' @@ -48,7 +56,11 @@ import type { ThunkDispatch } from 'redux-thunk' import type { BaseState } from '../../../../types' import type { StepType } from '../../../../form-types' -export function AddStepButton(): JSX.Element { +interface AddStepButtonProps { + hasText: boolean +} + +export function AddStepButton({ hasText }: AddStepButtonProps): JSX.Element { const { t } = useTranslation(['tooltip', 'button']) const enableComment = useSelector(getEnableComment) const dispatch = useDispatch>() @@ -151,16 +163,8 @@ export function AddStepButton(): JSX.Element { {showStepOverflowMenu ? ( { e.preventDefault() e.stopPropagation() @@ -176,6 +180,10 @@ export function AddStepButton(): JSX.Element { )} - {t('button:add_step')} + + {hasText ? {t('button:add_step')} : null} ) } + +const STEP_OVERFLOW_MENU_STYLE = css` + position: ${POSITION_ABSOLUTE}; + z-index: 5; + right: -7.75rem; + white-space: ${NO_WRAP}; + bottom: 4.2rem; + border-radius: ${BORDERS.borderRadius8}; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2); + background-color: ${COLORS.white}; + flex-direction: ${DIRECTION_COLUMN}; +` diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx index 70cdf70a984..1a9ac29f33d 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx @@ -49,6 +49,7 @@ export interface ConnectedStepInfoProps { dragHovered?: boolean openedOverflowMenuId?: string | null setOpenedOverflowMenuId?: Dispatch> + sidebarWidth: number } export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { @@ -58,6 +59,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { dragHovered = false, openedOverflowMenuId, setOpenedOverflowMenuId, + sidebarWidth, } = props const { t } = useTranslation('application') const dispatch = useDispatch>() @@ -227,6 +229,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { step.stepName || t(`stepType.${step.stepType}`) }`} dragHovered={dragHovered} + sidebarWidth={sidebarWidth} /> ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx index 02e1ad772fa..592cdec02f3 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx @@ -31,6 +31,7 @@ interface DragDropStepProps extends ConnectedStepItemProps { orderedStepIds: string[] openedOverflowMenuId?: string | null setOpenedOverflowMenuId?: Dispatch> + sidebarWidth: number } interface DropType { @@ -46,6 +47,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element { stepNumber, openedOverflowMenuId, setOpenedOverflowMenuId, + sidebarWidth, } = props const stepRef = useRef(null) @@ -94,6 +96,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element { stepNumber={stepNumber} stepId={stepId} dragHovered={hovered} + sidebarWidth={sidebarWidth} /> ) @@ -102,9 +105,10 @@ function DragDropStep(props: DragDropStepProps): JSX.Element { interface DraggableStepsProps { orderedStepIds: StepIdType[] reorderSteps: (steps: StepIdType[]) => void + sidebarWidth: number } export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null { - const { orderedStepIds, reorderSteps } = props + const { orderedStepIds, reorderSteps, sidebarWidth } = props const { t } = useTranslation('shared') const [openedOverflowMenuId, setOpenedOverflowMenuId] = useState< string | null @@ -146,14 +150,21 @@ export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null { orderedStepIds={orderedStepIds} openedOverflowMenuId={openedOverflowMenuId} setOpenedOverflowMenuId={setOpenedOverflowMenuId} + sidebarWidth={sidebarWidth} /> ))} - + ) } -function StepDragPreview(): JSX.Element | null { +interface StepDragPreviewProps { + sidebarWidth: number +} + +function StepDragPreview({ + sidebarWidth, +}: StepDragPreviewProps): JSX.Element | null { const [{ isDragging, itemType, item, currentOffset }] = useDrag(() => ({ type: DND_TYPES.STEP_ITEM, collect: (monitor: DragLayerMonitor) => ({ @@ -182,6 +193,7 @@ function StepDragPreview(): JSX.Element | null { ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx index 9d0289ddb9c..346c296855f 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx @@ -10,7 +10,13 @@ import { } from '../../../../ui/steps' import { StepContainer } from './StepContainer' -export function PresavedStep(): JSX.Element | null { +interface PresavedStepProps { + sidebarWidth: number +} + +export function PresavedStep({ + sidebarWidth, +}: PresavedStepProps): JSX.Element | null { const { t } = useTranslation('application') const presavedStepForm = useSelector(stepFormSelectors.getPresavedStepForm) const stepNumber = useSelector(stepFormSelectors.getOrderedStepIds).length + 1 @@ -39,6 +45,7 @@ export function PresavedStep(): JSX.Element | null { hovered={hovered} iconName={stepIconsByType[stepType]} title={`${stepNumber}. ${t(`stepType.${stepType}`)}`} + sidebarWidth={sidebarWidth} /> ) } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx index 3db0802fbd5..d0534b234cf 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx @@ -12,6 +12,7 @@ import { Divider, Flex, Icon, + JUSTIFY_CENTER, JUSTIFY_SPACE_BETWEEN, JUSTIFY_START, OverflowBtn, @@ -48,9 +49,12 @@ import type { BaseState } from '../../../../types' const STARTING_DECK_STATE = 'Starting deck' const FINAL_DECK_STATE = 'Ending deck' const PX_HEIGHT_TO_TOP_OF_CONTAINER = 32 +const PX_SIDEBAR_MIN_WIDTH_FOR_ICON = 179 + export interface StepContainerProps { title: string iconName: IconName + sidebarWidth: number openedOverflowMenuId?: string | null setOpenedOverflowMenuId?: Dispatch> stepId?: string @@ -83,6 +87,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element { dragHovered = false, setOpenedOverflowMenuId, openedOverflowMenuId, + sidebarWidth, } = props const [top, setTop] = useState(0) const menuRootRef = useRef(null) @@ -91,6 +96,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element { const dispatch = useDispatch>() const multiSelectItemIds = useSelector(getMultiSelectItemIds) + const hasText = sidebarWidth > PX_SIDEBAR_MIN_WIDTH_FOR_ICON let backgroundColor = isStartingOrEndingState ? COLORS.blue20 : COLORS.grey20 let color = COLORS.black90 if (selected) { @@ -183,14 +189,14 @@ export function StepContainer(props: StepContainerProps): JSX.Element { return ( <> - {showDeleteConfirmation && ( + {showDeleteConfirmation === true && ( )} - {showMultiDeleteConfirmation && ( + {showMultiDeleteConfirmation === true && ( - {iconName && ( + {iconName != null && ( )} - - {capitalizeFirstLetterAfterNumber(title)} - + {hasText ? ( + + {capitalizeFirstLetterAfterNumber(title)} + + ) : null} {selected && !isStartingOrEndingState ? ( ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx index 4f99cda8f54..7e7b0d83b6c 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex, + OVERFLOW_WRAP_ANYWHERE, POSITION_RELATIVE, SPACING, StyledText, @@ -27,7 +28,14 @@ import { DraggableSteps } from './DraggableSteps' import type { StepIdType } from '../../../../form-types' import type { ThunkDispatch } from '../../../../types' -export const TimelineToolbox = (): JSX.Element => { +const SIDEBAR_MIN_WIDTH_FOR_ICON = 179 +interface TimelineToolboxProps { + sidebarWidth: number +} + +export const TimelineToolbox = ({ + sidebarWidth, +}: TimelineToolboxProps): JSX.Element => { const { t } = useTranslation('protocol_steps') const orderedStepIds = useSelector(stepFormSelectors.getOrderedStepIds) const formData = useSelector(getUnsavedForm) @@ -64,30 +72,44 @@ export const TimelineToolbox = (): JSX.Element => { position={POSITION_RELATIVE} height="100%" maxHeight={`calc(100vh - ${NAV_BAR_HEIGHT_REM}rem - 2 * ${SPACING.spacing12})`} - width="19.5rem" + width={`${sidebarWidth / 16}rem`} title={ - + {t('timeline')} } titlePadding={SPACING.spacing12} childrenPadding={SPACING.spacing12} - confirmButton={formData != null ? undefined : } + confirmButton={ + formData != null ? undefined : ( + SIDEBAR_MIN_WIDTH_FOR_ICON} /> + ) + } > - + { dispatch(steplistActions.reorderSteps(stepIds)) }} + sidebarWidth={sidebarWidth} + /> + + - - ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx index 4b7b716b0c7..a2fcea0a7e2 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx @@ -1,4 +1,4 @@ -import { describe, it, vi, beforeEach } from 'vitest' +import { describe, it, vi, beforeEach, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { HEATERSHAKER_MODULE_TYPE, @@ -20,18 +20,25 @@ import { } from '../../../../../step-forms/selectors' import { getIsMultiSelectMode } from '../../../../../ui/steps' +import type { ComponentProps } from 'react' + vi.mock('../../../../../feature-flags/selectors') vi.mock('../../../../../ui/steps') vi.mock('../../../../../step-forms/selectors') -const render = () => { - return renderWithProviders(, { +const render = (props: ComponentProps) => { + return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('AddStepButton', () => { + let props: ComponentProps + beforeEach(() => { + props = { + hasText: true, + } vi.mocked(getEnableComment).mockReturnValue(true) vi.mocked(getCurrentFormIsPresaved).mockReturnValue(false) vi.mocked(getIsMultiSelectMode).mockReturnValue(false) @@ -73,8 +80,8 @@ describe('AddStepButton', () => { }) it('renders add step button and clicking on it renders the overflow menu with all modules', () => { - render() - fireEvent.click(screen.getByText('+ Add Step')) + render(props) + fireEvent.click(screen.getByText('Add Step')) screen.getByText('Comment') screen.getByText('Transfer') screen.getByText('Mix') @@ -84,4 +91,11 @@ describe('AddStepButton', () => { screen.getByText('Temperature') screen.getByText('Magnet') }) + + it('should not render texts if hasText is false', () => { + props.hasText = false + render(props) + const text = screen.queryByText('Add Step') + expect(text).toBeNull() + }) }) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx index 32df562e61a..4933dffe3ed 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx @@ -32,6 +32,7 @@ describe('StepContainer', () => { stepId: 'mockStepId', hasError: false, isStepAfterError: false, + sidebarWidth: 350, } vi.mocked(StepOverflowMenu).mockReturnValue(
mock StepOverflowMenu
diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx index c4ae078c572..f6b11ce36a5 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx @@ -6,25 +6,32 @@ import { getOrderedStepIds, getUnsavedForm, } from '../../../../../step-forms/selectors' -import { TimelineToolbox } from '../TimelineToolbox' import { TerminalItemStep } from '../TerminalItemStep' import { DraggableSteps } from '../DraggableSteps' import { PresavedStep } from '../PresavedStep' import { AddStepButton } from '../AddStepButton' +import { TimelineToolbox } from '../TimelineToolbox' + +import type { ComponentProps } from 'react' vi.mock('../AddStepButton') vi.mock('../DraggableSteps') vi.mock('../PresavedStep') vi.mock('../TerminalItemStep') vi.mock('../../../../../step-forms/selectors') -const render = () => { - return renderWithProviders(, { +const render = (props: ComponentProps) => { + return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TimelineToolbox', () => { + let props: ComponentProps + beforeEach(() => { + props = { + sidebarWidth: 350, + } vi.mocked(getOrderedStepIds).mockReturnValue(['mock1Step']) vi.mocked(getUnsavedForm).mockReturnValue(null) vi.mocked(TerminalItemStep).mockReturnValue( @@ -34,8 +41,9 @@ describe('TimelineToolbox', () => { vi.mocked(PresavedStep).mockReturnValue(
mock PresavedStep
) vi.mocked(AddStepButton).mockReturnValue(
mock AddStepButton
) }) + it('renders 2 terminal item steps, a draggable step and presaved step with toolbox title', () => { - render() + render(props) screen.getByText('Timeline') screen.getByText('mock AddStepButton') screen.getByText('mock PresavedStep') diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts index 2b4945b756b..14a0f0058af 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts @@ -1,2 +1,2 @@ -export * from './SubstepsToolbox' +export * from './SubStepsToolbox' export * from './TimelineToolbox' diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts index 2d918b7790f..b12d5598ca1 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts @@ -1,6 +1,7 @@ import round from 'lodash/round' import uniq from 'lodash/uniq' import { UAParser } from 'ua-parser-js' + import type { StepIdType } from '../../../../form-types' export const capitalizeFirstLetterAfterNumber = (title: string): string => diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx new file mode 100644 index 00000000000..4ea6b03d2ab --- /dev/null +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx @@ -0,0 +1,41 @@ +import { describe, it, vi, beforeEach } from 'vitest' +import { screen } from '@testing-library/react' + +import { i18n } from '../../../../assets/localization' +import { renderWithProviders } from '../../../../__testing-utils__' +import { DraggableSidebar } from '../DraggableSidebar' + +import type { ComponentProps } from 'react' + +vi.mock('../../../../step-forms/selectors') +vi.mock('../../../../ui/steps/selectors') +vi.mock('../../../../feature-flags/selectors') +vi.mock('../Timeline/DraggableSteps') +vi.mock('../Timeline/PresavedStep') +vi.mock('../Timeline/AddStepButton') + +const mockSetTargetWidth = vi.fn() + +const render = (props: ComponentProps) => { + return renderWithProviders(, { + i18nInstance: i18n, + }) +} + +describe('DraggableSidebar', () => { + let props: ComponentProps + beforeEach(() => { + props = { + setTargetWidth: mockSetTargetWidth, + } + }) + + it('renders initial timeline toolbox', () => { + render(props) + screen.getByText('Timeline') + screen.getByText('Starting deck') + screen.getByText('Ending deck') + }) + + // ToDo (kk: 2024/12/12): Add more tests +}) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx index 9ff98460fc6..31c1c93eafc 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx @@ -19,8 +19,10 @@ import { import { getEnableHotKeysDisplay } from '../../../../feature-flags/selectors' import { DeckSetupContainer } from '../../DeckSetup' import { OffDeck } from '../../Offdeck' +import { SubStepsToolbox } from '../Timeline' +import { DraggableSidebar } from '../DraggableSidebar' import { ProtocolSteps } from '..' -import { SubstepsToolbox, TimelineToolbox } from '../Timeline' + import type { SavedStepFormState } from '../../../../step-forms' vi.mock('../../Offdeck') @@ -31,6 +33,7 @@ vi.mock('../StepForm') vi.mock('../../DeckSetup') vi.mock('../StepSummary.tsx') vi.mock('../Timeline') +vi.mock('../DraggableSidebar') vi.mock('../../../../feature-flags/selectors') vi.mock('../../../../file-data/selectors') vi.mock('../../../../organisms/Alerts') @@ -64,7 +67,9 @@ describe('ProtocolSteps', () => { timeline: [], errors: [], }) - vi.mocked(TimelineToolbox).mockReturnValue(
mock TimelineToolbox
) + vi.mocked(DraggableSidebar).mockReturnValue( +
mock DraggableSidebar
+ ) vi.mocked(DeckSetupContainer).mockReturnValue(
mock DeckSetupContainer
) @@ -72,7 +77,7 @@ describe('ProtocolSteps', () => { vi.mocked(OffDeck).mockReturnValue(
mock OffDeck
) vi.mocked(getUnsavedForm).mockReturnValue(null) vi.mocked(getSelectedSubstep).mockReturnValue(null) - vi.mocked(SubstepsToolbox).mockReturnValue(
mock SubstepsToolbox
) + vi.mocked(SubStepsToolbox).mockReturnValue(
mock SubStepsToolbox
) vi.mocked(getEnableHotKeysDisplay).mockReturnValue(true) vi.mocked(getSavedStepForms).mockReturnValue( MOCK_STEP_FORMS as SavedStepFormState @@ -84,7 +89,7 @@ describe('ProtocolSteps', () => { it('renders each component in ProtocolSteps', () => { render() - screen.getByText('mock TimelineToolbox') + screen.getByText('mock DraggableSidebar') screen.getByText('mock DeckSetupContainer') }) @@ -98,7 +103,7 @@ describe('ProtocolSteps', () => { it('renders the substepToolbox when selectedSubstep is not null', () => { vi.mocked(getSelectedSubstep).mockReturnValue('mockId') render() - screen.getByText('mock SubstepsToolbox') + screen.getByText('mock SubStepsToolbox') }) it('renders the hot keys display', () => { diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx index 97f337c2dcd..38f4979b0cf 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' + import { ALIGN_CENTER, COLORS, @@ -10,6 +11,7 @@ import { JUSTIFY_CENTER, JUSTIFY_SPACE_BETWEEN, POSITION_FIXED, + POSITION_RELATIVE, SPACING, StyledText, Tag, @@ -30,7 +32,7 @@ import { } from '../../../ui/steps/selectors' import { DeckSetupContainer } from '../DeckSetup' import { OffDeck } from '../Offdeck' -import { TimelineToolbox, SubstepsToolbox } from './Timeline' +import { SubStepsToolbox } from './Timeline' import { StepForm } from './StepForm' import { StepSummary } from './StepSummary' import { BatchEditToolbox } from './BatchEditToolbox' @@ -39,6 +41,7 @@ import { getRobotStateTimeline, } from '../../../file-data/selectors' import { TimelineAlerts } from '../../../organisms' +import { DraggableSidebar } from './DraggableSidebar' const CONTENT_MAX_WIDTH = '44.6704375rem' @@ -56,6 +59,7 @@ export function ProtocolSteps(): JSX.Element { const [deckView, setDeckView] = useState< typeof leftString | typeof rightString >(leftString) + const [targetWidth, setTargetWidth] = useState(350) const currentHoveredStepId = useSelector(getHoveredStepId) const currentSelectedStepId = useSelector(getSelectedStepId) @@ -82,15 +86,17 @@ export function ProtocolSteps(): JSX.Element { width="100%" padding={SPACING.spacing12} gridGap={SPACING.spacing16} - justifyContent={JUSTIFY_SPACE_BETWEEN} > - + + + {formData == null && selectedSubstep ? ( - + ) : null} {isMultiSelectMode ? : null}