Skip to content

Commit

Permalink
add data display part to parameters tab
Browse files Browse the repository at this point in the history
  • Loading branch information
koji committed Mar 18, 2024
1 parent 07636e5 commit 1cfcb68
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 14 deletions.
2 changes: 1 addition & 1 deletion app/src/assets/localization/en/protocol_details.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
"location": "location",
"modules": "modules",
"name": "Name",
"num_choices": "{{num}} choices",
"no_available_robots_found": "No available robots found",
"no_parameters": "No parameters specified in this protocol",
"no_summary": "no summary specified for this protocol.",
"not_connected": "not connected",
"not_in_protocol": "no {{section}} is specified for this protocol",
"num_choices": "{{num}} choices",
"off": "Off",
"on_off": "On, off",
"on": "On",
Expand Down
6 changes: 6 additions & 0 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"calibration_required": "Calibration required",
"calibration_status": "calibration status",
"calibration": "Calibration",
"cancel_and_restart_to_edit": "Cancel the run and restart setup to edit",
"closing": "Closing...",
"complete_setup_before_proceeding": "complete setup before continuing run",
"configure": "Configure",
Expand Down Expand Up @@ -157,6 +158,7 @@
"multiple_of_most_modules": "You can use multiples of most module types within a single Python protocol by connecting and loading the modules in a specific order. The robot will initialize the matching module attached to the lowest numbered port first, regardless of what deck slot it occupies.",
"must_have_labware_and_pip": "Protocol must load labware and a pipette",
"n_a": "N/A",
"name": "Name",
"no_data": "no data",
"no_labware_offset_data": "no labware offset data yet",
"no_modules_or_fixtures": "No modules or fixtures are specified for this protocol.",
Expand All @@ -170,13 +172,15 @@
"not_calibrated": "Not calibrated yet",
"not_configured": "not configured",
"off_deck": "Off deck",
"off": "Off",
"offset_data": "Offset Data",
"offsets_applied_plural": "{{count}} offsets applied",
"offsets_applied": "{{count}} offset applied",
"on_adapter_in_mod": "on {{adapterName}} in {{moduleName}}",
"on_adapter": "on {{adapterName}}",
"on_deck": "On deck",
"on-deck_labware": "{{count}} on-deck labware",
"on": "On",
"opening": "Opening...",
"parameters": "Parameters",
"pipette_mismatch": "Pipette generation mismatch.",
Expand Down Expand Up @@ -245,6 +249,8 @@
"update_deck": "Update deck",
"usb_connected_no_port_info": "USB Port Connected",
"usb_port_connected": "USB Port {{port}}",
"value": "Value",
"values_are_view_only": "Values are view-only",
"view_current_offsets": "View current offsets",
"view_moam": "View setup instructions for placing modules of the same type to the robot.",
"view_setup_instructions": "View setup instructions",
Expand Down
144 changes: 132 additions & 12 deletions app/src/organisms/Devices/ProtocolRun/ProtocolRunRunTimeParameters.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

