Git Product home page Git Product logo

ugnissoftware / react-spreadsheet-import Goto Github PK

View Code? Open in Web Editor NEW
417.0 7.0 107.0 30.12 MB

Import flow for Excel (.xlsx) and CSV file with automated column matching and validation.

Home Page: https://ugnissoftware.github.io/react-spreadsheet-import/iframe.html?id=react-spreadsheet-import--basic&args=&viewMode=story

License: MIT License

TypeScript 100.00%
sheet import upload csv sheet-import react data-import xlsx xls excel

react-spreadsheet-import's Introduction

RSI react-spreadsheet-import ⚡️

GitHub Workflow Status GitHub npm


A component used for importing XLS / XLSX / CSV documents built with Chakra UI. Import flow combines:

  • 📥 Uploader
  • ⚙️ Parser
  • 📊 File preview
  • 🧪 UI for column mapping
  • ✏ UI for validating and editing data

Demo

Features

  • Custom styles - edit Chakra UI theme to match your project's styles 🎨
  • Custom validation rules - make sure valid data is being imported, easily spot and correct errors
  • Hooks - alter raw data after upload or make adjustments on data changes
  • Auto-mapping columns - automatically map most likely value to your template values, e.g. name -> firstName

rsi-preview

Figma

We provide full figma designs. You can copy the designs here

Getting started

npm i react-spreadsheet-import

Using the component: (it's up to you when the flow is open and what you do on submit with the imported data)

import { ReactSpreadsheetImport } from "react-spreadsheet-import";

<ReactSpreadsheetImport isOpen={isOpen} onClose={onClose} onSubmit={onSubmit} fields={fields} />

Required Props

  // Determines if modal is visible.
  isOpen: Boolean
  // Called when flow is closed without reaching submit.
  onClose: () => void
  // Called after user completes the flow. Provides data array, where data keys matches your field keys. 
  onSubmit: (data, file) => void | Promise<any>

Fields

Fields describe what data you are trying to collect.

const fields = [
  {
    // Visible in table header and when matching columns.
    label: "Name",
    // This is the key used for this field when we call onSubmit.
    key: "name",
    // Allows for better automatic column matching. Optional.
    alternateMatches: ["first name", "first"],
    // Used when editing and validating information.
    fieldType: {
      // There are 3 types - "input" / "checkbox" / "select".
      type: "input",
    },
    // Used in the first step to provide an example of what data is expected in this field. Optional.
    example: "Stephanie",
    // Can have multiple validations that are visible in Validation Step table.
    validations: [
      {
        // Can be "required" / "unique" / "regex"
        rule: "required",
        errorMessage: "Name is required",
        // There can be "info" / "warning" / "error" levels. Optional. Default "error".
        level: "error",
      },
    ],
  },
] as const

Optional Props

Hooks

You can transform and validate data with custom hooks. There are hooks after each step:

  • uploadStepHook - runs only once after uploading the file.
  • selectHeaderStepHook - runs only once after selecting the header row in spreadsheet.
  • matchColumnsStepHook - runs only once after column matching. Operations on data that are expensive should be done here.

The last step - validation step has 2 unique hooks that run only in that step with different performance tradeoffs:

  • tableHook - runs at the start and on any change. Runs on all rows. Very expensive, but can change rows that depend on other rows.
  • rowHook - runs at the start and on any row change. Runs only on the rows changed. Fastest, most validations and transformations should be done here.

Example:

<ReactSpreadsheetImport
  rowHook={(data, addError) => {
    // Validation
    if (data.name === "John") {
      addError("name", { message: "No Johns allowed", level: "info" })
    }
    // Transformation
    return { ...data, name: "Not John" }
    // Sorry John
  }}
/>

Initial state

In rare case when you need to skip the beginning of the flow, you can start the flow from any of the steps.

  • initialStepState - initial state of component that will be rendered on load.
  initialStepState?: StepState
  
  type StepState =
    | {
        type: StepType.upload
      }
    | {
        type: StepType.selectSheet
        workbook: XLSX.WorkBook
      }
    | {
        type: StepType.selectHeader
        data: RawData[]
      }
    | {
        type: StepType.matchColumns
        data: RawData[]
        headerValues: RawData
      }
    | {
        type: StepType.validateData
        data: any[]
      }

  type RawData = Array<string | undefined>

  // XLSX.workbook type is native to SheetJS and can be viewed here: https://github.com/SheetJS/sheetjs/blob/83ddb4c1203f6bac052d8c1608b32fead02ea32f/types/index.d.ts#L269

Example:

import { ReactSpreadsheetImport, StepType } from "react-spreadsheet-import";

<ReactSpreadsheetImport
  initialStepState={{
    type: StepType.matchColumns,
    data: [
      ["Josh", "2"],
      ["Charlie", "3"],
      ["Lena", "50"],
    ],
    headerValues: ["name", "age"],
  }}
/>

Dates and time

Excel stores dates and times as numbers - offsets from an epoch. When reading xlsx files SheetJS provides date formatting helpers. Default date import format is yyyy-mm-dd. Date parsing with SheetJS sometimes yields unexpected results, therefore thorough date validations are recommended.

  • dateFormat - sets SheetJS dateNF option. Can be used to format dates when importing sheet data.
  • parseRaw - sets SheetJS raw option. If true, date formatting will be applied to XLSX date fields only. Default is true

Common date-time formats can be viewed here.

Other optional props

  // Allows submitting with errors. Default: true
  allowInvalidSubmit?: boolean
  // Translations for each text. See customisation bellow
  translations?: object
  // Theme configuration passed to underlying Chakra-UI. See customisation bellow
  customTheme?: object
  // Specifies maximum number of rows for a single import
  maxRecords?: number
  // Maximum upload filesize (in bytes)
  maxFileSize?: number
  // Automatically map imported headers to specified fields if possible. Default: true
  autoMapHeaders?: boolean
  // When field type is "select", automatically match values if possible. Default: false
  autoMapSelectValues?: boolean
  // Headers matching accuracy: 1 for strict and up for more flexible matching. Default: 2
  autoMapDistance?: number
  // Enable navigation in stepper component and show back button. Default: false
  isNavigationEnabled?: boolean

Customisation

Customising styles (colors, fonts)

You can see default theme we use here. Your override should match this object's structure.

There are 3 ways you can style the component:

1.) Change theme colors globally

    <ReactSpreadsheetImport
        {...mockRsiValues}
        isOpen={isOpen}
        onClose={onClose}
        onSubmit={setData}
        customTheme={{
          colors: {
            background: 'white',
            ...
            rsi: {
              // your brand colors should go here
              50: '...'
              ...
              500: 'teal',
              ...
              900: "...",
            },
          },
        }}
      />

