@opencloning/ui 1.0.2-test.0 → 1.1.0-dev.5
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 +49 -0
- package/package.json +11 -7
- package/scripts/inject-version.js +17 -0
- package/scripts/reset-version.js +16 -0
- package/src/components/DescriptionEditor.jsx +1 -2
- package/src/components/DragAndDropCloningHistoryWrapper.jsx +1 -1
- package/src/components/ExternalServicesStatusCheck.jsx +2 -1
- package/src/components/MainSequenceCheckBox.jsx +2 -5
- package/src/components/NetworkNode.jsx +1 -3
- package/src/components/OpenCloning.jsx +3 -3
- package/src/components/assembler/AssemblePartWidget.jsx +1 -1
- package/src/components/assembler/Assembler.jsx +1 -2
- package/src/components/dummy/DummyInterface.js +1 -2
- package/src/components/eLabFTW/eLabFTWInterface.js +1 -2
- package/src/components/form/EnzymeMultiSelect.cy.jsx +20 -9
- package/src/components/form/GetRequestMultiSelect.jsx +1 -3
- package/src/components/form/LabelWithTooltip.jsx +1 -1
- package/src/components/form/PostRequestSelect.jsx +1 -3
- package/src/components/index.js +1 -0
- package/src/components/navigation/ButtonWithMenu.jsx +1 -3
- package/src/components/navigation/MainAppBar.jsx +2 -9
- package/src/components/navigation/SelectTemplateDialog.jsx +1 -1
- package/src/components/primers/PrimerForm.jsx +2 -4
- package/src/components/primers/PrimerList.cy.jsx +86 -78
- package/src/components/primers/PrimerList.jsx +1 -1
- package/src/components/primers/PrimerTableRow.cy.jsx +31 -18
- package/src/components/primers/PrimerTableRow.jsx +1 -4
- package/src/components/primers/SelectPrimerForm.jsx +1 -1
- package/src/components/primers/import_primers/ImportPrimersButton.jsx +1 -4
- package/src/components/primers/import_primers/ImportPrimersTable.jsx +2 -5
- package/src/components/primers/import_primers/PrimerDatabaseImportForm.jsx +1 -2
- package/src/components/primers/primer_design/SequenceTabComponents/CollapsableLabel.jsx +1 -1
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignForm.jsx +1 -1
- package/src/components/primers/primer_design/SequenceTabComponents/RestrictionSpacerForm.jsx +1 -1
- package/src/components/primers/primer_details/PrimerInfoIcon.jsx +1 -2
- package/src/components/settings/SettingsTab.jsx +2 -3
- package/src/components/sources/CollectionSource.jsx +1 -1
- package/src/components/sources/MultipleOutputsSelector.jsx +1 -2
- package/src/components/sources/NewSourceBox.jsx +2 -3
- package/src/components/sources/PCRUnitForm.jsx +1 -3
- package/src/components/sources/Source.jsx +0 -3
- package/src/components/sources/SourceBox.jsx +2 -2
- package/src/components/sources/SourceFile.jsx +1 -2
- package/src/components/sources/SourceGenomeRegion.cy.jsx +24 -9
- package/src/components/sources/SourceGenomeRegion.jsx +1 -6
- package/src/components/sources/SourceRepositoryId.jsx +2 -9
- package/src/components/sources/SourceTypeSelector.jsx +3 -6
- package/src/components/verification/SequencingFileRow.jsx +1 -2
- package/src/components/verification/VerificationFileDialog.cy.jsx +35 -8
- package/src/hooks/useBackendRoute.js +3 -2
- package/src/hooks/useConfig.js +2 -0
- package/src/hooks/useDatabase.js +2 -2
- package/src/hooks/useHttpClient.js +9 -3
- package/src/hooks/useInitializeApp.js +15 -0
- package/src/hooks/useUrlParamsLoader.js +149 -0
- package/src/index.css +314 -0
- package/src/index.js +8 -0
- package/src/providers/ConfigProvider.jsx +51 -0
- package/src/providers/index.js +3 -0
- package/src/version.js +2 -0
- package/src/components/sources/KnownSourceErrors.jsx +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# @opencloning/ui
|
|
2
2
|
|
|
3
|
+
## 1.1.0-dev.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6df1c20: Last dummy test to check if auto pre-release works
|
|
8
|
+
- Updated dependencies [6df1c20]
|
|
9
|
+
- @opencloning/store@1.1.0-dev.5
|
|
10
|
+
- @opencloning/utils@1.1.0-dev.5
|
|
11
|
+
|
|
12
|
+
## 1.1.0-dev.4
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 07106ac: Dummy change to test that new action works
|
|
17
|
+
- @opencloning/store@1.1.0-dev.4
|
|
18
|
+
- @opencloning/utils@1.1.0-dev.4
|
|
19
|
+
|
|
20
|
+
## 1.1.0-test.3
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- a870a5e: Fix imports from mui and icons library, and add husky hook to validate that it does not happen again
|
|
25
|
+
- @opencloning/store@1.1.0-test.3
|
|
26
|
+
- @opencloning/utils@1.1.0-test.3
|
|
27
|
+
|
|
28
|
+
## 1.1.0-test.2
|
|
29
|
+
|
|
30
|
+
### Minor Changes
|
|
31
|
+
|
|
32
|
+
- d46f09d: Handle version display with scripts
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Updated dependencies [d46f09d]
|
|
37
|
+
- @opencloning/store@1.1.0-test.2
|
|
38
|
+
- @opencloning/utils@1.1.0-test.2
|
|
39
|
+
|
|
40
|
+
## 1.1.0-test.1
|
|
41
|
+
|
|
42
|
+
### Minor Changes
|
|
43
|
+
|
|
44
|
+
- 02dbc55: Switch to using provider for configuration rather than state
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [02dbc55]
|
|
49
|
+
- @opencloning/store@1.1.0-test.1
|
|
50
|
+
- @opencloning/utils@1.1.0-test.1
|
|
51
|
+
|
|
3
52
|
## 1.0.2-test.0
|
|
4
53
|
|
|
5
54
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencloning/ui",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0-dev.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"prepack": "node scripts/inject-version.js",
|
|
8
|
+
"postpack": "node scripts/reset-version.js"
|
|
9
|
+
},
|
|
6
10
|
"exports": {
|
|
7
11
|
".": "./src/index.js",
|
|
8
|
-
"./components": "./src/components/index.js"
|
|
12
|
+
"./components": "./src/components/index.js",
|
|
13
|
+
"./providers/ConfigProvider": "./src/providers/index.js",
|
|
14
|
+
"./hooks/useConfig": "./src/hooks/useConfig.js"
|
|
9
15
|
},
|
|
10
16
|
"repository": {
|
|
11
17
|
"type": "git",
|
|
@@ -17,8 +23,8 @@
|
|
|
17
23
|
"@emotion/styled": "^11.14.0",
|
|
18
24
|
"@mui/icons-material": "^5.15.17",
|
|
19
25
|
"@mui/material": "^5.15.17",
|
|
20
|
-
"@opencloning/store": "1.0
|
|
21
|
-
"@opencloning/utils": "1.0
|
|
26
|
+
"@opencloning/store": "1.1.0-dev.5",
|
|
27
|
+
"@opencloning/utils": "1.1.0-dev.5",
|
|
22
28
|
"@teselagen/bio-parsers": "^0.4.32",
|
|
23
29
|
"@teselagen/ove": "^0.8.18",
|
|
24
30
|
"@teselagen/range-utils": "^0.3.13",
|
|
@@ -26,9 +32,7 @@
|
|
|
26
32
|
"@zip.js/zip.js": "^2.7.62",
|
|
27
33
|
"axios": "^1.12.2",
|
|
28
34
|
"lodash-es": "^4.17.21",
|
|
29
|
-
"react": "^
|
|
30
|
-
"react-draggable": "^4.4.6",
|
|
31
|
-
"react-redux": "^8.1.3"
|
|
35
|
+
"react-draggable": "^4.4.6"
|
|
32
36
|
},
|
|
33
37
|
"peerDependencies": {
|
|
34
38
|
"react": "^18.3.1",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
10
|
+
const file = join(__dirname, '..', 'src', 'version.js');
|
|
11
|
+
|
|
12
|
+
const content = `// Version placeholder - replaced at publish time via prepack script
|
|
13
|
+
export const version = "${pkg.version}";
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
writeFileSync(file, content, 'utf-8');
|
|
17
|
+
console.log(`✓ Injected version ${pkg.version}`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFileSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const file = join(__dirname, '..', 'src', 'version.js');
|
|
10
|
+
|
|
11
|
+
const content = `// Version placeholder - replaced at publish time via prepack script
|
|
12
|
+
export const version = "__VERSION__";
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
writeFileSync(file, content, 'utf-8');
|
|
16
|
+
console.log('✓ Reset version placeholder');
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
2
|
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
3
|
-
import TextField from '@mui/material
|
|
4
|
-
import { Button, FormControl } from '@mui/material';
|
|
3
|
+
import { TextField, Button, FormControl } from '@mui/material';
|
|
5
4
|
import { cloningActions } from '@opencloning/store/cloning';
|
|
6
5
|
|
|
7
6
|
function DescriptionEditor() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Tooltip } from '@mui/material';
|
|
3
3
|
import { UploadFile } from '@mui/icons-material';
|
|
4
|
-
import CancelIcon from '@mui/icons-material
|
|
4
|
+
import { Cancel as CancelIcon } from '@mui/icons-material';
|
|
5
5
|
import useDragAndDropFile from '../hooks/useDragAndDropFile';
|
|
6
6
|
import LoadCloningHistoryWrapper from './LoadCloningHistoryWrapper';
|
|
7
7
|
|
|
@@ -4,6 +4,7 @@ import { useDispatch } from 'react-redux';
|
|
|
4
4
|
import useBackendRoute from '../hooks/useBackendRoute';
|
|
5
5
|
import useHttpClient from '../hooks/useHttpClient';
|
|
6
6
|
import { cloningActions } from '@opencloning/store/cloning';
|
|
7
|
+
import { version } from '../index';
|
|
7
8
|
|
|
8
9
|
const { updateAppInfo } = cloningActions;
|
|
9
10
|
|
|
@@ -16,7 +17,7 @@ function ExternalServicesStatusCheck() {
|
|
|
16
17
|
const backendRoute = useBackendRoute();
|
|
17
18
|
const httpClient = useHttpClient();
|
|
18
19
|
React.useEffect(() => {
|
|
19
|
-
dispatch(updateAppInfo({ frontendVersion:
|
|
20
|
+
dispatch(updateAppInfo({ frontendVersion: version }));
|
|
20
21
|
setLoading(true);
|
|
21
22
|
const checkServices = async () => {
|
|
22
23
|
const services = [
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import VisibilityIcon from '@mui/icons-material
|
|
3
|
-
import DownloadIcon from '@mui/icons-material/Download';
|
|
4
|
-
import EditIcon from '@mui/icons-material/Edit';
|
|
5
|
-
import CheckIcon from '@mui/icons-material/Rule';
|
|
6
|
-
import Tooltip from '@mui/material/Tooltip';
|
|
2
|
+
import { Visibility as VisibilityIcon, Download as DownloadIcon, Edit as EditIcon, Rule as CheckIcon } from '@mui/icons-material';
|
|
7
3
|
import { useDispatch, useSelector, useStore } from 'react-redux';
|
|
4
|
+
import { Tooltip } from '@mui/material';
|
|
8
5
|
import { isEqual } from 'lodash-es';
|
|
9
6
|
import { cloningActions } from '@opencloning/store/cloning';
|
|
10
7
|
import useStoreEditor from '../hooks/useStoreEditor';
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import React, { useRef } from 'react';
|
|
2
2
|
import { useDispatch, useSelector } from 'react-redux';
|
|
3
3
|
import { Box, Tooltip } from '@mui/material';
|
|
4
|
-
import VisibilityOffIcon from '@mui/icons-material
|
|
5
|
-
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
6
|
-
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
|
4
|
+
import { VisibilityOff as VisibilityOffIcon, Visibility as VisibilityIcon, AddCircle as AddCircleIcon } from '@mui/icons-material';
|
|
7
5
|
import { isEqual } from 'lodash-es';
|
|
8
6
|
import Source from './sources/Source';
|
|
9
7
|
import './NetworkTree.css';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react';
|
|
2
2
|
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
-
import Tabs from '@mui/material
|
|
3
|
+
import { Tabs } from '@mui/material';
|
|
4
4
|
import { isEqual } from 'lodash-es';
|
|
5
5
|
import DescriptionEditor from './DescriptionEditor';
|
|
6
6
|
import PrimerList from './primers/PrimerList';
|
|
@@ -13,6 +13,7 @@ import CloningHistory from './CloningHistory';
|
|
|
13
13
|
import SequenceTab from './SequenceTab';
|
|
14
14
|
import AppAlerts from './AppAlerts';
|
|
15
15
|
import Assembler from './assembler/Assembler';
|
|
16
|
+
import { useConfig } from '../hooks/useConfig';
|
|
16
17
|
|
|
17
18
|
const { setCurrentTab } = cloningActions;
|
|
18
19
|
|
|
@@ -21,8 +22,7 @@ function OpenCloning() {
|
|
|
21
22
|
const currentTab = useSelector((state) => state.cloning.currentTab);
|
|
22
23
|
const tabPanelsRef = useRef(null);
|
|
23
24
|
const [smallDevice, setSmallDevice] = useState(window.innerWidth < 600);
|
|
24
|
-
const hasAppBar =
|
|
25
|
-
const enableAssembler = useSelector((state) => state.cloning.config.enableAssembler);
|
|
25
|
+
const { showAppBar: hasAppBar, enableAssembler } = useConfig();
|
|
26
26
|
|
|
27
27
|
React.useEffect(() => {
|
|
28
28
|
const handleResize = () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { TextField, FormControl, InputLabel, Select, MenuItem, Box, Grid, Paper, Typography, Table, TableContainer, TableHead, TableBody, TableRow, TableCell, Button } from '@mui/material'
|
|
3
|
-
import ContentCopyIcon from '@mui/icons-material
|
|
3
|
+
import { ContentCopy as ContentCopyIcon } from '@mui/icons-material'
|
|
4
4
|
import AssemblerPart from './AssemblerPart'
|
|
5
5
|
|
|
6
6
|
/* eslint-disable camelcase */
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import data2 from './assembler_data2.json'
|
|
3
3
|
import { Alert, Autocomplete, Box, Button, CircularProgress, FormControl, IconButton, InputAdornment, InputLabel, MenuItem, Select, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField } from '@mui/material'
|
|
4
|
-
import ClearIcon from '@mui/icons-material
|
|
4
|
+
import { Clear as ClearIcon, Visibility as VisibilityIcon } from '@mui/icons-material';
|
|
5
5
|
import { useAssembler } from './useAssembler';
|
|
6
6
|
import { arrayCombinations } from '../eLabFTW/utils';
|
|
7
|
-
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
8
7
|
import { useDispatch } from 'react-redux';
|
|
9
8
|
import { cloningActions } from '@opencloning/store/cloning';
|
|
10
9
|
import RequestStatusWrapper from '../form/RequestStatusWrapper';
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import SaveIcon from '@mui/icons-material
|
|
2
|
-
import LinkIcon from '@mui/icons-material/Link';
|
|
1
|
+
import { Save as SaveIcon, Link as LinkIcon } from '@mui/icons-material';
|
|
3
2
|
import GetSequenceFileAndDatabaseIdComponent from './GetSequenceFileAndDatabaseIdComponent';
|
|
4
3
|
|
|
5
4
|
export default {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import SaveIcon from '@mui/icons-material
|
|
2
|
-
import LinkIcon from '@mui/icons-material/Link';
|
|
1
|
+
import { Save as SaveIcon, Link as LinkIcon } from '@mui/icons-material';
|
|
3
2
|
import GetSequenceFileAndDatabaseIdComponent from './GetSequenceFileAndDatabaseIdComponent';
|
|
4
3
|
import SubmitToDatabaseComponent from './SubmitToDatabaseComponent';
|
|
5
4
|
import PrimersNotInDatabaseComponent from './PrimersNotInDatabaseComponent';
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import EnzymeMultiSelect from './EnzymeMultiSelect';
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import { ConfigProvider } from '@opencloning/ui/providers/ConfigProvider';
|
|
4
|
+
|
|
5
|
+
const config = {
|
|
6
|
+
backendUrl: 'http://127.0.0.1:8000',
|
|
7
|
+
};
|
|
5
8
|
|
|
6
|
-
const { setConfig } = cloningActions;
|
|
7
9
|
describe('<EnzymeMultiSelect />', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
store.dispatch(setConfig({ backendUrl: 'http://127.0.0.1:8000' }));
|
|
10
|
-
});
|
|
11
10
|
it('can add and remove enzymes, sets enzymes', () => {
|
|
12
11
|
// see: https://on.cypress.io/mounting-react
|
|
13
12
|
const setEnzymesSpy = cy.spy().as('setEnzymesSpy');
|
|
14
|
-
cy.mount(
|
|
13
|
+
cy.mount(
|
|
14
|
+
<ConfigProvider config={config}>
|
|
15
|
+
<EnzymeMultiSelect setEnzymes={setEnzymesSpy} />
|
|
16
|
+
</ConfigProvider>
|
|
17
|
+
);
|
|
15
18
|
cy.get('.MuiInputBase-root').click();
|
|
16
19
|
// All enzymes shown
|
|
17
20
|
cy.get('div[role="presentation"]', { timeout: 20000 }).contains('AanI');
|
|
@@ -46,7 +49,11 @@ describe('<EnzymeMultiSelect />', () => {
|
|
|
46
49
|
statusCode: 500,
|
|
47
50
|
body: 'Server down',
|
|
48
51
|
});
|
|
49
|
-
cy.mount(
|
|
52
|
+
cy.mount(
|
|
53
|
+
<ConfigProvider config={config}>
|
|
54
|
+
<EnzymeMultiSelect setEnzymes={() => {}} />
|
|
55
|
+
</ConfigProvider>
|
|
56
|
+
);
|
|
50
57
|
cy.get('.MuiAlert-message').contains('Could not retrieve enzymes from server');
|
|
51
58
|
});
|
|
52
59
|
it('shows loading message', () => {
|
|
@@ -54,7 +61,11 @@ describe('<EnzymeMultiSelect />', () => {
|
|
|
54
61
|
delayMs: 1000,
|
|
55
62
|
body: { enzyme_names: ['EcoRI', 'SalI'] },
|
|
56
63
|
});
|
|
57
|
-
cy.mount(
|
|
64
|
+
cy.mount(
|
|
65
|
+
<ConfigProvider config={config}>
|
|
66
|
+
<EnzymeMultiSelect setEnzymes={() => {}} />
|
|
67
|
+
</ConfigProvider>
|
|
68
|
+
);
|
|
58
69
|
cy.get('.MuiCircularProgress-svg');
|
|
59
70
|
cy.contains('retrieving enzymes...').should('exist');
|
|
60
71
|
});
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import Autocomplete from '@mui/material
|
|
3
|
-
import TextField from '@mui/material/TextField';
|
|
4
|
-
import { Alert, Button, CircularProgress, FormControl, FormHelperText, InputLabel, MenuItem, Select } from '@mui/material';
|
|
2
|
+
import { Autocomplete, TextField, Alert, Button, CircularProgress, FormControl, FormHelperText, InputLabel, MenuItem, Select } from '@mui/material';
|
|
5
3
|
|
|
6
4
|
export default function GetRequestMultiSelect({
|
|
7
5
|
getOptionsFromResponse,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import Autocomplete from '@mui/material
|
|
3
|
-
import TextField from '@mui/material/TextField';
|
|
4
|
-
import { Alert, Button, FormControl } from '@mui/material';
|
|
2
|
+
import { Autocomplete, TextField, Alert, Button, FormControl } from '@mui/material';
|
|
5
3
|
|
|
6
4
|
export default function PostRequestSelect({ setValue, getOptions, getOptionLabel, isOptionEqualToValue, textLabel, disableFiltering = true, ...rest }) {
|
|
7
5
|
// The reason for disableFiltering is that we allow the server to filter the options,
|
package/src/components/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import Button from '@mui/material
|
|
3
|
-
import Menu from '@mui/material/Menu';
|
|
4
|
-
import MenuItem from '@mui/material/MenuItem';
|
|
2
|
+
import { Button, Menu, MenuItem } from '@mui/material';
|
|
5
3
|
|
|
6
4
|
export default function ButtonWithMenu({ children, menuItems }) {
|
|
7
5
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import AppBar from '@mui/material
|
|
3
|
-
import
|
|
4
|
-
import Toolbar from '@mui/material/Toolbar';
|
|
5
|
-
import Container from '@mui/material/Container';
|
|
6
|
-
import { Button, Tooltip } from '@mui/material';
|
|
2
|
+
import { AppBar, Box, Toolbar, Container, Button, Tooltip, IconButton, Menu, MenuItem } from '@mui/material';
|
|
3
|
+
import { Menu as MenuIcon } from '@mui/icons-material';
|
|
7
4
|
import './MainAppBar.css';
|
|
8
5
|
import { useDispatch } from 'react-redux';
|
|
9
|
-
import IconButton from '@mui/material/IconButton';
|
|
10
|
-
import Menu from '@mui/material/Menu';
|
|
11
|
-
import MenuItem from '@mui/material/MenuItem';
|
|
12
|
-
import MenuIcon from '@mui/icons-material/Menu';
|
|
13
6
|
import ButtonWithMenu from './ButtonWithMenu';
|
|
14
7
|
import { downloadCloningStrategyAsSvg, formatTemplate, loadHistoryFile, loadFilesToSessionStorage } from '@opencloning/utils/readNwrite';
|
|
15
8
|
import SelectExampleDialog from './SelectExampleDialog';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Accordion, AccordionDetails, AccordionSummary, Alert, Button, CircularProgress, Dialog, DialogContent, DialogTitle, IconButton, List, ListItem, ListItemButton, ListItemText } from '@mui/material';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import ExpandMoreIcon from '@mui/icons-material
|
|
4
|
+
import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material';
|
|
5
5
|
import useHttpClient from '../../hooks/useHttpClient';
|
|
6
6
|
|
|
7
7
|
function SelectTemplateDialog({ onClose, open }) {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import CheckCircleIcon from '@mui/icons-material
|
|
3
|
-
import
|
|
4
|
-
import Tooltip from '@mui/material/Tooltip';
|
|
5
|
-
import { IconButton } from '@mui/material';
|
|
2
|
+
import { CheckCircle as CheckCircleIcon, Cancel as CancelIcon } from '@mui/icons-material';
|
|
3
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
6
4
|
import ValidatedTextField from '../form/ValidatedTextField';
|
|
7
5
|
|
|
8
6
|
import './PrimerForm.css';
|
|
@@ -3,93 +3,101 @@ import PrimerList from './PrimerList';
|
|
|
3
3
|
import store from '@opencloning/store';
|
|
4
4
|
import { cloningActions } from '@opencloning/store/cloning';
|
|
5
5
|
import { Provider } from 'react-redux';
|
|
6
|
+
import { ConfigProvider } from '@opencloning/ui/providers/ConfigProvider';
|
|
6
7
|
|
|
7
|
-
const {
|
|
8
|
+
const { setPrimers, setGlobalPrimerSettings } = cloningActions;
|
|
9
|
+
|
|
10
|
+
const config = {
|
|
11
|
+
backendUrl: 'http://127.0.0.1:8000',
|
|
12
|
+
};
|
|
8
13
|
|
|
9
14
|
const mockReply = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
statusCode: 200, body: {
|
|
16
|
+
melting_temperature: 60, gc_content: .5, homodimer: {
|
|
17
|
+
melting_temperature: 0,
|
|
18
|
+
deltaG: 0,
|
|
19
|
+
figure: "dummy_figure"
|
|
20
|
+
},
|
|
21
|
+
hairpin: {
|
|
22
|
+
melting_temperature: 0,
|
|
23
|
+
deltaG: 0,
|
|
24
|
+
figure: "dummy_figure"
|
|
25
|
+
},
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
describe('PrimerList', () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
store.dispatch(setPrimers([
|
|
30
|
-
{ id: 1, name: 'P1', sequence: 'TCATTAAAGTTAACG' },
|
|
31
|
-
]));
|
|
30
|
+
it('displays the right information', () => {
|
|
31
|
+
store.dispatch(setPrimers([
|
|
32
|
+
{ id: 1, name: 'P1', sequence: 'TCATTAAAGTTAACG' },
|
|
33
|
+
]));
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let calls = 0;
|
|
47
|
-
store.dispatch(setPrimers([
|
|
48
|
-
{ id: 1, name: 'P1', sequence: 'AAA' },
|
|
49
|
-
]));
|
|
50
|
-
cy.intercept('POST', 'http://127.0.0.1:8000/primer_details*', (req) => {
|
|
51
|
-
calls += 1;
|
|
52
|
-
const respReply = calls === 1 ? mockReply : {
|
|
53
|
-
statusCode: 200, body: {
|
|
54
|
-
...mockReply.body,
|
|
55
|
-
melting_temperature: calls === 1 ? 60 : 70,
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
expect(req.body).to.deep.equal({
|
|
59
|
-
sequence: 'AAA',
|
|
60
|
-
settings: {
|
|
61
|
-
primer_dna_conc: calls === 1 ? 50 : 100,
|
|
62
|
-
primer_salt_monovalent: 50,
|
|
63
|
-
primer_salt_divalent: 1.5,
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
req.reply(respReply);
|
|
67
|
-
}).as('primerDetails');
|
|
35
|
+
cy.mount(
|
|
36
|
+
<Provider store={store}>
|
|
37
|
+
<ConfigProvider config={config}>
|
|
38
|
+
<PrimerList />
|
|
39
|
+
</ConfigProvider>
|
|
40
|
+
</Provider>
|
|
41
|
+
);
|
|
42
|
+
cy.get('td.name').contains('P1');
|
|
43
|
+
cy.get('td.length').contains('15');
|
|
44
|
+
cy.get('td.gc-content').contains('27');
|
|
45
|
+
cy.get('td.melting-temperature').contains('37.5');
|
|
46
|
+
cy.get('td.sequence').contains('TCATTAAAGTTAACG');
|
|
47
|
+
});
|
|
68
48
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
49
|
+
it('caches primer details across re-renders and re-renders on global settings change', () => {
|
|
50
|
+
let calls = 0;
|
|
51
|
+
store.dispatch(setPrimers([
|
|
52
|
+
{ id: 1, name: 'P1', sequence: 'AAA' },
|
|
53
|
+
]));
|
|
54
|
+
cy.intercept('POST', 'http://127.0.0.1:8000/primer_details*', (req) => {
|
|
55
|
+
calls += 1;
|
|
56
|
+
const respReply = calls === 1 ? mockReply : {
|
|
57
|
+
statusCode: 200, body: {
|
|
58
|
+
...mockReply.body,
|
|
59
|
+
melting_temperature: calls === 1 ? 60 : 70,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
expect(req.body).to.deep.equal({
|
|
63
|
+
sequence: 'AAA',
|
|
64
|
+
settings: {
|
|
65
|
+
primer_dna_conc: calls === 1 ? 50 : 100,
|
|
66
|
+
primer_salt_monovalent: 50,
|
|
67
|
+
primer_salt_divalent: 1.5,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
req.reply(respReply);
|
|
71
|
+
}).as('primerDetails');
|
|
91
72
|
|
|
92
|
-
|
|
73
|
+
// First mount triggers two network calls (one per unique primer sequence)
|
|
74
|
+
cy.mount(
|
|
75
|
+
<Provider store={store}>
|
|
76
|
+
<ConfigProvider config={config}>
|
|
77
|
+
<PrimerList />
|
|
78
|
+
</ConfigProvider>
|
|
79
|
+
</Provider>)
|
|
80
|
+
cy.contains('Loading...').should('not.exist');
|
|
81
|
+
cy.wait('@primerDetails');
|
|
82
|
+
cy.then(() => {
|
|
83
|
+
expect(calls).to.equal(1);
|
|
84
|
+
cy.mount(
|
|
85
|
+
<Provider store={store}>
|
|
86
|
+
<ConfigProvider config={config}>
|
|
87
|
+
<PrimerList />
|
|
88
|
+
</ConfigProvider>
|
|
89
|
+
</Provider>)
|
|
90
|
+
cy.then(() => {
|
|
91
|
+
expect(calls).to.equal(1);
|
|
92
|
+
});
|
|
93
|
+
store.dispatch(setGlobalPrimerSettings({ primer_dna_conc: 100 }))
|
|
94
|
+
cy.wait('@primerDetails');
|
|
95
|
+
cy.then(() => {
|
|
96
|
+
expect(calls).to.equal(2);
|
|
97
|
+
cy.get('td.melting-temperature').contains('70');
|
|
98
|
+
});
|
|
93
99
|
|
|
94
100
|
});
|
|
101
|
+
|
|
102
|
+
});
|
|
95
103
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
|
3
|
-
import Button from '@mui/material
|
|
3
|
+
import { Button } from '@mui/material';
|
|
4
4
|
import PrimerForm from './PrimerForm';
|
|
5
5
|
import PrimerTableRow from './PrimerTableRow';
|
|
6
6
|
import './PrimerList.css';
|