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 { useRsi } from "../../../hooks/useRsi"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
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> = {
|
type ColumnGridProps<T extends string> = {
|
||||||
columns: Columns<T>
|
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 { useCallback, useState } from "react"
|
||||||
import { useRsi } from "../../hooks/useRsi"
|
import { useRsi } from "../../hooks/useRsi"
|
||||||
import { DropZone } from "./components/DropZone"
|
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 type { Column as RDGColumn, RenderEditCellProps, FormatterProps } from "react-data-grid"
|
||||||
import { Box, Checkbox, Input, Switch, Tooltip } from "@chakra-ui/react"
|
import { useRowSelection } from "react-data-grid"
|
||||||
import type { Data, Fields } from "../../../types"
|
import { Checkbox, Input, Switch } from "@chakra-ui/react"
|
||||||
|
import type { Data, Fields, Field, SelectOption } from "../../../types"
|
||||||
import type { ChangeEvent } from "react"
|
import type { ChangeEvent } from "react"
|
||||||
import type { Meta } from "../types"
|
import type { Meta } from "../types"
|
||||||
import { CgInfo } from "react-icons/cg"
|
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"
|
const SELECT_COLUMN_KEY = "select-row"
|
||||||
|
|
||||||
@@ -13,7 +20,9 @@ function autoFocusAndSelect(input: HTMLInputElement | null) {
|
|||||||
input?.select()
|
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,
|
key: SELECT_COLUMN_KEY,
|
||||||
name: "",
|
name: "",
|
||||||
@@ -24,7 +33,7 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
|||||||
sortable: false,
|
sortable: false,
|
||||||
frozen: true,
|
frozen: true,
|
||||||
cellClass: "rdg-checkbox",
|
cellClass: "rdg-checkbox",
|
||||||
formatter: (props) => {
|
formatter: (props: FormatterProps<RowType<T>>) => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const [isRowSelected, onRowSelectionChange] = useRowSelection()
|
const [isRowSelected, onRowSelectionChange] = useRowSelection()
|
||||||
return (
|
return (
|
||||||
@@ -44,44 +53,58 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
...fields.map(
|
...fields.map(
|
||||||
(column): Column<Data<T> & Meta> => ({
|
(column: Field<T>): RDGColumn<RowType<T>> => ({
|
||||||
key: column.key,
|
key: column.key,
|
||||||
name: column.label,
|
name: column.label,
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
headerRenderer: () => (
|
headerRenderer: () => (
|
||||||
<Box display="flex" gap={1} alignItems="center" position="relative">
|
<div className="flex gap-1 items-center relative">
|
||||||
<Box flex={1} overflow="hidden" textOverflow="ellipsis">
|
<div className="flex-1 overflow-hidden text-ellipsis">
|
||||||
{column.label}
|
{column.label}
|
||||||
</Box>
|
</div>
|
||||||
{column.description && (
|
{column.description && (
|
||||||
<Tooltip placement="top" hasArrow label={column.description}>
|
<div className="flex-none">
|
||||||
<Box flex={"0 0 auto"}>
|
<CgInfo className="h-4 w-4" />
|
||||||
<CgInfo size="16px" />
|
</div>
|
||||||
</Box>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</div>
|
||||||
),
|
),
|
||||||
editable: column.fieldType.type !== "checkbox",
|
editable: column.fieldType.type !== "checkbox",
|
||||||
editor: ({ row, onRowChange, onClose }) => {
|
editor: ({ row, onRowChange, onClose }: RenderEditCellProps<RowType<T>>) => {
|
||||||
let component
|
let component
|
||||||
|
|
||||||
switch (column.fieldType.type) {
|
switch (column.fieldType.type) {
|
||||||
case "select":
|
case "select":
|
||||||
component = (
|
component = (
|
||||||
<TableSelect
|
<Select
|
||||||
value={column.fieldType.options.find((option) => option.value === (row[column.key] as string))}
|
defaultOpen
|
||||||
onChange={(value) => {
|
value={row[column.key] as string}
|
||||||
onRowChange({ ...row, [column.key]: value?.value }, true)
|
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
|
break
|
||||||
default:
|
default:
|
||||||
component = (
|
component = (
|
||||||
<Box paddingInlineStart="0.5rem">
|
<div className="pl-2">
|
||||||
<Input
|
<Input
|
||||||
ref={autoFocusAndSelect}
|
ref={autoFocusAndSelect}
|
||||||
variant="unstyled"
|
variant="unstyled"
|
||||||
@@ -93,7 +116,7 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
|||||||
}}
|
}}
|
||||||
onBlur={() => onClose(true)}
|
onBlur={() => onClose(true)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,16 +125,14 @@ export const generateColumns = <T extends string>(fields: Fields<T>): Column<Dat
|
|||||||
editorOptions: {
|
editorOptions: {
|
||||||
editOnClick: true,
|
editOnClick: true,
|
||||||
},
|
},
|
||||||
formatter: ({ row, onRowChange }) => {
|
formatter: ({ row, onRowChange }: FormatterProps<RowType<T>>) => {
|
||||||
let component
|
let component
|
||||||
|
|
||||||
switch (column.fieldType.type) {
|
switch (column.fieldType.type) {
|
||||||
case "checkbox":
|
case "checkbox":
|
||||||
component = (
|
component = (
|
||||||
<Box
|
<div
|
||||||
display="flex"
|
className="flex items-center h-full"
|
||||||
alignItems="center"
|
|
||||||
height="100%"
|
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation()
|
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] })
|
onRowChange({ ...row, [column.key]: !row[column.key as T] })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</div>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
case "select":
|
case "select":
|
||||||
component = (
|
component = (
|
||||||
<Box minWidth="100%" minHeight="100%" overflow="hidden" textOverflow="ellipsis">
|
<div className="min-w-full min-h-full overflow-hidden text-ellipsis">
|
||||||
{column.fieldType.options.find((option) => option.value === row[column.key as T])?.label || null}
|
{column.fieldType.options.find((option: SelectOption) => option.value === row[column.key as T])?.label || null}
|
||||||
</Box>
|
</div>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
component = (
|
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]}
|
{row[column.key as T]}
|
||||||
</Box>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.__errors?.[column.key]) {
|
if (row.__errors?.[column.key]) {
|
||||||
return (
|
return (
|
||||||
<Tooltip placement="top" hasArrow label={row.__errors?.[column.key]?.message}>
|
<div className="relative group">
|
||||||
{component}
|
{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