Skip to content

Commit 9edf9bd

Browse files
authored
Add x and y column pickers to graph types (#14052)
* Add x and y column picker to line graph * Use column selector in heatmap options * Add column selector to histogram * Add column selector to scatter plot * Fix tests * Add xColumn and yColumn to data structure in line graph tests * Move scatter container defaults to visSwitcher * Add fillcolumn validity checking to histogram * regularize XYcontainer and render it and lineplussingle stat in visSwitcher * Initialize all views with null columns * Place Line and Scatter Options x-y column selector behind cloud feature flag * Rename getGroupableColumnSelection * Add defaults to x/y column selections in line graphs * Add defaults to x/y column selections in scatter
1 parent f2d198c commit 9edf9bd

18 files changed

+465
-213
lines changed

dashboard.go

+4
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,8 @@ type LinePlusSingleStatProperties struct {
591591
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
592592
Note string `json:"note"`
593593
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
594+
XColumn string `json:"xColumn"`
595+
YColumn string `json:"yColumn"`
594596
}
595597

596598
// XYViewProperties represents options for line, bar, step, or stacked view in Chronograf
@@ -603,6 +605,8 @@ type XYViewProperties struct {
603605
ViewColors []ViewColor `json:"colors"`
604606
Note string `json:"note"`
605607
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
608+
XColumn string `json:"xColumn"`
609+
YColumn string `json:"yColumn"`
606610
}
607611

608612
// SingleStatViewProperties represents options for single stat view in Chronograf

dashboard_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ func TestView_MarshalJSON(t *testing.T) {
4747
"legend": {},
4848
"geom": "",
4949
"note": "",
50-
"showNoteWhenEmpty": false
50+
"showNoteWhenEmpty": false,
51+
"xColumn": "",
52+
"yColumn": ""
5153
}
5254
}
5355
`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Libraries
2+
import React, {FunctionComponent} from 'react'
3+
4+
// Components
5+
import {Dropdown, Form, ComponentStatus} from 'src/clockface'
6+
7+
interface Props {
8+
selectedColumn: string
9+
availableColumns: string[]
10+
axisName: string
11+
onSelectColumn: (col: string) => void
12+
}
13+
14+
const ColumnSelector: FunctionComponent<Props> = ({
15+
selectedColumn,
16+
onSelectColumn,
17+
availableColumns,
18+
axisName,
19+
}) => {
20+
return (
21+
<Form.Element label={`${axisName.toUpperCase()} Column`}>
22+
<Dropdown
23+
selectedID={selectedColumn}
24+
onChange={onSelectColumn}
25+
status={
26+
availableColumns.length == 0
27+
? ComponentStatus.Disabled
28+
: ComponentStatus.Default
29+
}
30+
titleText="None"
31+
>
32+
{availableColumns.map(columnName => (
33+
<Dropdown.Item id={columnName} key={columnName} value={columnName}>
34+
{columnName}
35+
</Dropdown.Item>
36+
))}
37+
</Dropdown>
38+
</Form.Element>
39+
)
40+
}
41+
42+
export default ColumnSelector

ui/src/shared/components/HistogramContainer.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ const HistogramContainer: FunctionComponent<Props> = ({
4646
columnKeys.includes(xColumn) ? table.getColumn(xColumn, 'number') : []
4747
)
4848

49-
const isValidView = xColumn && columnKeys.includes(xColumn)
50-
fillColumns.every(col => columnKeys.includes(col))
49+
const isValidView =
50+
xColumn &&
51+
columnKeys.includes(xColumn) &&
52+
fillColumns.every(col => columnKeys.includes(col))
5153

5254
if (!isValidView) {
5355
return <EmptyGraphMessage message={INVALID_DATA_COPY} />

ui/src/shared/components/RefreshingViewSwitcher.tsx

+36-26
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,20 @@ const RefreshingViewSwitcher: FunctionComponent<Props> = ({
6565
)
6666
case ViewType.XY:
6767
return (
68-
<XYContainer
69-
files={files}
70-
viewProperties={properties}
71-
loading={loading}
72-
>
73-
{config => <Plot config={config} />}
74-
</XYContainer>
68+
<VisTableTransform files={files}>
69+
{({table, fluxGroupKeyUnion}) => (
70+
<XYContainer
71+
table={table}
72+
fluxGroupKeyUnion={fluxGroupKeyUnion}
73+
viewProperties={properties}
74+
loading={loading}
75+
>
76+
{config => <Plot config={config} />}
77+
</XYContainer>
78+
)}
79+
</VisTableTransform>
7580
)
81+
7682
case ViewType.LinePlusSingleStat:
7783
const xyProperties = {
7884
...properties,
@@ -88,24 +94,29 @@ const RefreshingViewSwitcher: FunctionComponent<Props> = ({
8894
} as SingleStatView
8995

9096
return (
91-
<XYContainer
92-
files={files}
93-
viewProperties={xyProperties}
94-
loading={loading}
95-
>
96-
{config => (
97-
<Plot config={config}>
98-
<LatestValueTransform table={config.table} quiet={true}>
99-
{latestValue => (
100-
<SingleStat
101-
stat={latestValue}
102-
properties={singleStatProperties}
103-
/>
104-
)}
105-
</LatestValueTransform>
106-
</Plot>
97+
<VisTableTransform files={files}>
98+
{({table, fluxGroupKeyUnion}) => (
99+
<XYContainer
100+
table={table}
101+
fluxGroupKeyUnion={fluxGroupKeyUnion}
102+
viewProperties={xyProperties}
103+
loading={loading}
104+
>
105+
{config => (
106+
<Plot config={config}>
107+
<LatestValueTransform table={config.table} quiet={true}>
108+
{latestValue => (
109+
<SingleStat
110+
stat={latestValue}
111+
properties={singleStatProperties}
112+
/>
113+
)}
114+
</LatestValueTransform>
115+
</Plot>
116+
)}
117+
</XYContainer>
107118
)}
108-
</XYContainer>
119+
</VisTableTransform>
109120
)
110121
case ViewType.Histogram:
111122
return (
@@ -138,10 +149,9 @@ const RefreshingViewSwitcher: FunctionComponent<Props> = ({
138149
case ViewType.Scatter:
139150
return (
140151
<VisTableTransform files={files}>
141-
{({table, fluxGroupKeyUnion}) => (
152+
{({table}) => (
142153
<ScatterContainer
143154
table={table}
144-
fluxGroupKeyUnion={fluxGroupKeyUnion}
145155
loading={loading}
146156
viewProperties={properties}
147157
>

ui/src/shared/components/ScatterContainer.tsx

+8-50
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Libraries
2-
import React, {FunctionComponent, useEffect} from 'react'
3-
import {connect} from 'react-redux'
2+
import React, {FunctionComponent} from 'react'
43
import {Config, Table} from '@influxdata/vis'
54

65
// Components
@@ -9,7 +8,7 @@ import GraphLoadingDots from 'src/shared/components/GraphLoadingDots'
98

109
// Utils
1110
import {useVisDomainSettings} from 'src/shared/utils/useVisDomainSettings'
12-
import {getFormatter, chooseYColumn, chooseXColumn} from 'src/shared/utils/vis'
11+
import {getFormatter, chooseXColumn, chooseYColumn} from 'src/shared/utils/vis'
1312

1413
// Constants
1514
import {VIS_THEME} from 'src/shared/constants'
@@ -18,28 +17,19 @@ import {INVALID_DATA_COPY} from 'src/shared/copy/cell'
1817

1918
// Types
2019
import {RemoteDataState, ScatterView} from 'src/types'
21-
import {setFillColumns, setSymbolColumns} from 'src/timeMachine/actions'
2220

23-
interface OwnProps {
21+
interface Props {
2422
table: Table
2523
fluxGroupKeyUnion?: string[]
2624
loading: RemoteDataState
2725
viewProperties: ScatterView
2826
children: (config: Config) => JSX.Element
2927
}
3028

31-
interface DispatchProps {
32-
onSetFillColumns: typeof setFillColumns
33-
onSetSymbolColumns: typeof setSymbolColumns
34-
}
35-
36-
type Props = OwnProps & DispatchProps
37-
3829
const ScatterContainer: FunctionComponent<Props> = ({
3930
table,
4031
loading,
4132
children,
42-
fluxGroupKeyUnion,
4333
viewProperties: {
4434
xAxisLabel,
4535
yAxisLabel,
@@ -50,39 +40,15 @@ const ScatterContainer: FunctionComponent<Props> = ({
5040
colors,
5141
xDomain: storedXDomain,
5242
yDomain: storedYDomain,
43+
xColumn: storedXColumn,
44+
yColumn: storedYColumn,
5345
},
54-
onSetFillColumns,
55-
onSetSymbolColumns,
5646
}) => {
57-
useEffect(() => {
58-
if (fluxGroupKeyUnion && (!storedSymbol || !storedFill)) {
59-
// if new view, maximize variations in symbol and color
60-
const filteredGroupKeys = fluxGroupKeyUnion.filter(
61-
k =>
62-
![
63-
'result',
64-
'table',
65-
'_measurement',
66-
'_start',
67-
'_stop',
68-
'_field',
69-
].includes(k)
70-
)
71-
if (!storedSymbol) {
72-
onSetSymbolColumns(filteredGroupKeys)
73-
}
74-
if (!storedFill) {
75-
onSetFillColumns(filteredGroupKeys)
76-
}
77-
}
78-
})
79-
8047
const fillColumns = storedFill || []
8148
const symbolColumns = storedSymbol || []
8249

83-
// TODO: allow xcolumn and ycolumn to be user selectable
84-
const xColumn = chooseXColumn(table)
85-
const yColumn = chooseYColumn(table)
50+
const xColumn = storedXColumn || chooseXColumn(table)
51+
const yColumn = storedYColumn || chooseYColumn(table)
8652

8753
const columnKeys = table.columnKeys
8854

@@ -150,12 +116,4 @@ const ScatterContainer: FunctionComponent<Props> = ({
150116
)
151117
}
152118

153-
const mdtp = {
154-
onSetFillColumns: setFillColumns,
155-
onSetSymbolColumns: setSymbolColumns,
156-
}
157-
158-
export default connect<{}, DispatchProps, {}>(
159-
null,
160-
mdtp
161-
)(ScatterContainer)
119+
export default ScatterContainer

ui/src/shared/components/XYContainer.tsx

+18-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Libraries
22
import React, {FunctionComponent, useMemo} from 'react'
3-
import {Config, fromFlux} from '@influxdata/vis'
3+
import {Config, Table} from '@influxdata/vis'
44

55
// Components
66
import EmptyGraphMessage from 'src/shared/components/EmptyGraphMessage'
@@ -26,19 +26,23 @@ import {INVALID_DATA_COPY} from 'src/shared/copy/cell'
2626
import {RemoteDataState, XYView} from 'src/types'
2727

2828
interface Props {
29-
files: string[]
29+
table: Table
30+
fluxGroupKeyUnion: string[]
3031
loading: RemoteDataState
3132
viewProperties: XYView
3233
children: (config: Config) => JSX.Element
3334
}
3435

3536
const XYContainer: FunctionComponent<Props> = ({
36-
files,
37+
table,
38+
fluxGroupKeyUnion,
3739
loading,
3840
children,
3941
viewProperties: {
4042
geom,
4143
colors,
44+
xColumn: storedXColumn,
45+
yColumn: storedYColumn,
4246
axes: {
4347
x: {label: xAxisLabel, bounds: xBounds},
4448
y: {
@@ -51,18 +55,12 @@ const XYContainer: FunctionComponent<Props> = ({
5155
},
5256
},
5357
}) => {
54-
const {table, fluxGroupKeyUnion} = useMemo(
55-
() => fromFlux(files.join('\n\n')),
56-
[files]
57-
)
58-
59-
// Eventually these will be configurable in the line graph options UI
60-
const xColumn = chooseXColumn(table)
61-
const yColumn = chooseYColumn(table)
62-
6358
const storedXDomain = useMemo(() => parseBounds(xBounds), [xBounds])
6459
const storedYDomain = useMemo(() => parseBounds(yBounds), [yBounds])
6560

61+
const xColumn = storedXColumn || chooseXColumn(table)
62+
const yColumn = storedYColumn || chooseYColumn(table)
63+
6664
const columnKeys = table.columnKeys
6765

6866
const [xDomain, onSetXDomain, onResetXDomain] = useVisDomainSettings(
@@ -74,7 +72,14 @@ const XYContainer: FunctionComponent<Props> = ({
7472
storedYDomain,
7573
columnKeys.includes(yColumn) ? table.getColumn(yColumn, 'number') : []
7674
)
77-
if (!xColumn || !yColumn) {
75+
76+
const isValidView =
77+
xColumn &&
78+
columnKeys.includes(xColumn) &&
79+
yColumn &&
80+
columnKeys.includes(yColumn)
81+
82+
if (!isValidView) {
7883
return <EmptyGraphMessage message={INVALID_DATA_COPY} />
7984
}
8085

ui/src/shared/utils/mocks/resourceToTemplate.ts

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ export const myView: View = {
8181
colors: [],
8282
note: '',
8383
showNoteWhenEmpty: false,
84+
xColumn: null,
85+
yColumn: null,
8486
},
8587
}
8688

ui/src/shared/utils/resourceToTemplate.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ describe('resourceToTemplate', () => {
343343
colors: [],
344344
note: '',
345345
showNoteWhenEmpty: false,
346+
xColumn: null,
347+
yColumn: null,
346348
},
347349
},
348350
},

0 commit comments

Comments
 (0)