Skip to content

Commit 5048c4a

Browse files
authored
Merge pull request #197 from trevorloflin/master
Validation hooks fixes
2 parents 2cf0821 + 076eec1 commit 5048c4a

4 files changed

Lines changed: 89 additions & 72 deletions

File tree

src/steps/ValidationStep/ValidationStep.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useMemo, useState } from "react"
1+
import { useCallback, useMemo, useState, useEffect } from "react"
22
import { Box, Button, Heading, ModalBody, Switch, useStyleConfig } from "@chakra-ui/react"
33
import { ContinueButton } from "../../components/ContinueButton"
44
import { useRsi } from "../../hooks/useRsi"
@@ -22,20 +22,18 @@ export const ValidationStep = <T extends string>({ initialData, file }: Props<T>
2222
"ValidationStep",
2323
) as (typeof themeOverrides)["components"]["ValidationStep"]["baseStyle"]
2424

25-
const [data, setData] = useState<(Data<T> & Meta)[]>(
26-
useMemo(
27-
() => addErrorsAndRunHooks<T>(initialData, fields, rowHook, tableHook),
28-
// eslint-disable-next-line react-hooks/exhaustive-deps
29-
[],
30-
),
31-
)
25+
const [data, setData] = useState<(Data<T> & Meta)[]>(initialData as (Data<T> & Meta)[])
26+
useEffect(() => {
27+
addErrorsAndRunHooks<T>(initialData, fields, rowHook, tableHook).then((data) => setData(data))
28+
}, [initialData, fields, rowHook, tableHook])
29+
3230
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number | string>>(new Set())
3331
const [filterByErrors, setFilterByErrors] = useState(false)
3432
const [showSubmitAlert, setShowSubmitAlert] = useState(false)
3533

3634
const updateData = useCallback(
37-
(rows: typeof data) => {
38-
setData(addErrorsAndRunHooks<T>(rows, fields, rowHook, tableHook))
35+
async (rows: typeof data, indexes?: number[]) => {
36+
setData(await addErrorsAndRunHooks<T>(rows, fields, rowHook, tableHook, indexes))
3937
},
4038
[setData, rowHook, tableHook, fields],
4139
)
@@ -48,16 +46,17 @@ export const ValidationStep = <T extends string>({ initialData, file }: Props<T>
4846
}
4947
}
5048

