-
Notifications
You must be signed in to change notification settings - Fork 178
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(app): add Parameters tab to odd protocolDetails (#14657)
- Loading branch information
Showing
7 changed files
with
365 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import * as React from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import styled from 'styled-components' | ||
import { | ||
BORDERS, | ||
COLORS, | ||
Flex, | ||
SPACING, | ||
TYPOGRAPHY, | ||
WRAP, | ||
} from '@opentrons/components' | ||
import { StyledText } from '../../atoms/text' | ||
import { useToaster } from '../../organisms/ToasterOven' | ||
import { useRunTimeParameters } from '../Protocols/hooks' | ||
import { EmptySection } from './EmptySection' | ||
import type { RunTimeParameter } from '@opentrons/shared-data' | ||
|
||
const Table = styled('table')` | ||
font-size: ${TYPOGRAPHY.fontSize22}; | ||
width: 100%; | ||
border-spacing: 0 ${SPACING.spacing8}; | ||
margin: ${SPACING.spacing16} 0; | ||
text-align: ${TYPOGRAPHY.textAlignLeft}; | ||
` | ||
const TableHeader = styled('th')` | ||
font-size: ${TYPOGRAPHY.fontSize20}; | ||
font-weight: ${TYPOGRAPHY.fontWeightSemiBold}; | ||
padding: ${SPACING.spacing4}; | ||
color: ${COLORS.grey60}; | ||
` | ||
|
||
const TableRow = styled('tr')` | ||
background-color: ${COLORS.grey35}; | ||
border: 1px ${COLORS.white} solid; | ||
height: 4.75rem; | ||
` | ||
|
||
const TableDatum = styled('td')` | ||
padding: ${SPACING.spacing4}; | ||
white-space: break-spaces; | ||
text-overflow: ${WRAP}; | ||
&:first-child { | ||
border-top-left-radius: ${BORDERS.borderRadius4}; | ||
border-bottom-left-radius: ${BORDERS.borderRadius4}; | ||
} | ||
&:last-child { | ||
border-top-right-radius: ${BORDERS.borderRadius4}; | ||
border-bottom-right-radius: ${BORDERS.borderRadius4}; | ||
} | ||
` | ||
|
||
export const Parameters = (props: { protocolId: string }): JSX.Element => { | ||
const runTimeParameters = useRunTimeParameters(props.protocolId) | ||
const { makeSnackbar } = useToaster() | ||
const { t, i18n } = useTranslation('protocol_details') | ||
|
||
const makeSnack = (): void => { | ||
makeSnackbar(t('start_setup_customize_values')) | ||
} | ||
|
||
const getRange = (parameter: RunTimeParameter): string => { | ||
const { type } = parameter | ||
const min = 'min' in parameter ? parameter.min : 0 | ||
const max = 'max' in parameter ? parameter.max : 0 | ||
const numChoices = 'choices' in parameter ? parameter.choices.length : 0 | ||
let range: string | null = null | ||
if (numChoices === 2 && 'choices' in parameter) { | ||
range = `${parameter.choices[0].displayName}, ${parameter.choices[1].displayName}` | ||
} | ||
|
||
switch (type) { | ||
case 'boolean': { | ||
return t('on_off') | ||
} | ||
case 'float': | ||
case 'int': { | ||
return `${min}-${max}` | ||
} | ||
case 'str': { | ||
return range ?? t('num_choices', { num: numChoices }) | ||
} | ||
default: | ||
// Should never hit this case | ||
return '' | ||
} | ||
} | ||
|
||
const getDefault = (parameter: RunTimeParameter): string => { | ||
const { type, default: defaultValue } = parameter | ||
const suffix = | ||
'suffix' in parameter && parameter.suffix != null ? parameter.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 parameter && parameter.choices != null) { | ||
const choice = parameter.choices.find( | ||
choice => choice.value === defaultValue | ||
) | ||
if (choice != null) { | ||
return choice.displayName | ||
} | ||
} | ||
break | ||
} | ||
return '' | ||
} | ||
|
||
return runTimeParameters.length > 0 ? ( | ||
<Table onClick={makeSnack} data-testid="Parameters_table"> | ||
<thead> | ||
<tr> | ||
<TableHeader> | ||
<StyledText paddingLeft={SPACING.spacing24}> | ||
{i18n.format(t('name'), 'capitalize')} | ||
</StyledText> | ||
</TableHeader> | ||
<TableHeader> | ||
<StyledText paddingLeft={SPACING.spacing24}> | ||
{i18n.format(t('default_value'), 'capitalize')} | ||
</StyledText> | ||
</TableHeader> | ||
<TableHeader> | ||
<StyledText paddingLeft={SPACING.spacing24}> | ||
{i18n.format(t('range'), 'capitalize')} | ||
</StyledText> | ||
</TableHeader> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{runTimeParameters.map((parameter, index) => { | ||
return ( | ||
<TableRow key={index}> | ||
<TableDatum> | ||
<Flex paddingLeft={SPACING.spacing24}> | ||
{parameter.displayName} | ||
</Flex> | ||
</TableDatum> | ||
<TableDatum> | ||
<Flex paddingLeft={SPACING.spacing24} color={COLORS.grey60}> | ||
{getDefault(parameter)} | ||
</Flex> | ||
</TableDatum> | ||
<TableDatum> | ||
<Flex paddingLeft={SPACING.spacing24} color={COLORS.grey60}> | ||
{getRange(parameter)} | ||
</Flex> | ||
</TableDatum> | ||
</TableRow> | ||
) | ||
})} | ||
</tbody> | ||
</Table> | ||
) : ( | ||
<EmptySection section="parameters" /> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
app/src/pages/ProtocolDetails/__tests__/Parameters.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import * as React from 'react' | ||
import { when } from 'vitest-when' | ||
import { it, describe, beforeEach, vi } from 'vitest' | ||
import { screen } from '@testing-library/react' | ||
import { i18n } from '../../../i18n' | ||
import { useToaster } from '../../../organisms/ToasterOven' | ||
import { renderWithProviders } from '../../../__testing-utils__' | ||
import { useRunTimeParameters } from '../../Protocols/hooks' | ||
import { Parameters } from '../Parameters' | ||
import type { RunTimeParameter } from '@opentrons/shared-data' | ||
|
||
vi.mock('../../../organisms/ToasterOven') | ||
vi.mock('../../Protocols/hooks') | ||
|
||
const mockRTPData: RunTimeParameter[] = [ | ||
{ | ||
displayName: 'Dry Run', | ||
variableName: 'DRYRUN', | ||
description: 'a dry run description', | ||
type: 'boolean', | ||
default: false, | ||
}, | ||
{ | ||
displayName: 'Use Gripper', | ||
variableName: 'USE_GRIPPER', | ||
description: '', | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
displayName: 'Trash Tips', | ||
variableName: 'TIP_TRASH', | ||
description: 'throw tip in trash', | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
displayName: 'Deactivate Temperatures', | ||
variableName: 'DEACTIVATE_TEMP', | ||
description: 'deactivate temperature?', | ||
type: 'boolean', | ||
default: true, | ||
}, | ||
{ | ||
displayName: 'Columns of Samples', | ||
variableName: 'COLUMNS', | ||
description: '', | ||
suffix: 'mL', | ||
type: 'int', | ||
min: 1, | ||
max: 14, | ||
default: 4, | ||
}, | ||
{ | ||
displayName: 'PCR Cycles', | ||
variableName: 'PCR_CYCLES', | ||
description: '', | ||
type: 'int', | ||
min: 1, | ||
max: 10, | ||
default: 6, | ||
}, | ||
{ | ||
displayName: 'EtoH Volume', | ||
variableName: 'ETOH_VOLUME', | ||
description: '', | ||
type: 'float', | ||
min: 1.5, | ||
max: 10.0, | ||
default: 6.5, | ||
}, | ||
{ | ||
displayName: 'Default Module Offsets', | ||
variableName: 'DEFAULT_OFFSETS', | ||
description: '', | ||
type: 'str', | ||
choices: [ | ||
{ | ||
displayName: 'no offsets', | ||
value: 'none', | ||
}, | ||
{ | ||
displayName: 'temp offset', | ||
value: '1', | ||
}, | ||
{ | ||
displayName: 'heater-shaker offset', | ||
value: '2', | ||
}, | ||
], | ||
default: 'none', | ||
}, | ||
{ | ||
displayName: '2 choices', | ||
variableName: 'TWO', | ||
description: '', | ||
type: 'str', | ||
choices: [ | ||
{ | ||
displayName: 'one choice', | ||
value: '1', | ||
}, | ||
{ | ||
displayName: 'the second', | ||
value: '2', | ||
}, | ||
], | ||
default: '2', | ||
}, | ||
] | ||
|
||
const render = (props: React.ComponentProps<typeof Parameters>) => { | ||
return renderWithProviders(<Parameters {...props} />, { | ||
i18nInstance: i18n, | ||
}) | ||
} | ||
const MOCK_MAKE_SNACK_BAR = vi.fn() | ||
describe('Parameters', () => { | ||
let props: React.ComponentProps<typeof Parameters> | ||
|
||
beforeEach(() => { | ||
props = { | ||
protocolId: 'mockId', | ||
} | ||
when(useToaster) | ||
.calledWith() | ||
.thenReturn({ | ||
makeSnackBar: MOCK_MAKE_SNACK_BAR, | ||
} as any) | ||
vi.mocked(useRunTimeParameters).mockReturnValue(mockRTPData) | ||
}) | ||
it('renders the parameters labels and mock data', () => { | ||
render(props) | ||
screen.getByText('Name') | ||
screen.getByText('Default value') | ||
screen.getByText('Range') | ||
screen.getByText('Dry Run') | ||
screen.getByText('6.5') | ||
screen.getByText('Use Gripper') | ||
screen.getByText('Default Module Offsets') | ||
screen.getByText('3 choices') | ||
screen.getByText('EtoH Volume') | ||
screen.getByText('one choice, the second') | ||
}) | ||
}) |
Oops, something went wrong.