Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): populate ChooseRobotSlideout with runtime parameters #14706

Merged
merged 10 commits into from
Mar 25, 2024
2 changes: 2 additions & 0 deletions app/src/assets/localization/en/protocol_details.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"modules": "modules",
"name": "Name",
"no_available_robots_found": "No available robots found",
"no_custom_values": "No custom values specified",
"no_parameters": "No parameters specified in this protocol",
"no_summary": "no summary specified for this protocol.",
"not_connected": "not connected",
Expand All @@ -58,6 +59,7 @@
"range": "Range",
"read_less": "read less",
"read_more": "read more",
"restore_defaults": "Restore default values",
"right_mount": "right mount",
"robot_configuration": "robot configuration",
"robot_is_busy_with_protocol": "{{robotName}} is busy with {{protocolName}} in {{runStatus}} state. Do you want to clear it and proceed?",
Expand Down
101 changes: 61 additions & 40 deletions app/src/atoms/MenuList/DropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
BORDERS,
Icon,
useOnClickOutside,
POSITION_RELATIVE,
} from '@opentrons/components'
import { StyledText } from '../text'
import { MenuItem } from './MenuItem'
Expand All @@ -22,13 +23,16 @@ export interface DropdownOption {
value: string
}

export type DropdownBorder = 'rounded' | 'neutral'
ncdiehl11 marked this conversation as resolved.
Show resolved Hide resolved

export interface DropdownMenuProps {
filterOptions: DropdownOption[]
onClick: (value: string) => void
currentOption: DropdownOption
width?: string
dropdownType?: 'rounded' | 'neutral'
dropdownType?: DropdownBorder
title?: string
caption?: string | null
}