51-
const updateRow = useCallback(
49+
const updateRows = useCallback(
5250
(rows: typeof data, changedData?: RowsChangeData<(typeof data)[number]>) => {
5351
const changes = changedData?.indexes.reduce((acc, index) => {
5452
// when data is filtered val !== actual index in data
5553
const realIndex = data.findIndex((value) => value.__index === rows[index].__index)
5654
acc[realIndex] = rows[index]
5755
return acc
5856
}, {} as Record<number, (typeof data)[number]>)
57+
const realIndexes = changes == null ? undefined : Object.keys(changes).map((index) => Number(index))
5958
const newData = Object.assign([], data, changes)
60-
updateData(newData)
59+
updateData(newData, realIndexes)
6160
},
6261
[data, updateData],
6362
)
@@ -136,7 +135,7 @@ export const ValidationStep = <T extends string>({ initialData, file }: Props<T>
136135
<Table
137136
rowKeyGetter={rowKeyGetter}
138137
rows={tableData}
139-
onRowsChange={updateRow}
138+
onRowsChange={updateRows}
140139
columns={columns}
141140
selectedRows={selectedRows}
142141
onSelectedRowsChange={setSelectedRows}
@@ -155,3 +154,5 @@ export const ValidationStep = <T extends string>({ initialData, file }: Props<T>
155154
</>
156155
)
157156
}
157+
158+

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

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "@testing-library/jest-dom"
2-
import { render, waitFor, screen } from "@testing-library/react"
2+
import { render, waitFor, screen, act } from "@testing-library/react"
33
import { ValidationStep } from "../ValidationStep"
44
import { defaultRSIProps, defaultTheme } from "../../../ReactSpreadsheetImport"
55
import { Providers } from "../../../components/Providers"
@@ -579,23 +579,25 @@ describe("Validation step tests", () => {
579579
},
580580
] as const
581581

582-
render(
583-
<Providers
584-
theme={defaultTheme}
585-
rsiValues={{
586-
...mockValues,
587-
fields,
588-
rowHook: (value) => ({
589-
name: value.name?.toString()?.split(/(\s+)/)[0],
590-
lastName: value.name?.toString()?.split(/(\s+)/)[2],
591-
}),
592-
}}
593-
>
594-
<ModalWrapper isOpen={true} onClose={() => {}}>
595-
<ValidationStep initialData={initialData} file={file} />
596-
</ModalWrapper>
597-
</Providers>,
598-
)
582+
await act(async () => {
583+
render(
584+
<Providers
585+
theme={defaultTheme}
586+
rsiValues={{
587+
...mockValues,
588+
fields,
589+
rowHook: (value) => ({
590+
name: value.name?.toString()?.split(/(\s+)/)[0],
591+
lastName: value.name?.toString()?.split(/(\s+)/)[2],
592+
}),
593+
}}
594+
>
595+
<ModalWrapper isOpen={true} onClose={() => {}}>
596+
<ValidationStep initialData={initialData} file={file} />
597+
</ModalWrapper>
598+
</Providers>,
599+
)
600+
})
599601

600602
const nameCell = screen.getByRole("gridcell", {
601603
name: NAME,
@@ -620,6 +622,7 @@ describe("Validation step tests", () => {
620622
})
621623
expect(newLastNameCell).toBeInTheDocument()
622624
})
625+
623626
test("Row hook raises error", async () => {
624627
const WRONG_NAME = "Johnny"
625628
const RIGHT_NAME = "Jonathan"
@@ -638,24 +641,26 @@ describe("Validation step tests", () => {
638641
},
639642
] as const
640643

641-
render(
642-
<Providers
643-
theme={defaultTheme}
644-
rsiValues={{
645-
...mockValues,
646-
fields,
647-
rowHook: (value, setError) => {
648-
if (value.name === WRONG_NAME) {
649-
setError(fields[0].key, { message: "Wrong name", level: "error" })
650-
}
651-
return value
652-
},
653-
}}
654-
>
655-
<ModalWrapper isOpen={true} onClose={() => {}}>
656-
<ValidationStep initialData={initialData} file={file} />
657-
</ModalWrapper>
658-
</Providers>,
644+
await act(async () =>
645+
render(
646+
<Providers
647+
theme={defaultTheme}
648+
rsiValues={{
649+
...mockValues,
650+
fields,
651+
rowHook: (value, setError) => {
652+
if (value.name === WRONG_NAME) {
653+
setError(fields[0].key, { message: "Wrong name", level: "error" })
654+
}
655+
return value
656+
},
657+
}}
658+
>
659+
<ModalWrapper isOpen={true} onClose={() => {}}>
660+
<ValidationStep initialData={initialData} file={file} />
661+
</ModalWrapper>
662+
</Providers>,
663+
),
659664
)
660665

661666
const switchFilter = getFilterSwitch()
@@ -702,23 +707,25 @@ describe("Validation step tests", () => {
702707
},
703708
] as const
704709

705-
render(
706-
<Providers
707-
theme={defaultTheme}
708-
rsiValues={{
709-
...mockValues,
710-
fields,
711-
tableHook: (data) =>
712-
data.map((value) => ({
713-
name: value.name + ADDITION,
714-
})),
715-
}}
716-
>
717-
<ModalWrapper isOpen={true} onClose={() => {}}>
718-
<ValidationStep initialData={initialData} file={file} />
719-
</ModalWrapper>
720-
</Providers>,
721-
)
710+
await act(async () => {
711+
render(
712+
<Providers
713+
theme={defaultTheme}
714+
rsiValues={{
715+
...mockValues,
716+
fields,
717+
tableHook: (data) =>
718+
data.map((value) => ({
719+
name: value.name + ADDITION,
720+
})),
721+
}}
722+
>
723+
<ModalWrapper isOpen={true} onClose={() => {}}>
724+
<ValidationStep initialData={initialData} file={file} />
725+
</ModalWrapper>
726+
</Providers>,
727+
)
728+
})
722729

723730
const nameCell = screen.getByRole("gridcell", {
724731
name: NAME + ADDITION,

src/steps/ValidationStep/utils/dataMutations.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import type { Data, Fields, Info, RowHook, TableHook } from "../../../types"
22
import type { Meta, Errors } from "../types"
33
import { v4 } from "uuid"
44

5-
export const addErrorsAndRunHooks = <T extends string>(
5+
export const addErrorsAndRunHooks = async <T extends string>(
66
data: (Data<T> & Partial<Meta>)[],
77
fields: Fields<T>,
88
rowHook?: RowHook<T>,
99
tableHook?: TableHook<T>,
10-
): (Data<T> & Meta)[] => {
10+
changedRowIndexes?: number[],
11+
): Promise<(Data<T> & Meta)[]> => {
1112
const errors: Errors = {}
1213

1314
const addHookError = (rowIndex: number, fieldKey: T, error: Info) => {
@@ -18,11 +19,19 @@ export const addErrorsAndRunHooks = <T extends string>(
1819
}
1920

2021
if (tableHook) {
21-
data = tableHook(data, addHookError)
22+
data = await tableHook(data, addHookError)
2223
}
2324

2425
if (rowHook) {
25-
data = data.map((value, index) => rowHook(value, (...props) => addHookError(index, ...props), data))
26+
if (changedRowIndexes != null) {
27+
for (const index of changedRowIndexes) {
28+
data[index] = await rowHook(data[index], (...props) => addHookError(index, ...props), data)
29+
}
30+
} else {
31+
data = await Promise.all(
32+
data.map(async (value, index) => rowHook(value, (...props) => addHookError(index, ...props), data)),
33+
)
34+
}
2635
}
2736

2837
fields.forEach((field) => {

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@ export type RowHook<T extends string> = (
121121
row: Data<T>,
122122
addError: (fieldKey: T, error: Info) => void,
123123
table: Data<T>[],
124-
) => Data<T>
124+
) => Data<T> | Promise<Data<T>>
125125
export type TableHook<T extends string> = (
126126
table: Data<T>[],
127127
addError: (rowIndex: number, fieldKey: T, error: Info) => void,
128-
) => Data<T>[]
128+
) => Data<T>[] | Promise<Data<T>[]>
129129

130130
export type ErrorLevel = "info" | "warning" | "error"
131131

0 commit comments

Comments
 (0)