-
Notifications
You must be signed in to change notification settings - Fork 25
/
GeneSearchProvider.js
133 lines (117 loc) · 3.81 KB
/
GeneSearchProvider.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import React, { useState } from 'react'
import _cloneDeep from 'lodash/cloneDeep'
import _isEqual from 'lodash/isEqual'
import { navigate, useLocation } from '@reach/router'
import * as queryString from 'query-string'
import { fetchSearch, buildSearchQueryString } from 'lib/scp-api'
import { buildParamsFromQuery as buildStudyParamsFromQuery } from 'providers/StudySearchProvider'
/*
* This component and context shares a LOT in common with StudySearchProvider
* Once we decide how Study and gene search interact in the UI, these two providers
* should be refactored to consolidate search logic
*/
export const emptySearch = {
params: {
genes: '',
genePage: 1
},
results: [],
isLoading: false,
isLoaded: false,
isError: false,
updateSearch: () => {
throw new Error(
'You are trying to use this context outside of a Provider container'
)
}
}
export const GeneSearchContext = React.createContext(emptySearch)
/**
* renders a GeneSearchContext tied to its props,
* fires route navigate on changes to params
*/
export function PropsGeneSearchProvider(props) {
const defaultState = _cloneDeep(emptySearch)
defaultState.updateSearch = updateSearch
defaultState.performSearch = performSearch
const [searchState, setSearchState] = useState(defaultState)
const searchParams = props.searchParams
/**
* Update search parameters in URL
*
* @param {Object} newParams Parameters to update
*/
async function updateSearch(newParams) {
let mergedParams = Object.assign({}, searchParams, newParams)
// reset the page to 1 for new searches, unless otherwise specified
// we convert 'page' to 'genePage' so the url param
// doesn't conflict with the study search page
mergedParams.genePage = newParams.page ? newParams.page : 1
// merge in the study params so that state is saved between tabs
mergedParams = Object.assign(buildStudyParamsFromQuery(window.location.search), mergedParams)
const queryString = buildSearchQueryString('study', mergedParams)
navigate(`?${queryString}`)
}
/** perform the actual API search */
async function performSearch() {
// reset the scroll in case they scrolled down to read prior results
window.scrollTo(0, 0)
const results = await fetchSearch(
// Ensures event properties include "type": "gene" in search logging.
'gene',
{
page: searchParams.page,
genes: searchParams.genes
}
)
setSearchState({
params: searchParams,
isError: results.ok === false,
isLoading: false,
isLoaded: true,
results,
updateSearch
})
}
if (!_isEqual(searchParams, searchState.params ||
!searchState.isLoading &&
!searchState.isLoaded)) {
performSearch(searchParams)
setSearchState({
params: searchParams,
isError: false,
isLoading: true,
isLoaded: false,
results: [],
updateSearch
})
}
return (
<GeneSearchContext.Provider value={searchState}>
{ props.children }
</GeneSearchContext.Provider>
)
}
/** returns an object built from the query params and defaults */
export function buildParamsFromQuery(query, preset) {
const queryParams = queryString.parse(query)
return {
page: queryParams.genePage ? parseInt(queryParams.genePage) : 1,
genes: queryParams.genes ? queryParams.genes : '',
preset: preset ? preset : queryString.preset_search
}
}
/**
* Self-contained component for providing a url-routable
* GeneSearchContext and rendering children.
* The routing is all via query params
*/
export default function GeneSearchProvider(props) {
const location = useLocation()
const searchParams = buildParamsFromQuery(location.search, props.preset)
return (
<PropsGeneSearchProvider searchParams={searchParams}>
{props.children}
</PropsGeneSearchProvider>
)
}