Remove unneeded files, more shadcn conversions
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogContent,
|
||||
AlertDialogOverlay,
|
||||
Button,
|
||||
} from "@chakra-ui/react"
|
||||
import { useRef } from "react"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onConfirm: () => void
|
||||
}
|
||||
|
||||
export const ConfirmCloseAlert = ({ isOpen, onClose, onConfirm }: Props) => {
|
||||
const { translations } = useRsi()
|
||||
const cancelRef = useRef<HTMLButtonElement | null>(null)
|
||||
|
||||
return (
|
||||
<AlertDialog isOpen={isOpen} onClose={onClose} leastDestructiveRef={cancelRef} isCentered id="rsi">
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>{translations.alerts.confirmClose.headerTitle}</AlertDialogHeader>
|
||||
<AlertDialogBody>{translations.alerts.confirmClose.bodyText}</AlertDialogBody>
|
||||
<AlertDialogFooter>
|
||||
<Button ref={cancelRef} onClick={onClose} variant="secondary">
|
||||
{translations.alerts.confirmClose.cancelButtonTitle}
|
||||
</Button>
|
||||
<Button colorScheme="red" onClick={onConfirm} ml={3}>
|
||||
{translations.alerts.confirmClose.exitButtonTitle}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onConfirm: () => void
|
||||
}
|
||||
|
||||
export const SubmitDataAlert = ({ isOpen, onClose, onConfirm }: Props) => {
|
||||
const { allowInvalidSubmit, translations } = useRsi()
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<AlertDialog defaultOpen>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{translations.alerts.submitIncomplete.headerTitle}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{allowInvalidSubmit
|
||||
? translations.alerts.submitIncomplete.bodyText
|
||||
: translations.alerts.submitIncomplete.bodyTextSubmitForbidden}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={onClose}>
|
||||
{translations.alerts.submitIncomplete.cancelButtonTitle}
|
||||
</AlertDialogCancel>
|
||||
{allowInvalidSubmit && (
|
||||
<AlertDialogAction onClick={onConfirm}>
|
||||
{translations.alerts.submitIncomplete.finishButtonTitle}
|
||||
</AlertDialogAction>
|
||||
)}
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onConfirm: () => void
|
||||
fields: string[]
|
||||
}
|
||||
|
||||
export const UnmatchedFieldsAlert = ({ isOpen, onClose, onConfirm, fields }: Props) => {
|
||||
const { allowInvalidSubmit, translations } = useRsi()
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<AlertDialog defaultOpen>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{translations.alerts.unmatchedRequiredFields.headerTitle}
|
||||
</AlertDialogTitle>
|
||||
<div className="space-y-3">
|
||||
<AlertDialogDescription>
|
||||
{translations.alerts.unmatchedRequiredFields.bodyText}
|
||||
</AlertDialogDescription>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{translations.alerts.unmatchedRequiredFields.listTitle}{" "}
|
||||
<span className="font-bold">
|
||||
{fields.join(", ")}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={onClose}>
|
||||
{translations.alerts.unmatchedRequiredFields.cancelButtonTitle}
|
||||
</AlertDialogCancel>
|
||||
{allowInvalidSubmit && (
|
||||
<AlertDialogAction onClick={onConfirm}>
|
||||
{translations.alerts.unmatchedRequiredFields.continueButtonTitle}
|
||||
</AlertDialogAction>
|
||||
)}
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Button, ModalFooter, useStyleConfig } from "@chakra-ui/react"
|
||||
import { themeOverrides } from "../theme"
|
||||
|
||||
type ContinueButtonProps = {
|
||||
onContinue: (val: any) => void
|
||||
onBack?: () => void
|
||||
title: string
|
||||
backTitle?: string
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
export const ContinueButton = ({ onContinue, onBack, title, backTitle, isLoading }: ContinueButtonProps) => {
|
||||
const styles = useStyleConfig("Modal") as (typeof themeOverrides)["components"]["Modal"]["baseStyle"]
|
||||
const nextButtonMobileWidth = onBack ? "8rem" : "100%"
|
||||
return (
|
||||
<ModalFooter>
|
||||
{onBack && (
|
||||
<Button size="md" sx={styles.backButton} onClick={onBack} isLoading={isLoading} variant="link">
|
||||
{backTitle}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="lg"
|
||||
w={{ base: nextButtonMobileWidth, md: "21rem" }}
|
||||
sx={styles.continueButton}
|
||||
onClick={onContinue}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Box } from "@chakra-ui/react"
|
||||
|
||||
type FadingWrapperProps = {
|
||||
gridColumn: string
|
||||
gridRow: string
|
||||
}
|
||||
|
||||
export const FadingWrapper = ({ gridColumn, gridRow }: FadingWrapperProps) => (
|
||||
<>
|
||||
<Box
|
||||
gridColumn={gridColumn}
|
||||
gridRow={gridRow}
|
||||
borderRadius="1.2rem"
|
||||
border="1px solid"
|
||||
borderColor="border"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<Box
|
||||
gridColumn={gridColumn}
|
||||
gridRow={gridRow}
|
||||
pointerEvents="none"
|
||||
bgGradient="linear(to bottom, backgroundAlpha, background)"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Select } from "chakra-react-select"
|
||||
import type { SelectOption } from "../../types"
|
||||
import { customComponents } from "./MenuPortal"
|
||||
import { useStyleConfig } from "@chakra-ui/react"
|
||||
import type { Styles } from "../../steps/MatchColumnsStep/components/ColumnGrid"
|
||||
interface Props {
|
||||
onChange: (value: SelectOption | null) => void
|
||||
value?: SelectOption
|
||||
options: readonly SelectOption[]
|
||||
placeholder?: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export const MatchColumnSelect = ({ onChange, value, options, placeholder, name }: Props) => {
|
||||
const styles = useStyleConfig("MatchColumnsStep") as Styles
|
||||
return (
|
||||
<Select<SelectOption, false>
|
||||
value={value || null}
|
||||
colorScheme="gray"
|
||||
useBasicStyles
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
options={options}
|
||||
chakraStyles={styles.select}
|
||||
menuPosition="fixed"
|
||||
components={customComponents}
|
||||
aria-label={name}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import React, { useEffect, useLayoutEffect, useState } from "react"
|
||||
import ReactDOM from "react-dom"
|
||||
import { Box, useTheme } from "@chakra-ui/react"
|
||||
import { usePopper } from "@chakra-ui/popper"
|
||||
import { rootId } from "../Providers"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
|
||||
function createWrapperAndAppendToBody(wrapperId: string) {
|
||||
const wrapperElement = document.createElement("div")
|
||||
wrapperElement.setAttribute("id", wrapperId)
|
||||
document.body.appendChild(wrapperElement)
|
||||
return wrapperElement
|
||||
}
|
||||
|
||||
export const SELECT_DROPDOWN_ID = "react-select-dropdown-wrapper"
|
||||
|
||||
interface PortalProps {
|
||||
controlElement: HTMLDivElement | null
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const MenuPortal = (props: PortalProps) => {
|
||||
const theme = useTheme()
|
||||
const { rtl } = useRsi()
|
||||
const { popperRef, referenceRef } = usePopper({
|
||||
strategy: "fixed",
|
||||
matchWidth: true,
|
||||
})
|
||||
const [wrapperElement, setWrapperElement] = useState<HTMLElement | null>(null)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
let element = document.getElementById(SELECT_DROPDOWN_ID)
|
||||
let systemCreated = false
|
||||
if (!element) {
|
||||
systemCreated = true
|
||||
element = createWrapperAndAppendToBody(SELECT_DROPDOWN_ID)
|
||||
}
|
||||
setWrapperElement(element)
|
||||
|
||||
return () => {
|
||||
if (systemCreated && element?.parentNode) {
|
||||
element.parentNode.removeChild(element)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
referenceRef(props.controlElement)
|
||||
}, [props.controlElement, referenceRef])
|
||||
|
||||
// wrapperElement state will be null on very first render.
|
||||
if (wrapperElement === null) return null
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<Box
|
||||
dir={rtl ? "rtl" : "ltr"}
|
||||
ref={popperRef}
|
||||
zIndex={theme.zIndices.tooltip}
|
||||
sx={{
|
||||
"&[data-popper-reference-hidden]": {
|
||||
visibility: "hidden",
|
||||
pointerEvents: "none",
|
||||
},
|
||||
}}
|
||||
id={rootId}
|
||||
>
|
||||
{props.children}
|
||||
</Box>,
|
||||
wrapperElement,
|
||||
)
|
||||
}
|
||||
|
||||
export const customComponents = {
|
||||
MenuPortal,
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { rootId } from "../Providers"
|
||||
import { Select } from "chakra-react-select"
|
||||
import type { SelectOption } from "../../types"
|
||||
import { useStyleConfig } from "@chakra-ui/react"
|
||||
import type { themeOverrides } from "../../theme"
|
||||
|
||||
interface Props {
|
||||
onChange: (value: SelectOption | null) => void
|
||||
value?: SelectOption
|
||||
options: readonly SelectOption[]
|
||||
}
|
||||
|
||||
export const TableSelect = ({ onChange, value, options }: Props) => {
|
||||
const styles = useStyleConfig(
|
||||
"ValidationStep",
|
||||
) as (typeof themeOverrides)["components"]["ValidationStep"]["baseStyle"]
|
||||
return (
|
||||
<Select<SelectOption, false>
|
||||
autoFocus
|
||||
useBasicStyles
|
||||
size="sm"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder=" "
|
||||
closeMenuOnScroll
|
||||
menuPosition="fixed"
|
||||
menuIsOpen
|
||||
menuPortalTarget={document.getElementById(rootId)}
|
||||
options={options}
|
||||
chakraStyles={styles.select}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -4,9 +4,6 @@ import { ColumnType } from "../MatchColumnsStep"
|
||||
import { useRsi } from "../../../hooks/useRsi"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
||||
import { Box } from "@/components/ui/box"
|
||||
import { Text } from "@/components/ui/text"
|
||||
import { FadingWrapper } from "@/components/ui/fading-wrapper"
|
||||
|
||||
type ColumnGridProps<T extends string> = {
|
||||
columns: Columns<T>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Box, Text, useStyleConfig } from "@chakra-ui/react"
|
||||
import { MatchColumnSelect } from "../../../components/Selects/MatchColumnSelect"
|
||||
import { getFieldOptions } from "../utils/getFieldOptions"
|
||||
import { useRsi } from "../../../hooks/useRsi"
|
||||
import type { MatchedOptions, MatchedSelectColumn, MatchedSelectOptionsColumn } from "../MatchColumnsStep"
|
||||
import type { Styles } from "./ColumnGrid"
|
||||
|
||||
interface Props<T> {
|
||||
option: MatchedOptions<T> | Partial<MatchedOptions<T>>
|
||||
column: MatchedSelectColumn<T> | MatchedSelectOptionsColumn<T>
|
||||
onSubChange: (val: T, index: number, option: string) => void
|
||||
}
|
||||
|
||||
export const SubMatchingSelect = <T extends string>({ option, column, onSubChange }: Props<T>) => {
|
||||
const styles = useStyleConfig("MatchColumnsStep") as Styles
|
||||
const { translations, fields } = useRsi<T>()
|
||||
const options = getFieldOptions(fields, column.value)
|
||||
const value = options.find((opt) => opt.value == option.value)
|
||||
|
||||
return (
|
||||
<Box pl={2} pb="0.375rem">
|
||||
<Text sx={styles.selectColumn.selectLabel}>{option.entry}</Text>
|
||||
<MatchColumnSelect
|
||||
value={value}
|
||||
placeholder={translations.matchColumnsStep.subSelectPlaceholder}
|
||||
onChange={(value) => onSubChange(value?.value as T, column.index, option.entry!)}
|
||||
options={options}
|
||||
name={option.entry}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { defaultTheme } from "../../../ReactSpreadsheetImport"
|
||||
import { MatchColumnsStep } from "../MatchColumnsStep"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
|
||||
export default {
|
||||
title: "Match Columns Steps",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
}
|
||||
|
||||
const mockData = [
|
||||
["id", "first_name", "last_name", "email", "gender", "ip_address"],
|
||||
["2", "Geno", "Gencke", "ggencke0@tinypic.com", "Female", "17.204.180.40"],
|
||||
["3", "Bertram", "Twyford", "btwyford1@seattletimes.com", "Genderqueer", "188.98.2.13"],
|
||||
["4", "Tersina", "Isacke", "tisacke2@edublogs.org", "Non-binary", "237.69.180.31"],
|
||||
["5", "Yoko", "Guilliland", "yguilliland3@elegantthemes.com", "Male", "179.123.237.119"],
|
||||
["6", "Freida", "Fearns", "ffearns4@fotki.com", "Male", "184.48.15.1"],
|
||||
["7", "Mildrid", "Mount", "mmount5@last.fm", "Male", "26.97.160.103"],
|
||||
["8", "Jolene", "Darlington", "jdarlington6@jalbum.net", "Agender", "172.14.232.84"],
|
||||
["9", "Craig", "Dickie", "cdickie7@virginia.edu", "Male", "143.248.220.47"],
|
||||
["10", "Jere", "Shier", "jshier8@comcast.net", "Agender", "10.143.62.161"],
|
||||
]
|
||||
|
||||
export const Basic = () => (
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={mockData[0] as string[]} data={mockData.slice(1)} onContinue={() => {}} />
|
||||
</ModalWrapper>
|
||||
</Providers>
|
||||
)
|
||||
@@ -1,870 +0,0 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import { render, waitFor, screen } from "@testing-library/react"
|
||||
import { MatchColumnsStep } from "../MatchColumnsStep"
|
||||
import { defaultTheme, ReactSpreadsheetImport } from "../../../ReactSpreadsheetImport"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import type { Fields } from "../../../types"
|
||||
import selectEvent from "react-select-event"
|
||||
import { translations } from "../../../translationsRSIProps"
|
||||
import { SELECT_DROPDOWN_ID } from "../../../components/Selects/MenuPortal"
|
||||
import { StepType } from "../../UploadFlow"
|
||||
|
||||
const fields: Fields<any> = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "Stephanie",
|
||||
},
|
||||
{
|
||||
label: "Mobile Phone",
|
||||
key: "mobile",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "+12323423",
|
||||
},
|
||||
{
|
||||
label: "Is cool",
|
||||
key: "is_cool",
|
||||
fieldType: {
|
||||
type: "checkbox",
|
||||
},
|
||||
example: "No",
|
||||
},
|
||||
]
|
||||
|
||||
const CONTINUE_BUTTON = "Next"
|
||||
const MUTATED_ENTRY = "mutated entry"
|
||||
const ERROR_MESSAGE = "Something happened"
|
||||
|
||||
describe("Match Columns automatic matching", () => {
|
||||
test("AutoMatch column and click next", async () => {
|
||||
const header = ["namezz", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
// finds only names with automatic matching
|
||||
const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("AutoMatching disabled does not match any columns", async () => {
|
||||
const header = ["Name", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
// finds only names with automatic matching
|
||||
const result = [{}, {}, {}]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields, autoMapHeaders: false }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("AutoMatching exact values", async () => {
|
||||
const header = ["Name", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
// finds only names with automatic matching
|
||||
const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields, autoMapDistance: 1 }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("AutoMatches only one value", async () => {
|
||||
const header = ["first name", "name", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
// finds only names with automatic matching
|
||||
const result = [{ name: data[0][1] }, { name: data[1][1] }, { name: data[2][1] }]
|
||||
|
||||
const alternativeFields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
alternateMatches: ["first name"],
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "Stephanie",
|
||||
},
|
||||
] as const
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields: alternativeFields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("AutoMatches select values on mount", async () => {
|
||||
const header = ["first name", "count", "Email"]
|
||||
const OPTION_RESULT_ONE = "John"
|
||||
const OPTION_RESULT_ONE_VALUE = "1"
|
||||
const OPTION_RESULT_TWO = "Dane"
|
||||
const OPTION_RESULT_TWO_VALUE = "2"
|
||||
const OPTION_RESULT_THREE = "Kane"
|
||||
const data = [
|
||||
// match by option label
|
||||
[OPTION_RESULT_ONE, "123", "j@j.com"],
|
||||
// match by option value
|
||||
[OPTION_RESULT_TWO_VALUE, "333", "dane@bane.com"],
|
||||
// do not match
|
||||
[OPTION_RESULT_THREE, "534", "kane@linch.com"],
|
||||
]
|
||||
const options = [
|
||||
{ label: OPTION_RESULT_ONE, value: OPTION_RESULT_ONE_VALUE },
|
||||
{ label: OPTION_RESULT_TWO, value: OPTION_RESULT_TWO_VALUE },
|
||||
]
|
||||
// finds only names with automatic matching
|
||||
const result = [{ name: OPTION_RESULT_ONE_VALUE }, { name: OPTION_RESULT_TWO_VALUE }, { name: undefined }]
|
||||
|
||||
const alternativeFields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
alternateMatches: ["first name"],
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options,
|
||||
},
|
||||
example: "Stephanie",
|
||||
},
|
||||
] as const
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{ ...mockRsiValues, fields: alternativeFields, autoMapSelectValues: true }}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
expect(screen.getByText(/1 Unmatched/)).toBeInTheDocument()
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("Does not auto match select values when autoMapSelectValues:false", async () => {
|
||||
const header = ["first name", "count", "Email"]
|
||||
const OPTION_RESULT_ONE = "John"
|
||||
const OPTION_RESULT_ONE_VALUE = "1"
|
||||
const OPTION_RESULT_TWO = "Dane"
|
||||
const OPTION_RESULT_TWO_VALUE = "2"
|
||||
const OPTION_RESULT_THREE = "Kane"
|
||||
const data = [
|
||||
// match by option label
|
||||
[OPTION_RESULT_ONE, "123", "j@j.com"],
|
||||
// match by option value
|
||||
[OPTION_RESULT_TWO_VALUE, "333", "dane@bane.com"],
|
||||
// do not match
|
||||
[OPTION_RESULT_THREE, "534", "kane@linch.com"],
|
||||
]
|
||||
const options = [
|
||||
{ label: OPTION_RESULT_ONE, value: OPTION_RESULT_ONE_VALUE },
|
||||
{ label: OPTION_RESULT_TWO, value: OPTION_RESULT_TWO_VALUE },
|
||||
]
|
||||
const result = [{ name: undefined }, { name: undefined }, { name: undefined }]
|
||||
|
||||
const alternativeFields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
alternateMatches: ["first name"],
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options,
|
||||
},
|
||||
example: "Stephanie",
|
||||
},
|
||||
] as const
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{ ...mockRsiValues, fields: alternativeFields, autoMapSelectValues: false }}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
expect(screen.getByText(/3 Unmatched/)).toBeInTheDocument()
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("AutoMatches select values on select", async () => {
|
||||
const header = ["first name", "count", "Email"]
|
||||
const OPTION_RESULT_ONE = "John"
|
||||
const OPTION_RESULT_ONE_VALUE = "1"
|
||||
const OPTION_RESULT_TWO = "Dane"
|
||||
const OPTION_RESULT_TWO_VALUE = "2"
|
||||
const OPTION_RESULT_THREE = "Kane"
|
||||
const data = [
|
||||
// match by option label
|
||||
[OPTION_RESULT_ONE, "123", "j@j.com"],
|
||||
// match by option value
|
||||
[OPTION_RESULT_TWO_VALUE, "333", "dane@bane.com"],
|
||||
// do not match
|
||||
[OPTION_RESULT_THREE, "534", "kane@linch.com"],
|
||||
]
|
||||
const options = [
|
||||
{ label: OPTION_RESULT_ONE, value: OPTION_RESULT_ONE_VALUE },
|
||||
{ label: OPTION_RESULT_TWO, value: OPTION_RESULT_TWO_VALUE },
|
||||
]
|
||||
// finds only names with automatic matching
|
||||
const result = [{ name: OPTION_RESULT_ONE_VALUE }, { name: OPTION_RESULT_TWO_VALUE }, { name: undefined }]
|
||||
|
||||
const alternativeFields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options,
|
||||
},
|
||||
example: "Stephanie",
|
||||
},
|
||||
] as const
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{ ...mockRsiValues, fields: alternativeFields, autoMapSelectValues: true }}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
<div id={SELECT_DROPDOWN_ID} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(header[0]), alternativeFields[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
expect(screen.getByText(/1 Unmatched/)).toBeInTheDocument()
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("Boolean-like values are returned as Booleans", async () => {
|
||||
const header = ["namezz", "is_cool", "Email"]
|
||||
const data = [
|
||||
["John", "yes", "j@j.com"],
|
||||
["Dane", "TRUE", "dane@bane.com"],
|
||||
["Kane", "false", "kane@linch.com"],
|
||||
["Kaney", "no", "kane@linch.com"],
|
||||
["Kanye", "maybe", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const result = [
|
||||
{ name: data[0][0], is_cool: true },
|
||||
{ name: data[1][0], is_cool: true },
|
||||
{ name: data[2][0], is_cool: false },
|
||||
{ name: data[3][0], is_cool: false },
|
||||
{ name: data[4][0], is_cool: false },
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("Boolean-like values are returned as Booleans for 'booleanMatches' props", async () => {
|
||||
const BOOLEAN_MATCHES_VALUE = "definitely"
|
||||
const header = ["is_cool"]
|
||||
const data = [["true"], ["false"], [BOOLEAN_MATCHES_VALUE]]
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: "Is cool",
|
||||
key: "is_cool",
|
||||
fieldType: {
|
||||
type: "checkbox",
|
||||
booleanMatches: { [BOOLEAN_MATCHES_VALUE]: true },
|
||||
},
|
||||
example: "No",
|
||||
},
|
||||
] as const
|
||||
|
||||
const result = [{ is_cool: true }, { is_cool: false }, { is_cool: true }]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Match Columns general tests", () => {
|
||||
test("Displays all user header columns", async () => {
|
||||
const header = ["namezz", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
expect(screen.getByText(header[0])).toBeInTheDocument()
|
||||
expect(screen.getByText(header[1])).toBeInTheDocument()
|
||||
expect(screen.getByText(header[2])).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Displays two rows of example data", async () => {
|
||||
const header = ["namezz", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
// only displays two rows
|
||||
expect(screen.queryByText(data[0][0])).toBeInTheDocument()
|
||||
expect(screen.queryByText(data[0][1])).toBeInTheDocument()
|
||||
expect(screen.queryByText(data[0][2])).toBeInTheDocument()
|
||||
expect(screen.queryByText(data[1][0])).toBeInTheDocument()
|
||||
expect(screen.queryByText(data[1][1])).toBeInTheDocument()
|
||||
expect(screen.queryByText(data[1][2])).toBeInTheDocument()
|
||||
expect(screen.queryByText(data[2][0])).not.toBeInTheDocument()
|
||||
expect(screen.queryByText(data[2][1])).not.toBeInTheDocument()
|
||||
expect(screen.queryByText(data[2][2])).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Displays all fields in selects dropdown", async () => {
|
||||
const header = ["Something random", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const firstSelect = screen.getByLabelText(header[0])
|
||||
|
||||
await userEvent.click(firstSelect)
|
||||
|
||||
fields.forEach((field) => {
|
||||
expect(screen.queryByText(field.label)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
test("Manually matches first column", async () => {
|
||||
const header = ["Something random", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
<div id={SELECT_DROPDOWN_ID} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(header[0]), fields[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("Checkmark changes when field is matched", async () => {
|
||||
const header = ["Something random", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
<div id={SELECT_DROPDOWN_ID} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const checkmark = screen.getAllByTestId("column-checkmark")[0]
|
||||
// kinda dumb way to check if it has checkmark or not
|
||||
expect(checkmark).toBeEmptyDOMElement()
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(header[0]), fields[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
expect(checkmark).not.toBeEmptyDOMElement()
|
||||
})
|
||||
|
||||
test("Selecting select field adds more selects", async () => {
|
||||
const OPTION_ONE = "one"
|
||||
const OPTION_TWO = "two"
|
||||
const OPTION_RESULT_ONE = "uno"
|
||||
const OPTION_RESULT_TWO = "dos"
|
||||
const options = [
|
||||
{ label: "One", value: OPTION_RESULT_ONE },
|
||||
{ label: "Two", value: OPTION_RESULT_TWO },
|
||||
]
|
||||
const header = ["Something random"]
|
||||
const data = [[OPTION_ONE], [OPTION_TWO], [OPTION_ONE]]
|
||||
|
||||
const result = [
|
||||
{
|
||||
team: OPTION_RESULT_ONE,
|
||||
},
|
||||
{
|
||||
team: OPTION_RESULT_TWO,
|
||||
},
|
||||
{
|
||||
team: OPTION_RESULT_ONE,
|
||||
},
|
||||
]
|
||||
|
||||
const enumFields = [
|
||||
{
|
||||
label: "Team",
|
||||
key: "team",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: options,
|
||||
},
|
||||
},
|
||||
] as const
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields: enumFields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
<div id={SELECT_DROPDOWN_ID} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId("accordion-button")).not.toBeInTheDocument()
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(header[0]), enumFields[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
expect(screen.queryByTestId("accordion-button")).toBeInTheDocument()
|
||||
|
||||
await userEvent.click(screen.getByTestId("accordion-button"))
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(data[0][0]), options[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(data[1][0]), options[1].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(result)
|
||||
})
|
||||
|
||||
test("Can ignore columns", async () => {
|
||||
const header = ["Something random", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const ignoreButton = screen.getAllByLabelText("Ignore column")[0]
|
||||
|
||||
expect(screen.queryByText(translations.matchColumnsStep.ignoredColumnText)).not.toBeInTheDocument()
|
||||
|
||||
await userEvent.click(ignoreButton)
|
||||
|
||||
expect(screen.queryByText(translations.matchColumnsStep.ignoredColumnText)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Required unselected fields show warning alert on submit", async () => {
|
||||
const header = ["Something random", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const requiredFields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "Stephanie",
|
||||
validations: [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Hello",
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields: requiredFields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
expect(onContinue).not.toBeCalled()
|
||||
expect(screen.queryByText(translations.alerts.unmatchedRequiredFields.bodyText)).toBeInTheDocument()
|
||||
|
||||
const continueButton = screen.getByRole("button", {
|
||||
name: "Continue",
|
||||
})
|
||||
|
||||
await userEvent.click(continueButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
test("Selecting the same field twice shows toast", async () => {
|
||||
const header = ["Something random", "Phone", "Email"]
|
||||
const data = [
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
["Kane", "534", "kane@linch.com"],
|
||||
]
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockRsiValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<MatchColumnsStep headerValues={header} data={data} onContinue={onContinue} />
|
||||
<div id={SELECT_DROPDOWN_ID} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
await selectEvent.select(screen.getByLabelText(header[0]), fields[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
await selectEvent.select(screen.getByLabelText(header[1]), fields[0].label, {
|
||||
container: document.getElementById(SELECT_DROPDOWN_ID)!,
|
||||
})
|
||||
|
||||
const toasts = await screen.queryAllByText(translations.matchColumnsStep.duplicateColumnWarningDescription)
|
||||
|
||||
expect(toasts?.[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("matchColumnsStepHook should be called after columns are matched", async () => {
|
||||
const matchColumnsStepHook = jest.fn(async (values) => values)
|
||||
const mockValues = {
|
||||
...mockRsiValues,
|
||||
fields: mockRsiValues.fields.filter((field) => field.key === "name" || field.key === "age"),
|
||||
}
|
||||
render(
|
||||
<ReactSpreadsheetImport
|
||||
{...mockValues}
|
||||
matchColumnsStepHook={matchColumnsStepHook}
|
||||
initialStepState={{
|
||||
type: StepType.matchColumns,
|
||||
data: [
|
||||
["Josh", "2"],
|
||||
["Charlie", "3"],
|
||||
["Lena", "50"],
|
||||
],
|
||||
headerValues: ["name", "age"],
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
|
||||
const continueButton = screen.getByText(CONTINUE_BUTTON)
|
||||
await userEvent.click(continueButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(matchColumnsStepHook).toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
test("matchColumnsStepHook mutations to rawData should show up in ValidationStep", async () => {
|
||||
const matchColumnsStepHook = jest.fn(async ([firstEntry, ...values]) => {
|
||||
return [{ ...firstEntry, name: MUTATED_ENTRY }, ...values]
|
||||
})
|
||||
const mockValues = {
|
||||
...mockRsiValues,
|
||||
fields: mockRsiValues.fields.filter((field) => field.key === "name" || field.key === "age"),
|
||||
}
|
||||
render(
|
||||
<ReactSpreadsheetImport
|
||||
{...mockValues}
|
||||
matchColumnsStepHook={matchColumnsStepHook}
|
||||
initialStepState={{
|
||||
type: StepType.matchColumns,
|
||||
data: [
|
||||
["Josh", "2"],
|
||||
["Charlie", "3"],
|
||||
["Lena", "50"],
|
||||
],
|
||||
headerValues: ["name", "age"],
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
|
||||
const continueButton = screen.getByText(CONTINUE_BUTTON)
|
||||
await userEvent.click(continueButton)
|
||||
|
||||
const mutatedEntry = await screen.findByText(MUTATED_ENTRY)
|
||||
expect(mutatedEntry).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Should show error toast if error is thrown in matchColumnsStepHook", async () => {
|
||||
const matchColumnsStepHook = jest.fn(async () => {
|
||||
throw new Error(ERROR_MESSAGE)
|
||||
return undefined as any
|
||||
})
|
||||
|
||||
const mockValues = {
|
||||
...mockRsiValues,
|
||||
fields: mockRsiValues.fields.filter((field) => field.key === "name" || field.key === "age"),
|
||||
}
|
||||
|
||||
render(
|
||||
<ReactSpreadsheetImport
|
||||
{...mockValues}
|
||||
matchColumnsStepHook={matchColumnsStepHook}
|
||||
initialStepState={{
|
||||
type: StepType.matchColumns,
|
||||
data: [
|
||||
["Josh", "2"],
|
||||
["Charlie", "3"],
|
||||
["Lena", "50"],
|
||||
],
|
||||
headerValues: ["name", "age"],
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
|
||||
const continueButton = screen.getByText(CONTINUE_BUTTON)
|
||||
await userEvent.click(continueButton)
|
||||
|
||||
const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
|
||||
expect(errorToast?.[0]).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -1,19 +0,0 @@
|
||||
import { headerSelectionTableFields, mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { SelectHeaderStep } from "../SelectHeaderStep"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
import { defaultTheme } from "../../../ReactSpreadsheetImport"
|
||||
export default {
|
||||
title: "Select Header Step",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
}
|
||||
|
||||
export const Basic = () => (
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<SelectHeaderStep data={headerSelectionTableFields} onContinue={async () => {}} />
|
||||
</ModalWrapper>
|
||||
</Providers>
|
||||
)
|
||||
@@ -1,217 +0,0 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import { render, waitFor, screen, fireEvent } from "@testing-library/react"
|
||||
import { SelectHeaderStep } from "../SelectHeaderStep"
|
||||
import { defaultTheme, ReactSpreadsheetImport } from "../../../ReactSpreadsheetImport"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { readFileSync } from "fs"
|
||||
import { StepType } from "../../UploadFlow"
|
||||
|
||||
const MUTATED_HEADER = "mutated header"
|
||||
const CONTINUE_BUTTON = "Next"
|
||||
const ERROR_MESSAGE = "Something happened"
|
||||
const RAW_DATE = "2020-03-03"
|
||||
const FORMATTED_DATE = "2020/03/03"
|
||||
const TRAILING_CELL = "trailingcell"
|
||||
|
||||
describe("Select header step tests", () => {
|
||||
test("Select header row and click next", async () => {
|
||||
const data = [
|
||||
["Some random header"],
|
||||
["2030"],
|
||||
["Name", "Phone", "Email"],
|
||||
["John", "123", "j@j.com"],
|
||||
["Dane", "333", "dane@bane.com"],
|
||||
]
|
||||
const selectRowIndex = 2
|
||||
|
||||
const onContinue = jest.fn()
|
||||
const onBack = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<SelectHeaderStep data={data} onContinue={onContinue} onBack={onBack} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const radioButtons = screen.getAllByRole("radio")
|
||||
|
||||
await userEvent.click(radioButtons[selectRowIndex])
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(data[selectRowIndex])
|
||||
expect(onContinue.mock.calls[0][1]).toEqual(data.slice(selectRowIndex + 1))
|
||||
})
|
||||
|
||||
test("selectHeaderStepHook should be called after header is selected", async () => {
|
||||
const selectHeaderStepHook = jest.fn(async (headerValues, data) => {
|
||||
return { headerValues, data }
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} selectHeaderStepHook={selectHeaderStepHook} />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
const data = readFileSync(__dirname + "/../../../../static/Workbook2.xlsx")
|
||||
fireEvent.drop(uploader, {
|
||||
target: {
|
||||
files: [
|
||||
new File([data], "testFile.xlsx", {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
const continueButton = await screen.findByText(CONTINUE_BUTTON, undefined, { timeout: 10000 })
|
||||
fireEvent.click(continueButton)
|
||||
await waitFor(() => {
|
||||
expect(selectHeaderStepHook).toBeCalledWith(
|
||||
["name", "age", "date"],
|
||||
[
|
||||
["Josh", "2", "2020-03-03"],
|
||||
["Charlie", "3", "2010-04-04"],
|
||||
["Lena", "50", "1994-02-27"],
|
||||
],
|
||||
)
|
||||
})
|
||||
})
|
||||
test("selectHeaderStepHook should be able to modify raw data", async () => {
|
||||
const selectHeaderStepHook = jest.fn(async ([val, ...headerValues], data) => {
|
||||
return { headerValues: [MUTATED_HEADER, ...headerValues], data }
|
||||
})
|
||||
render(
|
||||
<ReactSpreadsheetImport
|
||||
{...mockRsiValues}
|
||||
selectHeaderStepHook={selectHeaderStepHook}
|
||||
initialStepState={{
|
||||
type: StepType.selectHeader,
|
||||
data: [
|
||||
["name", "age"],
|
||||
["Josh", "2"],
|
||||
["Charlie", "3"],
|
||||
["Lena", "50"],
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
const continueButton = screen.getByText(CONTINUE_BUTTON)
|
||||
fireEvent.click(continueButton)
|
||||
const mutatedHeader = await screen.findByText(MUTATED_HEADER)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mutatedHeader).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
test("Should show error toast if error is thrown in selectHeaderStepHook", async () => {
|
||||
const selectHeaderStepHook = jest.fn(async () => {
|
||||
throw new Error(ERROR_MESSAGE)
|
||||
return undefined as any
|
||||
})
|
||||
render(
|
||||
<ReactSpreadsheetImport
|
||||
{...mockRsiValues}
|
||||
selectHeaderStepHook={selectHeaderStepHook}
|
||||
initialStepState={{
|
||||
type: StepType.selectHeader,
|
||||
data: [
|
||||
["name", "age"],
|
||||
["Josh", "2"],
|
||||
["Charlie", "3"],
|
||||
["Lena", "50"],
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
const continueButton = screen.getByText(CONTINUE_BUTTON)
|
||||
await userEvent.click(continueButton)
|
||||
|
||||
const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
|
||||
expect(errorToast?.[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("dateFormat property should NOT be applied to dates read from csv files IF parseRaw=true", async () => {
|
||||
const file = new File([RAW_DATE], "test.csv", {
|
||||
type: "text/csv",
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} dateFormat="yyyy/mm/dd" parseRaw={true} />)
|
||||
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
fireEvent.drop(uploader, {
|
||||
target: { files: [file] },
|
||||
})
|
||||
|
||||
const el = await screen.findByText(RAW_DATE, undefined, { timeout: 5000 })
|
||||
expect(el).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("dateFormat property should be applied to dates read from csv files IF parseRaw=false", async () => {
|
||||
const file = new File([RAW_DATE], "test.csv", {
|
||||
type: "text/csv",
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} dateFormat="yyyy/mm/dd" parseRaw={false} />)
|
||||
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
fireEvent.drop(uploader, {
|
||||
target: { files: [file] },
|
||||
})
|
||||
|
||||
const el = await screen.findByText(FORMATTED_DATE, undefined, { timeout: 5000 })
|
||||
expect(el).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("dateFormat property should be applied to dates read from xlsx files", async () => {
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} dateFormat="yyyy/mm/dd" />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
const data = readFileSync(__dirname + "/../../../../static/Workbook2.xlsx")
|
||||
fireEvent.drop(uploader, {
|
||||
target: {
|
||||
files: [
|
||||
new File([data], "testFile.xlsx", {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
const el = await screen.findByText(FORMATTED_DATE, undefined, { timeout: 10000 })
|
||||
expect(el).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test.skip(
|
||||
"trailing (not under a header) cells should be rendered in SelectHeaderStep table, " +
|
||||
"but not in MatchColumnStep if a shorter row is selected as a header",
|
||||
async () => {
|
||||
const selectHeaderStepHook = jest.fn(async (headerValues, data) => {
|
||||
return { headerValues, data }
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} selectHeaderStepHook={selectHeaderStepHook} />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
const data = readFileSync(__dirname + "/../../../../static/TrailingCellsWorkbook.xlsx")
|
||||
fireEvent.drop(uploader, {
|
||||
target: {
|
||||
files: [
|
||||
new File([data], "testFile.xlsx", {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
const trailingCell = await screen.findByText(TRAILING_CELL, undefined, { timeout: 10000 })
|
||||
expect(trailingCell).toBeInTheDocument()
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
await userEvent.click(nextButton)
|
||||
const trailingCellNextPage = await screen.findByText(TRAILING_CELL, undefined, { timeout: 10000 })
|
||||
expect(trailingCellNextPage).not.toBeInTheDocument()
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { defaultTheme } from "../../../ReactSpreadsheetImport"
|
||||
import { SelectSheetStep } from "../SelectSheetStep"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
|
||||
export default {
|
||||
title: "Select Sheet Step",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
}
|
||||
|
||||
const sheetNames = ["Sheet1", "Sheet2", "Sheet3"]
|
||||
|
||||
export const Basic = () => (
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<SelectSheetStep sheetNames={sheetNames} onContinue={async () => {}} />
|
||||
</ModalWrapper>
|
||||
</Providers>
|
||||
)
|
||||
@@ -1,118 +0,0 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import { render, waitFor, screen, fireEvent, act } from "@testing-library/react"
|
||||
import { SelectSheetStep } from "../SelectSheetStep"
|
||||
import { defaultTheme, ReactSpreadsheetImport } from "../../../ReactSpreadsheetImport"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { readFileSync } from "fs"
|
||||
const SHEET_TITLE_1 = "Sheet1"
|
||||
const SHEET_TITLE_2 = "Sheet2"
|
||||
const SELECT_HEADER_TABLE_ENTRY_1 = "Charlie"
|
||||
const SELECT_HEADER_TABLE_ENTRY_2 = "Josh"
|
||||
const SELECT_HEADER_TABLE_ENTRY_3 = "50"
|
||||
const ERROR_MESSAGE = "Something happened"
|
||||
|
||||
test("Should render select sheet screen if multi-sheet excel file was uploaded", async () => {
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
const data = readFileSync(__dirname + "/../../../../static/Workbook1.xlsx")
|
||||
fireEvent.drop(uploader, {
|
||||
target: {
|
||||
files: [
|
||||
new File([data], "testFile.xlsx", {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const sheetTitle = await screen.findByText(SHEET_TITLE_1, undefined, { timeout: 5000 })
|
||||
const sheetTitle2 = screen.getByRole("radio", { name: SHEET_TITLE_2 })
|
||||
expect(sheetTitle).toBeInTheDocument()
|
||||
expect(sheetTitle2).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Should render select header screen with relevant data if single-sheet excel file was uploaded", async () => {
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
const data = readFileSync(__dirname + "/../../../../static/Workbook2.xlsx")
|
||||
fireEvent.drop(uploader, {
|
||||
target: {
|
||||
files: [
|
||||
new File([data], "testFile.xlsx", {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
const tableEntry1 = await screen.findByText(SELECT_HEADER_TABLE_ENTRY_1, undefined, { timeout: 5000 })
|
||||
const tableEntry2 = screen.getByRole("gridcell", { name: SELECT_HEADER_TABLE_ENTRY_2 })
|
||||
const tableEntry3 = screen.getByRole("gridcell", { name: SELECT_HEADER_TABLE_ENTRY_3 })
|
||||
|
||||
expect(tableEntry1).toBeInTheDocument()
|
||||
expect(tableEntry2).toBeInTheDocument()
|
||||
expect(tableEntry3).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Select sheet and click next", async () => {
|
||||
const sheetNames = ["Sheet1", "Sheet2"]
|
||||
const selectSheetIndex = 1
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<SelectSheetStep sheetNames={sheetNames} onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const firstRadio = screen.getByLabelText(sheetNames[selectSheetIndex])
|
||||
|
||||
await userEvent.click(firstRadio)
|
||||
|
||||
const nextButton = screen.getByRole("button", {
|
||||
name: "Next",
|
||||
})
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
})
|
||||
expect(onContinue.mock.calls[0][0]).toEqual(sheetNames[selectSheetIndex])
|
||||
})
|
||||
|
||||
test("Should show error toast if error is thrown in uploadStepHook", async () => {
|
||||
const uploadStepHook = jest.fn(async () => {
|
||||
throw new Error(ERROR_MESSAGE)
|
||||
return undefined as any
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} uploadStepHook={uploadStepHook} />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
const data = readFileSync(__dirname + "/../../../../static/Workbook1.xlsx")
|
||||
fireEvent.drop(uploader, {
|
||||
target: {
|
||||
files: [
|
||||
new File([data], "testFile.xlsx", {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const nextButton = await screen.findByRole(
|
||||
"button",
|
||||
{
|
||||
name: "Next",
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
)
|
||||
|
||||
await userEvent.click(nextButton)
|
||||
|
||||
const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
|
||||
expect(errorToast?.[0]).toBeInTheDocument()
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import type XLSX from "xlsx-ugnis"
|
||||
import type XLSX from "xlsx"
|
||||
import { useCallback, useState } from "react"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
import { DropZone } from "./components/DropZone"
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { UploadStep } from "../UploadStep"
|
||||
import { defaultTheme } from "../../../ReactSpreadsheetImport"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
|
||||
export default {
|
||||
title: "Upload Step",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
}
|
||||
|
||||
export const Basic = () => {
|
||||
return (
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<UploadStep onContinue={async () => {}} />
|
||||
</ModalWrapper>
|
||||
</Providers>
|
||||
)
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import { render, fireEvent, waitFor, screen } from "@testing-library/react"
|
||||
import { UploadStep } from "../UploadStep"
|
||||
import { defaultTheme, ReactSpreadsheetImport } from "../../../ReactSpreadsheetImport"
|
||||
import { mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
|
||||
const MUTATED_RAW_DATA = "Bye"
|
||||
const ERROR_MESSAGE = "Something happened while uploading"
|
||||
|
||||
test("Upload a file", async () => {
|
||||
const file = new File(["Hello, Hello, Hello, Hello"], "test.csv", { type: "text/csv" })
|
||||
|
||||
const onContinue = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<UploadStep onContinue={onContinue} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
fireEvent.drop(uploader, {
|
||||
target: { files: [file] },
|
||||
})
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onContinue).toBeCalled()
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
)
|
||||
})
|
||||
|
||||
test("Should call uploadStepHook on file upload", async () => {
|
||||
const file = new File(["Hello, Hello, Hello, Hello"], "test.csv", { type: "text/csv" })
|
||||
const uploadStepHook = jest.fn(async (values) => {
|
||||
return values
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} uploadStepHook={uploadStepHook} />)
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
fireEvent.drop(uploader, {
|
||||
target: { files: [file] },
|
||||
})
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(uploadStepHook).toBeCalled()
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
)
|
||||
})
|
||||
|
||||
test("uploadStepHook should be able to mutate raw upload data", async () => {
|
||||
const file = new File(["Hello, Hello, Hello, Hello"], "test.csv", { type: "text/csv" })
|
||||
const uploadStepHook = jest.fn(async ([[, ...values]]) => {
|
||||
return [[MUTATED_RAW_DATA, ...values]]
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} uploadStepHook={uploadStepHook} />)
|
||||
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
fireEvent.drop(uploader, {
|
||||
target: { files: [file] },
|
||||
})
|
||||
|
||||
const el = await screen.findByText(MUTATED_RAW_DATA, undefined, { timeout: 5000 })
|
||||
expect(el).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Should show error toast if error is thrown in uploadStepHook", async () => {
|
||||
const file = new File(["Hello, Hello, Hello, Hello"], "test.csv", { type: "text/csv" })
|
||||
const uploadStepHook = jest.fn(async () => {
|
||||
throw new Error(ERROR_MESSAGE)
|
||||
return undefined as any
|
||||
})
|
||||
render(<ReactSpreadsheetImport {...mockRsiValues} uploadStepHook={uploadStepHook} />)
|
||||
|
||||
const uploader = screen.getByTestId("rsi-dropzone")
|
||||
fireEvent.drop(uploader, {
|
||||
target: { files: [file] },
|
||||
})
|
||||
|
||||
const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
|
||||
expect(errorToast?.[0]).toBeInTheDocument()
|
||||
})
|
||||
@@ -1,10 +1,17 @@
|
||||
import DataGrid, { Column, useRowSelection } from "react-data-grid"
|
||||
import { Box, Checkbox, Input, Switch, Tooltip } from "@chakra-ui/react"
|
||||
import type { Data, Fields } from "../../../types"
|
||||
import type { Column as RDGColumn, RenderEditCellProps, FormatterProps } from "react-data-grid"
|
||||
import { useRowSelection } from "react-data-grid"
|
||||
import { Checkbox, Input, Switch } from "@chakra-ui/react"
|
||||
import type { Data, Fields, Field, SelectOption } from "../../../types"
|
||||
import type { ChangeEvent } from "react"
|
||||
import type { Meta } from "../types"
|
||||
import { CgInfo } from "react-icons/cg"
|
||||
import { TableSelect } from "../../../components/Selects/TableSelect"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
|
||||
const SELECT_COLUMN_KEY = "select-row"
|
||||
|
||||
@@ -13,7 +20,9 @@ function autoFocusAndSelect(input: HTMLInputElement | null) {
|
||||
input?.select()
|
||||
}
|
||||
|
||||
export const generateColumns = <T extends string>(fields: Fields<T>): Column<Data<T> & Meta>[] => [
|
||||
type RowType<T extends string> = Data<T> & Meta
|
||||
|
||||
export const generateColumns = <T extends string>(fields: Fields<T>): RDGColumn<RowType<T>>[] => [
|
||||
{
|
||||
key: SELECT_COLUMN_KEY,
|
||||
name: "",
|
||||
@@ -24,7 +33,7 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
||||
sortable: false,
|
||||
frozen: true,
|
||||
cellClass: "rdg-checkbox",
|
||||
formatter: (props) => {
|
||||
formatter: (props: FormatterProps<RowType<T>>) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [isRowSelected, onRowSelectionChange] = useRowSelection()
|
||||
return (
|
||||
@@ -44,44 +53,58 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
||||
},
|
||||
},
|
||||
...fields.map(
|
||||
(column): Column<Data<T> & Meta> => ({
|
||||
(column: Field<T>): RDGColumn<RowType<T>> => ({
|
||||
key: column.key,
|
||||
name: column.label,
|
||||
minWidth: 150,
|
||||
resizable: true,
|
||||
headerRenderer: () => (
|
||||
<Box display="flex" gap={1} alignItems="center" position="relative">
|
||||
<Box flex={1} overflow="hidden" textOverflow="ellipsis">
|
||||
<div className="flex gap-1 items-center relative">
|
||||
<div className="flex-1 overflow-hidden text-ellipsis">
|
||||
{column.label}
|
||||
</Box>
|
||||
</div>
|
||||
{column.description && (
|
||||
<Tooltip placement="top" hasArrow label={column.description}>
|
||||
<Box flex={"0 0 auto"}>
|
||||
<CgInfo size="16px" />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<div className="flex-none">
|
||||
<CgInfo className="h-4 w-4" />
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
),
|
||||
editable: column.fieldType.type !== "checkbox",
|
||||
editor: ({ row, onRowChange, onClose }) => {
|
||||
editor: ({ row, onRowChange, onClose }: RenderEditCellProps<RowType<T>>) => {
|
||||
let component
|
||||
|
||||
switch (column.fieldType.type) {
|
||||
case "select":
|
||||
component = (
|
||||
<TableSelect
|
||||
value={column.fieldType.options.find((option) => option.value === (row[column.key] as string))}
|
||||
onChange={(value) => {
|
||||
onRowChange({ ...row, [column.key]: value?.value }, true)
|
||||
<Select
|
||||
defaultOpen
|
||||
value={row[column.key] as string}
|
||||
onValueChange={(value) => {
|
||||
onRowChange({ ...row, [column.key]: value }, true)
|
||||
}}
|
||||
options={column.fieldType.options}
|
||||
/>
|
||||
>
|
||||
<SelectTrigger className="w-full border-0 focus:ring-0">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent
|
||||
position="popper"
|
||||
className="z-[1000]"
|
||||
align="start"
|
||||
side="bottom"
|
||||
>
|
||||
{column.fieldType.options.map((option: SelectOption) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
break
|
||||
default:
|
||||
component = (
|
||||
<Box paddingInlineStart="0.5rem">
|
||||
<div className="pl-2">
|
||||
<Input
|
||||
ref={autoFocusAndSelect}
|
||||
variant="unstyled"
|
||||
@@ -93,7 +116,7 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
||||
}}
|
||||
onBlur={() => onClose(true)}
|
||||
/>
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -102,16 +125,14 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
||||
editorOptions: {
|
||||
editOnClick: true,
|
||||
},
|
||||
formatter: ({ row, onRowChange }) => {
|
||||
formatter: ({ row, onRowChange }: FormatterProps<RowType<T>>) => {
|
||||
let component
|
||||
|
||||
switch (column.fieldType.type) {
|
||||
case "checkbox":
|
||||
component = (
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
height="100%"
|
||||
<div
|
||||
className="flex items-center h-full"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
@@ -122,29 +143,32 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
||||
onRowChange({ ...row, [column.key]: !row[column.key as T] })
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
break
|
||||
case "select":
|
||||
component = (
|
||||
<Box minWidth="100%" minHeight="100%" overflow="hidden" textOverflow="ellipsis">
|
||||
{column.fieldType.options.find((option) => option.value === row[column.key as T])?.label || null}
|
||||
</Box>
|
||||
<div className="min-w-full min-h-full overflow-hidden text-ellipsis">
|
||||
{column.fieldType.options.find((option: SelectOption) => option.value === row[column.key as T])?.label || null}
|
||||
</div>
|
||||
)
|
||||
break
|
||||
default:
|
||||
component = (
|
||||
<Box minWidth="100%" minHeight="100%" overflow="hidden" textOverflow="ellipsis">
|
||||
<div className="min-w-full min-h-full overflow-hidden text-ellipsis">
|
||||
{row[column.key as T]}
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (row.__errors?.[column.key]) {
|
||||
return (
|
||||
<Tooltip placement="top" hasArrow label={row.__errors?.[column.key]?.message}>
|
||||
<div className="relative group">
|
||||
{component}
|
||||
</Tooltip>
|
||||
<div className="absolute left-0 -top-8 z-50 hidden group-hover:block bg-popover text-popover-foreground text-sm p-2 rounded shadow">
|
||||
{row.__errors?.[column.key]?.message}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { editableTableInitialData, mockRsiValues } from "../../../stories/mockRsiValues"
|
||||
import { ValidationStep } from "../ValidationStep"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { defaultTheme } from "../../../ReactSpreadsheetImport"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
import { addErrorsAndRunHooks } from "../utils/dataMutations"
|
||||
|
||||
export default {
|
||||
title: "Validation Step",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
}
|
||||
|
||||
const file = new File([""], "file.csv")
|
||||
const data = await addErrorsAndRunHooks(editableTableInitialData, mockRsiValues.fields)
|
||||
|
||||
export const Basic = () => {
|
||||
return (
|
||||
<Providers theme={defaultTheme} rsiValues={mockRsiValues}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep initialData={data} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>
|
||||
)
|
||||
}
|
||||
@@ -1,956 +0,0 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import { render, waitFor, screen, act } from "@testing-library/react"
|
||||
import { ValidationStep } from "../ValidationStep"
|
||||
import { defaultRSIProps, defaultTheme } from "../../../ReactSpreadsheetImport"
|
||||
import { Providers } from "../../../components/Providers"
|
||||
import { ModalWrapper } from "../../../components/ModalWrapper"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { translations } from "../../../translationsRSIProps"
|
||||
import { addErrorsAndRunHooks } from "../utils/dataMutations"
|
||||
import { Fields, RowHook, TableHook } from "../../../types"
|
||||
|
||||
type fieldKeys<T extends Fields<string>> = T[number]["key"]
|
||||
|
||||
const mockValues = {
|
||||
...defaultRSIProps,
|
||||
fields: [],
|
||||
onSubmit: () => {},
|
||||
isOpen: true,
|
||||
onClose: () => {},
|
||||
} as const
|
||||
|
||||
const getFilterSwitch = () =>
|
||||
screen.getByRole("checkbox", {
|
||||
name: translations.validationStep.filterSwitchTitle,
|
||||
})
|
||||
|
||||
const file = new File([""], "file.csv")
|
||||
|
||||
describe("Validation step tests", () => {
|
||||
test("Submit data", async () => {
|
||||
const onSubmit = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, onSubmit: onSubmit }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep initialData={[]} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const finishButton = screen.getByRole("button", {
|
||||
name: "Confirm",
|
||||
})
|
||||
|
||||
await userEvent.click(finishButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toBeCalledWith({ all: [], invalidData: [], validData: [] }, file)
|
||||
})
|
||||
})
|
||||
|
||||
test("Submit data without returning promise", async () => {
|
||||
const onSuccess = jest.fn()
|
||||
const onSubmit = jest.fn(() => {
|
||||
onSuccess()
|
||||
})
|
||||
const onClose = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, onSubmit, onClose }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep initialData={[]} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const finishButton = screen.getByRole("button", {
|
||||
name: "Confirm",
|
||||
})
|
||||
|
||||
await userEvent.click(finishButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toBeCalledWith({ all: [], invalidData: [], validData: [] }, file)
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(onSuccess).toBeCalled()
|
||||
expect(onClose).toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
test("Submit data with a successful async return", async () => {
|
||||
const onSuccess = jest.fn()
|
||||
const onSubmit = jest.fn(async (): Promise<void> => {
|
||||
onSuccess()
|
||||
return Promise.resolve()
|
||||
})
|
||||
const onClose = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, onSubmit, onClose }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep initialData={[]} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const finishButton = screen.getByRole("button", {
|
||||
name: "Confirm",
|
||||
})
|
||||
|
||||
await userEvent.click(finishButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toBeCalledWith({ all: [], invalidData: [], validData: [] }, file)
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(onSuccess).toBeCalled()
|
||||
expect(onClose).toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
test("Submit data with a unsuccessful async return", async () => {
|
||||
const ERROR_MESSAGE = "ERROR has occurred"
|
||||
const onReject = jest.fn()
|
||||
const onSubmit = jest.fn(async (): Promise<void> => {
|
||||
onReject()
|
||||
throw new Error(ERROR_MESSAGE)
|
||||
})
|
||||
const onClose = jest.fn()
|
||||
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, onSubmit, onClose }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep initialData={[]} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const finishButton = screen.getByRole("button", {
|
||||
name: "Confirm",
|
||||
})
|
||||
|
||||
await userEvent.click(finishButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toBeCalledWith({ all: [], invalidData: [], validData: [] }, file)
|
||||
})
|
||||
|
||||
const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
|
||||
|
||||
expect(onReject).toBeCalled()
|
||||
expect(errorToast?.[0]).toBeInTheDocument()
|
||||
expect(onClose).not.toBeCalled()
|
||||
})
|
||||
|
||||
test("Filters rows with required errors", async () => {
|
||||
const UNIQUE_NAME = "very unique name"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
validations: [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Name is required",
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: UNIQUE_NAME,
|
||||
},
|
||||
{
|
||||
name: undefined,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const allRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsWithHeader).toHaveLength(3)
|
||||
|
||||
const validRow = screen.getByText(UNIQUE_NAME)
|
||||
expect(validRow).toBeInTheDocument()
|
||||
|
||||
const switchFilter = getFilterSwitch()
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
const filteredRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsWithHeader).toHaveLength(2)
|
||||
})
|
||||
|
||||
test("Filters rows with errors, fixes row, removes filter", async () => {
|
||||
const UNIQUE_NAME = "very unique name"
|
||||
const SECOND_UNIQUE_NAME = "another unique name"
|
||||
const FINAL_NAME = "just name"
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
validations: [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Name is required",
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: UNIQUE_NAME,
|
||||
},
|
||||
{
|
||||
name: undefined,
|
||||
},
|
||||
{
|
||||
name: SECOND_UNIQUE_NAME,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
const onSubmit = jest.fn()
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, fields, onSubmit }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const allRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsWithHeader).toHaveLength(4)
|
||||
|
||||
const validRow = screen.getByText(UNIQUE_NAME)
|
||||
expect(validRow).toBeInTheDocument()
|
||||
|
||||
const switchFilter = getFilterSwitch()
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
const filteredRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsWithHeader).toHaveLength(2)
|
||||
|
||||
// don't really know another way to select an empty cell
|
||||
const emptyCell = screen.getAllByRole("gridcell", { name: undefined })[1]
|
||||
await userEvent.click(emptyCell)
|
||||
|
||||
await userEvent.keyboard(FINAL_NAME + "{enter}")
|
||||
|
||||
const filteredRowsNoErrorsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsNoErrorsWithHeader).toHaveLength(1)
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
const allRowsFixedWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsFixedWithHeader).toHaveLength(4)
|
||||
|
||||
const finishButton = screen.getByRole("button", {
|
||||
name: "Confirm",
|
||||
})
|
||||
|
||||
await userEvent.click(finishButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
test("Filters rows with unique errors", async () => {
|
||||
const NON_UNIQUE_NAME = "very unique name"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
validations: [
|
||||
{
|
||||
rule: "unique",
|
||||
errorMessage: "Name must be unique",
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: NON_UNIQUE_NAME,
|
||||
},
|
||||
{
|
||||
name: NON_UNIQUE_NAME,
|
||||
},
|
||||
{
|
||||
name: "I am fine",
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const allRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsWithHeader).toHaveLength(4)
|
||||
|
||||
const switchFilter = getFilterSwitch()
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
const filteredRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsWithHeader).toHaveLength(3)
|
||||
})
|
||||
test("Filters rows with regex errors", async () => {
|
||||
const NOT_A_NUMBER = "not a number"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
validations: [
|
||||
{
|
||||
rule: "regex",
|
||||
errorMessage: "Name must be unique",
|
||||
value: "^[0-9]*$",
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: NOT_A_NUMBER,
|
||||
},
|
||||
{
|
||||
name: "1234",
|
||||
},
|
||||
{
|
||||
name: "9999999",
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const allRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsWithHeader).toHaveLength(4)
|
||||
|
||||
const switchFilter = getFilterSwitch()
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
const filteredRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsWithHeader).toHaveLength(2)
|
||||
})
|
||||
|
||||
test("Deletes selected rows", async () => {
|
||||
const FIRST_DELETE = "first"
|
||||
const SECOND_DELETE = "second"
|
||||
const THIRD = "third"
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: FIRST_DELETE,
|
||||
},
|
||||
{
|
||||
name: SECOND_DELETE,
|
||||
},
|
||||
{
|
||||
name: THIRD,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const allRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsWithHeader).toHaveLength(4)
|
||||
|
||||
const switchFilters = screen.getAllByRole("checkbox", {
|
||||
name: "Select",
|
||||
})
|
||||
|
||||
await userEvent.click(switchFilters[0])
|
||||
await userEvent.click(switchFilters[1])
|
||||
|
||||
const discardButton = screen.getByRole("button", {
|
||||
name: "Discard selected rows",
|
||||
})
|
||||
|
||||
await userEvent.click(discardButton)
|
||||
|
||||
const filteredRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsWithHeader).toHaveLength(2)
|
||||
|
||||
const validRow = screen.getByText(THIRD)
|
||||
expect(validRow).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Deletes selected rows, changes the last one", async () => {
|
||||
const FIRST_DELETE = "first"
|
||||
const SECOND_DELETE = "second"
|
||||
const THIRD = "third"
|
||||
const THIRD_CHANGED = "third_changed"
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: FIRST_DELETE,
|
||||
},
|
||||
{
|
||||
name: SECOND_DELETE,
|
||||
},
|
||||
{
|
||||
name: THIRD,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
render(
|
||||
<Providers theme={defaultTheme} rsiValues={{ ...mockValues, fields }}>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const allRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(allRowsWithHeader).toHaveLength(4)
|
||||
|
||||
const switchFilters = screen.getAllByRole("checkbox", {
|
||||
name: "Select",
|
||||
})
|
||||
|
||||
await userEvent.click(switchFilters[0])
|
||||
await userEvent.click(switchFilters[1])
|
||||
|
||||
const discardButton = screen.getByRole("button", {
|
||||
name: "Discard selected rows",
|
||||
})
|
||||
|
||||
await userEvent.click(discardButton)
|
||||
|
||||
const filteredRowsWithHeader = await screen.findAllByRole("row")
|
||||
expect(filteredRowsWithHeader).toHaveLength(2)
|
||||
|
||||
const nameCell = screen.getByRole("gridcell", {
|
||||
name: THIRD,
|
||||
})
|
||||
|
||||
await userEvent.click(nameCell)
|
||||
|
||||
screen.getByRole<HTMLInputElement>("textbox")
|
||||
await userEvent.keyboard(THIRD_CHANGED + "{enter}")
|
||||
|
||||
const validRow = screen.getByText(THIRD_CHANGED)
|
||||
expect(validRow).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("All inputs change values", async () => {
|
||||
const NAME = "John"
|
||||
const NEW_NAME = "Johnny"
|
||||
const OPTIONS = [
|
||||
{ value: "one", label: "ONE" },
|
||||
{ value: "two", label: "TWO" },
|
||||
] as const
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "lastName",
|
||||
key: "lastName",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: OPTIONS,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "is cool",
|
||||
key: "is_cool",
|
||||
fieldType: {
|
||||
type: "checkbox",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: NAME,
|
||||
lastName: OPTIONS[0].value,
|
||||
is_cool: false,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
)
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{
|
||||
...mockValues,
|
||||
fields,
|
||||
}}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
// input
|
||||
const nameCell = screen.getByRole("gridcell", {
|
||||
name: NAME,
|
||||
})
|
||||
|
||||
await userEvent.click(nameCell)
|
||||
|
||||
const input: HTMLInputElement | null = screen.getByRole<HTMLInputElement>("textbox")
|
||||
|
||||
expect(input).toHaveValue(NAME)
|
||||
expect(input).toHaveFocus()
|
||||
expect(input.selectionStart).toBe(0)
|
||||
expect(input.selectionEnd).toBe(NAME.length)
|
||||
|
||||
await userEvent.keyboard(NEW_NAME + "{enter}")
|
||||
expect(input).not.toBeInTheDocument()
|
||||
|
||||
const newNameCell = screen.getByRole("gridcell", {
|
||||
name: NEW_NAME,
|
||||
})
|
||||
expect(newNameCell).toBeInTheDocument()
|
||||
|
||||
// select
|
||||
const lastNameCell = screen.getByRole("gridcell", {
|
||||
name: OPTIONS[0].label,
|
||||
})
|
||||
await userEvent.click(lastNameCell)
|
||||
|
||||
const newOption = screen.getByRole("button", {
|
||||
name: OPTIONS[1].label,
|
||||
})
|
||||
await userEvent.click(newOption)
|
||||
expect(newOption).not.toBeInTheDocument()
|
||||
|
||||
const newLastName = screen.getByRole("gridcell", {
|
||||
name: OPTIONS[1].label,
|
||||
})
|
||||
expect(newLastName).toBeInTheDocument()
|
||||
|
||||
// Boolean
|
||||
const checkbox = screen.getByRole("checkbox", {
|
||||
name: "",
|
||||
})
|
||||
|
||||
expect(checkbox).not.toBeChecked()
|
||||
|
||||
await userEvent.click(checkbox)
|
||||
|
||||
expect(checkbox).toBeChecked()
|
||||
})
|
||||
|
||||
test("Row hook transforms data", async () => {
|
||||
const NAME = "John"
|
||||
const LASTNAME = "Doe"
|
||||
const NEW_NAME = "Johnny"
|
||||
const NEW_LASTNAME = "CENA"
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "lastName",
|
||||
key: "lastName",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const rowHook: RowHook<fieldKeys<typeof fields>> = (value) => ({
|
||||
name: value.name?.toString()?.split(/(\s+)/)[0],
|
||||
lastName: value.name?.toString()?.split(/(\s+)/)[2],
|
||||
})
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: NAME + " " + LASTNAME,
|
||||
lastName: undefined,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
rowHook,
|
||||
)
|
||||
await act(async () => {
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{
|
||||
...mockValues,
|
||||
fields,
|
||||
rowHook,
|
||||
}}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
})
|
||||
|
||||
const nameCell = screen.getByRole("gridcell", {
|
||||
name: NAME,
|
||||
})
|
||||
expect(nameCell).toBeInTheDocument()
|
||||
const lastNameCell = screen.getByRole("gridcell", {
|
||||
name: LASTNAME,
|
||||
})
|
||||
expect(lastNameCell).toBeInTheDocument()
|
||||
|
||||
// activate input
|
||||
await userEvent.click(nameCell)
|
||||
|
||||
await userEvent.keyboard(NEW_NAME + " " + NEW_LASTNAME + "{enter}")
|
||||
|
||||
const newNameCell = screen.getByRole("gridcell", {
|
||||
name: NEW_NAME,
|
||||
})
|
||||
expect(newNameCell).toBeInTheDocument()
|
||||
const newLastNameCell = screen.getByRole("gridcell", {
|
||||
name: NEW_LASTNAME,
|
||||
})
|
||||
expect(newLastNameCell).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("Row hook only runs on a single row", async () => {
|
||||
const NAME = "John"
|
||||
const NEW_NAME = "Kate"
|
||||
const LAST_NAME = "Doe"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "lastName",
|
||||
key: "lastName",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const mockedHook = jest.fn((a) => a)
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: NAME,
|
||||
lastName: LAST_NAME,
|
||||
},
|
||||
{
|
||||
name: "Johnny",
|
||||
lastName: "Doeson",
|
||||
},
|
||||
],
|
||||
fields,
|
||||
mockedHook,
|
||||
)
|
||||
await act(async () => {
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{
|
||||
...mockValues,
|
||||
fields,
|
||||
rowHook: mockedHook,
|
||||
}}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
})
|
||||
|
||||
// initially row hook is called for each row
|
||||
expect(mockedHook.mock.calls.length).toBe(2)
|
||||
|
||||
const nameCell = screen.getByRole("gridcell", {
|
||||
name: NAME,
|
||||
})
|
||||
expect(nameCell).toBeInTheDocument()
|
||||
|
||||
// activate input
|
||||
await userEvent.click(nameCell)
|
||||
|
||||
await userEvent.keyboard(NEW_NAME + "{enter}")
|
||||
|
||||
expect(mockedHook.mock.calls[2][0]?.name).toBe(NEW_NAME)
|
||||
expect(mockedHook.mock.calls.length).toBe(3)
|
||||
})
|
||||
|
||||
test("Row hook raises error", async () => {
|
||||
const WRONG_NAME = "Johnny"
|
||||
const RIGHT_NAME = "Jonathan"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
|
||||
const rowHook: RowHook<fieldKeys<typeof fields>> = (value, setError) => {
|
||||
if (value.name === WRONG_NAME) {
|
||||
setError(fields[0].key, { message: "Wrong name", level: "error" })
|
||||
}
|
||||
return value
|
||||
}
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: WRONG_NAME,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
rowHook,
|
||||
)
|
||||
await act(async () =>
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{
|
||||
...mockValues,
|
||||
fields,
|
||||
rowHook,
|
||||
}}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
),
|
||||
)
|
||||
|
||||
const switchFilter = getFilterSwitch()
|
||||
|
||||
await expect(await screen.findAllByRole("row")).toHaveLength(2)
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
await expect(await screen.findAllByRole("row")).toHaveLength(2)
|
||||
|
||||
const nameCell = screen.getByRole("gridcell", {
|
||||
name: WRONG_NAME,
|
||||
})
|
||||
expect(nameCell).toBeInTheDocument()
|
||||
|
||||
await userEvent.click(nameCell)
|
||||
screen.getByRole<HTMLInputElement>("textbox")
|
||||
|
||||
await userEvent.keyboard(RIGHT_NAME + "{enter}")
|
||||
|
||||
await expect(await screen.findAllByRole("row")).toHaveLength(1)
|
||||
})
|
||||
|
||||
test("Table hook transforms data", async () => {
|
||||
const NAME = "John"
|
||||
const SECOND_NAME = "Doe"
|
||||
const NEW_NAME = "Jakee"
|
||||
const ADDITION = "last"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const tableHook: TableHook<fieldKeys<typeof fields>> = (data) =>
|
||||
data.map((value) => ({
|
||||
name: value.name + ADDITION,
|
||||
}))
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: NAME,
|
||||
},
|
||||
{
|
||||
name: SECOND_NAME,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
undefined,
|
||||
tableHook,
|
||||
)
|
||||
await act(async () => {
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{
|
||||
...mockValues,
|
||||
fields,
|
||||
tableHook,
|
||||
}}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
})
|
||||
|
||||
const nameCell = screen.getByRole("gridcell", {
|
||||
name: NAME + ADDITION,
|
||||
})
|
||||
expect(nameCell).toBeInTheDocument()
|
||||
const lastNameCell = screen.getByRole("gridcell", {
|
||||
name: SECOND_NAME + ADDITION,
|
||||
})
|
||||
expect(lastNameCell).toBeInTheDocument()
|
||||
|
||||
// activate input
|
||||
await userEvent.click(nameCell)
|
||||
|
||||
await userEvent.keyboard(NEW_NAME + "{enter}")
|
||||
|
||||
const newNameCell = screen.getByRole("gridcell", {
|
||||
name: NEW_NAME + ADDITION,
|
||||
})
|
||||
expect(newNameCell).toBeInTheDocument()
|
||||
})
|
||||
test("Table hook raises error", async () => {
|
||||
const WRONG_NAME = "Johnny"
|
||||
const RIGHT_NAME = "Jonathan"
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const tableHook: TableHook<fieldKeys<typeof fields>> = (data, setError) => {
|
||||
data.forEach((value, index) => {
|
||||
if (value.name === WRONG_NAME) {
|
||||
setError(index, fields[0].key, { message: "Wrong name", level: "error" })
|
||||
}
|
||||
return value
|
||||
})
|
||||
return data
|
||||
}
|
||||
const initialData = await addErrorsAndRunHooks(
|
||||
[
|
||||
{
|
||||
name: WRONG_NAME,
|
||||
},
|
||||
{
|
||||
name: WRONG_NAME,
|
||||
},
|
||||
],
|
||||
fields,
|
||||
undefined,
|
||||
tableHook,
|
||||
)
|
||||
render(
|
||||
<Providers
|
||||
theme={defaultTheme}
|
||||
rsiValues={{
|
||||
...mockValues,
|
||||
fields,
|
||||
tableHook,
|
||||
}}
|
||||
>
|
||||
<ModalWrapper isOpen={true} onClose={() => {}}>
|
||||
<ValidationStep<fieldKeys<typeof fields>> initialData={initialData} file={file} />
|
||||
</ModalWrapper>
|
||||
</Providers>,
|
||||
)
|
||||
|
||||
const switchFilter = getFilterSwitch()
|
||||
|
||||
await expect(await screen.findAllByRole("row")).toHaveLength(3)
|
||||
|
||||
await userEvent.click(switchFilter)
|
||||
|
||||
await expect(await screen.findAllByRole("row")).toHaveLength(3)
|
||||
|
||||
const nameCell = await screen.getAllByRole("gridcell", {
|
||||
name: WRONG_NAME,
|
||||
})[0]
|
||||
|
||||
await userEvent.click(nameCell)
|
||||
screen.getByRole<HTMLInputElement>("textbox")
|
||||
|
||||
await userEvent.keyboard(RIGHT_NAME + "{enter}")
|
||||
|
||||
await expect(await screen.findAllByRole("row")).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
@@ -1,58 +0,0 @@
|
||||
import { ReactSpreadsheetImport } from "../ReactSpreadsheetImport"
|
||||
import { Box, Link, Code, Button, useDisclosure } from "@chakra-ui/react"
|
||||
import { mockRsiValues } from "./mockRsiValues"
|
||||
import { useState } from "react"
|
||||
import type { Result } from "src/types"
|
||||
|
||||
export default {
|
||||
title: "React spreadsheet import",
|
||||
}
|
||||
|
||||
export const Basic = () => {
|
||||
const [data, setData] = useState<Result<any> | null>(null)
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
return (
|
||||
<>
|
||||
<Box py={20} display="flex" gap="8px" alignItems="center">
|
||||
<Button onClick={onOpen} border="2px solid #7069FA" p="8px" borderRadius="8px">
|
||||
Open Flow
|
||||
</Button>
|
||||
(make sure you have a file to upload)
|
||||
</Box>
|
||||
<Link href="./exampleFile.csv" border="2px solid #718096" p="8px" borderRadius="8px" download="exampleCSV">
|
||||
Download example file
|
||||
</Link>
|
||||
<ReactSpreadsheetImport {...mockRsiValues} isOpen={isOpen} onClose={onClose} onSubmit={setData} />
|
||||
{!!data && (
|
||||
<Box pt={64} display="flex" gap="8px" flexDirection="column">
|
||||
<b>Returned data (showing first 100 rows):</b>
|
||||
<Code
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
borderRadius="16px"
|
||||
fontSize="12px"
|
||||
background="#4A5568"
|
||||
color="white"
|
||||
p={32}
|
||||
>
|
||||
<pre>
|
||||
{JSON.stringify(
|
||||
{
|
||||
validData: data.validData.slice(0, 100),
|
||||
invalidData: data.invalidData.slice(0, 100),
|
||||
all: data.all.slice(0, 100),
|
||||
},
|
||||
undefined,
|
||||
4,
|
||||
)}
|
||||
</pre>
|
||||
</Code>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Basic.parameters = {
|
||||
chromatic: { disableSnapshot: true },
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
import type { RsiProps } from "../types"
|
||||
import { defaultRSIProps } from "../ReactSpreadsheetImport"
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: "Name",
|
||||
key: "name",
|
||||
alternateMatches: ["first name", "first"],
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "Stephanie",
|
||||
validations: [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Name is required",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Surname",
|
||||
key: "surname",
|
||||
alternateMatches: ["second name", "last name", "last"],
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "McDonald",
|
||||
validations: [
|
||||
{
|
||||
rule: "unique",
|
||||
errorMessage: "Last name must be unique",
|
||||
level: "info",
|
||||
},
|
||||
],
|
||||
description: "Family / Last name",
|
||||
},
|
||||
{
|
||||
label: "Age",
|
||||
key: "age",
|
||||
alternateMatches: ["years"],
|
||||
fieldType: {
|
||||
type: "input",
|
||||
},
|
||||
example: "23",
|
||||
validations: [
|
||||
{
|
||||
rule: "regex",
|
||||
value: "^\\d+$",
|
||||
errorMessage: "Age must be a number",
|
||||
level: "warning",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Team",
|
||||
key: "team",
|
||||
alternateMatches: ["department"],
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "Team One", value: "one" },
|
||||
{ label: "Team Two", value: "two" },
|
||||
],
|
||||
},
|
||||
example: "Team one",
|
||||
validations: [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Team is required",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Is manager",
|
||||
key: "is_manager",
|
||||
alternateMatches: ["manages"],
|
||||
fieldType: {
|
||||
type: "checkbox",
|
||||
booleanMatches: {},
|
||||
},
|
||||
example: "true",
|
||||
},
|
||||
] as const
|
||||
|
||||
const mockComponentBehaviourForTypes = <T extends string>(props: RsiProps<T>) => props
|
||||
|
||||
export const mockRsiValues = mockComponentBehaviourForTypes({
|
||||
...defaultRSIProps,
|
||||
fields: fields,
|
||||
onSubmit: (data) => {
|
||||
console.log(data.all.map((value) => value))
|
||||
},
|
||||
isOpen: true,
|
||||
onClose: () => {},
|
||||
// uploadStepHook: async (data) => {
|
||||
// await new Promise((resolve) => {
|
||||
// setTimeout(() => resolve(data), 4000)
|
||||
// })
|
||||
// return data
|
||||
// },
|
||||
// selectHeaderStepHook: async (hData, data) => {
|
||||
// await new Promise((resolve) => {
|
||||
// setTimeout(
|
||||
// () =>
|
||||
// resolve({
|
||||
// headerValues: hData,
|
||||
// data,
|
||||
// }),
|
||||
// 4000,
|
||||
// )
|
||||
// })
|
||||
// return {
|
||||
// headerValues: hData,
|
||||
// data,
|
||||
// }
|
||||
// },
|
||||
// // Runs after column matching and on entry change, more performant
|
||||
// matchColumnsStepHook: async (data) => {
|
||||
// await new Promise((resolve) => {
|
||||
// setTimeout(() => resolve(data), 4000)
|
||||
// })
|
||||
// return data
|
||||
// },
|
||||
})
|
||||
|
||||
export const editableTableInitialData = [
|
||||
{
|
||||
name: "Hello",
|
||||
surname: "Hello",
|
||||
age: "123123",
|
||||
team: "one",
|
||||
is_manager: true,
|
||||
},
|
||||
{
|
||||
name: "Hello",
|
||||
surname: "Hello",
|
||||
age: "12312zsas3",
|
||||
team: "two",
|
||||
is_manager: true,
|
||||
},
|
||||
{
|
||||
name: "Whooaasdasdawdawdawdiouasdiuasdisdhasd",
|
||||
surname: "Hello",
|
||||
age: "123123",
|
||||
team: undefined,
|
||||
is_manager: false,
|
||||
},
|
||||
{
|
||||
name: "Goodbye",
|
||||
surname: "Goodbye",
|
||||
age: "111",
|
||||
team: "two",
|
||||
is_manager: true,
|
||||
},
|
||||
]
|
||||
|
||||
export const headerSelectionTableFields = [
|
||||
["text", "num", "select", "bool"],
|
||||
["second", "123", "one", "true"],
|
||||
["third", "123", "one", "true"],
|
||||
["fourth", "123", "one", "true"],
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
import "@testing-library/jest-dom"
|
||||
import { render } from "@testing-library/react"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { ReactSpreadsheetImport } from "../ReactSpreadsheetImport"
|
||||
import { mockRsiValues } from "../stories/mockRsiValues"
|
||||
|
||||
test("Close modal", async () => {
|
||||
let isOpen = true
|
||||
const onClose = jest.fn(() => {
|
||||
isOpen = !isOpen
|
||||
})
|
||||
const { getByText, getByLabelText } = render(
|
||||
<ReactSpreadsheetImport {...mockRsiValues} onClose={onClose} isOpen={isOpen} />,
|
||||
)
|
||||
|
||||
const closeButton = getByLabelText("Close modal")
|
||||
|
||||
await userEvent.click(closeButton)
|
||||
|
||||
const confirmButton = getByText("Exit flow")
|
||||
|
||||
await userEvent.click(confirmButton)
|
||||
expect(onClose).toBeCalled()
|
||||
})
|
||||
@@ -1,63 +0,0 @@
|
||||
// Yeeted from https://github.com/adazzle/react-data-grid/blob/main/test/setup.ts
|
||||
if (typeof window !== "undefined") {
|
||||
window.ResizeObserver ??= class {
|
||||
callback: ResizeObserverCallback
|
||||
|
||||
constructor(callback: ResizeObserverCallback) {
|
||||
this.callback = callback
|
||||
}
|
||||
|
||||
observe() {
|
||||
this.callback([], this)
|
||||
}
|
||||
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
// patch clientWidth/clientHeight to pretend we're rendering DataGrid at 1080p
|
||||
Object.defineProperties(HTMLDivElement.prototype, {
|
||||
clientWidth: {
|
||||
get(this: HTMLDivElement) {
|
||||
return this.classList.contains("rdg") ? 1920 : 0
|
||||
},
|
||||
},
|
||||
clientHeight: {
|
||||
get(this: HTMLDivElement) {
|
||||
return this.classList.contains("rdg") ? 1080 : 0
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Element.prototype.setPointerCapture ??= () => {}
|
||||
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
})
|
||||
|
||||
Object.defineProperty(global, "ResizeObserver", {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(() => ({
|
||||
observe: jest.fn(),
|
||||
unobserve: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
})),
|
||||
})
|
||||
|
||||
Object.defineProperty(window.HTMLElement.prototype, "scrollIntoView", {
|
||||
writable: true,
|
||||
value: jest.fn(),
|
||||
})
|
||||
}
|
||||
|
||||
jest.setTimeout(30000)
|
||||
Reference in New Issue
Block a user