import {
ALIGN_CENTER,
BORDERS,
COLORS,
DIRECTION_COLUMN,
DIRECTION_ROW,
Expand All @@ -11,10 +14,12 @@ import {
} from '@opentrons/components'

import { StyledText } from '../../../atoms/text'
import { Banner } from '../../../atoms/Banner'
import { Divider } from '../../../atoms/structure'
import { NoParameter } from '../../ProtocolDetails/ProtocolParameters/NoParameter'
import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis'

import { RunTimeParameter } from '@opentrons/shared-data'
import type { RunTimeParameter } from '@opentrons/shared-data'

const mockData: RunTimeParameter[] = [
{
Expand Down Expand Up @@ -156,11 +161,44 @@ export function ProtocolRunRuntimeParameters({
}: ProtocolRunRuntimeParametersProps): JSX.Element {
const { t } = useTranslation('protocol_setup')
const mostRecentAnalysis = useMostRecentCompletedAnalysis(runId)
const parameters = mostRecentAnalysis?.runTimeParameters ?? mockData
const isNoParameter = parameters.length < 1
// ToDo (kk:03/18/2024) mockData will be replaced with []
const runTimeParameters = mostRecentAnalysis?.runTimeParameters ?? mockData
const isNoParameter = runTimeParameters.length < 1

const formattedValue = (runTimeParameter: RunTimeParameter): string => {
const { type, default: defaultValue } = runTimeParameter
const suffix =
'suffix' in runTimeParameter && runTimeParameter.suffix != null
? runTimeParameter.suffix
: ''
switch (type) {
case 'int':
case 'float':
return `${defaultValue.toString()} ${suffix}`
case 'boolean':
return Boolean(defaultValue) ? t('on') : t('off')
case 'str':
if ('choices' in runTimeParameter && runTimeParameter.choices != null) {
const choice = runTimeParameter.choices.find(
choice => choice.value === defaultValue
)
if (choice != null) {
return choice.displayName
}
}
break
}
return ''
}
// ToDO (kk:03/18/2024) Need to add Chip to updated runTime parameter value
// This part will be implemented in a following PR since need to runTime parameter slideout
return (
<Flex flexDirection={DIRECTION_COLUMN}>
<Flex backgroundColor={COLORS.white} padding={SPACING.spacing16}>
<>
<Flex
flexDirection={DIRECTION_COLUMN}
padding={SPACING.spacing16}
gridGap={SPACING.spacing10}
>
<Flex
flexDirection={DIRECTION_ROW}
gridGap={SPACING.spacing8}
Expand All @@ -175,14 +213,96 @@ export function ProtocolRunRuntimeParameters({
</StyledText>
) : null}
</Flex>
{!isNoParameter ? (
<Banner
type="informing"
width="100%"
iconMarginLeft={SPACING.spacing4}
>
<Flex flexDirection={DIRECTION_COLUMN}>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('values_are_view_only')}
</StyledText>
<StyledText as="p">{t('cancel_and_restart_to_edit')}</StyledText>
</Flex>
</Banner>
) : null}
</Flex>
<Flex backgroundColor={COLORS.white} padding={SPACING.spacing16}>
{isNoParameter ? (
{isNoParameter ? (
<Flex padding={SPACING.spacing16}>
<NoParameter />
) : (
<StyledText>{'parameters'}</StyledText>
)}
</Flex>
</Flex>
</Flex>
) : (
<>
<Divider width="100%" />
<Flex flexDirection={DIRECTION_COLUMN} padding={SPACING.spacing16}>
<StyledTable>
<thead>
<StyledTableHeader>{t('name')}</StyledTableHeader>
<StyledTableHeader>{t('value')}</StyledTableHeader>
</thead>
<tbody>
{runTimeParameters.map(
(parameter: RunTimeParameter, index: number) => {
return (
<StyledTableRow
isLast={index === runTimeParameters.length - 1}
key={`runTimeParameter-${index}`}
>
<StyledTableCell
isLast={index === runTimeParameters.length - 1}
>
<StyledText as="p">
{parameter.displayName}
</StyledText>
</StyledTableCell>
<StyledTableCell
isLast={index === runTimeParameters.length - 1}
>
<StyledText as="p">
{formattedValue(parameter)}
</StyledText>
</StyledTableCell>
</StyledTableRow>
)
}
)}
</tbody>
</StyledTable>
</Flex>
</>
)}
</>
)
}

const StyledTable = styled.table`
width: 100%;
border-collapse: collapse;
text-align: left;
`

const StyledTableHeader = styled.th`
${TYPOGRAPHY.labelSemiBold}
padding: ${SPACING.spacing8};
border-bottom: ${BORDERS.lineBorder};
`

interface StyledTableRowProps {
isLast: boolean
}

const StyledTableRow = styled.tr<StyledTableRowProps>`
padding: ${SPACING.spacing8};
border-bottom: ${props => (props.isLast ? 'none' : BORDERS.lineBorder)};
`

interface StyledTableCellProps {
isLast: boolean
}

const StyledTableCell = styled.td<StyledTableCellProps>`
padding-left: ${SPACING.spacing8};
padding-top: ${SPACING.spacing12};
padding-bottom: ${props => (props.isLast ? 0 : SPACING.spacing12)};
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import * as React from 'react'
import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest'
import { screen } from '@testing-library/react'
import { when } from 'vitest-when'

import { renderWithProviders } from '../../../../__testing-utils__'
import { i18n } from '../../../../i18n'
import { NoParameter } from '../../../ProtocolDetails/ProtocolParameters/NoParameter'
import { useMostRecentCompletedAnalysis } from '../../../LabwarePositionCheck/useMostRecentCompletedAnalysis'

import { ProtocolRunRuntimeParameters } from '../ProtocolRunRunTimeParameters'

import type {
CompletedProtocolAnalysis,
RunTimeParameter,
} from '@opentrons/shared-data'

vi.mock('../../../ProtocolDetails/ProtocolParameters/NoParameter')

const RUN_ID = 'mockId'

const mockRunTimeParameterData: RunTimeParameter[] = [
{
displayName: 'Dry Run',
variableName: 'DRYRUN',
description: 'Is this a dry or wet run? Wet is true, dry is false',
type: 'boolean',
default: false,
},
{
displayName: 'Columns of Samples',
variableName: 'COLUMNS',
description: 'How many columns do you want?',
type: 'int',
min: 1,
max: 14,
default: 4,
},
{
displayName: 'EtoH Volume',
variableName: 'ETOH_VOLUME',
description: '70% ethanol volume',
type: 'float',
suffix: 'mL',
min: 1.5,
max: 10.0,
default: 6.5,
},
{
displayName: 'Default Module Offsets',
variableName: 'DEFAULT_OFFSETS',
description: 'default module offsets for temp, H-S, and none',
type: 'str',
choices: [
{
displayName: 'No offsets',
value: 'none',
},
{
displayName: 'temp offset',
value: '1',
},
{
displayName: 'heater-shaker offset',
value: '2',
},
],
default: 'none',
},
]

const render = (
props: React.ComponentProps<typeof ProtocolRunRuntimeParameters>
) => {
return renderWithProviders(<ProtocolRunRuntimeParameters {...props} />, {
i18nInstance: i18n,
})
}

describe(() => {
let props: React.ComponentProps<typeof ProtocolRunRuntimeParameters>
beforeEach(() => {
props = {
runId: RUN_ID,
}
vi.mocked(NoParameter).mockReturnValue(<div>mock NoParameter</div>)
when(vi.mocked(useMostRecentCompletedAnalysis))
.calledWith(RUN_ID)
.thenReturn({
runTimeParameters: mockRunTimeParameterData,
} as CompletedProtocolAnalysis)
})

afterEach(() => {
vi.clearAllMocks()
})

it('should render title, and banner when RunTimeParameters are note empty', () => {
render(props)
screen.getByText('Parameters')
screen.getByText('Default values')
screen.getByText('Values are view-only')
screen.getByText('Cancel the run and restart setup to edit')
screen.getByText('Name')
screen.getByText('Value')
})

it('should render RunTimeParameters when RunTimeParameters are note empty', () => {
render(props)
screen.getByText('Dry Run')
screen.getByText('Off')
screen.getByText('Columns of Samples')
screen.getByText('4')
screen.getByText('EtoH Volume')
screen.getByText('6.5 mL')
screen.getByText('Default Module Offsets')
screen.getByText('No offsets')
})

it('should render mock NoParameter component when RunTimeParameters are empty', () => {
when(vi.mocked(useMostRecentCompletedAnalysis))
.calledWith(RUN_ID)
.thenReturn({
runTimeParameters: [] as RunTimeParameter[],
} as CompletedProtocolAnalysis)
render(props)
screen.getByText('Parameters')
expect(screen.queryByText('Default values')).not.toBeInTheDocument()
screen.getByText('mock NoParameter')
})

// ToDo Additional test will be implemented when chip component is added
})
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function ProtocolParameters({
<Banner
type="informing"
width="100%"
iconMarginRight={SPACING.spacing12}
iconMarginLeft={SPACING.spacing4}
>
<Flex flexDirection={DIRECTION_COLUMN}>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
Expand Down

0 comments on commit 1cfcb68

Please sign in to comment.