Screenshot 2022-04-13 at 10 24 34

2.) Change all components of the same type, like all Buttons, at the same time

<ReactSpreadsheetImport
  {...mockRsiValues}
  isOpen={isOpen}
  onClose={onClose}
  onSubmit={setData}
  customTheme={{
    components: {
      Button: {
        baseStyle: {
          borderRadius: "none",
        },
        defaultProps: {
          colorScheme: "yellow",
        },
      },
    },
  }}
/>

Screenshot 2022-04-13 at 11 04 30

3.) Change components specifically in each Step.

    <ReactSpreadsheetImport
        {...mockRsiValues}
        isOpen={isOpen}
        onClose={onClose}
        onSubmit={setData}
        customTheme={{
          components: {
            UploadStep: {
              baseStyle: {
                dropzoneButton: {
                  bg: "red",
                },
              },
            },
          },
        }}
      />

Screenshot 2022-04-13 at 10 21 58

Underneath we use Chakra-UI, you can send in a custom theme for us to apply. Read more about themes here

Changing text (translations)

You can change any text in the flow:

<ReactSpreadsheetImport
  translations={{
    uploadStep: {
      title: "Upload Employees",
    },
  }}
/>

You can see all the translation keys here

VS other libraries

Flatfile vs react-spreadsheet-import and Dromo vs react-spreadsheet-import:

RSI Flatfile Dromo
Licence MIT Proprietary Proprietary
Price Free Paid Paid
Support Github Issues Enterprise Enterprise
Self-host Yes Paid Paid
Hosted solution In development Yes Yes
On-prem deployment N/A Yes Yes
Hooks Yes Yes Yes
Automatic header matching Yes Yes Yes
Data validation Yes Yes Yes
Custom styling Yes Yes Yes
Translations Yes Yes Yes
Trademarked words Data Hooks No Yes No

React-spreadsheet-import can be used as a free and open-source alternative to Flatfile and Dromo.

Contributing

Feel free to open issues if you have any questions or notice bugs. If you want different component behaviour, consider forking the project.

Credits

Created by Ugnis. Julita Kriauciunaite and Karolis Masiulis. You can contact us at [email protected]

react-spreadsheet-import's People

Contributors

abhishek-butola avatar audi2014 avatar dependabot[bot] avatar glorianb avatar ineentho avatar jeffnv avatar jtordgeman avatar julitork avatar masiulis avatar mmbfreitas avatar trevorloflin avatar yo1l avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-spreadsheet-import's Issues

What combination of React, Chakra, and etc versions should I have to be able to use this project?

Hello, this project is awesome, however, I'm struggling to find the right combination of frontend library versions to use in order to use this project.

First, I found out that the project has a potentially unlisted dependency: Chakra. So, I added that.

Now, I'm getting the following error in a view that imports this library:

chakra-ui-hooks.esm.js:235 Uncaught TypeError: react__WEBPACK_IMPORTED_MODULE_0__.useId is not a function
    at useId (chakra-ui-hooks.esm.js:235:1)
    at useIds (chakra-ui-hooks.esm.js:261:1)
    at useModal (chakra-ui-modal.esm.js:145:1)
    at Modal (chakra-ui-modal.esm.js:326:1)
    at renderWithHooks (react-dom.development.js:14985:1)
    at mountIndeterminateComponent (react-dom.development.js:17811:1)
    at beginWork (react-dom.development.js:19049:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994:1)
    at invokeGuardedCallback (react-dom.development.js:4056:1)
    at beginWork$1 (react-dom.development.js:23964:1)
    at performUnitOfWork (react-dom.development.js:22776:1)
    at workLoopSync (react-dom.development.js:22707:1)
    at renderRootSync (react-dom.development.js:22670:1)
    at performSyncWorkOnRoot (react-dom.development.js:22293:1)
    at react-dom.development.js:11327:1
    at unstable_runWithPriority (scheduler.development.js:468:1)
    at runWithPriority$1 (react-dom.development.js:11276:1)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11322:1)
    at workLoop (scheduler.development.js:417:1)
    at flushWork (scheduler.development.js:390:1)
    at MessagePort.performWorkUntilDeadline (scheduler.development.js:157:1)

I'm wondering if I'm perhaps missing some other dependencies or actions I should take in order to use this library?

Thank you.

Our package.json:

