@opencloning/ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/package.json +37 -0
- package/src/components/AppAlerts.jsx +19 -0
- package/src/components/CloningHistory.jsx +40 -0
- package/src/components/DataModelDisplayer.jsx +30 -0
- package/src/components/DescriptionEditor.jsx +68 -0
- package/src/components/DownloadCloningStrategyDialog.jsx +84 -0
- package/src/components/DownloadSequenceFileDialog.jsx +90 -0
- package/src/components/DragAndDropCloningHistoryWrapper.jsx +39 -0
- package/src/components/DraggableDialogPaper.jsx +16 -0
- package/src/components/EditSequenceNameDialog.jsx +90 -0
- package/src/components/ExternalServicesStatusCheck.jsx +92 -0
- package/src/components/HistoryLoadedDialog.jsx +49 -0
- package/src/components/LoadCloningHistoryWrapper.jsx +166 -0
- package/src/components/MainSequenceCheckBox.jsx +83 -0
- package/src/components/MainSequenceEditor.jsx +165 -0
- package/src/components/NetworkNode.jsx +159 -0
- package/src/components/NetworkTree.css +127 -0
- package/src/components/ObjectTable.jsx +24 -0
- package/src/components/OpenCloning.jsx +102 -0
- package/src/components/OverhangsDisplay.jsx +25 -0
- package/src/components/SequenceEditor.jsx +120 -0
- package/src/components/SequenceTab.jsx +14 -0
- package/src/components/TemplateSequence.jsx +38 -0
- package/src/components/annotation/PlannotateAnnotationReport.jsx +33 -0
- package/src/components/annotation/useUpdateAnnotationInMainSequence.js +39 -0
- package/src/components/assembler/AssemblePartWidget.jsx +252 -0
- package/src/components/assembler/Assembler.jsx +273 -0
- package/src/components/assembler/AssemblerPart.jsx +99 -0
- package/src/components/assembler/StopIcon.jsx +34 -0
- package/src/components/assembler/assembler_data2.json +50 -0
- package/src/components/assembler/assembly_component.module.css +81 -0
- package/src/components/assembler/moclo.json +110 -0
- package/src/components/assembler/sbol_visual_glyphs/LICENSE.html +21 -0
- package/src/components/assembler/sbol_visual_glyphs/assembly-scar.svg +63 -0
- package/src/components/assembler/sbol_visual_glyphs/cds-stop.svg +85 -0
- package/src/components/assembler/sbol_visual_glyphs/cds.svg +60 -0
- package/src/components/assembler/sbol_visual_glyphs/chromosomal-locus.svg +78 -0
- package/src/components/assembler/sbol_visual_glyphs/engineered-region.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs/five-prime-sticky-restriction-site.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs/origin-of-replication.svg +57 -0
- package/src/components/assembler/sbol_visual_glyphs/primer-binding-site.svg +59 -0
- package/src/components/assembler/sbol_visual_glyphs/promoter.svg +60 -0
- package/src/components/assembler/sbol_visual_glyphs/ribosome-entry-site.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs/specific-recombination-site.svg +59 -0
- package/src/components/assembler/sbol_visual_glyphs/terminator.svg +60 -0
- package/src/components/assembler/sbol_visual_glyphs/three-prime-sticky-restriction-site.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs.js +36 -0
- package/src/components/assembler/useAssembler.js +71 -0
- package/src/components/dummy/DummyInterface.js +41 -0
- package/src/components/dummy/GetSequenceFileAndDatabaseIdComponent.jsx +59 -0
- package/src/components/eLabFTW/ELabFTWCategorySelect.cy.jsx +86 -0
- package/src/components/eLabFTW/ELabFTWCategorySelect.jsx +43 -0
- package/src/components/eLabFTW/ELabFTWFileSelect.cy.jsx +43 -0
- package/src/components/eLabFTW/ELabFTWFileSelect.jsx +29 -0
- package/src/components/eLabFTW/ELabFTWResourceSelect.cy.jsx +107 -0
- package/src/components/eLabFTW/ELabFTWResourceSelect.jsx +23 -0
- package/src/components/eLabFTW/GetPrimerComponent.cy.jsx +261 -0
- package/src/components/eLabFTW/GetPrimerComponent.jsx +55 -0
- package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.cy.jsx +184 -0
- package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.jsx +62 -0
- package/src/components/eLabFTW/LoadHistoryComponent.cy.jsx +235 -0
- package/src/components/eLabFTW/LoadHistoryComponent.jsx +51 -0
- package/src/components/eLabFTW/PrimersNotInDatabaseComponent.cy.jsx +159 -0
- package/src/components/eLabFTW/PrimersNotInDatabaseComponent.jsx +54 -0
- package/src/components/eLabFTW/SubmitToDatabaseComponent.cy.jsx +185 -0
- package/src/components/eLabFTW/SubmitToDatabaseComponent.jsx +51 -0
- package/src/components/eLabFTW/common.js +26 -0
- package/src/components/eLabFTW/eLabFTWInterface.js +294 -0
- package/src/components/eLabFTW/eLabFTWInterface.test.js +839 -0
- package/src/components/eLabFTW/envValues.js +7 -0
- package/src/components/eLabFTW/utils.js +39 -0
- package/src/components/form/CustomFormHelperText.jsx +10 -0
- package/src/components/form/EnzymeMultiSelect.cy.jsx +61 -0
- package/src/components/form/EnzymeMultiSelect.jsx +34 -0
- package/src/components/form/GetRequestMultiSelect.jsx +107 -0
- package/src/components/form/LabelWithTooltip.jsx +16 -0
- package/src/components/form/PostRequestSelect.cy.jsx +70 -0
- package/src/components/form/PostRequestSelect.jsx +86 -0
- package/src/components/form/RequestStatusWrapper.jsx +17 -0
- package/src/components/form/RetryAlert.jsx +20 -0
- package/src/components/form/ServerErrorMessage.jsx +10 -0
- package/src/components/form/SubmitButtonBackendAPI.jsx +15 -0
- package/src/components/form/SubmitToDatabaseDialog.jsx +133 -0
- package/src/components/form/TextFieldValidate.jsx +67 -0
- package/src/components/form/ValidatedTextField.jsx +33 -0
- package/src/components/form/intermediates_disclaimer.svg +181 -0
- package/src/components/navigation/ButtonWithMenu.jsx +43 -0
- package/src/components/navigation/CustomTab.jsx +14 -0
- package/src/components/navigation/FeedbackDialog.jsx +34 -0
- package/src/components/navigation/GithubCornerRight.jsx +29 -0
- package/src/components/navigation/MainAppBar.css +26 -0
- package/src/components/navigation/MainAppBar.jsx +205 -0
- package/src/components/navigation/SelectExampleDialog.jsx +69 -0
- package/src/components/navigation/SelectTemplateDialog.jsx +107 -0
- package/src/components/navigation/TabPanel.jsx +28 -0
- package/src/components/navigation/VersionDialog.jsx +33 -0
- package/src/components/primers/CreatePrimerFromSequenceForm.jsx +42 -0
- package/src/components/primers/DownloadPrimersButton.jsx +104 -0
- package/src/components/primers/PrimerForm.css +14 -0
- package/src/components/primers/PrimerForm.jsx +107 -0
- package/src/components/primers/PrimerList.css +46 -0
- package/src/components/primers/PrimerList.cy.jsx +95 -0
- package/src/components/primers/PrimerList.jsx +126 -0
- package/src/components/primers/PrimerTableRow.cy.jsx +57 -0
- package/src/components/primers/PrimerTableRow.jsx +84 -0
- package/src/components/primers/SelectPrimerForm.jsx +66 -0
- package/src/components/primers/import_primers/ImportPrimersButton.jsx +101 -0
- package/src/components/primers/import_primers/ImportPrimersTable.jsx +51 -0
- package/src/components/primers/import_primers/PrimerDatabaseImportForm.jsx +107 -0
- package/src/components/primers/import_primers/styles.css +60 -0
- package/src/components/primers/primer_design/SequenceTabComponents/CollapsableLabel.jsx +31 -0
- package/src/components/primers/primer_design/SequenceTabComponents/GatewayRoiSelect.jsx +164 -0
- package/src/components/primers/primer_design/SequenceTabComponents/OrientationPicker.jsx +37 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignContext.jsx +369 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignEBIC.jsx +24 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignForm.jsx +29 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGatewayBP.jsx +36 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGibsonAssembly.jsx +26 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignHomologousRecombination.jsx +32 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignRestriction.jsx +25 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignSimplePair.jsx +25 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignStepper.jsx +53 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesigner.jsx +80 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerResultForm.jsx +49 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerSettingsForm.jsx +68 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerSpacerForm.jsx +84 -0
- package/src/components/primers/primer_design/SequenceTabComponents/RestrictionSpacerForm.jsx +85 -0
- package/src/components/primers/primer_design/SequenceTabComponents/StepNavigation.jsx +48 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPanelEBICSettings.jsx +216 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPanelResults.jsx +42 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPanelSelectRoi.jsx +61 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPannelSettings.jsx +59 -0
- package/src/components/primers/primer_design/SequenceTabComponents/primerDesignMinimalValues.json +5 -0
- package/src/components/primers/primer_design/SequenceTabComponents/useEBICPrimerDesignSettings.js +31 -0
- package/src/components/primers/primer_design/SequenceTabComponents/useEnzymePrimerDesignSettings.js +57 -0
- package/src/components/primers/primer_design/SequenceTabComponents/useGatewayPrimerDesignSettings.js +18 -0
- package/src/components/primers/primer_design/SequenceTabComponents/usePrimerDesignSettings.js +31 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignGatewayBP.jsx +88 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignGibsonAssembly.jsx +65 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignHomologousRecombination.jsx +84 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignSourceForm.jsx +74 -0
- package/src/components/primers/primer_design/common/NoAttPSitesError.jsx +31 -0
- package/src/components/primers/primer_details/PCRTable.cy.jsx +51 -0
- package/src/components/primers/primer_details/PCRTable.jsx +35 -0
- package/src/components/primers/primer_details/Primer3Figure.jsx +25 -0
- package/src/components/primers/primer_details/PrimerDetailsTds.jsx +39 -0
- package/src/components/primers/primer_details/PrimerInfoIcon.cy.jsx +137 -0
- package/src/components/primers/primer_details/PrimerInfoIcon.jsx +132 -0
- package/src/components/primers/primer_details/TableSection.jsx +17 -0
- package/src/components/primers/primer_details/primerDetailsFormatting.js +3 -0
- package/src/components/primers/primer_details/useMultiplePrimerDetails.js +29 -0
- package/src/components/primers/primer_details/usePCRDetails.js +47 -0
- package/src/components/primers/primer_details/usePrimerDetailsEndpoints.js +49 -0
- package/src/components/primers/primer_details/useSinglePrimerSequenceDetails.js +25 -0
- package/src/components/primers/primersToTabularFile.js +49 -0
- package/src/components/primers/primersToTabularFile.test.js +108 -0
- package/src/components/settings/SettingsTab.cy.jsx +267 -0
- package/src/components/settings/SettingsTab.jsx +170 -0
- package/src/components/sources/AssemblyPlanDisplayer.cy.jsx +22 -0
- package/src/components/sources/AssemblyPlanDisplayer.jsx +27 -0
- package/src/components/sources/CollectionSource.jsx +97 -0
- package/src/components/sources/FinishedSource.jsx +397 -0
- package/src/components/sources/KnownSourceErrors.jsx +50 -0
- package/src/components/sources/MultipleInputsSelector.jsx +63 -0
- package/src/components/sources/MultipleOutputsSelector.jsx +63 -0
- package/src/components/sources/NewSourceBox.jsx +37 -0
- package/src/components/sources/PCRUnitForm.jsx +102 -0
- package/src/components/sources/SingleInputSelector.jsx +36 -0
- package/src/components/sources/Source.jsx +125 -0
- package/src/components/sources/SourceAnnotation.jsx +44 -0
- package/src/components/sources/SourceAssembly.jsx +201 -0
- package/src/components/sources/SourceBox.css +18 -0
- package/src/components/sources/SourceBox.jsx +60 -0
- package/src/components/sources/SourceCopySequence.jsx +38 -0
- package/src/components/sources/SourceDatabase.jsx +28 -0
- package/src/components/sources/SourceFile.jsx +188 -0
- package/src/components/sources/SourceGenomeRegion.cy.jsx +131 -0
- package/src/components/sources/SourceGenomeRegion.jsx +486 -0
- package/src/components/sources/SourceHomologousRecombination.jsx +125 -0
- package/src/components/sources/SourceKnownGenomeRegion.jsx +60 -0
- package/src/components/sources/SourceManuallyTyped.jsx +116 -0
- package/src/components/sources/SourcePCRorHybridization.jsx +165 -0
- package/src/components/sources/SourcePolymeraseExtension.jsx +44 -0
- package/src/components/sources/SourceRepositoryId.jsx +409 -0
- package/src/components/sources/SourceRestriction.jsx +41 -0
- package/src/components/sources/SourceReverseComplement.jsx +33 -0
- package/src/components/sources/SourceTypeSelector.jsx +94 -0
- package/src/components/sources/SubSequenceDisplayer.jsx +70 -0
- package/src/components/sources/VerifyDeleteDialog.jsx +23 -0
- package/src/components/sources/repositoryMetadata.js +14 -0
- package/src/components/verification/LoadFromDatabaseButton.jsx +90 -0
- package/src/components/verification/SequencingFileRow.jsx +34 -0
- package/src/components/verification/VerificationFileDialog.cy.jsx +176 -0
- package/src/components/verification/VerificationFileDialog.jsx +248 -0
- package/src/config/defaultMainEditorProps.js +44 -0
- package/src/hooks/useAlerts.js +16 -0
- package/src/hooks/useBackendAPI.js +51 -0
- package/src/hooks/useBackendRoute.js +22 -0
- package/src/hooks/useDatabase.js +18 -0
- package/src/hooks/useDragAndDropFile.js +31 -0
- package/src/hooks/useGatewaySites.js +40 -0
- package/src/hooks/useHttpClient.js +12 -0
- package/src/hooks/useLoadDatabaseFile.js +108 -0
- package/src/hooks/useStoreEditor.js +101 -0
- package/src/hooks/useValidateState.js +43 -0
- package/vitest.config.js +18 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
import { Button, Card, CardContent, CardHeader, Stack, TextField, InputAdornment, Typography, IconButton, Box } from '@mui/material';
|
|
4
|
+
import { cloningActions } from '@opencloning/store/cloning';
|
|
5
|
+
import Tooltip from '@mui/material/Tooltip';
|
|
6
|
+
import InfoIcon from '@mui/icons-material/Info';
|
|
7
|
+
|
|
8
|
+
const { setGlobalPrimerSettings } = cloningActions;
|
|
9
|
+
|
|
10
|
+
function HeaderWithTooltip() {
|
|
11
|
+
const tooltipMessage = (
|
|
12
|
+
<Box>
|
|
13
|
+
These settings affect thermodynamic calculations for:
|
|
14
|
+
<ul>
|
|
15
|
+
<li>Primer design</li>
|
|
16
|
+
<li>Values displayed in the primer table</li>
|
|
17
|
+
<li>Values displayed in the PCR details</li>
|
|
18
|
+
</ul>
|
|
19
|
+
</Box>
|
|
20
|
+
)
|
|
21
|
+
const title = (
|
|
22
|
+
<Box>
|
|
23
|
+
Global Primer Settings
|
|
24
|
+
<Tooltip title={tooltipMessage} arrow placement="right">
|
|
25
|
+
<IconButton size="small">
|
|
26
|
+
<InfoIcon />
|
|
27
|
+
</IconButton>
|
|
28
|
+
</Tooltip>
|
|
29
|
+
</Box>
|
|
30
|
+
)
|
|
31
|
+
return (
|
|
32
|
+
<CardHeader
|
|
33
|
+
title={title}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
function GlobalPrimerSettingsSection() {
|
|
43
|
+
const dispatch = useDispatch();
|
|
44
|
+
const current = useSelector((state) => state.cloning.globalPrimerSettings);
|
|
45
|
+
const [editing, setEditing] = React.useState(false);
|
|
46
|
+
const [form, setForm] = React.useState({
|
|
47
|
+
primer_dna_conc: current.primer_dna_conc,
|
|
48
|
+
primer_salt_monovalent: current.primer_salt_monovalent,
|
|
49
|
+
primer_salt_divalent: current.primer_salt_divalent,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const fieldsValid = {
|
|
53
|
+
primer_dna_conc: form.primer_dna_conc > 0,
|
|
54
|
+
primer_salt_monovalent: form.primer_salt_monovalent > 0,
|
|
55
|
+
primer_salt_divalent: form.primer_salt_divalent > 0,
|
|
56
|
+
}
|
|
57
|
+
const allFieldsValid = Object.values(fieldsValid).every((valid) => valid);
|
|
58
|
+
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
if (!editing) {
|
|
61
|
+
setForm({
|
|
62
|
+
primer_dna_conc: current.primer_dna_conc,
|
|
63
|
+
primer_salt_monovalent: current.primer_salt_monovalent,
|
|
64
|
+
primer_salt_divalent: current.primer_salt_divalent,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}, [current, editing]);
|
|
68
|
+
|
|
69
|
+
const onChange = (key) => (e) => {
|
|
70
|
+
setForm((prev) => ({ ...prev, [key]: e.target.value }));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const onCancel = () => {
|
|
74
|
+
setEditing(false);
|
|
75
|
+
setForm({
|
|
76
|
+
primer_dna_conc: current.primer_dna_conc,
|
|
77
|
+
primer_salt_monovalent: current.primer_salt_monovalent,
|
|
78
|
+
primer_salt_divalent: current.primer_salt_divalent,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onSave = () => {
|
|
83
|
+
dispatch(setGlobalPrimerSettings({
|
|
84
|
+
primer_dna_conc: Number(form.primer_dna_conc),
|
|
85
|
+
primer_salt_monovalent: Number(form.primer_salt_monovalent),
|
|
86
|
+
primer_salt_divalent: Number(form.primer_salt_divalent),
|
|
87
|
+
}));
|
|
88
|
+
setEditing(false);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Card className="settings-tab">
|
|
93
|
+
<HeaderWithTooltip />
|
|
94
|
+
<CardContent sx={{ margin: 'auto' }}>
|
|
95
|
+
|
|
96
|
+
<Stack spacing={2} sx={{ maxWidth: 300, margin: 'auto' }}>
|
|
97
|
+
<TextField
|
|
98
|
+
label="Primer DNA concentration"
|
|
99
|
+
type="number"
|
|
100
|
+
value={form.primer_dna_conc}
|
|
101
|
+
onChange={onChange('primer_dna_conc')}
|
|
102
|
+
inputProps={{ min: 0, step: '1' }}
|
|
103
|
+
variant="standard"
|
|
104
|
+
required
|
|
105
|
+
disabled={!editing}
|
|
106
|
+
InputProps={{
|
|
107
|
+
endAdornment: <InputAdornment position="end">nM</InputAdornment>,
|
|
108
|
+
}}
|
|
109
|
+
error={!fieldsValid.primer_dna_conc}
|
|
110
|
+
helperText={!fieldsValid.primer_dna_conc ? 'Must be greater than 0' : ''}
|
|
111
|
+
/>
|
|
112
|
+
<TextField
|
|
113
|
+
label="Monovalent ions"
|
|
114
|
+
type="number"
|
|
115
|
+
value={form.primer_salt_monovalent}
|
|
116
|
+
onChange={onChange('primer_salt_monovalent')}
|
|
117
|
+
inputProps={{ min: 0, step: '0.1' }}
|
|
118
|
+
variant="standard"
|
|
119
|
+
required
|
|
120
|
+
disabled={!editing}
|
|
121
|
+
InputProps={{
|
|
122
|
+
endAdornment: <InputAdornment position="end">mM</InputAdornment>,
|
|
123
|
+
}}
|
|
124
|
+
error={!fieldsValid.primer_salt_monovalent}
|
|
125
|
+
helperText={!fieldsValid.primer_salt_monovalent ? 'Must be greater than 0' : ''}
|
|
126
|
+
/>
|
|
127
|
+
<TextField
|
|
128
|
+
label="Divalent ions"
|
|
129
|
+
type="number"
|
|
130
|
+
value={form.primer_salt_divalent}
|
|
131
|
+
onChange={onChange('primer_salt_divalent')}
|
|
132
|
+
inputProps={{ min: 0, step: '0.1' }}
|
|
133
|
+
variant="standard"
|
|
134
|
+
required
|
|
135
|
+
disabled={!editing}
|
|
136
|
+
InputProps={{
|
|
137
|
+
endAdornment: <InputAdornment position="end">mM</InputAdornment>,
|
|
138
|
+
}}
|
|
139
|
+
error={!fieldsValid.primer_salt_divalent}
|
|
140
|
+
helperText={!fieldsValid.primer_salt_divalent ? 'Must be greater than 0' : ''}
|
|
141
|
+
/>
|
|
142
|
+
{!editing ? (
|
|
143
|
+
<Stack direction="row" justifyContent="center">
|
|
144
|
+
<Button variant="contained" onClick={() => setEditing(true)}>Edit</Button>
|
|
145
|
+
</Stack>
|
|
146
|
+
) : (
|
|
147
|
+
<Stack direction="row" spacing={1} justifyContent="center">
|
|
148
|
+
<Button onClick={onCancel}>Cancel</Button>
|
|
149
|
+
<Button variant="contained" onClick={onSave} disabled={!allFieldsValid}>Save</Button>
|
|
150
|
+
</Stack>
|
|
151
|
+
)}
|
|
152
|
+
</Stack>
|
|
153
|
+
</CardContent>
|
|
154
|
+
</Card>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function SettingsTab() {
|
|
159
|
+
return (
|
|
160
|
+
<div style={{ padding: 16, display: 'flex', justifyContent: 'center' }}>
|
|
161
|
+
<div style={{ maxWidth: 600, width: '100%' }}>
|
|
162
|
+
<GlobalPrimerSettingsSection />
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default SettingsTab;
|
|
169
|
+
|
|
170
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import AssemblyPlanDisplayer from './AssemblyPlanDisplayer';
|
|
3
|
+
|
|
4
|
+
describe('<AssemblyPlanDisplayer />', () => {
|
|
5
|
+
it('Represents the assembly plan', () => {
|
|
6
|
+
// see: https://on.cypress.io/mounting-react
|
|
7
|
+
cy.mount(<AssemblyPlanDisplayer source={{ input: [
|
|
8
|
+
{ type: 'AssemblyFragment', sequence: 1, left_location: 'aa', right_location: 'bb', reverse_complemented: false },
|
|
9
|
+
{ type: 'AssemblyFragment', sequence: 2, left_location: 'cc', right_location: 'dd', reverse_complemented: true },
|
|
10
|
+
] }}
|
|
11
|
+
/>);
|
|
12
|
+
cy.get('.assembly-plan-displayer').contains('1[aa,bb] - 2_rc[cc,dd]');
|
|
13
|
+
});
|
|
14
|
+
it('Represents the assembly plan with null coordinates', () => {
|
|
15
|
+
cy.mount(<AssemblyPlanDisplayer source={{ input: [
|
|
16
|
+
{ type: 'AssemblyFragment', sequence: 1, left_location: null, right_location: 'aa', reverse_complemented: false },
|
|
17
|
+
{ type: 'AssemblyFragment', sequence: 2, left_location: 'bb', right_location: null, reverse_complemented: true },
|
|
18
|
+
] }}
|
|
19
|
+
/>);
|
|
20
|
+
cy.get('.assembly-plan-displayer').contains('1[,aa] - 2_rc[bb,]');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
function AssemblyPlanDisplayer({
|
|
4
|
+
source,
|
|
5
|
+
}) {
|
|
6
|
+
const assemblyFragments = source.input.filter(i => i.type === 'AssemblyFragment');
|
|
7
|
+
if (!assemblyFragments.length) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const fragments = assemblyFragments.map((fragment) => {
|
|
12
|
+
const { sequence, left_location, right_location, reverse_complemented } = fragment;
|
|
13
|
+
const leftPart = left_location || '';
|
|
14
|
+
const rightPart = right_location || '';
|
|
15
|
+
return `${sequence}${reverse_complemented ? '_rc' : ''}[${leftPart},${rightPart}]`;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Join left-right pairs with
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="assembly-plan-displayer">
|
|
22
|
+
{fragments.join(' - ')}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default React.memo(AssemblyPlanDisplayer);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { FormControl, InputLabel, MenuItem, Select } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
|
4
|
+
import { useDispatch, useStore } from 'react-redux';
|
|
5
|
+
import SubmitButtonBackendAPI from '../form/SubmitButtonBackendAPI';
|
|
6
|
+
import { classNameToEndPointMap } from '@opencloning/utils/sourceFunctions';
|
|
7
|
+
import ObjectTable from '../ObjectTable';
|
|
8
|
+
import { cloningActions } from '@opencloning/store/cloning';
|
|
9
|
+
|
|
10
|
+
function CollectionSource({ source, requestStatus, sendPostRequest }) {
|
|
11
|
+
const { id: sourceId, options, image: imageInfo, title, description } = source;
|
|
12
|
+
const [image, imageWidth] = imageInfo || [null, null];
|
|
13
|
+
const [selectedOption, setSelectedOption] = React.useState(null);
|
|
14
|
+
const dispatch = useDispatch();
|
|
15
|
+
const store = useStore();
|
|
16
|
+
const { replaceSource } = cloningActions;
|
|
17
|
+
|
|
18
|
+
const onSubmit = (event) => {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
const { source: selectedSource } = options.find((option) => option.name === selectedOption);
|
|
21
|
+
// Delete options field
|
|
22
|
+
const modifySource = (s) => {
|
|
23
|
+
const sourceOut = { ...s };
|
|
24
|
+
delete sourceOut.options;
|
|
25
|
+
return sourceOut;
|
|
26
|
+
};
|
|
27
|
+
const endpoint = classNameToEndPointMap[selectedSource.type];
|
|
28
|
+
let requestData;
|
|
29
|
+
if (selectedSource.type === 'AddgeneIdSource') {
|
|
30
|
+
requestData = { id: sourceId, ...selectedSource };
|
|
31
|
+
} else if (selectedSource.type === 'OligoHybridizationSource') {
|
|
32
|
+
const { primers } = store.getState().cloning;
|
|
33
|
+
const forwardOligo = primers.find((primer) => primer.id === selectedSource.input[0].sequence);
|
|
34
|
+
const reverseOligo = primers.find((primer) => primer.id === selectedSource.input[1].sequence);
|
|
35
|
+
requestData = { source: { id: sourceId, ...selectedSource }, primers: [forwardOligo, reverseOligo] };
|
|
36
|
+
}
|
|
37
|
+
sendPostRequest({ endpoint, requestData, source, modifySource });
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const onChange = (event) => {
|
|
41
|
+
setSelectedOption(event.target.value);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const turnIntoBlankSource = () => {
|
|
45
|
+
setSelectedOption(null);
|
|
46
|
+
dispatch(replaceSource({
|
|
47
|
+
id: sourceId,
|
|
48
|
+
input: [],
|
|
49
|
+
type: null,
|
|
50
|
+
}));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const selectedOptionObject = options.find((option) => option.name === selectedOption);
|
|
54
|
+
return (
|
|
55
|
+
<div className="collection-source">
|
|
56
|
+
{title && <h2>{title}</h2>}
|
|
57
|
+
{description && <p>{description}</p>}
|
|
58
|
+
{image && <img src={image} width={imageWidth} alt="Collection source icon" />}
|
|
59
|
+
<form onSubmit={onSubmit}>
|
|
60
|
+
<FormControl fullWidth>
|
|
61
|
+
<InputLabel id="select-collection-source">Select a sequence</InputLabel>
|
|
62
|
+
<Select
|
|
63
|
+
value={selectedOption !== null ? selectedOption : ''}
|
|
64
|
+
onChange={onChange}
|
|
65
|
+
labelId="select-collection-source"
|
|
66
|
+
label="Select a sequence"
|
|
67
|
+
// Limits the height of the dropdown (adds scroll)
|
|
68
|
+
MenuProps={{ PaperProps: { style: { maxHeight: 250 } } }}
|
|
69
|
+
>
|
|
70
|
+
<MenuItem onClick={turnIntoBlankSource} value="">
|
|
71
|
+
<AddCircleIcon color="success" />
|
|
72
|
+
<em style={{ marginLeft: 8 }}>Add your own</em>
|
|
73
|
+
</MenuItem>
|
|
74
|
+
{options.map((option) => (
|
|
75
|
+
<MenuItem key={option.name} value={option.name}>
|
|
76
|
+
{option.name}
|
|
77
|
+
</MenuItem>
|
|
78
|
+
))}
|
|
79
|
+
</Select>
|
|
80
|
+
</FormControl>
|
|
81
|
+
|
|
82
|
+
{selectedOption !== null && (
|
|
83
|
+
<>
|
|
84
|
+
{selectedOptionObject.info && <ObjectTable object={selectedOptionObject.info} />}
|
|
85
|
+
<SubmitButtonBackendAPI
|
|
86
|
+
requestStatus={requestStatus}
|
|
87
|
+
{...(import.meta.env.VITE_UMAMI_WEBSITE_ID && { "data-umami-event": "submit-collection-source" })}
|
|
88
|
+
>Submit</SubmitButtonBackendAPI>
|
|
89
|
+
</>
|
|
90
|
+
)}
|
|
91
|
+
</form>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default CollectionSource;
|