-
Notifications
You must be signed in to change notification settings - Fork 179
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(labware-creator): Break out spacing and offset sections
closes #7707
- Loading branch information
Showing
5 changed files
with
432 additions
and
83 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
labware-library/src/labware-creator/components/__tests__/sections/GridOffset.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,141 @@ | ||
import React from 'react' | ||
import { FormikConfig } from 'formik' | ||
import isEqual from 'lodash/isEqual' | ||
import { when } from 'jest-when' | ||
import { render, screen, MatcherFunction } from '@testing-library/react' | ||
import { getDefaultFormState, LabwareFields } from '../../../fields' | ||
import { isEveryFieldHidden } from '../../../utils' | ||
import { GridOffset } from '../../sections/GridOffset' | ||
import { FormAlerts } from '../../FormAlerts' | ||
import { TextField } from '../../TextField' | ||
|
||
import { wrapInFormik } from '../../utils/wrapInFormik' | ||
|
||
jest.mock('../../../utils') | ||
jest.mock('../../TextField') | ||
jest.mock('../../FormAlerts') | ||
|
||
const FormAlertsMock = FormAlerts as jest.MockedFunction<typeof FormAlerts> | ||
|
||
const textFieldMock = TextField as jest.MockedFunction<typeof TextField> | ||
|
||
const isEveryFieldHiddenMock = isEveryFieldHidden as jest.MockedFunction< | ||
typeof isEveryFieldHidden | ||
> | ||
|
||
const formikConfig: FormikConfig<LabwareFields> = { | ||
initialValues: getDefaultFormState(), | ||
onSubmit: jest.fn(), | ||
} | ||
|
||
type Query = (f: MatcherFunction) => HTMLElement | ||
|
||
// This helper function is needed to grab text with other html markup within | ||
const withMarkup = (query: Query) => (text: string): HTMLElement => | ||
// @ts-expect-error `from` doesnt want null | undefined | ||
query((content: string, element: HTMLElement) => { | ||
const hasText = (element: HTMLElement): boolean => | ||
element.textContent === text | ||
const childrenDontHaveText = Array.from(element.children).every( | ||
child => !hasText(child as HTMLElement) | ||
) | ||
return hasText(element) && childrenDontHaveText | ||
}) | ||
|
||
describe('GridOffset', () => { | ||
beforeEach(() => { | ||
textFieldMock.mockImplementation(args => { | ||
if (args.name === 'gridOffsetX') { | ||
return <div>gridOffsetX text field</div> | ||
} | ||
if (args.name === 'gridOffsetY') { | ||
return <div>gridOffsetY text field</div> | ||
} else { | ||
return <div></div> | ||
} | ||
}) | ||
|
||
FormAlertsMock.mockImplementation(args => { | ||
if ( | ||
isEqual(args, { | ||
touched: {}, | ||
errors: {}, | ||
fieldList: ['gridOffsetX', 'gridOffsetY'], | ||
}) | ||
) { | ||
return <div>mock alerts</div> | ||
} else { | ||
return <div></div> | ||
} | ||
}) | ||
}) | ||
|
||
afterEach(() => { | ||
jest.restoreAllMocks() | ||
}) | ||
|
||
it('should render when fields are visible', () => { | ||
render(wrapInFormik(<GridOffset />, formikConfig)) | ||
expect(screen.getByText('Grid Offset')) | ||
expect(screen.getByText('mock alerts')) | ||
expect( | ||
screen.getByText( | ||
"Corner offset informs the robot how far the grid of wells is from the slot's top left corner." | ||
) | ||
) | ||
expect(screen.getByText('gridOffsetX text field')) | ||
expect(screen.getByText('gridOffsetY text field')) | ||
}) | ||
|
||
it('should update instructions when reservoir is selected', () => { | ||
const { getByText } = render( | ||
wrapInFormik(<GridOffset />, { | ||
...formikConfig, | ||
initialValues: { | ||
...formikConfig.initialValues, | ||
labwareType: 'reservoir', | ||
}, | ||
}) | ||
) | ||
|
||
const getByTextWithMarkup = withMarkup(getByText) | ||
getByTextWithMarkup( | ||
"Find the measurement from the center of the top left-most well to the edge of the labware's footprint." | ||
) | ||
}) | ||
|
||
it('should NOT render when the labware type is aluminumBlock', () => { | ||
const { container } = render( | ||
wrapInFormik(<GridOffset />, { | ||
...formikConfig, | ||
initialValues: { | ||
...formikConfig.initialValues, | ||
labwareType: 'aluminumBlock', | ||
}, | ||
}) | ||
) | ||
expect(container.firstChild).toBe(null) | ||
}) | ||
|
||
it('should NOT render when the labware type is tubeRack', () => { | ||
const { container } = render( | ||
wrapInFormik(<GridOffset />, { | ||
...formikConfig, | ||
initialValues: { | ||
...formikConfig.initialValues, | ||
labwareType: 'tubeRack', | ||
}, | ||
}) | ||
) | ||
expect(container.firstChild).toBe(null) | ||
}) | ||
|
||
it('should not render when all fields are hidden', () => { | ||
when(isEveryFieldHiddenMock) | ||
.calledWith(['gridOffsetX', 'gridOffsetY'], formikConfig.initialValues) | ||
.mockReturnValue(true) | ||
|
||
const { container } = render(wrapInFormik(<GridOffset />, formikConfig)) | ||
expect(container.firstChild).toBe(null) | ||
}) | ||
}) |
109 changes: 109 additions & 0 deletions
109
labware-library/src/labware-creator/components/__tests__/sections/WellSpacing.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,109 @@ | ||
import React from 'react' | ||
import { FormikConfig } from 'formik' | ||
import isEqual from 'lodash/isEqual' | ||
import { when } from 'jest-when' | ||
import { render, screen } from '@testing-library/react' | ||
import { getDefaultFormState, LabwareFields } from '../../../fields' | ||
import { isEveryFieldHidden } from '../../../utils' | ||
import { WellSpacing } from '../../sections/WellSpacing' | ||
import { FormAlerts } from '../../FormAlerts' | ||
import { TextField } from '../../TextField' | ||
|
||
import { wrapInFormik } from '../../utils/wrapInFormik' | ||
|
||
jest.mock('../../../utils') | ||
jest.mock('../../TextField') | ||
jest.mock('../../FormAlerts') | ||
|
||
const FormAlertsMock = FormAlerts as jest.MockedFunction<typeof FormAlerts> | ||
|
||
const textFieldMock = TextField as jest.MockedFunction<typeof TextField> | ||
|
||
const isEveryFieldHiddenMock = isEveryFieldHidden as jest.MockedFunction< | ||
typeof isEveryFieldHidden | ||
> | ||
|
||
const formikConfig: FormikConfig<LabwareFields> = { | ||
initialValues: getDefaultFormState(), | ||
onSubmit: jest.fn(), | ||
} | ||
|
||
describe('WellSpacing', () => { | ||
beforeEach(() => { | ||
textFieldMock.mockImplementation(args => { | ||
if (args.name === 'gridSpacingX') { | ||
return <div>gridSpacingX text field</div> | ||
} | ||
if (args.name === 'gridSpacingY') { | ||
return <div>gridSpacingY text field</div> | ||
} else { | ||
return <div></div> | ||
} | ||
}) | ||
|
||
FormAlertsMock.mockImplementation(args => { | ||
if ( | ||
isEqual(args, { | ||
touched: {}, | ||
errors: {}, | ||
fieldList: ['gridSpacingX', 'gridSpacingY'], | ||
}) | ||
) { | ||
return <div>mock alerts</div> | ||
} else { | ||
return <div></div> | ||
} | ||
}) | ||
}) | ||
|
||
afterEach(() => { | ||
jest.restoreAllMocks() | ||
}) | ||
|
||
it('should render when fields are visible', () => { | ||
render(wrapInFormik(<WellSpacing />, formikConfig)) | ||
expect(screen.getByText('Well Spacing')) | ||
expect(screen.getByText('mock alerts')) | ||
expect( | ||
screen.getByText( | ||
'Well spacing measurements inform the robot how far away rows and columns are from each other.' | ||
) | ||
) | ||
expect(screen.getByText('gridSpacingX text field')) | ||
expect(screen.getByText('gridSpacingX text field')) | ||
}) | ||
|
||
it('should NOT render when the labware type is aluminumBlock', () => { | ||
const { container } = render( | ||
wrapInFormik(<WellSpacing />, { | ||
...formikConfig, | ||
initialValues: { | ||
...formikConfig.initialValues, | ||
labwareType: 'aluminumBlock', | ||
}, | ||
}) | ||
) | ||
expect(container.firstChild).toBe(null) | ||
}) | ||
it('should NOT render when the labware type is tubeRack', () => { | ||
const { container } = render( | ||
wrapInFormik(<WellSpacing />, { | ||
...formikConfig, | ||
initialValues: { | ||
...formikConfig.initialValues, | ||
labwareType: 'tubeRack', | ||
}, | ||
}) | ||
) | ||
expect(container.firstChild).toBe(null) | ||
}) | ||
|
||
it('should not render when all fields are hidden', () => { | ||
when(isEveryFieldHiddenMock) | ||
.calledWith(['gridSpacingX', 'gridSpacingY'], formikConfig.initialValues) | ||
.mockReturnValue(true) | ||
|
||
const { container } = render(wrapInFormik(<WellSpacing />, formikConfig)) | ||
expect(container.firstChild).toBe(null) | ||
}) | ||
}) |
93 changes: 93 additions & 0 deletions
93
labware-library/src/labware-creator/components/sections/GridOffset.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,93 @@ | ||
import * as React from 'react' | ||
import { useFormikContext } from 'formik' | ||
import { makeMaskToDecimal } from '../../fieldMasks' | ||
import { isEveryFieldHidden } from '../../utils' | ||
import { LabwareFields } from '../../fields' | ||
import { FormAlerts } from '../FormAlerts' | ||
import { TextField } from '../TextField' | ||
import { XYOffsetImg } from '../diagrams' | ||
import { SectionBody } from './SectionBody' | ||
|
||
import styles from '../../styles.css' | ||
|
||
const maskTo2Decimal = makeMaskToDecimal(2) | ||
|
||
interface Props { | ||
values: LabwareFields | ||
} | ||
|
||
// TODO (ka 2021-5-11): Broke this out here since we will need to have more conditions for tips | ||
const Instructions = (props: Props): JSX.Element => { | ||
const { values } = props | ||
return ( | ||
<> | ||
<p> | ||
Find the measurement from the center of{' '} | ||
<strong> | ||
{values.labwareType === 'reservoir' | ||
? 'the top left-most well' | ||
: 'well A1'} | ||
</strong>{' '} | ||
to the edge of the labware{"'"}s footprint. | ||
</p> | ||
<p> | ||
Corner offset informs the robot how far the grid of wells is from the | ||
slot{"'"}s top left corner. | ||
</p> | ||
<div className={styles.help_text}> | ||
<img src={require('../../images/offset_helpText.svg')} /> | ||
</div> | ||
</> | ||
) | ||
} | ||
|
||
const Content = (props: Props): JSX.Element => { | ||
const { values } = props | ||
return ( | ||
<div className={styles.flex_row}> | ||
<div className={styles.instructions_column}> | ||
<Instructions values={values} /> | ||
</div> | ||
<div className={styles.diagram_column}> | ||
<XYOffsetImg | ||
labwareType={values.labwareType} | ||
wellShape={values.wellShape} | ||
/> | ||
</div> | ||
<div className={styles.form_fields_column}> | ||
<TextField | ||
name="gridOffsetX" | ||
inputMasks={[maskTo2Decimal]} | ||
units="mm" | ||
/> | ||
<TextField | ||
name="gridOffsetY" | ||
inputMasks={[maskTo2Decimal]} | ||
units="mm" | ||
/> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export const GridOffset = (): JSX.Element | null => { | ||
const fieldList: Array<keyof LabwareFields> = ['gridOffsetX', 'gridOffsetY'] | ||
const { values, errors, touched } = useFormikContext<LabwareFields>() | ||
if ( | ||
isEveryFieldHidden(fieldList, values) || | ||
(values.labwareType != null && | ||
['aluminumBlock', 'tubeRack'].includes(values.labwareType)) | ||
) { | ||
return null | ||
} | ||
return ( | ||
<div className={styles.new_definition_section}> | ||
<SectionBody label="Grid Offset"> | ||
<> | ||
<FormAlerts touched={touched} errors={errors} fieldList={fieldList} /> | ||
<Content values={values} /> | ||
</> | ||
</SectionBody> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.