{
    "name": "app",
    "version": "3.1.0",
    "private": true,
    "dependencies": {
        "@auth0/auth0-spa-js": "^1.19.4",
        "@babel/core": "^7.17.2",
        "@chakra-ui/react": "^2.2.1",
        "@emotion/cache": "^11.7.1",
        "@emotion/react": "^11.7.1",
        "@emotion/styled": "^11.6.0",
        "@fullcalendar/daygrid": "^5.10.1",
        "@fullcalendar/interaction": "^5.10.1",
        "@fullcalendar/list": "^5.10.1",
        "@fullcalendar/react": "^5.10.1",
        "@fullcalendar/timegrid": "^5.10.1",
        "@fullcalendar/timeline": "^5.10.1",
        "@hookform/resolvers": "^2.8.8",
        "@mui/icons-material": "^5.4.1",
        "@mui/lab": "^5.0.0-alpha.68",
        "@mui/material": "^5.4.1",
        "@mui/styles": "^5.4.1",
        "@mui/system": "^5.4.1",
        "@mui/utils": "^5.4.1",
        "@mui/x-data-grid": "^5.10.0",
        "@mui/x-data-grid-pro": "^5.10.0",
        "@mui/x-date-pickers": "^5.0.0-alpha.3",
        "@mui/x-license-pro": "^5.10.0",
        "@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
        "@reduxjs/toolkit": "^1.7.2",
        "@svgr/webpack": "5.5.0",
        "@tabler/icons": "^1.53.0",
        "@types/chance": "^1.1.3",
        "@types/draft-js": "^0.11.9",
        "@types/image-to-base64": "^2.1.0",
        "@types/jsonwebtoken": "^8.5.8",
        "@types/react": "^17.0.39",
        "@types/react-beautiful-dnd": "^13.1.2",
        "@types/react-copy-to-clipboard": "^5.0.2",
        "@types/react-currency-format": "^1.0.0",
        "@types/react-dom": "^17.0.11",
        "@types/react-draft-wysiwyg": "^1.13.4",
        "@types/react-google-recaptcha": "^2.1.3",
        "@types/react-redux": "^7.1.22",
        "@types/react-router-dom": "^5.3.3",
        "@types/react-slick": "^0.23.8",
        "@types/react-syntax-highlighter": "^13.5.2",
        "@types/react-window": "^1.8.5",
        "@types/uuid": "^8.3.4",
        "@types/webpack-env": "^1.16.3",
        "@typescript-eslint/eslint-plugin": "4.4.1",
        "@typescript-eslint/parser": "^4.24.0",
        "amazon-cognito-identity-js": "^5.2.6",
        "apexcharts": "^3.33.1",
        "axios": "^0.25.0",
        "axios-mock-adapter": "^1.20.0",
        "babel-eslint": "^10.1.0",
        "babel-jest": "^26.6.0",
        "babel-loader": "8.1.0",
        "babel-plugin-named-asset-import": "^0.3.7",
        "babel-preset-react-app": "^10.0.0",
        "bfj": "^7.0.2",
        "camelcase": "^6.1.0",
        "case-sensitive-paths-webpack-plugin": "2.3.0",
        "chance": "^1.1.8",
        "css-loader": "4.3.0",
        "csstype": "^3.0.10",
        "date-fns": "^2.28.0",
        "dotenv": "8.2.0",
        "dotenv-expand": "5.1.0",
        "draft-js": "^0.11.7",
        "emoji-picker-react": "^3.5.1",
        "eslint": "^7.27.0",
        "eslint-config-airbnb-typescript": "^12.3.1",
        "eslint-config-prettier": "^8.3.0",
        "eslint-config-react-app": "6.0.0",
        "eslint-import-resolver-typescript": "^2.5.0",
        "eslint-plugin-flowtype": "^5.7.2",
        "eslint-plugin-import": "^2.25.4",
        "eslint-plugin-jsx-a11y": "^6.5.1",
        "eslint-plugin-prettier": "^3.4.0",
        "eslint-plugin-react": "^7.28.0",
        "eslint-plugin-react-hooks": "^4.3.0",
        "eslint-plugin-testing-library": "^3.9.2",
        "eslint-webpack-plugin": "^2.5.2",
        "file-loader": "6.1.1",
        "firebase": "^9.6.6",
        "formik": "^2.2.9",
        "framer-motion": "^4.1.13",
        "fs-extra": "^9.0.1",
        "history": "^5.2.0",
        "html-webpack-plugin": "4.5.0",
        "identity-obj-proxy": "3.0.0",
        "image-to-base64": "^2.2.0",
        "jest": "26.6.0",
        "jest-circus": "26.6.0",
        "jest-resolve": "26.6.0",
        "jest-watch-typeahead": "0.6.1",
        "jsonwebtoken": "^8.5.1",
        "jwt-decode": "^3.1.2",
        "lodash": "^4.17.21",
        "logrocket": "^3.0.0",
        "material-ui-popup-state": "^2.0.0",
        "mini-css-extract-plugin": "0.11.3",
        "optimize-css-assets-webpack-plugin": "5.0.4",
        "pnp-webpack-plugin": "1.6.4",
        "postcss-flexbugs-fixes": "4.2.1",
        "postcss-loader": "3.0.0",
        "postcss-normalize": "8.0.1",
        "postcss-preset-env": "6.7.0",
        "postcss-safe-parser": "5.0.2",
        "prettier": "^2.5.1",
        "prompts": "2.4.0",
        "react": "^17.0.2",
        "react-apexcharts": "^1.3.9",
        "react-app-polyfill": "^2.0.0",
        "react-beautiful-dnd": "^13.1.0",
        "react-copy-to-clipboard": "^5.0.4",
        "react-currency-format": "^1.1.0",
        "react-dev-utils": "^11.0.3",
        "react-dom": "^17.0.2",
        "react-draft-wysiwyg": "^1.14.7",
        "react-draggable": "^4.4.4",
        "react-dropzone": "^12.0.2",
        "react-error-overlay": "6.0.9",
        "react-google-recaptcha": "^2.1.0",
        "react-hook-form": "^7.26.1",
        "react-images": "^1.2.0-beta.7",
        "react-intersection-observer": "^8.33.1",
        "react-intl": "^5.24.6",
        "react-markdown": "^8.0.0",
        "react-number-format": "^4.9.1",
        "react-organizational-chart": "^2.1.1",
        "react-otp-input-rc-17": "^2.4.1-minor",
        "react-perfect-scrollbar": "^1.5.8",
        "react-quill": "^2.0.0-beta.4",
        "react-redux": "^7.2.6",
        "react-refresh": "^0.8.3",
        "react-router-dom": "^6.2.1",
        "react-slick": "^0.28.1",
        "react-spreadsheet-import": "^2.0.6",
        "react-syntax-highlighter": "^15.4.5",
        "react-timer-hook": "^3.0.5",
        "react-to-print": "^2.14.4",
        "react-window": "^1.8.6",
        "redux": "^4.1.2",
        "redux-persist": "^6.0.0",
        "remark-gfm": "^3.0.1",
        "resolve": "1.18.1",
        "resolve-url-loader": "^3.1.2",
        "sass": "^1.49.7",
        "sass-loader": "^10.0.5",
        "semver": "7.3.2",
        "slick-carousel": "^1.8.1",
        "style-loader": "1.3.0",
        "stylis-plugin-rtl": "^2.1.1",
        "terser-webpack-plugin": "4.2.3",
        "ts-pnp": "1.2.0",
        "typescript": "^4.5.5",
        "url-loader": "4.1.1",
        "uuid": "^8.3.2",
        "web-vitals": "^2.1.4",
        "webpack": "4.44.2",
        "webpack-dev-server": "3.11.1",
        "webpack-env": "^0.8.0",
        "webpack-manifest-plugin": "2.2.0",
        "workbox-webpack-plugin": "5.1.4",
        "yup": "^0.32.11"
    },
    "resolutions": {
        "@types/react": "17.0.39",
        "@types/react-dom": "17.0.11"
    },
    "scripts": {
        "start": "node scripts/start.js",
        "build": "node scripts/build.js",
        "test": "node scripts/test.js",
        "lint": "eslint . src/ --ext .js,.jsx,.ts,.tsx",
        "lint:fix": "npm run lint -- --fix"
    },
    "eslintConfig": {
        "extends": [
            "react-app",
            "react-app/jest"
        ]
    },
    "babel": {
        "presets": [
            "react-app"
        ]
    },
    "browserslist": {
        "production": [
            ">0.2%",
            "not dead",
            "not op_mini all"
        ],
        "development": [
            ">0.2%",
            "not dead",
            "not op_mini all"
        ]
    },
    "devDependencies": {},
    "jest": {
        "roots": [
            "<rootDir>/src"
        ],
        "collectCoverageFrom": [
            "src/**/*.{js,jsx,ts,tsx}",
            "!src/**/*.d.ts"
        ],
        "setupFiles": [
            "react-app-polyfill/jsdom"
        ],
        "setupFilesAfterEnv": [],
        "testMatch": [
            "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
            "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
        ],
        "testEnvironment": "jsdom",
        "testRunner": "/home/caleb/src/xxx/ui/node_modules/jest-circus/runner.js",
        "transform": {
            "^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/config/jest/babelTransform.js",
            "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
            "^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
        },
        "transformIgnorePatterns": [
            "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
            "^.+\\.module\\.(css|sass|scss)$"
        ],
        "modulePaths": [
            "/home/caleb/src/xxx/ui/src"
        ],
        "moduleNameMapper": {
            "^react-native$": "react-native-web",
            "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
        },
        "moduleFileExtensions": [
            "web.js",
            "js",
            "web.ts",
            "ts",
            "web.tsx",
            "tsx",
            "json",
            "web.jsx",
            "jsx",
            "node"
        ],
        "watchPlugins": [
            "jest-watch-typeahead/filename",
            "jest-watch-typeahead/testname"
        ],
        "resetMocks": true
    }
}

