Skip to content

Commit 5ed800c

Browse files
committed
[SIDE-DRAWER-31]: Mobile repsonsiveness (#1016)
## TL;DR This PR addresses issues with mobile responsiveness in the UI revamp: * Vertical stack for test result, save step, and check step buttons * Text overflowing buttons when in small screens: * Check step again * Save without checking * Sets a minimum width for step headers to prevent them from becoming too small in small screens ## How to test? - [ ] Test result, save, and check step buttons should be stacked on mobile - [ ] Test result, save, and check step buttons remain in a single row in non-mobile - [ ] Long text is truncated to single-line with ellipsis when in small screens - [ ] Empty trigger header - [ ] Check step / Check step again - [ ] Save / Save without checking ## Screenshots ### Stacked buttons in small screens <img width="387" alt="Screenshot 2025-05-28 at 5 45 27 PM" src="https://github.com/user-attachments/assets/b1e80182-3828-4984-985e-f4751fa51208" /> <img width="387" alt="Screenshot 2025-05-28 at 5 45 58 PM" src="https://github.com/user-attachments/assets/9a0aab79-e959-4ecb-ad40-737dceca7e82" /> <img width="387" alt="Screenshot 2025-05-28 at 5 46 17 PM" src="https://github.com/user-attachments/assets/2fc85f8a-9784-4832-bec9-76490fec50a1" /> https://github.com/user-attachments/assets/18322d4d-2cdc-4810-aa02-e7ccb7dfdc91 ### Minimum width for step headers to prevent them from becoming too small in small screens https://github.com/user-attachments/assets/d89ecf31-99d1-46e1-87b1-bd78c1b9d70f <img width="389" alt="Screenshot 2025-05-28 at 5 43 27 PM" src="https://github.com/user-attachments/assets/bbc189c3-b8df-49a8-b36c-4a39c008259f" />
1 parent b241e14 commit 5ed800c

File tree

7 files changed

+132
-84
lines changed

7 files changed

+132
-84
lines changed

packages/frontend/src/components/Editor/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ export const EDITOR_MARGIN_TOP_NUM = 61
22
export const EDITOR_MARGIN_TOP = `${EDITOR_MARGIN_TOP_NUM}px`
33
export const EDITOR_MAX_HEIGHT = `calc(100vh - ${EDITOR_MARGIN_TOP})`
44
export const EDITOR_RIGHT_DRAWER_WIDTH = '60%'
5+
6+
export const MIN_FLOW_STEP_WIDTH = '320px'

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function EmptyFlowStepHeader(
4646
boxSize={6}
4747
color="interaction.sub.default"
4848
/>
49-
<Text textStyle="subhead-1">
49+
<Text textStyle="subhead-1" noOfLines={1}>
5050
{isTrigger ? 'Choose how you want your workflow to start' : 'Add step'}
5151
</Text>
5252
</Flex>

packages/frontend/src/components/FlowStep/FlowStepWrapper.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { Flex } from '@chakra-ui/react'
22
import { useIsMobile } from '@opengovsg/design-system-react'
33

4+
import { MIN_FLOW_STEP_WIDTH } from '@/components/Editor/constants'
5+
46
interface FlowStepWrapperProps {
57
children: React.ReactNode
8+
isNested?: boolean
69
}
710

811
export default function FlowStepWrapper(props: FlowStepWrapperProps) {
9-
const { children } = props
12+
const { children, isNested } = props
1013
const isMobile = useIsMobile()
1114

1215
return (
@@ -15,6 +18,7 @@ export default function FlowStepWrapper(props: FlowStepWrapperProps) {
1518
display={isMobile ? 'block' : 'flex'}
1619
flexDir="column"
1720
w="100%"
21+
minW={isNested ? '100%' : MIN_FLOW_STEP_WIDTH}
1822
>
1923
{children}
2024
</Flex>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export default function FlowStep(
168168
}
169169

170170
return (
171-
<FlowStepWrapper>
171+
<FlowStepWrapper isNested={isNested}>
172172
{!app || !selectedActionOrTrigger ? (
173173
<EmptyFlowStepHeader
174174
isNested={isNested}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Box, Flex, Icon, Text } from '@chakra-ui/react'
66
import { EditorContext } from '@/contexts/Editor'
77
import { getFlowStepHeaderWidth, getToolboxIcon } from '@/helpers/editor'
88

9+
import { MIN_FLOW_STEP_WIDTH } from '../Editor/constants'
10+
911
import Error from './Content/Error'
1012
import IfThen from './Content/IfThen'
1113
import { flowStepGroupStyles } from './styles'
@@ -31,12 +33,17 @@ export default function FlowStepGroup(props: FlowStepGroupProps) {
3133
}, [groupedSteps])
3234

3335
return (
34-
<Flex w="100%" alignItems="center" justifyContent="center">
36+
<Flex
37+
w="100%"
38+
alignItems="center"
39+
justifyContent={isDrawerOpen ? 'flex-start' : 'center'}
40+
>
3541
{/* FIXME (kevinkim-ogp): above is a temporary wrapper to ensure the flow step group is centered when drawer is closed */}
3642
<Flex
3743
{...flowStepGroupStyles.container}
3844
display={isMobile ? 'block' : 'flex'}
3945
w={getFlowStepHeaderWidth(isDrawerOpen, isMobile)}
46+
minW={MIN_FLOW_STEP_WIDTH}
4047
>
4148
<Box {...flowStepGroupStyles.header} w="100%">
4249
<Flex

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

Lines changed: 114 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { useFormContext } from 'react-hook-form'
55
import { BiChevronDown, BiChevronUp } from 'react-icons/bi'
66
import {
77
Box,
8-
Flex,
8+
Grid,
9+
GridItem,
910
HStack,
10-
Stack,
1111
Text,
1212
Tooltip,
1313
VStack,
@@ -91,6 +91,7 @@ export default function FlowStepTestController(
9191
allApps,
9292
currentTestExecutionStep,
9393
readOnly,
94+
isMobile,
9495
isTestExecuting,
9596
testExecutionSteps,
9697
varInfoMap,
@@ -204,14 +205,20 @@ export default function FlowStepTestController(
204205
isReadOnly={readOnly}
205206
>
206207
<Button
207-
variant={infoBoxVariant === 'unstyled' ? undefined : 'clear'}
208+
variant={
209+
infoBoxVariant === 'unstyled'
210+
? undefined
211+
: isMobile
212+
? 'outline'
213+
: 'clear'
214+
}
208215
onClick={handleSaveAndTest}
209216
isLoading={isTestExecuting}
210217
colorScheme={infoBoxVariant === 'unstyled' ? 'primary' : 'black'}
211218
size="sm"
212219
isDisabled={!isValid || readOnly}
213220
>
214-
Check step again
221+
<Text noOfLines={1}>Check step again</Text>
215222
</Button>
216223
</CheckStepTooltip>
217224
),
@@ -239,22 +246,39 @@ export default function FlowStepTestController(
239246
/>
240247
</VStack>
241248
)}
242-
<Stack {...flowStepTestControllerStyles.container} ref={containerRef}>
243-
<VStack w="100%">
244-
{shouldShowTestResults ? (
245-
<VStack w="100%">
246-
<HStack w="100%">
247-
<Infobox
248-
{...flowStepTestControllerStyles.testedInfobox}
249-
variant={infoBoxVariant}
250-
borderBottomRadius={isTestResultOpen ? 0 : undefined}
251-
icon={infoBoxVariant === 'unstyled' ? <></> : null}
249+
<VStack
250+
{...flowStepTestControllerStyles.container}
251+
ref={containerRef}
252+
w="100%"
253+
>
254+
{shouldShowTestResults ? (
255+
<VStack w="100%">
256+
<HStack w="100%">
257+
<Infobox
258+
{...flowStepTestControllerStyles.testedInfobox}
259+
variant={infoBoxVariant}
260+
borderBottomRadius={isTestResultOpen ? 0 : undefined}
261+
icon={infoBoxVariant === 'unstyled' ? <></> : null}
262+
>
263+
<Grid
264+
justifyContent="space-between"
265+
alignItems="center"
266+
w="100%"
267+
templateAreas={{
268+
base: `
269+
"test-result"
270+
"save-button"
271+
"check-button"
272+
`,
273+
md: `"test-result save-button check-button"`,
274+
}}
275+
gridTemplateColumns={{
276+
base: '1fr',
277+
md: '1fr auto auto',
278+
}}
279+
rowGap={2}
252280
>
253-
<Flex
254-
justifyContent="space-between"
255-
alignItems="center"
256-
w="100%"
257-
>
281+
<GridItem area="test-result">
258282
{isIfThenStep && isLastTestExecutionCurrent ? (
259283
// NOTE: special handling for If-then
260284
// do not need button as there are no variables to display
@@ -275,84 +299,94 @@ export default function FlowStepTestController(
275299
}
276300
isDisabled={isTestExecuting}
277301
>
278-
<Text color="base.content.default">{infoBoxText}</Text>
302+
<Text
303+
color="base.content.default"
304+
noOfLines={1}
305+
textAlign="left"
306+
>
307+
{infoBoxText}
308+
</Text>
279309
{!isTestExecuting && (
280310
<Box ml={2} color="base.content.default">
281311
{getChevronIcon()}
282312
</Box>
283313
)}
284314
</Button>
285315
)}
286-
{shouldShowSaveButton ? (
287-
<Flex gap={2}>
316+
</GridItem>
317+
{shouldShowSaveButton ? (
318+
<>
319+
<GridItem area="save-button">
288320
<Button
289-
variant="clear"
321+
variant={isMobile ? 'outline' : 'clear'}
290322
size="sm"
291323
colorScheme="black"
292324
onClick={handleSave}
293325
isDisabled={!isLastTestExecutionCurrent && !isDirty}
326+
mr={2}
294327
>
295-
{!isLastTestExecutionCurrent && !isDirty
296-
? 'Saved'
297-
: 'Save without checking'}
328+
<Text noOfLines={1}>
329+
{!isLastTestExecutionCurrent && !isDirty
330+
? 'Saved'
331+
: 'Save without checking'}
332+
</Text>
298333
</Button>
334+
</GridItem>
335+
<GridItem area="check-button">
299336
{CheckAgainButton}
300-
</Flex>
301-
) : (
302-
CheckAgainButton
303-
)}
304-
</Flex>
305-
</Infobox>
306-
</HStack>
307-
<TestResult
308-
step={step}
309-
selectedActionOrTrigger={selectedActionOrTrigger}
310-
variables={testVariables}
311-
isMock={currentTestExecutionStep?.metadata?.isMock}
312-
isOpen={isTestResultOpen}
313-
isIfThenStep={isIfThenStep}
314-
/>
315-
</VStack>
316-
) : (
317-
<VStack w="100%" gap={2}>
318-
{lastErrorDetails && (
319-
<Box w="100%">
320-
<ErrorResult
321-
errorDetails={lastErrorDetails}
322-
isTestRun={true}
323-
/>
324-
</Box>
337+
</GridItem>
338+
</>
339+
) : (
340+
<GridItem area="check-button">{CheckAgainButton}</GridItem>
341+
)}
342+
</Grid>
343+
</Infobox>
344+
</HStack>
345+
<TestResult
346+
step={step}
347+
selectedActionOrTrigger={selectedActionOrTrigger}
348+
variables={testVariables}
349+
isMock={currentTestExecutionStep?.metadata?.isMock}
350+
isOpen={isTestResultOpen}
351+
isIfThenStep={isIfThenStep}
352+
/>
353+
</VStack>
354+
) : (
355+
<VStack w="100%" gap={2}>
356+
{lastErrorDetails && (
357+
<Box w="100%">
358+
<ErrorResult errorDetails={lastErrorDetails} isTestRun={true} />
359+
</Box>
360+
)}
361+
<HStack w="100%" justifyContent="flex-end">
362+
{!step.webhookUrl && (
363+
<Button
364+
isDisabled={readOnly || isSaving || !isDirty}
365+
isLoading={isSaving}
366+
variant="clear"
367+
onClick={handleSave}
368+
>
369+
{isDirty ? 'Save' : 'Saved'}
370+
</Button>
325371
)}
326-
<HStack w="100%" justifyContent="flex-end">
327-
{!step.webhookUrl && (
328-
<Button
329-
isDisabled={readOnly || isSaving || !isDirty}
330-
isLoading={isSaving}
331-
variant="clear"
332-
onClick={handleSave}
333-
>
334-
{isDirty ? 'Save' : 'Saved'}
335-
</Button>
336-
)}
337-
<CheckStepTooltip
338-
hasDeletedVars={hasDeletedVars}
339-
isDisabled={shouldAllowCheckStep}
340-
isReadOnly={readOnly}
372+
<CheckStepTooltip
373+
hasDeletedVars={hasDeletedVars}
374+
isDisabled={shouldAllowCheckStep}
375+
isReadOnly={readOnly}
376+
>
377+
<Button
378+
onClick={handleSaveAndTest}
379+
data-test="flow-substep-continue-button"
380+
isDisabled={!shouldAllowCheckStep}
381+
isLoading={isTestExecuting}
341382
>
342-
<Button
343-
onClick={handleSaveAndTest}
344-
data-test="flow-substep-continue-button"
345-
isDisabled={!shouldAllowCheckStep}
346-
isLoading={isTestExecuting}
347-
>
348-
Check step
349-
</Button>
350-
</CheckStepTooltip>
351-
</HStack>
352-
</VStack>
353-
)}
354-
</VStack>
355-
</Stack>
383+
Check step
384+
</Button>
385+
</CheckStepTooltip>
386+
</HStack>
387+
</VStack>
388+
)}
389+
</VStack>
356390
</>
357391
)
358392
}

packages/frontend/src/components/FlowStepTestController/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const flowStepTestControllerStyles = {
99
borderTop: '1px solid',
1010
borderTopColor: 'base.divider.medium',
1111
bg: 'white',
12+
backgroundClip: 'border-box',
1213
position: 'sticky' as PositionProps['position'],
1314
bottom: 0,
1415
zIndex: 1,

0 commit comments

Comments
 (0)