Skip to content

Commit 4a33df0

Browse files
authored
Merge pull request #2274 from broadinstitute/jb-hca-result-filter
Adding Options button for search requests
2 parents cf82c1d + 027db32 commit 4a33df0

File tree

13 files changed

+245
-46
lines changed

13 files changed

+245
-46
lines changed

app/controllers/api/v1/search_controller.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ class SearchController < ApiBaseController
6464
key :required, false
6565
key :type, :string
6666
end
67+
parameter do
68+
key :name, :external
69+
key :in, :query
70+
key :description, 'External search services to query in addition to SCP'
71+
key :required, false
72+
key :type, :string
73+
key :enum, %w[hca]
74+
end
6775
parameter do
6876
key :name, :page
6977
key :in, :query
@@ -269,10 +277,11 @@ def index
269277
# convert to array to allow appending external search results (Azul, TDR, etc.)
270278
@studies = @studies.to_a
271279

272-
# perform Azul search if there are facets/terms provided by user
280+
# perform Azul search if there are facets/terms provided by user, and they requested HCA results
273281
# run this before inferred search so that they are weighted and sorted correctly
274282
# skip if user is searching inside a collection or they are performing global gene search
275-
if (@facets.present? || @term_list.present?) && (@selected_branding_group.nil? && @search_type == :study)
283+
include_azul = params[:external] == 'hca' && @search_type == :study && @selected_branding_group.nil?
284+
if (@facets.present? || @term_list.present?) && include_azul
276285
begin
277286
azul_results = ::AzulSearchService.append_results_to_studies(@studies,
278287
selected_facets: @facets,

app/javascript/components/search/controls/FacetsPanel.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import FacetControl from './FacetControl'
88
import CombinedFacetControl from './CombinedFacetControl'
99
import MoreFacetsButton from './MoreFacetsButton'
1010
import { SearchFacetContext } from '~/providers/SearchFacetProvider'
11+
import OptionsButton from '~/components/search/controls/OptionsButton'
1112

1213
const defaultFacetIds = ['disease', 'species']
1314
const moreFacetIds = [
@@ -71,6 +72,7 @@ export default function FacetsPanel() {
7172
}
7273
<CombinedFacetControl controlDisplayName="cell type" facetIds={['cell_type', 'cell_type__custom']}/>
7374
<MoreFacetsButton facets={moreFacets} />
75+
<OptionsButton />
7476
<Modal
7577
show={showSearchHelpModal}
7678
onHide={() => closeModal(setShowSearchHelpModal)}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React, { useState, useContext } from 'react'
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3+
import { faCogs } from '@fortawesome/free-solid-svg-icons'
4+
import { Popover, OverlayTrigger } from 'react-bootstrap'
5+
import { StudySearchContext } from '~/providers/StudySearchProvider'
6+
7+
import OptionsControl from '~/components/search/controls/OptionsControl'
8+
9+
export default function OptionsButton() {
10+
const searchContext = useContext(StudySearchContext)
11+
const [showOptions, setShowOptions] = useState(false)
12+
const configuredOptions = [
13+
{ searchProp: 'external', value: 'hca', label: 'Include HCA results' }
14+
]
15+
16+
const optionsPopover = <Popover data-analytics-name='search-options-menu' id='search-options-menu'>
17+
<ul className="facet-filter-list">
18+
{
19+
configuredOptions.map((option) => {
20+
return <OptionsControl
21+
key={option.searchProp}
22+
searchContext={searchContext}
23+
searchProp={option.searchProp}
24+
value={option.value}
25+
label={option.label}/>
26+
})
27+
}
28+
</ul>
29+
</Popover>
30+
31+
return (
32+
<OverlayTrigger trigger={['click']} placement='bottom' animation={false} overlay={optionsPopover}>
33+
<span id="search-options-button" data-testid="search-options-button"
34+
className={`facet ${showOptions ? 'active' : ''}`}>
35+
<a onClick={() => setShowOptions(!showOptions)}>
36+
<FontAwesomeIcon className="icon-left" icon={faCogs}/>Options
37+
</a>
38+
</span>
39+
</OverlayTrigger>
40+
)
41+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React, { useState } from 'react'
2+
3+
export default function OptionsControl({searchContext, searchProp, value, label}) {
4+
const defaultChecked = searchContext.params[searchProp] === value
5+
const [isChecked, setIsChecked] = useState(defaultChecked)
6+
7+
/** toggle state of checkbox */
8+
function toggleCheckbox(checked) {
9+
setIsChecked(checked)
10+
searchContext.updateSearch({ [searchProp] : checked ? value : null })
11+
}
12+
13+
return (
14+
<li id={`options-control-${searchProp}`} key={`options-control-${searchProp}`}>
15+
<label>
16+
<input type="checkbox" checked={isChecked} onChange={() => {toggleCheckbox(!isChecked)}}/>
17+
<span onClick={() => {toggleCheckbox(!isChecked)}} >{ label }</span>
18+
</label>
19+
</li>
20+
)
21+
}

app/javascript/lib/scp-api.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ export async function fetchSearch(type, searchParams, mock=false) {
898898
export function buildSearchQueryString(type, searchParams) {
899899
const facetsParam = buildFacetQueryString(searchParams.facets)
900900

901-
const params = ['page', 'order', 'terms', 'preset', 'genes', 'genePage']
901+
const params = ['page', 'order', 'terms', 'external', 'preset', 'genes', 'genePage']
902902
let otherParamString = params.map(param => {
903903
return searchParams[param] ? `&${param}=${searchParams[param]}` : ''
904904
}).join('')

app/javascript/providers/GeneSearchProvider.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export function buildParamsFromQuery(query, preset) {
127127
page: queryParams.genePage ? parseInt(queryParams.genePage) : 1,
128128
genes: cleanGeneParams,
129129
terms: queryParams.terms ? queryParams.terms : '',
130+
external: queryParams.external ? queryParams.external : '',
130131
facets: buildFacetsFromQueryString(queryParams.facets),
131132
preset: preset ? preset : queryString.preset_search
132133
}

app/javascript/providers/SearchSelectionProvider.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function SearchSelectionProvider(props) {
4343
const [selection, setSelection] = useState(
4444
appliedSelection ?
4545
appliedSelection :
46-
{ terms: '', facets: {} })
46+
{ terms: '', facets: {}, external: '' })
4747
selection.updateSelection = updateSelection
4848
selection.updateFacet = updateFacet
4949
selection.performSearch = performSearch

app/javascript/providers/StudySearchProvider.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const emptySearch = {
1717
params: {
1818
terms: '',
1919
facets: {},
20+
external: '',
2021
page: 1,
2122
preset_search: undefined,
2223
order: undefined
@@ -74,6 +75,15 @@ export function useContextStudySearch() {
7475
return useContext(StudySearchContext)
7576
}
7677

78+
/** Merges the external parameter into the searchParams object */
79+
export function mergeExternalParam(searchParams, newParams) {
80+
if (Object.keys(newParams).length === 1 && Object.keys(newParams)[0] === 'external') {
81+
return newParams.external
82+
} else {
83+
return searchParams.external
84+
}
85+
}
86+
7787
/**
7888
* renders a StudySearchContext tied to its props,
7989
* fires route navigate on changes to params
@@ -97,6 +107,7 @@ export function PropsStudySearchProvider(props) {
97107
// reset the page to 1 for new searches, unless otherwise specified
98108
search.page = newParams.page ? newParams.page : 1
99109
search.preset = undefined // for now, exclude preset from the page URL--it's in the component props instead
110+
search.external = mergeExternalParam(searchParams, newParams)
100111
const mergedParams = Object.assign(buildGeneParamsFromQuery(window.location.search), search)
101112
const queryString = buildSearchQueryString('study', mergedParams)
102113
navigate(`?${queryString}`)
@@ -153,6 +164,7 @@ export function buildParamsFromQuery(query, preset) {
153164
page: queryParams.page ? parseInt(queryParams.page) : 1,
154165
terms: queryParams.terms ? queryParams.terms : '',
155166
facets: buildFacetsFromQueryString(queryParams.facets),
167+
external: queryParams.external ? queryParams.external : '',
156168
preset: preset ? preset : queryString.preset_search,
157169
order: queryParams.order
158170
}

app/javascript/styles/_search.scss

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,35 @@ span.text-search {
393393
background-color: inherit;
394394
}
395395
}
396+
397+
#search-options-menu {
398+
ul {
399+
list-style-type: none;
400+
padding-left: 0px;
401+
}
402+
403+
ul.facet-filter-list {
404+
max-height: 300px;
405+
overflow-y: auto;
406+
margin-bottom: 0;
407+
label {
408+
display: inline-block;
409+
white-space: nowrap;
410+
}
411+
input {
412+
font-size: 2em;
413+
vertical-align: middle;
414+
margin-top: 0px;
415+
margin-right: 12px;
416+
}
417+
label span {
418+
vertical-align: middle;
419+
font-weight: normal;
420+
}
421+
}
422+
423+
li {
424+
width: 100%;
425+
margin-bottom: 0.5em;
426+
}
427+
}

app/javascript/styles/_searchPanel.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
vertical-align: bottom;
77
}
88

9-
#download-button {
9+
#download-button{
1010
float: right;
1111
}
1212
}

0 commit comments

Comments
 (0)