Merge branch 'master' into bugfix/groupbys-in-alert-rule
commit
e044f5b76a
|
@ -11,6 +11,7 @@
|
||||||
1. [#3215](https://github.com/influxdata/chronograf/pull/3215): Fix Template Variables Control Bar to top of dashboard page
|
1. [#3215](https://github.com/influxdata/chronograf/pull/3215): Fix Template Variables Control Bar to top of dashboard page
|
||||||
1. [#3214](https://github.com/influxdata/chronograf/pull/3214): Remove extra click when creating dashboard cell
|
1. [#3214](https://github.com/influxdata/chronograf/pull/3214): Remove extra click when creating dashboard cell
|
||||||
1. [#3256](https://github.com/influxdata/chronograf/pull/3256): Reduce font sizes in dashboards for increased space efficiency
|
1. [#3256](https://github.com/influxdata/chronograf/pull/3256): Reduce font sizes in dashboards for increased space efficiency
|
||||||
|
1. [#3320](https://github.com/influxdata/chronograf/pull/3320): Add overlay animation to Template Variables Manager
|
||||||
1. [#3245](https://github.com/influxdata/chronograf/pull/3245): Display 'no results' on cells without results
|
1. [#3245](https://github.com/influxdata/chronograf/pull/3245): Display 'no results' on cells without results
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
1. [#3281](https://github.com/influxdata/chronograf/pull/3281): Fix base path for kapacitor logs
|
1. [#3281](https://github.com/influxdata/chronograf/pull/3281): Fix base path for kapacitor logs
|
||||||
1. [#3284](https://github.com/influxdata/chronograf/pull/3284): Fix logout when using basepath & simplify basepath usage (deprecates `PREFIX_ROUTES`)
|
1. [#3284](https://github.com/influxdata/chronograf/pull/3284): Fix logout when using basepath & simplify basepath usage (deprecates `PREFIX_ROUTES`)
|
||||||
1. [#3349](https://github.com/influxdata/chronograf/pull/3349): Fix graphs in alert rule builder for queries that include groupby
|
1. [#3349](https://github.com/influxdata/chronograf/pull/3349): Fix graphs in alert rule builder for queries that include groupby
|
||||||
|
1. [#3345](https://github.com/influxdata/chronograf/pull/3345): Fix auto not showing in the group by dropdown and explorer getting disconnected
|
||||||
|
|
||||||
## v1.4.4.1 [2018-04-16]
|
## v1.4.4.1 [2018-04-16]
|
||||||
|
|
||||||
|
|
|
@ -63,3 +63,49 @@ export const updateKapacitorBody = {
|
||||||
proxy: '/chronograf/v1/sources/47/kapacitors/1/proxy',
|
proxy: '/chronograf/v1/sources/47/kapacitors/1/proxy',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const queryConfig = {
|
||||||
|
queries: [
|
||||||
|
{
|
||||||
|
id: '60842c85-8bc7-4180-a844-b974e47a98cd',
|
||||||
|
query:
|
||||||
|
'SELECT mean(:fields:), mean("usage_user") AS "mean_usage_user" FROM "telegraf"."autogen"."cpu" WHERE time > :dashboardTime: GROUP BY time(:interval:) FILL(null)',
|
||||||
|
queryConfig: {
|
||||||
|
id: '60842c85-8bc7-4180-a844-b974e47a98cd',
|
||||||
|
database: 'telegraf',
|
||||||
|
measurement: 'cpu',
|
||||||
|
retentionPolicy: 'autogen',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
value: 'mean',
|
||||||
|
type: 'func',
|
||||||
|
alias: '',
|
||||||
|
args: [{value: 'usage_idle', type: 'field', alias: ''}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'mean',
|
||||||
|
type: 'func',
|
||||||
|
alias: 'mean_usage_user',
|
||||||
|
args: [{value: 'usage_user', type: 'field', alias: ''}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: {},
|
||||||
|
groupBy: {time: 'auto', tags: []},
|
||||||
|
areTagsAccepted: false,
|
||||||
|
fill: 'null',
|
||||||
|
rawText:
|
||||||
|
'SELECT mean(:fields:), mean("usage_user") AS "mean_usage_user" FROM "telegraf"."autogen"."cpu" WHERE time > :dashboardTime: GROUP BY time(:interval:) FILL(null)',
|
||||||
|
range: null,
|
||||||
|
shifts: [],
|
||||||
|
},
|
||||||
|
queryTemplated:
|
||||||
|
'SELECT mean("usage_idle"), mean("usage_user") AS "mean_usage_user" FROM "telegraf"."autogen"."cpu" WHERE time > :dashboardTime: GROUP BY time(:interval:) FILL(null)',
|
||||||
|
tempVars: [
|
||||||
|
{
|
||||||
|
tempVar: ':fields:',
|
||||||
|
values: [{value: 'usage_idle', type: 'fieldKey', selected: true}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import {kapacitor} from 'mocks/dummy'
|
import {kapacitor, queryConfig} from 'mocks/dummy'
|
||||||
|
|
||||||
export const getKapacitor = jest.fn(() => Promise.resolve(kapacitor))
|
export const getKapacitor = jest.fn(() => Promise.resolve(kapacitor))
|
||||||
export const getActiveKapacitor = jest.fn(() => Promise.resolve(kapacitor))
|
export const getActiveKapacitor = jest.fn(() => Promise.resolve(kapacitor))
|
||||||
export const createKapacitor = jest.fn(() => Promise.resolve({data: kapacitor}))
|
export const createKapacitor = jest.fn(() => Promise.resolve({data: kapacitor}))
|
||||||
export const updateKapacitor = jest.fn(() => Promise.resolve({data: kapacitor}))
|
export const updateKapacitor = jest.fn(() => Promise.resolve({data: kapacitor}))
|
||||||
export const pingKapacitor = jest.fn(() => Promise.resolve())
|
export const pingKapacitor = jest.fn(() => Promise.resolve())
|
||||||
|
export const getQueryConfigAndStatus = jest.fn(() =>
|
||||||
|
Promise.resolve({data: queryConfig})
|
||||||
|
)
|
||||||
|
|
|
@ -3,9 +3,11 @@ import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import SideNav from 'src/side_nav'
|
import SideNav from 'src/side_nav'
|
||||||
import Notifications from 'shared/components/Notifications'
|
import Notifications from 'shared/components/Notifications'
|
||||||
|
import Overlay from 'shared/components/OverlayTechnology'
|
||||||
|
|
||||||
const App = ({children}) => (
|
const App = ({children}) => (
|
||||||
<div className="chronograf-root">
|
<div className="chronograf-root">
|
||||||
|
<Overlay />
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<SideNav />
|
<SideNav />
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -51,6 +51,8 @@ interface Props {
|
||||||
errorThrown: () => void
|
errorThrown: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SourceContext = React.createContext()
|
||||||
|
|
||||||
// Acts as a 'router middleware'. The main `App` component is responsible for
|
// Acts as a 'router middleware'. The main `App` component is responsible for
|
||||||
// getting the list of data sources, but not every page requires them to function.
|
// getting the list of data sources, but not every page requires them to function.
|
||||||
// Routes that do require data sources can be nested under this component.
|
// Routes that do require data sources can be nested under this component.
|
||||||
|
@ -205,8 +207,10 @@ export class CheckSources extends Component<Props, State> {
|
||||||
// TODO: guard against invalid resource access
|
// TODO: guard against invalid resource access
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.props.children &&
|
<SourceContext.Provider value={source}>
|
||||||
React.cloneElement(this.props.children, {...this.props, source})
|
{this.props.children &&
|
||||||
|
React.cloneElement(this.props.children, {...this.props, source})}
|
||||||
|
</SourceContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CEOBottom: SFC<Props> = ({children}) => (
|
const CEOBottom: SFC<Props> = ({children}) => (
|
||||||
<div className="overlay-technology--editor">{children}</div>
|
<div className="ceo--editor">{children}</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default CEOBottom
|
export default CEOBottom
|
||||||
|
|
|
@ -3,6 +3,11 @@ import React, {Component} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import uuid from 'uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CellEditorOverlayActions,
|
||||||
|
CellEditorOverlayActionsFunc,
|
||||||
|
} from 'src/types/dashboard'
|
||||||
|
|
||||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||||
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
||||||
import Visualization from 'src/dashboards/components/Visualization'
|
import Visualization from 'src/dashboards/components/Visualization'
|
||||||
|
@ -14,7 +19,7 @@ import * as queryModifiers from 'src/utils/queryTransitions'
|
||||||
|
|
||||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||||
import {buildQuery} from 'src/utils/influxql'
|
import {buildQuery} from 'src/utils/influxql'
|
||||||
import {getQueryConfig} from 'src/shared/apis'
|
import {getQueryConfigAndStatus} from 'src/shared/apis'
|
||||||
import {IS_STATIC_LEGEND} from 'src/shared/constants'
|
import {IS_STATIC_LEGEND} from 'src/shared/constants'
|
||||||
import {ColorString, ColorNumber} from 'src/types/colors'
|
import {ColorString, ColorNumber} from 'src/types/colors'
|
||||||
import {nextSource} from 'src/dashboards/utils/sources'
|
import {nextSource} from 'src/dashboards/utils/sources'
|
||||||
|
@ -25,11 +30,21 @@ import {
|
||||||
} from 'src/dashboards/constants'
|
} from 'src/dashboards/constants'
|
||||||
import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
|
import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
|
||||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||||
import {AUTO_GROUP_BY} from 'src/shared/constants'
|
import {
|
||||||
|
AUTO_GROUP_BY,
|
||||||
|
PREDEFINED_TEMP_VARS,
|
||||||
|
TEMP_VAR_DASHBOARD_TIME,
|
||||||
|
} from 'src/shared/constants'
|
||||||
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
||||||
import {TimeRange, Source, Query} from 'src/types'
|
import {
|
||||||
import {Status} from 'src/types/query'
|
TimeRange,
|
||||||
import {Cell, CellQuery, Legend} from 'src/types/dashboard'
|
Source,
|
||||||
|
QueryConfig,
|
||||||
|
Cell,
|
||||||
|
CellQuery,
|
||||||
|
Legend,
|
||||||
|
Status,
|
||||||
|
} from 'src/types'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
const staticLegend: Legend = {
|
const staticLegend: Legend = {
|
||||||
|
@ -65,15 +80,15 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
queriesWorkingDraft: Query[]
|
queriesWorkingDraft: QueryConfig[]
|
||||||
activeQueryIndex: number
|
activeQueryIndex: number
|
||||||
isDisplayOptionsTabActive: boolean
|
isDisplayOptionsTabActive: boolean
|
||||||
isStaticLegend: boolean
|
isStaticLegend: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const createWorkingDraft = (source: string, query: CellQuery): Query => {
|
const createWorkingDraft = (source: string, query: CellQuery): QueryConfig => {
|
||||||
const {queryConfig} = query
|
const {queryConfig} = query
|
||||||
const draft: Query = {
|
const draft: QueryConfig = {
|
||||||
...queryConfig,
|
...queryConfig,
|
||||||
id: uuid.v4(),
|
id: uuid.v4(),
|
||||||
source,
|
source,
|
||||||
|
@ -84,8 +99,8 @@ const createWorkingDraft = (source: string, query: CellQuery): Query => {
|
||||||
|
|
||||||
const createWorkingDrafts = (
|
const createWorkingDrafts = (
|
||||||
source: string,
|
source: string,
|
||||||
queries: CellQuery[] = []
|
queries: CellQuery[]
|
||||||
): Query[] =>
|
): QueryConfig[] =>
|
||||||
_.cloneDeep(
|
_.cloneDeep(
|
||||||
queries.map((query: CellQuery) => createWorkingDraft(source, query))
|
queries.map((query: CellQuery) => createWorkingDraft(source, query))
|
||||||
)
|
)
|
||||||
|
@ -142,8 +157,10 @@ class CellEditorOverlay extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
if (this.overlayRef) {
|
||||||
this.overlayRef.focus()
|
this.overlayRef.focus()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
|
@ -228,7 +245,10 @@ class CellEditorOverlay extends Component<Props, State> {
|
||||||
this.overlayRef = r
|
this.overlayRef = r
|
||||||
}
|
}
|
||||||
|
|
||||||
private queryStateReducer = queryModifier => (queryID, ...payload) => {
|
private queryStateReducer = (queryModifier): CellEditorOverlayActionsFunc => (
|
||||||
|
queryID: string,
|
||||||
|
...payload: any[]
|
||||||
|
) => {
|
||||||
const {queriesWorkingDraft} = this.state
|
const {queriesWorkingDraft} = this.state
|
||||||
const query = queriesWorkingDraft.find(q => q.id === queryID)
|
const query = queriesWorkingDraft.find(q => q.id === queryID)
|
||||||
|
|
||||||
|
@ -270,7 +290,7 @@ class CellEditorOverlay extends Component<Props, State> {
|
||||||
const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
|
const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
|
||||||
|
|
||||||
const queries = queriesWorkingDraft.map(q => {
|
const queries = queriesWorkingDraft.map(q => {
|
||||||
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
|
const timeRange = q.range || {upper: null, lower: TEMP_VAR_DASHBOARD_TIME}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
queryConfig: q,
|
queryConfig: q,
|
||||||
|
@ -317,20 +337,91 @@ class CellEditorOverlay extends Component<Props, State> {
|
||||||
|
|
||||||
private getActiveQuery = () => {
|
private getActiveQuery = () => {
|
||||||
const {queriesWorkingDraft, activeQueryIndex} = this.state
|
const {queriesWorkingDraft, activeQueryIndex} = this.state
|
||||||
|
const activeQuery = _.get(
|
||||||
|
queriesWorkingDraft,
|
||||||
|
activeQueryIndex,
|
||||||
|
queriesWorkingDraft[0]
|
||||||
|
)
|
||||||
|
|
||||||
return _.get(queriesWorkingDraft, activeQueryIndex, queriesWorkingDraft[0])
|
const queryText = _.get(activeQuery, 'rawText', '')
|
||||||
|
const userDefinedTempVarsInQuery = this.findUserDefinedTempVarsInQuery(
|
||||||
|
queryText,
|
||||||
|
this.props.templates
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!!userDefinedTempVarsInQuery.length) {
|
||||||
|
activeQuery.isQuerySupportedByExplorer = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleEditRawText = async (url, id, text) => {
|
return activeQuery
|
||||||
const templates = removeUnselectedTemplateValues(this.props.templates)
|
}
|
||||||
|
|
||||||
// use this as the handler passed into fetchTimeSeries to update a query status
|
private findUserDefinedTempVarsInQuery = (
|
||||||
try {
|
query: string,
|
||||||
const {data} = await getQueryConfig(url, [{query: text, id}], templates)
|
templates: Template[]
|
||||||
const config = data.queries.find(q => q.id === id)
|
): Template[] => {
|
||||||
const nextQueries = this.state.queriesWorkingDraft.map(
|
return templates.filter((temp: Template) => {
|
||||||
q => (q.id === id ? {...config.queryConfig, source: q.source} : q)
|
if (!query) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const isPredefinedTempVar: boolean = !!PREDEFINED_TEMP_VARS.find(
|
||||||
|
t => t === temp.tempVar
|
||||||
)
|
)
|
||||||
|
if (!isPredefinedTempVar) {
|
||||||
|
return query.includes(temp.tempVar)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// The schema explorer is not built to handle user defined template variables
|
||||||
|
// in the query in a clear manner. If they are being used, we indicate that in
|
||||||
|
// the query config in order to disable the fields column down stream because
|
||||||
|
// at this point the query string is disconnected from the schema explorer.
|
||||||
|
private handleEditRawText = async (
|
||||||
|
url: string,
|
||||||
|
id: string,
|
||||||
|
text: string
|
||||||
|
): Promise<void> => {
|
||||||
|
const userDefinedTempVarsInQuery = this.findUserDefinedTempVarsInQuery(
|
||||||
|
text,
|
||||||
|
this.props.templates
|
||||||
|
)
|
||||||
|
|
||||||
|
const isUsingUserDefinedTempVars: boolean = !!userDefinedTempVarsInQuery.length
|
||||||
|
|
||||||
|
try {
|
||||||
|
const selectedTempVars: Template[] = isUsingUserDefinedTempVars
|
||||||
|
? removeUnselectedTemplateValues(userDefinedTempVarsInQuery)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const {data} = await getQueryConfigAndStatus(
|
||||||
|
url,
|
||||||
|
[{query: text, id}],
|
||||||
|
selectedTempVars
|
||||||
|
)
|
||||||
|
|
||||||
|
const config = data.queries.find(q => q.id === id)
|
||||||
|
const nextQueries: QueryConfig[] = this.state.queriesWorkingDraft.map(
|
||||||
|
(q: QueryConfig) => {
|
||||||
|
if (q.id === id) {
|
||||||
|
const isQuerySupportedByExplorer = !isUsingUserDefinedTempVars
|
||||||
|
|
||||||
|
if (isUsingUserDefinedTempVars) {
|
||||||
|
return {...q, rawText: text, isQuerySupportedByExplorer}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config.queryConfig,
|
||||||
|
source: q.source,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
this.setState({queriesWorkingDraft: nextQueries})
|
this.setState({queriesWorkingDraft: nextQueries})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
@ -389,17 +480,32 @@ class CellEditorOverlay extends Component<Props, State> {
|
||||||
const {queriesWorkingDraft} = this.state
|
const {queriesWorkingDraft} = this.state
|
||||||
|
|
||||||
return queriesWorkingDraft.every(
|
return queriesWorkingDraft.every(
|
||||||
(query: Query) =>
|
(query: QueryConfig) =>
|
||||||
(!!query.measurement && !!query.database && !!query.fields.length) ||
|
(!!query.measurement && !!query.database && !!query.fields.length) ||
|
||||||
!!query.rawText
|
!!query.rawText
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get queryActions() {
|
private get queryActions(): CellEditorOverlayActions {
|
||||||
return {
|
const original = {
|
||||||
editRawTextAsync: this.handleEditRawText,
|
editRawTextAsync: () => Promise.resolve(),
|
||||||
..._.mapValues(queryModifiers, this.queryStateReducer),
|
...queryModifiers,
|
||||||
}
|
}
|
||||||
|
const mapped = _.reduce<CellEditorOverlayActions, CellEditorOverlayActions>(
|
||||||
|
original,
|
||||||
|
(acc, v, k) => {
|
||||||
|
acc[k] = this.queryStateReducer(v)
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
original
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
...mapped,
|
||||||
|
editRawTextAsync: this.handleEditRawText,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private get sourceLink(): string {
|
private get sourceLink(): string {
|
||||||
|
|
|
@ -42,16 +42,14 @@ class MeasurementDropdown extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getMeasurements = async () => {
|
_getMeasurements = async () => {
|
||||||
const {
|
|
||||||
source: {
|
|
||||||
links: {proxy},
|
|
||||||
},
|
|
||||||
} = this.context
|
|
||||||
const {
|
const {
|
||||||
measurement,
|
measurement,
|
||||||
database,
|
database,
|
||||||
onSelectMeasurement,
|
onSelectMeasurement,
|
||||||
onErrorThrown,
|
onErrorThrown,
|
||||||
|
source: {
|
||||||
|
links: {proxy},
|
||||||
|
},
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -72,7 +70,12 @@ class MeasurementDropdown extends Component {
|
||||||
|
|
||||||
const {func, shape, string} = PropTypes
|
const {func, shape, string} = PropTypes
|
||||||
|
|
||||||
MeasurementDropdown.contextTypes = {
|
MeasurementDropdown.propTypes = {
|
||||||
|
database: string.isRequired,
|
||||||
|
measurement: string,
|
||||||
|
onSelectMeasurement: func.isRequired,
|
||||||
|
onStartEdit: func.isRequired,
|
||||||
|
onErrorThrown: func.isRequired,
|
||||||
source: shape({
|
source: shape({
|
||||||
links: shape({
|
links: shape({
|
||||||
proxy: string.isRequired,
|
proxy: string.isRequired,
|
||||||
|
@ -80,12 +83,4 @@ MeasurementDropdown.contextTypes = {
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
MeasurementDropdown.propTypes = {
|
|
||||||
database: string.isRequired,
|
|
||||||
measurement: string,
|
|
||||||
onSelectMeasurement: func.isRequired,
|
|
||||||
onStartEdit: func.isRequired,
|
|
||||||
onErrorThrown: func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MeasurementDropdown
|
export default MeasurementDropdown
|
||||||
|
|
|
@ -1,20 +1,44 @@
|
||||||
import React from 'react'
|
import React, {SFC} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import {QueryConfig, Source, SourceLinks, TimeRange} from 'src/types'
|
||||||
|
import {CellEditorOverlayActions} from 'src/types/dashboard'
|
||||||
|
|
||||||
import EmptyQuery from 'src/shared/components/EmptyQuery'
|
import EmptyQuery from 'src/shared/components/EmptyQuery'
|
||||||
import QueryTabList from 'src/shared/components/QueryTabList'
|
import QueryTabList from 'src/shared/components/QueryTabList'
|
||||||
import QueryTextArea from 'src/dashboards/components/QueryTextArea'
|
import QueryTextArea from 'src/dashboards/components/QueryTextArea'
|
||||||
import SchemaExplorer from 'src/shared/components/SchemaExplorer'
|
import SchemaExplorer from 'src/shared/components/SchemaExplorer'
|
||||||
import {buildQuery} from 'utils/influxql'
|
import {buildQuery} from 'src/utils/influxql'
|
||||||
import {TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
import {TYPE_QUERY_CONFIG, TEMPLATE_RANGE} from 'src/dashboards/constants'
|
||||||
|
|
||||||
const TEMPLATE_RANGE = {upper: null, lower: ':dashboardTime:'}
|
const rawTextBinder = (
|
||||||
const rawTextBinder = (links, id, action) => text =>
|
links: SourceLinks,
|
||||||
action(links.queries, id, text)
|
id: string,
|
||||||
const buildText = q =>
|
action: (linksQueries: string, id: string, text: string) => void
|
||||||
|
) => (text: string) => action(links.queries, id, text)
|
||||||
|
|
||||||
|
const buildText = (q: QueryConfig): string =>
|
||||||
q.rawText || buildQuery(TYPE_QUERY_CONFIG, q.range || TEMPLATE_RANGE, q) || ''
|
q.rawText || buildQuery(TYPE_QUERY_CONFIG, q.range || TEMPLATE_RANGE, q) || ''
|
||||||
|
|
||||||
const QueryMaker = ({
|
interface Template {
|
||||||
|
tempVar: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
source: Source
|
||||||
|
queries: QueryConfig[]
|
||||||
|
timeRange: TimeRange
|
||||||
|
actions: CellEditorOverlayActions
|
||||||
|
setActiveQueryIndex: (index: number) => void
|
||||||
|
onDeleteQuery: (index: number) => void
|
||||||
|
activeQueryIndex: number
|
||||||
|
activeQuery: QueryConfig
|
||||||
|
onAddQuery: () => void
|
||||||
|
templates: Template[]
|
||||||
|
initialGroupByTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const QueryMaker: SFC<Props> = ({
|
||||||
source,
|
source,
|
||||||
actions,
|
actions,
|
||||||
queries,
|
queries,
|
||||||
|
@ -52,8 +76,12 @@ const QueryMaker = ({
|
||||||
source={source}
|
source={source}
|
||||||
actions={actions}
|
actions={actions}
|
||||||
query={activeQuery}
|
query={activeQuery}
|
||||||
onAddQuery={onAddQuery}
|
|
||||||
initialGroupByTime={initialGroupByTime}
|
initialGroupByTime={initialGroupByTime}
|
||||||
|
isQuerySupportedByExplorer={_.get(
|
||||||
|
activeQuery,
|
||||||
|
'isQuerySupportedByExplorer',
|
||||||
|
true
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
@ -62,43 +90,4 @@ const QueryMaker = ({
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {arrayOf, func, number, shape, string} = PropTypes
|
|
||||||
|
|
||||||
QueryMaker.propTypes = {
|
|
||||||
source: shape({
|
|
||||||
links: shape({
|
|
||||||
queries: string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
queries: arrayOf(shape({})).isRequired,
|
|
||||||
timeRange: shape({
|
|
||||||
upper: string,
|
|
||||||
lower: string,
|
|
||||||
}).isRequired,
|
|
||||||
actions: shape({
|
|
||||||
chooseNamespace: func.isRequired,
|
|
||||||
chooseMeasurement: func.isRequired,
|
|
||||||
chooseTag: func.isRequired,
|
|
||||||
groupByTag: func.isRequired,
|
|
||||||
toggleField: func.isRequired,
|
|
||||||
groupByTime: func.isRequired,
|
|
||||||
toggleTagAcceptance: func.isRequired,
|
|
||||||
fill: func,
|
|
||||||
applyFuncsToField: func.isRequired,
|
|
||||||
editRawTextAsync: func.isRequired,
|
|
||||||
addInitialField: func.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
setActiveQueryIndex: func.isRequired,
|
|
||||||
onDeleteQuery: func.isRequired,
|
|
||||||
activeQueryIndex: number,
|
|
||||||
activeQuery: shape({}),
|
|
||||||
onAddQuery: func.isRequired,
|
|
||||||
templates: arrayOf(
|
|
||||||
shape({
|
|
||||||
tempVar: string.isRequired,
|
|
||||||
})
|
|
||||||
).isRequired,
|
|
||||||
initialGroupByTime: string.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default QueryMaker
|
export default QueryMaker
|
|
@ -42,7 +42,7 @@ const TemplateVariableManager = ({
|
||||||
>
|
>
|
||||||
Save Changes
|
Save Changes
|
||||||
</button>
|
</button>
|
||||||
<span className="page-header__dismiss" onClick={onClose(isEdited)} />
|
<span className="page-header__dismiss" onClick={onClose} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="template-variable-manager--body">
|
<div className="template-variable-manager--body">
|
||||||
|
@ -175,16 +175,29 @@ class TemplateVariableManagerWrapper extends Component {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDismissManager = () => {
|
||||||
|
const {onDismissOverlay} = this.props
|
||||||
|
const {isEdited} = this.state
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isEdited ||
|
||||||
|
(isEdited && confirm('Do you want to close without saving?')) // eslint-disable-line no-alert
|
||||||
|
) {
|
||||||
|
onDismissOverlay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {rows, isEdited} = this.state
|
const {rows, isEdited} = this.state
|
||||||
return (
|
return (
|
||||||
<TemplateVariableManager
|
<TemplateVariableManager
|
||||||
{...this.props}
|
{...this.props}
|
||||||
|
templates={rows}
|
||||||
|
isEdited={isEdited}
|
||||||
|
onClose={this.handleDismissManager}
|
||||||
onRunQuerySuccess={this.onRunQuerySuccess}
|
onRunQuerySuccess={this.onRunQuerySuccess}
|
||||||
onSaveTemplatesSuccess={this.onSaveTemplatesSuccess}
|
onSaveTemplatesSuccess={this.onSaveTemplatesSuccess}
|
||||||
onAddVariable={this.onAddVariable}
|
onAddVariable={this.onAddVariable}
|
||||||
templates={rows}
|
|
||||||
isEdited={isEdited}
|
|
||||||
onDelete={this.onDeleteTemplateVariable}
|
onDelete={this.onDeleteTemplateVariable}
|
||||||
tempVarAlreadyExists={this.tempVarAlreadyExists}
|
tempVarAlreadyExists={this.tempVarAlreadyExists}
|
||||||
/>
|
/>
|
||||||
|
@ -204,13 +217,7 @@ TemplateVariableManager.propTypes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplateVariableManagerWrapper.propTypes = {
|
TemplateVariableManagerWrapper.propTypes = {
|
||||||
onClose: func.isRequired,
|
|
||||||
onEditTemplateVariables: func.isRequired,
|
onEditTemplateVariables: func.isRequired,
|
||||||
source: shape({
|
|
||||||
links: shape({
|
|
||||||
proxy: string,
|
|
||||||
}),
|
|
||||||
}).isRequired,
|
|
||||||
templates: arrayOf(
|
templates: arrayOf(
|
||||||
shape({
|
shape({
|
||||||
type: string.isRequired,
|
type: string.isRequired,
|
||||||
|
@ -229,6 +236,7 @@ TemplateVariableManagerWrapper.propTypes = {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
onRunQueryFailure: func.isRequired,
|
onRunQueryFailure: func.isRequired,
|
||||||
|
onDismissOverlay: func,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TemplateVariableManagerWrapper
|
export default TemplateVariableManagerWrapper
|
||||||
|
|
|
@ -46,6 +46,7 @@ const TemplateVariableRow = ({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onErrorThrown,
|
onErrorThrown,
|
||||||
onDeleteTempVar,
|
onDeleteTempVar,
|
||||||
|
source,
|
||||||
}) => (
|
}) => (
|
||||||
<form
|
<form
|
||||||
className={classnames('template-variable-manager--table-row', {
|
className={classnames('template-variable-manager--table-row', {
|
||||||
|
@ -78,6 +79,7 @@ const TemplateVariableRow = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="tvm--col-3">
|
<div className="tvm--col-3">
|
||||||
<TemplateQueryBuilder
|
<TemplateQueryBuilder
|
||||||
|
source={source}
|
||||||
onSelectDatabase={onSelectDatabase}
|
onSelectDatabase={onSelectDatabase}
|
||||||
selectedType={selectedType}
|
selectedType={selectedType}
|
||||||
selectedDatabase={selectedDatabase}
|
selectedDatabase={selectedDatabase}
|
||||||
|
|
|
@ -14,6 +14,7 @@ const TemplateQueryBuilder = ({
|
||||||
onSelectTagKey,
|
onSelectTagKey,
|
||||||
onStartEdit,
|
onStartEdit,
|
||||||
onErrorThrown,
|
onErrorThrown,
|
||||||
|
source,
|
||||||
}) => {
|
}) => {
|
||||||
switch (selectedType) {
|
switch (selectedType) {
|
||||||
case 'csv':
|
case 'csv':
|
||||||
|
@ -25,6 +26,7 @@ const TemplateQueryBuilder = ({
|
||||||
<div className="tvm-query-builder">
|
<div className="tvm-query-builder">
|
||||||
<span className="tvm-query-builder--text">SHOW MEASUREMENTS ON</span>
|
<span className="tvm-query-builder--text">SHOW MEASUREMENTS ON</span>
|
||||||
<DatabaseDropdown
|
<DatabaseDropdown
|
||||||
|
source={source}
|
||||||
onSelectDatabase={onSelectDatabase}
|
onSelectDatabase={onSelectDatabase}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
onStartEdit={onStartEdit}
|
onStartEdit={onStartEdit}
|
||||||
|
@ -40,6 +42,7 @@ const TemplateQueryBuilder = ({
|
||||||
SHOW {selectedType === 'fieldKeys' ? 'FIELD' : 'TAG'} KEYS ON
|
SHOW {selectedType === 'fieldKeys' ? 'FIELD' : 'TAG'} KEYS ON
|
||||||
</span>
|
</span>
|
||||||
<DatabaseDropdown
|
<DatabaseDropdown
|
||||||
|
source={source}
|
||||||
onSelectDatabase={onSelectDatabase}
|
onSelectDatabase={onSelectDatabase}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
onStartEdit={onStartEdit}
|
onStartEdit={onStartEdit}
|
||||||
|
@ -48,6 +51,7 @@ const TemplateQueryBuilder = ({
|
||||||
<span className="tvm-query-builder--text">FROM</span>
|
<span className="tvm-query-builder--text">FROM</span>
|
||||||
{selectedDatabase ? (
|
{selectedDatabase ? (
|
||||||
<MeasurementDropdown
|
<MeasurementDropdown
|
||||||
|
source={source}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
measurement={selectedMeasurement}
|
measurement={selectedMeasurement}
|
||||||
onSelectMeasurement={onSelectMeasurement}
|
onSelectMeasurement={onSelectMeasurement}
|
||||||
|
@ -64,6 +68,7 @@ const TemplateQueryBuilder = ({
|
||||||
<div className="tvm-query-builder">
|
<div className="tvm-query-builder">
|
||||||
<span className="tvm-query-builder--text">SHOW TAG VALUES ON</span>
|
<span className="tvm-query-builder--text">SHOW TAG VALUES ON</span>
|
||||||
<DatabaseDropdown
|
<DatabaseDropdown
|
||||||
|
source={source}
|
||||||
onSelectDatabase={onSelectDatabase}
|
onSelectDatabase={onSelectDatabase}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
onStartEdit={onStartEdit}
|
onStartEdit={onStartEdit}
|
||||||
|
@ -72,6 +77,7 @@ const TemplateQueryBuilder = ({
|
||||||
<span className="tvm-query-builder--text">FROM</span>
|
<span className="tvm-query-builder--text">FROM</span>
|
||||||
{selectedDatabase ? (
|
{selectedDatabase ? (
|
||||||
<MeasurementDropdown
|
<MeasurementDropdown
|
||||||
|
source={source}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
measurement={selectedMeasurement}
|
measurement={selectedMeasurement}
|
||||||
onSelectMeasurement={onSelectMeasurement}
|
onSelectMeasurement={onSelectMeasurement}
|
||||||
|
@ -105,7 +111,7 @@ const TemplateQueryBuilder = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {func, string} = PropTypes
|
const {func, shape, string} = PropTypes
|
||||||
|
|
||||||
TemplateQueryBuilder.propTypes = {
|
TemplateQueryBuilder.propTypes = {
|
||||||
selectedType: string.isRequired,
|
selectedType: string.isRequired,
|
||||||
|
@ -117,6 +123,11 @@ TemplateQueryBuilder.propTypes = {
|
||||||
selectedDatabase: string,
|
selectedDatabase: string,
|
||||||
selectedTagKey: string,
|
selectedTagKey: string,
|
||||||
onErrorThrown: func.isRequired,
|
onErrorThrown: func.isRequired,
|
||||||
|
source: shape({
|
||||||
|
links: shape({
|
||||||
|
proxy: string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
}).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TemplateQueryBuilder
|
export default TemplateQueryBuilder
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
DEFAULT_FIX_FIRST_COLUMN,
|
DEFAULT_FIX_FIRST_COLUMN,
|
||||||
} from 'src/shared/constants/tableGraph'
|
} from 'src/shared/constants/tableGraph'
|
||||||
import {CELL_TYPE_LINE} from 'src/dashboards/graphics/graph'
|
import {CELL_TYPE_LINE} from 'src/dashboards/graphics/graph'
|
||||||
|
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
|
||||||
|
|
||||||
export const UNTITLED_CELL_LINE = 'Untitled Line Graph'
|
export const UNTITLED_CELL_LINE = 'Untitled Line Graph'
|
||||||
export const UNTITLED_CELL_STACKED = 'Untitled Stacked Gracph'
|
export const UNTITLED_CELL_STACKED = 'Untitled Stacked Gracph'
|
||||||
|
@ -151,3 +152,4 @@ export const TYPE_QUERY_CONFIG = 'queryConfig'
|
||||||
export const TYPE_SHIFTED = 'shifted queryConfig'
|
export const TYPE_SHIFTED = 'shifted queryConfig'
|
||||||
export const TYPE_IFQL = 'ifql'
|
export const TYPE_IFQL = 'ifql'
|
||||||
export const DASHBOARD_NAME_MAX_LENGTH = 50
|
export const DASHBOARD_NAME_MAX_LENGTH = 50
|
||||||
|
export const TEMPLATE_RANGE = {upper: null, lower: TEMP_VAR_DASHBOARD_TIME}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import _ from 'lodash'
|
||||||
|
|
||||||
import {isUserAuthorized, EDITOR_ROLE} from 'src/auth/Authorized'
|
import {isUserAuthorized, EDITOR_ROLE} from 'src/auth/Authorized'
|
||||||
|
|
||||||
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
|
||||||
import CellEditorOverlay from 'src/dashboards/components/CellEditorOverlay'
|
import CellEditorOverlay from 'src/dashboards/components/CellEditorOverlay'
|
||||||
import DashboardHeader from 'src/dashboards/components/DashboardHeader'
|
import DashboardHeader from 'src/dashboards/components/DashboardHeader'
|
||||||
import Dashboard from 'src/dashboards/components/Dashboard'
|
import Dashboard from 'src/dashboards/components/Dashboard'
|
||||||
|
@ -28,6 +27,7 @@ import {
|
||||||
showCellEditorOverlay,
|
showCellEditorOverlay,
|
||||||
hideCellEditorOverlay,
|
hideCellEditorOverlay,
|
||||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||||
|
import {showOverlay} from 'src/shared/actions/overlayTechnology'
|
||||||
|
|
||||||
import {dismissEditingAnnotation} from 'src/shared/actions/annotations'
|
import {dismissEditingAnnotation} from 'src/shared/actions/annotations'
|
||||||
|
|
||||||
|
@ -36,10 +36,16 @@ import {
|
||||||
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
||||||
} from 'shared/actions/app'
|
} from 'shared/actions/app'
|
||||||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
||||||
import {interval, DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
import {
|
||||||
|
interval,
|
||||||
|
DASHBOARD_LAYOUT_ROW_HEIGHT,
|
||||||
|
TEMP_VAR_DASHBOARD_TIME,
|
||||||
|
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
|
} from 'shared/constants'
|
||||||
import {notifyDashboardNotFound} from 'shared/copy/notifications'
|
import {notifyDashboardNotFound} from 'shared/copy/notifications'
|
||||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import {OverlayContext} from 'src/shared/components/OverlayTechnology'
|
||||||
|
|
||||||
const FORMAT_INFLUXQL = 'influxql'
|
const FORMAT_INFLUXQL = 'influxql'
|
||||||
const defaultTimeRange = {
|
const defaultTimeRange = {
|
||||||
|
@ -57,7 +63,6 @@ class DashboardPage extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
isEditMode: false,
|
isEditMode: false,
|
||||||
selectedCell: null,
|
selectedCell: null,
|
||||||
isTemplating: false,
|
|
||||||
zoomedTimeRange: {zoomedLower: null, zoomedUpper: null},
|
zoomedTimeRange: {zoomedLower: null, zoomedUpper: null},
|
||||||
scrollTop: 0,
|
scrollTop: 0,
|
||||||
windowHeight: window.innerHeight,
|
windowHeight: window.innerHeight,
|
||||||
|
@ -149,16 +154,28 @@ class DashboardPage extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpenTemplateManager = () => {
|
handleOpenTemplateManager = () => {
|
||||||
this.setState({isTemplating: true})
|
const {handleShowOverlay, dashboard, source} = this.props
|
||||||
|
const options = {
|
||||||
|
dismissOnClickOutside: false,
|
||||||
|
dismissOnEscape: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCloseTemplateManager = isEdited => () => {
|
handleShowOverlay(
|
||||||
if (
|
<OverlayContext.Consumer>
|
||||||
!isEdited ||
|
{({onDismissOverlay}) => {
|
||||||
(isEdited && confirm('Do you want to close without saving?')) // eslint-disable-line no-alert
|
return (
|
||||||
) {
|
<TemplateVariableManager
|
||||||
this.setState({isTemplating: false})
|
source={source}
|
||||||
}
|
templates={dashboard.templates}
|
||||||
|
onDismissOverlay={onDismissOverlay}
|
||||||
|
onRunQueryFailure={this.handleRunQueryFailure}
|
||||||
|
onEditTemplateVariables={this.handleEditTemplateVariables}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</OverlayContext.Consumer>,
|
||||||
|
options
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSaveEditedCell = newCell => {
|
handleSaveEditedCell = newCell => {
|
||||||
|
@ -321,7 +338,7 @@ class DashboardPage extends Component {
|
||||||
|
|
||||||
const dashboardTime = {
|
const dashboardTime = {
|
||||||
id: 'dashtime',
|
id: 'dashtime',
|
||||||
tempVar: ':dashboardTime:',
|
tempVar: TEMP_VAR_DASHBOARD_TIME,
|
||||||
type: lowerType,
|
type: lowerType,
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
|
@ -334,7 +351,7 @@ class DashboardPage extends Component {
|
||||||
|
|
||||||
const upperDashboardTime = {
|
const upperDashboardTime = {
|
||||||
id: 'upperdashtime',
|
id: 'upperdashtime',
|
||||||
tempVar: ':upperDashboardTime:',
|
tempVar: TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
type: upperType,
|
type: upperType,
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
|
@ -357,7 +374,7 @@ class DashboardPage extends Component {
|
||||||
templatesIncludingDashTime = []
|
templatesIncludingDashTime = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const {isEditMode, isTemplating} = this.state
|
const {isEditMode} = this.state
|
||||||
|
|
||||||
const names = dashboards.map(d => ({
|
const names = dashboards.map(d => ({
|
||||||
name: d.name,
|
name: d.name,
|
||||||
|
@ -365,17 +382,6 @@ class DashboardPage extends Component {
|
||||||
}))
|
}))
|
||||||
return (
|
return (
|
||||||
<div className="page dashboard-page">
|
<div className="page dashboard-page">
|
||||||
{isTemplating ? (
|
|
||||||
<OverlayTechnologies>
|
|
||||||
<TemplateVariableManager
|
|
||||||
source={source}
|
|
||||||
templates={dashboard.templates}
|
|
||||||
onClose={this.handleCloseTemplateManager}
|
|
||||||
onRunQueryFailure={this.handleRunQueryFailure}
|
|
||||||
onEditTemplateVariables={this.handleEditTemplateVariables}
|
|
||||||
/>
|
|
||||||
</OverlayTechnologies>
|
|
||||||
) : null}
|
|
||||||
{selectedCell ? (
|
{selectedCell ? (
|
||||||
<CellEditorOverlay
|
<CellEditorOverlay
|
||||||
source={source}
|
source={source}
|
||||||
|
@ -534,6 +540,7 @@ DashboardPage.propTypes = {
|
||||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||||
gaugeColors: colorsNumberSchema.isRequired,
|
gaugeColors: colorsNumberSchema.isRequired,
|
||||||
lineColors: colorsStringSchema.isRequired,
|
lineColors: colorsStringSchema.isRequired,
|
||||||
|
handleShowOverlay: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, {params: {dashboardID}}) => {
|
const mapStateToProps = (state, {params: {dashboardID}}) => {
|
||||||
|
@ -613,6 +620,7 @@ const mapDispatchToProps = dispatch => ({
|
||||||
dismissEditingAnnotation,
|
dismissEditingAnnotation,
|
||||||
dispatch
|
dispatch
|
||||||
),
|
),
|
||||||
|
handleShowOverlay: bindActionCreators(showOverlay, dispatch),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import {Query} from 'src/types'
|
import {QueryConfig} from 'src/types'
|
||||||
|
|
||||||
export const nextSource = (prevQuery: Query, nextQuery: Query): string => {
|
export const nextSource = (
|
||||||
|
prevQuery: QueryConfig,
|
||||||
|
nextQuery: QueryConfig
|
||||||
|
): string => {
|
||||||
if (nextQuery.source) {
|
if (nextQuery.source) {
|
||||||
return nextQuery.source
|
return nextQuery.source
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import uuid from 'uuid'
|
import uuid from 'uuid'
|
||||||
|
|
||||||
import {getQueryConfig} from 'shared/apis'
|
import {getQueryConfigAndStatus} from 'shared/apis'
|
||||||
|
|
||||||
import {errorThrown} from 'shared/actions/errors'
|
import {errorThrown} from 'shared/actions/errors'
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ export const timeShift = (queryID, shift) => ({
|
||||||
// Async actions
|
// Async actions
|
||||||
export const editRawTextAsync = (url, id, text) => async dispatch => {
|
export const editRawTextAsync = (url, id, text) => async dispatch => {
|
||||||
try {
|
try {
|
||||||
const {data} = await getQueryConfig(url, [{query: text, id}])
|
const {data} = await getQueryConfigAndStatus(url, [{query: text, id}])
|
||||||
const config = data.queries.find(q => q.id === id)
|
const config = data.queries.find(q => q.id === id)
|
||||||
dispatch(updateQueryConfig(config.queryConfig))
|
dispatch(updateQueryConfig(config.queryConfig))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,14 +1,45 @@
|
||||||
import React, {Component} from 'react'
|
import React, {PureComponent, MouseEvent} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import FunctionSelector from 'shared/components/FunctionSelector'
|
import FunctionSelector from 'src/shared/components/FunctionSelector'
|
||||||
import {firstFieldName} from 'shared/reducers/helpers/fields'
|
import {firstFieldName} from 'src/shared/reducers/helpers/fields'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
interface Field {
|
||||||
|
type: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FuncArg {
|
||||||
|
value: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldFunc extends Field {
|
||||||
|
args: FuncArg[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApplyFuncsToFieldArgs {
|
||||||
|
field: Field
|
||||||
|
funcs: FuncArg[]
|
||||||
|
}
|
||||||
|
interface Props {
|
||||||
|
fieldFuncs: FieldFunc[]
|
||||||
|
isSelected: boolean
|
||||||
|
onToggleField: (field: Field) => void
|
||||||
|
onApplyFuncsToField: (args: ApplyFuncsToFieldArgs) => void
|
||||||
|
isKapacitorRule: boolean
|
||||||
|
funcs: string[]
|
||||||
|
isDisabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
isOpen: boolean
|
||||||
|
}
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class FieldListItem extends Component {
|
class FieldListItem extends PureComponent<Props, State> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -16,55 +47,10 @@ class FieldListItem extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFunctionsMenu = e => {
|
public render() {
|
||||||
if (e) {
|
const {isKapacitorRule, isSelected, funcs, isDisabled} = this.props
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
this.setState({isOpen: !this.state.isOpen})
|
|
||||||
}
|
|
||||||
|
|
||||||
close = () => {
|
|
||||||
this.setState({isOpen: false})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleToggleField = () => {
|
|
||||||
const {onToggleField} = this.props
|
|
||||||
const value = this._getFieldName()
|
|
||||||
|
|
||||||
onToggleField({value, type: 'field'})
|
|
||||||
this.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
handleApplyFunctions = selectedFuncs => {
|
|
||||||
const {onApplyFuncsToField} = this.props
|
|
||||||
const fieldName = this._getFieldName()
|
|
||||||
const field = {value: fieldName, type: 'field'}
|
|
||||||
|
|
||||||
onApplyFuncsToField({
|
|
||||||
field,
|
|
||||||
funcs: selectedFuncs.map(this._makeFunc),
|
|
||||||
})
|
|
||||||
this.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
_makeFunc = value => ({
|
|
||||||
value,
|
|
||||||
type: 'func',
|
|
||||||
})
|
|
||||||
|
|
||||||
_getFieldName = () => {
|
|
||||||
const {fieldFuncs} = this.props
|
|
||||||
const fieldFunc = _.head(fieldFuncs)
|
|
||||||
|
|
||||||
return _.get(fieldFunc, 'type') === 'field'
|
|
||||||
? _.get(fieldFunc, 'value')
|
|
||||||
: firstFieldName(_.get(fieldFunc, 'args'))
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {isKapacitorRule, isSelected, funcs} = this.props
|
|
||||||
const {isOpen} = this.state
|
const {isOpen} = this.state
|
||||||
const fieldName = this._getFieldName()
|
const fieldName = this.getFieldName()
|
||||||
|
|
||||||
let fieldFuncsLabel
|
let fieldFuncsLabel
|
||||||
const num = funcs.length
|
const num = funcs.length
|
||||||
|
@ -84,6 +70,7 @@ class FieldListItem extends Component {
|
||||||
<div
|
<div
|
||||||
className={classnames('query-builder--list-item', {
|
className={classnames('query-builder--list-item', {
|
||||||
active: isSelected,
|
active: isSelected,
|
||||||
|
disabled: isDisabled,
|
||||||
})}
|
})}
|
||||||
onClick={this.handleToggleField}
|
onClick={this.handleToggleField}
|
||||||
data-test={`query-builder-list-item-field-${fieldName}`}
|
data-test={`query-builder-list-item-field-${fieldName}`}
|
||||||
|
@ -98,6 +85,7 @@ class FieldListItem extends Component {
|
||||||
active: isOpen,
|
active: isOpen,
|
||||||
'btn-default': !num,
|
'btn-default': !num,
|
||||||
'btn-primary': num,
|
'btn-primary': num,
|
||||||
|
disabled: isDisabled,
|
||||||
})}
|
})}
|
||||||
onClick={this.toggleFunctionsMenu}
|
onClick={this.toggleFunctionsMenu}
|
||||||
data-test={`query-builder-list-item-function-${fieldName}`}
|
data-test={`query-builder-list-item-function-${fieldName}`}
|
||||||
|
@ -116,28 +104,54 @@ class FieldListItem extends Component {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleFunctionsMenu = (e: MouseEvent<HTMLElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
const {isDisabled} = this.props
|
||||||
|
if (isDisabled) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const {string, shape, func, arrayOf, bool} = PropTypes
|
this.setState({isOpen: !this.state.isOpen})
|
||||||
|
|
||||||
FieldListItem.propTypes = {
|
|
||||||
fieldFuncs: arrayOf(
|
|
||||||
shape({
|
|
||||||
type: string.isRequired,
|
|
||||||
value: string.isRequired,
|
|
||||||
alias: string,
|
|
||||||
args: arrayOf(
|
|
||||||
shape({
|
|
||||||
type: string.isRequired,
|
|
||||||
value: string.isRequired,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
})
|
|
||||||
).isRequired,
|
|
||||||
isSelected: bool.isRequired,
|
|
||||||
onToggleField: func.isRequired,
|
|
||||||
onApplyFuncsToField: func.isRequired,
|
|
||||||
isKapacitorRule: bool.isRequired,
|
|
||||||
funcs: arrayOf(string.isRequired).isRequired,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private close = (): void => {
|
||||||
|
this.setState({isOpen: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleToggleField = (): void => {
|
||||||
|
const {onToggleField} = this.props
|
||||||
|
const value = this.getFieldName()
|
||||||
|
|
||||||
|
onToggleField({value, type: 'field'})
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleApplyFunctions = (selectedFuncs: string[]) => {
|
||||||
|
const {onApplyFuncsToField} = this.props
|
||||||
|
const fieldName = this.getFieldName()
|
||||||
|
const field: Field = {value: fieldName, type: 'field'}
|
||||||
|
|
||||||
|
onApplyFuncsToField({
|
||||||
|
field,
|
||||||
|
funcs: selectedFuncs.map(val => this.makeFuncArg(val)),
|
||||||
|
})
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private makeFuncArg = (value: string): FuncArg => ({
|
||||||
|
value,
|
||||||
|
type: 'func',
|
||||||
|
})
|
||||||
|
|
||||||
|
private getFieldName = (): string => {
|
||||||
|
const {fieldFuncs} = this.props
|
||||||
|
const fieldFunc = _.head(fieldFuncs)
|
||||||
|
|
||||||
|
return _.get(fieldFunc, 'type') === 'field'
|
||||||
|
? _.get(fieldFunc, 'value')
|
||||||
|
: firstFieldName(_.get(fieldFunc, 'args'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default FieldListItem
|
export default FieldListItem
|
|
@ -1,24 +1,40 @@
|
||||||
import React from 'react'
|
import React, {SFC} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {withRouter} from 'react-router'
|
import {withRouter} from 'react-router'
|
||||||
|
import {Location} from 'history'
|
||||||
|
|
||||||
import groupByTimeOptions from 'src/data_explorer/data/groupByTimes'
|
import groupByTimeOptions from 'src/data_explorer/data/groupByTimes'
|
||||||
|
|
||||||
import Dropdown from 'shared/components/Dropdown'
|
import Dropdown from 'src/shared/components/Dropdown'
|
||||||
|
|
||||||
import {AUTO_GROUP_BY} from 'shared/constants'
|
import {AUTO_GROUP_BY} from 'src/shared/constants'
|
||||||
|
import {GroupBy} from 'src/types'
|
||||||
|
|
||||||
const isInRuleBuilder = pathname => pathname.includes('alert-rules')
|
interface GroupByTimeOption {
|
||||||
|
defaultTimeBound: string
|
||||||
|
seconds: number
|
||||||
|
menuOption: string
|
||||||
|
}
|
||||||
|
|
||||||
const getOptions = pathname =>
|
interface Props {
|
||||||
|
location?: Location
|
||||||
|
selected: string
|
||||||
|
onChooseGroupByTime: (groupBy: GroupBy) => void
|
||||||
|
isDisabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInRuleBuilder = (pathname: string): boolean =>
|
||||||
|
pathname.includes('alert-rules')
|
||||||
|
|
||||||
|
const getOptions = (pathname: string): GroupByTimeOption[] =>
|
||||||
isInRuleBuilder(pathname)
|
isInRuleBuilder(pathname)
|
||||||
? groupByTimeOptions.filter(({menuOption}) => menuOption !== AUTO_GROUP_BY)
|
? groupByTimeOptions.filter(({menuOption}) => menuOption !== AUTO_GROUP_BY)
|
||||||
: groupByTimeOptions
|
: groupByTimeOptions
|
||||||
|
|
||||||
const GroupByTimeDropdown = ({
|
const GroupByTimeDropdown: SFC<Props> = ({
|
||||||
selected,
|
selected,
|
||||||
onChooseGroupByTime,
|
onChooseGroupByTime,
|
||||||
location: {pathname},
|
location: {pathname},
|
||||||
|
isDisabled,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="group-by-time">
|
<div className="group-by-time">
|
||||||
<label className="group-by-time--label">Group by:</label>
|
<label className="group-by-time--label">Group by:</label>
|
||||||
|
@ -32,18 +48,9 @@ const GroupByTimeDropdown = ({
|
||||||
}))}
|
}))}
|
||||||
onChoose={onChooseGroupByTime}
|
onChoose={onChooseGroupByTime}
|
||||||
selected={selected || 'Time'}
|
selected={selected || 'Time'}
|
||||||
|
disabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {func, string, shape} = PropTypes
|
|
||||||
|
|
||||||
GroupByTimeDropdown.propTypes = {
|
|
||||||
location: shape({
|
|
||||||
pathname: string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
selected: string,
|
|
||||||
onChooseGroupByTime: func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(GroupByTimeDropdown)
|
export default withRouter(GroupByTimeDropdown)
|
|
@ -2,7 +2,7 @@ import React, {PureComponent} from 'react'
|
||||||
|
|
||||||
import QueryEditor from './QueryEditor'
|
import QueryEditor from './QueryEditor'
|
||||||
import SchemaExplorer from 'src/shared/components/SchemaExplorer'
|
import SchemaExplorer from 'src/shared/components/SchemaExplorer'
|
||||||
import {Source, Query} from 'src/types'
|
import {Source, QueryConfig} from 'src/types'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
const rawTextBinder = (links, id, action) => text =>
|
const rawTextBinder = (links, id, action) => text =>
|
||||||
|
@ -12,7 +12,7 @@ interface Props {
|
||||||
source: Source
|
source: Source
|
||||||
rawText: string
|
rawText: string
|
||||||
actions: any
|
actions: any
|
||||||
activeQuery: Query
|
activeQuery: QueryConfig
|
||||||
initialGroupByTime: string
|
initialGroupByTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ class WriteDataForm extends Component {
|
||||||
handleFileInputRef = el => (this.fileInput = el)
|
handleFileInputRef = el => (this.fileInput = el)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {onClose, errorThrown} = this.props
|
const {onClose, errorThrown, source} = this.props
|
||||||
const {dragClass} = this.state
|
const {dragClass} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -148,6 +148,7 @@ class WriteDataForm extends Component {
|
||||||
<div className="write-data-form">
|
<div className="write-data-form">
|
||||||
<WriteDataHeader
|
<WriteDataHeader
|
||||||
{...this.state}
|
{...this.state}
|
||||||
|
source={source}
|
||||||
handleSelectDatabase={this.handleSelectDatabase}
|
handleSelectDatabase={this.handleSelectDatabase}
|
||||||
errorThrown={errorThrown}
|
errorThrown={errorThrown}
|
||||||
toggleWriteView={this.toggleWriteView}
|
toggleWriteView={this.toggleWriteView}
|
||||||
|
|
|
@ -9,11 +9,13 @@ const WriteDataHeader = ({
|
||||||
toggleWriteView,
|
toggleWriteView,
|
||||||
isManual,
|
isManual,
|
||||||
onClose,
|
onClose,
|
||||||
|
source,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="write-data-form--header">
|
<div className="write-data-form--header">
|
||||||
<div className="page-header__left">
|
<div className="page-header__left">
|
||||||
<h1 className="page-header__title">Write Data To</h1>
|
<h1 className="page-header__title">Write Data To</h1>
|
||||||
<DatabaseDropdown
|
<DatabaseDropdown
|
||||||
|
source={source}
|
||||||
onSelectDatabase={handleSelectDatabase}
|
onSelectDatabase={handleSelectDatabase}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
onErrorThrown={errorThrown}
|
onErrorThrown={errorThrown}
|
||||||
|
@ -40,7 +42,7 @@ const WriteDataHeader = ({
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {func, string, bool} = PropTypes
|
const {func, shape, string, bool} = PropTypes
|
||||||
|
|
||||||
WriteDataHeader.propTypes = {
|
WriteDataHeader.propTypes = {
|
||||||
handleSelectDatabase: func.isRequired,
|
handleSelectDatabase: func.isRequired,
|
||||||
|
@ -49,6 +51,11 @@ WriteDataHeader.propTypes = {
|
||||||
errorThrown: func.isRequired,
|
errorThrown: func.isRequired,
|
||||||
onClose: func.isRequired,
|
onClose: func.isRequired,
|
||||||
isManual: bool,
|
isManual: bool,
|
||||||
|
source: shape({
|
||||||
|
links: shape({
|
||||||
|
proxy: string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
}).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WriteDataHeader
|
export default WriteDataHeader
|
||||||
|
|
|
@ -26,12 +26,12 @@ import {writeLineProtocolAsync} from 'src/data_explorer/actions/view/write'
|
||||||
import {buildRawText} from 'src/utils/influxql'
|
import {buildRawText} from 'src/utils/influxql'
|
||||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||||
|
|
||||||
import {Source, Query, TimeRange} from 'src/types'
|
import {Source, QueryConfig, TimeRange} from 'src/types'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
source: Source
|
source: Source
|
||||||
queryConfigs: Query[]
|
queryConfigs: QueryConfig[]
|
||||||
queryConfigActions: any // TODO: actually type these
|
queryConfigActions: any // TODO: actually type these
|
||||||
autoRefresh: number
|
autoRefresh: number
|
||||||
handleChooseAutoRefresh: () => void
|
handleChooseAutoRefresh: () => void
|
||||||
|
@ -169,7 +169,7 @@ export class DataExplorer extends PureComponent<Props, State> {
|
||||||
return _.get(this.props.queryConfigs, ['0', 'database'], null)
|
return _.get(this.props.queryConfigs, ['0', 'database'], null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get activeQuery(): Query {
|
private get activeQuery(): QueryConfig {
|
||||||
const {queryConfigs} = this.props
|
const {queryConfigs} = this.props
|
||||||
|
|
||||||
if (queryConfigs.length === 0) {
|
if (queryConfigs.length === 0) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import {TEMP_VAR_INTERVAL} from 'src/shared/constants'
|
||||||
|
|
||||||
const groupByTimes = [
|
const groupByTimes = [
|
||||||
{defaultTimeBound: ':interval:', seconds: 604800, menuOption: 'auto'},
|
{defaultTimeBound: TEMP_VAR_INTERVAL, seconds: 604800, menuOption: 'auto'},
|
||||||
{defaultTimeBound: 'now() - 5m', seconds: 10, menuOption: '10s'},
|
{defaultTimeBound: 'now() - 5m', seconds: 10, menuOption: '10s'},
|
||||||
{defaultTimeBound: 'now() - 15m', seconds: 60, menuOption: '1m'},
|
{defaultTimeBound: 'now() - 15m', seconds: 60, menuOption: '1m'},
|
||||||
{defaultTimeBound: 'now() - 1h', seconds: 300, menuOption: '5m'},
|
{defaultTimeBound: 'now() - 1h', seconds: 300, menuOption: '5m'},
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {ReactElement} from 'react'
|
||||||
|
|
||||||
|
type OverlayNodeType = ReactElement<any>
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
dismissOnClickOutside?: boolean
|
||||||
|
dismissOnEscape?: boolean
|
||||||
|
transitionTime?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showOverlay = (
|
||||||
|
OverlayNode: OverlayNodeType,
|
||||||
|
options: Options
|
||||||
|
) => ({
|
||||||
|
type: 'SHOW_OVERLAY',
|
||||||
|
payload: {OverlayNode, options},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const dismissOverlay = () => ({
|
||||||
|
type: 'DISMISS_OVERLAY',
|
||||||
|
})
|
|
@ -231,7 +231,7 @@ export function kapacitorProxy(kapacitor, method, path, body) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getQueryConfig = (url, queries, tempVars) =>
|
export const getQueryConfigAndStatus = (url, queries, tempVars) =>
|
||||||
AJAX({
|
AJAX({
|
||||||
url,
|
url,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, {Component, ComponentClass} from 'react'
|
import React, {Component, ComponentClass} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import {getQueryConfig} from 'src/shared/apis'
|
import {getQueryConfigAndStatus} from 'src/shared/apis'
|
||||||
import {fetchTimeSeries} from 'src/shared/apis/query'
|
import {fetchTimeSeries} from 'src/shared/apis/query'
|
||||||
import {DEFAULT_TIME_SERIES} from 'src/shared/constants/series'
|
import {DEFAULT_TIME_SERIES} from 'src/shared/constants/series'
|
||||||
import {TimeSeriesServerResponse, TimeSeriesResponse} from 'src/types/series'
|
import {TimeSeriesServerResponse, TimeSeriesResponse} from 'src/types/series'
|
||||||
|
@ -265,7 +265,11 @@ const AutoRefresh = (
|
||||||
const host = _.isArray(q.host) ? q.host[0] : q.host
|
const host = _.isArray(q.host) ? q.host[0] : q.host
|
||||||
const url = host.replace('proxy', 'queries')
|
const url = host.replace('proxy', 'queries')
|
||||||
const text = q.text
|
const text = q.text
|
||||||
const {data} = await getQueryConfig(url, [{query: text}], templates)
|
const {data} = await getQueryConfigAndStatus(
|
||||||
|
url,
|
||||||
|
[{query: text}],
|
||||||
|
templates
|
||||||
|
)
|
||||||
return data.queries[0].queryAST
|
return data.queries[0].queryAST
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,8 +2,10 @@ import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
|
||||||
|
|
||||||
const CustomTimeIndicator = ({queries}) => {
|
const CustomTimeIndicator = ({queries}) => {
|
||||||
const q = queries.find(({query}) => !query.includes(':dashboardTime:'))
|
const q = queries.find(({query}) => !query.includes(TEMP_VAR_DASHBOARD_TIME))
|
||||||
const customLower = _.get(q, ['queryConfig', 'range', 'lower'], null)
|
const customLower = _.get(q, ['queryConfig', 'range', 'lower'], null)
|
||||||
const customUpper = _.get(q, ['queryConfig', 'range', 'upper'], null)
|
const customUpper = _.get(q, ['queryConfig', 'range', 'upper'], null)
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,7 @@ class DatabaseDropdown extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDatabases = async () => {
|
_getDatabases = async () => {
|
||||||
const {source} = this.context
|
const {source, database, onSelectDatabase, onErrorThrown} = this.props
|
||||||
const {database, onSelectDatabase, onErrorThrown} = this.props
|
|
||||||
const proxy = source.links.proxy
|
const proxy = source.links.proxy
|
||||||
try {
|
try {
|
||||||
const {data} = await showDatabases(proxy)
|
const {data} = await showDatabases(proxy)
|
||||||
|
@ -65,7 +64,11 @@ class DatabaseDropdown extends Component {
|
||||||
|
|
||||||
const {func, shape, string} = PropTypes
|
const {func, shape, string} = PropTypes
|
||||||
|
|
||||||
DatabaseDropdown.contextTypes = {
|
DatabaseDropdown.propTypes = {
|
||||||
|
database: string,
|
||||||
|
onSelectDatabase: func.isRequired,
|
||||||
|
onStartEdit: func,
|
||||||
|
onErrorThrown: func.isRequired,
|
||||||
source: shape({
|
source: shape({
|
||||||
links: shape({
|
links: shape({
|
||||||
proxy: string.isRequired,
|
proxy: string.isRequired,
|
||||||
|
@ -73,11 +76,4 @@ DatabaseDropdown.contextTypes = {
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseDropdown.propTypes = {
|
|
||||||
database: string,
|
|
||||||
onSelectDatabase: func.isRequired,
|
|
||||||
onStartEdit: func,
|
|
||||||
onErrorThrown: func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DatabaseDropdown
|
export default DatabaseDropdown
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, {PureComponent} from 'react'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import {Query, Source} from 'src/types'
|
import {QueryConfig, Source} from 'src/types'
|
||||||
import {Namespace} from 'src/types/query'
|
import {Namespace} from 'src/types/query'
|
||||||
|
|
||||||
import {showDatabases, showRetentionPolicies} from 'src/shared/apis/metaQuery'
|
import {showDatabases, showRetentionPolicies} from 'src/shared/apis/metaQuery'
|
||||||
|
@ -15,7 +15,7 @@ import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
interface DatabaseListProps {
|
interface DatabaseListProps {
|
||||||
query: Query
|
query: QueryConfig
|
||||||
querySource: Source
|
querySource: Source
|
||||||
onChooseNamespace: (namespace: Namespace) => void
|
onChooseNamespace: (namespace: Namespace) => void
|
||||||
source: Source
|
source: Source
|
||||||
|
@ -102,7 +102,7 @@ class DatabaseList extends PureComponent<DatabaseListProps, DatabaseListState> {
|
||||||
return () => this.props.onChooseNamespace(namespace)
|
return () => this.props.onChooseNamespace(namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
public isActive(query: Query, {database, retentionPolicy}: Namespace) {
|
public isActive(query: QueryConfig, {database, retentionPolicy}: Namespace) {
|
||||||
return (
|
return (
|
||||||
database === query.database && retentionPolicy === query.retentionPolicy
|
database === query.database && retentionPolicy === query.retentionPolicy
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,23 +1,83 @@
|
||||||
import React, {Component} from 'react'
|
import React, {PureComponent} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import QueryOptions from 'shared/components/QueryOptions'
|
import {QueryConfig, GroupBy, Source, TimeShift} from 'src/types'
|
||||||
import FieldListItem from 'src/data_explorer/components/FieldListItem'
|
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
|
||||||
|
|
||||||
import {showFieldKeys} from 'shared/apis/metaQuery'
|
import QueryOptions from 'src/shared/components/QueryOptions'
|
||||||
import showFieldKeysParser from 'shared/parsing/showFieldKeys'
|
import FieldListItem from 'src/data_explorer/components/FieldListItem'
|
||||||
|
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
|
|
||||||
|
import {showFieldKeys} from 'src/shared/apis/metaQuery'
|
||||||
|
import showFieldKeysParser from 'src/shared/parsing/showFieldKeys'
|
||||||
import {
|
import {
|
||||||
functionNames,
|
functionNames,
|
||||||
numFunctions,
|
numFunctions,
|
||||||
getFieldsWithName,
|
getFieldsWithName,
|
||||||
getFuncsByFieldName,
|
getFuncsByFieldName,
|
||||||
} from 'shared/reducers/helpers/fields'
|
} from 'src/shared/reducers/helpers/fields'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
interface GroupByOption extends GroupBy {
|
||||||
|
menuOption: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeShiftOption extends TimeShift {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
interface Links {
|
||||||
|
proxy: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Field {
|
||||||
|
type: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldFunc extends Field {
|
||||||
|
args: FuncArg[]
|
||||||
|
}
|
||||||
|
interface FuncArg {
|
||||||
|
value: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApplyFuncsToFieldArgs {
|
||||||
|
field: Field
|
||||||
|
funcs: FuncArg[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
query: QueryConfig
|
||||||
|
onTimeShift: (shift: TimeShiftOption) => void
|
||||||
|
onToggleField: (field: Field) => void
|
||||||
|
onGroupByTime: (groupByOption: string) => void
|
||||||
|
onFill: (fill: string) => void
|
||||||
|
applyFuncsToField: (field: ApplyFuncsToFieldArgs, groupBy: GroupBy) => void
|
||||||
|
isKapacitorRule: boolean
|
||||||
|
querySource: {
|
||||||
|
links: Links
|
||||||
|
}
|
||||||
|
removeFuncs: (fields: Field[]) => void
|
||||||
|
addInitialField: (field: Field, groupBy: GroupBy) => void
|
||||||
|
initialGroupByTime: string | null
|
||||||
|
isQuerySupportedByExplorer: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
fields: Field[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
source: Source
|
||||||
|
}
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class FieldList extends Component {
|
class FieldList extends PureComponent<Props, State> {
|
||||||
|
public static context: Context
|
||||||
|
public static defaultProps: Partial<Props> = {
|
||||||
|
isKapacitorRule: false,
|
||||||
|
initialGroupByTime: null,
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -25,16 +85,16 @@ class FieldList extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount() {
|
||||||
const {database, measurement} = this.props.query
|
const {database, measurement} = this.props.query
|
||||||
if (!database || !measurement) {
|
if (!database || !measurement) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getFields()
|
this.getFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
public componentDidUpdate(prevProps) {
|
||||||
const {querySource, query} = this.props
|
const {querySource, query} = this.props
|
||||||
const {database, measurement, retentionPolicy} = query
|
const {database, measurement, retentionPolicy} = query
|
||||||
const {
|
const {
|
||||||
|
@ -55,26 +115,100 @@ class FieldList extends Component {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getFields()
|
this.getFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGroupByTime = groupBy => {
|
public render() {
|
||||||
|
const {
|
||||||
|
query: {database, measurement, fields = [], groupBy, fill, shifts},
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
|
isKapacitorRule,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const hasAggregates = numFunctions(fields) > 0
|
||||||
|
const noDBorMeas = !database || !measurement
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="query-builder--column">
|
||||||
|
<div className="query-builder--heading">
|
||||||
|
<span>Fields</span>
|
||||||
|
{hasAggregates ? (
|
||||||
|
<QueryOptions
|
||||||
|
fill={fill}
|
||||||
|
shift={_.first(shifts)}
|
||||||
|
groupBy={groupBy}
|
||||||
|
onFill={this.handleFill}
|
||||||
|
isKapacitorRule={isKapacitorRule}
|
||||||
|
onTimeShift={this.handleTimeShift}
|
||||||
|
onGroupByTime={this.handleGroupByTime}
|
||||||
|
isDisabled={!isQuerySupportedByExplorer}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{noDBorMeas ? (
|
||||||
|
<div className="query-builder--list-empty">
|
||||||
|
<span>
|
||||||
|
No <strong>Measurement</strong> selected
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="query-builder--list">
|
||||||
|
<FancyScrollbar>
|
||||||
|
{this.state.fields.map((fieldFunc, i) => {
|
||||||
|
const selectedFields = getFieldsWithName(
|
||||||
|
fieldFunc.value,
|
||||||
|
fields
|
||||||
|
)
|
||||||
|
|
||||||
|
const funcs: FieldFunc[] = getFuncsByFieldName(
|
||||||
|
fieldFunc.value,
|
||||||
|
fields
|
||||||
|
)
|
||||||
|
const fieldFuncs = selectedFields.length
|
||||||
|
? selectedFields
|
||||||
|
: [fieldFunc]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldListItem
|
||||||
|
key={i}
|
||||||
|
onToggleField={this.handleToggleField}
|
||||||
|
onApplyFuncsToField={this.handleApplyFuncs}
|
||||||
|
isSelected={!!selectedFields.length}
|
||||||
|
fieldFuncs={fieldFuncs}
|
||||||
|
funcs={functionNames(funcs)}
|
||||||
|
isKapacitorRule={isKapacitorRule}
|
||||||
|
isDisabled={!isQuerySupportedByExplorer}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</FancyScrollbar>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleGroupByTime = (groupBy: GroupByOption): void => {
|
||||||
this.props.onGroupByTime(groupBy.menuOption)
|
this.props.onGroupByTime(groupBy.menuOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFill = fill => {
|
private handleFill = (fill: string): void => {
|
||||||
this.props.onFill(fill)
|
this.props.onFill(fill)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleField = field => {
|
private handleToggleField = (field: Field) => {
|
||||||
const {
|
const {
|
||||||
query,
|
query,
|
||||||
onToggleField,
|
onToggleField,
|
||||||
addInitialField,
|
addInitialField,
|
||||||
initialGroupByTime: time,
|
initialGroupByTime: time,
|
||||||
isKapacitorRule,
|
isKapacitorRule,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
} = this.props
|
} = this.props
|
||||||
const {fields, groupBy} = query
|
const {fields, groupBy} = query
|
||||||
|
if (!isQuerySupportedByExplorer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const initialGroupBy = {...groupBy, time}
|
const initialGroupBy = {...groupBy, time}
|
||||||
|
|
||||||
if (!_.size(fields)) {
|
if (!_.size(fields)) {
|
||||||
|
@ -86,7 +220,7 @@ class FieldList extends Component {
|
||||||
onToggleField(field)
|
onToggleField(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleApplyFuncs = fieldFunc => {
|
private handleApplyFuncs = (fieldFunc: ApplyFuncsToFieldArgs): void => {
|
||||||
const {
|
const {
|
||||||
query,
|
query,
|
||||||
removeFuncs,
|
removeFuncs,
|
||||||
|
@ -109,11 +243,11 @@ class FieldList extends Component {
|
||||||
applyFuncsToField(fieldFunc, groupBy)
|
applyFuncsToField(fieldFunc, groupBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTimeShift = shift => {
|
private handleTimeShift = (shift: TimeShiftOption): void => {
|
||||||
this.props.onTimeShift(shift)
|
this.props.onTimeShift(shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFields = () => {
|
private getFields = (): void => {
|
||||||
const {database, measurement, retentionPolicy} = this.props.query
|
const {database, measurement, retentionPolicy} = this.props.query
|
||||||
const {source} = this.context
|
const {source} = this.context
|
||||||
const {querySource} = this.props
|
const {querySource} = this.props
|
||||||
|
@ -137,114 +271,6 @@ class FieldList extends Component {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
query: {database, measurement, fields = [], groupBy, fill, shifts},
|
|
||||||
isKapacitorRule,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
const hasAggregates = numFunctions(fields) > 0
|
|
||||||
const noDBorMeas = !database || !measurement
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="query-builder--column">
|
|
||||||
<div className="query-builder--heading">
|
|
||||||
<span>Fields</span>
|
|
||||||
{hasAggregates ? (
|
|
||||||
<QueryOptions
|
|
||||||
fill={fill}
|
|
||||||
shift={_.first(shifts)}
|
|
||||||
groupBy={groupBy}
|
|
||||||
onFill={this.handleFill}
|
|
||||||
isKapacitorRule={isKapacitorRule}
|
|
||||||
onTimeShift={this.handleTimeShift}
|
|
||||||
onGroupByTime={this.handleGroupByTime}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
{noDBorMeas ? (
|
|
||||||
<div className="query-builder--list-empty">
|
|
||||||
<span>
|
|
||||||
No <strong>Measurement</strong> selected
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="query-builder--list">
|
|
||||||
<FancyScrollbar>
|
|
||||||
{this.state.fields.map((fieldFunc, i) => {
|
|
||||||
const selectedFields = getFieldsWithName(
|
|
||||||
fieldFunc.value,
|
|
||||||
fields
|
|
||||||
)
|
|
||||||
|
|
||||||
const funcs = getFuncsByFieldName(fieldFunc.value, fields)
|
|
||||||
const fieldFuncs = selectedFields.length
|
|
||||||
? selectedFields
|
|
||||||
: [fieldFunc]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldListItem
|
|
||||||
key={i}
|
|
||||||
onToggleField={this.handleToggleField}
|
|
||||||
onApplyFuncsToField={this.handleApplyFuncs}
|
|
||||||
isSelected={!!selectedFields.length}
|
|
||||||
fieldFuncs={fieldFuncs}
|
|
||||||
funcs={functionNames(funcs)}
|
|
||||||
isKapacitorRule={isKapacitorRule}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</FancyScrollbar>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
|
||||||
|
|
||||||
FieldList.defaultProps = {
|
|
||||||
isKapacitorRule: false,
|
|
||||||
initialGroupByTime: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
FieldList.contextTypes = {
|
|
||||||
source: shape({
|
|
||||||
links: shape({
|
|
||||||
proxy: string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
FieldList.propTypes = {
|
|
||||||
query: shape({
|
|
||||||
database: string,
|
|
||||||
retentionPolicy: string,
|
|
||||||
measurement: string,
|
|
||||||
shifts: arrayOf(
|
|
||||||
shape({
|
|
||||||
label: string,
|
|
||||||
unit: string,
|
|
||||||
quantity: string,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}).isRequired,
|
|
||||||
onTimeShift: func,
|
|
||||||
onToggleField: func.isRequired,
|
|
||||||
onGroupByTime: func.isRequired,
|
|
||||||
onFill: func,
|
|
||||||
applyFuncsToField: func.isRequired,
|
|
||||||
isKapacitorRule: bool,
|
|
||||||
querySource: shape({
|
|
||||||
links: shape({
|
|
||||||
proxy: string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
}),
|
|
||||||
removeFuncs: func.isRequired,
|
|
||||||
addInitialField: func,
|
|
||||||
initialGroupByTime: string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FieldList
|
export default FieldList
|
|
@ -1,18 +1,48 @@
|
||||||
import React, {Component} from 'react'
|
import React, {
|
||||||
import PropTypes from 'prop-types'
|
PureComponent,
|
||||||
import Dropdown from 'shared/components/Dropdown'
|
FocusEvent,
|
||||||
|
ChangeEvent,
|
||||||
|
KeyboardEvent,
|
||||||
|
} from 'react'
|
||||||
|
import Dropdown from 'src/shared/components/Dropdown'
|
||||||
|
|
||||||
import {NULL_STRING, NUMBER} from 'shared/constants/queryFillOptions'
|
import {NULL_STRING, NUMBER} from 'src/shared/constants/queryFillOptions'
|
||||||
|
|
||||||
import queryFills from 'shared/data/queryFills'
|
import queryFills from 'src/shared/data/queryFills'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onChooseFill: (text: string) => void
|
||||||
|
value: string
|
||||||
|
size?: string
|
||||||
|
theme?: string
|
||||||
|
isDisabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Item {
|
||||||
|
type: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
interface State {
|
||||||
|
selected: Item
|
||||||
|
currentNumberValue: string
|
||||||
|
resetNumberValue: string
|
||||||
|
}
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class FillQuery extends Component {
|
class FillQuery extends PureComponent<Props, State> {
|
||||||
|
public static defaultProps: Partial<Props> = {
|
||||||
|
size: 'sm',
|
||||||
|
theme: 'blue',
|
||||||
|
value: NULL_STRING,
|
||||||
|
}
|
||||||
|
|
||||||
|
private numberInput: HTMLElement
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
const isNumberValue = !isNaN(Number(props.value))
|
const isNumberValue: boolean = !isNaN(Number(props.value))
|
||||||
|
|
||||||
this.state = isNumberValue
|
this.state = isNumberValue
|
||||||
? {
|
? {
|
||||||
|
@ -27,66 +57,8 @@ class FillQuery extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDropdown = item => {
|
public render() {
|
||||||
if (item.text === NUMBER) {
|
const {size, theme, isDisabled} = this.props
|
||||||
this.setState({selected: item}, () => {
|
|
||||||
this.numberInput.focus()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.setState({selected: item}, () => {
|
|
||||||
this.props.onChooseFill(item.text)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputBlur = e => {
|
|
||||||
const nextNumberValue = e.target.value
|
|
||||||
? e.target.value
|
|
||||||
: this.state.resetNumberValue || '0'
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
currentNumberValue: nextNumberValue,
|
|
||||||
resetNumberValue: nextNumberValue,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.props.onChooseFill(nextNumberValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputChange = e => {
|
|
||||||
const currentNumberValue = e.target.value
|
|
||||||
|
|
||||||
this.setState({currentNumberValue})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyDown = e => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.numberInput.blur()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyUp = e => {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
this.setState({currentNumberValue: this.state.resetNumberValue}, () => {
|
|
||||||
this.numberInput.blur()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getColor = theme => {
|
|
||||||
switch (theme) {
|
|
||||||
case 'BLUE':
|
|
||||||
return 'plutonium'
|
|
||||||
case 'GREEN':
|
|
||||||
return 'malachite'
|
|
||||||
case 'PURPLE':
|
|
||||||
return 'astronaut'
|
|
||||||
default:
|
|
||||||
return 'plutonium'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {size, theme} = this.props
|
|
||||||
const {selected, currentNumberValue} = this.state
|
const {selected, currentNumberValue} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -114,26 +86,70 @@ class FillQuery extends Component {
|
||||||
buttonColor="btn-info"
|
buttonColor="btn-info"
|
||||||
menuClass={`dropdown-${this.getColor(theme)}`}
|
menuClass={`dropdown-${this.getColor(theme)}`}
|
||||||
onChoose={this.handleDropdown}
|
onChoose={this.handleDropdown}
|
||||||
|
disabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
<label className="fill-query--label">Fill:</label>
|
<label className="fill-query--label">Fill:</label>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleDropdown = (item: Item): void => {
|
||||||
|
if (item.text === NUMBER) {
|
||||||
|
this.setState({selected: item}, () => {
|
||||||
|
this.numberInput.focus()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.setState({selected: item}, () => {
|
||||||
|
this.props.onChooseFill(item.text)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {func, string} = PropTypes
|
private handleInputBlur = (e: FocusEvent<HTMLInputElement>): void => {
|
||||||
|
const nextNumberValue = e.target.value
|
||||||
|
? e.target.value
|
||||||
|
: this.state.resetNumberValue || '0'
|
||||||
|
|
||||||
FillQuery.defaultProps = {
|
this.setState({
|
||||||
size: 'sm',
|
currentNumberValue: nextNumberValue,
|
||||||
theme: 'blue',
|
resetNumberValue: nextNumberValue,
|
||||||
value: NULL_STRING,
|
})
|
||||||
|
|
||||||
|
this.props.onChooseFill(nextNumberValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
FillQuery.propTypes = {
|
private handleInputChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||||
onChooseFill: func.isRequired,
|
const currentNumberValue = e.target.value
|
||||||
value: string,
|
|
||||||
size: string,
|
this.setState({currentNumberValue})
|
||||||
theme: string,
|
}
|
||||||
|
|
||||||
|
private handleKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.numberInput.blur()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleKeyUp = (e: KeyboardEvent<HTMLInputElement>): void => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.setState({currentNumberValue: this.state.resetNumberValue}, () => {
|
||||||
|
this.numberInput.blur()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getColor = (theme: string): string => {
|
||||||
|
switch (theme) {
|
||||||
|
case 'BLUE':
|
||||||
|
return 'plutonium'
|
||||||
|
case 'GREEN':
|
||||||
|
return 'malachite'
|
||||||
|
case 'PURPLE':
|
||||||
|
return 'astronaut'
|
||||||
|
default:
|
||||||
|
return 'plutonium'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FillQuery
|
export default FillQuery
|
|
@ -6,7 +6,7 @@ import _ from 'lodash'
|
||||||
import {showMeasurements} from 'src/shared/apis/metaQuery'
|
import {showMeasurements} from 'src/shared/apis/metaQuery'
|
||||||
import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
|
import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
|
||||||
|
|
||||||
import {Query, Source} from 'src/types'
|
import {QueryConfig, Source} from 'src/types'
|
||||||
|
|
||||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
import MeasurementListFilter from 'src/shared/components/MeasurementListFilter'
|
import MeasurementListFilter from 'src/shared/components/MeasurementListFilter'
|
||||||
|
@ -14,11 +14,12 @@ import MeasurementListItem from 'src/shared/components/MeasurementListItem'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
query: Query
|
query: QueryConfig
|
||||||
querySource: Source
|
querySource: Source
|
||||||
onChooseTag: () => void
|
onChooseTag: () => void
|
||||||
onGroupByTag: () => void
|
onGroupByTag: () => void
|
||||||
onToggleTagAcceptance: () => void
|
onToggleTagAcceptance: () => void
|
||||||
|
isQuerySupportedByExplorer: boolean
|
||||||
onChooseMeasurement: (measurement: string) => void
|
onChooseMeasurement: (measurement: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +118,13 @@ class MeasurementList extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {query, querySource, onChooseTag, onGroupByTag} = this.props
|
const {
|
||||||
|
query,
|
||||||
|
querySource,
|
||||||
|
onChooseTag,
|
||||||
|
onGroupByTag,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
|
} = this.props
|
||||||
const {database, areTagsAccepted} = query
|
const {database, areTagsAccepted} = query
|
||||||
const {filtered} = this.state
|
const {filtered} = this.state
|
||||||
|
|
||||||
|
@ -147,6 +154,7 @@ class MeasurementList extends PureComponent<Props, State> {
|
||||||
areTagsAccepted={areTagsAccepted}
|
areTagsAccepted={areTagsAccepted}
|
||||||
onAcceptReject={this.handleAcceptReject}
|
onAcceptReject={this.handleAcceptReject}
|
||||||
isActive={measurement === query.measurement}
|
isActive={measurement === query.measurement}
|
||||||
|
isQuerySupportedByExplorer={isQuerySupportedByExplorer}
|
||||||
numTagsActive={Object.keys(query.tags).length}
|
numTagsActive={Object.keys(query.tags).length}
|
||||||
onChooseMeasurement={this.handleChoosemeasurement}
|
onChooseMeasurement={this.handleChoosemeasurement}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -38,6 +38,7 @@ interface Props {
|
||||||
onChooseTag: () => void
|
onChooseTag: () => void
|
||||||
onGroupByTag: () => void
|
onGroupByTag: () => void
|
||||||
onAcceptReject: () => void
|
onAcceptReject: () => void
|
||||||
|
isQuerySupportedByExplorer: boolean
|
||||||
onChooseMeasurement: (measurement: string) => () => void
|
onChooseMeasurement: (measurement: string) => () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ class MeasurementListItem extends PureComponent<Props, State> {
|
||||||
onGroupByTag,
|
onGroupByTag,
|
||||||
numTagsActive,
|
numTagsActive,
|
||||||
areTagsAccepted,
|
areTagsAccepted,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -80,6 +82,7 @@ class MeasurementListItem extends PureComponent<Props, State> {
|
||||||
<div
|
<div
|
||||||
className={classnames('flip-toggle', {
|
className={classnames('flip-toggle', {
|
||||||
flipped: areTagsAccepted,
|
flipped: areTagsAccepted,
|
||||||
|
disabled: !isQuerySupportedByExplorer,
|
||||||
})}
|
})}
|
||||||
onClick={this.handleAcceptReject}
|
onClick={this.handleAcceptReject}
|
||||||
>
|
>
|
||||||
|
@ -96,6 +99,7 @@ class MeasurementListItem extends PureComponent<Props, State> {
|
||||||
querySource={querySource}
|
querySource={querySource}
|
||||||
onChooseTag={onChooseTag}
|
onChooseTag={onChooseTag}
|
||||||
onGroupByTag={onGroupByTag}
|
onGroupByTag={onGroupByTag}
|
||||||
|
isQuerySupportedByExplorer={isQuerySupportedByExplorer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -105,6 +109,11 @@ class MeasurementListItem extends PureComponent<Props, State> {
|
||||||
private handleAcceptReject = (e: MouseEvent<HTMLElement>) => {
|
private handleAcceptReject = (e: MouseEvent<HTMLElement>) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const {isQuerySupportedByExplorer} = this.props
|
||||||
|
if (!isQuerySupportedByExplorer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const {onAcceptReject} = this.props
|
const {onAcceptReject} = this.props
|
||||||
onAcceptReject()
|
onAcceptReject()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import React, {PureComponent, ComponentClass} from 'react'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {bindActionCreators} from 'redux'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
import {dismissOverlay} from 'src/shared/actions/overlayTechnology'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
OverlayNode?: ComponentClass<any>
|
||||||
|
dismissOnClickOutside?: boolean
|
||||||
|
dismissOnEscape?: boolean
|
||||||
|
transitionTime?: number
|
||||||
|
handleDismissOverlay: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
visible: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OverlayContext = React.createContext()
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class Overlay extends PureComponent<Props, State> {
|
||||||
|
public static defaultProps: Partial<Props> = {
|
||||||
|
dismissOnClickOutside: false,
|
||||||
|
dismissOnEscape: false,
|
||||||
|
transitionTime: 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
private animationTimer: number
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
visible: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(prevProps) {
|
||||||
|
if (prevProps.OverlayNode === null && this.props.OverlayNode) {
|
||||||
|
return this.setState({visible: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {OverlayNode} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OverlayContext.Provider
|
||||||
|
value={{
|
||||||
|
onDismissOverlay: this.handleAnimateDismiss,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={this.overlayClass}>
|
||||||
|
<div className="overlay--dialog">{OverlayNode}</div>
|
||||||
|
<div className="overlay--mask" onClick={this.handleClickOutside} />
|
||||||
|
</div>
|
||||||
|
</OverlayContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get overlayClass(): string {
|
||||||
|
const {visible} = this.state
|
||||||
|
return `overlay-tech ${visible ? 'show' : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleClickOutside = () => {
|
||||||
|
const {handleDismissOverlay, dismissOnClickOutside} = this.props
|
||||||
|
|
||||||
|
if (dismissOnClickOutside) {
|
||||||
|
handleDismissOverlay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleAnimateDismiss = () => {
|
||||||
|
const {transitionTime} = this.props
|
||||||
|
this.setState({visible: false})
|
||||||
|
this.animationTimer = window.setTimeout(this.handleDismiss, transitionTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleDismiss = () => {
|
||||||
|
const {handleDismissOverlay} = this.props
|
||||||
|
handleDismissOverlay()
|
||||||
|
clearTimeout(this.animationTimer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = ({
|
||||||
|
overlayTechnology: {
|
||||||
|
OverlayNode,
|
||||||
|
options: {dismissOnClickOutside, dismissOnEscape, transitionTime},
|
||||||
|
},
|
||||||
|
}) => ({
|
||||||
|
OverlayNode,
|
||||||
|
dismissOnClickOutside,
|
||||||
|
dismissOnEscape,
|
||||||
|
transitionTime,
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleDismissOverlay: bindActionCreators(dismissOverlay, dispatch),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Overlay)
|
|
@ -1,10 +1,23 @@
|
||||||
import React from 'react'
|
import React, {SFC} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
import {GroupBy, TimeShift} from 'src/types'
|
||||||
|
|
||||||
import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown'
|
import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown'
|
||||||
import TimeShiftDropdown from 'src/shared/components/TimeShiftDropdown'
|
import TimeShiftDropdown from 'src/shared/components/TimeShiftDropdown'
|
||||||
import FillQuery from 'shared/components/FillQuery'
|
import FillQuery from 'src/shared/components/FillQuery'
|
||||||
|
|
||||||
const QueryOptions = ({
|
interface Props {
|
||||||
|
fill: string
|
||||||
|
onFill: (fill: string) => void
|
||||||
|
groupBy: GroupBy
|
||||||
|
shift: TimeShift
|
||||||
|
onGroupByTime: (groupBy: GroupBy) => void
|
||||||
|
isKapacitorRule: boolean
|
||||||
|
onTimeShift: (shift: TimeShift) => void
|
||||||
|
isDisabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const QueryOptions: SFC<Props> = ({
|
||||||
fill,
|
fill,
|
||||||
shift,
|
shift,
|
||||||
onFill,
|
onFill,
|
||||||
|
@ -12,36 +25,25 @@ const QueryOptions = ({
|
||||||
onTimeShift,
|
onTimeShift,
|
||||||
onGroupByTime,
|
onGroupByTime,
|
||||||
isKapacitorRule,
|
isKapacitorRule,
|
||||||
|
isDisabled,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="query-builder--groupby-fill-container">
|
<div className="query-builder--groupby-fill-container">
|
||||||
<GroupByTimeDropdown
|
<GroupByTimeDropdown
|
||||||
selected={groupBy.time}
|
selected={groupBy.time}
|
||||||
onChooseGroupByTime={onGroupByTime}
|
onChooseGroupByTime={onGroupByTime}
|
||||||
|
isDisabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
{isKapacitorRule ? null : (
|
{isKapacitorRule ? null : (
|
||||||
<TimeShiftDropdown
|
<TimeShiftDropdown
|
||||||
selected={shift && shift.label}
|
selected={shift && shift.label}
|
||||||
onChooseTimeShift={onTimeShift}
|
onChooseTimeShift={onTimeShift}
|
||||||
|
isDisabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isKapacitorRule ? null : <FillQuery value={fill} onChooseFill={onFill} />}
|
{isKapacitorRule ? null : (
|
||||||
|
<FillQuery value={fill} onChooseFill={onFill} isDisabled={isDisabled} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {bool, func, shape, string} = PropTypes
|
|
||||||
|
|
||||||
QueryOptions.propTypes = {
|
|
||||||
fill: string,
|
|
||||||
onFill: func.isRequired,
|
|
||||||
groupBy: shape({
|
|
||||||
time: string,
|
|
||||||
}).isRequired,
|
|
||||||
shift: shape({
|
|
||||||
label: string,
|
|
||||||
}),
|
|
||||||
onGroupByTime: func.isRequired,
|
|
||||||
isKapacitorRule: bool.isRequired,
|
|
||||||
onTimeShift: func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default QueryOptions
|
export default QueryOptions
|
|
@ -9,7 +9,6 @@ const actionBinder = (id, action) => (...args) => action(id, ...args)
|
||||||
|
|
||||||
const SchemaExplorer = ({
|
const SchemaExplorer = ({
|
||||||
query,
|
query,
|
||||||
query: {id},
|
|
||||||
source,
|
source,
|
||||||
initialGroupByTime,
|
initialGroupByTime,
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -26,7 +25,11 @@ const SchemaExplorer = ({
|
||||||
applyFuncsToField,
|
applyFuncsToField,
|
||||||
toggleTagAcceptance,
|
toggleTagAcceptance,
|
||||||
},
|
},
|
||||||
}) => (
|
isQuerySupportedByExplorer = true,
|
||||||
|
}) => {
|
||||||
|
const {id} = query
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="query-builder">
|
<div className="query-builder">
|
||||||
<DatabaseList
|
<DatabaseList
|
||||||
query={query}
|
query={query}
|
||||||
|
@ -41,6 +44,7 @@ const SchemaExplorer = ({
|
||||||
onGroupByTag={actionBinder(id, groupByTag)}
|
onGroupByTag={actionBinder(id, groupByTag)}
|
||||||
onChooseMeasurement={actionBinder(id, chooseMeasurement)}
|
onChooseMeasurement={actionBinder(id, chooseMeasurement)}
|
||||||
onToggleTagAcceptance={actionBinder(id, toggleTagAcceptance)}
|
onToggleTagAcceptance={actionBinder(id, toggleTagAcceptance)}
|
||||||
|
isQuerySupportedByExplorer={isQuerySupportedByExplorer}
|
||||||
/>
|
/>
|
||||||
<FieldList
|
<FieldList
|
||||||
source={source}
|
source={source}
|
||||||
|
@ -54,11 +58,17 @@ const SchemaExplorer = ({
|
||||||
onGroupByTime={actionBinder(id, groupByTime)}
|
onGroupByTime={actionBinder(id, groupByTime)}
|
||||||
addInitialField={actionBinder(id, addInitialField)}
|
addInitialField={actionBinder(id, addInitialField)}
|
||||||
applyFuncsToField={actionBinder(id, applyFuncsToField)}
|
applyFuncsToField={actionBinder(id, applyFuncsToField)}
|
||||||
|
isQuerySupportedByExplorer={isQuerySupportedByExplorer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const {func, shape, string} = PropTypes
|
const {bool, func, shape, string} = PropTypes
|
||||||
|
|
||||||
|
SchemaExplorer.defaultProps = {
|
||||||
|
isQuerySupportedByExplorer: true,
|
||||||
|
}
|
||||||
|
|
||||||
SchemaExplorer.propTypes = {
|
SchemaExplorer.propTypes = {
|
||||||
query: shape({
|
query: shape({
|
||||||
|
@ -80,6 +90,7 @@ SchemaExplorer.propTypes = {
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
source: shape({}),
|
source: shape({}),
|
||||||
initialGroupByTime: string.isRequired,
|
initialGroupByTime: string.isRequired,
|
||||||
|
isQuerySupportedByExplorer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SchemaExplorer
|
export default SchemaExplorer
|
||||||
|
|
|
@ -40,6 +40,7 @@ interface Props {
|
||||||
querySource: Source
|
querySource: Source
|
||||||
onChooseTag: () => void
|
onChooseTag: () => void
|
||||||
onGroupByTag: () => void
|
onGroupByTag: () => void
|
||||||
|
isQuerySupportedByExplorer: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -129,7 +130,12 @@ class TagList extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {query, onChooseTag, onGroupByTag} = this.props
|
const {
|
||||||
|
query,
|
||||||
|
onChooseTag,
|
||||||
|
onGroupByTag,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="query-builder--sub-list">
|
<div className="query-builder--sub-list">
|
||||||
|
@ -142,6 +148,7 @@ class TagList extends PureComponent<Props, State> {
|
||||||
onGroupByTag={onGroupByTag}
|
onGroupByTag={onGroupByTag}
|
||||||
selectedTagValues={query.tags[tagKey] || []}
|
selectedTagValues={query.tags[tagKey] || []}
|
||||||
isUsingGroupBy={query.groupBy.tags.indexOf(tagKey) > -1}
|
isUsingGroupBy={query.groupBy.tags.indexOf(tagKey) > -1}
|
||||||
|
isQuerySupportedByExplorer={isQuerySupportedByExplorer}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,6 +14,7 @@ interface Props {
|
||||||
selectedTagValues: string[]
|
selectedTagValues: string[]
|
||||||
isUsingGroupBy?: boolean
|
isUsingGroupBy?: boolean
|
||||||
onChooseTag: (tag: Tag) => void
|
onChooseTag: (tag: Tag) => void
|
||||||
|
isQuerySupportedByExplorer: boolean
|
||||||
onGroupByTag: (tagKey: string) => void
|
onGroupByTag: (tagKey: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,10 +37,16 @@ class TagListItem extends PureComponent<Props, State> {
|
||||||
this.handleGroupBy = this.handleGroupBy.bind(this)
|
this.handleGroupBy = this.handleGroupBy.bind(this)
|
||||||
this.handleClickKey = this.handleClickKey.bind(this)
|
this.handleClickKey = this.handleClickKey.bind(this)
|
||||||
this.handleFilterText = this.handleFilterText.bind(this)
|
this.handleFilterText = this.handleFilterText.bind(this)
|
||||||
|
this.handleInputClick = this.handleInputClick.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleChoose(tagValue: string, e: MouseEvent<HTMLElement>) {
|
public handleChoose(tagValue: string, e: MouseEvent<HTMLElement>) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const {isQuerySupportedByExplorer} = this.props
|
||||||
|
if (!isQuerySupportedByExplorer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.props.onChooseTag({key: this.props.tagKey, value: tagValue})
|
this.props.onChooseTag({key: this.props.tagKey, value: tagValue})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +74,11 @@ class TagListItem extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleGroupBy(e) {
|
public handleGroupBy(e) {
|
||||||
|
const {isQuerySupportedByExplorer} = this.props
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
if (!isQuerySupportedByExplorer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.props.onGroupByTag(this.props.tagKey)
|
this.props.onGroupByTag(this.props.tagKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +87,11 @@ class TagListItem extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderTagValues() {
|
public renderTagValues() {
|
||||||
const {tagValues, selectedTagValues} = this.props
|
const {
|
||||||
|
tagValues,
|
||||||
|
selectedTagValues,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
|
} = this.props
|
||||||
if (!tagValues || !tagValues.length) {
|
if (!tagValues || !tagValues.length) {
|
||||||
return <div>no tag values</div>
|
return <div>no tag values</div>
|
||||||
}
|
}
|
||||||
|
@ -103,6 +118,7 @@ class TagListItem extends PureComponent<Props, State> {
|
||||||
{filtered.map(v => {
|
{filtered.map(v => {
|
||||||
const cx = classnames('query-builder--list-item', {
|
const cx = classnames('query-builder--list-item', {
|
||||||
active: selectedTagValues.indexOf(v) > -1,
|
active: selectedTagValues.indexOf(v) > -1,
|
||||||
|
disabled: !isQuerySupportedByExplorer,
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -123,7 +139,12 @@ class TagListItem extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {tagKey, tagValues, isUsingGroupBy} = this.props
|
const {
|
||||||
|
tagKey,
|
||||||
|
tagValues,
|
||||||
|
isUsingGroupBy,
|
||||||
|
isQuerySupportedByExplorer,
|
||||||
|
} = this.props
|
||||||
const {isOpen} = this.state
|
const {isOpen} = this.state
|
||||||
const tagItemLabel = `${tagKey} — ${tagValues.length}`
|
const tagItemLabel = `${tagKey} — ${tagValues.length}`
|
||||||
|
|
||||||
|
@ -142,6 +163,7 @@ class TagListItem extends PureComponent<Props, State> {
|
||||||
className={classnames('btn btn-xs group-by-tag', {
|
className={classnames('btn btn-xs group-by-tag', {
|
||||||
'btn-default': !isUsingGroupBy,
|
'btn-default': !isUsingGroupBy,
|
||||||
'btn-primary': isUsingGroupBy,
|
'btn-primary': isUsingGroupBy,
|
||||||
|
disabled: !isQuerySupportedByExplorer,
|
||||||
})}
|
})}
|
||||||
onClick={this.handleGroupBy}
|
onClick={this.handleGroupBy}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Dropdown from 'shared/components/Dropdown'
|
|
||||||
import {TIME_SHIFTS} from 'shared/constants/timeShift'
|
|
||||||
|
|
||||||
const TimeShiftDropdown = ({selected, onChooseTimeShift}) => (
|
|
||||||
<div className="group-by-time">
|
|
||||||
<label className="group-by-time--label">Compare:</label>
|
|
||||||
<Dropdown
|
|
||||||
className="group-by-time--dropdown"
|
|
||||||
buttonColor="btn-info"
|
|
||||||
items={TIME_SHIFTS}
|
|
||||||
onChoose={onChooseTimeShift}
|
|
||||||
selected={selected || 'none'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
const {func, string} = PropTypes
|
|
||||||
|
|
||||||
TimeShiftDropdown.propTypes = {
|
|
||||||
selected: string,
|
|
||||||
onChooseTimeShift: func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TimeShiftDropdown
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React, {SFC} from 'react'
|
||||||
|
|
||||||
|
import Dropdown from 'src/shared/components/Dropdown'
|
||||||
|
import {TIME_SHIFTS} from 'src/shared/constants/timeShift'
|
||||||
|
import {TimeShift} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
selected: string
|
||||||
|
onChooseTimeShift: (timeShift: TimeShift) => void
|
||||||
|
isDisabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimeShiftDropdown: SFC<Props> = ({
|
||||||
|
selected,
|
||||||
|
onChooseTimeShift,
|
||||||
|
isDisabled,
|
||||||
|
}) => (
|
||||||
|
<div className="group-by-time">
|
||||||
|
<label className="group-by-time--label">Compare:</label>
|
||||||
|
<Dropdown
|
||||||
|
className="group-by-time--dropdown"
|
||||||
|
buttonColor="btn-info"
|
||||||
|
items={TIME_SHIFTS}
|
||||||
|
onChoose={onChooseTimeShift}
|
||||||
|
selected={selected || 'none'}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default TimeShiftDropdown
|
|
@ -1 +1 @@
|
||||||
export const OVERLAY_TECHNOLOGY = 'overlay-technology'
|
export const OVERLAY_TECHNOLOGY = 'ceo'
|
||||||
|
|
|
@ -59,7 +59,7 @@ export const generateThresholdsListHexs = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lastValue) {
|
if (!lastValue) {
|
||||||
return {...defaultColoring, textColor: baseColor}
|
return {...defaultColoring, textColor: baseColor.hex}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the single stat is above a line graph never have a background color
|
// If the single stat is above a line graph never have a background color
|
||||||
|
|
|
@ -410,6 +410,13 @@ export const VIS_VIEWS = [GRAPH, TABLE]
|
||||||
|
|
||||||
// InfluxQL Macros
|
// InfluxQL Macros
|
||||||
export const TEMP_VAR_INTERVAL = ':interval:'
|
export const TEMP_VAR_INTERVAL = ':interval:'
|
||||||
|
export const TEMP_VAR_DASHBOARD_TIME = ':dashboardTime:'
|
||||||
|
export const TEMP_VAR_UPPER_DASHBOARD_TIME = ':upperDashboardTime:'
|
||||||
|
export const PREDEFINED_TEMP_VARS = [
|
||||||
|
TEMP_VAR_INTERVAL,
|
||||||
|
TEMP_VAR_DASHBOARD_TIME,
|
||||||
|
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
|
]
|
||||||
export const INITIAL_GROUP_BY_TIME = '10s'
|
export const INITIAL_GROUP_BY_TIME = '10s'
|
||||||
export const AUTO_GROUP_BY = 'auto'
|
export const AUTO_GROUP_BY = 'auto'
|
||||||
|
|
||||||
|
@ -443,7 +450,7 @@ export const intervalValuesPoints = [
|
||||||
export const interval = {
|
export const interval = {
|
||||||
id: 'interval',
|
id: 'interval',
|
||||||
type: 'autoGroupBy',
|
type: 'autoGroupBy',
|
||||||
tempVar: ':interval:',
|
tempVar: TEMP_VAR_INTERVAL,
|
||||||
label: 'automatically determine the best group by time',
|
label: 'automatically determine the best group by time',
|
||||||
values: intervalValuesPoints,
|
values: intervalValuesPoints,
|
||||||
}
|
}
|
||||||
|
|
|
@ -438,6 +438,13 @@ export const notifyCellDeleted = name => ({
|
||||||
message: `Deleted "${name}" from dashboard.`,
|
message: `Deleted "${name}" from dashboard.`,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const notifyBuilderDisabled = () => ({
|
||||||
|
type: 'info',
|
||||||
|
icon: 'graphline',
|
||||||
|
duration: 7500,
|
||||||
|
message: `Your query contains a user-defined Template Variable. The Schema Explorer cannot render the query and is disabled.`,
|
||||||
|
})
|
||||||
|
|
||||||
// Rule Builder Notifications
|
// Rule Builder Notifications
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
export const notifyAlertRuleCreated = () => ({
|
export const notifyAlertRuleCreated = () => ({
|
||||||
|
|
|
@ -42,6 +42,12 @@ export const firstFieldName = fields => _.head(fieldNamesDeep(fields))
|
||||||
export const hasField = (fieldName, fields) =>
|
export const hasField = (fieldName, fields) =>
|
||||||
fieldNamesDeep(fields).some(f => f === fieldName)
|
fieldNamesDeep(fields).some(f => f === fieldName)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getAllFields and funcs with fieldName
|
||||||
|
* @param {string} fieldName
|
||||||
|
* @param {FieldFunc[]} fields
|
||||||
|
* @returns {FieldFunc[]}
|
||||||
|
*/
|
||||||
// getAllFields and funcs with fieldName
|
// getAllFields and funcs with fieldName
|
||||||
export const getFieldsWithName = (fieldName, fields) =>
|
export const getFieldsWithName = (fieldName, fields) =>
|
||||||
getFieldsDeep(fields).filter(f => f.value === fieldName)
|
getFieldsDeep(fields).filter(f => f.value === fieldName)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
const initialState = {
|
||||||
|
options: {
|
||||||
|
dismissOnClickOutside: false,
|
||||||
|
dismissOnEscape: false,
|
||||||
|
transitionTime: 300,
|
||||||
|
},
|
||||||
|
OverlayNode: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function overlayTechnology(state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SHOW_OVERLAY': {
|
||||||
|
const {OverlayNode, options} = action.payload
|
||||||
|
|
||||||
|
return {...state, OverlayNode, options}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'DISMISS_OVERLAY': {
|
||||||
|
const {options} = initialState
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
OverlayNode: null,
|
||||||
|
options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
|
@ -8,6 +8,10 @@ import LayoutRenderer from 'shared/components/LayoutRenderer'
|
||||||
|
|
||||||
import {fixtureStatusPageCells} from 'src/status/fixtures'
|
import {fixtureStatusPageCells} from 'src/status/fixtures'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import {
|
||||||
|
TEMP_VAR_DASHBOARD_TIME,
|
||||||
|
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
|
} from 'src/shared/constants'
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class StatusPage extends Component {
|
class StatusPage extends Component {
|
||||||
|
@ -25,7 +29,7 @@ class StatusPage extends Component {
|
||||||
|
|
||||||
const dashboardTime = {
|
const dashboardTime = {
|
||||||
id: 'dashtime',
|
id: 'dashtime',
|
||||||
tempVar: ':dashboardTime:',
|
tempVar: TEMP_VAR_DASHBOARD_TIME,
|
||||||
type: 'constant',
|
type: 'constant',
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
|
@ -38,7 +42,7 @@ class StatusPage extends Component {
|
||||||
|
|
||||||
const upperDashboardTime = {
|
const upperDashboardTime = {
|
||||||
id: 'upperdashtime',
|
id: 'upperdashtime',
|
||||||
tempVar: ':upperDashboardTime:',
|
tempVar: TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
type: 'constant',
|
type: 'constant',
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||||
|
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
|
||||||
|
|
||||||
export const fixtureStatusPageCells = [
|
export const fixtureStatusPageCells = [
|
||||||
{
|
{
|
||||||
|
@ -13,8 +14,7 @@ export const fixtureStatusPageCells = [
|
||||||
colors: DEFAULT_LINE_COLORS,
|
colors: DEFAULT_LINE_COLORS,
|
||||||
queries: [
|
queries: [
|
||||||
{
|
{
|
||||||
query:
|
query: `SELECT count("value") AS "count_value" FROM "chronograf"."autogen"."alerts" WHERE time > ${TEMP_VAR_DASHBOARD_TIME} GROUP BY time(1d)`,
|
||||||
'SELECT count("value") AS "count_value" FROM "chronograf"."autogen"."alerts" WHERE time > :dashboardTime: GROUP BY time(1d)',
|
|
||||||
label: 'Events',
|
label: 'Events',
|
||||||
queryConfig: {
|
queryConfig: {
|
||||||
database: 'chronograf',
|
database: 'chronograf',
|
||||||
|
|
|
@ -13,6 +13,7 @@ import adminReducers from 'src/admin/reducers'
|
||||||
import kapacitorReducers from 'src/kapacitor/reducers'
|
import kapacitorReducers from 'src/kapacitor/reducers'
|
||||||
import dashboardUI from 'src/dashboards/reducers/ui'
|
import dashboardUI from 'src/dashboards/reducers/ui'
|
||||||
import cellEditorOverlay from 'src/dashboards/reducers/cellEditorOverlay'
|
import cellEditorOverlay from 'src/dashboards/reducers/cellEditorOverlay'
|
||||||
|
import overlayTechnology from 'src/shared/reducers/overlayTechnology'
|
||||||
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
|
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
|
||||||
import persistStateEnhancer from './persistStateEnhancer'
|
import persistStateEnhancer from './persistStateEnhancer'
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ const rootReducer = combineReducers({
|
||||||
...adminReducers,
|
...adminReducers,
|
||||||
dashboardUI,
|
dashboardUI,
|
||||||
cellEditorOverlay,
|
cellEditorOverlay,
|
||||||
|
overlayTechnology,
|
||||||
dashTimeV1,
|
dashTimeV1,
|
||||||
routing: routerReducer,
|
routing: routerReducer,
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
@import 'layout/page-header';
|
@import 'layout/page-header';
|
||||||
@import 'layout/page-subsections';
|
@import 'layout/page-subsections';
|
||||||
@import 'layout/sidebar';
|
@import 'layout/sidebar';
|
||||||
@import 'layout/overlay';
|
@import 'layout/overlay-technology';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@import 'components/annotations';
|
@import 'components/annotations';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Flip Toggle
|
Flip Toggle
|
||||||
-------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
Toggles between 2 options using a 3D transition
|
Toggles between 2 options using a 3D transition
|
||||||
Aesthetic and space conservative
|
Aesthetic and space conservative
|
||||||
*/
|
*/
|
||||||
|
@ -63,3 +63,19 @@ $flip-toggle-size: 28px;
|
||||||
.flip-toggle.flipped .flip-toggle--container {
|
.flip-toggle.flipped .flip-toggle--container {
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Disabled State
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.flip-toggle.disabled,
|
||||||
|
.flip-toggle.disabled:hover {
|
||||||
|
.flip-toggle--front,
|
||||||
|
.flip-toggle--back {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: $g3-castle;
|
||||||
|
color: $g9-mountain;
|
||||||
|
border-color: $g5-pepper;
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,6 +98,20 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
/* Disabled State */
|
||||||
|
&.disabled {
|
||||||
|
font-style: italic;
|
||||||
|
color: $query-builder--list-item-text-disabled;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: default;
|
||||||
|
background-color: $query-builder--list-item-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $query-builder--list-item-bg-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Filter Element */
|
/* Filter Element */
|
||||||
.query-builder--filter {
|
.query-builder--filter {
|
||||||
|
@ -156,6 +170,10 @@
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transition: transform 0.25s ease, opacity 0.25s ease;
|
transition: transform 0.25s ease, opacity 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disabled &:after {
|
||||||
|
background-color: $g5-pepper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.query-builder--list-item.active .query-builder--checkbox:after {
|
.query-builder--list-item.active .query-builder--checkbox:after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
|
@ -63,6 +63,7 @@ $query-builder--list-item-bg-hover: $g4-onyx;
|
||||||
$query-builder--list-item-text-hover: $g15-platinum;
|
$query-builder--list-item-text-hover: $g15-platinum;
|
||||||
$query-builder--list-item-bg-active: $g4-onyx;
|
$query-builder--list-item-bg-active: $g4-onyx;
|
||||||
$query-builder--list-item-text-active: $g18-cloud;
|
$query-builder--list-item-text-active: $g18-cloud;
|
||||||
|
$query-builder--list-item-text-disabled: $g9-mountain;
|
||||||
|
|
||||||
$query-builder--sub-list-gutter: 24px;
|
$query-builder--sub-list-gutter: 24px;
|
||||||
$query-builder--sub-list-bg: $query-builder--list-item-bg-active;
|
$query-builder--sub-list-bg: $query-builder--list-item-bg-active;
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Overlay Technology Styles
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
%overlay-styles {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay--mask {
|
||||||
|
@extend %overlay-styles;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.25s ease;
|
||||||
|
@include gradient-diag-down($c-pool,$c-comet);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay--dialog {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
transform: translateY(72px);
|
||||||
|
opacity: 0;
|
||||||
|
transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-tech {
|
||||||
|
@extend %overlay-styles;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
z-index: 9999;;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open State
|
||||||
|
.overlay-tech.show {
|
||||||
|
.overlay--mask {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.overlay--dialog {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
Manages Overlays
|
|
||||||
----------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
.page, .sidebar {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.page {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
.sidebar {
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
// Ensures that sidebar menus appear above the rest of the app on hover
|
|
||||||
&:hover {z-index: 2;}
|
|
||||||
&:hover + .page {z-index: 1;}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make Overlay Technology full screen
|
|
||||||
.overlay-technology {
|
|
||||||
left: -($sidebar--width) !important;
|
|
||||||
|
|
||||||
// Hacky way to ensure proper appearance of file upload modal
|
|
||||||
// Needed to have a this div nested inside of itself for the
|
|
||||||
// Drag & drop feature to work correctly
|
|
||||||
.overlay-technology {
|
|
||||||
left: 0 !important;
|
|
||||||
&:before {display: none;}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -342,3 +342,17 @@ span.icon.sidebar--icon.sidebar--icon__superadmin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensures that sidebar menus appear above the rest of the app on hover
|
||||||
|
.page, .sidebar {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.page {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.sidebar {
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:hover {z-index: 2;}
|
||||||
|
&:hover + .page {z-index: 1;}
|
||||||
|
}
|
|
@ -1,13 +1,27 @@
|
||||||
/*
|
/*
|
||||||
Styles for Overlay Technology (aka Cell Edit Mode)
|
Styles for Cell Editor Overlay
|
||||||
------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$overlay-controls-height: 60px;
|
$overlay-controls-height: 60px;
|
||||||
$overlay-controls-bg: $g2-kevlar;
|
$overlay-controls-bg: $g2-kevlar;
|
||||||
$overlay-z: 100;
|
$overlay-z: 100;
|
||||||
|
|
||||||
.overlay-technology {
|
|
||||||
|
// Make Overlay Technology full screen
|
||||||
|
.ceo {
|
||||||
|
left: -($sidebar--width) !important;
|
||||||
|
|
||||||
|
// Hacky way to ensure proper appearance of file upload modal
|
||||||
|
// Needed to have a this div nested inside of itself for the
|
||||||
|
// Drag & drop feature to work correctly
|
||||||
|
.ceo {
|
||||||
|
left: 0 !important;
|
||||||
|
&:before {display: none;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ceo {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -79,13 +93,13 @@ $overlay-z: 100;
|
||||||
@include no-user-select;
|
@include no-user-select;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.overlay-technology--editor {
|
.ceo--editor {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.overlay-technology--editor .query-maker--empty {
|
.ceo--editor .query-maker--empty {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.overlay-controls .confirm-or-cancel {
|
.overlay-controls .confirm-or-cancel {
|
||||||
|
@ -93,18 +107,18 @@ $overlay-z: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Graph editing in Dashboards is a little smaller so the dash can be seen in the background */
|
/* Graph editing in Dashboards is a little smaller so the dash can be seen in the background */
|
||||||
.overlay-technology .graph {
|
.ceo .graph {
|
||||||
margin: 0 15%;
|
margin: 0 15%;
|
||||||
}
|
}
|
||||||
.overlay-technology .query-maker {
|
.ceo .query-maker {
|
||||||
flex: 1 0 0%;
|
flex: 1 0 0%;
|
||||||
padding: 0 18px;
|
padding: 0 18px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: $g2-kevlar;
|
background-color: $g2-kevlar;
|
||||||
}
|
}
|
||||||
.overlay-technology .query-maker--tabs {
|
.ceo .query-maker--tabs {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.overlay-technology .query-maker--tab-contents {
|
.ceo .query-maker--tab-contents {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
|
@ -486,10 +486,10 @@ $dash-graph-options-arrow: 8px;
|
||||||
@import '../components/template-control-bar';
|
@import '../components/template-control-bar';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Overylay Technology (Cell Edit Mode)
|
Cell Editor Overlay
|
||||||
------------------------------------------------------
|
------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@import 'overlay-technology';
|
@import 'cell-editor-overlay';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Template Variables Manager
|
Template Variables Manager
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {Query} from 'src/types'
|
import {QueryConfig} from 'src/types'
|
||||||
import {ColorString} from 'src/types/colors'
|
import {ColorString} from 'src/types/colors'
|
||||||
|
|
||||||
interface Axis {
|
interface Axis {
|
||||||
bounds: [string, string]
|
bounds: [string, string]
|
||||||
label: string
|
label: string
|
||||||
|
@ -9,18 +10,18 @@ interface Axis {
|
||||||
scale: string
|
scale: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Axes {
|
export interface Axes {
|
||||||
x: Axis
|
x: Axis
|
||||||
y: Axis
|
y: Axis
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FieldName {
|
export interface FieldName {
|
||||||
internalName: string
|
internalName: string
|
||||||
displayName: string
|
displayName: string
|
||||||
visible: boolean
|
visible: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TableOptions {
|
export interface TableOptions {
|
||||||
verticalTimeAxis: boolean
|
verticalTimeAxis: boolean
|
||||||
sortBy: FieldName
|
sortBy: FieldName
|
||||||
wrapping: string
|
wrapping: string
|
||||||
|
@ -33,7 +34,7 @@ interface CellLinks {
|
||||||
|
|
||||||
export interface CellQuery {
|
export interface CellQuery {
|
||||||
query: string
|
query: string
|
||||||
queryConfig: Query
|
queryConfig: QueryConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Legend {
|
export interface Legend {
|
||||||
|
@ -41,7 +42,7 @@ export interface Legend {
|
||||||
orientation?: string
|
orientation?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DecimalPlaces {
|
export interface DecimalPlaces {
|
||||||
isEnforced: boolean
|
isEnforced: boolean
|
||||||
digits: number
|
digits: number
|
||||||
}
|
}
|
||||||
|
@ -75,3 +76,21 @@ export interface Template {
|
||||||
tempVar: string
|
tempVar: string
|
||||||
values: TemplateValue[]
|
values: TemplateValue[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CellEditorOverlayActionsFunc = (id: string, ...args: any[]) => any
|
||||||
|
|
||||||
|
export interface CellEditorOverlayActions {
|
||||||
|
chooseNamespace: (id: string) => void
|
||||||
|
chooseMeasurement: (id: string) => void
|
||||||
|
applyFuncsToField: (id: string) => void
|
||||||
|
chooseTag: (id: string) => void
|
||||||
|
groupByTag: (id: string) => void
|
||||||
|
toggleField: (id: string) => void
|
||||||
|
groupByTime: (id: string) => void
|
||||||
|
toggleTagAcceptance: (id: string) => void
|
||||||
|
fill: (id: string) => void
|
||||||
|
editRawTextAsync: (url: string, id: string, text: string) => Promise<void>
|
||||||
|
addInitialField: (id: string) => void
|
||||||
|
removeFuncs: (id: string) => void
|
||||||
|
timeShift: (id: string) => void
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
import {AuthLinks, Organization, Role, User, Me} from './auth'
|
import {AuthLinks, Organization, Role, User, Me} from './auth'
|
||||||
import {Query, QueryConfig, TimeRange} from './query'
|
import {Template, Cell, CellQuery, Legend} from './dashboard'
|
||||||
|
import {
|
||||||
|
GroupBy,
|
||||||
|
QueryConfig,
|
||||||
|
Status,
|
||||||
|
TimeRange,
|
||||||
|
TimeShift,
|
||||||
|
Field,
|
||||||
|
} from './query'
|
||||||
import {AlertRule, Kapacitor, Task} from './kapacitor'
|
import {AlertRule, Kapacitor, Task} from './kapacitor'
|
||||||
import {Source} from './sources'
|
import {Source, SourceLinks} from './sources'
|
||||||
import {DropdownAction, DropdownItem} from './shared'
|
import {DropdownAction, DropdownItem} from './shared'
|
||||||
|
import {Notification} from 'src/kapacitor/components/AlertOutputs'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Me,
|
Me,
|
||||||
|
@ -10,13 +19,22 @@ export {
|
||||||
Role,
|
Role,
|
||||||
User,
|
User,
|
||||||
Organization,
|
Organization,
|
||||||
|
Template,
|
||||||
|
Cell,
|
||||||
|
CellQuery,
|
||||||
|
Legend,
|
||||||
|
Status,
|
||||||
|
QueryConfig,
|
||||||
|
TimeShift,
|
||||||
|
Field,
|
||||||
|
GroupBy,
|
||||||
AlertRule,
|
AlertRule,
|
||||||
Kapacitor,
|
Kapacitor,
|
||||||
Query,
|
|
||||||
QueryConfig,
|
|
||||||
Source,
|
Source,
|
||||||
|
SourceLinks,
|
||||||
DropdownAction,
|
DropdownAction,
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
Task,
|
Task,
|
||||||
|
Notification,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export interface Query {
|
export interface QueryConfig {
|
||||||
id: QueryID
|
id?: string
|
||||||
database: string
|
database: string
|
||||||
measurement: string
|
measurement: string
|
||||||
retentionPolicy: string
|
retentionPolicy: string
|
||||||
|
@ -7,12 +7,13 @@ export interface Query {
|
||||||
tags: Tags
|
tags: Tags
|
||||||
groupBy: GroupBy
|
groupBy: GroupBy
|
||||||
areTagsAccepted: boolean
|
areTagsAccepted: boolean
|
||||||
rawText: string | null
|
rawText: string
|
||||||
range?: DurationRange | null
|
range?: DurationRange | null
|
||||||
source?: string
|
source?: string
|
||||||
fill?: string
|
fill?: string
|
||||||
status?: Status
|
status?: Status
|
||||||
shifts: TimeShift[]
|
shifts: TimeShift[]
|
||||||
|
isQuerySupportedByExplorer?: boolean // doesn't come from server -- is set in CellEditorOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Field {
|
export interface Field {
|
||||||
|
@ -22,8 +23,6 @@ export interface Field {
|
||||||
args?: Args[]
|
args?: Args[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryID = string
|
|
||||||
|
|
||||||
export interface Args {
|
export interface Args {
|
||||||
value: string
|
value: string
|
||||||
type: string
|
type: string
|
||||||
|
@ -69,18 +68,3 @@ export interface TimeShift {
|
||||||
unit: string
|
unit: string
|
||||||
quantity: string
|
quantity: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryConfig {
|
|
||||||
id?: string
|
|
||||||
database: string
|
|
||||||
measurement: string
|
|
||||||
retentionPolicy: string
|
|
||||||
fields: Field[]
|
|
||||||
tags: Tags
|
|
||||||
groupBy: GroupBy
|
|
||||||
areTagsAccepted: boolean
|
|
||||||
fill?: string
|
|
||||||
rawText: string
|
|
||||||
range: DurationRange
|
|
||||||
shifts: TimeShift[]
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import {buildQuery} from 'utils/influxql'
|
import {buildQuery} from 'utils/influxql'
|
||||||
import {TYPE_SHIFTED, TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
import {TYPE_SHIFTED, TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
||||||
|
import {
|
||||||
|
TEMP_VAR_DASHBOARD_TIME,
|
||||||
|
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
|
} from 'src/shared/constants'
|
||||||
import {timeRanges} from 'shared/data/timeRanges'
|
import {timeRanges} from 'shared/data/timeRanges'
|
||||||
|
|
||||||
const buildCannedDashboardQuery = (query, {lower, upper}, host) => {
|
const buildCannedDashboardQuery = (query, {lower, upper}, host) => {
|
||||||
|
@ -48,8 +52,8 @@ export const buildQueriesForLayouts = (cell, source, timeRange, host) => {
|
||||||
queryConfig: {database, measurement, fields, shifts, rawText, range},
|
queryConfig: {database, measurement, fields, shifts, rawText, range},
|
||||||
} = query
|
} = query
|
||||||
const tR = range || {
|
const tR = range || {
|
||||||
upper: ':upperDashboardTime:',
|
upper: TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
lower: ':dashboardTime:',
|
lower: TEMP_VAR_DASHBOARD_TIME,
|
||||||
}
|
}
|
||||||
|
|
||||||
queryText =
|
queryText =
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {shallow} from 'enzyme'
|
||||||
|
|
||||||
|
import CellEditorOverlay from 'src/dashboards/components/CellEditorOverlay'
|
||||||
|
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
||||||
|
import {
|
||||||
|
source,
|
||||||
|
cell,
|
||||||
|
timeRange,
|
||||||
|
userDefinedTemplateVariables,
|
||||||
|
predefinedTemplateVariables,
|
||||||
|
thresholdsListColors,
|
||||||
|
gaugeColors,
|
||||||
|
lineColors,
|
||||||
|
query,
|
||||||
|
} from 'test/fixtures'
|
||||||
|
|
||||||
|
jest.mock('src/shared/apis', () => require('mocks/shared/apis'))
|
||||||
|
|
||||||
|
const setup = (override = {}) => {
|
||||||
|
const props = {
|
||||||
|
source,
|
||||||
|
sources: [source],
|
||||||
|
cell,
|
||||||
|
timeRange,
|
||||||
|
autoRefresh: 0,
|
||||||
|
dashboardID: '9',
|
||||||
|
queryStatus: {
|
||||||
|
queryID: null,
|
||||||
|
status: null,
|
||||||
|
},
|
||||||
|
templates: [
|
||||||
|
...userDefinedTemplateVariables,
|
||||||
|
...predefinedTemplateVariables,
|
||||||
|
],
|
||||||
|
thresholdsListType: 'text',
|
||||||
|
thresholdsListColors,
|
||||||
|
gaugeColors,
|
||||||
|
lineColors,
|
||||||
|
editQueryStatus: () => {},
|
||||||
|
onCancel: () => {},
|
||||||
|
onSave: () => {},
|
||||||
|
notify: () => {},
|
||||||
|
...override,
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = shallow(<CellEditorOverlay {...props} />)
|
||||||
|
|
||||||
|
return {props, wrapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Dashboards.Components.CellEditorOverlay', () => {
|
||||||
|
describe('rendering', () => {
|
||||||
|
describe('if a predefined template variable is used in the query', () => {
|
||||||
|
it('should render the query maker with isQuerySupportedByExplorer as false', () => {
|
||||||
|
const queryText =
|
||||||
|
'SELECT mean(:fields:), mean("usage_user") AS "mean_usage_user" FROM "telegraf"."autogen"."cpu" WHERE time > :dashboardTime: GROUP BY time(:interval:) FILL(null)'
|
||||||
|
const {queryConfig} = query
|
||||||
|
const updatedQueryConfig = {...queryConfig, rawText: queryText}
|
||||||
|
const updatedQueries = [
|
||||||
|
{...query, query: queryText, queryConfig: updatedQueryConfig},
|
||||||
|
]
|
||||||
|
const updatedCell = {...cell, queries: updatedQueries}
|
||||||
|
const {wrapper} = setup({cell: updatedCell})
|
||||||
|
|
||||||
|
const queryMaker = wrapper.find(QueryMaker)
|
||||||
|
const activeQuery = queryMaker.prop('activeQuery')
|
||||||
|
|
||||||
|
expect(activeQuery.isQuerySupportedByExplorer).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,330 @@
|
||||||
|
import {
|
||||||
|
Source,
|
||||||
|
CellQuery,
|
||||||
|
SourceLinks,
|
||||||
|
Cell,
|
||||||
|
TimeRange,
|
||||||
|
Template,
|
||||||
|
} from 'src/types'
|
||||||
|
import {Axes, TableOptions, FieldName, DecimalPlaces} from 'src/types/dashboard'
|
||||||
|
import {ColorString, ColorNumber} from 'src/types/colors'
|
||||||
|
|
||||||
|
export const sourceLinks: SourceLinks = {
|
||||||
|
self: '/chronograf/v1/sources/4',
|
||||||
|
kapacitors: '/chronograf/v1/sources/4/kapacitors',
|
||||||
|
proxy: '/chronograf/v1/sources/4/proxy',
|
||||||
|
queries: '/chronograf/v1/sources/4/queries',
|
||||||
|
write: '/chronograf/v1/sources/4/write',
|
||||||
|
permissions: '/chronograf/v1/sources/4/permissions',
|
||||||
|
users: '/chronograf/v1/sources/4/users',
|
||||||
|
databases: '/chronograf/v1/sources/4/dbs',
|
||||||
|
annotations: '/chronograf/v1/sources/4/annotations',
|
||||||
|
health: '/chronograf/v1/sources/4/health',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const source: Source = {
|
||||||
|
id: '4',
|
||||||
|
name: 'Influx 1',
|
||||||
|
type: 'influx',
|
||||||
|
url: 'http://localhost:8086',
|
||||||
|
default: false,
|
||||||
|
telegraf: 'telegraf',
|
||||||
|
organization: 'default',
|
||||||
|
role: 'viewer',
|
||||||
|
defaultRP: '',
|
||||||
|
links: sourceLinks,
|
||||||
|
insecureSkipVerify: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const query: CellQuery = {
|
||||||
|
query:
|
||||||
|
'SELECT mean("usage_idle") AS "mean_usage_idle", mean("usage_user") AS "mean_usage_user" FROM "telegraf"."autogen"."cpu" WHERE time > :dashboardTime: GROUP BY time(:interval:) FILL(null)',
|
||||||
|
queryConfig: {
|
||||||
|
database: 'telegraf',
|
||||||
|
measurement: 'cpu',
|
||||||
|
retentionPolicy: 'autogen',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
value: 'mean',
|
||||||
|
type: 'func',
|
||||||
|
alias: 'mean_usage_idle',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
value: 'usage_idle',
|
||||||
|
type: 'field',
|
||||||
|
alias: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'mean',
|
||||||
|
type: 'func',
|
||||||
|
alias: 'mean_usage_user',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
value: 'usage_user',
|
||||||
|
type: 'field',
|
||||||
|
alias: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: {},
|
||||||
|
groupBy: {
|
||||||
|
time: 'auto',
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
areTagsAccepted: false,
|
||||||
|
fill: 'null',
|
||||||
|
rawText: null,
|
||||||
|
range: null,
|
||||||
|
shifts: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const axes: Axes = {
|
||||||
|
x: {
|
||||||
|
bounds: ['', ''],
|
||||||
|
label: '',
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
base: '10',
|
||||||
|
scale: 'linear',
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
bounds: ['', ''],
|
||||||
|
label: '',
|
||||||
|
prefix: '',
|
||||||
|
suffix: '',
|
||||||
|
base: '10',
|
||||||
|
scale: 'linear',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fieldOptions: FieldName[] = [
|
||||||
|
{
|
||||||
|
internalName: 'time',
|
||||||
|
displayName: '',
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const tableOptions: TableOptions = {
|
||||||
|
verticalTimeAxis: true,
|
||||||
|
sortBy: {
|
||||||
|
internalName: 'time',
|
||||||
|
displayName: '',
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
wrapping: 'truncate',
|
||||||
|
fixFirstColumn: true,
|
||||||
|
}
|
||||||
|
export const lineColors: ColorString[] = [
|
||||||
|
{
|
||||||
|
id: '574fb0a3-0a26-44d7-8d71-d4981756acb1',
|
||||||
|
type: 'scale',
|
||||||
|
hex: '#31C0F6',
|
||||||
|
name: 'Nineteen Eighty Four',
|
||||||
|
value: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3b9750f9-d41d-4100-8ee6-bd2785237f35',
|
||||||
|
type: 'scale',
|
||||||
|
hex: '#A500A5',
|
||||||
|
name: 'Nineteen Eighty Four',
|
||||||
|
value: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8d39064f-8124-4967-ae22-ffe14e425781',
|
||||||
|
type: 'scale',
|
||||||
|
hex: '#FF7E27',
|
||||||
|
name: 'Nineteen Eighty Four',
|
||||||
|
value: '0',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const decimalPlaces: DecimalPlaces = {
|
||||||
|
isEnforced: true,
|
||||||
|
digits: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cell: Cell = {
|
||||||
|
id: '67435af2-17bf-4caa-a5fc-0dd1ffb40dab',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: 8,
|
||||||
|
h: 4,
|
||||||
|
name: 'Untitled Line Graph',
|
||||||
|
queries: [query],
|
||||||
|
axes: axes,
|
||||||
|
type: 'line',
|
||||||
|
colors: lineColors,
|
||||||
|
legend: {},
|
||||||
|
tableOptions: tableOptions,
|
||||||
|
fieldOptions: fieldOptions,
|
||||||
|
timeFormat: 'MM/DD/YYYY HH:mm:ss',
|
||||||
|
decimalPlaces: decimalPlaces,
|
||||||
|
links: {
|
||||||
|
self:
|
||||||
|
'/chronograf/v1/dashboards/9/cells/67435af2-17bf-4caa-a5fc-0dd1ffb40dab',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fullTimeRange = {
|
||||||
|
dashboardID: 9,
|
||||||
|
defaultGroupBy: '10s',
|
||||||
|
seconds: 300,
|
||||||
|
inputValue: 'Past 5 minutes',
|
||||||
|
lower: 'now() - 5m',
|
||||||
|
upper: null,
|
||||||
|
menuOption: 'Past 5 minutes',
|
||||||
|
format: 'influxql',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const timeRange: TimeRange = {
|
||||||
|
lower: 'now() - 5m',
|
||||||
|
upper: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const userDefinedTemplateVariables: Template[] = [
|
||||||
|
{
|
||||||
|
tempVar: ':fields:',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_guest',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_guest_nice',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: true,
|
||||||
|
value: 'usage_idle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_iowait',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_irq',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_nice',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_softirq',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_steal',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'usage_user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: '2b8dca84-879c-4555-a7cf-97f2951f8643',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tempVar: ':measurements:',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
selected: true,
|
||||||
|
value: 'cpu',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'disk',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'diskio',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'mem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'processes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'swap',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selected: false,
|
||||||
|
value: 'system',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: '18855209-12db-4619-9834-1d7eb643ae6e',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const predefinedTemplateVariables: Template[] = [
|
||||||
|
{
|
||||||
|
id: 'dashtime',
|
||||||
|
tempVar: ':dashboardTime:',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
value: 'now() - 5m',
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'upperdashtime',
|
||||||
|
tempVar: ':upperDashboardTime:',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
value: 'now()',
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'interval',
|
||||||
|
tempVar: ':interval:',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
value: '333',
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const thresholdsListColors: ColorNumber[] = [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
hex: '#00C9FF',
|
||||||
|
id: 'base',
|
||||||
|
name: 'laser',
|
||||||
|
value: -1000000000000000000,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const gaugeColors: ColorNumber[] = [
|
||||||
|
{
|
||||||
|
type: 'min',
|
||||||
|
hex: '#00C9FF',
|
||||||
|
id: '0',
|
||||||
|
name: 'laser',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'max',
|
||||||
|
hex: '#9394FF',
|
||||||
|
id: '1',
|
||||||
|
name: 'comet',
|
||||||
|
value: 100,
|
||||||
|
},
|
||||||
|
]
|
|
@ -13,6 +13,7 @@ const setup = (override = {}) => {
|
||||||
onToggleTagAcceptance: () => {},
|
onToggleTagAcceptance: () => {},
|
||||||
query,
|
query,
|
||||||
querySource: source,
|
querySource: source,
|
||||||
|
isQuerySupportedByExplorer: true,
|
||||||
...override,
|
...override,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ const setup = (overrides = {}) => {
|
||||||
measurement: 'test',
|
measurement: 'test',
|
||||||
numTagsActive: 3,
|
numTagsActive: 3,
|
||||||
areTagsAccepted: true,
|
areTagsAccepted: true,
|
||||||
|
isQuerySupportedByExplorer: true,
|
||||||
onChooseTag: () => {},
|
onChooseTag: () => {},
|
||||||
onGroupByTag: () => {},
|
onGroupByTag: () => {},
|
||||||
onAcceptReject: () => {},
|
onAcceptReject: () => {},
|
||||||
|
|
|
@ -13,6 +13,7 @@ const setup = (override = {}) => {
|
||||||
onGroupByTag: () => {},
|
onGroupByTag: () => {},
|
||||||
query,
|
query,
|
||||||
querySource: source,
|
querySource: source,
|
||||||
|
isQuerySupportedByExplorer: true,
|
||||||
...override,
|
...override,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue