Skip to content

Commit f505b8d

Browse files
committed
feat: show for-each in editor
1 parent b3cc100 commit f505b8d

File tree

11 files changed

+242
-15
lines changed

11 files changed

+242
-15
lines changed

packages/frontend/src/components/FlowStepConfigurationModal/ChooseAppAndEvent/ChooseApp.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
TOOLBOX_ACTIONS,
3030
TOOLBOX_APP_KEY,
3131
useIfThenInitializer,
32+
useIsForEachSelectable,
3233
useIsIfThenSelectable,
3334
} from '@/helpers/toolbox'
3435

@@ -78,6 +79,7 @@ export default function ChooseApp(props: ChooseAppProps) {
7879
[onSelectApp, onSelectAppEvent],
7980
)
8081

82+
const isForEachSelectable = useIsForEachSelectable({ isLastStep })
8183
const isIfThenSelectable = useIsIfThenSelectable({ isLastStep })
8284
const toolboxActionsToDisplay = useMemo(() => {
8385
if (isLoading || !launchDarkly.flags) {
@@ -273,8 +275,10 @@ export default function ChooseApp(props: ChooseAppProps) {
273275
action={action}
274276
onSelectAppEvent={() => onSelectAppEvent(app, action)}
275277
isDisabled={
276-
action.key === TOOLBOX_ACTIONS.IfThen &&
277-
!isIfThenSelectable
278+
(action.key === TOOLBOX_ACTIONS.IfThen &&
279+
!isIfThenSelectable) ||
280+
(action.key === TOOLBOX_ACTIONS.ForEach &&
281+
!isForEachSelectable)
278282
}
279283
searchQuery={searchQuery}
280284
/>

packages/frontend/src/components/FlowStepConfigurationModal/ChooseAppAndEvent/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { GET_APP_CONNECTIONS } from '@/graphql/queries/get-app-connections'
88
import {
99
TOOLBOX_ACTIONS,
1010
TOOLBOX_APP_KEY,
11+
useForEachInitializer,
1112
useIfThenInitializer,
1213
} from '@/helpers/toolbox'
1314

@@ -68,6 +69,7 @@ export default function ChooseAppAndEvent(props: ChooseAppAndEventProps) {
6869
return excelConnections[0]
6970
}, [selectedApp, appConnectionsData])
7071

72+
const [initializeForEach] = useForEachInitializer()
7173
const [initializeIfThen] = useIfThenInitializer()
7274

7375
/**
@@ -134,6 +136,11 @@ export default function ChooseAppAndEvent(props: ChooseAppAndEventProps) {
134136
})
135137
newStepId = updatedStep.id
136138
newStepIndex = updatedStep.position - 1
139+
140+
// account for for-each
141+
if (triggerOrAction.key === TOOLBOX_ACTIONS.ForEach) {
142+
await initializeForEach(step)
143+
}
137144
}
138145
}
139146
patchModalState({ isLoading: false })
@@ -155,6 +162,7 @@ export default function ChooseAppAndEvent(props: ChooseAppAndEventProps) {
155162
onCreateStep,
156163
initializeIfThen,
157164
onUpdateStep,
165+
initializeForEach,
158166
],
159167
)
160168

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { IStep } from '@plumber/types'
2+
3+
import { Fragment, useContext, useMemo } from 'react'
4+
import { Flex } from '@chakra-ui/react'
5+
6+
import { EditorContext } from '@/contexts/Editor'
7+
import { FlowStep, FlowStepGroup } from '@/exports/components'
8+
9+
import { HoverAddStepButton } from '../IfThen/HoverAddStepButton'
10+
import { allowAddStep } from '../utils'
11+
12+
interface ForEachProps {
13+
groupedSteps: IStep[][]
14+
stepsBeforeGroup: IStep[]
15+
}
16+
17+
export default function ForEach(props: ForEachProps) {
18+
const { groupedSteps } = props
19+
20+
const { isDrawerOpen, readOnly: isEditorReadOnly } = useContext(EditorContext)
21+
const forEachSteps = groupedSteps[0]
22+
const ifThenSteps = useMemo(() => {
23+
if (groupedSteps.length === 1) {
24+
return []
25+
}
26+
return groupedSteps.slice(1)
27+
}, [groupedSteps])
28+
29+
const canAddStep =
30+
allowAddStep(forEachSteps) ||
31+
(forEachSteps.length === 1 && ifThenSteps.length !== 0)
32+
33+
return (
34+
<Flex flexDir="column" alignItems="center" borderRadius="lg" w="100%">
35+
<Flex flexDir="column" w="100%" px={4} py={3}>
36+
{forEachSteps?.map((step, index) => {
37+
return (
38+
<Fragment key={step.id}>
39+
<FlowStep
40+
step={step}
41+
index={index}
42+
isDeletable={index !== 0}
43+
isNested={true}
44+
isLastStep={index === forEachSteps.length - 1}
45+
/>
46+
<HoverAddStepButton
47+
isDisabled={isEditorReadOnly || !canAddStep}
48+
isDrawerOpen={isDrawerOpen}
49+
isLastStep={
50+
index === forEachSteps.length - 1 && ifThenSteps.length === 0
51+
}
52+
prevStepId={step.id}
53+
/>
54+
</Fragment>
55+
)
56+
})}
57+
{ifThenSteps.length > 0 && (
58+
<FlowStepGroup
59+
stepsBeforeGroup={forEachSteps}
60+
groupedSteps={ifThenSteps}
61+
/>
62+
)}
63+
</Flex>
64+
</Flex>
65+
)
66+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const forEachStyles = {
2+
addStepButton: {
3+
alignItems: 'center',
4+
bg: 'white',
5+
borderWidth: '1px',
6+
borderColor: 'base.divider.medium',
7+
borderRadius: 'lg',
8+
gap: 2,
9+
justifyContent: 'flex-start',
10+
overflow: 'hidden',
11+
px: 4,
12+
py: 2,
13+
variant: 'outline',
14+
w: 'full',
15+
_hover: {
16+
bg: 'interaction.muted.neutral.hover',
17+
cursor: 'pointer',
18+
},
19+
_active: {
20+
bg: 'interaction.muted.neutral.active',
21+
},
22+
},
23+
}

packages/frontend/src/components/FlowStepGroup/Content/IfThen/Branch.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ import { EditorContext } from '@/contexts/Editor'
3030
import { DELETE_STEP } from '@/graphql/mutations/delete-step'
3131
import { GET_FLOW } from '@/graphql/queries/get-flow'
3232

33+
import { allowAddStep } from '../utils'
34+
3335
import { HoverAddStepButton } from './HoverAddStepButton'
3436
import { branchStyles } from './styles'
35-
import { allowAddStep } from './utils'
3637

3738
interface BranchProps {
3839
branchSteps: IStep[]

packages/frontend/src/components/FlowStepGroup/Content/IfThen/styles.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export const hoverAddStepButtonStyles = {
5050
alignItems: 'center',
5151
justifyContent: 'center',
5252
direction: 'row' as FlexProps['direction'],
53-
m: 1,
54-
mb: 0,
53+
my: 1,
54+
mx: 0,
5555
transition: 'all 0.3s ease',
5656
},
5757
button: {

packages/frontend/src/components/FlowStepGroup/Content/IfThen/utils.ts renamed to packages/frontend/src/components/FlowStepGroup/Content/utils.ts

File renamed without changes.

packages/frontend/src/components/FlowStepGroup/index.tsx

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { IStep } from '@plumber/types'
22

33
import { useContext, useMemo } from 'react'
4+
import { BiTrash } from 'react-icons/bi'
45
import { Box, Flex, Icon, Text } from '@chakra-ui/react'
6+
import { IconButton } from '@opengovsg/design-system-react'
57

68
import { EditorContext } from '@/contexts/Editor'
79
import { getFlowStepHeaderWidth, getToolboxIcon } from '@/helpers/editor'
10+
import { TOOLBOX_ACTIONS } from '@/helpers/toolbox'
811

912
import { MIN_FLOW_STEP_WIDTH } from '../Editor/constants'
1013

1114
import Error from './Content/Error'
15+
import ForEach from './Content/ForEach'
1216
import IfThen from './Content/IfThen'
1317
import { flowStepGroupStyles } from './styles'
1418

@@ -25,10 +29,21 @@ export default function FlowStepGroup(props: FlowStepGroupProps) {
2529
let stepGroupType: string | null = null
2630
let stepGroupCaption: string | null = null
2731

28-
if (groupedSteps[0]?.[0]?.key === 'ifThen') {
29-
stepGroupType = 'ifThen'
32+
const groupKey = groupedSteps[0]?.[0]?.key
33+
if (!groupKey) {
34+
return { stepGroupType: null, stepGroupCaption: null }
35+
}
36+
37+
if (groupKey === TOOLBOX_ACTIONS.IfThen) {
38+
stepGroupType = TOOLBOX_ACTIONS.IfThen
3039
stepGroupCaption = 'If-then'
3140
}
41+
42+
if (groupKey === TOOLBOX_ACTIONS.ForEach) {
43+
stepGroupType = TOOLBOX_ACTIONS.ForEach
44+
stepGroupCaption = 'For each'
45+
}
46+
3247
return { stepGroupType, stepGroupCaption }
3348
}, [groupedSteps])
3449

@@ -54,6 +69,7 @@ export default function FlowStepGroup(props: FlowStepGroupProps) {
5469
w="full"
5570
borderLeftWidth={0}
5671
borderRightWidth={0}
72+
role="group"
5773
>
5874
<Flex {...flowStepGroupStyles.iconWrapper}>
5975
{/* App icon */}
@@ -70,13 +86,31 @@ export default function FlowStepGroup(props: FlowStepGroupProps) {
7086
</Text>
7187
</Flex>
7288
</Flex>
89+
{stepGroupType === TOOLBOX_ACTIONS.ForEach && (
90+
<Flex ml="auto" opacity={0} _groupHover={{ opacity: 1 }}>
91+
<IconButton
92+
boxSize={8}
93+
variant="clear"
94+
aria-label="Delete for each action"
95+
icon={<BiTrash />}
96+
colorScheme="secondary"
97+
/>
98+
</Flex>
99+
)}
73100
</Flex>
74101
</Box>
75-
{stepGroupType === 'ifThen' ? (
102+
{stepGroupType === TOOLBOX_ACTIONS.IfThen ? (
76103
<IfThen
77104
groupedSteps={groupedSteps}
78105
stepsBeforeGroup={stepsBeforeGroup}
79106
/>
107+
) : stepGroupType === TOOLBOX_ACTIONS.ForEach ? (
108+
<>
109+
<ForEach
110+
groupedSteps={groupedSteps}
111+
stepsBeforeGroup={stepsBeforeGroup}
112+
/>
113+
</>
80114
) : (
81115
<Error />
82116
)}

packages/frontend/src/contexts/Editor.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { GET_TEST_EXECUTION_STEPS } from '@/graphql/queries/get-test-execution-s
3030
import {
3131
TOOLBOX_ACTIONS,
3232
TOOLBOX_APP_KEY,
33+
useForEachInitializer,
3334
useIfThenInitializer,
3435
} from '@/helpers/toolbox'
3536
import { extractVariables, StepWithVariables } from '@/helpers/variables'
@@ -224,6 +225,7 @@ export const EditorProvider = ({
224225

225226
const [createStep] = useMutation(CREATE_STEP, { refetchQueries: [GET_FLOW] })
226227

228+
const [initializeForEach] = useForEachInitializer()
227229
const [initializeIfThen] = useIfThenInitializer()
228230

229231
// Add a step to the flow with the given appKey and eventKey
@@ -254,8 +256,8 @@ export const EditorProvider = ({
254256
const newStep = createdStep.data.createStep
255257
setCurrentStepId(newStep.id)
256258

257-
// account for the if-then edge case
258-
if (appKey === TOOLBOX_APP_KEY && eventKey === TOOLBOX_ACTIONS.IfThen) {
259+
// account for the for-each and if-then
260+
if (appKey === TOOLBOX_APP_KEY) {
259261
// Get the complete step data from the cache
260262
const { getFlow: updatedFlow } = client.readQuery({
261263
query: GET_FLOW,
@@ -271,15 +273,21 @@ export const EditorProvider = ({
271273
...completeStep,
272274
flowId: flowId,
273275
}
274-
return (await initializeIfThen(
275-
completeStepWithFlow,
276-
)) as unknown as IStep
276+
if (eventKey === TOOLBOX_ACTIONS.IfThen) {
277+
return (await initializeIfThen(
278+
completeStepWithFlow,
279+
)) as unknown as IStep
280+
}
281+
282+
if (eventKey === TOOLBOX_ACTIONS.ForEach) {
283+
await initializeForEach(completeStepWithFlow)
284+
}
277285
}
278286
}
279287

280288
return newStep as IStep
281289
},
282-
[createStep, flowId, initializeIfThen, setCurrentStepId],
290+
[createStep, flowId, initializeForEach, initializeIfThen],
283291
)
284292

285293
/**

0 commit comments

Comments
 (0)