From ed085389669509768882bb1bff7d4b0d6cda35d9 Mon Sep 17 00:00:00 2001 From: Thomas Willheim Date: Tue, 7 Jan 2025 13:54:14 -0800 Subject: [PATCH] Refactor file imports and update version; add DefaultVal demo component --- output.json | 5 + packages/file-utils/package.json | 2 +- packages/file-utils/src/file-utils.js | 14 ++- packages/ove/package.json | 2 +- packages/ove/src/AutoAnnotate.js | 2 +- packages/ove/src/fileUtils.js | 103 -------------------- packages/ui/demo/src/examples/DefaultVal.js | 37 +++++++ packages/ui/demo/src/index.js | 4 + packages/ui/package.json | 2 +- packages/ui/src/FormComponents/index.js | 20 +++- 10 files changed, 78 insertions(+), 113 deletions(-) delete mode 100644 packages/ove/src/fileUtils.js create mode 100644 packages/ui/demo/src/examples/DefaultVal.js diff --git a/output.json b/output.json index 3577132f..81fe7f27 100644 --- a/output.json +++ b/output.json @@ -878,6 +878,11 @@ "source": "ove", "target": "shared-demo", "type": "static" + }, + { + "source": "ove", + "target": "file-utils", + "type": "static" } ], "ui": [ diff --git a/packages/file-utils/package.json b/packages/file-utils/package.json index f530a96c..7ff37309 100644 --- a/packages/file-utils/package.json +++ b/packages/file-utils/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/file-utils", - "version": "0.3.16", + "version": "0.3.17", "type": "module", "dependencies": { "bluebird": "^3.7.2", diff --git a/packages/file-utils/src/file-utils.js b/packages/file-utils/src/file-utils.js index 3c6b9c9b..bdb98755 100644 --- a/packages/file-utils/src/file-utils.js +++ b/packages/file-utils/src/file-utils.js @@ -74,8 +74,7 @@ export const extractZipFiles = async allFiles => { const defaultCsvParserOptions = { header: true, - skipEmptyLines: "greedy", - trimHeaders: true + skipEmptyLines: "greedy" }; export const setupCsvParserOptions = (parserOptions = {}) => { const { @@ -123,6 +122,17 @@ export const setupCsvParserOptions = (parserOptions = {}) => { return transHeader; }; } + // tnw: the papaparse trimHeaders option was removed so we need to trim headers manually + const transformToAlwaysRun = header => header.trim(); + if (parserOptions.transformHeader) { + const existingTransformHeader = parserOptions.transformHeader; + papaParseOpts.transformHeader = header => { + const trimmedHeader = transformToAlwaysRun(header); + return existingTransformHeader(trimmedHeader); + }; + } else { + papaParseOpts.transformHeader = transformToAlwaysRun; + } return papaParseOpts; }; diff --git a/packages/ove/package.json b/packages/ove/package.json index 4d1059f9..32a73c4c 100644 --- a/packages/ove/package.json +++ b/packages/ove/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/ove", - "version": "0.7.8", + "version": "0.7.9", "main": "./src/index.js", "type": "module", "exports": { diff --git a/packages/ove/src/AutoAnnotate.js b/packages/ove/src/AutoAnnotate.js index 2efc9a60..2327ba07 100644 --- a/packages/ove/src/AutoAnnotate.js +++ b/packages/ove/src/AutoAnnotate.js @@ -12,7 +12,7 @@ import { parseCsvFile, validateCSVRequiredHeaders, validateCSVRow -} from "./fileUtils"; +} from "@teselagen/file-utils"; import downloadjs from "downloadjs"; import { autoAnnotate, diff --git a/packages/ove/src/fileUtils.js b/packages/ove/src/fileUtils.js deleted file mode 100644 index 96534e8d..00000000 --- a/packages/ove/src/fileUtils.js +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */ -import { parse } from "papaparse"; - -export const allowedCsvFileTypes = [".csv", ".txt", ".xlsx"]; - -export const isZipFile = file => { - const type = file.mimetype || file.type; - return type === "application/zip" || type === "application/x-zip-compressed"; -}; - -export const getExt = file => file.name.split(".").pop(); -export const isExcelFile = file => getExt(file) === "xlsx"; -export const isCsvFile = file => getExt(file) === "csv"; -export const isTextFile = file => ["text", "txt"].includes(getExt(file)); - -const defaultCsvParserOptions = { - header: true, - skipEmptyLines: "greedy", - trimHeaders: true - // delimiter: "," -}; - -export const parseCsvFile = (csvFile, parserOptions = {}) => { - return new Promise((resolve, reject) => { - parse(csvFile.originFileObj, { - ...defaultCsvParserOptions, - complete: results => { - if (results && results.errors && results.errors.length) { - return reject("Error in csv: " + JSON.stringify(results.errors)); - } - resolve(results); - }, - error: error => { - reject(error); - }, - ...parserOptions - }); - }); -}; - -export const parseCsvString = (csvString, parserOptions = {}) => { - return parse(csvString, { ...defaultCsvParserOptions, ...parserOptions }); -}; - -export const cleanCommaSeparatedCell = cellData => - (cellData || "") - .split(",") - .map(n => n.trim()) - .filter(n => n); - -/** - * Because the csv rows might not have the same header keys in some cases (extended properties) - * this function will make sure that each row will have all headers so that the export - * does not drop fields - * @param {*} rows - */ -export const cleanCsvExport = rows => { - const allHeaders = []; - rows.forEach(row => { - Object.keys(row).forEach(header => { - if (!allHeaders.includes(header)) { - allHeaders.push(header); - } - }); - }); - rows.forEach(row => { - allHeaders.forEach(header => { - row[header] = row[header] || ""; - }); - }); - return rows; -}; - -export const validateCSVRequiredHeaders = ( - fields, - requiredHeaders, - filename -) => { - const missingRequiredHeaders = requiredHeaders.filter(field => { - return !fields.includes(field); - }); - if (missingRequiredHeaders.length) { - const name = filename ? `The file ${filename}` : "CSV file"; - return `${name} is missing required headers. (${missingRequiredHeaders.join( - ", " - )})`; - } -}; - -export const validateCSVRow = (row, requiredHeaders, index) => { - const missingRequiredFields = requiredHeaders.filter(field => !row[field]); - if (missingRequiredFields.length) { - if (missingRequiredFields.length === 1) { - return `Row ${index + 1} is missing the required field "${ - missingRequiredFields[0] - }"`; - } else { - return `Row ${ - index + 1 - } is missing these required fields: ${missingRequiredFields.join(", ")}`; - } - } -}; diff --git a/packages/ui/demo/src/examples/DefaultVal.js b/packages/ui/demo/src/examples/DefaultVal.js new file mode 100644 index 00000000..cc32d248 --- /dev/null +++ b/packages/ui/demo/src/examples/DefaultVal.js @@ -0,0 +1,37 @@ +import React from "react"; +import { withProps } from "recompose"; + +import { InputField } from "../../../src"; +import { compose } from "redux"; +import { reduxForm } from "redux-form"; + +export default compose( + withProps(() => { + return { + initialValues: { + defaultVal: "Default Value From Initial" + } + }; + }), + reduxForm({ + form: "defaultValForm" + }) +)(function DefaultVal() { + return ( +
+ {/* default val demo for InputField */} + + +
+ ); +}); diff --git a/packages/ui/demo/src/index.js b/packages/ui/demo/src/index.js index a2d94ba6..5fe4bcf8 100644 --- a/packages/ui/demo/src/index.js +++ b/packages/ui/demo/src/index.js @@ -21,6 +21,7 @@ import TimelineDemo from "./examples/TimelineDemo"; import UploaderDemo from "./examples/UploaderDemo"; import IntentTextDemo from "./examples/IntentText"; import ScrollToTopDemo from "./examples/ScrollToTop"; +import DefaultValDemo from "./examples/DefaultVal"; import showAppSpinnerDemo from "./examples/showAppSpinnerDemo"; import EditableCellTable from "./examples/EditableCellTable"; @@ -256,6 +257,9 @@ const demos = { ScrollToTop: { demo: ScrollToTopDemo }, + DefaultVal: { + demo: DefaultValDemo + }, PromptUnsavedChanges: { demo: PromptUnsavedChanges } diff --git a/packages/ui/package.json b/packages/ui/package.json index d3984af2..832f3ca4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/ui", - "version": "0.7.11", + "version": "0.7.12", "main": "./src/index.js", "type": "module", "exports": { diff --git a/packages/ui/src/FormComponents/index.js b/packages/ui/src/FormComponents/index.js index 888b8b8b..6025f663 100644 --- a/packages/ui/src/FormComponents/index.js +++ b/packages/ui/src/FormComponents/index.js @@ -7,6 +7,7 @@ import React, { useContext, useEffect, useMemo, + useRef, useState } from "react"; import { Field, change } from "redux-form"; @@ -136,7 +137,7 @@ const AbstractInput = ({ disabled, fileLimit, inlineLabel, - input: { name }, + input: { name, value }, intent, isLabelTooltip, isLoadingDefaultValue, @@ -161,18 +162,29 @@ const AbstractInput = ({ tooltipProps }) => { const dispatch = useDispatch(); + const initalValuePassed = useRef(value); const onDefaultValChanged = useStableReference(_onDefaultValChanged); const onFieldSubmit = useStableReference(_onFieldSubmit); - + const doesNotHaveInitialValue = + !isLoadingDefaultValue && !initalValuePassed.current; // This only takes care that the default Value is changed when it is changed in the parent component useEffect(() => { - if (defaultValue !== undefined) { + //if the input already has an initial value being passed to it, we don't want to override it with the default value + if (defaultValue !== undefined && doesNotHaveInitialValue) { dispatch(change(form, name, defaultValue)); onDefaultValChanged.current && onDefaultValChanged.current(defaultValue, name, form); onFieldSubmit.current && onFieldSubmit.current(defaultValue); } - }, [defaultValue, dispatch, form, name, onDefaultValChanged, onFieldSubmit]); + }, [ + defaultValue, + dispatch, + form, + name, + onDefaultValChanged, + onFieldSubmit, + doesNotHaveInitialValue + ]); // if our custom field level validation is happening then we don't want to show the error visually const showError =