Skip to content

Commit 8eb14ae

Browse files
authored
refactor(app): Desktop implementation for SelectRecoveryOption (#15691)
This PR got away from me a little bit (and I still have to test it - the sum of these two is why it's in draft). It's probably best to view it commit by commit. Overall, the point is to implement the desktop styling for the SelectRecoveryOption component of error recovery, here: https://www.figma.com/design/8dMeu8MuPfXoORtOV6cACO/Feature%3A-Error-Recovery-August-Release?node-id=4829-78111&t=bTwOek0mZSA61ugm-4 . This is a little weird because it is exactly semantically equivalent to the ODD panel, but has a very different styling - two columns instead of one. It also uses the radio buttons that we use only in OT-2 tip length calibration method selection, which are old and use cssmodules and need a cleanup. The approach here was to add some fundamental structure components to InterventionModal that will render two columns on desktop (as the TwoColumn structure, including min-size and wrap) and one column on the ODD at full width. Then, we can build on top of that in ErrorRecovery, with the big difference being that the ErrorRecovery component also folds in the standard ER footer (or will - already had to find/replace a bunch of stuff, going to move other components over to specifying their footers through the wrapper as we update their styles). There were also some incidental refactors. By commit, - 981e828 : we have these utility components for visualizing structure components; make them handle sometimes being size limited and sometimes not being - a7917e7 : Add a wrapper around the RadioGroup component. We're going to need to update these styles further (that's a todo) but we don't want to, or I don't want to, have to mess with cssmodules. Also I think @TamarZanzouri is touching this stuff at the same time, so keep it isolated and droppable. Did I let the worms in my brain drive and make a typescript wrapper for getting the change events to take an inferred union of button values? Yes. Also note the one change to the underlying component to specify IDs so that we can use aria label linking in the passed components, which is both good practice and allows tests to work later. - e835f86 The error recovery component for viewing failed and next commands was bundled with presenting text in the left column, which is not what we want this panel to have, so split it out. - 699cb79 The first fun one: Add a component that can responsively present one or two columns, and by "responsively" i mean abuse css mediaquery to only do it on touchscreen and maintain the two-column responsive presentation through desktop resizes. Best appreciated in storybook. - 52150cb Simultaneous refactor and feature - we had a RecoveryContentWrapper but it was really just a single column, this isn't helpful, add a single and twocolumn version (lots of find and replace since I changed the name) and then add a OneOrTwoColumn on top of that. These wrappers also encompass rendering footers because the footer needs to be in different places in one or two column - 00f38e8 Use all that stuff to render SelectRecoveryOption! Note that the tests now run on both presentations and it all works because of that aria label stuff. ## Todo - [x] Visual tests, at all (this is why it's a draft)
1 parent fa23071 commit 8eb14ae

30 files changed

+664
-295
lines changed
Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,13 @@
11
import * as React from 'react'
22

3-
import {
4-
LegacyStyledText,
5-
Box,
6-
Flex,
7-
BORDERS,
8-
RESPONSIVENESS,
9-
SPACING,
10-
ALIGN_CENTER,
11-
JUSTIFY_CENTER,
12-
} from '@opentrons/components'
3+
import { Box, RESPONSIVENESS } from '@opentrons/components'
134

145
import { OneColumn as OneColumnComponent } from './'
6+
import { StandInContent } from './story-utils/StandIn'
157

168
import type { Meta, StoryObj } from '@storybook/react'
179

18-
function StandInContent(): JSX.Element {
19-
return (
20-
<Flex
21-
border={'4px dashed #A864FFFF'}
22-
borderRadius={BORDERS.borderRadius8}
23-
margin={SPACING.spacing16}
24-
height="104px"
25-
backgroundColor="#A864FF19"
26-
alignItems={ALIGN_CENTER}
27-
justifyContent={JUSTIFY_CENTER}
28-
>
29-
<LegacyStyledText as="h1">
30-
This is a standin for some other component
31-
</LegacyStyledText>
32-
</Flex>
33-
)
34-
}
35-
36-
const meta: Meta<React.ComponentProps<OneColumnComponent>> = {
10+
const meta: Meta<React.ComponentProps<typeof OneColumnComponent>> = {
3711
title: 'App/Molecules/InterventionModal/OneColumn',
3812
component: OneColumnComponent,
3913
render: args => (
@@ -46,14 +20,14 @@ const meta: Meta<React.ComponentProps<OneColumnComponent>> = {
4620
`}
4721
>
4822
<OneColumnComponent>
49-
<StandInContent />
23+
<StandInContent>This is a standin for another component</StandInContent>
5024
</OneColumnComponent>
5125
</Box>
5226
),
5327
}
5428

5529
export default meta
5630

57-
export type Story = StoryObj<OneColumnComponent>
31+
export type Story = StoryObj<typeof OneColumnComponent>
5832

5933
export const ExampleOneColumn: Story = { args: {} }
Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
import * as React from 'react'
22

3-
import { Box } from '@opentrons/components'
3+
import {
4+
Flex,
5+
DIRECTION_COLUMN,
6+
JUSTIFY_SPACE_BETWEEN,
7+
} from '@opentrons/components'
8+
import type { StyleProps } from '@opentrons/components'
49

5-
export interface OneColumnProps {
10+
export interface OneColumnProps extends StyleProps {
611
children: React.ReactNode
712
}
813

9-
export function OneColumn({ children }: OneColumnProps): JSX.Element {
10-
return <Box width="100%">{children}</Box>
14+
export function OneColumn({
15+
children,
16+
...styleProps
17+
}: OneColumnProps): JSX.Element {
18+
return (
19+
<Flex
20+
flexDirection={DIRECTION_COLUMN}
21+
justifyContent={JUSTIFY_SPACE_BETWEEN}
22+
width="100%"
23+
{...styleProps}
24+
>
25+
{children}
26+
</Flex>
27+
)
1128
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as React from 'react'
2+
3+
import { OneColumnOrTwoColumn } from './'
4+
5+
import { StandInContent } from './story-utils/StandIn'
6+
import { VisibleContainer } from './story-utils/VisibleContainer'
7+
import { css } from 'styled-components'
8+
import {
9+
RESPONSIVENESS,
10+
Flex,
11+
ALIGN_CENTER,
12+
JUSTIFY_SPACE_AROUND,
13+
DIRECTION_COLUMN,
14+
} from '@opentrons/components'
15+
16+
import type { Meta, StoryObj } from '@storybook/react'
17+
18+
function Wrapper(props: {}): JSX.Element {
19+
return (
20+
<OneColumnOrTwoColumn>
21+
<StandInContent>
22+
<Flex
23+
flexDirection={DIRECTION_COLUMN}
24+
alignItems={ALIGN_CENTER}
25+
justifyContent={JUSTIFY_SPACE_AROUND}
26+
height="100%"
27+
>
28+
This component is the only one shown on the ODD.
29+
</Flex>
30+
</StandInContent>
31+
<StandInContent>
32+
<Flex
33+
flexDirection={DIRECTION_COLUMN}
34+
alignItems={ALIGN_CENTER}
35+
justifyContent={JUSTIFY_SPACE_AROUND}
36+
height="100%"
37+
>
38+
This component is shown in the right column on desktop.
39+
</Flex>
40+
</StandInContent>
41+
</OneColumnOrTwoColumn>
42+
)
43+
}
44+
45+
const meta: Meta<React.ComponentProps<typeof Wrapper>> = {
46+
title: 'App/Molecules/InterventionModal/OneColumnOrTwoColumn',
47+
component: Wrapper,
48+
decorators: [
49+
Story => (
50+
<VisibleContainer
51+
css={css`
52+
min-width: min(max-content, 100vw);
53+
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
54+
width: 500px;
55+
}
56+
`}
57+
>
58+
<Story />
59+
</VisibleContainer>
60+
),
61+
],
62+
}
63+
64+
export default meta
65+
66+
type Story = StoryObj<typeof Wrapper>
67+
68+
export const OneOrTwoColumn: Story = {}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react'
2+
3+
import { css } from 'styled-components'
4+
import {
5+
Flex,
6+
Box,
7+
DIRECTION_ROW,
8+
SPACING,
9+
WRAP,
10+
RESPONSIVENESS,
11+
} from '@opentrons/components'
12+
import type { StyleProps } from '@opentrons/components'
13+
import { TWO_COLUMN_ELEMENT_MIN_WIDTH } from './constants'
14+
15+
export interface OneColumnOrTwoColumnProps extends StyleProps {
16+
children: [React.ReactNode, React.ReactNode]
17+
}
18+
19+
export function OneColumnOrTwoColumn({
20+
children: [leftOrSingleElement, optionallyDisplayedRightElement],
21+
...styleProps
22+
}: OneColumnOrTwoColumnProps): JSX.Element {
23+
return (
24+
<Flex
25+
flexDirection={DIRECTION_ROW}
26+
gap={SPACING.spacing40}
27+
flexWrap={WRAP}
28+
{...styleProps}
29+
>
30+
<Box
31+
flex="1"
32+
css={css`
33+
min-width: ${TWO_COLUMN_ELEMENT_MIN_WIDTH};
34+
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
35+
min-width: none;
36+
width: 100%;
37+
}
38+
`}
39+
>
40+
{leftOrSingleElement}
41+
</Box>
42+
<Box
43+
flex="1"
44+
minWidth={TWO_COLUMN_ELEMENT_MIN_WIDTH}
45+
css={css`
46+
@media ${RESPONSIVENESS.touchscreenMediaQuerySpecs} {
47+
display: none;
48+
}
49+
`}
50+
>
51+
{optionallyDisplayedRightElement}
52+
</Box>
53+
</Flex>
54+
)
55+
}

app/src/molecules/InterventionModal/TwoColumn.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
import * as React from 'react'
22

33
import { Flex, Box, DIRECTION_ROW, SPACING, WRAP } from '@opentrons/components'
4+
import type { StyleProps } from '@opentrons/components'
5+
import { TWO_COLUMN_ELEMENT_MIN_WIDTH } from './constants'
46

5-
export interface TwoColumnProps {
7+
export interface TwoColumnProps extends StyleProps {
68
children: [React.ReactNode, React.ReactNode]
79
}
810

911
export function TwoColumn({
1012
children: [leftElement, rightElement],
13+
...styleProps
1114
}: TwoColumnProps): JSX.Element {
1215
return (
13-
<Flex flexDirection={DIRECTION_ROW} gap={SPACING.spacing40} flexWrap={WRAP}>
14-
<Box flex="1" minWidth="17.1875rem">
16+
<Flex
17+
flexDirection={DIRECTION_ROW}
18+
gap={SPACING.spacing40}
19+
flexWrap={WRAP}
20+
{...styleProps}
21+
>
22+
<Box flex="1" minWidth={TWO_COLUMN_ELEMENT_MIN_WIDTH}>
1523
{leftElement}
1624
</Box>
17-
<Box flex="1" minWidth="17.1875rem">
25+
<Box flex="1" minWidth={TWO_COLUMN_ELEMENT_MIN_WIDTH}>
1826
{rightElement}
1927
</Box>
2028
</Flex>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const TWO_COLUMN_ELEMENT_MIN_WIDTH = '17.1875rem' as const

app/src/molecules/InterventionModal/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type { IconName } from '@opentrons/components'
2323
import { ModalContentOneColSimpleButtons } from './ModalContentOneColSimpleButtons'
2424
import { TwoColumn } from './TwoColumn'
2525
import { OneColumn } from './OneColumn'
26+
import { OneColumnOrTwoColumn } from './OneColumnOrTwoColumn'
2627
import { ModalContentMixed } from './ModalContentMixed'
2728
import { DescriptionContent } from './DescriptionContent'
2829
import { DeckMapContent } from './DeckMapContent'
@@ -31,6 +32,7 @@ export {
3132
ModalContentOneColSimpleButtons,
3233
TwoColumn,
3334
OneColumn,
35+
OneColumnOrTwoColumn,
3436
ModalContentMixed,
3537
DescriptionContent,
3638
DeckMapContent,
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import * as React from 'react'
22
import { Box, BORDERS } from '@opentrons/components'
33

4-
export function StandInContent(): JSX.Element {
4+
export function StandInContent({
5+
children,
6+
}: {
7+
children?: React.ReactNode
8+
}): JSX.Element {
59
return (
610
<Box
711
border={'4px dashed #A864FFFF'}
812
borderRadius={BORDERS.borderRadius8}
913
height="104px"
1014
backgroundColor="#A864FF19"
11-
/>
15+
>
16+
{children}
17+
</Box>
1218
)
1319
}

app/src/molecules/InterventionModal/story-utils/VisibleContainer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import * as React from 'react'
22

33
import { Box, BORDERS, SPACING } from '@opentrons/components'
4+
import type { StyleProps } from '@opentrons/components'
45

5-
export interface VisibleContainerProps {
6+
export interface VisibleContainerProps extends StyleProps {
67
children: JSX.Element | JSX.Element[]
78
}
89

910
export function VisibleContainer({
1011
children,
12+
...styleProps
1113
}: VisibleContainerProps): JSX.Element {
1214
return (
1315
<Box
@@ -18,6 +20,7 @@ export function VisibleContainer({
1820
maxWidth="100vp"
1921
maxHeight="100vp"
2022
padding={SPACING.spacing32}
23+
{...styleProps}
2124
>
2225
{children}
2326
</Box>

app/src/organisms/ErrorRecoveryFlows/RecoveryDoorOpen.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import {
1616
} from '@opentrons/components'
1717
import { RUN_STATUS_AWAITING_RECOVERY_BLOCKED_BY_OPEN_DOOR } from '@opentrons/api-client'
1818

19-
import { RecoveryContentWrapper, RecoveryFooterButtons } from './shared'
19+
import {
20+
RecoverySingleColumnContentWrapper,
21+
RecoveryFooterButtons,
22+
} from './shared'
2023

2124
import type { RecoveryContentProps } from './types'
2225

@@ -31,7 +34,7 @@ export function RecoveryDoorOpen({
3134
const { t } = useTranslation('error_recovery')
3235

3336
return (
34-
<RecoveryContentWrapper>
37+
<RecoverySingleColumnContentWrapper>
3538
<Flex
3639
padding={SPACING.spacing40}
3740
gridGap={SPACING.spacing24}
@@ -70,7 +73,7 @@ export function RecoveryDoorOpen({
7073
isLoadingPrimaryBtnAction={isResumeRecoveryLoading}
7174
/>
7275
</Flex>
73-
</RecoveryContentWrapper>
76+
</RecoverySingleColumnContentWrapper>
7477
)
7578
}
7679

0 commit comments

Comments
 (0)