Spanish translation

Wanted to share, didn't have time to fork and add:

{
                    uploadStep: {
                        title: "Carga de Documento",
                        manifestTitle: "Los datos que se esperan:",
                        manifestDescription: "Tendrás oportunidad de renombrar o remover columnas en los siguientes pasos",
                        maxRecordsExceeded: (maxRecords: string) => `Demasiados registros, ${maxRecords} permitidos`,
                        dropzone: {
                            title: "Selecciona tu archivo .xlsx, .xls o .csv",
                            errorToastDescription: "Carga de archivo rechazada",
                            activeDropzoneTitle: "Arrastra el archivo aquí...",
                            buttonTitle: "Seleccionar",
                            loadingTitle: "Procesando..."
                        },
                        selectSheet: {
                            title: "Selecciona la pestaña a usar",
                            nextButtonTitle: "Siguiente"
                        }
                    },
                    selectHeaderStep: {
                        title: "Selección de Encabezado",
                        nextButtonTitle: "Siguiente"
                    },
                    matchColumnsStep: {
                        title: "Empata las columnas",
                        nextButtonTitle: "Siguiente",
                        userTableTitle: "Tu tabla",
                        templateTitle: "Se convertirá en",
                        selectPlaceholder: "Selecciona la columna...",
                        ignoredColumnText: "Columna Ignorada",
                        subSelectPlaceholder: "Seleccionar...",
                        matchDropdownTitle: "Empatar",
                        unmatched: "Sin empatar",
                        duplicateColumnWarningTitle: "Se de seleccionó otra columna",
                        duplicateColumnWarningDescription: "Las columnas no pueden estar duplicadas"
                    },
                    validationStep: {
                        title: "Valida la información",
                        nextButtonTitle: "Confirmar",
                        noRowsMessage: "No se encontró información",
                        noRowsMessageWhenFiltered: "La información no tiene errores",
                        discardButtonTitle: "Descartar filas seleccionadas",
                        filterSwitchTitle: "Mostrar filas con error"
                    },
                    alerts: {
                        confirmClose: {
                            headerTitle: "Salir",
                            bodyText: "Deseas cancelar la carga? La información no se guardará",
                            cancelButtonTitle: "Cancelar",
                            exitButtonTitle: "Salir"
                        },
                        submitIncomplete: {
                            headerTitle: "Se detectaron errores",
                            bodyText: "Todavía existen algunas filas con errores; los errores se ignorarán cuando se mande la información.",
                            bodyTextSubmitForbidden: "Todavía hay filas con errores.",
                            cancelButtonTitle: "Cancelar",
                            finishButtonTitle: "Enviar"
                        },
                        unmatchedRequiredFields: {
                            headerTitle: "No se empataron todas las columnas",
                            bodyText: "Hay columnas requeridas que no se han emparejado o ignorado. ¿Quieres continuar?",
                            listTitle: "Columnas no empatadas:",
                            cancelButtonTitle: "Cancelar",
                            continueButtonTitle: "Continuar"
                        },
                        toast: {
                            error: "Error"
                        }
                    }
                }

