Skip to content

Commit 3b92488

Browse files
authored
Merge pull request #233 from UgnisSoftware/UGN-416
Add async onSubmit
2 parents ef15f1c + 4b71537 commit 3b92488

File tree

5 files changed

+94
-7
lines changed

5 files changed

+94
-7
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ import { ReactSpreadsheetImport } from "react-spreadsheet-import";
5555
isOpen: Boolean
5656
// Called when flow is closed without reaching submit.
5757
onClose: () => void
58-
// Called after user completes the flow. Provides data array, where data keys matches your field keys.
59-
onSubmit: (data, file) => void
58+
// Called after user completes the flow. Provides data array, where data keys matches your field keys.
59+
onSubmit: (data, file) => void | Promise<any>
6060
```
6161

6262
### Fields

src/steps/ValidationStep/ValidationStep.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useMemo, useState } from "react"
2-
import { Box, Button, Heading, ModalBody, Switch, useStyleConfig } from "@chakra-ui/react"
2+
import { Box, Button, Heading, ModalBody, Switch, useStyleConfig, useToast } from "@chakra-ui/react"
33
import { ContinueButton } from "../../components/ContinueButton"
44
import { useRsi } from "../../hooks/useRsi"
55
import type { Meta } from "./types"
@@ -22,12 +22,14 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
2222
const styles = useStyleConfig(
2323
"ValidationStep",
2424
) as (typeof themeOverrides)["components"]["ValidationStep"]["baseStyle"]
25+
const toast = useToast()
2526

2627
const [data, setData] = useState<(Data<T> & Meta)[]>(initialData)
2728

2829
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number | string>>(new Set())
2930
const [filterByErrors, setFilterByErrors] = useState(false)
3031
const [showSubmitAlert, setShowSubmitAlert] = useState(false)
32+
const [isSubmitting, setSubmitting] = useState(false)
3133

3234
const updateData = useCallback(
3335
async (rows: typeof data, indexes?: number[]) => {
@@ -96,9 +98,25 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
9698
},
9799
{ validData: [] as Data<T>[], invalidData: [] as Data<T>[], all: data },
98100
)
99-
onSubmit(calculatedData, file)
100101
setShowSubmitAlert(false)
101-
onClose()
102+
setSubmitting(true)
103+
onSubmit(calculatedData, file)
104+
?.then(() => {
105+
onClose()
106+
})
107+
.catch((err: Error) => {
108+
toast({
109+
status: "error",
110+
variant: "left-accent",
111+
position: "bottom-left",
112+
title: `${translations.alerts.submitError.title}`,
113+
description: err?.message || `${translations.alerts.submitError.defaultMessage}`,
114+
isClosable: true,
115+
})
116+
})
117+
.finally(() => {
118+
setSubmitting(false)
119+
})
102120
}
103121
const onContinue = () => {
104122
const invalidData = data.find((value) => {
@@ -153,6 +171,7 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
153171
/>
154172
</ModalBody>
155173
<ContinueButton
174+
isLoading={isSubmitting}
156175
onContinue={onContinue}
157176
onBack={onBack}
158177
title={translations.validationStep.nextButtonTitle}

src/steps/ValidationStep/tests/ValidationStep.test.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,70 @@ describe("Validation step tests", () => {
4848
})
4949
})
5050

51+
test("Submit data with a successful async return", async () => {
52+
const onSuccess = jest.fn()
53+
const onSubmit = jest.fn(async (): Promise<void> => {
54+
onSuccess()
55+
return Promise.resolve()
56+
})
57+
const onClose = jest.fn()
58+
render(
59+
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, onSubmit, onClose }}>
60+
<ModalWrapper isOpen={true} onClose={() => {}}>
61+
<ValidationStep initialData={[]} file={file} />
62+
</ModalWrapper>
63+
</Providers>,
64+
)
65+
66+
const finishButton = screen.getByRole("button", {
67+
name: "Confirm",
68+
})
69+
70+
await userEvent.click(finishButton)
71+
72+
await waitFor(() => {
73+
expect(onSubmit).toBeCalledWith({ all: [], invalidData: [], validData: [] }, file)
74+
})
75+
await waitFor(() => {
76+
expect(onSuccess).toBeCalled()
77+
expect(onClose).toBeCalled()
78+
})
79+
})
80+
81+
test("Submit data with a unsuccessful async return", async () => {
82+
const ERROR_MESSAGE = "ERROR has occurred"
83+
const onReject = jest.fn()
84+
const onSubmit = jest.fn(async (): Promise<void> => {
85+
onReject()
86+
throw new Error(ERROR_MESSAGE)
87+
})
88+
const onClose = jest.fn()
89+
90+
render(
91+
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, onSubmit, onClose }}>
92+
<ModalWrapper isOpen={true} onClose={() => {}}>
93+
<ValidationStep initialData={[]} file={file} />
94+
</ModalWrapper>
95+
</Providers>,
96+
)
97+
98+
const finishButton = screen.getByRole("button", {
99+
name: "Confirm",
100+
})
101+
102+
await userEvent.click(finishButton)
103+
104+
await waitFor(() => {
105+
expect(onSubmit).toBeCalledWith({ all: [], invalidData: [], validData: [] }, file)
106+
})
107+
108+
const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
109+
110+
expect(onReject).toBeCalled()
111+
expect(errorToast?.[0]).toBeInTheDocument()
112+
expect(onClose).not.toBeCalled()
113+
})
114+
51115
test("Filters rows with required errors", async () => {
52116
const UNIQUE_NAME = "very unique name"
53117
const fields = [

src/translationsRSIProps.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ export const translations = {
6161
cancelButtonTitle: "Cancel",
6262
finishButtonTitle: "Submit",
6363
},
64+
submitError: {
65+
title: "Error",
66+
defaultMessage: "An error occurred while submitting data",
67+
},
6468
unmatchedRequiredFields: {
6569
headerTitle: "Not all columns matched",
6670
bodyText: "There are required columns that are not matched or ignored. Do you want to continue?",

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export type RsiProps<T extends string> = {
2121
rowHook?: RowHook<T>
2222
// Runs after column matching and on entry change
2323
tableHook?: TableHook<T>
24-
// Function called after user finishes the flow
25-
onSubmit: (data: Result<T>, file: File) => void
24+
// Function called after user finishes the flow. You can return a promise that will be awaited.
25+
onSubmit: (data: Result<T>, file: File) => void | Promise<any>
2626
// Allows submitting with errors. Default: true
2727
allowInvalidSubmit?: boolean
2828
// Enable navigation in stepper component and show back button. Default: false

0 commit comments

Comments
 (0)