Enable selection of default values for tempvars

Co-authored-by: Alirie Gray <alirie.gray@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>
pull/10616/head
Alirie Gray 2018-06-22 17:17:00 -07:00 committed by ebb-tide
parent 48e030b0fd
commit 4dbf4d065d
19 changed files with 313 additions and 57 deletions

View File

@ -1,4 +1,5 @@
import _ from 'lodash'
import {getDeep} from 'src/utils/wrappers'
import {
Dashboard,
@ -140,3 +141,67 @@ export const findInvalidTempVarsInURLQuery = (
return urlQueryParamsTempVarsWithInvalidValues
}
const makeDefault = (template: Template, value: string): Template => {
const found = template.values.find(v => v.value === value)
let valueToChoose
if (found) {
valueToChoose = found.value
} else {
valueToChoose = getDeep<string>(template, 'values.0.value', '')
}
const valuesWithDefault = template.values.map(v => {
if (v.value === valueToChoose) {
return {...v, default: true}
} else {
return {...v, default: false}
}
})
return {...template, values: valuesWithDefault}
}
const makeSelected = (template: Template, value: string): Template => {
const found = template.values.find(v => v.value === value)
const defaultValue = template.values.find(v => v.default)
let valueToChoose
if (found) {
valueToChoose = found.value
} else if (defaultValue) {
valueToChoose = defaultValue
} else {
valueToChoose = getDeep<string>(template, 'values.0.value', '')
}
const valuesWithDefault = template.values.map(v => {
if (v.value === valueToChoose) {
return {...v, selected: true}
} else {
return {...v, selected: false}
}
})
return {...template, values: valuesWithDefault}
}
export const reconcileDefaultAndSelectedValues = (
nextTemplate: Template,
nextNextTemplate: Template
): Template => {
const selectedValue = nextTemplate.values.find(v => v.selected)
const defaultValue = nextTemplate.values.find(v => v.default)
// make selected from default
const TemplateWithDefault = makeDefault(
nextNextTemplate,
getDeep<string>(defaultValue, 'value', '')
)
const TemplateWithDefaultAndSelected = makeSelected(
TemplateWithDefault,
getDeep<string>(selectedValue, 'value', '')
)
return TemplateWithDefaultAndSelected
}

View File

@ -441,7 +441,12 @@ export const DEFAULT_SOURCE = {
export const defaultIntervalValue = '333'
export const intervalValuesPoints = [
{value: defaultIntervalValue, type: TemplateValueType.Points, selected: true},
{
value: defaultIntervalValue,
type: TemplateValueType.Points,
selected: true,
default: true,
},
]
export const interval = {

View File

@ -1,5 +1,8 @@
import React, {PureComponent, ChangeEvent} from 'react'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplatePreviewList from 'src/tempVars/components/TemplatePreviewList'
import DragAndDrop from 'src/shared/components/DragAndDrop'
import {notifyCSVUploadFailed} from 'src/shared/copy/notifications'
@ -25,6 +28,7 @@ class CSVTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
public render() {
const {templateValues, templateValuesString} = this.state
const {onUpdateDefaultTemplateValue} = this.props
const pluralizer = templateValues.length === 1 ? '' : 's'
return (
<>
@ -54,7 +58,11 @@ class CSVTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
}
</p>
{templateValues.length > 0 && (
<TemplatePreviewList items={templateValues} />
<TemplatePreviewList
items={templateValues}
defaultValue={this.defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
)}
</div>
</>
@ -72,8 +80,6 @@ class CSVTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
this.props.notify(notifyCSVUploadFailed())
return
}
// account for newline separated values too.
// should return values be strings only.
this.setState({templateValuesString: uploadContent})
@ -82,41 +88,6 @@ class CSVTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
onUpdateTemplate({...template, values: nextValues})
}
private get validFileExtension(): string {
return '.csv'
}
private handleChange = (e: ChangeEvent<HTMLTextAreaElement>): void => {
this.setState({templateValuesString: e.target.value})
}
private getValuesFromString(templateValuesString) {
// trim whitepsace, return array of values
let templateValues
if (templateValuesString.trim() === '') {
templateValues = []
} else {
templateValues = templateValuesString.split(',').map(s => s.trim())
}
this.setState({templateValues})
const nextValues = templateValues.map((value: string): TemplateValue => {
return {
type: TemplateValueType.CSV,
value,
selected: false,
}
})
if (nextValues.length > 0) {
nextValues[0].selected = true
}
return nextValues
}
private handleBlur = (): void => {
const {template, onUpdateTemplate} = this.props
const {templateValuesString} = this.state
@ -125,6 +96,47 @@ class CSVTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
onUpdateTemplate({...template, values: nextValues})
}
private get validFileExtension(): string {
return '.csv'
}
private get defaultValue(): string {
const {template} = this.props
const defaultTemplateValue = template.values.find(v => v.default)
return getDeep<string>(defaultTemplateValue, 'value', '')
}
private handleChange = (e: ChangeEvent<HTMLTextAreaElement>): void => {
this.setState({templateValuesString: e.target.value})
}
private getValuesFromString(templateValuesString) {
let templateValues
if (templateValuesString.trim() === '') {
templateValues = []
} else {
templateValues = templateValuesString.split(',').map(s => s.trim())
}
// account for newline separated values.
// de-duplicate entries
// remove empty strings
this.setState({templateValues})
const nextValues = templateValues.map((value: string): TemplateValue => {
return {
type: TemplateValueType.CSV,
value,
selected: false,
default: false,
}
})
return nextValues
}
}
export default CSVTemplateBuilder

View File

@ -1,4 +1,5 @@
import React, {PureComponent} from 'react'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {showDatabases} from 'src/shared/apis/metaQuery'
@ -36,6 +37,7 @@ class DatabasesTemplateBuilder extends PureComponent<
public render() {
const {databases, databasesStatus} = this.state
const {onUpdateDefaultTemplateValue} = this.props
return (
<div className="temp-builder databases-temp-builder">
@ -48,10 +50,17 @@ class DatabasesTemplateBuilder extends PureComponent<
<TemplateMetaQueryPreview
items={databases}
loadingStatus={databasesStatus}
defaultValue={this.defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
</div>
)
}
private get defaultValue(): string {
const {template} = this.props
const defaultTemplateValue = template.values.find(v => v.default)
return getDeep<string>(defaultTemplateValue, 'value', '')
}
private async loadDatabases(): Promise<void> {
const {template, source, onUpdateTemplate} = this.props
@ -72,6 +81,7 @@ class DatabasesTemplateBuilder extends PureComponent<
type: TemplateValueType.Database,
value: db,
selected: false,
default: false,
}
})

View File

@ -21,7 +21,12 @@ const fetchKeys = async (source, db, measurement): Promise<string[]> => {
class FieldKeysTemplateBuilder extends PureComponent<TemplateBuilderProps> {
public render() {
const {template, source, onUpdateTemplate} = this.props
const {
template,
source,
onUpdateTemplate,
onUpdateDefaultTemplateValue,
} = this.props
return (
<KeysTemplateBuilder
@ -31,6 +36,7 @@ class FieldKeysTemplateBuilder extends PureComponent<TemplateBuilderProps> {
template={template}
source={source}
onUpdateTemplate={onUpdateTemplate}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
)
}

View File

@ -1,6 +1,6 @@
import React, {PureComponent} from 'react'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import Dropdown from 'src/shared/components/Dropdown'
import {showDatabases, showMeasurements} from 'src/shared/apis/metaQuery'
@ -64,7 +64,7 @@ class KeysTemplateBuilder extends PureComponent<Props, State> {
}
public render() {
const {queryPrefix} = this.props
const {queryPrefix, onUpdateDefaultTemplateValue} = this.props
const {
databases,
databasesStatus,
@ -101,10 +101,20 @@ class KeysTemplateBuilder extends PureComponent<Props, State> {
</DropdownLoadingPlaceholder>
</div>
</div>
<TemplateMetaQueryPreview items={keys} loadingStatus={keysStatus} />
<TemplateMetaQueryPreview
items={keys}
loadingStatus={keysStatus}
defaultValue={this.defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
</div>
)
}
private get defaultValue(): string {
const {template} = this.props
const defaultTemplateValue = template.values.find(v => v.default)
return getDeep<string>(defaultTemplateValue, 'value', '')
}
private async loadDatabases(): Promise<void> {
const {source} = this.props
@ -188,6 +198,7 @@ class KeysTemplateBuilder extends PureComponent<Props, State> {
type: templateValueType,
value,
selected: false,
default: false,
}
})

View File

@ -1,5 +1,6 @@
import React, {PureComponent} from 'react'
import _ from 'lodash'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import Dropdown from 'src/shared/components/Dropdown'
@ -48,6 +49,7 @@ class MeasurementsTemplateBuilder extends PureComponent<
}
public render() {
const {onUpdateDefaultTemplateValue} = this.props
const {
databases,
databasesStatus,
@ -75,10 +77,17 @@ class MeasurementsTemplateBuilder extends PureComponent<
<TemplateMetaQueryPreview
items={measurements}
loadingStatus={measurementsStatus}
defaultValue={this.defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
</div>
)
}
private get defaultValue(): string {
const {template} = this.props
const defaultTemplateValue = template.values.find(v => v.default)
return getDeep<string>(defaultTemplateValue, 'value', '')
}
private async loadDatabases(): Promise<void> {
const {source} = this.props
@ -129,6 +138,7 @@ class MeasurementsTemplateBuilder extends PureComponent<
type: TemplateValueType.Measurement,
value,
selected: false,
default: false,
}
})

View File

@ -1,11 +1,11 @@
import React, {PureComponent, ChangeEvent} from 'react'
import _ from 'lodash'
import {getDeep} from 'src/utils/wrappers'
import {proxy} from 'src/utils/queryUrlGenerator'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplateMetaQueryPreview from 'src/tempVars/components/TemplateMetaQueryPreview'
import {parseMetaQuery, isInvalidMetaQuery} from 'src/tempVars/utils/parsing'
import {getDeep} from 'src/utils/wrappers'
import {
TemplateBuilderProps,
@ -76,6 +76,7 @@ class CustomMetaQueryTemplateBuilder extends PureComponent<
}
private renderResults() {
const {onUpdateDefaultTemplateValue} = this.props
const {metaQueryResults, metaQueryResultsStatus} = this.state
if (this.showInvalidMetaQueryMessage) {
@ -90,9 +91,16 @@ class CustomMetaQueryTemplateBuilder extends PureComponent<
<TemplateMetaQueryPreview
items={metaQueryResults}
loadingStatus={metaQueryResultsStatus}
defaultValue={this.defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
)
}
private get defaultValue(): string {
const {template} = this.props
const defaultTemplateValue = template.values.find(v => v.default)
return getDeep<string>(defaultTemplateValue, 'value', '')
}
private get showInvalidMetaQueryMessage(): boolean {
const {metaQuery} = this.state
@ -141,6 +149,7 @@ class CustomMetaQueryTemplateBuilder extends PureComponent<
type: TemplateValueType.MetaQuery,
value: result,
selected: false,
default: false,
}
})

View File

@ -24,7 +24,12 @@ export const fetchTagKeys = async (
class TagKeysTemplateBuilder extends PureComponent<TemplateBuilderProps> {
public render() {
const {template, source, onUpdateTemplate} = this.props
const {
template,
source,
onUpdateTemplate,
onUpdateDefaultTemplateValue,
} = this.props
return (
<KeysTemplateBuilder
@ -34,6 +39,7 @@ class TagKeysTemplateBuilder extends PureComponent<TemplateBuilderProps> {
template={template}
source={source}
onUpdateTemplate={onUpdateTemplate}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
)
}

View File

@ -1,5 +1,6 @@
import React, {PureComponent} from 'react'
import _ from 'lodash'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import Dropdown from 'src/shared/components/Dropdown'
@ -64,6 +65,7 @@ class KeysTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
}
public render() {
const {onUpdateDefaultTemplateValue} = this.props
const {
databases,
databasesStatus,
@ -117,11 +119,19 @@ class KeysTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
<TemplateMetaQueryPreview
items={tagValues}
loadingStatus={tagValuesStatus}
defaultValue={this.defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
</div>
)
}
private get defaultValue(): string {
const {template} = this.props
const defaultTemplateValue = template.values.find(v => v.default)
return getDeep<string>(defaultTemplateValue, 'value', '')
}
private async loadDatabases(): Promise<void> {
const {source} = this.props
@ -229,6 +239,7 @@ class KeysTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
type: TemplateValueType.TagValue,
value,
selected: false,
default: false,
}
})

View File

@ -8,12 +8,19 @@ import {RemoteDataState} from 'src/types'
interface Props {
items: string[]
loadingStatus: RemoteDataState
defaultValue: string
onUpdateDefaultTemplateValue: (s: string) => void
}
@ErrorHandling
class TemplateMetaQueryPreview extends PureComponent<Props> {
public render() {
const {items, loadingStatus} = this.props
const {
items,
loadingStatus,
onUpdateDefaultTemplateValue,
defaultValue,
} = this.props
if (loadingStatus === RemoteDataState.NotStarted) {
return <div className="temp-builder-results" />
@ -52,7 +59,13 @@ class TemplateMetaQueryPreview extends PureComponent<Props> {
<p>
Meta Query returned <strong>{items.length}</strong> value{pluralizer}
</p>
{items.length > 0 && <TemplatePreviewList items={items} />}
{items.length > 0 && (
<TemplatePreviewList
items={items}
defaultValue={defaultValue}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
)}
</div>
)
}

View File

@ -10,6 +10,8 @@ const RESULTS_TO_DISPLAY = 10
interface Props {
items: string[]
defaultValue: string
onUpdateDefaultTemplateValue: (v: string) => void
}
@ErrorHandling
@ -23,16 +25,19 @@ class TemplatePreviewList extends PureComponent<Props> {
style={{height: `${this.resultsListHeight}px`}}
>
<FancyScrollbar>
{items.map(db => {
{items.map(item => {
return (
<li
key={uuid.v4()}
style={{
height: `${LI_HEIGHT}px`,
marginBottom: `${LI_MARGIN_BOTTOM}px`,
zIndex: 9010,
}}
onClick={this.selectDefault(item)}
>
{db}
{item}
{this.defaultIndicator(item)}
</li>
)
})}
@ -41,6 +46,18 @@ class TemplatePreviewList extends PureComponent<Props> {
)
}
private selectDefault = item => () => {
const {onUpdateDefaultTemplateValue} = this.props
onUpdateDefaultTemplateValue(item)
}
private defaultIndicator(item: string): JSX.Element {
const {defaultValue} = this.props
if (item === defaultValue) {
return <div>{' ******'}</div>
}
}
private get resultsListHeight() {
const {items} = this.props
const count = Math.min(items.length, RESULTS_TO_DISPLAY)

View File

@ -12,6 +12,7 @@ import Dropdown from 'src/shared/components/Dropdown'
import ConfirmButton from 'src/shared/components/ConfirmButton'
import {getDeep} from 'src/utils/wrappers'
import {notify as notifyActionCreator} from 'src/shared/actions/notifications'
import {reconcileDefaultAndSelectedValues} from 'src/dashboards/utils/tempVars'
import DatabasesTemplateBuilder from 'src/tempVars/components/DatabasesTemplateBuilder'
import CSVTemplateBuilder from 'src/tempVars/components/CSVTemplateBuilder'
@ -155,6 +156,9 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
source={source}
onUpdateTemplate={this.handleUpdateTemplate}
notify={notify}
onUpdateDefaultTemplateValue={
this.handleUpdateDefaultTemplateValue
}
/>
</div>
<ConfirmButton
@ -184,8 +188,31 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
return component
}
private handleUpdateTemplate = (nextTemplate: Template): void => {
this.setState({nextTemplate})
private handleUpdateDefaultTemplateValue = (value: string): void => {
const {
nextTemplate,
nextTemplate: {values},
} = this.state
const nextValues = values.map(v => {
if (v.value === value) {
return {...v, default: true}
} else {
return {...v, default: false}
}
})
this.setState({nextTemplate: {...nextTemplate, values: nextValues}})
}
private handleUpdateTemplate = (nextNextTemplate: Template): void => {
const {nextTemplate} = this.state
const TemplateWithDefaultAndSelected = reconcileDefaultAndSelectedValues(
nextTemplate,
nextNextTemplate
)
this.setState({nextTemplate: TemplateWithDefaultAndSelected})
}
private handleChooseType = ({type}) => {

View File

@ -73,6 +73,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
value: '_internal',
type: TemplateValueType.Database,
selected: true,
default: true,
},
],
type: TemplateType.Databases,

View File

@ -17,6 +17,7 @@ export interface TemplateValue {
value: string
type: TemplateValueType
selected: boolean
default: boolean
}
export interface TemplateQuery {
@ -62,5 +63,6 @@ export interface TemplateBuilderProps {
template: Template
source: Source
onUpdateTemplate: (nextTemplate: Template) => void
onUpdateDefaultTemplateValue: (v: string) => void
notify?: (message: Notification) => void
}

View File

@ -26,9 +26,24 @@ const t2 = {
label: 'test csv',
tempVar: ':temperature:',
values: [
{value: '98.7', type: TemplateValueType.Measurement, selected: false},
{value: '99.1', type: TemplateValueType.Measurement, selected: false},
{value: '101.3', type: TemplateValueType.Measurement, selected: true},
{
value: '98.7',
type: TemplateValueType.Measurement,
selected: false,
default: true,
},
{
value: '99.1',
type: TemplateValueType.Measurement,
selected: false,
default: false,
},
{
value: '101.3',
type: TemplateValueType.Measurement,
selected: true,
default: false,
},
],
}

View File

@ -208,51 +208,61 @@ export const userDefinedTemplateVariables: Template[] = [
values: [
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_guest',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_guest_nice',
},
{
selected: true,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_idle',
},
{
selected: false,
default: true,
type: TemplateValueType.FieldKey,
value: 'usage_iowait',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_irq',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_nice',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_softirq',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_steal',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_system',
},
{
selected: false,
default: false,
type: TemplateValueType.FieldKey,
value: 'usage_user',
},
@ -266,36 +276,43 @@ export const userDefinedTemplateVariables: Template[] = [
values: [
{
selected: true,
default: true,
type: TemplateValueType.Measurement,
value: 'cpu',
},
{
selected: false,
default: false,
type: TemplateValueType.Measurement,
value: 'disk',
},
{
selected: false,
default: false,
type: TemplateValueType.Measurement,
value: 'diskio',
},
{
selected: false,
default: false,
type: TemplateValueType.Measurement,
value: 'mem',
},
{
selected: false,
default: false,
type: TemplateValueType.Measurement,
value: 'processes',
},
{
selected: false,
default: false,
type: TemplateValueType.Measurement,
value: 'swap',
},
{
selected: false,
default: false,
type: TemplateValueType.Measurement,
value: 'system',
},
@ -313,6 +330,7 @@ const dashtimeTempVar: Template = {
value: 'now() - 5m',
type: TemplateValueType.Constant,
selected: true,
default: true,
},
],
label: '',
@ -326,6 +344,7 @@ const upperdashtimeTempVar: Template = {
value: 'now()',
type: TemplateValueType.Constant,
selected: true,
default: true,
},
],
label: '',

View File

@ -602,9 +602,24 @@ export const template: Template = {
influxql: 'SHOW TAGS WHERE CHRONOGIRAFFE = "friend"',
},
values: [
{value: 'us-west', type: TemplateValueType.TagKey, selected: false},
{value: 'us-east', type: TemplateValueType.TagKey, selected: true},
{value: 'us-mount', type: TemplateValueType.TagKey, selected: false},
{
value: 'us-west',
type: TemplateValueType.TagKey,
selected: false,
default: false,
},
{
value: 'us-east',
type: TemplateValueType.TagKey,
selected: true,
default: true,
},
{
value: 'us-mount',
type: TemplateValueType.TagKey,
selected: false,
default: false,
},
],
}

View File

@ -19,11 +19,13 @@ const defaultProps = {
value: 'firstValue',
type: TemplateValueType.Constant,
selected: false,
default: false,
},
{
value: 'secondValue',
type: TemplateValueType.Constant,
selected: false,
default: false,
},
],
},