Free-form "custom" fields?

Howdy! This is a super interesting library 😄

I'm wondering if it's possible to define fields "on the fly"? Basically, I want someone with a header of "Some Field X" to create a mapping of "Some Field X" => "data in that field". The use case is to support custom fields that don't need to be defined in advance.

framer-motion package version not compatible with react-app

I get this error while installing RSI on a brand new react-app (with create react-app) or when installing on an existing project (pure JS, no TS installed) :

Can't import the named export 'Children' from non EcmaScript module (only default export is available)

Reproduction
Node : v16.15.0
package.json :

"dependencies": {
    "react": "^17.0.2",
    "react-dom": "17.0.2",
    "react-scripts": "^4.0.3",
    "react-spreadsheet-import": "^2.0.3"
  }

Possible Solution
I've already seen this PR that proposed a fix by downgrading framer-motion : #93
I tried it by forking project, changing lib version to 4.1.17 and importing on my project the new builded lib. It works perfectly

More about the error
This is a known issue due to ReactCreateApp not working with ESmodule since version5 :
reactioncommerce/reaction-component-library#399

  • A fix was proposed but still in alpha
  • A workaround is proposed but not really easy and little bit compelling while using RCA out of the box)

Downgrading framer-motion doesn't seems to have impact and may allow to easily install RSI (it should be upgraded when RCA fixed this issue)
I'd be glad to discuss it further / to propose a PR :)

PS : thx for this awesome lib, i'm considering to contribute to it ! Cheers !

Deleting rows results in future updates

I seem to have stumbled across a bug that results after deleting a row and then editing a row further in the import.

      const changes = changedData?.indexes.reduce((acc, val) => {
        const realIndex = rows[val].__index
        acc[realIndex] = rows[val]
        return acc
      }, {} as Record<number, typeof data[number]>)
      const newData = Object.assign([], data, changes)
      updateData(newData)

Let's say that there are 3 rows [0, 1, 2]. Row 0 is deleted. If 1 is changed, then data[0] should be updated because the old first record is deleted. However, acc[realIndex]= causes the changes to reference the old __index value, so data[1] gets updated and overwrites the data stored there.

Error when trying to upload a csv-file

Unfortunately i can't share the csv-file which i'm using to produce this error as it contains customer data but it crashes directly after selecting the file or dropping it in the drop-area and then it's "loading..." forever.

Screenshot 2023-02-16 at 12 32 26

Screenshot 2023-02-16 at 12 32 15

Excel file with accent not supported

Hi !
I was never able to import my Excel files (had to use GoogleSheet to convert to CSV file, Excel export uses ";" as separator)

Problem

I just figured out accents characters aren't supported (such as é, è or à)

Reproduction

I joined a simple file with an accent to allow you to reproduce.
FileWithAccent.xlsx

Solution

For now I don't have any solution, I'll dig into your code. But my first guest is that the lib RSI uses to parse XSLX and CSV files doesn't support accents by default. Maybe a little bit of configuration should do the trick

Thx for reading !

Close button disappeared

After upgrade to v3.1.0 the "close" button in the right top corner is gone. Any idea how that could be possible?

Screenshot 2023-05-25 at 09 55 10

rowHook runs on all row

As described on the doc rowHook first runs through on all row after that it supposed to run only on the row that changed but it runs on all row making it expensive function

Columns Cannot be Duplicated

I agree this is a good message, just not sure it Is this supposed to look like this? Is there a way to style this?

image

Converts long integers to e-notation

First of all great package

When I upload sequence of numbers either formatted as number or text in my office
Screenshot from 2022-09-25 22-47-25

it converts it to e-notation
Screenshot from 2022-09-25 22-47-12

is there any way to bypass or deal with it ?

minimum viable exemple with nextjs

Hello,
This might not be the place to ask, but what would be the way to use this in a nextjs app template?
I cannot find a minimum example that I could use.

I think it would be useful for a lot of beginners like me.

Error messages not shown in rows.

Error messages added through the rowHook using addError doesn't get displayed. Although, the errors seem to be there as you can't submit.

React 18 support

Hi there!
Is there any plan / are you working on supporting React 18?

Thanks!

Validation with addError doesn't seem to fire

Hi,

I'm unable to use the addError, while attempting to validate certain fields. The addError doesn't seem to work, and rows the the name equalling Lyssa aren't getting marked as rows with error when I turn on "Show only rows with error"

Any help with this?

