Replace separate CSV manual and file entry dropdowns with one builder type that accepts both

Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>
feature/upload-template-var-csv
Alirie Gray 2018-06-22 12:48:26 -07:00
parent 707bb9b16d
commit db3cca42fd
9 changed files with 75 additions and 199 deletions

View File

@ -1,94 +0,0 @@
import React, {PureComponent, ChangeEvent} from 'react'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplatePreviewList from 'src/tempVars/components/TemplatePreviewList'
import {TemplateBuilderProps, TemplateValueType, TemplateValue} from 'src/types'
interface State {
templateValues: string[]
templateValuesString: string
}
@ErrorHandling
class CSVManualTemplateBuilder extends PureComponent<
TemplateBuilderProps,
State
> {
public constructor(props: TemplateBuilderProps) {
super(props)
const templateValues = props.template.values.map(v => v.value)
this.state = {
templateValues,
templateValuesString: templateValues.join(', '),
}
}
public render() {
const {templateValues, templateValuesString} = this.state
const pluralizer = templateValues.length === 1 ? '' : 's'
return (
<div className="temp-builder csv-temp-builder">
<div className="form-group">
<label>Comma Separated Values</label>
<div className="temp-builder--mq-controls">
<textarea
className="form-control"
value={templateValuesString}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
</div>
</div>
<div className="temp-builder-results">
<p>
CSV contains <strong>{templateValues.length}</strong> value{
pluralizer
}
</p>
{templateValues.length > 0 && (
<TemplatePreviewList items={templateValues} />
)}
</div>
</div>
)
}
private handleChange = (e: ChangeEvent<HTMLTextAreaElement>): void => {
this.setState({templateValuesString: e.target.value})
}
private handleBlur = (): void => {
const {template, onUpdateTemplate} = this.props
const {templateValuesString} = this.state
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
}
onUpdateTemplate({...template, values: nextValues})
}
}
export default CSVManualTemplateBuilder

View File

