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 scatterpull/14029/head
parent
f2d198c169
commit
9edf9bd6ee
|
@ -591,6 +591,8 @@ type LinePlusSingleStatProperties struct {
|
|||
DecimalPlaces DecimalPlaces `json:"decimalPlaces"`
|
||||
Note string `json:"note"`
|
||||
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
|
||||
XColumn string `json:"xColumn"`
|
||||
YColumn string `json:"yColumn"`
|
||||
}
|
||||
|
||||
// XYViewProperties represents options for line, bar, step, or stacked view in Chronograf
|
||||
|
@ -603,6 +605,8 @@ type XYViewProperties struct {
|
|||
ViewColors []ViewColor `json:"colors"`
|
||||
Note string `json:"note"`
|
||||
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
|
||||
XColumn string `json:"xColumn"`
|
||||
YColumn string `json:"yColumn"`
|
||||
}
|
||||
|
||||
// SingleStatViewProperties represents options for single stat view in Chronograf
|
||||
|
|
|
@ -47,7 +47,9 @@ func TestView_MarshalJSON(t *testing.T) {
|
|||
"legend": {},
|
||||
"geom": "",
|
||||
"note": "",
|
||||
"showNoteWhenEmpty": false
|
||||
"showNoteWhenEmpty": false,
|
||||
"xColumn": "",
|
||||
"yColumn": ""
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// Libraries
|
||||
import React, {FunctionComponent} from 'react'
|
||||
|
||||
// Components
|
||||
import {Dropdown, Form, ComponentStatus} from 'src/clockface'
|
||||
|
||||
interface Props {
|
||||
selectedColumn: string
|
||||
availableColumns: string[]
|
||||
axisName: string
|
||||
onSelectColumn: (col: string) => void
|
||||
}
|
||||
|
||||
const ColumnSelector: FunctionComponent<Props> = ({
|
||||
selectedColumn,
|
||||
onSelectColumn,
|
||||
availableColumns,
|
||||
axisName,
|
||||
}) => {
|
||||
return (
|
||||
<Form.Element label={`${axisName.toUpperCase()} Column`}>
|
||||
<Dropdown
|
||||
selectedID={selectedColumn}
|
||||
onChange={onSelectColumn}
|
||||
status={
|
||||
availableColumns.length == 0
|
||||
? ComponentStatus.Disabled
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
titleText="None"
|
||||
>
|
||||
{availableColumns.map(columnName => (
|
||||
<Dropdown.Item id={columnName} key={columnName} value={columnName}>
|
||||
{columnName}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown>
|
||||
</Form.Element>
|
||||
)
|
||||
}
|
||||
|
||||
export default ColumnSelector
|
|
@ -46,8 +46,10 @@ const HistogramContainer: FunctionComponent<Props> = ({
|
|||
columnKeys.includes(xColumn) ? table.getColumn(xColumn, 'number') : []
|
||||
)
|
||||
|
||||
const isValidView = xColumn && columnKeys.includes(xColumn)
|
||||
fillColumns.every(col => columnKeys.includes(col))
|
||||
const isValidView =
|
||||
xColumn &&
|
||||
columnKeys.includes(xColumn) &&
|
||||
fillColumns.every(col => columnKeys.includes(col))
|
||||
|
||||
if (!isValidView) {
|
||||
return <EmptyGraphMessage message={INVALID_DATA_COPY} />
|
||||
|
|
|
@ -65,14 +65,20 @@ const RefreshingViewSwitcher: FunctionComponent<Props> = ({
|
|||
)
|
||||
case ViewType.XY:
|
||||
return (
|
||||
<XYContainer
|
||||
files={files}
|
||||
viewProperties={properties}
|
||||
loading={loading}
|
||||
>
|
||||
{config => <Plot config={config} />}
|
||||
</XYContainer>
|
||||
<VisTableTransform files={files}>
|
||||
{({table, fluxGroupKeyUnion}) => (
|
||||
<XYContainer
|
||||
table={table}
|
||||
fluxGroupKeyUnion={fluxGroupKeyUnion}
|
||||
viewProperties={properties}
|
||||
loading={loading}
|
||||
>
|
||||
{config => <Plot config={config} />}
|
||||
</XYContainer>
|
||||
)}
|
||||
</VisTableTransform>
|
||||
)
|
||||
|
||||
case ViewType.LinePlusSingleStat:
|
||||
const xyProperties = {
|
||||
...properties,
|
||||
|
@ -88,24 +94,29 @@ const RefreshingViewSwitcher: FunctionComponent<Props> = ({
|
|||
} as SingleStatView
|
||||
|
||||
return (
|
||||
<XYContainer
|
||||
files={files}
|
||||
viewProperties={xyProperties}
|
||||
loading={loading}
|
||||
>
|
||||
{config => (
|
||||
<Plot config={config}>
|
||||
<LatestValueTransform table={config.table} quiet={true}>
|
||||
{latestValue => (
|
||||
<SingleStat
|
||||
stat={latestValue}
|
||||
properties={singleStatProperties}
|
||||
/>
|
||||
)}
|
||||
</LatestValueTransform>
|
||||
</Plot>
|
||||
<VisTableTransform files={files}>
|
||||
{({table, fluxGroupKeyUnion}) => (
|
||||
<XYContainer
|
||||
table={table}
|
||||
fluxGroupKeyUnion={fluxGroupKeyUnion}
|
||||
viewProperties={xyProperties}
|
||||
loading={loading}
|
||||
>
|
||||
{config => (
|
||||
<Plot config={config}>
|
||||
<LatestValueTransform table={config.table} quiet={true}>
|
||||
{latestValue => (
|
||||
<SingleStat
|
||||
stat={latestValue}
|
||||
properties={singleStatProperties}
|
||||
/>
|
||||
)}
|
||||
</LatestValueTransform>
|
||||
</Plot>
|
||||
)}
|
||||
</XYContainer>
|
||||
)}
|
||||
</XYContainer>
|
||||
</VisTableTransform>
|
||||
)
|
||||
case ViewType.Histogram:
|
||||
return (
|
||||
|
@ -138,10 +149,9 @@ const RefreshingViewSwitcher: FunctionComponent<Props> = ({
|
|||
case ViewType.Scatter:
|
||||
return (
|
||||
<VisTableTransform files={files}>
|
||||
{({table, fluxGroupKeyUnion}) => (
|
||||
{({table}) => (
|
||||
<ScatterContainer
|
||||
table={table}
|
||||
fluxGroupKeyUnion={fluxGroupKeyUnion}
|
||||
loading={loading}
|
||||
viewProperties={properties}
|
||||
>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Libraries
|
||||
import React, {FunctionComponent, useEffect} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import React, {FunctionComponent} from 'react'
|
||||
import {Config, Table} from '@influxdata/vis'
|
||||
|
||||
// Components
|
||||
|
@ -9,7 +8,7 @@ import GraphLoadingDots from 'src/shared/components/GraphLoadingDots'
|
|||
|
||||
// Utils
|
||||
import {useVisDomainSettings} from 'src/shared/utils/useVisDomainSettings'
|
||||
import {getFormatter, chooseYColumn, chooseXColumn} from 'src/shared/utils/vis'
|
||||
import {getFormatter, chooseXColumn, chooseYColumn} from 'src/shared/utils/vis'
|
||||
|
||||
// Constants
|
||||
import {VIS_THEME} from 'src/shared/constants'
|
||||
|
@ -18,9 +17,8 @@ import {INVALID_DATA_COPY} from 'src/shared/copy/cell'
|
|||
|
||||
// Types
|
||||
import {RemoteDataState, ScatterView} from 'src/types'
|
||||
import {setFillColumns, setSymbolColumns} from 'src/timeMachine/actions'
|
||||
|
||||
interface OwnProps {
|
||||
interface Props {
|
||||
table: Table
|
||||
fluxGroupKeyUnion?: string[]
|
||||
loading: RemoteDataState
|
||||
|
@ -28,18 +26,10 @@ interface OwnProps {
|
|||
children: (config: Config) => JSX.Element
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
onSetFillColumns: typeof setFillColumns
|
||||
onSetSymbolColumns: typeof setSymbolColumns
|
||||
}
|
||||
|
||||
type Props = OwnProps & DispatchProps
|
||||
|
||||
const ScatterContainer: FunctionComponent<Props> = ({
|
||||
table,
|
||||
loading,
|
||||
children,
|
||||
fluxGroupKeyUnion,
|
||||
viewProperties: {
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
|
@ -50,39 +40,15 @@ const ScatterContainer: FunctionComponent<Props> = ({
|
|||
colors,
|
||||
xDomain: storedXDomain,
|
||||
yDomain: storedYDomain,
|
||||
xColumn: storedXColumn,
|
||||
yColumn: storedYColumn,
|
||||
},
|
||||
onSetFillColumns,
|
||||
onSetSymbolColumns,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
if (fluxGroupKeyUnion && (!storedSymbol || !storedFill)) {
|
||||
// if new view, maximize variations in symbol and color
|
||||
const filteredGroupKeys = fluxGroupKeyUnion.filter(
|
||||
k =>
|
||||
![
|
||||
'result',
|
||||
'table',
|
||||
'_measurement',
|
||||
'_start',
|
||||
'_stop',
|
||||
'_field',
|
||||
].includes(k)
|
||||
)
|
||||
if (!storedSymbol) {
|
||||
onSetSymbolColumns(filteredGroupKeys)
|
||||
}
|
||||
if (!storedFill) {
|
||||
onSetFillColumns(filteredGroupKeys)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const fillColumns = storedFill || []
|
||||
const symbolColumns = storedSymbol || []
|
||||
|
||||
// TODO: allow xcolumn and ycolumn to be user selectable
|
||||
const xColumn = chooseXColumn(table)
|
||||
const yColumn = chooseYColumn(table)
|
||||
const xColumn = storedXColumn || chooseXColumn(table)
|
||||
const yColumn = storedYColumn || chooseYColumn(table)
|
||||
|
||||
const columnKeys = table.columnKeys
|
||||
|
||||
|
@ -150,12 +116,4 @@ const ScatterContainer: FunctionComponent<Props> = ({
|
|||
)
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
onSetFillColumns: setFillColumns,
|
||||
onSetSymbolColumns: setSymbolColumns,
|
||||
}
|
||||
|
||||
export default connect<{}, DispatchProps, {}>(
|
||||
null,
|
||||
mdtp
|
||||
)(ScatterContainer)
|
||||
export default ScatterContainer
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Libraries
|
||||
import React, {FunctionComponent, useMemo} from 'react'
|
||||
import {Config, fromFlux} from '@influxdata/vis'
|
||||
import {Config, Table} from '@influxdata/vis'
|
||||
|
||||
// Components
|
||||
import EmptyGraphMessage from 'src/shared/components/EmptyGraphMessage'
|
||||
|
@ -26,19 +26,23 @@ import {INVALID_DATA_COPY} from 'src/shared/copy/cell'
|
|||
import {RemoteDataState, XYView} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
files: string[]
|
||||
table: Table
|
||||
fluxGroupKeyUnion: string[]
|
||||
loading: RemoteDataState
|
||||
viewProperties: XYView
|
||||
children: (config: Config) => JSX.Element
|
||||
}
|
||||
|
||||
const XYContainer: FunctionComponent<Props> = ({
|
||||
files,
|
||||
table,
|
||||
fluxGroupKeyUnion,
|
||||
loading,
|
||||
children,
|
||||
viewProperties: {
|
||||
geom,
|
||||
colors,
|
||||
xColumn: storedXColumn,
|
||||
yColumn: storedYColumn,
|
||||
axes: {
|
||||
x: {label: xAxisLabel, bounds: xBounds},
|
||||
y: {
|
||||
|
@ -51,18 +55,12 @@ const XYContainer: FunctionComponent<Props> = ({
|
|||
},
|
||||
},
|
||||
}) => {
|
||||
const {table, fluxGroupKeyUnion} = useMemo(
|
||||
() => fromFlux(files.join('\n\n')),
|
||||
[files]
|
||||
)
|
||||
|
||||
// Eventually these will be configurable in the line graph options UI
|
||||
const xColumn = chooseXColumn(table)
|
||||
const yColumn = chooseYColumn(table)
|
||||
|
||||
const storedXDomain = useMemo(() => parseBounds(xBounds), [xBounds])
|
||||
const storedYDomain = useMemo(() => parseBounds(yBounds), [yBounds])
|
||||
|
||||
const xColumn = storedXColumn || chooseXColumn(table)
|
||||
const yColumn = storedYColumn || chooseYColumn(table)
|
||||
|
||||
const columnKeys = table.columnKeys
|
||||
|
||||
const [xDomain, onSetXDomain, onResetXDomain] = useVisDomainSettings(
|
||||
|
@ -74,7 +72,14 @@ const XYContainer: FunctionComponent<Props> = ({
|
|||
storedYDomain,
|
||||
columnKeys.includes(yColumn) ? table.getColumn(yColumn, 'number') : []
|
||||
)
|
||||
if (!xColumn || !yColumn) {
|
||||
|
||||
const isValidView =
|
||||
xColumn &&
|
||||
columnKeys.includes(xColumn) &&
|
||||
yColumn &&
|
||||
columnKeys.includes(yColumn)
|
||||
|
||||
if (!isValidView) {
|
||||
return <EmptyGraphMessage message={INVALID_DATA_COPY} />
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@ export const myView: View = {
|
|||
colors: [],
|
||||
note: '',
|
||||
showNoteWhenEmpty: false,
|
||||
xColumn: null,
|
||||
yColumn: null,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -343,6 +343,8 @@ describe('resourceToTemplate', () => {
|
|||
colors: [],
|
||||
note: '',
|
||||
showNoteWhenEmpty: false,
|
||||
xColumn: null,
|
||||
yColumn: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -118,6 +118,8 @@ const NEW_VIEW_CREATORS = {
|
|||
type: ViewType.XY,
|
||||
shape: ViewShape.ChronografV2,
|
||||
geom: XYViewGeom.Line,
|
||||
xColumn: null,
|
||||
yColumn: null,
|
||||
},
|
||||
}),
|
||||
[ViewType.Histogram]: (): NewView<HistogramView> => ({
|
||||
|
@ -126,7 +128,7 @@ const NEW_VIEW_CREATORS = {
|
|||
queries: [],
|
||||
type: ViewType.Histogram,
|
||||
shape: ViewShape.ChronografV2,
|
||||
xColumn: '_value',
|
||||
xColumn: null,
|
||||
xDomain: null,
|
||||
xAxisLabel: '',
|
||||
fillColumns: null,
|
||||
|
@ -143,8 +145,8 @@ const NEW_VIEW_CREATORS = {
|
|||
queries: [],
|
||||
type: ViewType.Heatmap,
|
||||
shape: ViewShape.ChronografV2,
|
||||
xColumn: '_time',
|
||||
yColumn: '_value',
|
||||
xColumn: null,
|
||||
yColumn: null,
|
||||
xDomain: null,
|
||||
yDomain: null,
|
||||
xAxisLabel: '',
|
||||
|
@ -182,6 +184,8 @@ const NEW_VIEW_CREATORS = {
|
|||
...defaultSingleStatViewProperties(),
|
||||
type: ViewType.LinePlusSingleStat,
|
||||
shape: ViewShape.ChronografV2,
|
||||
xColumn: null,
|
||||
yColumn: null,
|
||||
},
|
||||
}),
|
||||
[ViewType.Table]: (): NewView<TableView> => ({
|
||||
|
@ -225,9 +229,9 @@ const NEW_VIEW_CREATORS = {
|
|||
showNoteWhenEmpty: false,
|
||||
fillColumns: null,
|
||||
symbolColumns: null,
|
||||
xColumn: '_time',
|
||||
xColumn: null,
|
||||
xDomain: null,
|
||||
yColumn: '_value',
|
||||
yColumn: null,
|
||||
yDomain: null,
|
||||
xAxisLabel: '',
|
||||
yAxisLabel: '',
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
getXColumnSelection,
|
||||
getYColumnSelection,
|
||||
getFillColumnsSelection,
|
||||
getSymbolColumnsSelection,
|
||||
} from 'src/timeMachine/selectors'
|
||||
|
||||
// Types
|
||||
|
@ -19,15 +20,12 @@ interface StateProps {
|
|||
xColumn: string
|
||||
yColumn: string
|
||||
fillColumns: string[]
|
||||
symbolColumns: string[]
|
||||
fluxGroupKeyUnion: string[]
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
children: (props: {
|
||||
table: Table
|
||||
xColumn: string
|
||||
yColumn: string
|
||||
fillColumns: string[]
|
||||
}) => JSX.Element
|
||||
children: (props: StateProps) => JSX.Element
|
||||
}
|
||||
|
||||
type Props = StateProps & OwnProps
|
||||
|
@ -37,22 +35,34 @@ const VisDataTransform: FunctionComponent<Props> = ({
|
|||
xColumn,
|
||||
yColumn,
|
||||
fillColumns,
|
||||
symbolColumns,
|
||||
children,
|
||||
fluxGroupKeyUnion,
|
||||
}) => {
|
||||
return children({table, xColumn, yColumn, fillColumns})
|
||||
return children({
|
||||
table,
|
||||
xColumn,
|
||||
yColumn,
|
||||
fillColumns,
|
||||
symbolColumns,
|
||||
fluxGroupKeyUnion,
|
||||
})
|
||||
}
|
||||
|
||||
const mstp = (state: AppState) => {
|
||||
const table = getVisTable(state)
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const {table, fluxGroupKeyUnion} = getVisTable(state)
|
||||
const xColumn = getXColumnSelection(state)
|
||||
const yColumn = getYColumnSelection(state)
|
||||
const fillColumns = getFillColumnsSelection(state)
|
||||
const symbolColumns = getSymbolColumnsSelection(state)
|
||||
|
||||
return {
|
||||
table,
|
||||
xColumn,
|
||||
yColumn,
|
||||
fillColumns,
|
||||
symbolColumns,
|
||||
fluxGroupKeyUnion,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ import HistogramContainer from 'src/shared/components/HistogramContainer'
|
|||
import VisDataTransform from 'src/timeMachine/components/VisDataTransform'
|
||||
import RefreshingViewSwitcher from 'src/shared/components/RefreshingViewSwitcher'
|
||||
import HeatmapContainer from 'src/shared/components/HeatmapContainer'
|
||||
import ScatterContainer from 'src/shared/components/ScatterContainer'
|
||||
import SingleStat from 'src/shared/components/SingleStat'
|
||||
import LatestValueTransform from 'src/shared/components/LatestValueTransform'
|
||||
|
||||
// Utils
|
||||
import {getActiveTimeMachine, getTables} from 'src/timeMachine/selectors'
|
||||
|
@ -21,7 +24,11 @@ import {
|
|||
FluxTable,
|
||||
RemoteDataState,
|
||||
AppState,
|
||||
XYViewGeom,
|
||||
XYView,
|
||||
SingleStatView,
|
||||
} from 'src/types'
|
||||
import XYContainer from 'src/shared/components/XYContainer'
|
||||
|
||||
interface StateProps {
|
||||
files: string[]
|
||||
|
@ -90,6 +97,94 @@ const VisSwitcher: FunctionComponent<StateProps> = ({
|
|||
)
|
||||
}
|
||||
|
||||
if (properties.type === ViewType.Scatter) {
|
||||
return (
|
||||
<VisDataTransform>
|
||||
{({table, xColumn, yColumn, fillColumns, symbolColumns}) => (
|
||||
<ScatterContainer
|
||||
table={table}
|
||||
loading={loading}
|
||||
viewProperties={{
|
||||
...properties,
|
||||
xColumn,
|
||||
yColumn,
|
||||
fillColumns,
|
||||
symbolColumns,
|
||||
}}
|
||||
>
|
||||
{config => <Plot config={config} />}
|
||||
</ScatterContainer>
|
||||
)}
|
||||
</VisDataTransform>
|
||||
)
|
||||
}
|
||||
|
||||
if (properties.type === ViewType.XY) {
|
||||
return (
|
||||
<VisDataTransform>
|
||||
{({table, fluxGroupKeyUnion, xColumn, yColumn}) => (
|
||||
<XYContainer
|
||||
table={table}
|
||||
fluxGroupKeyUnion={fluxGroupKeyUnion}
|
||||
loading={loading}
|
||||
viewProperties={{
|
||||
...properties,
|
||||
xColumn,
|
||||
yColumn,
|
||||
}}
|
||||
>
|
||||
{config => <Plot config={config} />}
|
||||
</XYContainer>
|
||||
)}
|
||||
</VisDataTransform>
|
||||
)
|
||||
}
|
||||
|
||||
if (properties.type === ViewType.LinePlusSingleStat) {
|
||||
const xyProperties = {
|
||||
...properties,
|
||||
colors: properties.colors.filter(c => c.type === 'scale'),
|
||||
type: ViewType.XY,
|
||||
geom: XYViewGeom.Line,
|
||||
} as XYView
|
||||
|
||||
const singleStatProperties = {
|
||||
...properties,
|
||||
colors: properties.colors.filter(c => c.type !== 'scale'),
|
||||
type: ViewType.SingleStat,
|
||||
} as SingleStatView
|
||||
|
||||
return (
|
||||
<VisDataTransform>
|
||||
{({table, fluxGroupKeyUnion, xColumn, yColumn}) => (
|
||||
<XYContainer
|
||||
table={table}
|
||||
fluxGroupKeyUnion={fluxGroupKeyUnion}
|
||||
loading={loading}
|
||||
viewProperties={{
|
||||
...xyProperties,
|
||||
xColumn,
|
||||
yColumn,
|
||||
}}
|
||||
>
|
||||
{config => (
|
||||
<Plot config={config}>
|
||||
<LatestValueTransform table={table} quiet={true}>
|
||||
{latestValue => (
|
||||
<SingleStat
|
||||
stat={latestValue}
|
||||
properties={singleStatProperties}
|
||||
/>
|
||||
)}
|
||||
</LatestValueTransform>
|
||||
</Plot>
|
||||
)}
|
||||
</XYContainer>
|
||||
)}
|
||||
</VisDataTransform>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<RefreshingViewSwitcher
|
||||
tables={tables}
|
||||
|
|
|
@ -2,18 +2,12 @@
|
|||
import React, {FunctionComponent, ChangeEvent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {VIRIDIS, MAGMA, INFERNO, PLASMA} from '@influxdata/vis'
|
||||
import {
|
||||
Dropdown,
|
||||
Form,
|
||||
Grid,
|
||||
Input,
|
||||
Columns,
|
||||
InputType,
|
||||
} from '@influxdata/clockface'
|
||||
import {Form, Grid, Input, Columns, InputType} from '@influxdata/clockface'
|
||||
|
||||
// Components
|
||||
import AutoDomainInput from 'src/shared/components/AutoDomainInput'
|
||||
import HexColorSchemeDropdown from 'src/shared/components/HexColorSchemeDropdown'
|
||||
import ColumnSelector from 'src/shared/components/ColumnSelector'
|
||||
|
||||
// Actions
|
||||
import {
|
||||
|
@ -37,7 +31,6 @@ import {
|
|||
} from 'src/timeMachine/selectors'
|
||||
|
||||
// Types
|
||||
import {ComponentStatus} from '@influxdata/clockface'
|
||||
import {AppState} from 'src/types'
|
||||
|
||||
const HEATMAP_COLOR_SCHEMES = [
|
||||
|
@ -82,10 +75,6 @@ interface OwnProps {
|
|||
type Props = StateProps & DispatchProps & OwnProps
|
||||
|
||||
const HeatmapOptions: FunctionComponent<Props> = props => {
|
||||
const dataDropdownStatus = props.numericColumns.length
|
||||
? ComponentStatus.Default
|
||||
: ComponentStatus.Disabled
|
||||
|
||||
const onSetBinSize = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const val = +e.target.value
|
||||
|
||||
|
@ -100,34 +89,19 @@ const HeatmapOptions: FunctionComponent<Props> = props => {
|
|||
<Grid.Column>
|
||||
<h4 className="view-options--header">Customize Heatmap</h4>
|
||||
<h5 className="view-options--header">Data</h5>
|
||||
<Form.Element label="X Column">
|
||||
<Dropdown
|
||||
selectedID={props.xColumn}
|
||||
onChange={props.onSetXColumn}
|
||||
status={dataDropdownStatus}
|
||||
titleText="None"
|
||||
>
|
||||
{props.numericColumns.map(columnName => (
|
||||
<Dropdown.Item id={columnName} key={columnName} value={columnName}>
|
||||
{columnName}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown>
|
||||
</Form.Element>
|
||||
<Form.Element label="Y Column">
|
||||
<Dropdown
|
||||
selectedID={props.yColumn}
|
||||
onChange={props.onSetYColumn}
|
||||
status={dataDropdownStatus}
|
||||
titleText="None"
|
||||
>
|
||||
{props.numericColumns.map(columnName => (
|
||||
<Dropdown.Item id={columnName} key={columnName} value={columnName}>
|
||||
{columnName}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown>
|
||||
</Form.Element>
|
||||
<ColumnSelector
|
||||
selectedColumn={props.xColumn}
|
||||
onSelectColumn={props.onSetXColumn}
|
||||
availableColumns={props.numericColumns}
|
||||
axisName="x"
|
||||
/>
|
||||
<ColumnSelector
|
||||
selectedColumn={props.yColumn}
|
||||
onSelectColumn={props.onSetYColumn}
|
||||
availableColumns={props.numericColumns}
|
||||
axisName="y"
|
||||
/>
|
||||
|
||||
<h5 className="view-options--header">Options</h5>
|
||||
<Form.Element label="Color Scheme">
|
||||
<HexColorSchemeDropdown
|
||||
|
|
|
@ -32,11 +32,12 @@ import {ComponentStatus} from '@influxdata/clockface'
|
|||
import {HistogramPosition} from '@influxdata/vis'
|
||||
import {Color} from 'src/types/colors'
|
||||
import {AppState} from 'src/types'
|
||||
import ColumnSelector from 'src/shared/components/ColumnSelector'
|
||||
|
||||
interface StateProps {
|
||||
xColumn: string
|
||||
fillColumns: string[]
|
||||
availableXColumns: string[]
|
||||
numericColumns: string[]
|
||||
availableGroupColumns: string[]
|
||||
}
|
||||
|
||||
|
@ -64,7 +65,7 @@ const HistogramOptions: SFC<Props> = props => {
|
|||
const {
|
||||
xColumn,
|
||||
fillColumns,
|
||||
availableXColumns,
|
||||
numericColumns,
|
||||
availableGroupColumns,
|
||||
position,
|
||||
binCount,
|
||||
|
@ -80,10 +81,6 @@ const HistogramOptions: SFC<Props> = props => {
|
|||
onSetXAxisLabel,
|
||||
} = props
|
||||
|
||||
const xDropdownStatus = availableXColumns.length
|
||||
? ComponentStatus.Default
|
||||
: ComponentStatus.Disabled
|
||||
|
||||
const groupDropdownStatus = availableGroupColumns.length
|
||||
? ComponentStatus.Default
|
||||
: ComponentStatus.Disabled
|
||||
|
@ -92,20 +89,12 @@ const HistogramOptions: SFC<Props> = props => {
|
|||
<Grid.Column>
|
||||
<h4 className="view-options--header">Customize Histogram</h4>
|
||||
<h5 className="view-options--header">Data</h5>
|
||||
<Form.Element label="Column">
|
||||
<Dropdown
|
||||
selectedID={xColumn}
|
||||
onChange={onSetXColumn}
|
||||
status={xDropdownStatus}
|
||||
titleText="None"
|
||||
>
|
||||
{availableXColumns.map(columnName => (
|
||||
<Dropdown.Item id={columnName} key={columnName} value={columnName}>
|
||||
{columnName}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown>
|
||||
</Form.Element>
|
||||
<ColumnSelector
|
||||
selectedColumn={xColumn}
|
||||
onSelectColumn={onSetXColumn}
|
||||
availableColumns={numericColumns}
|
||||
axisName="x"
|
||||
/>
|
||||
<Form.Element label="Group By">
|
||||
<MultiSelectDropdown
|
||||
selectedIDs={fillColumns}
|
||||
|
@ -163,12 +152,12 @@ const HistogramOptions: SFC<Props> = props => {
|
|||
}
|
||||
|
||||
const mstp = (state: AppState) => {
|
||||
const availableXColumns = getNumericColumns(state)
|
||||
const numericColumns = getNumericColumns(state)
|
||||
const availableGroupColumns = getGroupableColumns(state)
|
||||
const xColumn = getXColumnSelection(state)
|
||||
const fillColumns = getFillColumnsSelection(state)
|
||||
|
||||
return {availableXColumns, availableGroupColumns, xColumn, fillColumns}
|
||||
return {numericColumns, availableGroupColumns, xColumn, fillColumns}
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
|
|
|
@ -10,6 +10,7 @@ import AxisAffixes from 'src/timeMachine/components/view_options/AxisAffixes'
|
|||
import ColorSelector from 'src/timeMachine/components/view_options/ColorSelector'
|
||||
import AutoDomainInput from 'src/shared/components/AutoDomainInput'
|
||||
import YAxisBase from 'src/timeMachine/components/view_options/YAxisBase'
|
||||
import ColumnSelector from 'src/shared/components/ColumnSelector'
|
||||
|
||||
// Actions
|
||||
import {
|
||||
|
@ -20,15 +21,24 @@ import {
|
|||
setYAxisBounds,
|
||||
setYAxisBase,
|
||||
setGeom,
|
||||
setXColumn,
|
||||
setYColumn,
|
||||
} from 'src/timeMachine/actions'
|
||||
|
||||
// Utils
|
||||
import {parseBounds} from 'src/shared/utils/vis'
|
||||
import {
|
||||
getXColumnSelection,
|
||||
getYColumnSelection,
|
||||
getNumericColumns,
|
||||
} from 'src/timeMachine/selectors'
|
||||
|
||||
// Types
|
||||
import {ViewType} from 'src/types'
|
||||
import {Axes, XYViewGeom} from 'src/types/dashboards'
|
||||
import {Color} from 'src/types/colors'
|
||||
import {AppState} from 'src/types'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
|
||||
interface OwnProps {
|
||||
type: ViewType
|
||||
|
@ -37,6 +47,12 @@ interface OwnProps {
|
|||
colors: Color[]
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
xColumn: string
|
||||
yColumn: string
|
||||
numericColumns: string[]
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
onUpdateYAxisLabel: typeof setYAxisLabel
|
||||
onUpdateAxisPrefix: typeof setAxisPrefix
|
||||
|
@ -44,10 +60,12 @@ interface DispatchProps {
|
|||
onUpdateYAxisBounds: typeof setYAxisBounds
|
||||
onUpdateYAxisBase: typeof setYAxisBase
|
||||
onUpdateColors: typeof setColors
|
||||
onSetXColumn: typeof setXColumn
|
||||
onSetYColumn: typeof setYColumn
|
||||
onSetGeom: typeof setGeom
|
||||
}
|
||||
|
||||
type Props = OwnProps & DispatchProps
|
||||
type Props = OwnProps & DispatchProps & StateProps
|
||||
|
||||
class LineOptions extends PureComponent<Props> {
|
||||
public render() {
|
||||
|
@ -63,12 +81,32 @@ class LineOptions extends PureComponent<Props> {
|
|||
onUpdateAxisSuffix,
|
||||
onUpdateYAxisBase,
|
||||
onSetGeom,
|
||||
onSetYColumn,
|
||||
yColumn,
|
||||
onSetXColumn,
|
||||
xColumn,
|
||||
numericColumns,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid.Column>
|
||||
<h4 className="view-options--header">Customize Line Graph</h4>
|
||||
<CloudExclude>
|
||||
<h5 className="view-options--header">Data</h5>
|
||||
<ColumnSelector
|
||||
selectedColumn={xColumn}
|
||||
onSelectColumn={onSetXColumn}
|
||||
availableColumns={numericColumns}
|
||||
axisName="x"
|
||||
/>
|
||||
<ColumnSelector
|
||||
selectedColumn={yColumn}
|
||||
onSelectColumn={onSetYColumn}
|
||||
availableColumns={numericColumns}
|
||||
axisName="y"
|
||||
/>
|
||||
</CloudExclude>
|
||||
<h5 className="view-options--header">Options</h5>
|
||||
</Grid.Column>
|
||||
{geom && <Geom geom={geom} onSetGeom={onSetGeom} />}
|
||||
|
@ -116,17 +154,27 @@ class LineOptions extends PureComponent<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
const mstp = (state: AppState) => {
|
||||
const xColumn = getXColumnSelection(state)
|
||||
const yColumn = getYColumnSelection(state)
|
||||
const numericColumns = getNumericColumns(state)
|
||||
|
||||
return {xColumn, yColumn, numericColumns}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
onUpdateYAxisLabel: setYAxisLabel,
|
||||
onUpdateAxisPrefix: setAxisPrefix,
|
||||
onUpdateAxisSuffix: setAxisSuffix,
|
||||
onUpdateYAxisBounds: setYAxisBounds,
|
||||
onUpdateYAxisBase: setYAxisBase,
|
||||
onSetXColumn: setXColumn,
|
||||
onSetYColumn: setYColumn,
|
||||
onUpdateColors: setColors,
|
||||
onSetGeom: setGeom,
|
||||
}
|
||||
|
||||
export default connect<{}, DispatchProps, OwnProps>(
|
||||
null,
|
||||
export default connect<StateProps, DispatchProps, OwnProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(LineOptions)
|
||||
|
|
|
@ -26,6 +26,8 @@ import {
|
|||
setAxisSuffix,
|
||||
setColorHexes,
|
||||
setYDomain,
|
||||
setXColumn,
|
||||
setYColumn,
|
||||
} from 'src/timeMachine/actions'
|
||||
|
||||
// Utils
|
||||
|
@ -33,6 +35,9 @@ import {
|
|||
getGroupableColumns,
|
||||
getFillColumnsSelection,
|
||||
getSymbolColumnsSelection,
|
||||
getXColumnSelection,
|
||||
getYColumnSelection,
|
||||
getNumericColumns,
|
||||
} from 'src/timeMachine/selectors'
|
||||
|
||||
// Types
|
||||
|
@ -40,6 +45,8 @@ import {ComponentStatus} from '@influxdata/clockface'
|
|||
import {AppState} from 'src/types'
|
||||
import HexColorSchemeDropdown from 'src/shared/components/HexColorSchemeDropdown'
|
||||
import AutoDomainInput from 'src/shared/components/AutoDomainInput'
|
||||
import ColumnSelector from 'src/shared/components/ColumnSelector'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
|
||||
const COLOR_SCHEMES = [
|
||||
{name: 'Nineteen Eighty Four', colors: NINETEEN_EIGHTY_FOUR},
|
||||
|
@ -55,6 +62,9 @@ interface StateProps {
|
|||
fillColumns: string[]
|
||||
symbolColumns: string[]
|
||||
availableGroupColumns: string[]
|
||||
xColumn: string
|
||||
yColumn: string
|
||||
numericColumns: string[]
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
|
@ -66,6 +76,8 @@ interface DispatchProps {
|
|||
onUpdateAxisSuffix: typeof setAxisSuffix
|
||||
onUpdateAxisPrefix: typeof setAxisPrefix
|
||||
onSetYDomain: typeof setYDomain
|
||||
onSetXColumn: typeof setXColumn
|
||||
onSetYColumn: typeof setYColumn
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -106,6 +118,11 @@ const ScatterOptions: SFC<Props> = props => {
|
|||
onUpdateAxisPrefix,
|
||||
yDomain,
|
||||
onSetYDomain,
|
||||
xColumn,
|
||||
yColumn,
|
||||
numericColumns,
|
||||
onSetXColumn,
|
||||
onSetYColumn,
|
||||
} = props
|
||||
|
||||
const groupDropdownStatus = availableGroupColumns.length
|
||||
|
@ -151,6 +168,20 @@ const ScatterOptions: SFC<Props> = props => {
|
|||
))}
|
||||
</MultiSelectDropdown>
|
||||
</Form.Element>
|
||||
<CloudExclude>
|
||||
<ColumnSelector
|
||||
selectedColumn={xColumn}
|
||||
onSelectColumn={onSetXColumn}
|
||||
availableColumns={numericColumns}
|
||||
axisName="x"
|
||||
/>
|
||||
<ColumnSelector
|
||||
selectedColumn={yColumn}
|
||||
onSelectColumn={onSetYColumn}
|
||||
availableColumns={numericColumns}
|
||||
axisName="y"
|
||||
/>
|
||||
</CloudExclude>
|
||||
<h5 className="view-options--header">Options</h5>
|
||||
<Form.Element label="Color Scheme">
|
||||
<HexColorSchemeDropdown
|
||||
|
@ -195,8 +226,18 @@ const mstp = (state: AppState): StateProps => {
|
|||
const availableGroupColumns = getGroupableColumns(state)
|
||||
const fillColumns = getFillColumnsSelection(state)
|
||||
const symbolColumns = getSymbolColumnsSelection(state)
|
||||
const xColumn = getXColumnSelection(state)
|
||||
const yColumn = getYColumnSelection(state)
|
||||
const numericColumns = getNumericColumns(state)
|
||||
|
||||
return {availableGroupColumns, fillColumns, symbolColumns}
|
||||
return {
|
||||
availableGroupColumns,
|
||||
fillColumns,
|
||||
symbolColumns,
|
||||
xColumn,
|
||||
yColumn,
|
||||
numericColumns,
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
|
@ -208,6 +249,8 @@ const mdtp = {
|
|||
onUpdateAxisPrefix: setAxisPrefix,
|
||||
onUpdateAxisSuffix: setAxisSuffix,
|
||||
onSetYDomain: setYDomain,
|
||||
onSetXColumn: setXColumn,
|
||||
onSetYColumn: setYColumn,
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps, OwnProps>(
|
||||
|
|
|
@ -5,6 +5,7 @@ import {fromFlux, Table} from '@influxdata/vis'
|
|||
|
||||
// Utils
|
||||
import {parseResponse} from 'src/shared/parsing/flux/response'
|
||||
import {chooseYColumn, chooseXColumn} from 'src/shared/utils/vis'
|
||||
|
||||
// Types
|
||||
import {
|
||||
|
@ -37,11 +38,13 @@ export const getTables = (state: AppState): FluxTable[] =>
|
|||
|
||||
const getVisTableMemoized = memoizeOne(fromFlux)
|
||||
|
||||
export const getVisTable = (state: AppState): Table => {
|
||||
export const getVisTable = (
|
||||
state: AppState
|
||||
): {table: Table; fluxGroupKeyUnion: string[]} => {
|
||||
const files = getActiveTimeMachine(state).queryResults.files || []
|
||||
const {table} = getVisTableMemoized(files.join('\n\n'))
|
||||
const {table, fluxGroupKeyUnion} = getVisTableMemoized(files.join('\n\n'))
|
||||
|
||||
return table
|
||||
return {table, fluxGroupKeyUnion}
|
||||
}
|
||||
|
||||
const getNumericColumnsMemoized = memoizeOne(
|
||||
|
@ -63,14 +66,14 @@ const getNumericColumnsMemoized = memoizeOne(
|
|||
)
|
||||
|
||||
export const getNumericColumns = (state: AppState): string[] => {
|
||||
const table = getVisTable(state)
|
||||
const {table} = getVisTable(state)
|
||||
|
||||
return getNumericColumnsMemoized(table)
|
||||
}
|
||||
|
||||
const getGroupableColumnsMemoized = memoizeOne(
|
||||
(table: Table): string[] => {
|
||||
const invalidGroupColumns = new Set(['_value', '_start', '_stop', '_time'])
|
||||
const invalidGroupColumns = new Set(['_value', '_time', 'table'])
|
||||
const groupableColumns = table.columnKeys.filter(
|
||||
name => !invalidGroupColumns.has(name)
|
||||
)
|
||||
|
@ -80,25 +83,21 @@ const getGroupableColumnsMemoized = memoizeOne(
|
|||
)
|
||||
|
||||
export const getGroupableColumns = (state: AppState): string[] => {
|
||||
const table = getVisTable(state)
|
||||
const {table} = getVisTable(state)
|
||||
|
||||
return getGroupableColumnsMemoized(table)
|
||||
}
|
||||
|
||||
const selectXYColumn = (validColumns: string[], preference: string): string => {
|
||||
const selectXYColumn = (
|
||||
validColumns: string[],
|
||||
preference: string,
|
||||
defaultSelection: string
|
||||
): string => {
|
||||
if (preference && validColumns.includes(preference)) {
|
||||
return preference
|
||||
}
|
||||
|
||||
if (validColumns.includes('_value')) {
|
||||
return '_value'
|
||||
}
|
||||
|
||||
if (validColumns.length) {
|
||||
return validColumns[0]
|
||||
}
|
||||
|
||||
return null
|
||||
return defaultSelection
|
||||
}
|
||||
|
||||
const getXColumnSelectionMemoized = memoizeOne(selectXYColumn)
|
||||
|
@ -107,59 +106,83 @@ const getYColumnSelectionMemoized = memoizeOne(selectXYColumn)
|
|||
|
||||
export const getXColumnSelection = (state: AppState): string => {
|
||||
const validXColumns = getNumericColumns(state)
|
||||
|
||||
const preference = get(getActiveTimeMachine(state), 'view.properties.xColumn')
|
||||
|
||||
return getXColumnSelectionMemoized(validXColumns, preference)
|
||||
const {table} = getVisTable(state)
|
||||
const defaultSelection = chooseXColumn(table)
|
||||
|
||||
return getXColumnSelectionMemoized(
|
||||
validXColumns,
|
||||
preference,
|
||||
defaultSelection
|
||||
)
|
||||
}
|
||||
|
||||
export const getYColumnSelection = (state: AppState): string => {
|
||||
const validYColumns = getNumericColumns(state)
|
||||
|
||||
const preference = get(getActiveTimeMachine(state), 'view.properties.yColumn')
|
||||
|
||||
return getYColumnSelectionMemoized(validYColumns, preference)
|
||||
const {table} = getVisTable(state)
|
||||
|
||||
const defaultSelection = chooseYColumn(table)
|
||||
|
||||
return getYColumnSelectionMemoized(
|
||||
validYColumns,
|
||||
preference,
|
||||
defaultSelection
|
||||
)
|
||||
}
|
||||
|
||||
const getFillColumnsSelectionMemoized = memoizeOne(
|
||||
(validFillColumns: string[], preference: string[]): string[] => {
|
||||
if (preference && preference.every(col => validFillColumns.includes(col))) {
|
||||
return preference
|
||||
}
|
||||
|
||||
return []
|
||||
const getGroupableColumnSelection = (
|
||||
validColumns: string[],
|
||||
preference: string[],
|
||||
fluxGroupKeyUnion: string[]
|
||||
): string[] => {
|
||||
if (preference && preference.every(col => validColumns.includes(col))) {
|
||||
return preference
|
||||
}
|
||||
|
||||
return fluxGroupKeyUnion
|
||||
}
|
||||
|
||||
const getFillColumnsSelectionMemoized = memoizeOne(getGroupableColumnSelection)
|
||||
|
||||
const getSymbolColumnsSelectionMemoized = memoizeOne(
|
||||
getGroupableColumnSelection
|
||||
)
|
||||
|
||||
export const getFillColumnsSelection = (state: AppState): string[] => {
|
||||
const validFillColumns = getGroupableColumns(state)
|
||||
|
||||
const preference = get(
|
||||
getActiveTimeMachine(state),
|
||||
'view.properties.fillColumns'
|
||||
)
|
||||
|
||||
return getFillColumnsSelectionMemoized(validFillColumns, preference)
|
||||
const {fluxGroupKeyUnion} = getVisTable(state)
|
||||
|
||||
return getFillColumnsSelectionMemoized(
|
||||
validFillColumns,
|
||||
preference,
|
||||
fluxGroupKeyUnion
|
||||
)
|
||||
}
|
||||
|
||||
const getSymbolColumnsSelectionMemoized = memoizeOne(
|
||||
(validSymbolColumns: string[], preference: string[]): string[] => {
|
||||
if (
|
||||
preference &&
|
||||
preference.every(col => validSymbolColumns.includes(col))
|
||||
) {
|
||||
return preference
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
)
|
||||
|
||||
export const getSymbolColumnsSelection = (state: AppState): string[] => {
|
||||
const validSymbolColumns = getGroupableColumns(state)
|
||||
const preference = get(
|
||||
getActiveTimeMachine(state),
|
||||
'view.properties.symbolColumns'
|
||||
)
|
||||
const {fluxGroupKeyUnion} = getVisTable(state)
|
||||
|
||||
return getSymbolColumnsSelectionMemoized(validSymbolColumns, preference)
|
||||
return getSymbolColumnsSelectionMemoized(
|
||||
validSymbolColumns,
|
||||
preference,
|
||||
fluxGroupKeyUnion
|
||||
)
|
||||
}
|
||||
|
||||
export const getSaveableView = (state: AppState): QueryView & {id?: string} => {
|
||||
|
@ -195,5 +218,40 @@ export const getSaveableView = (state: AppState): QueryView & {id?: string} => {
|
|||
}
|
||||
}
|
||||
|
||||
if (saveableView.properties.type === ViewType.Scatter) {
|
||||
saveableView = {
|
||||
...saveableView,
|
||||
properties: {
|
||||
...saveableView.properties,
|
||||
xColumn: getXColumnSelection(state),
|
||||
yColumn: getYColumnSelection(state),
|
||||
fillColumns: getFillColumnsSelection(state),
|
||||
symbolColumns: getSymbolColumnsSelection(state),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (saveableView.properties.type === ViewType.XY) {
|
||||
saveableView = {
|
||||
...saveableView,
|
||||
properties: {
|
||||
...saveableView.properties,
|
||||
xColumn: getXColumnSelection(state),
|
||||
yColumn: getYColumnSelection(state),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (saveableView.properties.type === ViewType.LinePlusSingleStat) {
|
||||
saveableView = {
|
||||
...saveableView,
|
||||
properties: {
|
||||
...saveableView.properties,
|
||||
xColumn: getXColumnSelection(state),
|
||||
yColumn: getYColumnSelection(state),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return saveableView
|
||||
}
|
||||
|
|
|
@ -173,6 +173,8 @@ export interface XYView {
|
|||
queries: DashboardQuery[]
|
||||
shape: ViewShape.ChronografV2
|
||||
axes: Axes
|
||||
xColumn: string
|
||||
yColumn: string
|
||||
colors: Color[]
|
||||
legend: Legend
|
||||
note: string
|
||||
|
@ -188,6 +190,8 @@ export interface LinePlusSingleStatView {
|
|||
legend: Legend
|
||||
prefix: string
|
||||
suffix: string
|
||||
xColumn: string
|
||||
yColumn: string
|
||||
decimalPlaces: DecimalPlaces
|
||||
note: string
|
||||
showNoteWhenEmpty: boolean
|
||||
|
|
Loading…
Reference in New Issue