<ReactSpreadsheetImport
                isOpen={opened}
                onClose={close}
                onSubmit={(dx) => {
                    console.log({ dx })
                }}
                fields={fields}
                allowInvalidSubmit={false}
                rowHook={(data, addError) => {
                    // Validation
                    if (data.name === "Lyssa") {
                        console.log({ data })
                        addError("name", { message: "No Lyssa allowed", level: "info" })
                    }
                    // Transformation
                    return { ...data, name: data.name }
                    // Sorry John
                }}
            />

exitBeforeEnter deprecated, must replace with mode='wait'

When using ReactSpreadsheetImport I received the following error:

Uncaught Error: Replace exitBeforeEnter with mode='wait'
at invariant (errors.mjs:19:19)
at AnimatePresence (index.mjs:73:65)
at renderWithHooks (react-dom.development.js:16305:18)
at mountIndeterminateComponent (react-dom.development.js:20069:13)
at beginWork (react-dom.development.js:21582:16)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:14)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16)
at invokeGuardedCallback (react-dom.development.js:4277:31)
at beginWork$1 (react-dom.development.js:27446:7)
at performUnitOfWork (react-dom.development.js:26552:12)

Stepper not sync with step content

Description

When the initialStepState props is different from { type: StepType.upload }, the stepper is not sync with the content of the step

Screenshots

Screenshot 1:
Capture1

Screenshot 2:
Capture2

To Reproduce

Code used for screenshot 1:

      <ReactSpreadsheetImport
        initialStepState={{
          type: StepType.selectHeader,
          data: [
            ["Josh", "2"],
            ["Charlie", "3"],
            ["Lena", "50"],
          ],
        }}
        isOpen
        onClose={() => {}}
        onSubmit={(data) => {}}
        fields={[
          { key: "name", label: "Nom", fieldType: { type: "input" } },
          { key: "age", label: "Age", fieldType: { type: "input" } },
        ]}
      />

Code used for screenshot 2:

      <ReactSpreadsheetImport
        initialStepState={{
          type: StepType.matchColumns,
          data: [
            ["Josh", "2"],
            ["Charlie", "3"],
            ["Lena", "50"],
          ],
          headerValues: ["name", "age"],
        }}
        isOpen
        onClose={() => {}}
        onSubmit={(data) => {}}
        fields={[
          { key: "name", label: "Nom", fieldType: { type: "input" } },
          { key: "age", label: "Age", fieldType: { type: "input" } },
        ]}
      />

And then click on Next button.

"Module parse failed: Unexpected token (13:123)"

I'm trying to use this excellent library, however I'm having difficulty probably with ES targets.

The full error is:

Failed to compile.

./node_modules/react-spreadsheet-import/dist/steps/MatchColumnsStep/utils/normalizeTableData.js 13:123
Module parse failed: Unexpected token (13:123)
File was processed with these loaders:
 * ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| 
|         if ("booleanMatches" in field.fieldType && Object.keys(field.fieldType).length) {
>           const booleanMatchKey = Object.keys(field.fieldType.booleanMatches || []).find(key => key.toLowerCase() === curr?.toLowerCase());
|           const booleanMatch = field.fieldType.booleanMatches?.[booleanMatchKey];
|           acc[column.value] = booleanMatchKey ? booleanMatch : normalizeCheckboxValue(curr);


Based on stackoverflow: https://stackoverflow.com/questions/63423384/you-may-need-an-additional-loader-to-handle-the-result-of-these-loaders I tried modifying our tsconfig.json. I've tried three different configurations, and all give the same error. This is on top of wiping yarn.lock, clearing yarn cache, wiping node_modules, and reinstalling everything, which was also suggested.

tsconfig.json:

Original:

{
    "compilerOptions": {
        "target": "es5",
        "module": "esnext",
        "jsx": "react-jsx",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "noUnusedLocals": false,
        "allowSyntheticDefaultImports": true,
        "noFallthroughCasesInSwitch": true,
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "noImplicitAny": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "types": ["node", "webpack-env"],
        "typeRoots": ["./types"],
        "baseUrl": "src"
    },
    "include": ["src"]
}

Attempt 1:

{
    "compilerOptions": {
        "target": "es5",
        "module": "esnext",
        "jsx": "react-jsx",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "lib": ["dom", "dom.iterable", "esnext", "ScriptHost", "ES2016.Array.Include"],
        "allowJs": true,
        "noUnusedLocals": false,
        "allowSyntheticDefaultImports": true,
        "noFallthroughCasesInSwitch": true,
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "noImplicitAny": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "types": ["node", "webpack-env"],
        "typeRoots": ["./types"],
        "baseUrl": "src"
    },
    "include": ["src"]
}

Attempt 2:

{
    "compilerOptions": {
        "target": "es6",
        "module": "esnext",
        "jsx": "react-jsx",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "lib": ["dom", "dom.iterable", "esnext", "ScriptHost", "ES2016.Array.Include"],
        "allowJs": true,
        "noUnusedLocals": false,
        "allowSyntheticDefaultImports": true,
        "noFallthroughCasesInSwitch": true,
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "noImplicitAny": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "types": ["node", "webpack-env"],
        "typeRoots": ["./types"],
        "baseUrl": "src"
    },
    "include": ["src"]
}

I'm happy to provide any more information needed, or, with guidance, make the necessary changes and issue a PR.

Can't resolve '@chakra-ui/react' , framer-motion, @chakra-ui/theme-tools

Hey ! I'm using node version 14.19.3 and npm v 6.14.17

I'm getting lots of errors in my page while compiling my react code :

Module not found: Error: Can't resolve 'framer-motion' or 'chakra-ui' in '/home/XXX/XXX/app/node_modules/@chakra-ui/menu/dist'
These errors only appears with version 14 of node, if I use version 16 it is working. However for aws servors purposes, I can't use version 16 at the moment. Is there a way to make it work ?

Also I can see a folder chakra ui being in my node-modules which is weird because the errors says it can't be found.

Thanks :)

