+
{
@@ -225,15 +227,19 @@ export default function createSequenceInputPopup(props) {
div.style.zIndex = "400000";
div.id = "sequenceInputBubble";
document.body.appendChild(div);
+ const root = createRoot(div);
+
+ const unmount = () => root.unmount();
const innerEl = (
);
- render(innerEl, div);
+ root.render(innerEl);
if (!caretEl) {
return console.error(
diff --git a/packages/ui/demo/src/examples/UploadCsvWizard.js b/packages/ui/demo/src/examples/UploadCsvWizard.js
index 3f68dd88..c76c8fca 100644
--- a/packages/ui/demo/src/examples/UploadCsvWizard.js
+++ b/packages/ui/demo/src/examples/UploadCsvWizard.js
@@ -6,7 +6,7 @@ import { FileUploadField } from "../../../src";
import DemoWrapper from "../DemoWrapper";
import { reduxForm } from "redux-form";
import { useToggle } from "../useToggle";
-import getIdOrCodeOrIndex from "../../../src/DataTable/utils/getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "../../../src/DataTable/utils";
const simpleValidateAgainst = {
fields: [{ path: "name" }, { path: "description" }, { path: "sequence" }]
diff --git a/packages/ui/demo/src/index.js b/packages/ui/demo/src/index.js
index adad2335..6e11ad22 100644
--- a/packages/ui/demo/src/index.js
+++ b/packages/ui/demo/src/index.js
@@ -25,7 +25,6 @@ import ScrollToTopDemo from "./examples/ScrollToTop";
import showAppSpinnerDemo from "./examples/showAppSpinnerDemo";
import EditableCellTable from "./examples/EditableCellTable";
import React from "react";
-import { render } from "react-dom";
import { Provider } from "react-redux";
import store from "./store";
import { FocusStyleManager } from "@blueprintjs/core";
@@ -33,6 +32,7 @@ import AdvancedOptionsDemo from "./examples/AdvancedOptionsDemo";
import FormComponents from "./examples/FormComponents";
import UploadCsvWizard from "./examples/UploadCsvWizard";
import TagSelectDemo from "./examples/TagSelectDemo";
+import { createRoot } from "react-dom/client";
FocusStyleManager.onlyShowFocusOnTabs();
@@ -261,4 +261,5 @@ const Demo = () => {
);
};
-render(, document.querySelector("#demo"));
+const root = createRoot(document.querySelector("#demo"));
+root.render();
diff --git a/packages/ui/src/DataTable/CellDragHandle.js b/packages/ui/src/DataTable/CellDragHandle.js
index 154918d7..99afd707 100644
--- a/packages/ui/src/DataTable/CellDragHandle.js
+++ b/packages/ui/src/DataTable/CellDragHandle.js
@@ -1,7 +1,6 @@
import { flatMap } from "lodash-es";
import { forEach } from "lodash-es";
import React, { useRef } from "react";
-import ReactDOM from "react-dom";
export function CellDragHandle({
thisTable,
@@ -15,7 +14,7 @@ export function CellDragHandle({
const rectangleCellPaths = useRef();
const handleDrag = useRef(e => {
- const table = ReactDOM.findDOMNode(thisTable).querySelector(".rt-table");
+ const table = thisTable.querySelector(".rt-table");
const trs = table.querySelectorAll(`.rt-tr-group.with-row-data`);
const [rowId, path] = cellId.split(":");
const selectedTr = table.querySelector(
@@ -83,7 +82,7 @@ export function CellDragHandle({
const mouseup = useRef(() => {
clearTimeout(timeoutkey.current);
- const table = ReactDOM.findDOMNode(thisTable);
+ const table = thisTable;
const trs = table.querySelectorAll(`.rt-tr-group.with-row-data`);
const [, path] = cellId.split(":");
//remove the dashed borders
diff --git a/packages/ui/src/DataTable/PagingTool.js b/packages/ui/src/DataTable/PagingTool.js
index d13335ec..f422bef5 100644
--- a/packages/ui/src/DataTable/PagingTool.js
+++ b/packages/ui/src/DataTable/PagingTool.js
@@ -5,7 +5,7 @@ import { noop, get, toInteger } from "lodash-es";
import { Button, Classes } from "@blueprintjs/core";
import { onEnterOrBlurHelper } from "../utils/handlerHelpers";
import { defaultPageSizes } from "./utils/queryParams";
-import getIdOrCodeOrIndex from "./utils/getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./utils";
function PagingInput({ disabled, onBlur, defaultPage }) {
const [page, setPage] = useState(defaultPage);
diff --git a/packages/ui/src/DataTable/index.js b/packages/ui/src/DataTable/index.js
index c49f533c..f38491b6 100644
--- a/packages/ui/src/DataTable/index.js
+++ b/packages/ui/src/DataTable/index.js
@@ -1,5 +1,4 @@
-import React from "react";
-import ReactDOM from "react-dom";
+import React, { createRef } from "react";
import {
invert,
toNumber,
@@ -27,7 +26,6 @@ import {
every
} from "lodash-es";
import joinUrl from "url-join";
-
import {
Button,
Menu,
@@ -124,6 +122,8 @@ const itemSizeEstimators = {
class DataTable extends React.Component {
constructor(props) {
super(props);
+
+ this.tableRef = createRef();
if (this.props.helperProp) {
this.props.helperProp.updateValidationHelper =
this.updateValidationHelper;
@@ -209,7 +209,6 @@ class DataTable extends React.Component {
columns: [],
fullscreen: false
};
-
static defaultProps = defaultProps;
handleEnterStartCellEdit = e => {
@@ -219,7 +218,7 @@ class DataTable extends React.Component {
flashTableBorder = () => {
try {
- const table = ReactDOM.findDOMNode(this.table);
+ const table = this.tableRef.current.tableRef;
table.classList.add("tgBorderBlue");
setTimeout(() => {
table.classList.remove("tgBorderBlue");
@@ -291,7 +290,6 @@ class DataTable extends React.Component {
reduxFormExpandedEntityIdMap,
change
} = newProps;
- const table = ReactDOM.findDOMNode(this.table);
const idMap = reduxFormSelectedEntityIdMap;
@@ -357,7 +355,8 @@ class DataTable extends React.Component {
// if not changing selectedIds then we just want to make sure selected entities
// stored in redux are in proper format
// if selected ids have changed then it will handle redux selection
- const tableScrollElement = table.getElementsByClassName("rt-table")[0];
+ const tableScrollElement =
+ this.tableRef.current.tableRef.getElementsByClassName("rt-table")[0];
const {
entities: oldEntities = [],
reduxFormSelectedEntityIdMap: oldIdMap
@@ -406,8 +405,9 @@ class DataTable extends React.Component {
const entityIndexToScrollTo = entities.findIndex(
e => e.id === idToScrollTo || e.code === idToScrollTo
);
- if (entityIndexToScrollTo === -1 || !table) return;
- const tableBody = table.querySelector(".rt-tbody");
+ if (entityIndexToScrollTo === -1 || !this.tableRef.current) return;
+ const tableBody =
+ this.tableRef.current.tableRef.querySelector(".rt-tbody");
if (!tableBody) return;
const rowEl =
tableBody.getElementsByClassName("rt-tr-group")[entityIndexToScrollTo];
@@ -504,7 +504,7 @@ class DataTable extends React.Component {
if (!entities.length && !isLoading && !showForcedHiddenColumns) {
setShowForcedHidden(true);
}
- // const table = ReactDOM.findDOMNode(this.table);
+ // const table = this.tableRef.current.tableRef;
// let theads = table.getElementsByClassName("rt-thead");
// let tbody = table.getElementsByClassName("rt-tbody")[0];
@@ -1694,9 +1694,7 @@ class DataTable extends React.Component {
)}
{
- if (n) this.table = n;
- }}
+ ref={this.tableRef}
// additionalBodyEl={}
className={classNames({
isCellEditable,
@@ -2268,7 +2266,7 @@ class DataTable extends React.Component {
refocusTable = () => {
setTimeout(() => {
- const table = ReactDOM.findDOMNode(this.table)?.closest(
+ const table = this.tableRef.current?.tableRef?.closest(
".data-table-container>div"
);
table?.focus();
@@ -2687,7 +2685,7 @@ class DataTable extends React.Component {
: isSelectedCell === PRIMARY_SELECTED_VAL) && (
{
+export const getIdOrCodeOrIndex = (record, rowIndex) => {
if (record.id || record.id === 0) {
return record.id;
} else if (record.code) {
diff --git a/packages/ui/src/DataTable/utils/handleCopyColumn.js b/packages/ui/src/DataTable/utils/handleCopyColumn.js
index d306053e..4aa04870 100644
--- a/packages/ui/src/DataTable/utils/handleCopyColumn.js
+++ b/packages/ui/src/DataTable/utils/handleCopyColumn.js
@@ -1,5 +1,5 @@
import { getAllRows } from "./getAllRows";
-import getIdOrCodeOrIndex from "./getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
import { handleCopyRows } from "./handleCopyRows";
export const handleCopyColumn = (e, cellWrapper, selectedRecords) => {
diff --git a/packages/ui/src/DataTable/utils/index.js b/packages/ui/src/DataTable/utils/index.js
index 48307406..2d85f6cb 100644
--- a/packages/ui/src/DataTable/utils/index.js
+++ b/packages/ui/src/DataTable/utils/index.js
@@ -1,7 +1,7 @@
import { isEntityClean } from "./isEntityClean";
import { getSelectedRowsFromEntities } from "./selection";
import { removeCleanRows } from "./removeCleanRows";
-import getIdOrCodeOrIndex from "./getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
import computePresets from "./computePresets";
import { getRecordsFromIdMap } from "./withSelectedEntities";
import { formatPasteData } from "./formatPasteData";
diff --git a/packages/ui/src/DataTable/utils/isEntityClean.js b/packages/ui/src/DataTable/utils/isEntityClean.js
index acf34e37..f9d507d4 100644
--- a/packages/ui/src/DataTable/utils/isEntityClean.js
+++ b/packages/ui/src/DataTable/utils/isEntityClean.js
@@ -1,13 +1,15 @@
export function isEntityClean(e) {
+ if (typeof e !== "object" || e === null) {
+ return true; // or return false depending on what you want for non-object inputs
+ }
let isClean = true;
- e.some((val, key) => {
- if (key === "id") return false;
- if (key === "_isClean") return false;
+ for (const [key, val] of Object.entries(e)) {
+ if (key === "id") continue;
+ if (key === "_isClean") continue;
if (val) {
isClean = false;
- return true;
+ break;
}
- return false;
- });
+ }
return isClean;
}
diff --git a/packages/ui/src/DataTable/utils/removeCleanRows.js b/packages/ui/src/DataTable/utils/removeCleanRows.js
index b95f5552..5958166a 100644
--- a/packages/ui/src/DataTable/utils/removeCleanRows.js
+++ b/packages/ui/src/DataTable/utils/removeCleanRows.js
@@ -1,7 +1,7 @@
import { isEntityClean } from "./isEntityClean";
import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
-export function removeCleanRows(reduxFormEntities, reduxFormCellValidation) {
+export const removeCleanRows = (reduxFormEntities, reduxFormCellValidation) => {
const toFilterOut = {};
const entsToUse = (reduxFormEntities || []).filter(e => {
if (!(e._isClean || isEntityClean(e))) return true;
@@ -12,11 +12,11 @@ export function removeCleanRows(reduxFormEntities, reduxFormCellValidation) {
});
const validationToUse = {};
- reduxFormCellValidation.forEach((v, k) => {
+ Object.entries(reduxFormCellValidation || {}).forEach(([k, v]) => {
const [rowId] = k.split(":");
if (!toFilterOut[rowId]) {
validationToUse[k] = v;
}
});
return { entsToUse, validationToUse };
-}
+};
diff --git a/packages/ui/src/DataTable/utils/rowClick.js b/packages/ui/src/DataTable/utils/rowClick.js
index 8c4c5570..e4ee123a 100644
--- a/packages/ui/src/DataTable/utils/rowClick.js
+++ b/packages/ui/src/DataTable/utils/rowClick.js
@@ -1,6 +1,6 @@
import { isEmpty, forEach, range } from "lodash-es";
import { getSelectedRowsFromEntities } from "./selection";
-import getIdOrCodeOrIndex from "./getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
import { getRecordsFromIdMap } from "./withSelectedEntities";
export default function rowClick(e, rowInfo, entities, props) {
diff --git a/packages/ui/src/DataTable/utils/selection.js b/packages/ui/src/DataTable/utils/selection.js
index ea4a08d8..39757606 100644
--- a/packages/ui/src/DataTable/utils/selection.js
+++ b/packages/ui/src/DataTable/utils/selection.js
@@ -1,4 +1,4 @@
-import getIdOrCodeOrIndex from "./getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
export const getSelectedRowsFromEntities = (entities, idMap) => {
if (!idMap) return [];
diff --git a/packages/ui/src/DataTable/utils/utils.js b/packages/ui/src/DataTable/utils/utils.js
index b15021e3..fe7b4d1c 100644
--- a/packages/ui/src/DataTable/utils/utils.js
+++ b/packages/ui/src/DataTable/utils/utils.js
@@ -1,4 +1,4 @@
-import getIdOrCodeOrIndex from "./getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./getIdOrCodeOrIndex";
export const getFieldPathToIndex = schema => {
const fieldToIndex = {};
diff --git a/packages/ui/src/DataTable/validateTableWideErrors.js b/packages/ui/src/DataTable/validateTableWideErrors.js
index 11c2eb93..c29a36b2 100644
--- a/packages/ui/src/DataTable/validateTableWideErrors.js
+++ b/packages/ui/src/DataTable/validateTableWideErrors.js
@@ -1,4 +1,4 @@
-import getIdOrCodeOrIndex from "./utils/getIdOrCodeOrIndex";
+import { getIdOrCodeOrIndex } from "./utils";
import { getCellVal } from "./getCellVal";
import { forEach, isArray } from "lodash-es";
import { startCase } from "lodash-es";
diff --git a/packages/ui/src/FillWindow.js b/packages/ui/src/FillWindow.js
index ab25adc5..c333c342 100644
--- a/packages/ui/src/FillWindow.js
+++ b/packages/ui/src/FillWindow.js
@@ -1,6 +1,5 @@
-import React from "react";
+import React, { createPortal } from "react";
import { isFunction } from "lodash-es";
-import reactDom from "react-dom";
import rerenderOnWindowResize from "./rerenderOnWindowResize";
import "./FillWindow.css";
@@ -63,7 +62,7 @@ export default class FillWindow extends React.Component {
: this.props.children}
);
- if (asPortal) return reactDom.createPortal(inner, window.document.body);
+ if (asPortal) return createPortal(inner, window.document.body);
return inner;
}
}
diff --git a/packages/ui/src/FormComponents/Uploader.js b/packages/ui/src/FormComponents/Uploader.js
index e6c08cfa..2aa1b6aa 100644
--- a/packages/ui/src/FormComponents/Uploader.js
+++ b/packages/ui/src/FormComponents/Uploader.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useState } from "react";
+import React, { useEffect, useMemo, useRef, useState } from "react";
import {
Button,
Callout,
@@ -16,7 +16,6 @@ import classnames from "classnames";
import { nanoid } from "nanoid";
import papaparse, { unparse } from "papaparse";
import downloadjs from "downloadjs";
-import { configure, makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import UploadCsvWizardDialog, {
SimpleInsertDataDialog
@@ -30,7 +29,7 @@ import {
removeExt
} from "@teselagen/file-utils";
import tryToMatchSchemas from "./tryToMatchSchemas";
-import { forEach, isArray, isFunction, isPlainObject, noop } from "lodash-es";
+import { isArray, isFunction, isPlainObject, noop } from "lodash-es";
import { flatMap } from "lodash-es";
import urljoin from "url-join";
import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
@@ -38,14 +37,12 @@ import writeXlsxFile from "write-excel-file";
import { startCase } from "lodash-es";
import { getNewName } from "./getNewName";
import { isObject } from "lodash-es";
-import { connect } from "react-redux";
+import { useDispatch } from "react-redux";
import { initialize } from "redux-form";
import classNames from "classnames";
-import { compose } from "recompose";
import convertSchema from "../DataTable/utils/convertSchema";
import { LoadingDots } from "./LoadingDots";
-configure({ isolateGlobalState: true });
const helperText = [
`How to Use This Template to Upload New Data`,
`1. Go to the first tab and delete the example data.`,
@@ -64,58 +61,110 @@ const helperSchema = [
}
];
-class ValidateAgainstSchema {
- fields = [];
-
- constructor() {
- makeObservable(this, {
- fields: observable.shallow
- });
+const setValidateAgainstSchema = newValidateAgainstSchema => {
+ if (!newValidateAgainstSchema) {
+ return [];
}
-
- setValidateAgainstSchema(newValidateAgainstSchema) {
- if (!newValidateAgainstSchema) {
- this.fields = [];
- return;
- }
- const schema = convertSchema(newValidateAgainstSchema);
- if (
- schema.fields.some(f => {
- if (f.path === "id") {
- return true;
- }
- return false;
- })
- ) {
- throw new Error(
- `Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
- );
- }
- forEach(schema, (v, k) => {
- this[k] = v;
- });
+ const schema = convertSchema(newValidateAgainstSchema);
+ if (
+ schema.fields.some(f => {
+ if (f.path === "id") {
+ return true;
+ }
+ return false;
+ })
+ ) {
+ throw new Error(
+ `Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
+ );
}
-}
-
-// autorun(() => {
-// console.log(
-// `validateAgainstSchemaStore?.fields:`,
-// JSON.stringify(validateAgainstSchemaStore?.fields, null, 4)
-// );
-// });
-// validateAgainstSchemaStore.fields = ["hahah"];
-// validateAgainstSchemaStore.fields.push("yaa");
+ return schema;
+};
-// const validateAgainstSchema = observable.shallow({
-// fields: []
-// })
+const emptyPromise = Promise.resolve.bind(Promise);
-// validateAgainstSchema.fields = ["hahah"];
+const InnerDropZone = ({
+ getRootProps,
+ getInputProps,
+ isDragAccept,
+ isDragReject,
+ isDragActive,
+ className,
+ minimal,
+ dropzoneDisabled,
+ contentOverride,
+ simpleAccept,
+ innerIcon,
+ innerText,
+ validateAgainstSchema,
+ handleManuallyEnterData,
+ noBuildCsvOption,
+ showFilesCount,
+ fileList
+ // isDragActive
+ // isDragReject
+ // isDragAccept
+}) => (
+
+
+
+ {contentOverride || (
+
+ {innerIcon ||
}
+ {innerText || (minimal ? "Upload" : "Click or drag to upload")}
+ {validateAgainstSchema && !noBuildCsvOption && (
+
+ ...or {manualEnterMessage}
+ {/*
+ {manualEnterSubMessage}
+
*/}
+
+ )}
+
+ )}
+
-// wink wink
-const emptyPromise = Promise.resolve.bind(Promise);
+ {showFilesCount ? (
+
+ Files: {fileList ? fileList.length : 0}
+
+ ) : null}
+
+);
-function UploaderInner({
+const UploaderInner = ({
accept: __accept,
contentOverride: maybeContentOverride,
innerIcon,
@@ -141,14 +190,13 @@ function UploaderInner({
autoUnzip,
disabled: _disabled,
noBuildCsvOption,
- initializeForm,
showFilesCount,
threeDotMenuItems,
onPreviewClick
-}) {
+}) => {
+ const dispatch = useDispatch();
let dropzoneDisabled = _disabled;
let _accept = __accept;
- const validateAgainstSchemaStore = useRef(new ValidateAgainstSchema());
const [acceptLoading, setAcceptLoading] = useState();
const [resolvedAccept, setResolvedAccept] = useState();
if (resolvedAccept) {
@@ -169,9 +217,11 @@ function UploaderInner({
);
}
}, [__accept, isAcceptPromise]);
+
if (isAcceptPromise && !resolvedAccept) {
_accept = [];
}
+
if (acceptLoading) dropzoneDisabled = true;
const accept = !_accept
? undefined
@@ -188,16 +238,10 @@ function UploaderInner({
_validateAgainstSchema ||
accept?.find?.(a => a?.validateAgainstSchema)?.validateAgainstSchema;
- useEffect(() => {
- // validateAgainstSchema
- validateAgainstSchemaStore.current.setValidateAgainstSchema(
- validateAgainstSchemaToUse
- );
- }, [validateAgainstSchemaToUse]);
- let validateAgainstSchema;
- if (validateAgainstSchemaToUse) {
- validateAgainstSchema = validateAgainstSchemaStore.current;
- }
+ const validateAgainstSchema = useMemo(
+ () => setValidateAgainstSchema(validateAgainstSchemaToUse),
+ [validateAgainstSchemaToUse]
+ );
if (
(validateAgainstSchema || autoUnzip) &&
@@ -215,6 +259,7 @@ function UploaderInner({
const { showDialogPromise: showUploadCsvWizardDialog, comp } = useDialog({
ModalComponent: UploadCsvWizardDialog
});
+
const { showDialogPromise: showSimpleInsertDataDialog, comp: comp2 } =
useDialog({
ModalComponent: SimpleInsertDataDialog
@@ -553,7 +598,7 @@ function UploaderInner({
}
{...getFileDownloadAttr(exampleFile)}
key={i}
- >
+ />
);
}
)}
@@ -619,7 +664,7 @@ function UploaderInner({
}}
size={10}
icon="download"
- >
+ />
)}
@@ -631,7 +676,8 @@ function UploaderInner({
// make the dots below "load"
<>
- Accept Loading
+ Accept Loading
+
>
) : (
<>Accepts {simpleAccept}>
@@ -650,135 +696,135 @@ function UploaderInner({
.join(", ")
: undefined
}
- {...{
- onDrop: async (_acceptedFiles, rejectedFiles) => {
- let acceptedFiles = [];
- for (const file of _acceptedFiles) {
- if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
- const files = await filterFilesInZip(
- file,
- simpleAccept
- ?.split(", ")
- ?.map(a => (a.startsWith(".") ? a : "." + a)) || []
- );
- acceptedFiles.push(...files.map(f => f.originFileObj));
- } else {
- acceptedFiles.push(file);
- }
- }
- cleanupFiles();
- if (rejectedFiles.length) {
- let msg = "";
- rejectedFiles.forEach(file => {
- if (msg) msg += "\n";
- msg +=
- `${file.file.name}: ` +
- file.errors.map(err => err.message).join(", ");
- });
- window.toastr &&
- window.toastr.warning(
-
{msg}
- );
+ onDrop={async (_acceptedFiles, rejectedFiles) => {
+ let acceptedFiles = [];
+ for (const file of _acceptedFiles) {
+ if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
+ const files = await filterFilesInZip(
+ file,
+ simpleAccept
+ ?.split(", ")
+ ?.map(a => (a.startsWith(".") ? a : "." + a)) || []
+ );
+ acceptedFiles.push(...files.map(f => f.originFileObj));
+ } else {
+ acceptedFiles.push(file);
}
- if (!acceptedFiles.length) return;
- setLoading(true);
- acceptedFiles = trimFiles(acceptedFiles, fileLimit);
-
- acceptedFiles.forEach(file => {
- file.preview = URL.createObjectURL(file);
- file.loading = true;
- if (!file.id) {
- file.id = nanoid();
- }
- filesToClean.current.push(file);
+ }
+ cleanupFiles();
+ if (rejectedFiles.length) {
+ let msg = "";
+ rejectedFiles.forEach(file => {
+ if (msg) msg += "\n";
+ msg +=
+ `${file.file.name}: ` +
+ file.errors.map(err => err.message).join(", ");
});
-
- if (readBeforeUpload) {
- acceptedFiles = await Promise.all(
- acceptedFiles.map(file => {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsText(file, "UTF-8");
- reader.onload = evt => {
- file.parsedString = evt.target.result;
- resolve(file);
- };
- reader.onerror = err => {
- console.error("err:", err);
- reject(err);
- };
- });
- })
+ window.toastr &&
+ window.toastr.warning(
+
{msg}
);
+ }
+ if (!acceptedFiles.length) return;
+ setLoading(true);
+ acceptedFiles = trimFiles(acceptedFiles, fileLimit);
+
+ acceptedFiles.forEach(file => {
+ file.preview = URL.createObjectURL(file);
+ file.loading = true;
+ if (!file.id) {
+ file.id = nanoid();
}
- const cleanedAccepted = acceptedFiles.map(file => {
- return {
- originFileObj: file,
- originalFileObj: file,
- id: file.id,
- lastModified: file.lastModified,
- lastModifiedDate: file.lastModifiedDate,
- loading: file.loading,
- name: file.name,
- preview: file.preview,
- size: file.size,
- type: file.type,
- ...(file.parsedString
- ? { parsedString: file.parsedString }
- : {})
- };
- });
+ filesToClean.current.push(file);
+ });
- const toKeep = [];
- if (validateAgainstSchema) {
- const filesWIssues = [];
- const filesWOIssues = [];
- for (const [i, file] of cleanedAccepted.entries()) {
- if (isCsvOrExcelFile(file)) {
- let parsedF;
- try {
- parsedF = await parseCsvOrExcelFile(file, {
- csvParserOptions: isFunction(
- validateAgainstSchema.csvParserOptions
- )
- ? validateAgainstSchema.csvParserOptions({
- validateAgainstSchema
- })
- : validateAgainstSchema.csvParserOptions
- });
- } catch (error) {
- console.error("error:", error);
- window.toastr &&
- window.toastr.error(
- `There was an error parsing your file. Please try again. ${
- error.message || error
- }`
- );
- return;
- }
+ if (readBeforeUpload) {
+ acceptedFiles = await Promise.all(
+ acceptedFiles.map(file => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsText(file, "UTF-8");
+ reader.onload = evt => {
+ file.parsedString = evt.target.result;
+ resolve(file);
+ };
+ reader.onerror = err => {
+ console.error("err:", err);
+ reject(err);
+ };
+ });
+ })
+ );
+ }
+ const cleanedAccepted = acceptedFiles.map(file => {
+ return {
+ originFileObj: file,
+ originalFileObj: file,
+ id: file.id,
+ lastModified: file.lastModified,
+ lastModifiedDate: file.lastModifiedDate,
+ loading: file.loading,
+ name: file.name,
+ preview: file.preview,
+ size: file.size,
+ type: file.type,
+ ...(file.parsedString
+ ? { parsedString: file.parsedString }
+ : {})
+ };
+ });
- const {
- csvValidationIssue: _csvValidationIssue,
- matchedHeaders,
- userSchema,
- searchResults,
- ignoredHeadersMsg
- } = await tryToMatchSchemas({
- incomingData: parsedF.data,
- validateAgainstSchema
+ const toKeep = [];
+ if (validateAgainstSchema) {
+ const filesWIssues = [];
+ const filesWOIssues = [];
+ for (const [i, file] of cleanedAccepted.entries()) {
+ if (isCsvOrExcelFile(file)) {
+ let parsedF;
+ try {
+ parsedF = await parseCsvOrExcelFile(file, {
+ csvParserOptions: isFunction(
+ validateAgainstSchema.csvParserOptions
+ )
+ ? validateAgainstSchema.csvParserOptions({
+ validateAgainstSchema
+ })
+ : validateAgainstSchema.csvParserOptions
});
- if (userSchema?.userData?.length === 0) {
- console.error(
- `userSchema, parsedF.data:`,
- userSchema,
- parsedF.data
+ } catch (error) {
+ console.error("error:", error);
+ window.toastr &&
+ window.toastr.error(
+ `There was an error parsing your file. Please try again. ${
+ error.message || error
+ }`
);
- } else {
- toKeep.push(file);
- let csvValidationIssue = _csvValidationIssue;
- if (csvValidationIssue) {
- if (isObject(csvValidationIssue)) {
- initializeForm(
+ return;
+ }
+
+ const {
+ csvValidationIssue: _csvValidationIssue,
+ matchedHeaders,
+ userSchema,
+ searchResults,
+ ignoredHeadersMsg
+ } = await tryToMatchSchemas({
+ incomingData: parsedF.data,
+ validateAgainstSchema
+ });
+ if (userSchema?.userData?.length === 0) {
+ console.error(
+ `userSchema, parsedF.data:`,
+ userSchema,
+ parsedF.data
+ );
+ } else {
+ toKeep.push(file);
+ let csvValidationIssue = _csvValidationIssue;
+ if (csvValidationIssue) {
+ if (isObject(csvValidationIssue)) {
+ dispatch(
+ initialize(
`editableCellTable${
cleanedAccepted.length > 1 ? `-${i}` : ""
}`,
@@ -790,149 +836,142 @@ function UploaderInner({
keepValues: true,
updateUnregisteredFields: true
}
+ )
+ );
+ const err = Object.values(csvValidationIssue)[0];
+ // csvValidationIssue = `It looks like there was an error with your data - \n\n${
+ // err && err.message ? err.message : err
+ // }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
+ const errMsg = err && err.message ? err.message : err;
+ if (isPlainObject(errMsg)) {
+ throw new Error(
+ `errMsg is an object ${JSON.stringify(
+ errMsg,
+ null,
+ 4
+ )}`
);
- const err = Object.values(csvValidationIssue)[0];
- // csvValidationIssue = `It looks like there was an error with your data - \n\n${
- // err && err.message ? err.message : err
- // }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
- const errMsg =
- err && err.message ? err.message : err;
- if (isPlainObject(errMsg)) {
- throw new Error(
- `errMsg is an object ${JSON.stringify(
- errMsg,
- null,
- 4
- )}`
- );
- }
- csvValidationIssue = (
+ }
+ csvValidationIssue = (
+
-
- It looks like there was an error with your
- data (Correct on the Review Data page):
-
-
{errMsg}
-
- Please review your headers and then correct
- any errors on the next page.
-
+ It looks like there was an error with your data
+ (Correct on the Review Data page):
- );
- }
- filesWIssues.push({
- file,
- csvValidationIssue,
- ignoredHeadersMsg,
- matchedHeaders,
- userSchema,
- searchResults
- });
- } else {
- filesWOIssues.push({
- file,
- csvValidationIssue,
- ignoredHeadersMsg,
- matchedHeaders,
- userSchema,
- searchResults
- });
- const newFileName = removeExt(file.name) + `.csv`;
-
- const { newFile, cleanedEntities } = getNewCsvFile(
- userSchema.userData,
- newFileName
+
{errMsg}
+
+ Please review your headers and then correct any
+ errors on the next page.
+
+
);
-
- file.meta = parsedF.meta;
- file.hasEditClick = true;
- file.parsedData = cleanedEntities;
- file.name = newFileName;
- file.originFileObj = newFile;
- file.originalFileObj = newFile;
}
- }
- } else {
- toKeep.push(file);
- }
- }
- if (filesWIssues.length) {
- const { file } = filesWIssues[0];
- const allFiles = [...filesWIssues, ...filesWOIssues];
- const doAllFilesHaveSameHeaders = allFiles.every(f => {
- if (f.userSchema.fields && f.userSchema.fields.length) {
- return f.userSchema.fields.every((h, i) => {
- return (
- h.path === allFiles[0].userSchema.fields[i].path
- );
+ filesWIssues.push({
+ file,
+ csvValidationIssue,
+ ignoredHeadersMsg,
+ matchedHeaders,
+ userSchema,
+ searchResults
});
- }
- return false;
- });
- const multipleFiles = allFiles.length > 1;
- const { res } = await showUploadCsvWizardDialog(
- "onUploadWizardFinish",
- {
- dialogProps: {
- title: `Fix Up File${multipleFiles ? "s" : ""} ${
- multipleFiles
- ? ""
- : file.name
- ? `"${file.name}"`
- : ""
- }`
- },
- doAllFilesHaveSameHeaders,
- filesWIssues: allFiles,
- validateAgainstSchema
- }
- );
+ } else {
+ filesWOIssues.push({
+ file,
+ csvValidationIssue,
+ ignoredHeadersMsg,
+ matchedHeaders,
+ userSchema,
+ searchResults
+ });
+ const newFileName = removeExt(file.name) + `.csv`;
- if (!res) {
- window.toastr.warning(`File Upload Aborted`);
- return;
- } else {
- allFiles.forEach(({ file }, i) => {
- const newEntities = res[i];
- // const newFileName = removeExt(file.name) + `_updated.csv`;
- //swap out file with a new csv file
const { newFile, cleanedEntities } = getNewCsvFile(
- newEntities,
- file.name
+ userSchema.userData,
+ newFileName
);
+ file.meta = parsedF.meta;
file.hasEditClick = true;
file.parsedData = cleanedEntities;
- // file.name = newFileName;
+ file.name = newFileName;
file.originFileObj = newFile;
file.originalFileObj = newFile;
- });
- setTimeout(() => {
- //inside a timeout for cypress purposes
- window.toastr.success(
- `Added Fixed Up File${
- allFiles.length > 1 ? "s" : ""
- } ${allFiles.map(({ file }) => file.name).join(", ")}`
- );
- }, 200);
+ }
}
+ } else {
+ toKeep.push(file);
}
- } else {
- toKeep.push(...cleanedAccepted);
}
+ if (filesWIssues.length) {
+ const { file } = filesWIssues[0];
+ const allFiles = [...filesWIssues, ...filesWOIssues];
+ const doAllFilesHaveSameHeaders = allFiles.every(f => {
+ if (f.userSchema.fields && f.userSchema.fields.length) {
+ return f.userSchema.fields.every((h, i) => {
+ return h.path === allFiles[0].userSchema.fields[i].path;
+ });
+ }
+ return false;
+ });
+ const multipleFiles = allFiles.length > 1;
+ const { res } = await showUploadCsvWizardDialog(
+ "onUploadWizardFinish",
+ {
+ dialogProps: {
+ title: `Fix Up File${multipleFiles ? "s" : ""} ${
+ multipleFiles ? "" : file.name ? `"${file.name}"` : ""
+ }`
+ },
+ doAllFilesHaveSameHeaders,
+ filesWIssues: allFiles,
+ validateAgainstSchema
+ }
+ );
- if (toKeep.length === 0) {
- window.toastr &&
- window.toastr.error(
- `It looks like there wasn't any data in your file. Please add some data and try again`
- );
+ if (!res) {
+ window.toastr.warning(`File Upload Aborted`);
+ return;
+ } else {
+ allFiles.forEach(({ file }, i) => {
+ const newEntities = res[i];
+ // const newFileName = removeExt(file.name) + `_updated.csv`;
+ //swap out file with a new csv file
+ const { newFile, cleanedEntities } = getNewCsvFile(
+ newEntities,
+ file.name
+ );
+
+ file.hasEditClick = true;
+ file.parsedData = cleanedEntities;
+ // file.name = newFileName;
+ file.originFileObj = newFile;
+ file.originalFileObj = newFile;
+ });
+ setTimeout(() => {
+ //inside a timeout for cypress purposes
+ window.toastr.success(
+ `Added Fixed Up File${
+ allFiles.length > 1 ? "s" : ""
+ } ${allFiles.map(({ file }) => file.name).join(", ")}`
+ );
+ }, 200);
+ }
}
- const cleanedFileList = trimFiles(
- [...toKeep, ...fileListToUse],
- fileLimit
- );
- handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
+ } else {
+ toKeep.push(...cleanedAccepted);
}
+
+ if (toKeep.length === 0) {
+ window.toastr &&
+ window.toastr.error(
+ `It looks like there wasn't any data in your file. Please add some data and try again`
+ );
+ }
+ const cleanedFileList = trimFiles(
+ [...toKeep, ...fileListToUse],
+ fileLimit
+ );
+ handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
}}
{...dropzoneProps}
>
@@ -942,71 +981,26 @@ function UploaderInner({
isDragAccept,
isDragReject,
isDragActive
- // isDragActive
- // isDragReject
- // isDragAccept
}) => (
-
-
-
- {contentOverride || (
-
- {innerIcon || (
-
- )}
- {innerText ||
- (minimal ? "Upload" : "Click or drag to upload")}
- {validateAgainstSchema && !noBuildCsvOption && (
-
- ...or {manualEnterMessage}
- {/*
- {manualEnterSubMessage}
-
*/}
-
- )}
-
- )}
-
-
- {showFilesCount ? (
-
- Files: {fileList ? fileList.length : 0}
-
- ) : null}
-
+
)}
{/* {validateAgainstSchema &&
} */}
@@ -1188,12 +1182,9 @@ function UploaderInner({
>
);
-}
+};
-const Uploader = compose(
- connect(undefined, { initializeForm: initialize }),
- observer
-)(UploaderInner);
+const Uploader = observer(UploaderInner);
export default Uploader;
diff --git a/packages/ui/src/UploadCsvWizard.js b/packages/ui/src/UploadCsvWizard.js
index 92595324..0cf8f928 100644
--- a/packages/ui/src/UploadCsvWizard.js
+++ b/packages/ui/src/UploadCsvWizard.js
@@ -1,4 +1,4 @@
-import React, { useRef, useState } from "react";
+import React, { useRef, useState, useEffect } from "react";
import { reduxForm, change, formValueSelector, destroy } from "redux-form";
import { Callout, Icon, Intent, Tab, Tabs } from "@blueprintjs/core";
import immer from "immer";
@@ -12,10 +12,11 @@ import { tgFormValueSelector } from "./utils/tgFormValues";
import { some } from "lodash-es";
import { times } from "lodash-es";
import DialogFooter from "./DialogFooter";
-import DataTable, { removeCleanRows } from "./DataTable";
+import DataTable from "./DataTable";
+import { removeCleanRows } from "./DataTable/utils";
import wrapDialog from "./wrapDialog";
import { omit } from "lodash-es";
-import { connect } from "react-redux";
+import { connect, useDispatch, useSelector } from "react-redux";
import { MatchHeaders } from "./MatchHeaders";
import { isEmpty } from "lodash-es";
import { addSpecialPropToAsyncErrs } from "./FormComponents/tryToMatchSchemas";
@@ -35,64 +36,62 @@ const UploadCsvWizardDialog = compose(
reduxForm({
form: "UploadCsvWizardDialog"
}),
- connect(
- (state, props) => {
- if (props.filesWIssues.length > 0) {
- const reduxFormEntitiesArray = [];
- const finishedFiles = props.filesWIssues.map((f, i) => {
- const { reduxFormEntities, reduxFormCellValidation } =
- formValueSelector(`editableCellTable-${i}`)(
- state,
- "reduxFormEntities",
- "reduxFormCellValidation"
- );
- reduxFormEntitiesArray.push(reduxFormEntities);
- const { entsToUse, validationToUse } = removeCleanRows(
- reduxFormEntities,
- reduxFormCellValidation
- );
- return (
- entsToUse &&
- entsToUse.length &&
- !some(validationToUse, v => v) &&
- entsToUse
- );
- });
- return {
- reduxFormEntitiesArray,
- finishedFiles
- };
- }
- },
- { changeForm: change, destroyForms: destroy }
- ),
observer
)(function UploadCsvWizardDialogOuter({
- validateAgainstSchema,
- reduxFormEntitiesArray,
- filesWIssues: _filesWIssues,
- finishedFiles,
- onUploadWizardFinish,
- doAllFilesHaveSameHeaders,
- destroyForms,
csvValidationIssue,
+ doAllFilesHaveSameHeaders,
+ filesWIssues: _filesWIssues,
+ flippedMatchedHeaders,
ignoredHeadersMsg,
- searchResults,
matchedHeaders,
+ onUploadWizardFinish,
+ searchResults,
userSchema,
- flippedMatchedHeaders,
- changeForm
+ validateAgainstSchema
}) {
+ const dispatch = useDispatch();
// will unmount state hook
- React.useEffect(() => {
+ useEffect(() => {
return () => {
- destroyForms(
- "editableCellTable",
- ...times(_filesWIssues.length, i => `editableCellTable-${i}`)
+ dispatch(
+ destroy(
+ "editableCellTable",
+ ...times(_filesWIssues.length, i => `editableCellTable-${i}`)
+ )
);
};
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [_filesWIssues.length, dispatch]);
+
+ const changeForm = (...args) => dispatch(change(...args));
+ const { reduxFormEntitiesArray, finishedFiles } = useSelector(state => {
+ if (_filesWIssues.length > 0) {
+ const reduxFormEntitiesArray = [];
+ const finishedFiles = _filesWIssues.map((f, i) => {
+ const { reduxFormEntities, reduxFormCellValidation } =
+ formValueSelector(`editableCellTable-${i}`)(
+ state,
+ "reduxFormEntities",
+ "reduxFormCellValidation"
+ );
+ reduxFormEntitiesArray.push(reduxFormEntities);
+ const { entsToUse, validationToUse } = removeCleanRows(
+ reduxFormEntities,
+ reduxFormCellValidation
+ );
+ return (
+ entsToUse &&
+ entsToUse.length &&
+ !some(validationToUse, v => v) &&
+ entsToUse
+ );
+ });
+ return {
+ reduxFormEntitiesArray,
+ finishedFiles
+ };
+ }
+ });
+
const [hasSubmittedOuter, setSubmittedOuter] = useState();
const [steps, setSteps] = useState(getInitialSteps(true));
@@ -118,7 +117,6 @@ const UploadCsvWizardDialog = compose(
>
{filesWIssues.map((f, i) => {
const isGood = finishedFiles[i];
-
const isThisTheLastBadFile = finishedFiles.every((ff, j) => {
if (i === j) {
return true;
@@ -135,108 +133,98 @@ const UploadCsvWizardDialog = compose(