// TODO: (smb: 4/15/22) refactor this to use html select for accessibility
Expand All @@ -41,6 +45,7 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
width = '9.125rem',
dropdownType = 'rounded',
title,
caption,
} = props
const [showDropdownMenu, setShowDropdownMenu] = React.useState<boolean>(false)
const toggleSetShowDropdownMenu = (): void => {
Expand All @@ -65,6 +70,7 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
align-items: ${ALIGN_CENTER};
justify-content: ${JUSTIFY_SPACE_BETWEEN};
width: ${width};
height: 2.25rem;

&:hover {
border: 1px ${BORDERS.styleSolid}
Expand All @@ -88,53 +94,68 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
`

return (
<Flex flexDirection={DIRECTION_COLUMN}>
<Flex flexDirection={DIRECTION_COLUMN} ref={dropDownMenuWrapperRef}>
{title !== null ? (
<StyledText as="labelSemiBold" paddingBottom={SPACING.spacing8}>
{title}
</StyledText>
) : null}
<Flex
onClick={(e: MouseEvent) => {
e.preventDefault()
toggleSetShowDropdownMenu()
}}
css={DROPDOWN_STYLE}
ref={dropDownMenuWrapperRef}
>
<StyledText css={TYPOGRAPHY.pSemiBold}>{currentOption.name}</StyledText>
{showDropdownMenu ? (
<Icon height="0.75rem" name="menu-down" transform="rotate(180deg)" />
) : (
<Icon height="0.75rem" name="menu-down" />
)}
</Flex>
{showDropdownMenu && (
<Flex flexDirection={DIRECTION_COLUMN} position={POSITION_RELATIVE}>
<Flex
zIndex={2}
borderRadius={BORDERS.borderRadius8}
boxShadow="0px 1px 3px rgba(0, 0, 0, 0.2)"
position={POSITION_ABSOLUTE}
backgroundColor={COLORS.white}
top="8.5rem"
left={SPACING.spacing16}
flexDirection={DIRECTION_COLUMN}
width={width}
ref={dropDownMenuWrapperRef}
onClick={(e: MouseEvent) => {
e.preventDefault()
toggleSetShowDropdownMenu()
}}
css={DROPDOWN_STYLE}
>
{filterOptions.map((option, index) => (
<MenuItem
key={index}
onClick={() => {
onClick(option.value)
setShowDropdownMenu(false)
}}
>
{option.name}
</MenuItem>
))}
<StyledText
css={css`
${dropdownType === 'rounded'
? TYPOGRAPHY.pSemiBold
: TYPOGRAPHY.pRegular}
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`}
>
{currentOption.name}
</StyledText>
{showDropdownMenu ? (
<Icon size="1.2rem" name="menu-down" transform="rotate(180deg)" />
) : (
<Icon size="1.2rem" name="menu-down" />
)}
</Flex>
)}
{showDropdownMenu && (
<Flex
zIndex={2}
borderRadius={BORDERS.borderRadius8}
boxShadow={BORDERS.tinyDropShadow}
position={POSITION_ABSOLUTE}
backgroundColor={COLORS.white}
flexDirection={DIRECTION_COLUMN}
width={width}
top="2.5rem"
>
{filterOptions.map((option, index) => (
<MenuItem
key={`${option.name}-${index}`}
onClick={() => {
onClick(option.value)
setShowDropdownMenu(false)
}}
>
{option.name}
</MenuItem>
))}
</Flex>
)}
</Flex>
{caption != null ? (
<StyledText as="label" paddingTop={SPACING.spacing4}>
{caption}
</StyledText>
) : null}
</Flex>
)
}
24 changes: 16 additions & 8 deletions app/src/atoms/SelectField/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
CSSObjectWithLabel,
DropdownIndicatorProps,
} from 'react-select'
import type { DropdownBorder } from '../MenuList/DropdownMenu'

export interface SelectOption {
value: string
Expand All @@ -31,29 +32,36 @@ export type SelectProps = ReactSelectProps<SelectOption>

interface SelectComponentProps extends SelectProps {
width?: string
dropdownType?: DropdownBorder
}

const VOID_STYLE: unknown = undefined
const NO_STYLE_FN = (): CSSObjectWithLabel => VOID_STYLE as CSSObjectWithLabel

export function Select(props: SelectComponentProps): JSX.Element {
const { dropdownType, menuIsOpen, width } = props
const CLEAR_DEFAULT_STYLES_AND_SET_NEW_STYLES: StylesConfig<SelectOption> = {
clearIndicator: NO_STYLE_FN,
control: (styles: CSSObjectWithLabel) => ({
...styles,
borderRadius: BORDERS.borderRadiusFull,
border: BORDERS.lineBorder,
width: props.width ?? 'auto',
borderRadius:
dropdownType === 'rounded'
? BORDERS.borderRadiusFull
: BORDERS.borderRadius4,
border: `1px ${BORDERS.styleSolid} ${
menuIsOpen ? COLORS.blue50 : COLORS.grey50
}`,
width: width ?? 'auto',
height: SPACING.spacing16,
borderColor: COLORS.grey30,
boxShadow: 'none',
padding: SPACING.spacing6,
flexDirection: DIRECTION_ROW,
'&:hover': {
borderColor: COLORS.grey60,
borderColor: COLORS.grey50,
},
'&:active': {
borderColor: COLORS.grey60,
borderColor: COLORS.blue50,
},
}),
container: (styles: CSSObjectWithLabel) => ({
Expand Down Expand Up @@ -83,7 +91,7 @@ export function Select(props: SelectComponentProps): JSX.Element {
menu: (styles: CSSObjectWithLabel) => ({
...styles,
backgroundColor: COLORS.white,
width: props.width != null ? props.width : 'auto',
width: width != null ? width : 'auto',
boxShadowcha: '0px 1px 3px rgba(0, 0, 0, 0.2)',
borderRadius: '4px 4px 0px 0px',
marginTop: SPACING.spacing4,
Expand Down Expand Up @@ -155,9 +163,9 @@ function DropdownIndicator(
width={SPACING.spacing20}
>
{Boolean(props.selectProps.menuIsOpen) ? (
<Icon transform="rotate(180deg)" name="menu-down" height="1.25rem" />
<Icon transform="rotate(180deg)" name="menu-down" height="1rem" />
) : (
<Icon name="menu-down" height="1.25rem" />
<Icon name="menu-down" height="1rem" />
)}
</Box>
</components.DropdownIndicator>
Expand Down
29 changes: 26 additions & 3 deletions app/src/atoms/SelectField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import * as React from 'react'
import find from 'lodash/find'
import { Select } from './Select'
import { COLORS, Flex, TYPOGRAPHY } from '@opentrons/components'
import {
COLORS,
DIRECTION_COLUMN,
Flex,
TYPOGRAPHY,
SPACING,
} from '@opentrons/components'
import { css } from 'styled-components'
import { StyledText } from '../text'

import type { SelectProps, SelectOption } from './Select'
import type { ActionMeta, MultiValue, SingleValue } from 'react-select'
Expand All @@ -24,6 +31,8 @@ export interface SelectFieldProps {
menuPosition?: SelectProps['menuPosition']
/** render function for the option label passed to react-select */
formatOptionLabel?: SelectProps['formatOptionLabel']
/** optional title */
title?: React.ReactNode
/** optional caption. hidden when `error` is given */
caption?: React.ReactNode
/** if included, use error style and display error instead of caption */
Expand All @@ -40,10 +49,12 @@ export interface SelectFieldProps {
isSearchable?: boolean
/** optional width to specify the width of the select field and dropdown menu */
width?: string
dropdownType?: 'rounded' | 'neutral'
ncdiehl11 marked this conversation as resolved.
Show resolved Hide resolved
}

const CAPTION_STYLE = css`
font-size: ${TYPOGRAPHY.fontSizeCaption};
padding-top: ${SPACING.spacing4};
&.error {
color: ${COLORS.red50};
font-weight: ${TYPOGRAPHY.fontWeightSemiBold};
Expand All @@ -64,6 +75,8 @@ export function SelectField(props: SelectFieldProps): JSX.Element {
onLoseFocus,
isSearchable = true,
width,
title,
dropdownType = 'rounded',
} = props
const allOptions = options.flatMap(og =>
'options' in og ? og.options : [og]
Expand All @@ -72,7 +85,16 @@ export function SelectField(props: SelectFieldProps): JSX.Element {
const caption = error != null || props.caption

return (
<>
<Flex flexDirection={DIRECTION_COLUMN}>
{title != null ? (
<StyledText
as="label"
fontWeight={TYPOGRAPHY.fontWeightSemiBold}
paddingBottom={SPACING.spacing8}
>
{title}
</StyledText>
) : null}
<Select
id={id}
name={name}
Expand All @@ -83,6 +105,7 @@ export function SelectField(props: SelectFieldProps): JSX.Element {
isSearchable={isSearchable}
menuPosition={menuPosition}
width={width}
dropdownType={dropdownType}
tabIndex={2}
formatOptionLabel={formatOptionLabel}
onChange={(
Expand All @@ -95,6 +118,6 @@ export function SelectField(props: SelectFieldProps): JSX.Element {
onBlur={() => onLoseFocus?.(name)}
/>
{caption != null && <Flex css={CAPTION_STYLE}>{caption}</Flex>}
</>
</Flex>
)
}
Loading
Loading