Import date auto format

Problem :

When importing date value there is an auto-formatting doing two things :

  • formatting DD/MM/YYYY to DD/MM/YY (year is trimmed so there is no difference between 2019 and 1919)
  • removing leading 0 so 02/02/2019 is transformed to 2/2/19

First auto-formatting is a really big deal as data is now "corrupted"
/!\ uploadStepHook can't be used to bypass this behavior as data passed to this hook is already corrupted

Why it happens

I've located the formatting on mapWorkbook function during call to XLSX.utils.sheet_to_json with param raw: false (return of sheet_to_json will be formatted to string)
I've found this thread that explains how we can handle this thing with a date format
Also from the documentation of sheetJS about handling dates

Solutions proposed

  1. We could pass parameter raw: true (or allow parametrization) so values wouldn't be parsed to string and we'll be able to use Date object and parse it as we need (but this will force user to format object to string using uploadStepHook ...)
  2. We could pass parameter dateNF:'yyyy-mm-dd' with a default format so date user would be able to parse it using uploadStepHook. This format could also be a parameter

Solution n°2 sounds promising 😄

Thx for reading !
I'm really diving into the lib, thx a lot for you work ;)

MultiSelect for sheet

I really liked this library, and I would love if was possible to select multiple worksheets (the tabs) each time, in my use case this is the most important part to decide to use this library.

Support for RTL

Hey guys, the built it support for translating the UI is perfect!
How can I also change the UI direction to RTL? (Right-to-Left, for langueges such as Hebrew and Arabic)
image

Change Font Family

Hi!, Is there any way that i could change the font family used by RSI. Like, can i overwrite the chakra theme used or sth?
Ty

error - react-data-grid

Using React 17.0.2
After installing I get the error:

ERROR in ./node_modules/react-data-grid/lib/bundle.js 4:0-56

Module not found: Error: Can't resolve 'react/jsx-runtime' in 'C:\Users...\client\node_modules\react-data-grid\lib'
Did you mean 'jsx-runtime.js'?
BREAKING CHANGE: The request 'react/jsx-runtime' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '.mjs' file, or a '.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

Phone number getting parsed as a date

Have a field called phone number in this format 564-564-5644.

After getting parsed it is replaced with a date string 1900-01-00.

This only happening for .csv files if i convert csv to xlsx then it is showing correct result.

Umlauts not displayed correctly when importing .csv file

When importing a .csv file all "umlauts", like "ä, ü, ö, ..." gets display incorrect.

image-20221201-102047

I tested this with different sources, like hubspot .csv export, excel .csv export and test-data-generator csv.
All have the same outcome regarding the “umlauts”.
Maybe there's a problem with the charset?

Allow downloading template file during flow

Hello !

Description

On your example page you provide a template file BEFORE starting RSI flow.
In my use case, I have multiple RSI Modals for different data structures (each with different columns and validation rules)
I usually have a button to trigger RSI flow and I "could" add an option next to it in order to allow user to download a template file
BUT it will add an action I'm not sure my users will understand + sometime the RSI flow option is in a list of other options and it will be hard to understand that these to options are related.

Context

It seems to be a normal use-case to give the user a template file (in order to avoid avoid copy/pasting all headers)
It would be awesome to have this "download template" option on the FileSelection step !

Solutions

I can see multiple ways of doing it :

  • Have a button on the first step that allow direct download of template file (next to title ? Next to "upload file" ?)
  • Have the ability to add custom React Components on an area of the first step (next to title ?)

Plus, the "download template file" should have a specific callback that accepts a file and download it (file can then be downloading from an API or file cloud, or just generated from a JSON object)

I think this is a common use case and adding a specific button is acceptable. And if no callback is given this button is hidden to have the same behavior as the actual.

I'd be glad to discuss about it. Let me know if this is something you can consider or not.
And if you give me the specifications you are ready to add to your lib, I can create a PullRequest if you want :)

Have a good day !

onSubmit closes modal

If onSubmit is fired the modal closes automatically making it bad UX, example is that if user completes the flow frontend makes request to backend and backend checks if data is correct or is there any data that has already in the DB or something like that and if there is backend will send error to frontend and frontend will display that error notifying to user that there is an error and user will fix that data and sends again but modal closing automatically makes user to re upload sheet everytime if there is an error. I'm not an expert on this one but if there was an option to manually close modal after onSubmit that would be great!

Donate button needed

How can I donate some money to the developers? This repo is INCREDIBLE. Thank you!!!

I'm serious. Please provide an ethereum address or something!

CSV support for Hebrew

Hey guys:)
First, this package is amazing. Really, beautiful execution solving the problem.

I tried uploading a csv containing text in Hebrew and the it parsed to gibberish. From some exploration I did its some encoding problem.
Important to say that when uploading xlsx file it works perfectly!
image

Any idea what causes this and how can I fix this?

Table Styling - Colors

How do affect the styling colors in the table? I want the to be brand colors. I have altered the rsi colors completely, see here:

