Skip to content

Commit 9d032fd

Browse files
authored
Merge pull request #220 from UgnisSoftware/UGN-422
Back button navigation
2 parents 6a3168a + c62e20f commit 9d032fd

16 files changed

Lines changed: 163 additions & 62 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ Common date-time formats can be viewed [here](https://docs.sheetjs.com/docs/csf/
207207
autoMapSelectValues?: boolean
208208
// Headers matching accuracy: 1 for strict and up for more flexible matching. Default: 2
209209
autoMapDistance?: number
210+
// Enable navigation in stepper component and show back button. Default: false
211+
isNavigationEnabled?: boolean
210212
```
211213

212214
## Customisation

src/ReactSpreadsheetImport.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const defaultRSIProps: Partial<RsiProps<any>> = {
1414
autoMapSelectValues: false,
1515
allowInvalidSubmit: true,
1616
autoMapDistance: 2,
17+
isNavigationEnabled: false,
1718
translations: translations,
1819
uploadStepHook: async (value) => value,
1920
selectHeaderStepHook: async (headerValues, data) => ({ headerValues, data }),

src/components/ContinueButton.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
1-
import { Button, ModalFooter } from "@chakra-ui/react"
1+
import { Button, ModalFooter, useStyleConfig } from "@chakra-ui/react"
2+
import { themeOverrides } from "../theme"
23

34
type ContinueButtonProps = {
45
onContinue: (val: any) => void
6+
onBack?: () => void
57
title: string
8+
backTitle?: string
69
isLoading?: boolean
710
}
811

9-
export const ContinueButton = ({ onContinue, title, isLoading }: ContinueButtonProps) => (
10-
<ModalFooter>
11-
<Button size="lg" w="21rem" onClick={onContinue} isLoading={isLoading}>
12-
{title}
13-
</Button>
14-
</ModalFooter>
15-
)
12+
export const ContinueButton = ({ onContinue, onBack, title, backTitle, isLoading }: ContinueButtonProps) => {
13+
const styles = useStyleConfig("Modal") as (typeof themeOverrides)["components"]["Modal"]["baseStyle"]
14+
const nextButtonMobileWidth = onBack ? "8rem" : "100%"
15+
return (
16+
<ModalFooter>
17+
{onBack && (
18+
<Button size="md" sx={styles.backButton} onClick={onBack} isLoading={isLoading} variant="link">
19+
{backTitle}
20+
</Button>
21+
)}
22+
<Button
23+
size="lg"
24+
w={{ base: nextButtonMobileWidth, md: "21rem" }}
25+
sx={styles.continueButton}
26+
onClick={onContinue}
27+
isLoading={isLoading}
28+
>
29+
{title}
30+
</Button>
31+
</ModalFooter>
32+
)
33+
}

src/hooks/useRsiInitialStep.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/steps/MatchColumnsStep/MatchColumnsStep.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type MatchColumnsProps<T extends string> = {
1717
data: RawData[]
1818
headerValues: RawData
1919
onContinue: (data: any[], rawData: RawData[], columns: Columns<T>) => void
20+
onBack?: () => void
2021
}
2122

2223
export enum ColumnType {
@@ -62,7 +63,12 @@ export type Column<T extends string> =
6263

6364
export type Columns<T extends string> = Column<T>[]
6465

65-
export const MatchColumnsStep = <T extends string>({ data, headerValues, onContinue }: MatchColumnsProps<T>) => {
66+
export const MatchColumnsStep = <T extends string>({
67+
data,
68+
headerValues,
69+
onContinue,
70+
onBack,
71+
}: MatchColumnsProps<T>) => {
6672
const toast = useToast()
6773
const dataExample = data.slice(0, 2)
6874
const { fields, autoMapHeaders, autoMapSelectValues, autoMapDistance, translations } = useRsi<T>()
@@ -173,6 +179,7 @@ export const MatchColumnsStep = <T extends string>({ data, headerValues, onConti
173179
<ColumnGrid
174180
columns={columns}
175181
onContinue={handleOnContinue}
182+
onBack={onBack}
176183
isLoading={isLoading}
177184
userColumn={(column) => (
178185
<UserTableColumn

src/steps/MatchColumnsStep/components/ColumnGrid.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type ColumnGridProps<T extends string> = {
1111
userColumn: (column: Column<T>) => React.ReactNode
1212
templateColumn: (column: Column<T>) => React.ReactNode
1313
onContinue: (val: Record<string, string>[]) => void
14+
onBack?: () => void
1415
isLoading: boolean
1516
}
1617

@@ -21,6 +22,7 @@ export const ColumnGrid = <T extends string>({
2122
userColumn,
2223
templateColumn,
2324
onContinue,
25+
onBack,
2426
isLoading,
2527
}: ColumnGridProps<T>) => {
2628
const { translations } = useRsi()
@@ -66,7 +68,9 @@ export const ColumnGrid = <T extends string>({
6668
<ContinueButton
6769
isLoading={isLoading}
6870
onContinue={onContinue}
71+
onBack={onBack}
6972
title={translations.matchColumnsStep.nextButtonTitle}
73+
backTitle={translations.matchColumnsStep.backButtonTitle}
7074
/>
7175
</>
7276
)

src/steps/SelectHeaderStep/SelectHeaderStep.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import type { RawData } from "../../types"
99
type SelectHeaderProps = {
1010
data: RawData[]
1111
onContinue: (headerValues: RawData, data: RawData[]) => Promise<void>
12+
onBack?: () => void
1213
}
1314

14-
export const SelectHeaderStep = ({ data, onContinue }: SelectHeaderProps) => {
15+
export const SelectHeaderStep = ({ data, onContinue, onBack }: SelectHeaderProps) => {
1516
const styles = useStyleConfig(
1617
"SelectHeaderStep",
1718
) as (typeof themeOverrides)["components"]["SelectHeaderStep"]["baseStyle"]
@@ -36,7 +37,9 @@ export const SelectHeaderStep = ({ data, onContinue }: SelectHeaderProps) => {
3637
</ModalBody>
3738
<ContinueButton
3839
onContinue={handleContinue}
40+
onBack={onBack}
3941
title={translations.selectHeaderStep.nextButtonTitle}
42+
backTitle={translations.selectHeaderStep.backButtonTitle}
4043
isLoading={isLoading}
4144
/>
4245
</>

src/steps/SelectHeaderStep/tests/SelectHeaderStep.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ describe("Select header step tests", () => {
2828
const selectRowIndex = 2
2929

3030
const onContinue = jest.fn()
31+
const onBack = jest.fn()
3132
render(
3233
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
3334
<ModalWrapper isOpen={true} onClose={() => {}}>
34-
<SelectHeaderStep data={data} onContinue={onContinue} />
35+
<SelectHeaderStep data={data} onContinue={onContinue} onBack={onBack} />
3536
</ModalWrapper>
3637
</Providers>,
3738
)

src/steps/SelectSheetStep/SelectSheetStep.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import type { themeOverrides } from "../../theme"
77
type SelectSheetProps = {
88
sheetNames: string[]
99
onContinue: (sheetName: string) => Promise<void>
10+
onBack?: () => void
1011
}
1112

12-
export const SelectSheetStep = ({ sheetNames, onContinue }: SelectSheetProps) => {
13+
export const SelectSheetStep = ({ sheetNames, onContinue, onBack }: SelectSheetProps) => {
1314
const [isLoading, setIsLoading] = useState(false)
1415
const { translations } = useRsi()
1516
const [value, setValue] = useState(sheetNames[0])
@@ -42,7 +43,9 @@ export const SelectSheetStep = ({ sheetNames, onContinue }: SelectSheetProps) =>
4243
<ContinueButton
4344
isLoading={isLoading}
4445
onContinue={() => handleOnContinue(value)}
46+
onBack={onBack}
4547
title={translations.uploadStep.selectSheet.nextButtonTitle}
48+
backTitle={translations.uploadStep.selectSheet.backButtonTitle}
4649
/>
4750
</>
4851
)

src/steps/Steps.tsx

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,62 @@
1-
import { UploadFlow } from "./UploadFlow"
1+
import { StepState, StepType, UploadFlow } from "./UploadFlow"
22
import { ModalHeader } from "@chakra-ui/react"
33
import { useSteps, Step, Steps as Stepper } from "chakra-ui-steps"
44
import { CgCheck } from "react-icons/cg"
5+
56
import { useRsi } from "../hooks/useRsi"
6-
import { useRsiInitialStep } from "../hooks/useRsiInitialStep"
7+
import { useRef, useState } from "react"
8+
import { steps, stepTypeToStepIndex, stepIndexToStepType } from "../utils/steps"
79

810
const CheckIcon = ({ color }: { color: string }) => <CgCheck size="2.25rem" color={color} />
911

1012
export const Steps = () => {
11-
const { initialStepState, translations } = useRsi()
13+
const { initialStepState, translations, isNavigationEnabled } = useRsi()
1214

13-
const { steps, initialStep } = useRsiInitialStep(initialStepState?.type)
15+
const initialStep = stepTypeToStepIndex(initialStepState?.type)
1416

15-
const { nextStep, activeStep } = useSteps({
17+
const { nextStep, activeStep, setStep } = useSteps({
1618
initialStep,
1719
})
1820

21+
const [state, setState] = useState<StepState>(initialStepState || { type: StepType.upload })
22+
23+
const history = useRef<StepState[]>([])
24+
25+
const onClickStep = (stepIndex: number) => {
26+
const type = stepIndexToStepType(stepIndex)
27+
const historyIdx = history.current.findIndex((v) => v.type === type)
28+
if (historyIdx === -1) return
29+
const nextHistory = history.current.slice(0, historyIdx + 1)
30+
history.current = nextHistory
31+
setState(nextHistory[nextHistory.length - 1])
32+
setStep(stepIndex)
33+
}
34+
35+
const onBack = () => {
36+
onClickStep(Math.max(activeStep - 1, 0))
37+
}
38+
39+
const onNext = (v: StepState) => {
40+
history.current.push(state)
41+
setState(v)
42+
v.type !== StepType.selectSheet && nextStep()
43+
}
44+
1945
return (
2046
<>
2147
<ModalHeader display={["none", "none", "block"]}>
22-
<Stepper activeStep={activeStep} checkIcon={CheckIcon}>
48+
<Stepper
49+
activeStep={activeStep}
50+
checkIcon={CheckIcon}
51+
onClickStep={isNavigationEnabled ? onClickStep : undefined}
52+
responsive={false}
53+
>
2354
{steps.map((key) => (
2455
<Step label={translations[key].title} key={key} />
2556
))}
2657
</Stepper>
2758
</ModalHeader>
28-
<UploadFlow nextStep={nextStep} />
59+
<UploadFlow state={state} onNext={onNext} onBack={isNavigationEnabled ? onBack : undefined} />
2960
</>
3061
)
3162
}

0 commit comments

Comments
 (0)