diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/ConfirmCloseAlert.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/ConfirmCloseAlert.tsx deleted file mode 100644 index 6865cc7..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/ConfirmCloseAlert.tsx +++ /dev/null @@ -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(null) - - return ( - - - - {translations.alerts.confirmClose.headerTitle} - {translations.alerts.confirmClose.bodyText} - - - - - - - - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/SubmitDataAlert.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/SubmitDataAlert.tsx deleted file mode 100644 index 4488ebb..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/SubmitDataAlert.tsx +++ /dev/null @@ -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 ( - - - - - {translations.alerts.submitIncomplete.headerTitle} - - - {allowInvalidSubmit - ? translations.alerts.submitIncomplete.bodyText - : translations.alerts.submitIncomplete.bodyTextSubmitForbidden} - - - - - {translations.alerts.submitIncomplete.cancelButtonTitle} - - {allowInvalidSubmit && ( - - {translations.alerts.submitIncomplete.finishButtonTitle} - - )} - - - - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/UnmatchedFieldsAlert.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/UnmatchedFieldsAlert.tsx deleted file mode 100644 index f96f0bb..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/Alerts/UnmatchedFieldsAlert.tsx +++ /dev/null @@ -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 ( - - - - - {translations.alerts.unmatchedRequiredFields.headerTitle} - -
- - {translations.alerts.unmatchedRequiredFields.bodyText} - -

- {translations.alerts.unmatchedRequiredFields.listTitle}{" "} - - {fields.join(", ")} - -

-
-
- - - {translations.alerts.unmatchedRequiredFields.cancelButtonTitle} - - {allowInvalidSubmit && ( - - {translations.alerts.unmatchedRequiredFields.continueButtonTitle} - - )} - -
-
- ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/ContinueButton.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/ContinueButton.tsx deleted file mode 100644 index 2215aef..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/ContinueButton.tsx +++ /dev/null @@ -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 ( - - {onBack && ( - - )} - - - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/FadingWrapper.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/FadingWrapper.tsx deleted file mode 100644 index 537dc75..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/FadingWrapper.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Box } from "@chakra-ui/react" - -type FadingWrapperProps = { - gridColumn: string - gridRow: string -} - -export const FadingWrapper = ({ gridColumn, gridRow }: FadingWrapperProps) => ( - <> - - - -) diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/Selects/MatchColumnSelect.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/Selects/MatchColumnSelect.tsx deleted file mode 100644 index 7b33edd..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/Selects/MatchColumnSelect.tsx +++ /dev/null @@ -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 ( - - value={value || null} - colorScheme="gray" - useBasicStyles - onChange={onChange} - placeholder={placeholder} - options={options} - chakraStyles={styles.select} - menuPosition="fixed" - components={customComponents} - aria-label={name} - /> - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/Selects/MenuPortal.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/Selects/MenuPortal.tsx deleted file mode 100644 index b39b8db..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/Selects/MenuPortal.tsx +++ /dev/null @@ -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(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( - - {props.children} - , - wrapperElement, - ) -} - -export const customComponents = { - MenuPortal, -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/components/Selects/TableSelect.tsx b/inventory/src/lib/react-spreadsheet-import/src/components/Selects/TableSelect.tsx deleted file mode 100644 index 886a31d..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/components/Selects/TableSelect.tsx +++ /dev/null @@ -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 ( - - autoFocus - useBasicStyles - size="sm" - value={value} - onChange={onChange} - placeholder=" " - closeMenuOnScroll - menuPosition="fixed" - menuIsOpen - menuPortalTarget={document.getElementById(rootId)} - options={options} - chakraStyles={styles.select} - /> - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/ColumnGrid.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/ColumnGrid.tsx index b390dbf..539ac29 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/ColumnGrid.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/ColumnGrid.tsx @@ -4,9 +4,6 @@ import { ColumnType } from "../MatchColumnsStep" import { useRsi } from "../../../hooks/useRsi" import { Button } from "@/components/ui/button" import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area" -import { Box } from "@/components/ui/box" -import { Text } from "@/components/ui/text" -import { FadingWrapper } from "@/components/ui/fading-wrapper" type ColumnGridProps = { columns: Columns diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx deleted file mode 100644 index fbc3bc3..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx +++ /dev/null @@ -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 { - option: MatchedOptions | Partial> - column: MatchedSelectColumn | MatchedSelectOptionsColumn - onSubChange: (val: T, index: number, option: string) => void -} - -export const SubMatchingSelect = ({ option, column, onSubChange }: Props) => { - const styles = useStyleConfig("MatchColumnsStep") as Styles - const { translations, fields } = useRsi() - const options = getFieldOptions(fields, column.value) - const value = options.find((opt) => opt.value == option.value) - - return ( - - {option.entry} - onSubChange(value?.value as T, column.index, option.entry!)} - options={options} - name={option.entry} - /> - - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/stories/MatchColumns.stories.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/stories/MatchColumns.stories.tsx deleted file mode 100644 index e2b335e..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/stories/MatchColumns.stories.tsx +++ /dev/null @@ -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 = () => ( - - {}}> - {}} /> - - -) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx deleted file mode 100644 index ec1a3ea..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx +++ /dev/null @@ -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 = [ - { - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - -
- - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - // 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( - - {}}> - - - , - ) - - 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( - - {}}> - -
- - , - ) - - 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( - - {}}> - -
- - , - ) - - 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( - - {}}> - -
- - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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( - - {}}> - -
- - , - ) - - 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( - , - ) - - 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( - , - ) - - 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( - , - ) - - const continueButton = screen.getByText(CONTINUE_BUTTON) - await userEvent.click(continueButton) - - const errorToast = await screen.findAllByText(ERROR_MESSAGE, undefined, { timeout: 5000 }) - expect(errorToast?.[0]).toBeInTheDocument() - }) -}) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectHeaderStep/stories/SelectHeader.stories.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/SelectHeaderStep/stories/SelectHeader.stories.tsx deleted file mode 100644 index 9d344e2..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectHeaderStep/stories/SelectHeader.stories.tsx +++ /dev/null @@ -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 = () => ( - - {}}> - {}} /> - - -) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectHeaderStep/tests/SelectHeaderStep.test.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/SelectHeaderStep/tests/SelectHeaderStep.test.tsx deleted file mode 100644 index 204ba75..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectHeaderStep/tests/SelectHeaderStep.test.tsx +++ /dev/null @@ -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( - - {}}> - - - , - ) - - 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() - 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( - , - ) - 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( - , - ) - 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() - - 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() - - 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() - 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() - 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() - }, - ) -}) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectSheetStep/stories/SelectSheet.stories.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/SelectSheetStep/stories/SelectSheet.stories.tsx deleted file mode 100644 index b1c626c..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectSheetStep/stories/SelectSheet.stories.tsx +++ /dev/null @@ -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 = () => ( - - {}}> - {}} /> - - -) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectSheetStep/tests/SelectSheetStep.test.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/SelectSheetStep/tests/SelectSheetStep.test.tsx deleted file mode 100644 index 84bfcb5..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/SelectSheetStep/tests/SelectSheetStep.test.tsx +++ /dev/null @@ -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() - 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() - 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( - - {}}> - - - , - ) - - 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() - 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() -}) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/UploadStep.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/UploadStep.tsx index d0a7e7d..e2926d3 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/UploadStep.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/UploadStep.tsx @@ -1,4 +1,4 @@ -import type XLSX from "xlsx-ugnis" +import type XLSX from "xlsx" import { useCallback, useState } from "react" import { useRsi } from "../../hooks/useRsi" import { DropZone } from "./components/DropZone" diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/stories/Upload.stories.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/stories/Upload.stories.tsx deleted file mode 100644 index 716140c..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/stories/Upload.stories.tsx +++ /dev/null @@ -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 ( - - {}}> - {}} /> - - - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/tests/UploadStep.test.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/tests/UploadStep.test.tsx deleted file mode 100644 index 6717f75..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/UploadStep/tests/UploadStep.test.tsx +++ /dev/null @@ -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( - - {}}> - - - , - ) - - 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() - 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() - - 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() - - 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() -}) diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/components/columns.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/components/columns.tsx index 8f4b2fa..7bfe7bf 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/components/columns.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/components/columns.tsx @@ -1,10 +1,17 @@ -import DataGrid, { Column, useRowSelection } from "react-data-grid" -import { Box, Checkbox, Input, Switch, Tooltip } from "@chakra-ui/react" -import type { Data, Fields } from "../../../types" +import type { Column as RDGColumn, RenderEditCellProps, FormatterProps } from "react-data-grid" +import { useRowSelection } from "react-data-grid" +import { Checkbox, Input, Switch } from "@chakra-ui/react" +import type { Data, Fields, Field, SelectOption } from "../../../types" import type { ChangeEvent } from "react" import type { Meta } from "../types" import { CgInfo } from "react-icons/cg" -import { TableSelect } from "../../../components/Selects/TableSelect" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" const SELECT_COLUMN_KEY = "select-row" @@ -13,7 +20,9 @@ function autoFocusAndSelect(input: HTMLInputElement | null) { input?.select() } -export const generateColumns = (fields: Fields): Column & Meta>[] => [ +type RowType = Data & Meta + +export const generateColumns = (fields: Fields): RDGColumn>[] => [ { key: SELECT_COLUMN_KEY, name: "", @@ -24,7 +33,7 @@ export const generateColumns = (fields: Fields): Column { + formatter: (props: FormatterProps>) => { // eslint-disable-next-line react-hooks/rules-of-hooks const [isRowSelected, onRowSelectionChange] = useRowSelection() return ( @@ -44,44 +53,58 @@ export const generateColumns = (fields: Fields): Column & Meta> => ({ + (column: Field): RDGColumn> => ({ key: column.key, name: column.label, minWidth: 150, resizable: true, headerRenderer: () => ( - - +
+
{column.label} - +
{column.description && ( - - - - - +
+ +
)} - +
), editable: column.fieldType.type !== "checkbox", - editor: ({ row, onRowChange, onClose }) => { + editor: ({ row, onRowChange, onClose }: RenderEditCellProps>) => { let component switch (column.fieldType.type) { case "select": component = ( - option.value === (row[column.key] as string))} - onChange={(value) => { - onRowChange({ ...row, [column.key]: value?.value }, true) + ) break default: component = ( - +
(fields: Fields): Column onClose(true)} /> - +
) } @@ -102,16 +125,14 @@ export const generateColumns = (fields: Fields): Column { + formatter: ({ row, onRowChange }: FormatterProps>) => { let component switch (column.fieldType.type) { case "checkbox": component = ( - { event.stopPropagation() }} @@ -122,29 +143,32 @@ export const generateColumns = (fields: Fields): Column - +
) break case "select": component = ( - - {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} +
) break default: component = ( - +
{row[column.key as T]} - +
) } if (row.__errors?.[column.key]) { return ( - +
{component} - +
+ {row.__errors?.[column.key]?.message} +
+
) } diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/stories/Validation.stories.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/stories/Validation.stories.tsx deleted file mode 100644 index ab5decf..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/stories/Validation.stories.tsx +++ /dev/null @@ -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 ( - - {}}> - - - - ) -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/tests/ValidationStep.test.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/tests/ValidationStep.test.tsx deleted file mode 100644 index 59ca092..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStep/tests/ValidationStep.test.tsx +++ /dev/null @@ -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[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( - - {}}> - - - , - ) - - 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( - - {}}> - - - , - ) - - 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 => { - onSuccess() - return Promise.resolve() - }) - const onClose = jest.fn() - render( - - {}}> - - - , - ) - - 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 => { - onReject() - throw new Error(ERROR_MESSAGE) - }) - const onClose = jest.fn() - - render( - - {}}> - - - , - ) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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("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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - // input - const nameCell = screen.getByRole("gridcell", { - name: NAME, - }) - - await userEvent.click(nameCell) - - const input: HTMLInputElement | null = screen.getByRole("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> = (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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - }) - - 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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - }) - - // 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> = (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( - - {}}> - > initialData={initialData} file={file} /> - - , - ), - ) - - 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("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> = (data) => - data.map((value) => ({ - name: value.name + ADDITION, - })) - const initialData = await addErrorsAndRunHooks( - [ - { - name: NAME, - }, - { - name: SECOND_NAME, - }, - ], - fields, - undefined, - tableHook, - ) - await act(async () => { - render( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - }) - - 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> = (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( - - {}}> - > initialData={initialData} file={file} /> - - , - ) - - 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("textbox") - - await userEvent.keyboard(RIGHT_NAME + "{enter}") - - await expect(await screen.findAllByRole("row")).toHaveLength(2) - }) -}) diff --git a/inventory/src/lib/react-spreadsheet-import/src/stories/Default.stories.tsx b/inventory/src/lib/react-spreadsheet-import/src/stories/Default.stories.tsx deleted file mode 100644 index ce027f4..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/stories/Default.stories.tsx +++ /dev/null @@ -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 | null>(null) - const { isOpen, onOpen, onClose } = useDisclosure() - return ( - <> - - - (make sure you have a file to upload) - - - Download example file - - - {!!data && ( - - Returned data (showing first 100 rows): - -
-              {JSON.stringify(
-                {
-                  validData: data.validData.slice(0, 100),
-                  invalidData: data.invalidData.slice(0, 100),
-                  all: data.all.slice(0, 100),
-                },
-                undefined,
-                4,
-              )}
-            
-
-
- )} - - ) -} - -Basic.parameters = { - chromatic: { disableSnapshot: true }, -} diff --git a/inventory/src/lib/react-spreadsheet-import/src/stories/mockRsiValues.ts b/inventory/src/lib/react-spreadsheet-import/src/stories/mockRsiValues.ts deleted file mode 100644 index 6bbb200..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/stories/mockRsiValues.ts +++ /dev/null @@ -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 = (props: RsiProps) => 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"], -] diff --git a/inventory/src/lib/react-spreadsheet-import/src/tests/ReactSpreadsheetImport.test.tsx b/inventory/src/lib/react-spreadsheet-import/src/tests/ReactSpreadsheetImport.test.tsx deleted file mode 100644 index 8e347a4..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/tests/ReactSpreadsheetImport.test.tsx +++ /dev/null @@ -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( - , - ) - - const closeButton = getByLabelText("Close modal") - - await userEvent.click(closeButton) - - const confirmButton = getByText("Exit flow") - - await userEvent.click(confirmButton) - expect(onClose).toBeCalled() -}) diff --git a/inventory/src/lib/react-spreadsheet-import/src/tests/setup.ts b/inventory/src/lib/react-spreadsheet-import/src/tests/setup.ts deleted file mode 100644 index 320472e..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/tests/setup.ts +++ /dev/null @@ -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)