Skip to content

Commit 02a0615

Browse files
author
Sebastian Benjamin
authored
Merge pull request #104 from BimberLab/21.7_fb_isolate_search
Isolated search component
2 parents a4640ab + 8679b83 commit 02a0615

File tree

9 files changed

+20674
-370
lines changed

9 files changed

+20674
-370
lines changed

SequenceAnalysis/src/org/labkey/sequenceanalysis/pipeline/ReadsetCreationTask.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,3 +607,4 @@ private SequencePipelineSettings getSettings()
607607
return _settings;
608608
}
609609
}
610+

jbrowse/package-lock.json

Lines changed: 20467 additions & 365 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jbrowse/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
},
1515
"dependencies": {
1616
"@jbrowse/cli": "^1.4.4",
17-
"@jbrowse/react-linear-genome-view": "^1.4.4",
17+
"@jbrowse/react-linear-genome-view": "^1.5.0",
1818
"@labkey/components": "2.67.1",
1919
"assert": "^2.0.0",
2020
"browserify-zlib": "^0.2.0",
2121
"buffer": "^6.0.3",
22+
"mobx": "^5.0.0",
23+
"mobx-react": "^6.0.0",
24+
"mobx-state-tree": "3.14.1",
2225
"node-polyfill-webpack-plugin": "1.1.0",
2326
"path-browserify": "^1.0.1",
2427
"pkg": "^5.3.2",
@@ -30,7 +33,6 @@
3033
"vm-browserify": "^1.1.2"
3134
},
3235
"devDependencies": {
33-
"rimraf": "~3.0.2",
3436
"@labkey/build": "^4.0.1",
3537
"@types/enzyme": "^3.10.9",
3638
"@types/jest": "^27.0.1",
@@ -40,9 +42,10 @@
4042
"enzyme-adapter-react-16": "^1.15.6",
4143
"enzyme-to-json": "^3.6.2",
4244
"jest": "^27.2.0",
43-
"jest-teamcity-reporter": "^0.9.0",
4445
"jest-cli": "^27.2.0",
46+
"jest-teamcity-reporter": "^0.9.0",
4547
"react-test-renderer": "^17.0.2",
48+
"rimraf": "~3.0.2",
4649
"ts-jest": "^27.0.5"
4750
},
4851
"browser": {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { useState } from 'react'
2+
import { observer } from 'mobx-react'
3+
import { getSession } from '@jbrowse/core/util'
4+
import { RefNameAutocomplete } from '@jbrowse/plugin-linear-genome-view'
5+
import { ViewModel } from '@jbrowse/react-linear-genome-view'
6+
import BaseResult, { RefSequenceResult }from '@jbrowse/core/TextSearch/BaseResults'
7+
8+
const RefNameAutocompleteWrapper = observer(({ viewState, sessionParam }: { viewState: ViewModel, sessionParam: any}) => {
9+
function navigate() {
10+
if (op) {
11+
window.location.href = location.pathname.split('/').slice(0,-1).join('/') + '/jbrowse-jbrowse.view?session=' + sessionParam + '&location=' + op.getLocation()
12+
}
13+
}
14+
15+
const { session } = viewState
16+
const { view } = session
17+
18+
const { assemblyNames, assemblyManager } = getSession(view)
19+
20+
const [selectedAsm, setSelectedAsm] = useState(assemblyNames[0])
21+
const [op, setOption] = useState<BaseResult | undefined>()
22+
23+
const assembly = assemblyManager.get(selectedAsm)
24+
const regions = assembly?.regions || []
25+
26+
const selectedRegion = op?.getLocation()
27+
const message = !assemblyNames.length ? 'No configured assemblies' : ''
28+
29+
return (
30+
<span>
31+
<RefNameAutocomplete
32+
model={view}
33+
assemblyName={message ? undefined : selectedAsm}
34+
value={selectedRegion}
35+
onSelect={option => {
36+
setOption(option)
37+
}}
38+
TextFieldProps={{
39+
margin: 'normal',
40+
variant: 'outlined',
41+
helperText: 'Enter a sequence or location',
42+
}}
43+
/>
44+
45+
<button onClick={navigate}>
46+
Open
47+
</button>
48+
</span>
49+
)
50+
})
51+
52+
export default RefNameAutocompleteWrapper
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React, {useState, useEffect} from 'react'
2+
//import 'fontsource-roboto'
3+
import {
4+
createViewState,
5+
JBrowseLinearGenomeView,
6+
loadPlugins,
7+
} from '@jbrowse/react-linear-genome-view'
8+
9+
import { PluginConstructor } from '@jbrowse/core/Plugin'
10+
import { Ajax, ActionURL } from '@labkey/api'
11+
import MyProjectPlugin from "./plugins/MyProjectPlugin/index"
12+
import LogSession from "./plugins/LogSession/index"
13+
import ExtendedVariantPlugin from "./plugins/ExtendedVariantPlugin/index"
14+
import RefNameAutocompleteWrapper from "./RefNameAutocompleteWrapper"
15+
16+
const nativePlugins = [MyProjectPlugin, ExtendedVariantPlugin, LogSession]
17+
18+
function generateViewState(genome, plugins){
19+
return createViewState({
20+
assembly: genome.assembly ?? genome.assemblies,
21+
tracks: genome.tracks,
22+
configuration: genome.configuration,
23+
plugins: plugins.concat(nativePlugins),
24+
location: genome.location,
25+
defaultSession: genome.defaultSession,
26+
onChange: genome.onChange
27+
})
28+
}
29+
30+
function Search(){
31+
// Grab session + location information from URL params
32+
const queryParam = new URLSearchParams(window.location.search);
33+
const session = queryParam.get('session')
34+
const location = queryParam.get('location')
35+
36+
const [state, setState] = useState(null);
37+
const [plugins, setPlugins] = useState<PluginConstructor[]>();
38+
39+
// Get the LinearGenomeViewModel from the API, providing the session as a parameter
40+
useEffect(() => {
41+
Ajax.request({
42+
url: ActionURL.buildURL('jbrowse', 'getSession.api'),
43+
method: 'GET',
44+
success: async function(res){
45+
let jsonRes = JSON.parse(res.response);
46+
if (location) {
47+
jsonRes.location = location;
48+
}
49+
50+
var loadedPlugins = null
51+
if (jsonRes.plugins != null){
52+
try {
53+
loadedPlugins = await loadPlugins(jsonRes.plugins);
54+
} catch (error) {
55+
console.error("Error: ", error)
56+
}
57+
setPlugins(loadedPlugins);
58+
} else {
59+
loadedPlugins = []
60+
}
61+
setState(generateViewState(jsonRes, loadedPlugins));
62+
},
63+
failure: function(res){
64+
setState("invalid");
65+
console.log(res);
66+
},
67+
params: {session: session}
68+
});
69+
}, []);
70+
71+
// Error handle and then render the component
72+
if(session === null){
73+
return(<p>Error - no session provided.</p>)
74+
}
75+
else if (state === null){
76+
return (<p>Loading...</p>)
77+
}
78+
else if (state == "invalid") {
79+
return (<p>Error fetching config. See console for more details</p>)
80+
}
81+
return (
82+
<RefNameAutocompleteWrapper viewState={state} sessionParam={session}/>
83+
)
84+
}
85+
86+
export default Search
87+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
4+
import Search from '../Search'
5+
import { Ajax, Utils, ActionURL } from '@labkey/api'
6+
7+
const queryParam = new URLSearchParams(window.location.search);
8+
const session = queryParam.get('session')
9+
10+
11+
// Need to wait for container element to be available in labkey wrapper before render
12+
window.addEventListener('DOMContentLoaded', (event) => {
13+
ReactDOM.render(<Search />, document.getElementById('app'))
14+
});

jbrowse/src/client/JBrowse/Browser/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ const session = queryParam.get('session')
1111
// Need to wait for container element to be available in labkey wrapper before render
1212
window.addEventListener('DOMContentLoaded', (event) => {
1313
ReactDOM.render(<View />, document.getElementById('app'))
14-
});
14+
});

