diff --git a/src/client/common/config.js b/src/client/common/config.js index fdb492f0e..064bf09cc 100644 --- a/src/client/common/config.js +++ b/src/client/common/config.js @@ -22,7 +22,9 @@ const databases = [ ['Chemical Component Dictionary', 'http://identifiers.org/pdb-ccd/', ''], ['CAS', 'http://identifiers.org/cas/', ''], ['HPRD',' http://identifiers.org/hprd/',''], - ['RefSeq',' http://identifiers.org/refseq/',''] + ['RefSeq',' http://identifiers.org/refseq/',''], + ['NCBI Gene','http://identifiers.org/ncbigene/',''], + ['Gene Cards','http://identifiers.org/genecards/',''] ]; const publicationsURL = 'http://identifiers.org/pubmed/'; diff --git a/src/client/features/search/index.js b/src/client/features/search/index.js index 10eecf506..adb6e5e7a 100644 --- a/src/client/features/search/index.js +++ b/src/client/features/search/index.js @@ -9,6 +9,7 @@ const classNames = require('classnames'); const Icon = require('../../common/components').Icon; const { ServerAPI } = require('../../services'); +const databases = require('../../common/config').databases; class Search extends React.Component { @@ -66,24 +67,36 @@ class Search extends React.Component { genes: state.query.q.trim(), target: 'UNIPROT', }; - if(query.genes.includes(' ')){ - this.setState({ - landingLoading: false, - landing:[] - }); - return; - } this.setState({ landingLoading: true - },()=>{ - ServerAPI.geneQuery(query).then(geneQueryResult=>{ - geneQueryResult= geneQueryResult.geneInfo.map(gene=>gene.convertedAlias); - if(!_.isEmpty(geneQueryResult)){ - ServerAPI.getProteinInformation(geneQueryResult).then(infoResult=>{ + }); + ServerAPI.geneQuery(query).then(res=>{ + res=res.geneInfo; + if(!_.isEmpty(res)){ + const ids=res.map(gene=>gene.convertedAlias).join(','); + ServerAPI.getProteinInformation(ids).then(result=>{ + const landing=result.map(gene=>{ + let links=_.mapValues( _.pickBy({'Uniprot':{id:gene.accession},//to match the format the other 2 links are in + 'HGNC':gene.dbReferences.filter(entry =>entry.type=='HGNC')[0], + 'NCBI Gene':gene.dbReferences.filter(entry =>entry.type=='GeneID')[0], + 'Gene Cards':gene.dbReferences.filter(entry =>entry.type=='GeneCards')[0]}), + (value,key)=>{ + let link = databases.filter(databaseValue => key.toUpperCase() === databaseValue[0].toUpperCase()); + return link[0][1] + link[0][2] + value.id; + }); + + return { + accession:gene.accession, + name:gene.protein.recommendedName.fullName.value, + function: gene.comments[0].type==='FUNCTION' && gene.comments[0].text[0].value, + synonyms: _.hasIn(gene,'protein.alternativeName') && gene.protein.alternativeName.map(obj => obj.fullName.value).join(', '), + showMore:{full:!(result.length>1),function:false,synonyms:false}, + links:links + };}); + this.setState({ landingLoading: false, - landing:infoResult, - landingShowMore: [false,false], + landing:landing, }); }); } @@ -94,7 +107,6 @@ class Search extends React.Component { }); } }); - }); } componentDidMount() { @@ -188,62 +200,64 @@ class Search extends React.Component { ]) : h('div.search-hit-counter', `${state.searchResults.length} result${state.searchResults.length === 1 ? '' : 's'}`); + const handelShowMoreClick= (varToToggle,index) => { + const landing=state.landing; + landing[index].showMore[varToToggle]=!landing[index].showMore[varToToggle]; + this.setState({ landing:landing }); + }; + + const expandableText = (length,text,charToCutOn,type,cssClass,toggleVar,index)=>{ + let result = null; + const varToToggle= state.landing[index].showMore[toggleVar]; + const textToUse= (varToToggle|| text.length<=length)? + text+' ': text.slice(0,text.lastIndexOf(charToCutOn,length))+' '; + result=[h(`${type}`,{className:cssClass,key:'text'},textToUse)]; + if(text.length>length){ + result.push(h(`${type}.search-landing-link`,{onClick: ()=> handelShowMoreClick(toggleVar,index),key:'showMore'}, + varToToggle ? '« less': 'more »')); + } + return result; + }; + const landing = (state.landingLoading ) ? - h('div.search-landing.innner',[h(Loader, { loaded:!state.landingLoading , options: { color: '#16A085',position:'relative', top: '15px' }})]): - state.landing.map(box=>{ + h('div.search-landing-innner',[h(Loader, { loaded:false , options: { color: '#16A085',position:'relative', top: '15px' }})]): + state.landing.map((box,index)=>{ + const multipleBoxes = state.landing.length>1; + const title = [h('strong',{key:'name'},box.name),]; + if(multipleBoxes){ + title.push(h('strong.material-icons',{key:'arrow'},state.landing[index].showMore.full? 'expand_less': 'expand_more')); + } - const name = h('strong.search-landing-title',box.protein.recommendedName.fullName.value); - let synonyms=null; - if(_.hasIn(box,'protein.alternativeName')){ - const synonymsLength=115; - const synonymsTextLong= box.protein.alternativeName.map(obj => obj.fullName.value).join(', '); - const synonymsText= (state.landingShowMore[0]|| synonymsTextLong.length<=synonymsLength)? - synonymsTextLong+' ': synonymsTextLong.slice(0,synonymsTextLong.lastIndexOf(',',synonymsLength))+' '; - synonyms=[h('i.search-landing-small',{key:'text'},synonymsText)]; - if(synonymsTextLong.length>synonymsLength){ - synonyms.push( - h('i.search-landing-link',{onClick: e => this.setState({ landingShowMore: [!state.landingShowMore[0],state.landingShowMore[1]]}), - key:'showMore', className:classNames('search-landing-link','search-landing-small')},state.landingShowMore[0]? '« less': 'more »') - ); - } + let synonyms=[]; + if(box.synonyms){ + synonyms=expandableText(112, box.synonyms,',','i','search-landing-small','synonyms',index); } - let functions=null; - if(_.hasIn(box,'comments[0].text') && box.comments[0].type==='FUNCTION'){ - const functionsLength=280; - const functionTextLong=box.comments[0].text[0].value; - const functionText = (state.landingShowMore[1] || functionTextLong.length<=functionsLength)? - functionTextLong: functionTextLong.slice(0,functionTextLong.lastIndexOf(' ',functionsLength)); - functions=[h('span.search-landing-function',{key:'text'},functionText)]; - if(functionTextLong.length>functionsLength){ - functions.push( - h('span.search-landing-link',{onClick: e => this.setState({ landingShowMore: [state.landingShowMore[0],!state.landingShowMore[1]]}), key:'showMore'}, - state.landingShowMore[1]? '« less': 'more »') - ); - } + let functions=[]; + if(box.function){ + functions=expandableText(270, box.function,' ','span','search-landing-function','function',index); } - const ids = [ - box.accession, - box.dbReferences.filter(entry =>entry.type=='HGNC')[0], - box.dbReferences.filter(entry =>entry.type=='GeneID')[0], - box.dbReferences.filter(entry =>entry.type=='GeneCards')[0], - ]; - let links=[{text:'UniProt', link:`http://www.uniprot.org/uniprot/${ids[0]}`}]; - if(ids[1]) {links.push({text:'HGNC',link:`https://www.genenames.org/cgi-bin/gene_symbol_report?hgnc_id=${ids[1].id}`});} - if(ids[2]) {links.push({text:'NCBI Gene',link:`https://www.ncbi.nlm.nih.gov/gene/${ids[2].id}`});} - if(ids[3]) {links.push({text:'Gene Cards',link:`http://www.genecards.org/cgi-bin/carddisp.pl?gene=${ids[3].id}`});} - links=links.map(link=>{return h('a.search-landing-link',{key: link.text, href: link.link},link.text);}); - return h('div.search-landing.innner',{key: box.accession},[ - h('div.search-landing-section',[name]), - h('div.search-landing-section',[synonyms]), - h('div.search-landing-section',[functions]), - h('div.search-landing-section',[links]), - h(Link, { to: { pathname: '/interactions',search: queryString.stringify({ ID: box.accession })}, target: '_blank',className: 'search-landing-interactions' }, [ + let links=[]; + _.forIn((box.links),(value,key)=>{ + links.push(h('a.search-landing-link',{key: key, href: value},key)); + }); + + return [ + h('div.search-landing-title',{key:'title', + onClick: () => {if(multipleBoxes){handelShowMoreClick('full',index);}}, + className:classNames('search-landing-title',{'search-landing-title-multiple':multipleBoxes}), + },[title]), + box.showMore.full && + h('div.search-landing-innner',{key: box.accession},[ + h('div.search-landing-section',{key: 'synonyms'},[synonyms]), + h('div.search-landing-section',{key: 'functions'},[functions]), + h('div.search-landing-section',{key: 'links'},[links]), + h(Link, { to: { pathname: '/interactions',search: queryString.stringify({ ID: box.accession })}, + target: '_blank',className: 'search-landing-interactions', key:'interactions' }, [ h('button.search-landing-button', 'View Interactions'), - ]), - ]); - }); + ])]) + ];}); return h('div.search', [ h('div.search-header-container', [ diff --git a/src/client/services/server-api/index.js b/src/client/services/server-api/index.js index 7352543d0..efd4e0271 100644 --- a/src/client/services/server-api/index.js +++ b/src/client/services/server-api/index.js @@ -33,7 +33,7 @@ const ServerAPI = { }, getProteinInformation(uniprotId){ - return fetch(`https://www.ebi.ac.uk/proteins/api/proteins?offset=0&size=1&accession=${uniprotId}`,defaultFetchOpts).then(res => res.json()); + return fetch(`https://www.ebi.ac.uk/proteins/api/proteins?offset=0&accession=${uniprotId}`,defaultFetchOpts).then(res => res.json()); }, getNeighborhood(uniprotId,format){ diff --git a/src/styles/features/search.css b/src/styles/features/search.css index a5aa0be61..29ad46bc7 100644 --- a/src/styles/features/search.css +++ b/src/styles/features/search.css @@ -221,19 +221,25 @@ text-overflow: ellipsis; } .search-landing{ + max-width: var(--search-width); width: 100%; + background-color: var(--light-base-colour); + margin-top: 20px; } -.search-landing.innner{ - margin-top: 20px; +.search-landing-innner{ + box-sizing: border-box; + margin-bottom: 1px; + border:1px var(--light-base-colour-dark) solid; padding:10px; + padding-top: 5px; max-width: var(--search-width); width: 100%; - min-height: 30px; + min-height: 50px; background-color: var(--light-base-colour); display: flex; - vertical-align: middle; - justify-content: space-between; + vertical-align: center; + justify-content: baseline; flex-direction: column; } @@ -251,6 +257,17 @@ .search-landing-title{ font-size: 15pt; + padding:10px; + background-color: var(--light-base-colour-dark); + border-bottom:1px var(--light-base-colour-darker) solid; +} + +.search-landing-title-multiple{ + cursor:pointer; +} + +.search-landing-title:last-of-type{ + border-bottom-width:0px } .search-landing-link { @@ -265,7 +282,6 @@ } .search-landing-button{ - background-color: #0073BF; color: white; border-radius: 0px; @@ -280,10 +296,12 @@ background-color: #6E9ABD; color: white; } + .search-landing-interactions { margin-top: 5px; width: 100%; } + .search-item { display: flex; vertical-align: middle;