<ReactSpreadsheetImport
        isOpen={isOpen}
        onClose={onClose}
        onSubmit={onSubmit}
        fields={fields}
        customTheme={{
          colors: {
            rsi: {
              50: '#F0FFF4',
              100: '#C6F6D5',
              200: '#9AE6B4',
              300: '#68D391',
              400: '#48BB78',
              500: '#38A169',
              600: '#2F855A',
              700: '#276749',
              800: '#22543D',
              900: '#1C4532',
            },
            background: 'white',
          },
          components: {
            Checkbox: {
              ...checkbox,
            },
            Radio: {
              defaultProps: {
                size: 'md',
                colorScheme: 'brand',
              },
            },
            MatchColumnsStep: {
              baseStyle: {
                select: {
                  option: (provided: any, state: any) => ({
                    ...provided,
                    color: 'black',
                    bg: state.isSelected || state.isFocused ? 'brand.100' : provided.bg,
                    _hover: {
                      bg: 'brand.100',
                    },
                  }),
                } as ChakraStylesConfig<SelectOption>,
              },
            },
            ValidationStep: {
              baseStyle: {
                select: {
                  option: (provided, state) => ({
                    ...provided,
                    color: 'black',
                    bg: state.isSelected || state.isFocused ? 'secondaryBackground' : provided.bg,
                    _hover: {
                      bg: 'brand.100',
                    },
                  }),
                } as ChakraStylesConfig<SelectOption>,
              },
            },
          },
        }}
      />

image

Provide typescript types and enums

Is it possible to provide typescript support in form of types and enums, for example for field, fieldType, validation, ... ?

Would be awesome!

Customization of ValidateDataStep

Hi there,
Just wondering is there anyway to modify the ValidateDataStep as attached screenshot? Basically I need to add a check All checkbox and move the "Delete Selected Rows" button to the bottom.
Thanks,
Quentin
modify

Upload error if the file is non csv or excel

I know this package is designed for csv and excel files. But if I upload any other type of file like pdf its throwing error instead of showing some kind of message like "Please upload csv or excel file"

image

Warning: Support for defaultProps will be removed from function components in a future major release

I am hoping to use this with Next.js v13/v12. Just tried and got this error on terminal:

Warning: ReactSpreadsheetImport: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.
    at ReactSpreadsheetImport (webpack-internal:///(sc_client)/./node_modules/.pnpm/[email protected]_5dirdw6vksawy263lri76kextq/node_modules/react-spreadsheet-import/dist-commonjs/ReactSpreadsheetImport.js:30:38)
    at Home (webpack-internal:///(sc_client)/./app/(dashboard)/dashboard/page.tsx:13:17)

use custom Theme does not work everywhere?

Hi there,

i am trying to get custom themes to work with your Package. is there any description what styles are editable and what are not?
the theme i am using for example defines buttonprops. These styles are not reflected. (for example this violet "select file" button should appear white, but doesnt.

Any idea where i am not on the right track ;) ?

Thank you so much!

Back feature

Hello,

Are there any plans to support a "go back" feature ?
If so, would that be compatible with a router somehow with the browser back button ?

Would you be open to a pull request for the former feature only ?

Thanks !

Disable .csv-upload

Would be great to have a prop that disables the option to import a .csv, so that only excel-files can be uploaded.

Custom validators

Is there anyway to write custom validation logic? I see REQUIRED | REGEX | UNIQUE but was curious if there was a way for something like CUSTOM where a user defines a function for that field that does custom validation logic and returns true or false sort of like yup does with its test functionality.

Thanks!

Select options are not matched

When I have a field that is a select with options. the values from the xlsx are not automaticly matched with the field options.
not on label, and not on value.

tried both, and even played with selectHeaderStepHook and matchColumnsStepHook but no success.

fields:
image

options passed into the field:
image

tried both with typehinting it into a string and integer.

not matched:
image

any wat to automaticly match the values, based on label or value?

backgroud: we have several imports during the week. and between 10-20 product groups. and between 10-20 warehouse zones, its no fun to manually match these fields every import.

Handle wrong column number on row

Hi ! :)

Problem

When importing file with at least one row that has more columns than the selected header, I got a crash :

normalizeTableData.ts:10 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'type')
at normalizeTableData.ts:10:22
at Array.reduce (<anonymous>)
at normalizeTableData.ts:8:9
at Array.map (<anonymous>)
at normalizeTableData (normalizeTableData.ts:7:8)
at _callee2$ (MatchColumnsStep.tsx:149:22)
at tryCatch (runtime.js:63:40)
at Generator.invoke [as _invoke] (runtime.js:294:22)
at Generator.next (runtime.js:119:21)
at asyncGeneratorStep (main.2816d88b.iframe.bundle.js:1:76136)

It can't access column.type as column doesn't exist

Reproduction

Reproduced on RSI Demo page on google Chrome Version 103.0.5060.53 (Official Build) (x86_64) on macOs
Using this file content and selecting first row as header :

col_one,col_two
1,2,3

NB : if you select second row everything works fine

Solution

I think RSI should handle this case (after header selection) as its goal is file validation
Here are the solutions I can think of (no preference, this is a product decision ;) ) :

  • find maximum column and only allow selection of rows that has this maximum column number
  • ignore trailing columns as they don't follow the header definition

Thx for reading !

Buggy Table scrollbars

Problem:

When I import some files I have the scrollbars of the Table on SelectHeaderStep and ValidationStep that keep appearing and disappearing. So the component keeps resizing and looks like it's glitching

Reproduction:

Here is the content of the file I'm importing (this is not a use case but at least I can reproduce the bug with it):
{"file_uuid":"46c42960-8bb7-4209-b6a6-218b190c146e","message":"success"}
I'm on Google Chrome Version 103.0.5060.53 (official build) (x86_64) on MacOs 12.2.1
Screen resolution doesn't seem to have any influence

Investigation:

The value of grid-columns-template is constantly changing for the component Table on SelectHeaderStep (class name rdg-static)
I don't have a lot of clues of what is happening, just hoping chakra-ui is not the cause

Thx for reading !
If you need any help I'd be glad to contribute, I'll keep checking this bug on my own

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.