|
|
|
@ -1,5 +1,4 @@
|
|
|
|
|
import React, {Component} from 'react'
|
|
|
|
|
import PropTypes from 'prop-types'
|
|
|
|
|
|
|
|
|
|
import _ from 'lodash'
|
|
|
|
|
import uuid from 'uuid'
|
|
|
|
@ -9,13 +8,16 @@ import QueryMaker from 'src/dashboards/components/QueryMaker'
|
|
|
|
|
import Visualization from 'src/dashboards/components/Visualization'
|
|
|
|
|
import OverlayControls from 'src/dashboards/components/OverlayControls'
|
|
|
|
|
import DisplayOptions from 'src/dashboards/components/DisplayOptions'
|
|
|
|
|
import CEOBottom from 'src/dashboards/components/CEOBottom'
|
|
|
|
|
|
|
|
|
|
import * as queryModifiers from 'src/utils/queryTransitions'
|
|
|
|
|
|
|
|
|
|
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
|
|
|
|
import {buildQuery} from 'utils/influxql'
|
|
|
|
|
import {getQueryConfig} from 'shared/apis'
|
|
|
|
|
import {buildQuery} from 'src/utils/influxql'
|
|
|
|
|
import {getQueryConfig} from 'src/shared/apis'
|
|
|
|
|
import {IS_STATIC_LEGEND} from 'src/shared/constants'
|
|
|
|
|
import {ColorString, ColorNumber} from 'src/types/colors'
|
|
|
|
|
import {nextSource} from 'src/dashboards/utils/sources'
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
removeUnselectedTemplateValues,
|
|
|
|
@ -25,98 +27,237 @@ import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
|
|
|
|
|
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
|
|
|
|
import {AUTO_GROUP_BY} from 'src/shared/constants'
|
|
|
|
|
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
|
|
|
|
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
|
|
|
|
import {TimeRange, Source, Query} from 'src/types'
|
|
|
|
|
import {Status} from 'src/types/query'
|
|
|
|
|
import {Cell, CellQuery, Legend} from 'src/types/dashboard'
|
|
|
|
|
|
|
|
|
|
const staticLegend: Legend = {
|
|
|
|
|
type: 'static',
|
|
|
|
|
orientation: 'bottom',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Template {
|
|
|
|
|
tempVar: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface QueryStatus {
|
|
|
|
|
queryID: string
|
|
|
|
|
status: Status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
sources: Source[]
|
|
|
|
|
editQueryStatus: () => void
|
|
|
|
|
onCancel: () => void
|
|
|
|
|
onSave: (cell: Cell) => void
|
|
|
|
|
source: Source
|
|
|
|
|
dashboardID: string
|
|
|
|
|
queryStatus: QueryStatus
|
|
|
|
|
autoRefresh: number
|
|
|
|
|
templates: Template[]
|
|
|
|
|
timeRange: TimeRange
|
|
|
|
|
thresholdsListType: string
|
|
|
|
|
thresholdsListColors: ColorNumber[]
|
|
|
|
|
gaugeColors: ColorNumber[]
|
|
|
|
|
lineColors: ColorString[]
|
|
|
|
|
cell: Cell
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface State {
|
|
|
|
|
queriesWorkingDraft: Query[]
|
|
|
|
|
activeQueryIndex: number
|
|
|
|
|
isDisplayOptionsTabActive: boolean
|
|
|
|
|
isStaticLegend: boolean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const createWorkingDraft = (source: string, query: CellQuery): Query => {
|
|
|
|
|
const {queryConfig} = query
|
|
|
|
|
const draft: Query = {
|
|
|
|
|
...queryConfig,
|
|
|
|
|
id: uuid.v4(),
|
|
|
|
|
source,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return draft
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const createWorkingDrafts = (source: string, queries: CellQuery[]): Query[] =>
|
|
|
|
|
_.cloneDeep(
|
|
|
|
|
queries.map((query: CellQuery) => createWorkingDraft(source, query))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class CellEditorOverlay extends Component<Props, State> {
|
|
|
|
|
private overlayRef: HTMLDivElement
|
|
|
|
|
private formattedSources = this.props.sources.map(s => ({
|
|
|
|
|
...s,
|
|
|
|
|
text: `${s.name} @ ${s.url}`,
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
class CellEditorOverlay extends Component {
|
|
|
|
|
constructor(props) {
|
|
|
|
|
super(props)
|
|
|
|
|
|
|
|
|
|
const {cell: {queries, legend}, sources} = props
|
|
|
|
|
|
|
|
|
|
let source = _.get(queries, ['0', 'source'], null)
|
|
|
|
|
source = sources.find(s => s.links.self === source) || props.source
|
|
|
|
|
|
|
|
|
|
const queriesWorkingDraft = _.cloneDeep(
|
|
|
|
|
queries.map(({queryConfig}) => ({
|
|
|
|
|
...queryConfig,
|
|
|
|
|
id: uuid.v4(),
|
|
|
|
|
source,
|
|
|
|
|
}))
|
|
|
|
|
)
|
|
|
|
|
const {cell: {queries, legend}} = props
|
|
|
|
|
const queriesWorkingDraft = createWorkingDrafts(this.sourceLink, queries)
|
|
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
|
queriesWorkingDraft,
|
|
|
|
|
activeQueryIndex: 0,
|
|
|
|
|
isDisplayOptionsTabActive: false,
|
|
|
|
|
staticLegend: IS_STATIC_LEGEND(legend),
|
|
|
|
|
isStaticLegend: IS_STATIC_LEGEND(legend),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
|
public componentWillReceiveProps(nextProps: Props) {
|
|
|
|
|
const {status, queryID} = this.props.queryStatus
|
|
|
|
|
const nextStatus = nextProps.queryStatus
|
|
|
|
|
if (nextStatus.status && nextStatus.queryID) {
|
|
|
|
|
if (nextStatus.queryID !== queryID || nextStatus.status !== status) {
|
|
|
|
|
const nextQueries = this.state.queriesWorkingDraft.map(
|
|
|
|
|
q => (q.id === queryID ? {...q, status: nextStatus.status} : q)
|
|
|
|
|
)
|
|
|
|
|
this.setState({queriesWorkingDraft: nextQueries})
|
|
|
|
|
}
|
|
|
|
|
const {queriesWorkingDraft} = this.state
|
|
|
|
|
const {queryStatus} = nextProps
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
queryStatus.status &&
|
|
|
|
|
queryStatus.queryID &&
|
|
|
|
|
(queryStatus.queryID !== queryID || queryStatus.status !== status)
|
|
|
|
|
) {
|
|
|
|
|
const nextQueries = queriesWorkingDraft.map(
|
|
|
|
|
q => (q.id === queryID ? {...q, status: queryStatus.status} : q)
|
|
|
|
|
)
|
|
|
|
|
this.setState({queriesWorkingDraft: nextQueries})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentDidMount = () => {
|
|
|
|
|
public componentDidMount() {
|
|
|
|
|
this.overlayRef.focus()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queryStateReducer = queryModifier => (queryID, ...payload) => {
|
|
|
|
|
public render() {
|
|
|
|
|
const {
|
|
|
|
|
onCancel,
|
|
|
|
|
templates,
|
|
|
|
|
timeRange,
|
|
|
|
|
autoRefresh,
|
|
|
|
|
editQueryStatus,
|
|
|
|
|
} = this.props
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
activeQueryIndex,
|
|
|
|
|
isDisplayOptionsTabActive,
|
|
|
|
|
queriesWorkingDraft,
|
|
|
|
|
isStaticLegend,
|
|
|
|
|
} = this.state
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={OVERLAY_TECHNOLOGY}
|
|
|
|
|
onKeyDown={this.handleKeyDown}
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
ref={this.onRef}
|
|
|
|
|
>
|
|
|
|
|
<ResizeContainer
|
|
|
|
|
containerClass="resizer--full-size"
|
|
|
|
|
minTopHeight={MINIMUM_HEIGHTS.visualization}
|
|
|
|
|
minBottomHeight={MINIMUM_HEIGHTS.queryMaker}
|
|
|
|
|
initialTopHeight={INITIAL_HEIGHTS.visualization}
|
|
|
|
|
initialBottomHeight={INITIAL_HEIGHTS.queryMaker}
|
|
|
|
|
>
|
|
|
|
|
<Visualization
|
|
|
|
|
timeRange={timeRange}
|
|
|
|
|
templates={templates}
|
|
|
|
|
autoRefresh={autoRefresh}
|
|
|
|
|
queryConfigs={queriesWorkingDraft}
|
|
|
|
|
editQueryStatus={editQueryStatus}
|
|
|
|
|
staticLegend={isStaticLegend}
|
|
|
|
|
/>
|
|
|
|
|
<CEOBottom>
|
|
|
|
|
<OverlayControls
|
|
|
|
|
onCancel={onCancel}
|
|
|
|
|
queries={queriesWorkingDraft}
|
|
|
|
|
sources={this.formattedSources}
|
|
|
|
|
onSave={this.handleSaveCell}
|
|
|
|
|
selected={this.findSelectedSource()}
|
|
|
|
|
onSetQuerySource={this.handleSetQuerySource}
|
|
|
|
|
isSavable={this.isSaveable}
|
|
|
|
|
isDisplayOptionsTabActive={isDisplayOptionsTabActive}
|
|
|
|
|
onClickDisplayOptions={this.handleClickDisplayOptionsTab}
|
|
|
|
|
/>
|
|
|
|
|
{isDisplayOptionsTabActive ? (
|
|
|
|
|
<DisplayOptions
|
|
|
|
|
queryConfigs={queriesWorkingDraft}
|
|
|
|
|
onToggleStaticLegend={this.handleToggleStaticLegend}
|
|
|
|
|
staticLegend={isStaticLegend}
|
|
|
|
|
onResetFocus={this.handleResetFocus}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<QueryMaker
|
|
|
|
|
source={this.source}
|
|
|
|
|
templates={templates}
|
|
|
|
|
queries={queriesWorkingDraft}
|
|
|
|
|
actions={this.queryActions}
|
|
|
|
|
timeRange={timeRange}
|
|
|
|
|
onDeleteQuery={this.handleDeleteQuery}
|
|
|
|
|
onAddQuery={this.handleAddQuery}
|
|
|
|
|
activeQueryIndex={activeQueryIndex}
|
|
|
|
|
activeQuery={this.getActiveQuery()}
|
|
|
|
|
setActiveQueryIndex={this.handleSetActiveQueryIndex}
|
|
|
|
|
initialGroupByTime={AUTO_GROUP_BY}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</CEOBottom>
|
|
|
|
|
</ResizeContainer>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onRef = (r: HTMLDivElement) => {
|
|
|
|
|
this.overlayRef = r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private queryStateReducer = queryModifier => (queryID, ...payload) => {
|
|
|
|
|
const {queriesWorkingDraft} = this.state
|
|
|
|
|
const query = queriesWorkingDraft.find(q => q.id === queryID)
|
|
|
|
|
|
|
|
|
|
const nextQuery = queryModifier(query, ...payload)
|
|
|
|
|
|
|
|
|
|
const nextQueries = queriesWorkingDraft.map(
|
|
|
|
|
q =>
|
|
|
|
|
q.id === query.id
|
|
|
|
|
? {...nextQuery, source: this.nextSource(q, nextQuery)}
|
|
|
|
|
: q
|
|
|
|
|
)
|
|
|
|
|
const nextQueries = queriesWorkingDraft.map(q => {
|
|
|
|
|
if (q.id === query.id) {
|
|
|
|
|
return {...nextQuery, source: nextSource(q, nextQuery)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return q
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.setState({queriesWorkingDraft: nextQueries})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleAddQuery = () => {
|
|
|
|
|
private handleAddQuery = () => {
|
|
|
|
|
const {queriesWorkingDraft} = this.state
|
|
|
|
|
const newIndex = queriesWorkingDraft.length
|
|
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
|
queriesWorkingDraft: [
|
|
|
|
|
...queriesWorkingDraft,
|
|
|
|
|
defaultQueryConfig({id: uuid.v4()}),
|
|
|
|
|
{...defaultQueryConfig({id: uuid.v4()}), source: null},
|
|
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
this.handleSetActiveQueryIndex(newIndex)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleDeleteQuery = index => {
|
|
|
|
|
const nextQueries = this.state.queriesWorkingDraft.filter(
|
|
|
|
|
(__, i) => i !== index
|
|
|
|
|
)
|
|
|
|
|
private handleDeleteQuery = index => {
|
|
|
|
|
const {queriesWorkingDraft} = this.state
|
|
|
|
|
const nextQueries = queriesWorkingDraft.filter((__, i) => i !== index)
|
|
|
|
|
|
|
|
|
|
this.setState({queriesWorkingDraft: nextQueries})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleSaveCell = () => {
|
|
|
|
|
const {queriesWorkingDraft, staticLegend} = this.state
|
|
|
|
|
private handleSaveCell = () => {
|
|
|
|
|
const {queriesWorkingDraft, isStaticLegend} = this.state
|
|
|
|
|
const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
|
|
|
|
|
|
|
|
|
|
const queries = queriesWorkingDraft.map(q => {
|
|
|
|
|
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
|
|
|
|
|
const query = q.rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, q)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
queryConfig: q,
|
|
|
|
|
query,
|
|
|
|
|
source: _.get(q, ['source', 'links', 'self'], null),
|
|
|
|
|
query: q.rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, q),
|
|
|
|
|
source: q.source,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
@ -131,28 +272,23 @@ class CellEditorOverlay extends Component {
|
|
|
|
|
...cell,
|
|
|
|
|
queries,
|
|
|
|
|
colors,
|
|
|
|
|
legend: staticLegend
|
|
|
|
|
? {
|
|
|
|
|
type: 'static',
|
|
|
|
|
orientation: 'bottom',
|
|
|
|
|
}
|
|
|
|
|
: {},
|
|
|
|
|
legend: isStaticLegend ? staticLegend : {},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleClickDisplayOptionsTab = isDisplayOptionsTabActive => () => {
|
|
|
|
|
private handleClickDisplayOptionsTab = isDisplayOptionsTabActive => () => {
|
|
|
|
|
this.setState({isDisplayOptionsTabActive})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleSetActiveQueryIndex = activeQueryIndex => {
|
|
|
|
|
private handleSetActiveQueryIndex = activeQueryIndex => {
|
|
|
|
|
this.setState({activeQueryIndex})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleToggleStaticLegend = staticLegend => () => {
|
|
|
|
|
this.setState({staticLegend})
|
|
|
|
|
private handleToggleStaticLegend = isStaticLegend => () => {
|
|
|
|
|
this.setState({isStaticLegend})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleSetQuerySource = source => {
|
|
|
|
|
private handleSetQuerySource = source => {
|
|
|
|
|
const queriesWorkingDraft = this.state.queriesWorkingDraft.map(q => ({
|
|
|
|
|
..._.cloneDeep(q),
|
|
|
|
|
source,
|
|
|
|
@ -161,15 +297,13 @@ class CellEditorOverlay extends Component {
|
|
|
|
|
this.setState({queriesWorkingDraft})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getActiveQuery = () => {
|
|
|
|
|
private getActiveQuery = () => {
|
|
|
|
|
const {queriesWorkingDraft, activeQueryIndex} = this.state
|
|
|
|
|
const activeQuery = queriesWorkingDraft[activeQueryIndex]
|
|
|
|
|
const defaultQuery = queriesWorkingDraft[0]
|
|
|
|
|
|
|
|
|
|
return activeQuery || defaultQuery
|
|
|
|
|
return _.get(queriesWorkingDraft, activeQueryIndex, queriesWorkingDraft[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleEditRawText = async (url, id, text) => {
|
|
|
|
|
private handleEditRawText = async (url, id, text) => {
|
|
|
|
|
const templates = removeUnselectedTemplateValues(this.props.templates)
|
|
|
|
|
|
|
|
|
|
// use this as the handler passed into fetchTimeSeries to update a query status
|
|
|
|
@ -185,203 +319,86 @@ class CellEditorOverlay extends Component {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
formatSources = this.props.sources.map(s => ({
|
|
|
|
|
...s,
|
|
|
|
|
text: `${s.name} @ ${s.url}`,
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
findSelectedSource = () => {
|
|
|
|
|
private findSelectedSource = () => {
|
|
|
|
|
const {source} = this.props
|
|
|
|
|
const sources = this.formatSources
|
|
|
|
|
const query = _.get(this.state.queriesWorkingDraft, 0, false)
|
|
|
|
|
const sources = this.formattedSources
|
|
|
|
|
const currentSource = _.get(this.state.queriesWorkingDraft, '0.source')
|
|
|
|
|
|
|
|
|
|
if (!query || !query.source) {
|
|
|
|
|
if (!currentSource) {
|
|
|
|
|
const defaultSource = sources.find(s => s.id === source.id)
|
|
|
|
|
return (defaultSource && defaultSource.text) || 'No sources'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const selected = sources.find(s => s.id === query.source.id)
|
|
|
|
|
const selected = sources.find(s => s.links.self === currentSource)
|
|
|
|
|
return (selected && selected.text) || 'No sources'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getSource = () => {
|
|
|
|
|
const {source, sources} = this.props
|
|
|
|
|
const query = _.get(this.state.queriesWorkingDraft, 0, false)
|
|
|
|
|
private handleKeyDown = e => {
|
|
|
|
|
switch (e.key) {
|
|
|
|
|
case 'Enter':
|
|
|
|
|
if (!e.metaKey) {
|
|
|
|
|
return
|
|
|
|
|
} else if (e.target === this.overlayRef) {
|
|
|
|
|
this.handleSaveCell()
|
|
|
|
|
} else {
|
|
|
|
|
e.target.blur()
|
|
|
|
|
setTimeout(this.handleSaveCell, 50)
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
case 'Escape':
|
|
|
|
|
if (e.target === this.overlayRef) {
|
|
|
|
|
this.props.onCancel()
|
|
|
|
|
} else {
|
|
|
|
|
const targetIsDropdown = e.target.classList[0] === 'dropdown'
|
|
|
|
|
const targetIsButton = e.target.tagName === 'BUTTON'
|
|
|
|
|
|
|
|
|
|
if (!query || !query.source) {
|
|
|
|
|
return source
|
|
|
|
|
}
|
|
|
|
|
if (targetIsDropdown || targetIsButton) {
|
|
|
|
|
return this.props.onCancel()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const querySource = sources.find(s => s.id === query.source.id)
|
|
|
|
|
return querySource || source
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextSource = (prevQuery, nextQuery) => {
|
|
|
|
|
if (nextQuery.source) {
|
|
|
|
|
return nextQuery.source
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return prevQuery.source
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleKeyDown = e => {
|
|
|
|
|
if (e.key === 'Enter' && e.metaKey && e.target === this.overlayRef) {
|
|
|
|
|
this.handleSaveCell()
|
|
|
|
|
}
|
|
|
|
|
if (e.key === 'Enter' && e.metaKey && e.target !== this.overlayRef) {
|
|
|
|
|
e.target.blur()
|
|
|
|
|
setTimeout(this.handleSaveCell, 50)
|
|
|
|
|
}
|
|
|
|
|
if (e.key === 'Escape' && e.target === this.overlayRef) {
|
|
|
|
|
this.props.onCancel()
|
|
|
|
|
}
|
|
|
|
|
if (e.key === 'Escape' && e.target !== this.overlayRef) {
|
|
|
|
|
const targetIsDropdown = e.target.classList[0] === 'dropdown'
|
|
|
|
|
const targetIsButton = e.target.tagName === 'BUTTON'
|
|
|
|
|
|
|
|
|
|
if (targetIsDropdown || targetIsButton) {
|
|
|
|
|
return this.props.onCancel()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.target.blur()
|
|
|
|
|
this.overlayRef.focus()
|
|
|
|
|
e.target.blur()
|
|
|
|
|
this.overlayRef.focus()
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleResetFocus = () => {
|
|
|
|
|
private handleResetFocus = () => {
|
|
|
|
|
this.overlayRef.focus()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
const {
|
|
|
|
|
onCancel,
|
|
|
|
|
templates,
|
|
|
|
|
timeRange,
|
|
|
|
|
autoRefresh,
|
|
|
|
|
editQueryStatus,
|
|
|
|
|
} = this.props
|
|
|
|
|
private get isSaveable(): boolean {
|
|
|
|
|
const {queriesWorkingDraft} = this.state
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
activeQueryIndex,
|
|
|
|
|
isDisplayOptionsTabActive,
|
|
|
|
|
queriesWorkingDraft,
|
|
|
|
|
staticLegend,
|
|
|
|
|
} = this.state
|
|
|
|
|
return queriesWorkingDraft.every(
|
|
|
|
|
(query: Query) =>
|
|
|
|
|
(!!query.measurement && !!query.database && !!query.fields.length) ||
|
|
|
|
|
!!query.rawText
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const queryActions = {
|
|
|
|
|
private get queryActions() {
|
|
|
|
|
return {
|
|
|
|
|
editRawTextAsync: this.handleEditRawText,
|
|
|
|
|
..._.mapValues(queryModifiers, qm => this.queryStateReducer(qm)),
|
|
|
|
|
..._.mapValues(queryModifiers, this.queryStateReducer),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private get sourceLink(): string {
|
|
|
|
|
const {cell: {queries}, source: {links}} = this.props
|
|
|
|
|
return _.get(queries, '0.source.links.self', links.self)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private get source() {
|
|
|
|
|
const {source, sources} = this.props
|
|
|
|
|
const query = _.get(this.state.queriesWorkingDraft, 0, {source: null})
|
|
|
|
|
|
|
|
|
|
if (!query.source) {
|
|
|
|
|
return source
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isQuerySavable = query =>
|
|
|
|
|
(!!query.measurement && !!query.database && !!query.fields.length) ||
|
|
|
|
|
!!query.rawText
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={OVERLAY_TECHNOLOGY}
|
|
|
|
|
onKeyDown={this.handleKeyDown}
|
|
|
|
|
tabIndex="0"
|
|
|
|
|
ref={r => (this.overlayRef = r)}
|
|
|
|
|
>
|
|
|
|
|
<ResizeContainer
|
|
|
|
|
containerClass="resizer--full-size"
|
|
|
|
|
minTopHeight={MINIMUM_HEIGHTS.visualization}
|
|
|
|
|
minBottomHeight={MINIMUM_HEIGHTS.queryMaker}
|
|
|
|
|
initialTopHeight={INITIAL_HEIGHTS.visualization}
|
|
|
|
|
initialBottomHeight={INITIAL_HEIGHTS.queryMaker}
|
|
|
|
|
>
|
|
|
|
|
<Visualization
|
|
|
|
|
timeRange={timeRange}
|
|
|
|
|
templates={templates}
|
|
|
|
|
autoRefresh={autoRefresh}
|
|
|
|
|
queryConfigs={queriesWorkingDraft}
|
|
|
|
|
editQueryStatus={editQueryStatus}
|
|
|
|
|
staticLegend={staticLegend}
|
|
|
|
|
/>
|
|
|
|
|
<CEOBottom>
|
|
|
|
|
<OverlayControls
|
|
|
|
|
onCancel={onCancel}
|
|
|
|
|
queries={queriesWorkingDraft}
|
|
|
|
|
sources={this.formatSources}
|
|
|
|
|
onSave={this.handleSaveCell}
|
|
|
|
|
selected={this.findSelectedSource()}
|
|
|
|
|
onSetQuerySource={this.handleSetQuerySource}
|
|
|
|
|
isSavable={queriesWorkingDraft.every(isQuerySavable)}
|
|
|
|
|
isDisplayOptionsTabActive={isDisplayOptionsTabActive}
|
|
|
|
|
onClickDisplayOptions={this.handleClickDisplayOptionsTab}
|
|
|
|
|
/>
|
|
|
|
|
{isDisplayOptionsTabActive ? (
|
|
|
|
|
<DisplayOptions
|
|
|
|
|
queryConfigs={queriesWorkingDraft}
|
|
|
|
|
onToggleStaticLegend={this.handleToggleStaticLegend}
|
|
|
|
|
staticLegend={staticLegend}
|
|
|
|
|
onResetFocus={this.handleResetFocus}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<QueryMaker
|
|
|
|
|
source={this.getSource()}
|
|
|
|
|
templates={templates}
|
|
|
|
|
queries={queriesWorkingDraft}
|
|
|
|
|
actions={queryActions}
|
|
|
|
|
autoRefresh={autoRefresh}
|
|
|
|
|
timeRange={timeRange}
|
|
|
|
|
onDeleteQuery={this.handleDeleteQuery}
|
|
|
|
|
onAddQuery={this.handleAddQuery}
|
|
|
|
|
activeQueryIndex={activeQueryIndex}
|
|
|
|
|
activeQuery={this.getActiveQuery()}
|
|
|
|
|
setActiveQueryIndex={this.handleSetActiveQueryIndex}
|
|
|
|
|
initialGroupByTime={AUTO_GROUP_BY}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</CEOBottom>
|
|
|
|
|
</ResizeContainer>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
return sources.find(s => s.links.self === query.source) || source
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CEOBottom = ({children}) => (
|
|
|
|
|
<div className="overlay-technology--editor">{children}</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const {arrayOf, func, node, number, shape, string} = PropTypes
|
|
|
|
|
|
|
|
|
|
CellEditorOverlay.propTypes = {
|
|
|
|
|
onCancel: func.isRequired,
|
|
|
|
|
onSave: func.isRequired,
|
|
|
|
|
cell: shape({}).isRequired,
|
|
|
|
|
templates: arrayOf(
|
|
|
|
|
shape({
|
|
|
|
|
tempVar: string.isRequired,
|
|
|
|
|
})
|
|
|
|
|
).isRequired,
|
|
|
|
|
timeRange: shape({
|
|
|
|
|
upper: string,
|
|
|
|
|
lower: string,
|
|
|
|
|
}).isRequired,
|
|
|
|
|
autoRefresh: number.isRequired,
|
|
|
|
|
source: shape({
|
|
|
|
|
links: shape({
|
|
|
|
|
proxy: string.isRequired,
|
|
|
|
|
queries: string.isRequired,
|
|
|
|
|
}).isRequired,
|
|
|
|
|
}).isRequired,
|
|
|
|
|
editQueryStatus: func.isRequired,
|
|
|
|
|
queryStatus: shape({
|
|
|
|
|
queryID: string,
|
|
|
|
|
status: shape({}),
|
|
|
|
|
}).isRequired,
|
|
|
|
|
dashboardID: string.isRequired,
|
|
|
|
|
sources: arrayOf(shape()),
|
|
|
|
|
thresholdsListType: string.isRequired,
|
|
|
|
|
thresholdsListColors: colorsNumberSchema.isRequired,
|
|
|
|
|
gaugeColors: colorsNumberSchema.isRequired,
|
|
|
|
|
lineColors: colorsStringSchema.isRequired,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CEOBottom.propTypes = {
|
|
|
|
|
children: node,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default CellEditorOverlay
|