jbrowse/src/client/entryPoints.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,12 @@ module.exports = {
1010
permission: 'read',
1111
template: 'app',
1212
path: './src/client/JBrowse/Browser'
13+
},
14+
{
15+
name: 'search',
16+
title: 'JBrowse Search',
17+
permission: 'read',
18+
template: 'app',
19+
path: './src/client/JBrowse/Browser/Search'
1320
}]
14-
};
21+
};

jbrowse/test/src/org/labkey/test/tests/external/labModules/JBrowseTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.labkey.test.tests.external.labModules;
1717

18+
import org.apache.commons.lang3.StringUtils;
1819
import org.junit.Assert;
1920
import org.junit.Test;
2021
import org.junit.experimental.categories.Category;
@@ -38,6 +39,7 @@
3839
import org.labkey.test.util.ext4cmp.Ext4FieldRef;
3940
import org.labkey.test.util.external.labModules.LabModuleHelper;
4041
import org.openqa.selenium.By;
42+
import org.openqa.selenium.Keys;
4143
import org.openqa.selenium.WebElement;
4244
import org.openqa.selenium.interactions.Actions;
4345

@@ -89,6 +91,12 @@ private void testDemoNoSession()
8991
waitForElement(Locator.tagWithText("p", "Error - no session provided."));
9092
}
9193

