Skip to content

Commit

Permalink
Merge pull request #267 from legumeinfo/lis-association-search-element2
Browse files Browse the repository at this point in the history
Trait association search
  • Loading branch information
alancleary authored Nov 22, 2023
2 parents f75607e + 3b5c5fb commit d9d0608
Show file tree
Hide file tree
Showing 63 changed files with 6,974 additions and 3,957 deletions.
19 changes: 10 additions & 9 deletions dev/lis-gene-search-element.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ <h1>&lt;lis-gene-search-element&gt;</h1>
<p>
Optionally, all searches can be limited to a specific genus by setting the <code>genus</code> attribute/property.
This will cause the genus field of the search form to be automatically set and disabled so that users cannot change it.
You can try setting the <code>genus</code> property in this example using the <code>geneSearchElement</code> variable in the Web console.
Similarly, all searches can be limited to a specific species by setting the <code>species</code> attribute/property in conjunction with the <code>genus</code> attribute/property.
You can try setting the <code>genus</code> and <code>species</code> properties in this example using the <code>geneSearchElement</code> variable in the Web console.
</p>
<hr>

Expand All @@ -47,7 +48,7 @@ <h1>&lt;lis-gene-search-element&gt;</h1>
<script type="text/javascript">

// uses the LIS GraphQL API to get data used to construct the gene search form
const geneFormDataQuery = `
const formDataQuery = `
query FormDataQuery {
organisms {
results {
Expand All @@ -61,14 +62,14 @@ <h1>&lt;lis-gene-search-element&gt;</h1>
}
`;

function getGeneFormData({abortSignal}) {
return graphqlQuery(uri, geneFormDataQuery, {}, abortSignal)
function getFormData({abortSignal}) {
return graphqlQuery(uri, formDataQuery, {}, abortSignal)
.then(({data}) => {
// bin the strains by genus then species
const binnedFormData = {};
data.organisms.results.forEach(({genus, species, strains}) => {
if (!(genus in binnedFormData)) {
binnedFormData[genus] = {}
binnedFormData[genus] = {};
}
if (!(species in binnedFormData[genus])) {
binnedFormData[genus][species] = [];
Expand Down Expand Up @@ -133,8 +134,8 @@ <h1>&lt;lis-gene-search-element&gt;</h1>
page,
pageSize: 10,
};
// returns a Promise that resolves to an array of Gene objects the gene search
// Web Component knows how to parse: {name: string, description: string}[]

// shim the results for the Web Component
return graphqlQuery(uri, geneQuery, variables, abortSignal)
.then(({data}) => {
// extract the page info
Expand Down Expand Up @@ -171,9 +172,9 @@ <h1>&lt;lis-gene-search-element&gt;</h1>
});
}

// wait for the page to load before binding component properties
// bind component properties
const geneSearchElement = document.getElementById('gene-search');
geneSearchElement.formDataFunction = getGeneFormData;
geneSearchElement.formDataFunction = getFormData;
geneSearchElement.searchFunction = getGenes;

</script>
Expand Down
198 changes: 198 additions & 0 deletions dev/lis-trait-association-search-element.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<!DOCTYPE html>

<html>

<head>
<meta charset="utf-8" />
<title>LIS Web-Components - &lt;lis-trait-association-search-element&gt;</title>
<!-- CSS framework -->
<link rel="stylesheet" type="text/css" href="../node_modules/uikit/dist/css/uikit.min.css">
<script src="../node_modules/uikit/dist/js/uikit.min.js"></script>
<!-- web components polyfills -->
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="../node_modules/lit/polyfill-support.js"></script>
<!-- GraphQL -->
<script type="text/javascript" src="./graphql.js"></script>
<!-- web components -->
<script type="module" src="../lib/index.js"></script>
</head>

<body>

<div class="uk-container">
<h1>&lt;lis-trait-association-search-element&gt;</h1>
<p>
The <code>&lt;lis-trait-association-search-element&gt;</code> provides a form for performing trait association searches of GWAS/QTL studies and displays the results in a paginated view.
The search is performed using an external function provided to the component when it is added to the page.
In this example, the component performs a search for legume trait associations using the LIS GraphQL API query <code>traits</code>.
The form's genus and species selectors are also populated from this API.
See the source code for details.
Note that the form's input and page values are kept up to date in the URL query string parameters.
This allows users to share specific pages from a search via the URL and for the search history to be navigated via the Web browser's forward and back buttons.
If the query string parameters are present when the component loads then a search will be automatically performed with the query string parameter values.
</p>
<p>
Optionally, all searches can be limited to a specific genus by setting the <code>genus</code> attribute/property.
This will cause the genus field of the search form to be automatically set and disabled so that users cannot change it.
Similarly, all searches can be limited to a specific species by setting the <code>species</code> attribute/property in conjunction with the <code>genus</code> attribute/property.
You can try setting the <code>genus</code> and <code>species</code> properties in this example using the <code>traitAssociationSearchElement</code> variable in the Web console.
</p>

<hr>

<!-- the custom trait association search element -->
<lis-trait-association-search-element id="trait-association-search"></lis-trait-association-search-element>

</div>

<!-- set the search function by property because functions can't be set as attributes -->
<script type="text/javascript">

// uses the LIS GraphQL API to get data used to construct the search form
const formDataQuery = `
query FormDataQuery {
organisms {
results {
genus
species
}
}
}
`;

function getFormData({abortSignal}) {
return graphqlQuery(uri, formDataQuery, {}, abortSignal)
.then(({data}) => {
// bin the strains by genus then species
const binnedFormData = {};
data.organisms.results.forEach(({genus, species}) => {
if (!(genus in binnedFormData)) {
binnedFormData[genus] = [];
}
binnedFormData[genus].push(species);
});
// collapse the bins into arrays of objects
const genuses =
Object.entries(binnedFormData).map(([genus, binnedSpecies]) => {
const species = binnedSpecies.map((species) => {
return {species};
});
return {genus, species};
});
// return the expected form data object
return {genuses};
});
}

const traitQuery = `
query TraitQuery($pageSize: Int, $page: Int, $name: String, $studyType: String, $publicationId: String, $author: String) {
traits(pageSize: $pageSize, page: $page, name: $name, studyType: $studyType, publicationId: $publicationId, author: $author) {
results {
name
qtlStudy {
identifier
synopsis
description
genotypes
organism {
genus
species
}
dataSet {
publication {
firstAuthor
pubMedId
doi
}
}
}
gwas {
identifier
synopsis
description
genotypes
organism {
genus
species
}
dataSet {
publication {
firstAuthor
pubMedId
doi
}
}
}
}
pageInfo {
currentPage
pageSize
numResults
pageCount
hasPreviousPage
hasNextPage
}
}
}
`;

// search function
function getTraits(searchData, page,{abortSignal}) {
const genus = searchData['genus'];
const species = searchData['species'];
let studyType = searchData['type'];
if (studyType === 'QTL') {
studyType = 'QTLStudy';
}
const name = searchData['traits'];
const publicationId = searchData['pubId'];
const author = searchData['author'];
const variables = {
genus,
species,
studyType,
name,
publicationId,
author,
page,
pageSize: 10,
};

// shim the results for the Web Component
return graphqlQuery(uri, traitQuery, variables, abortSignal)
.then(({data}) => {
// extract the page info
const {hasNextPage: hasNext, numResults, pageSize, pageCount: numPages}
= data.traits.pageInfo;

// flatten results
const results =
data.traits.results.map(({name, ...trait}) => {
const type = trait.qtlStudy ? 'QTL' : 'GWAS';
const study = trait.qtlStudy || trait.gwas;
const {identifier, synopsis, description, genotypes} = study;
return {
identifier,
type,
synopsis,
description,
name,
genotypes
};
});
// construct the expected paginated results object
const paginatedResults = {hasNext, numResults, pageSize, numPages, results};
return paginatedResults;
});
}

// bind component properties
const traitAssociationSearchElement = document.getElementById('trait-association-search');
traitAssociationSearchElement.formDataFunction = getFormData;
traitAssociationSearchElement.searchFunction = getTraits;

</script>

</body>

</html>
2 changes: 1 addition & 1 deletion docs/assets/navigation.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d9d0608

Please sign in to comment.