@ -1,4 +1,4 @@
import React, {PureComponent} from 'react'
import React, {PureComponent, ChangeEvent} from 'react'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplatePreviewList from 'src/tempVars/components/TemplatePreviewList'
import DragAndDrop from 'src/shared/components/DragAndDrop'
@ -12,10 +12,7 @@ interface State {
}
@ErrorHandling
class CSVFileTemplateBuilder extends PureComponent<
TemplateBuilderProps,
State
> {
class CSVTemplateBuilder extends PureComponent<TemplateBuilderProps, State> {
public constructor(props: TemplateBuilderProps) {
super(props)
const templateValues = props.template.values.map(v => v.value)
@ -27,7 +24,7 @@ class CSVFileTemplateBuilder extends PureComponent<
}
public render() {
const {templateValues} = this.state
const {templateValues, templateValuesString} = this.state
const pluralizer = templateValues.length === 1 ? '' : 's'
return (
<>
@ -36,6 +33,19 @@ class CSVFileTemplateBuilder extends PureComponent<
fileTypesToAccept={this.validFileExtension}
handleSubmit={this.handleUploadFile}
/>
<div className="temp-builder csv-temp-builder" style={{zIndex: 9010}}>
<div className="form-group" style={{zIndex: 9010}}>
<label>Comma Separated Values</label>
<div className="temp-builder--mq-controls">
<textarea
className="form-control"
value={templateValuesString}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
</div>
</div>
</div>
<div className="temp-builder-results">
<p>
CSV contains <strong>{templateValues.length}</strong> value{
@ -61,17 +71,34 @@ class CSVFileTemplateBuilder extends PureComponent<
this.props.notify(notifyCSVUploadFailed())
return
}
let templateValues
if (uploadContent.trim() === '') {
templateValues = []
} else {
templateValues = uploadContent.split(',').map(s => s.trim())
}
// account for newline separated values too.
// should return values be strings only.
this.setState({templateValuesString: uploadContent})
const nextValues = this.getValuesFromString(uploadContent)
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 => {
@ -86,12 +113,17 @@ class CSVFileTemplateBuilder extends PureComponent<
nextValues[0].selected = true
}
onUpdateTemplate({...template, values: nextValues})
return nextValues
}
private get validFileExtension(): string {
return '.csv'
private handleBlur = (): void => {
const {template, onUpdateTemplate} = this.props
const {templateValuesString} = this.state
const nextValues = this.getValuesFromString(templateValuesString)
onUpdateTemplate({...template, values: nextValues})
}
}
export default CSVFileTemplateBuilder
export default CSVTemplateBuilder

View File

@ -14,8 +14,7 @@ import {getDeep} from 'src/utils/wrappers'
import {notify as notifyActionCreator} from 'src/shared/actions/notifications'
import DatabasesTemplateBuilder from 'src/tempVars/components/DatabasesTemplateBuilder'
import CSVManualTemplateBuilder from 'src/tempVars/components/CSVManualTemplateBuilder'
import CSVFileTemplateBuilder from 'src/tempVars/components/CSVFileTemplateBuilder'
import CSVTemplateBuilder from 'src/tempVars/components/CSVTemplateBuilder'
import MeasurementsTemplateBuilder from 'src/tempVars/components/MeasurementsTemplateBuilder'
import FieldKeysTemplateBuilder from 'src/tempVars/components/FieldKeysTemplateBuilder'
import TagKeysTemplateBuilder from 'src/tempVars/components/TagKeysTemplateBuilder'
@ -25,7 +24,6 @@ import MetaQueryTemplateBuilder from 'src/tempVars/components/MetaQueryTemplateB
import {
Template,
TemplateType,
BuilderType,
TemplateBuilderProps,
Source,
RemoteDataState,
@ -57,14 +55,13 @@ interface State {
}
const TEMPLATE_BUILDERS = {
[BuilderType.Databases]: DatabasesTemplateBuilder,
[BuilderType.CSVManual]: CSVManualTemplateBuilder,
[BuilderType.CSVFile]: CSVFileTemplateBuilder,
[BuilderType.Measurements]: MeasurementsTemplateBuilder,
[BuilderType.FieldKeys]: FieldKeysTemplateBuilder,
[BuilderType.TagKeys]: TagKeysTemplateBuilder,
[BuilderType.TagValues]: TagValuesTemplateBuilder,
[BuilderType.MetaQuery]: MetaQueryTemplateBuilder,
[TemplateType.Databases]: DatabasesTemplateBuilder,
[TemplateType.CSV]: CSVTemplateBuilder,
[TemplateType.Measurements]: MeasurementsTemplateBuilder,
[TemplateType.FieldKeys]: FieldKeysTemplateBuilder,
[TemplateType.TagKeys]: TagKeysTemplateBuilder,
[TemplateType.TagValues]: TagValuesTemplateBuilder,
[TemplateType.MetaQuery]: MetaQueryTemplateBuilder,
}
const formatName = name => `:${name.replace(/:/g, '')}:`
@ -175,15 +172,13 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
private get templateBuilder(): ComponentClass<TemplateBuilderProps> {
const {
nextTemplate: {builderType},
nextTemplate: {type},
} = this.state
const component = TEMPLATE_BUILDERS[builderType]
const component = TEMPLATE_BUILDERS[type]
if (!component) {
throw new Error(
`Could not find template builder for type "${builderType}"`
)
throw new Error(`Could not find template builder for type "${type}"`)
}
return component
@ -193,13 +188,13 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
this.setState({nextTemplate})
}
private handleChooseType = ({builderType}) => {
private handleChooseType = ({type}) => {
const {
nextTemplate: {id, tempVar},
} = this.state
const nextNextTemplate = {
...DEFAULT_TEMPLATES[builderType](),
...DEFAULT_TEMPLATES[type](),
id,
tempVar,
}
@ -290,11 +285,11 @@ class TemplateVariableEditor extends PureComponent<Props, State> {
private get dropdownSelection(): string {
const {
nextTemplate: {builderType},
nextTemplate: {type},
} = this.state
return getDeep(
TEMPLATE_TYPES_LIST.filter(t => t.builderType === builderType),
TEMPLATE_TYPES_LIST.filter(t => t.type === type),
'0.text',
''
)

View File

@ -2,54 +2,41 @@ import uuid from 'uuid'
import {TimeRange} from 'src/types/query'
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
import {Template, TemplateType, TemplateValueType, BuilderType} from 'src/types'
import {Template, TemplateType, TemplateValueType} from 'src/types'
interface TemplateTypesListItem {
text: string
type: TemplateType
builderType: BuilderType
}
export const TEMPLATE_TYPES_LIST: TemplateTypesListItem[] = [
{
text: 'Databases',
type: TemplateType.Databases,
builderType: BuilderType.Databases,
},
{
text: 'Measurements',
type: TemplateType.Measurements,
builderType: BuilderType.Measurements,
},
{
text: 'Field Keys',
type: TemplateType.FieldKeys,
builderType: BuilderType.FieldKeys,
},
{
text: 'Tag Keys',
type: TemplateType.TagKeys,
builderType: BuilderType.TagKeys,
},
{
text: 'Tag Values',
type: TemplateType.TagValues,
builderType: BuilderType.TagValues,
},
{
text: 'CSV (Manual Entry)',
type: TemplateType.CSV,
builderType: BuilderType.CSVManual,
},
{
text: 'CSV',
type: TemplateType.CSV,
builderType: BuilderType.CSVFile,
},
{
text: 'Custom Meta Query',
type: TemplateType.MetaQuery,
builderType: BuilderType.MetaQuery,
},
]
@ -77,7 +64,7 @@ interface DefaultTemplates {
}
export const DEFAULT_TEMPLATES: DefaultTemplates = {
[BuilderType.Databases]: () => {
[TemplateType.Databases]: () => {
return {
id: uuid.v4(),
tempVar: '',
@ -89,20 +76,18 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
},
],
type: TemplateType.Databases,
builderType: BuilderType.Databases,
label: '',
query: {
influxql: TEMPLATE_VARIABLE_QUERIES[TemplateType.Databases],
},
}
},
[BuilderType.Measurements]: () => {
[TemplateType.Measurements]: () => {
return {
id: uuid.v4(),
tempVar: '',
values: [],
type: TemplateType.Measurements,
builderType: BuilderType.Measurements,
label: '',
query: {
influxql: TEMPLATE_VARIABLE_QUERIES[TemplateType.Measurements],
@ -110,74 +95,58 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
},
}
},
[BuilderType.CSVManual]: () => {
[TemplateType.CSV]: () => {
return {
id: uuid.v4(),
tempVar: '',
values: [],
builderType: BuilderType.CSVManual,
type: TemplateType.CSV,
label: '',
query: {},
}
},
[BuilderType.CSVFile]: () => {
return {
id: uuid.v4(),
tempVar: '',
values: [],
builderType: BuilderType.CSVFile,
type: TemplateType.CSV,
label: '',
query: {},
}
},
[BuilderType.TagKeys]: () => {
[TemplateType.TagKeys]: () => {
return {
id: uuid.v4(),
tempVar: '',
values: [],
type: TemplateType.TagKeys,
builderType: BuilderType.TagKeys,
label: '',
query: {
influxql: TEMPLATE_VARIABLE_QUERIES[TemplateType.TagKeys],
},
}
},
[BuilderType.FieldKeys]: () => {
[TemplateType.FieldKeys]: () => {
return {
id: uuid.v4(),
tempVar: '',
values: [],
type: TemplateType.FieldKeys,
builderType: BuilderType.FieldKeys,
label: '',
query: {
influxql: TEMPLATE_VARIABLE_QUERIES[TemplateType.FieldKeys],
},
}
},
[BuilderType.TagValues]: () => {
[TemplateType.TagValues]: () => {
return {
id: uuid.v4(),
tempVar: '',
values: [],
type: TemplateType.TagValues,
builderType: BuilderType.TagValues,
label: '',
query: {
influxql: TEMPLATE_VARIABLE_QUERIES[TemplateType.TagValues],
},
}
},
[BuilderType.MetaQuery]: () => {
[TemplateType.MetaQuery]: () => {
return {
id: uuid.v4(),
tempVar: ':my-meta-query:',
values: [],
type: TemplateType.MetaQuery,
builderType: BuilderType.MetaQuery,
label: '',
query: {
influxql: '',

View File

@ -8,7 +8,6 @@ import {
TemplateValue,
URLQueryParams,
TemplateType,
BuilderType,
TemplateValueType,
TemplateUpdate,
TemplateBuilderProps,
@ -107,7 +106,6 @@ export {
JSONFeedData,
AnnotationInterface,
TemplateType,
BuilderType,
TemplateValueType,
TemplateUpdate,
TemplateBuilderProps,

View File

@ -40,26 +40,12 @@ export enum TemplateType {
MetaQuery = 'influxql',
}
export enum BuilderType {
AutoGroupBy = 'autoGroupBy',
Constant = 'constant',
FieldKeys = 'fieldKeys',
Measurements = 'measurements',
TagKeys = 'tagKeys',
TagValues = 'tagValues',
CSVManual = 'csvManual',
CSVFile = 'csvFile',
Databases = 'databases',
MetaQuery = 'influxql',
}
export interface Template {
id: string
tempVar: string
values: TemplateValue[]
type: TemplateType
label: string
builderType?: BuilderType
query?: TemplateQuery
}

View File

@ -9,7 +9,6 @@ import {
QueryConfig,
TemplateType,
TemplateValueType,
BuilderType,
} from 'src/types'
import {
Axes,
@ -205,7 +204,6 @@ export const userDefinedTemplateVariables: Template[] = [
{
tempVar: ':fields:',
type: TemplateType.FieldKeys,
builderType: BuilderType.FieldKeys,
label: '',
values: [
{
@ -264,7 +262,6 @@ export const userDefinedTemplateVariables: Template[] = [
{
tempVar: ':measurements:',
type: TemplateType.Measurements,
builderType: BuilderType.Measurements,
label: '',
values: [
{

View File

@ -1,10 +1,5 @@
import {Source, Template, Dashboard, Cell, CellType} from 'src/types'
import {
SourceLinks,
TemplateType,
BuilderType,
TemplateValueType,
} from 'src/types'
import {SourceLinks, TemplateType, TemplateValueType} from 'src/types'
export const role = {
name: '',
@ -596,7 +591,6 @@ export const hosts = {
export const template: Template = {
id: '1',
type: TemplateType.TagKeys,
builderType: BuilderType.TagKeys,
label: 'test query',
tempVar: ':region:',
query: {

View File

@ -3,7 +3,7 @@ import {shallow} from 'enzyme'
import TemplateControlBar from 'src/tempVars/components/TemplateControlBar'
import TemplateControlDropdown from 'src/tempVars/components/TemplateControlDropdown'
import {TemplateType, TemplateValueType, BuilderType} from 'src/types'
import {TemplateType, TemplateValueType} from 'src/types'
import {source} from 'test/resources'
const defaultProps = {
@ -14,7 +14,6 @@ const defaultProps = {
tempVar: ':alpha:',
label: '',
type: TemplateType.Constant,
builderType: BuilderType.Constant,
values: [
{
value: 'firstValue',