Skip to content

Commit 56e5af7

Browse files
authored
feat(6496): SQL graphing viewOptions: groupby tag keys (#6502)
* feat(6496): viewOptions to modify query by groupby tagKeys * fix(6496): Cannot run subquery if no bucket is selected. Show spinner when graph subquery is loading. * feat(6496): have groupby options be a based upon returned results, with the default groupby determined via the tagKeys in the schema * feat(6482): when choosing graph --> auto-open the Customize options in right hand panel. * chore(6496): use better naming conventions * feat(6496): make explicit what is persisted, and determine when it's cleared
1 parent 6bc116e commit 56e5af7

File tree

9 files changed

+287
-42
lines changed

9 files changed

+287
-42
lines changed

src/dataExplorer/components/DeleteScript.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {ResultsContext} from 'src/dataExplorer/context/results'
1212
import {RemoteDataState} from 'src/types'
1313
import {QueryContext} from 'src/shared/contexts/query'
1414
import {SCRIPT_EDITOR_PARAMS} from 'src/dataExplorer/components/resources'
15+
import {ResultsViewContext} from 'src/dataExplorer/context/resultsView'
1516

1617
let deleteScript
1718

@@ -28,6 +29,7 @@ const DeleteScript: FC<Props> = ({onBack, onClose}) => {
2829
const {resource} = useContext(PersistanceContext)
2930
const {cancel} = useContext(QueryContext)
3031
const {setStatus, setResult} = useContext(ResultsContext)
32+
const {clear: clearViewOptions} = useContext(ResultsViewContext)
3133
const history = useHistory()
3234
const dispatch = useDispatch()
3335
const org = useSelector(getOrg)
@@ -43,6 +45,7 @@ const DeleteScript: FC<Props> = ({onBack, onClose}) => {
4345

4446
setStatus(RemoteDataState.NotStarted)
4547
setResult(null)
48+
clearViewOptions()
4649
cancel()
4750
history.replace(
4851
`/orgs/${org.id}/data-explorer/from/script${SCRIPT_EDITOR_PARAMS}`

src/dataExplorer/components/Results.tsx

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import React, {FC, useState, useContext, useMemo, useCallback} from 'react'
1+
import React, {
2+
FC,
3+
useCallback,
4+
useContext,
5+
useEffect,
6+
useMemo,
7+
useState,
8+
} from 'react'
29
import {
310
FlexBox,
411
FlexDirection,
@@ -7,9 +14,21 @@ import {
714
IconFont,
815
ComponentStatus,
916
ComponentColor,
17+
SpinnerContainer,
18+
TechnoSpinner,
1019
} from '@influxdata/clockface'
1120

12-
import {RemoteDataState, SimpleTableViewProperties} from 'src/types'
21+
// Components
22+
import {SearchWidget} from 'src/shared/components/search_widget/SearchWidget'
23+
import {
24+
View,
25+
ViewTypeDropdown,
26+
ViewOptions,
27+
SUPPORTED_VISUALIZATIONS,
28+
} from 'src/visualization'
29+
import {SqlViewOptions} from 'src/dataExplorer/components/SqlViewOptions'
30+
31+
// Contexts
1332
import {ResultsContext} from 'src/dataExplorer/context/results'
1433
import {
1534
ResultsViewContext,
@@ -18,18 +37,17 @@ import {
1837
import {ChildResultsContext} from 'src/dataExplorer/context/results/childResults'
1938
import {SidebarContext} from 'src/dataExplorer/context/sidebar'
2039
import {PersistanceContext} from 'src/dataExplorer/context/persistance'
21-
import {SearchWidget} from 'src/shared/components/search_widget/SearchWidget'
22-
import {
23-
View,
24-
ViewTypeDropdown,
25-
ViewOptions,
26-
SUPPORTED_VISUALIZATIONS,
27-
} from 'src/visualization'
40+
41+
// Types
2842
import {FluxResult} from 'src/types/flows'
43+
import {RemoteDataState, SimpleTableViewProperties} from 'src/types'
2944

30-
import './Results.scss'
45+
// Utils
3146
import {bytesFormatter} from 'src/shared/copy/notifications'
3247

48+
import './Results.scss'
49+
import {LanguageType} from './resources'
50+
3351
const QueryStat: FC = () => {
3452
const {result} = useContext(ResultsContext)
3553

@@ -154,13 +172,15 @@ const GraphResults: FC = () => {
154172

155173
return (
156174
<div className="data-explorer-results--view">
157-
<View
158-
loading={status}
159-
properties={view.properties}
160-
result={result?.parsed}
161-
timeRange={range}
162-
hideTimer
163-
/>
175+
<SpinnerContainer loading={status} spinnerComponent={<TechnoSpinner />}>
176+
<View
177+
loading={status}
178+
properties={view.properties}
179+
result={result?.parsed}
180+
timeRange={range}
181+
hideTimer
182+
/>
183+
</SpinnerContainer>
164184
</div>
165185
)
166186
}
@@ -169,7 +189,10 @@ const WrappedOptions: FC = () => {
169189
// use parent `results` so all metadata is present for the viz options
170190
const {result} = useContext(ResultsContext)
171191
const {setResult, setStatus} = useContext(ChildResultsContext)
172-
const {view, setView /* setViewOptions*/} = useContext(ResultsViewContext)
192+
const {view, setView, selectViewOptions, viewOptions, selectedViewOptions} =
193+
useContext(ResultsViewContext)
194+
const {resource} = useContext(PersistanceContext)
195+
const dataExists = !!result?.parsed
173196

174197
const updateChildResults = useCallback(
175198
update => {
@@ -184,8 +207,19 @@ const WrappedOptions: FC = () => {
184207
[setStatus, setResult]
185208
)
186209

187-
// TODO: make component with `update={setViewOptions}`, for QxBuilder-specific graph subquery options
188-
const subQueryOptions = null
210+
if (!dataExists) {
211+
return null
212+
}
213+
214+
const subQueryOptions =
215+
resource?.language === LanguageType.SQL &&
216+
view.state == ViewStateType.Graph ? (
217+
<SqlViewOptions
218+
selectViewOptions={selectViewOptions}
219+
allViewOptions={viewOptions}
220+
selectedViewOptions={selectedViewOptions}
221+
/>
222+
) : null
189223

190224
return (
191225
<>
@@ -200,14 +234,26 @@ const WrappedOptions: FC = () => {
200234
}
201235

202236
const GraphHeader: FC = () => {
203-
const {view, setView} = useContext(ResultsViewContext)
237+
const {view, setView, viewOptions} = useContext(ResultsViewContext)
204238
const {result} = useContext(ResultsContext)
205239
const {result: subQueryResult} = useContext(ChildResultsContext)
206-
const {launch} = useContext(SidebarContext)
240+
const {launch, clear: closeSidebar} = useContext(SidebarContext)
241+
242+
const dataExists = !!result?.parsed
207243

208244
const launcher = () => {
209245
launch(<WrappedOptions />)
210246
}
247+
useEffect(() => {
248+
if (dataExists) {
249+
launcher()
250+
}
251+
}, [viewOptions])
252+
useEffect(() => {
253+
if (!dataExists) {
254+
closeSidebar()
255+
}
256+
}, [dataExists])
211257

212258
const updateType = viewType => {
213259
setView({
@@ -216,7 +262,6 @@ const GraphHeader: FC = () => {
216262
})
217263
}
218264

219-
const dataExists = !!result?.parsed
220265
const subqueryReturnsData = !!subQueryResult?.parsed
221266
let titleText = 'Configure Visualization'
222267
if (!dataExists) {

src/dataExplorer/components/SaveAsScript.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
copyToClipboardFailed,
3838
copyToClipboardSuccess,
3939
} from 'src/shared/copy/notifications'
40+
import {ResultsViewContext} from 'src/dataExplorer/context/resultsView'
4041
interface Props {
4142
language: LanguageType
4243
onClose: () => void
@@ -52,6 +53,7 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
5253
const isIoxOrg = useSelector(isOrgIOx)
5354
const {cancel} = useContext(QueryContext)
5455
const {setStatus, setResult} = useContext(ResultsContext)
56+
const {clear: clearViewOptions} = useContext(ResultsViewContext)
5557
const [error, setError] = useState<string>()
5658
// Setting the name to state rather than persisting it to session storage
5759
// so that we can cancel out of a name change if needed
@@ -96,6 +98,7 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
9698
cancel()
9799
setStatus(RemoteDataState.NotStarted)
98100
setResult(null)
101+
clearViewOptions()
99102

100103
if (isIoxOrg) {
101104
history.replace(

src/dataExplorer/components/ScriptQueryBuilder.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import {QueryProvider} from 'src/shared/contexts/query'
2121
import {EditorProvider} from 'src/shared/contexts/editor'
2222
import {ResultsProvider, ResultsContext} from 'src/dataExplorer/context/results'
2323
import {ChildResultsProvider} from 'src/dataExplorer/context/results/childResults'
24-
import {ResultsViewProvider} from 'src/dataExplorer/context/resultsView'
24+
import {
25+
ResultsViewProvider,
26+
ResultsViewContext,
27+
} from 'src/dataExplorer/context/resultsView'
2528
import {SidebarProvider} from 'src/dataExplorer/context/sidebar'
2629
import {
2730
PersistanceProvider,
@@ -59,12 +62,14 @@ const ScriptQueryBuilder: FC = () => {
5962
const isIoxOrg = useSelector(isOrgIOx)
6063
const {cancel} = useContext(QueryContext)
6164
const {setStatus, setResult} = useContext(ResultsContext)
65+
const {clear: clearViewOptions} = useContext(ResultsViewContext)
6266
const org = useSelector(getOrg)
6367

6468
const handleClear = useCallback(() => {
6569
cancel()
6670
setStatus(RemoteDataState.NotStarted)
6771
setResult(null)
72+
clearViewOptions()
6873

6974
if (isIoxOrg) {
7075
history.replace(
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@import '@influxdata/clockface/dist/variables.scss';
2+
3+
.sql-view-options {
4+
.cf-list {
5+
flex: 1 0 !important;
6+
7+
.selector-list--item {
8+
height: $cf-form-xs-height;
9+
}
10+
}
11+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React, {FC} from 'react'
2+
import {Columns, Grid} from '@influxdata/clockface'
3+
4+
import SelectorList from 'src/timeMachine/components/SelectorList'
5+
import SelectorTitle from 'src/dataExplorer/components/SelectorTitle'
6+
import {ViewOptions} from 'src/dataExplorer/context/resultsView'
7+
8+
import './SqlViewOptions.scss'
9+
10+
interface SqlViewOptionsT {
11+
selectViewOptions: (_: Partial<ViewOptions>) => void
12+
allViewOptions: ViewOptions
13+
selectedViewOptions: ViewOptions
14+
}
15+
16+
export const SqlViewOptions: FC<SqlViewOptionsT> = ({
17+
selectViewOptions,
18+
allViewOptions,
19+
selectedViewOptions,
20+
}) => {
21+
const handleSelectedListItem = (propKey, value) => {
22+
if ((selectedViewOptions[propKey] ?? []).includes(value)) {
23+
selectViewOptions({
24+
[propKey]: selectedViewOptions[propKey].filter(v => v !== value),
25+
})
26+
} else {
27+
selectViewOptions({
28+
[propKey]: (selectedViewOptions[propKey] ?? []).concat([value]),
29+
})
30+
}
31+
}
32+
33+
const groupbyTooltipContents = (
34+
<div>
35+
<span>Select the GROUPBY used for the graph subquery.</span>
36+
<br />
37+
<br />
38+
<span>
39+
Options are based on returned data results (refer to your table
40+
columns).
41+
</span>
42+
<br />
43+
<br />
44+
<span>
45+
By default, the GROUPBY is done on all tagKeys in order to produce a
46+
dataseries.
47+
</span>
48+
</div>
49+
)
50+
51+
return (
52+
<div className="view-options sql-view-options">
53+
<Grid>
54+
<Grid.Row>
55+
<Grid.Column
56+
widthXS={Columns.Twelve}
57+
widthMD={Columns.Six}
58+
widthLG={Columns.Four}
59+
className="view-options-container"
60+
>
61+
<h5 className="view-options--header">Query Modifier</h5>
62+
<SelectorTitle
63+
label="Grouping"
64+
tooltipContents={groupbyTooltipContents}
65+
/>
66+
<SelectorList
67+
items={allViewOptions?.groupby ?? []}
68+
selectedItems={selectedViewOptions?.groupby ?? []}
69+
onSelectItem={tagKey => handleSelectedListItem('groupby', tagKey)}
70+
multiSelect={true}
71+
/>
72+
</Grid.Column>
73+
</Grid.Row>
74+
</Grid>
75+
</div>
76+
)
77+
}

src/dataExplorer/context/results/childResults.tsx

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,33 @@ import {LanguageType} from 'src/dataExplorer/components/resources'
1717
// Utils
1818
import {rangeToParam} from 'src/dataExplorer/shared/utils'
1919

20-
const modifiersToApply = (_viewOptions: ViewOptions): SqlQueryModifiers => {
20+
const modifiersToApply = (viewOptions: ViewOptions): SqlQueryModifiers => {
21+
const prepend = []
22+
const append = []
23+
24+
// 1. groupby first
25+
if (viewOptions.groupby?.length) {
26+
append.push(
27+
`|> group(columns: [${viewOptions.groupby
28+
.map(columnName => `"${columnName}"`)
29+
.join(',')}])`
30+
)
31+
}
32+
33+
// 2. smoothing transformation next
2134
// e.g. to smooth by selected column foo. Rough example.
2235
const shouldSmooth = false
2336
if (shouldSmooth) {
24-
return {
25-
prepend: `import "experimental/polyline"`,
26-
append: `|> polyline.rdp(valColumn: "foo", timeColumn: "time")`,
27-
}
37+
prepend.push(`import "experimental/polyline"`)
38+
// append.push(`|> polyline.rdp(valColumn: "foo", timeColumn: "time")`) // TODO
2839
}
2940

30-
return null
41+
return Boolean(prepend.length + append.length)
42+
? {
43+
prepend: prepend.join('\n'),
44+
append: append.join('\n'),
45+
}
46+
: null
3147
}
3248

3349
interface ChildResultsContextType {
@@ -51,12 +67,13 @@ export const ChildResultsContext =
5167

5268
export const ChildResultsProvider: FC = ({children}) => {
5369
const [result, setResult] = useState<FluxResult>({} as FluxResult)
70+
const [queryModifers, setQueryModifiers] = useState(null)
5471
const [status, setStatus] = useState<RemoteDataState>(
5572
RemoteDataState.NotStarted
5673
)
5774
const {status: statusFromParent, result: resultFromParent} =
5875
useContext(ResultsContext)
59-
const {viewOptions} = useContext(ResultsViewContext)
76+
const {selectedViewOptions: viewOptions} = useContext(ResultsViewContext)
6077
const {
6178
query: queryText,
6279
selection,
@@ -78,10 +95,16 @@ export const ChildResultsProvider: FC = ({children}) => {
7895
return
7996
}
8097

81-
// TODO: tranform functions, based on viewOptions change
98+
const cannotRunIoxSqlMethod = !selection?.bucket
99+
if (cannotRunIoxSqlMethod) {
100+
return
101+
}
102+
82103
const sqlQueryModifiers = modifiersToApply(viewOptions)
104+
const previousQueryModifiers = queryModifers
105+
setQueryModifiers(sqlQueryModifiers)
83106

84-
if (!sqlQueryModifiers) {
107+
if (sqlQueryModifiers === previousQueryModifiers) {
85108
return
86109
}
87110

0 commit comments

Comments
 (0)