Skip to content

Commit fed50b4

Browse files
authored
refactor(app, components): pt 2 protocol viz prototype feedback (#19002)
closes AUTH-2131
1 parent 1bb5cfd commit fed50b4

File tree

12 files changed

+538
-269
lines changed

12 files changed

+538
-269
lines changed

app/src/organisms/Desktop/ProtocolDetails/AnnotatedSteps/AnnotatedGroup.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
22

33
import { COLORS, Icon, StyledText } from '@opentrons/components'
44

5-
import styles from './annotatedSteps.module.css'
5+
import styles from './annotatedsteps.module.css'
66
import { IndividualCommand } from './IndividualCommand'
77

88
import type { Dispatch, SetStateAction } from 'react'
@@ -14,10 +14,12 @@ import type {
1414
import type { LeafNode } from '/app/redux/protocol-storage'
1515

1616
interface AnnotatedGroupProps {
17+
scrollTargetId: string | null
1718
annotationType: string
1819
subCommands: LeafNode[]
1920
analysis: ProtocolAnalysisOutput | CompletedProtocolAnalysis
2021
allRunDefs: LabwareDefinition[]
22+
commandStartNumber: number
2123
setSelectedCommand?: Dispatch<SetStateAction<string | null>>
2224
handlePause?: () => void
2325
}
@@ -29,11 +31,12 @@ export function AnnotatedGroup(props: AnnotatedGroupProps): JSX.Element {
2931
allRunDefs,
3032
setSelectedCommand,
3133
handlePause,
34+
commandStartNumber,
35+
scrollTargetId,
3236
} = props
3337
const [isExpanded, setIsExpanded] = useState(() =>
3438
subCommands.some(command => command.isHighlighted)
3539
)
36-
3740
useEffect(() => {
3841
setIsExpanded(subCommands.some(command => command.isHighlighted))
3942
}, [subCommands])
@@ -61,9 +64,11 @@ export function AnnotatedGroup(props: AnnotatedGroupProps): JSX.Element {
6164
<div className={styles.annotated_group_expanded}>
6265
{subCommands.map((subCommand, index) => (
6366
<IndividualCommand
67+
scrollTargetId={scrollTargetId}
6468
fromGroup={false}
6569
key={`${subCommand.command.id}_${index}`}
6670
command={subCommand.command}
71+
commandNumber={commandStartNumber + index}
6772
analysis={analysis}
6873
isHighlighted={subCommand.isHighlighted}
6974
allRunDefs={allRunDefs}

app/src/organisms/Desktop/ProtocolDetails/AnnotatedSteps/IndividualCommand.tsx

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { useEffect, useRef } from 'react'
1+
import { useEffect, useRef, useState } from 'react'
22
import clsx from 'clsx'
33

4-
import { COLORS, CommandText } from '@opentrons/components'
4+
import { COLORS, CommandText, StyledText } from '@opentrons/components'
55
import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
66

77
import { CommandIcon } from '/app/molecules/Command'
88

9-
import styles from './annotatedSteps.module.css'
9+
import styles from './annotatedsteps.module.css'
1010

1111
import type { Dispatch, SetStateAction } from 'react'
1212
import type {
@@ -17,11 +17,13 @@ import type {
1717
} from '@opentrons/shared-data'
1818

1919
interface IndividualCommandProps {
20+
scrollTargetId: string | null
2021
command: RunTimeCommand
2122
analysis: ProtocolAnalysisOutput | CompletedProtocolAnalysis
2223
isHighlighted: boolean
2324
allRunDefs: LabwareDefinition[]
2425
fromGroup: boolean
26+
commandNumber: number
2527
setSelectedCommand?: Dispatch<SetStateAction<string | null>>
2628
}
2729
export function IndividualCommand({
@@ -31,19 +33,24 @@ export function IndividualCommand({
3133
allRunDefs,
3234
setSelectedCommand,
3335
fromGroup,
36+
commandNumber,
37+
scrollTargetId,
3438
}: IndividualCommandProps): JSX.Element {
39+
const [showNumber, setShowNumber] = useState<boolean>(false)
3540
const commandRef = useRef<HTMLDivElement | null>(null)
3641
const iconColor = isHighlighted ? COLORS.purple50 : COLORS.grey50
3742

3843
useEffect(() => {
39-
if (isHighlighted && commandRef.current) {
40-
commandRef.current.scrollIntoView({
41-
behavior: 'smooth',
42-
block: 'nearest',
43-
inline: 'nearest',
44+
if (isHighlighted && commandRef.current && command.id === scrollTargetId) {
45+
requestAnimationFrame(() => {
46+
commandRef.current?.scrollIntoView({
47+
behavior: 'smooth',
48+
block: 'nearest',
49+
inline: 'nearest',
50+
})
4451
})
4552
}
46-
}, [isHighlighted])
53+
}, [isHighlighted, scrollTargetId, command])
4754

4855
const commandWrapStyle = clsx(styles.individual_command_wrap, {
4956
[styles.individual_command_wrap_highlighted]: isHighlighted,
@@ -57,22 +64,37 @@ export function IndividualCommand({
5764
)
5865

5966
return (
60-
<div className={individualCommandContainerStyle} ref={commandRef}>
67+
<div
68+
className={individualCommandContainerStyle}
69+
ref={commandRef}
70+
onMouseEnter={() => {
71+
setShowNumber(true)
72+
}}
73+
onMouseLeave={() => {
74+
setShowNumber(false)
75+
}}
76+
>
6177
<div
6278
className={commandWrapStyle}
6379
onClick={() => {
6480
setSelectedCommand?.(command.id)
6581
}}
6682
>
6783
<div className={styles.individual_command} key={command.id}>
68-
<CommandIcon command={command} color={iconColor} />
69-
<CommandText
70-
command={command}
71-
robotType={analysis?.robotType ?? FLEX_ROBOT_TYPE}
72-
color={COLORS.black90}
73-
commandTextData={analysis}
74-
allRunDefs={allRunDefs}
75-
/>
84+
<div className={styles.individual_command_header}>
85+
<CommandIcon command={command} color={iconColor} />
86+
<CommandText
87+
command={command}
88+
robotType={analysis?.robotType ?? FLEX_ROBOT_TYPE}
89+
commandTextData={analysis}
90+
allRunDefs={allRunDefs}
91+
/>
92+
</div>
93+
{showNumber ? (
94+
<StyledText color={COLORS.grey60} desktopStyle="captionRegular">
95+
{commandNumber}
96+
</StyledText>
97+
) : null}
7698
</div>
7799
</div>
78100
</div>

app/src/organisms/Desktop/ProtocolDetails/AnnotatedSteps/annotatedSteps.module.css renamed to app/src/organisms/Desktop/ProtocolDetails/AnnotatedSteps/annotatedsteps.module.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@
6060
}
6161

6262
.individual_command {
63+
display: flex;
64+
width: 100%;
65+
align-items: center;
66+
justify-content: space-between;
67+
}
68+
69+
.individual_command_header {
6370
display: flex;
6471
align-items: center;
6572
gap: var(--spacing-8);

app/src/organisms/Desktop/ProtocolDetails/AnnotatedSteps/index.tsx

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo } from 'react'
1+
import { useEffect, useMemo, useState } from 'react'
22

33
import {
44
COLORS,
@@ -8,7 +8,7 @@ import {
88
} from '@opentrons/components'
99

1010
import { AnnotatedGroup } from './AnnotatedGroup'
11-
import styles from './annotatedSteps.module.css'
11+
import styles from './annotatedsteps.module.css'
1212
import { IndividualCommand } from './IndividualCommand'
1313

1414
import type { Dispatch, SetStateAction } from 'react'
@@ -34,6 +34,7 @@ export function AnnotatedSteps(props: AnnotatedStepsProps): JSX.Element {
3434
setSelectedCommand,
3535
handlePause,
3636
} = props
37+
const [scrollTargetId, setScrollTargetId] = useState<string | null>(null)
3738
const isValidRobotSideAnalysis = analysis != null
3839
const allRunDefs = useMemo(
3940
() =>
@@ -63,6 +64,25 @@ export function AnnotatedSteps(props: AnnotatedStepsProps): JSX.Element {
6364
}
6465
}
6566
})
67+
68+
useEffect(() => {
69+
if (groupedCommands != null) {
70+
const flatCommands = groupedCommands.flatMap(node =>
71+
'subCommands' in node ? node.subCommands : [node]
72+
)
73+
74+
const targetNode = flatCommands.find(
75+
node => analysis.commands.indexOf(node.command) === currentCommandIndex
76+
)
77+
78+
if (targetNode?.command.id && scrollTargetId !== targetNode.command.id) {
79+
setScrollTargetId(targetNode.command.id)
80+
}
81+
}
82+
}, [analysis, groupedCommands, currentCommandIndex, scrollTargetId])
83+
84+
let commandNumber = 0
85+
6686
return (
6787
<div className={styles.annotated_steps_container}>
6888
<div className={styles.annotated_steps_wrap}>
@@ -74,44 +94,59 @@ export function AnnotatedSteps(props: AnnotatedStepsProps): JSX.Element {
7494
nextIndex != null && 'annotationIndex' in nextIndex
7595

7696
if ('annotationIndex' in group) {
97+
const subCommandStartNumber = commandNumber + 1 // Starting number for this group
98+
commandNumber += group.subCommands.length
99+
77100
return (
78101
<AnnotatedGroup
79102
key={`group_${group.annotationIndex}_${index}`}
103+
scrollTargetId={scrollTargetId}
80104
analysis={analysis}
81105
annotationType={
82106
annotations[group.annotationIndex]?.machineReadableName
83107
}
84108
subCommands={group.subCommands}
109+
commandStartNumber={subCommandStartNumber}
85110
allRunDefs={allRunDefs}
86111
setSelectedCommand={setSelectedCommand}
87112
handlePause={handlePause}
88113
/>
89114
)
90115
} else {
116+
const currentCommandNumber = ++commandNumber
117+
91118
return (
92119
<IndividualCommand
120+
scrollTargetId={scrollTargetId}
93121
fromGroup={nextIsGrouped}
94122
key={group.command.id}
95123
command={group.command}
96124
isHighlighted={group.isHighlighted}
97125
analysis={analysis}
98126
allRunDefs={allRunDefs}
99127
setSelectedCommand={setSelectedCommand}
128+
commandNumber={currentCommandNumber}
100129
/>
101130
)
102131
}
103132
})
104-
: analysis.commands.map((command, index) => (
105-
<IndividualCommand
106-
fromGroup={false}
107-
key={`individual_${command.id}`}
108-
command={command}
109-
isHighlighted={index === currentCommandIndex}
110-
analysis={analysis}
111-
allRunDefs={allRunDefs}
112-
setSelectedCommand={setSelectedCommand}
113-
/>
114-
))}
133+
: analysis.commands.map((command, index) => {
134+
const currentCommandNumber = ++commandNumber
135+
136+
return (
137+
<IndividualCommand
138+
scrollTargetId={scrollTargetId}
139+
fromGroup={false}
140+
key={`individual_${command.id}`}
141+
command={command}
142+
commandNumber={currentCommandNumber}
143+
isHighlighted={index === currentCommandIndex}
144+
analysis={analysis}
145+
allRunDefs={allRunDefs}
146+
setSelectedCommand={setSelectedCommand}
147+
/>
148+
)
149+
})}
115150
{analysis?.errors.length > 0 ? (
116151
<div className={styles.annotated_steps_error_container}>
117152
{analysis?.errors.map(error => (

0 commit comments

Comments
 (0)