From b352e4b26a9c87f81807c4b64ff69d374cb7e47f Mon Sep 17 00:00:00 2001 From: Guillermo Espinosa Date: Wed, 11 Dec 2024 13:32:36 -0300 Subject: [PATCH] fix: ove test --- packages/ove/cypress/e2e/alignment.spec.js | 4 +- packages/ove/cypress/e2e/editing.spec.js | 4 +- packages/ove/cypress/e2e/editor.spec.js | 4 +- packages/ove/cypress/e2e/menuBar.spec.js | 11 +- packages/ove/src/DigestTool/DigestTool.js | 104 +++--- .../RemoveDuplicates/index.js | 304 +++++++++--------- packages/ove/src/utils/useFormValue.js | 7 + packages/ui/demo/src/examples/DataTable.js | 2 +- packages/ui/src/DataTable/index.js | 10 +- 9 files changed, 215 insertions(+), 235 deletions(-) create mode 100644 packages/ove/src/utils/useFormValue.js diff --git a/packages/ove/cypress/e2e/alignment.spec.js b/packages/ove/cypress/e2e/alignment.spec.js index 6976d4b4..49226505 100644 --- a/packages/ove/cypress/e2e/alignment.spec.js +++ b/packages/ove/cypress/e2e/alignment.spec.js @@ -105,7 +105,7 @@ describe("alignment", function () { cy.get("body").type("{shift}", { release: false }); cy.scrollAlignmentToPercent(0.99); cy.contains(`[data-alignment-track-index="1"] text`, 3510).click(); - cy.get(`[title="Selecting 3500 bps from 11 to 3510"]`); + cy.get(`[title="Selecting 3499 bps from 11 to 3509"]`); }); it("the alignment should show axis numbers correctly", function () { cy.visit("#/Alignment?alignmentDataId=39"); @@ -124,7 +124,7 @@ describe("alignment", function () { cy.get("body").type("{shift}", { release: false }); cy.scrollAlignmentToPercent(0.99); cy.contains(`[data-alignment-track-index="1"] text`, 3510).click(); - cy.get(`[title="Selecting 3455 bps from 56 to 3510"]`); + cy.get(`[title="Selecting 3454 bps from 56 to 3509"]`); }); it("scrolls the yellow scroll handle correctly", function () { cy.visit("#/Alignment"); diff --git a/packages/ove/cypress/e2e/editing.spec.js b/packages/ove/cypress/e2e/editing.spec.js index a5e8a9c0..007d270d 100644 --- a/packages/ove/cypress/e2e/editing.spec.js +++ b/packages/ove/cypress/e2e/editing.spec.js @@ -58,7 +58,7 @@ describe("editing", function () { cy.contains("Caret Between Bases 5293 and 1"); cy.contains(".ve-row-item-sequence", "5'gtcttatga"); }); - it(`should be able to insert data around the origin correctly + it(`should be able to insert data around the origin correctly - new sequence should be inserted after the origin`, () => { cy.selectRange(5297, 3); cy.replaceSelection("aaaaaa"); @@ -68,7 +68,7 @@ describe("editing", function () { cy.replaceSelection("tt"); cy.contains("Selecting 2 bps from 3 to 4"); }); - it(`should be able to revComp, comp selections that wrap the origin correctly + it(`should be able to revComp, comp selections that wrap the origin correctly - new sequence should be inserted after the origin`, () => { cy.selectRange(5297, 3); cy.contains("Jump to start").click(); diff --git a/packages/ove/cypress/e2e/editor.spec.js b/packages/ove/cypress/e2e/editor.spec.js index 2de4bd4a..55f35ee0 100644 --- a/packages/ove/cypress/e2e/editor.spec.js +++ b/packages/ove/cypress/e2e/editor.spec.js @@ -154,7 +154,7 @@ describe("editor", function () { cy.tgToggle("shouldAutosave"); cy.contains(".veRowViewPart", "Part 0").first().click(); cy.get(".veRowViewSelectionLayer").first().trigger("contextmenu"); - cy.get(".bp3-menu-item").contains("Cut").click(); + cy.get(".bp3-menu-item").contains("Cut").realClick(); cy.contains("onCopy callback triggered"); cy.contains("onSave callback triggered"); cy.contains("Selection Cut"); @@ -167,7 +167,7 @@ describe("editor", function () { cy.get(".veRowViewSelectionLayer").first().trigger("contextmenu"); //tnrnote: cut in cypress only works on electron, not firefox or chrome - cy.get(".bp3-menu-item").contains("Cut").click(); + cy.get(".bp3-menu-item").contains("Cut").realClick(); cy.contains("Selection Cut"); cy.get(".tg-menu-bar").contains("File").click(); cy.get(".bp3-menu-item").contains("Save As").click(); diff --git a/packages/ove/cypress/e2e/menuBar.spec.js b/packages/ove/cypress/e2e/menuBar.spec.js index 932fe613..e34bca0b 100644 --- a/packages/ove/cypress/e2e/menuBar.spec.js +++ b/packages/ove/cypress/e2e/menuBar.spec.js @@ -186,7 +186,6 @@ describe("menuBar", function () { // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(0); cy.focused().type("remove duplicate feature{enter}", { delay: 1 }); - cy.contains(".rt-td", "dbl term").should("exist"); cy.contains(".bp3-dialog button", "Remove 2 Duplicates"); cy.get(".bp3-dialog .bp3-icon-settings").click(); cy.get(".tg-test-ignore-name .tg-no-fill-field").click(); @@ -204,7 +203,7 @@ describe("menuBar", function () { cy.get(".tg-menu-bar-popover").contains("Select All").click(); cy.get(".tg-menu-bar").contains("Edit").click(); - cy.get(".tg-menu-bar-popover").contains("Cut").click(); + cy.get(".tg-menu-bar-popover").contains("Cut").realClick(); cy.get(".tg-menu-bar").contains("Edit").click({ force: true }); [ @@ -290,7 +289,7 @@ describe("menuBar", function () { }); it(` goTo, rotateTo work -can't go to a position outside of the sequence - -can go to a position inside the sequence + -can go to a position inside the sequence -can rotate the sequence to that position `, () => { cy.get(".tg-menu-bar").contains("Edit").click(); @@ -329,7 +328,7 @@ describe("menuBar", function () { it(` select range, copy, cut works -cannot select range outside of sequence //TODO - -can select a valid range + -can select a valid range -can copy the select bps -can cut the selected bps `, function () { @@ -348,10 +347,10 @@ describe("menuBar", function () { cy.get(".veStatusBar").contains(`5299`); cy.get(".tg-menu-bar").contains("Edit").click(); - cy.get(".tg-menu-bar-popover").contains("Copy").click(); + cy.get(".tg-menu-bar-popover").contains("Copy").realClick(); cy.contains("Selection Copied"); cy.get(".tg-menu-bar").contains("Edit").click(); - cy.get(".tg-menu-bar-popover").contains("Cut").click(); + cy.get(".tg-menu-bar-popover").contains("Cut").realClick(); cy.contains("Selection Cut"); cy.get(".veStatusBar").contains(`5288`); }); diff --git a/packages/ove/src/DigestTool/DigestTool.js b/packages/ove/src/DigestTool/DigestTool.js index 28322176..c1b1f6f2 100644 --- a/packages/ove/src/DigestTool/DigestTool.js +++ b/packages/ove/src/DigestTool/DigestTool.js @@ -1,13 +1,8 @@ -// import uniqid from "shortid"; -// import Ladder from "./Ladder"; -import { compose, withProps } from "recompose"; -// import selectionLayer from "../redux/selectionLayer"; -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; import { DataTable } from "@teselagen/ui"; import { getCutsiteType, getVirtualDigest } from "@teselagen/sequence-utils"; import CutsiteFilter from "../CutsiteFilter"; import Ladder from "./Ladder"; -// import getCutsiteType from "./getCutsiteType"; import { Tabs, Tab, @@ -27,18 +22,59 @@ export const DigestTool = props => { const [selectedTab, setSelectedTab] = useState("virtualDigest"); const { editorName, - // height = 100, dimensions = {}, - lanes, digestTool: { selectedFragment, computePartialDigest }, onDigestSave, - computePartialDigestDisabled, - computeDigestDisabled, updateComputePartialDigest, boxHeight, digestLaneRightClicked, - ladders + ladders, + sequenceData, + sequenceLength, + selectionLayerUpdate, + updateSelectedFragment } = props; + + const isCircular = sequenceData.circular; + const cutsites = sequenceData.cutsites; + const computePartialDigestDisabled = + cutsites.length > MAX_PARTIAL_DIGEST_CUTSITES; + const computeDigestDisabled = cutsites.length > MAX_DIGEST_CUTSITES; + + const lanes = useMemo(() => { + const { fragments } = getVirtualDigest({ + cutsites, + sequenceLength, + isCircular, + computePartialDigest, + computePartialDigestDisabled, + computeDigestDisabled + }); + const lanes = [ + fragments.map(f => ({ + ...f, + onFragmentSelect: () => { + selectionLayerUpdate({ + start: f.start, + end: f.end, + name: f.name + }); + updateSelectedFragment(f.Intentid); + } + })) + ]; + return lanes; + }, [ + computeDigestDisabled, + computePartialDigest, + computePartialDigestDisabled, + cutsites, + isCircular, + selectionLayerUpdate, + sequenceLength, + updateSelectedFragment + ]); + return (
{ - const isCircular = sequenceData.circular; - const cutsites = sequenceData.cutsites; - const computePartialDigestDisabled = - cutsites.length > MAX_PARTIAL_DIGEST_CUTSITES; - const computeDigestDisabled = cutsites.length > MAX_DIGEST_CUTSITES; - - const { fragments, overlappingEnzymes } = getVirtualDigest({ - cutsites, - sequenceLength, - isCircular, - computePartialDigest, - computePartialDigestDisabled, - computeDigestDisabled - }); - return { - computePartialDigestDisabled, - computeDigestDisabled, - lanes: [ - fragments.map(f => ({ - ...f, - onFragmentSelect: () => { - selectionLayerUpdate({ - start: f.start, - end: f.end, - name: f.name - }); - updateSelectedFragment(f.Intentid); - } - })) - ], - overlappingEnzymes - }; - } - ) -)(DigestTool); +export default withEditorInteractions(DigestTool); diff --git a/packages/ove/src/helperComponents/RemoveDuplicates/index.js b/packages/ove/src/helperComponents/RemoveDuplicates/index.js index 9ddbdef7..8b74a674 100644 --- a/packages/ove/src/helperComponents/RemoveDuplicates/index.js +++ b/packages/ove/src/helperComponents/RemoveDuplicates/index.js @@ -1,194 +1,178 @@ -import React from "react"; +import React, { useCallback, useMemo, useState } from "react"; import { reduxForm } from "redux-form"; - import { wrapDialog, DataTable, - withSelectedEntities, SwitchField, - tgFormValues + useTableEntities } from "@teselagen/ui"; import { compose } from "redux"; import { Button, Classes, Popover } from "@blueprintjs/core"; import classNames from "classnames"; - import withEditorProps from "../../withEditorProps"; import { forEach, camelCase, startCase } from "lodash-es"; import { sizeSchema } from "../PropertiesDialog/utils"; import { getRangeLength } from "@teselagen/range-utils"; +import { useFormValue } from "../../utils/useFormValue"; + +const dialogFormName = "RemoveDuplicatesDialog"; +const dataTableFormName = "duplicatesToRemove"; +const checkboxStyle = { marginTop: 0, marginBottom: 0 }; + +const RemoveDuplicatesDialog = props => { + const { + type, + sequenceData = { sequence: "" }, + sequenceLength, + isProtein, + hideModal + } = props; -class RemoveDuplicatesDialog extends React.Component { - state = { - dups: [] - }; - componentDidMount() { - this.recomputeDups(); - } + const { selectedEntities } = useTableEntities(dataTableFormName); - checkboxStyle = { marginTop: 0, marginBottom: 0 }; + const ignoreName = useFormValue(dialogFormName, "ignoreName"); + const ignoreStartAndEnd = useFormValue(dialogFormName, "ignoreStartAndEnd"); + const ignoreStrand = useFormValue(dialogFormName, "ignoreStrand"); - delayedRecomputeDups = () => { - setTimeout(() => { - this.recomputeDups(); - }); - }; - recomputeDups = () => { - const { - // hideModal, - type, - sequenceData = { sequence: "" }, - // handleSubmit, - sequenceLength, - ignoreName, - ignoreStrand, - ignoreStartAndEnd - // circular, - // upsertFeature - } = this.props; + const recomputeDups = useCallback( + values => { + const ignoreName = values?.ignoreName; + const ignoreStartAndEnd = values?.ignoreStartAndEnd; + const ignoreStrand = values?.ignoreStrand; + const annotations = sequenceData[type]; + const newDups = []; + const seqsHashByStartEndStrandName = {}; + forEach(annotations, a => { + const hash = `${ignoreStartAndEnd ? "" : a.start}&${ + ignoreStartAndEnd ? "" : a.end + }&${ignoreStrand ? "" : a.strand}&${ignoreName ? "" : a.name}`; + if (seqsHashByStartEndStrandName[hash]) { + newDups.push({ ...a, size: getRangeLength(a, sequenceLength) }); + } else { + seqsHashByStartEndStrandName[hash] = true; + } + }); + return newDups; + }, + [sequenceData, sequenceLength, type] + ); - const annotations = sequenceData[type]; - const dups = []; - const seqsHashByStartEndStrandName = {}; - forEach(annotations, a => { - const hash = `${ignoreStartAndEnd ? "" : a.start}&${ - ignoreStartAndEnd ? "" : a.end - }&${ignoreStrand ? "" : a.strand}&${ignoreName ? "" : a.name}`; - if (seqsHashByStartEndStrandName[hash]) { - dups.push({ ...a, size: getRangeLength(a, sequenceLength) }); - } else { - seqsHashByStartEndStrandName[hash] = true; - } - }); - this.setState({ dups }); - }; - render() { - const { duplicatesToRemoveSelectedEntities, hideModal, type } = this.props; + const [dups, setDups] = useState(recomputeDups); + const selectedIds = useMemo(() => dups.map(d => d.id), [dups]); - const selectedIds = this.state.dups.map(d => d.id); + const fieldSubmit = useCallback( + (newVal, field) => { + const values = { + ignoreName, + ignoreStartAndEnd, + ignoreStrand, + [field]: newVal + }; + const newDups = recomputeDups(values); + setDups(newDups); + }, + [ignoreName, ignoreStartAndEnd, ignoreStrand, recomputeDups] + ); - const schema = { + const schema = useMemo( + () => ({ fields: [ - // ...(noColor - // ? [] - // : [ - // { - // path: "color", - // type: "string", - // render: color => { - // return ( - // - //
- // - // ); - // } - // } - // ]), { path: "name", type: "string" }, // ...(noType ? [] : [{ path: "type", type: "string" }]), - sizeSchema(this.props.isProtein), + sizeSchema(isProtein), { path: "strand", type: "string" } ] - }; - // const sequenceLength = sequenceData.sequence.length; - // const isCirc = (this.state || {}).circular; - return ( -
- {/* {dups.map((d) => { - return
+ }), + [isProtein] + ); -
- })} */} - + +
+ } + content={ +
+
Ignore These Fields While Finding Duplicates:
+
+ fieldSubmit(newVal, "ignoreName")} + style={checkboxStyle} + name="ignoreName" + label="Name" + /> + fieldSubmit(newVal, "ignoreStrand")} + style={checkboxStyle} + name="ignoreStrand" + label="Strand" + /> + + fieldSubmit(newVal, "ignoreStartAndEnd") + } + style={checkboxStyle} + name="ignoreStartAndEnd" + label="Start and End" + /> +
+ } /> -
{ + props[camelCase(`delete_${type}`).slice(0, -1)]( + Object.keys(selectedEntities || {}) + ); + window.toastr.success( + `Successfully Deleted ${ + Object.keys(selectedEntities || {}).length + } ${startCase(type)}` + ); + hideModal(); }} + disabled={!Object.keys(selectedEntities || {}).length} > - } - content={ -
-
Ignore These Fields While Finding Duplicates:
-

- - - -
- } - >
- - -
+ Remove {Object.keys(selectedEntities || {}).length} Duplicates +
- ); - } -} +
+ ); +}; export default compose( wrapDialog(), withEditorProps, - - withSelectedEntities("duplicatesToRemove"), - - reduxForm({ - form: "RemoveDuplicatesDialog" - }), - tgFormValues("ignoreName", "ignoreStrand", "ignoreStartAndEnd") + reduxForm({ form: dialogFormName }) )(RemoveDuplicatesDialog); diff --git a/packages/ove/src/utils/useFormValue.js b/packages/ove/src/utils/useFormValue.js new file mode 100644 index 00000000..ec654230 --- /dev/null +++ b/packages/ove/src/utils/useFormValue.js @@ -0,0 +1,7 @@ +/* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */ +import { useSelector } from "react-redux"; +import { get } from "lodash"; + +export const useFormValue = (formName, field) => { + return useSelector(state => get(state.form?.[formName]?.values, field)); +}; diff --git a/packages/ui/demo/src/examples/DataTable.js b/packages/ui/demo/src/examples/DataTable.js index b3500c8f..be77b6e0 100644 --- a/packages/ui/demo/src/examples/DataTable.js +++ b/packages/ui/demo/src/examples/DataTable.js @@ -586,7 +586,7 @@ const DataTableDemo = () => { onRefresh={onRefresh} onSingleRowSelect={noop} selectAllByDefault={selectAllByDefault} - initialSelectedIds={selectedIds} + selectedIds={selectedIds} shouldShowSubComponent={r => r.id !== 1} showCount={showCount} SubComponent={withSubComponent ? SubComp : undefined} diff --git a/packages/ui/src/DataTable/index.js b/packages/ui/src/DataTable/index.js index 6d03ed8d..3a33a4c2 100644 --- a/packages/ui/src/DataTable/index.js +++ b/packages/ui/src/DataTable/index.js @@ -425,7 +425,7 @@ const DataTable = ({ hideSelectedCount = isSimple, hideSetPageSize, hideTotalPages, - initialSelectedIds, + selectedIds, isCellEditable, isCopyable = true, isEntityDisabled = noop, @@ -1752,17 +1752,15 @@ const DataTable = ({ // We need to make sure this only runs at the beggining useEffect(() => { - if (initialSelectedIds) { - if (alreadySelected.current) return; - setSelectedIds(initialSelectedIds); - alreadySelected.current = true; + if (selectedIds) { + setSelectedIds(selectedIds); } if (selectAllByDefault && entities && entities.length) { if (alreadySelected.current) return; setSelectedIds(entities.map(getIdOrCodeOrIndex)); alreadySelected.current = true; } - }, [entities, initialSelectedIds, selectAllByDefault, setSelectedIds]); + }, [entities, selectedIds, selectAllByDefault, setSelectedIds]); const TheadComponent = useCallback( ({ className, style, children }) => {