94+
private void testDemoSearchNoSession()
95+
{
96+
beginAt("/home/jbrowse-search.view?");
97+
waitForElement(Locator.tagWithText("p", "Error - no session provided."));
98+
}
99+
92100
private void testDemoUi()
93101
{
94102
beginAt("/home/jbrowse-jbrowse.view?session=demo");
@@ -384,6 +392,10 @@ private void testOutputFileProcessing() throws Exception
384392
dr = DataRegionTable.findDataRegionWithinWebpart(this, "Additional Tracks Provided By The Base Genome");
385393
Assert.assertEquals("Incorrect row count", 3, dr.getDataRowCount());
386394

395+
// Store session ID for later use
396+
String sessionId = StringUtils.trimToNull(getUrlParam("databaseId"));
397+
Assert.assertNotNull("Missing session ID on URL", sessionId);
398+
387399
// Now ensure default tracks appear:
388400
beginAt("/project/" + getContainerId() + "/begin.view");
389401
_helper.clickNavPanelItemAndWait("JBrowse Sessions:", 1);
@@ -392,6 +404,32 @@ private void testOutputFileProcessing() throws Exception
392404
waitAndClick(Locator.tagContainingText("span", "Show all regions in assembly").withClass("MuiButton-label"));
393405
waitForElement(Locator.tagWithText("span", "fakeData.gff").withClass("MuiTypography-root"));
394406
waitForElement(Locator.tagWithText("span", "fakeData.bed").withClass("MuiTypography-root"));
407+
408+
//Now test search:
409+
beginAt("/jbrowse/" + getContainerId() + "/search.view?session=" + sessionId);
410+
String search = "Ga";
411+
String optionText = "Gag";
412+
String expected = "SIVmac239_Test:10373..10493";
413+
414+
Locator searchLocator = Locator.tagWithClass("input", "MuiInputBase-input");
415+
waitForElement(searchLocator);
416+
WebElement searchBox = searchLocator.findElement(getDriver());
417+
searchBox.sendKeys(search);
418+
419+
Locator optionLocator = Locator.tagWithText("li", optionText);
420+
waitForElement(optionLocator);
421+
WebElement locator = optionLocator.findElement(getDriver());
422+
int locatorIndex = Integer.parseInt(locator.getAttribute("data-option-index"));
423+
424+
for (int i = 0; i <= locatorIndex; i++)
425+
{
426+
searchBox.sendKeys(Keys.ARROW_DOWN);
427+
}
428+
429+
searchBox.sendKeys(Keys.ENTER);
430+
431+
waitForElement(searchLocator);
432+
Assert.assertEquals("Correct ID selected", expected, searchBox.getAttribute("value"));
395433
}
396434

397435
public static <T> Collector<T, ?, T> toSingleton() {

0 commit comments

Comments
 (0)