From 2b0cdc3b11b4a8a6aec756bf1e996000aabdab83 Mon Sep 17 00:00:00 2001 From: Jethary Rader <66035149+jerader@users.noreply.github.com> Date: Thu, 9 May 2024 15:27:46 -0400 Subject: [PATCH] refactor(protocol-designer): tiprack option redesign to not have scroll (#15141) closes RQA-2681 --- components/src/forms/Select.tsx | 5 +- .../FilePipettesModal/TiprackOption.tsx | 104 ++++++++++++++---- .../FilePipettesModal/TiprackSelect.tsx | 43 +++++--- .../__tests__/TiprackOptions.test.tsx | 27 +++-- 4 files changed, 132 insertions(+), 47 deletions(-) diff --git a/components/src/forms/Select.tsx b/components/src/forms/Select.tsx index 6eafc8cc558..9e2b3d2082c 100644 --- a/components/src/forms/Select.tsx +++ b/components/src/forms/Select.tsx @@ -118,7 +118,10 @@ function DropdownIndicator( [styles.flipped]: props.selectProps.menuIsOpen, })} > - + ) diff --git a/protocol-designer/src/components/modals/FilePipettesModal/TiprackOption.tsx b/protocol-designer/src/components/modals/FilePipettesModal/TiprackOption.tsx index 8908d0e7614..086966230e3 100644 --- a/protocol-designer/src/components/modals/FilePipettesModal/TiprackOption.tsx +++ b/protocol-designer/src/components/modals/FilePipettesModal/TiprackOption.tsx @@ -1,41 +1,99 @@ import * as React from 'react' +import { useTranslation } from 'react-i18next' +import { css } from 'styled-components' import { Flex, - Text, - Icon, DIRECTION_ROW, COLORS, SPACING, ALIGN_CENTER, + StyledText, + BORDERS, + useHoverTooltip, + Tooltip, } from '@opentrons/components' interface TiprackOptionProps { onClick: React.MouseEventHandler isSelected: boolean + isDisabled: boolean text: React.ReactNode } export function TiprackOption(props: TiprackOptionProps): JSX.Element { - const { text, onClick, isSelected } = props + const { text, onClick, isSelected, isDisabled } = props + const { t } = useTranslation('tooltip') + const [targetProps, tooltipProps] = useHoverTooltip() + + const OPTION_STYLE = css` + background-color: ${COLORS.white}; + border-radius: ${BORDERS.borderRadius8}; + border: 1px ${BORDERS.styleSolid} ${COLORS.grey30}; + + &:hover { + background-color: ${COLORS.grey10}; + border: 1px ${BORDERS.styleSolid} ${COLORS.grey35}; + } + + &:focus { + outline: 2px ${BORDERS.styleSolid} ${COLORS.blue50}; + outline-offset: 3px; + } + ` + + const OPTION_SELECTED_STYLE = css` + ${OPTION_STYLE} + background-color: ${COLORS.blue10}; + border: 1px ${BORDERS.styleSolid} ${COLORS.blue50}; + + &:hover { + border: 1px ${BORDERS.styleSolid} ${COLORS.blue50}; + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2); + } + ` + + const OPTION_DISABLED_STYLE = css` + ${OPTION_STYLE} + background-color: ${COLORS.white}; + border: 1px ${BORDERS.styleSolid} ${COLORS.grey30}; + &:hover { + border: 1px ${BORDERS.styleSolid} ${COLORS.grey30}; + background-color: ${COLORS.white}; + } + ` + + let optionStyle + if (isDisabled) { + optionStyle = OPTION_DISABLED_STYLE + } else if (isSelected) { + optionStyle = OPTION_SELECTED_STYLE + } else { + optionStyle = OPTION_STYLE + } + return ( - - - {text} - + <> + + {text} + + {isDisabled ? ( + {t('disabled_no_space_pipette')} + ) : null} + ) } diff --git a/protocol-designer/src/components/modals/FilePipettesModal/TiprackSelect.tsx b/protocol-designer/src/components/modals/FilePipettesModal/TiprackSelect.tsx index 8aa608796ec..8c77e315531 100644 --- a/protocol-designer/src/components/modals/FilePipettesModal/TiprackSelect.tsx +++ b/protocol-designer/src/components/modals/FilePipettesModal/TiprackSelect.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Flex, DIRECTION_COLUMN } from '@opentrons/components' +import { Flex, DIRECTION_COLUMN, SPACING } from '@opentrons/components' import { TiprackOption } from './TiprackOption' import type { Mount } from '@opentrons/components' import type { FormPipettesByMount } from '../../../step-forms' @@ -30,21 +30,32 @@ export const TiprackSelect = ( return ( - {tiprackOptions.map(option => ( - { - const updatedValues = selectedValues?.includes(option.value) - ? selectedValues.filter(value => value !== option.value) - : [...(selectedValues ?? []), option.value] - onSetFieldValue( - `pipettesByMount.${mount}.tiprackDefURI`, - updatedValues.slice(0, 3) - ) - }} - /> + {tiprackOptions.map((option, index) => ( + + { + const updatedValues = selectedValues?.includes(option.value) + ? selectedValues.filter(value => value !== option.value) + : [...(selectedValues ?? []), option.value] + onSetFieldValue( + `pipettesByMount.${mount}.tiprackDefURI`, + updatedValues.slice(0, 3) + ) + }} + /> + ))} ) diff --git a/protocol-designer/src/components/modals/FilePipettesModal/__tests__/TiprackOptions.test.tsx b/protocol-designer/src/components/modals/FilePipettesModal/__tests__/TiprackOptions.test.tsx index 6b9004a2472..27810c71176 100644 --- a/protocol-designer/src/components/modals/FilePipettesModal/__tests__/TiprackOptions.test.tsx +++ b/protocol-designer/src/components/modals/FilePipettesModal/__tests__/TiprackOptions.test.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import { vi, describe, beforeEach, it, expect } from 'vitest' -import { screen } from '@testing-library/react' +import { BORDERS, COLORS } from '@opentrons/components' +import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '../../../../__testing-utils__' -import { COLORS } from '@opentrons/components' import { TiprackOption } from '../TiprackOption' const render = (props: React.ComponentProps) => { @@ -15,22 +15,35 @@ describe('TiprackOption', () => { props = { onClick: vi.fn(), isSelected: true, + isDisabled: false, text: 'mockText', } }) it('renders a selected tiprack option', () => { render(props) screen.getByText('mockText') - expect(screen.getByLabelText('TiprackOption_checkbox-marked')).toHaveStyle( - `color: ${COLORS.blue50}` + expect(screen.getByLabelText('TiprackOption_flex_mockText')).toHaveStyle( + `background-color: ${COLORS.blue10}` ) + fireEvent.click(screen.getByText('mockText')) + expect(props.onClick).toHaveBeenCalled() }) it('renders an unselected tiprack option', () => { props.isSelected = false render(props) screen.getByText('mockText') - expect( - screen.getByLabelText('TiprackOption_checkbox-blank-outline') - ).toHaveStyle(`color: ${COLORS.grey50}`) + expect(screen.getByLabelText('TiprackOption_flex_mockText')).toHaveStyle( + `background-color: ${COLORS.white}` + ) + fireEvent.click(screen.getByText('mockText')) + expect(props.onClick).toHaveBeenCalled() + }) + it('renders a disabled tiprack option', () => { + props.isSelected = false + props.isDisabled = true + render(props) + expect(screen.getByLabelText('TiprackOption_flex_mockText')).toHaveStyle( + `border: 1px ${BORDERS.styleSolid} ${COLORS.grey30}` + ) }) })