mirror of https://github.com/grafana/grafana
parent
889518e8e1
commit
3f1adf1390
@ -0,0 +1,167 @@ |
|||||||
|
// Libraries
|
||||||
|
import React, { PureComponent } from 'react'; |
||||||
|
|
||||||
|
// Utils
|
||||||
|
import { isValidTimeSpan } from 'app/core/utils/rangeutil'; |
||||||
|
|
||||||
|
// Components
|
||||||
|
import { Switch } from 'app/core/components/Switch/Switch'; |
||||||
|
import { Input } from 'app/core/components/Form'; |
||||||
|
import { EventsWithValidation } from 'app/core/components/Form/Input'; |
||||||
|
import { InputStatus } from 'app/core/components/Form/Input'; |
||||||
|
import DataSourceOption from './DataSourceOption'; |
||||||
|
|
||||||
|
// Types
|
||||||
|
import { PanelModel } from '../panel_model'; |
||||||
|
import { ValidationEvents, DataSourceSelectItem } from 'app/types'; |
||||||
|
|
||||||
|
const timeRangeValidationEvents: ValidationEvents = { |
||||||
|
[EventsWithValidation.onBlur]: [ |
||||||
|
{ |
||||||
|
rule: value => { |
||||||
|
if (!value) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return isValidTimeSpan(value); |
||||||
|
}, |
||||||
|
errorMessage: 'Not a valid timespan', |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
||||||
|
|
||||||
|
const emptyToNull = (value: string) => { |
||||||
|
return value === '' ? null : value; |
||||||
|
}; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
panel: PanelModel; |
||||||
|
datasource: DataSourceSelectItem; |
||||||
|
} |
||||||
|
|
||||||
|
export class QueryOptions extends PureComponent<Props> { |
||||||
|
onOverrideTime = (evt, status: InputStatus) => { |
||||||
|
const { value } = evt.target; |
||||||
|
const { panel } = this.props; |
||||||
|
const emptyToNullValue = emptyToNull(value); |
||||||
|
if (status === InputStatus.Valid && panel.timeFrom !== emptyToNullValue) { |
||||||
|
panel.timeFrom = emptyToNullValue; |
||||||
|
panel.refresh(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
onTimeShift = (evt, status: InputStatus) => { |
||||||
|
const { value } = evt.target; |
||||||
|
const { panel } = this.props; |
||||||
|
const emptyToNullValue = emptyToNull(value); |
||||||
|
if (status === InputStatus.Valid && panel.timeShift !== emptyToNullValue) { |
||||||
|
panel.timeShift = emptyToNullValue; |
||||||
|
panel.refresh(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
onToggleTimeOverride = () => { |
||||||
|
const { panel } = this.props; |
||||||
|
panel.hideTimeOverride = !panel.hideTimeOverride; |
||||||
|
panel.refresh(); |
||||||
|
}; |
||||||
|
|
||||||
|
renderOptions() { |
||||||
|
const { datasource, panel } = this.props; |
||||||
|
const { queryOptions } = datasource.meta; |
||||||
|
|
||||||
|
if (!queryOptions) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
const onChangeFn = (panelKey: string) => { |
||||||
|
return (value: string | number) => { |
||||||
|
panel[panelKey] = value; |
||||||
|
panel.refresh(); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const allOptions = { |
||||||
|
cacheTimeout: { |
||||||
|
label: 'Cache timeout', |
||||||
|
placeholder: '60', |
||||||
|
name: 'cacheTimeout', |
||||||
|
value: panel.cacheTimeout, |
||||||
|
tooltipInfo: ( |
||||||
|
<> |
||||||
|
If your time series store has a query cache this option can override the default cache timeout. Specify a |
||||||
|
numeric value in seconds. |
||||||
|
</> |
||||||
|
), |
||||||
|
}, |
||||||
|
maxDataPoints: { |
||||||
|
label: 'Max data points', |
||||||
|
placeholder: 'auto', |
||||||
|
name: 'maxDataPoints', |
||||||
|
value: panel.maxDataPoints, |
||||||
|
tooltipInfo: ( |
||||||
|
<> |
||||||
|
The maximum data points the query should return. For graphs this is automatically set to one data point per |
||||||
|
pixel. |
||||||
|
</> |
||||||
|
), |
||||||
|
}, |
||||||
|
minInterval: { |
||||||
|
label: 'Min time interval', |
||||||
|
placeholder: '0', |
||||||
|
name: 'minInterval', |
||||||
|
value: panel.interval, |
||||||
|
panelKey: 'interval', |
||||||
|
tooltipInfo: ( |
||||||
|
<> |
||||||
|
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '} |
||||||
|
<code>1m</code> if your data is written every minute. Access auto interval via variable{' '} |
||||||
|
<code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can |
||||||
|
be used in math expressions. |
||||||
|
</> |
||||||
|
), |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
return Object.keys(queryOptions).map(key => { |
||||||
|
const options = allOptions[key]; |
||||||
|
return <DataSourceOption key={key} {...options} onChange={onChangeFn(allOptions[key].panelKey || key)} />; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
render = () => { |
||||||
|
const hideTimeOverride = this.props.panel.hideTimeOverride; |
||||||
|
return ( |
||||||
|
<div className="gf-form-inline"> |
||||||
|
{this.renderOptions()} |
||||||
|
|
||||||
|
<div className="gf-form"> |
||||||
|
<span className="gf-form-label">Relative time</span> |
||||||
|
<Input |
||||||
|
type="text" |
||||||
|
className="width-6" |
||||||
|
placeholder="1h" |
||||||
|
onBlur={this.onOverrideTime} |
||||||
|
validationEvents={timeRangeValidationEvents} |
||||||
|
hideErrorMessage={true} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="gf-form"> |
||||||
|
<span className="gf-form-label">Time shift</span> |
||||||
|
<Input |
||||||
|
type="text" |
||||||
|
className="width-6" |
||||||
|
placeholder="1h" |
||||||
|
onBlur={this.onTimeShift} |
||||||
|
validationEvents={timeRangeValidationEvents} |
||||||
|
hideErrorMessage={true} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="gf-form-inline"> |
||||||
|
<Switch label="Hide time info" checked={hideTimeOverride} onChange={this.onToggleTimeOverride} /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
} |
@ -1,97 +0,0 @@ |
|||||||
import React, { PureComponent } from 'react'; |
|
||||||
import { Switch } from 'app/core/components/Switch/Switch'; |
|
||||||
import { Input } from 'app/core/components/Form'; |
|
||||||
import { isValidTimeSpan } from 'app/core/utils/rangeutil'; |
|
||||||
import { ValidationEvents } from 'app/types'; |
|
||||||
import { EventsWithValidation } from 'app/core/components/Form/Input'; |
|
||||||
import { PanelModel } from '../panel_model'; |
|
||||||
import { InputStatus } from 'app/core/components/Form/Input'; |
|
||||||
|
|
||||||
const timeRangeValidationEvents: ValidationEvents = { |
|
||||||
[EventsWithValidation.onBlur]: [ |
|
||||||
{ |
|
||||||
rule: value => { |
|
||||||
if (!value) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
return isValidTimeSpan(value); |
|
||||||
}, |
|
||||||
errorMessage: 'Not a valid timespan', |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
const emptyToNull = (value: string) => { |
|
||||||
return value === '' ? null : value; |
|
||||||
}; |
|
||||||
|
|
||||||
interface Props { |
|
||||||
panel: PanelModel; |
|
||||||
} |
|
||||||
|
|
||||||
export class TimeRangeOptions extends PureComponent<Props> { |
|
||||||
onOverrideTime = (evt, status: InputStatus) => { |
|
||||||
const { value } = evt.target; |
|
||||||
const { panel } = this.props; |
|
||||||
const emptyToNullValue = emptyToNull(value); |
|
||||||
if (status === InputStatus.Valid && panel.timeFrom !== emptyToNullValue) { |
|
||||||
panel.timeFrom = emptyToNullValue; |
|
||||||
panel.refresh(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
onTimeShift = (evt, status: InputStatus) => { |
|
||||||
const { value } = evt.target; |
|
||||||
const { panel } = this.props; |
|
||||||
const emptyToNullValue = emptyToNull(value); |
|
||||||
if (status === InputStatus.Valid && panel.timeShift !== emptyToNullValue) { |
|
||||||
panel.timeShift = emptyToNullValue; |
|
||||||
panel.refresh(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
onToggleTimeOverride = () => { |
|
||||||
const { panel } = this.props; |
|
||||||
panel.hideTimeOverride = !panel.hideTimeOverride; |
|
||||||
panel.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
render = () => { |
|
||||||
const hideTimeOverride = this.props.panel.hideTimeOverride; |
|
||||||
return ( |
|
||||||
<> |
|
||||||
<h5 className="section-heading">Time Range</h5> |
|
||||||
|
|
||||||
<div className="gf-form-group"> |
|
||||||
<div className="gf-form"> |
|
||||||
<span className="gf-form-label width-12">Override relative time</span> |
|
||||||
<Input |
|
||||||
type="text" |
|
||||||
className="gf-form-input max-width-8" |
|
||||||
placeholder="1h" |
|
||||||
onBlur={this.onOverrideTime} |
|
||||||
validationEvents={timeRangeValidationEvents} |
|
||||||
hideErrorMessage={true} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div className="gf-form"> |
|
||||||
<span className="gf-form-label width-12">Add time shift</span> |
|
||||||
<Input |
|
||||||
type="text" |
|
||||||
className="gf-form-input max-width-8" |
|
||||||
placeholder="1h" |
|
||||||
onBlur={this.onTimeShift} |
|
||||||
validationEvents={timeRangeValidationEvents} |
|
||||||
hideErrorMessage={true} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div className="gf-form-inline"> |
|
||||||
<Switch label="Hide time override info" checked={hideTimeOverride} onChange={this.onToggleTimeOverride} /> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</> |
|
||||||
); |
|
||||||
}; |
|
||||||
} |
|
@ -1,37 +1,49 @@ |
|||||||
<div class="editor-row"> |
<div class="panel-option-section"> |
||||||
<div class="section gf-form-group"> |
<div class="panel-option-section__header">Information</div> |
||||||
<h5 class="section-heading">Info</h5> |
<div class="panel-option-section__body"> |
||||||
<div class="gf-form"> |
<div class="section"> |
||||||
<span class="gf-form-label width-7">Title</span> |
<div class="gf-form"> |
||||||
<input type="text" class="gf-form-input width-25" ng-model='ctrl.panel.title' ng-model-onblur></input> |
<span class="gf-form-label width-7">Title</span> |
||||||
</div> |
<input type="text" class="gf-form-input width-25" ng-model='ctrl.panel.title' ng-model-onblur></input> |
||||||
<div class="gf-form gf-form--v-stretch"> |
</div> |
||||||
<span class="gf-form-label width-7">Description</span> |
<gf-form-switch class="gf-form" label-class="width-7" switch-class="max-width-6" label="Transparent" checked="ctrl.panel.transparent" on-change="ctrl.render()"></gf-form-switch> |
||||||
<textarea class="gf-form-input width-25" rows="3" ng-model="ctrl.panel.description" ng-model-onblur placeholder="Panel description, supports markdown & links"></textarea> |
</div> |
||||||
</div> |
<div class="section"> |
||||||
<gf-form-switch class="gf-form" label-class="width-7" switch-class="max-width-6" label="Transparent" checked="ctrl.panel.transparent" on-change="ctrl.render()"></gf-form-switch> |
<div class="gf-form gf-form--v-stretch"> |
||||||
</div> |
<span class="gf-form-label width-7">Description</span> |
||||||
|
<textarea class="gf-form-input width-25" rows="5" ng-model="ctrl.panel.description" ng-model-onblur placeholder="Panel description, supports markdown & links"></textarea> |
||||||
<div class="section gf-form-group"> |
</div> |
||||||
<h5 class="section-heading">Repeat</h5> |
</div> |
||||||
<div class="gf-form"> |
</div> |
||||||
<span class="gf-form-label width-9">For each value of</span> |
</div> |
||||||
<dash-repeat-option panel="ctrl.panel"></dash-repeat-option> |
|
||||||
</div> |
|
||||||
<div class="gf-form" ng-show="ctrl.panel.repeat"> |
|
||||||
<span class="gf-form-label width-9">Direction</span> |
|
||||||
<select class="gf-form-input" ng-model="ctrl.panel.repeatDirection" ng-options="f.value as f.text for f in [{value: 'v', text: 'Vertical'}, {value: 'h', text: 'Horizontal'}]"> |
|
||||||
<option value=""></option> |
|
||||||
</select> |
|
||||||
</div> |
|
||||||
<div class="gf-form" ng-show="ctrl.panel.repeat && ctrl.panel.repeatDirection == 'h'"> |
|
||||||
<span class="gf-form-label width-9">Min width</span> |
|
||||||
<select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]"> |
|
||||||
<option value=""></option> |
|
||||||
</select> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<panel-links-editor panel="ctrl.panel"></panel-links-editor> |
|
||||||
|
|
||||||
|
<div class="panel-option-section"> |
||||||
|
<div class="panel-option-section__header">Repeating</div> |
||||||
|
<div class="panel-option-section__body"> |
||||||
|
<div class="section"> |
||||||
|
<div class="gf-form"> |
||||||
|
<span class="gf-form-label width-9">Repat</span> |
||||||
|
<dash-repeat-option panel="ctrl.panel"></dash-repeat-option> |
||||||
|
</div> |
||||||
|
<div class="gf-form" ng-show="ctrl.panel.repeat"> |
||||||
|
<span class="gf-form-label width-9">Direction</span> |
||||||
|
<select class="gf-form-input" ng-model="ctrl.panel.repeatDirection" ng-options="f.value as f.text for f in [{value: 'v', text: 'Vertical'}, {value: 'h', text: 'Horizontal'}]"> |
||||||
|
<option value=""></option> |
||||||
|
</select> |
||||||
|
</div> |
||||||
|
<div class="gf-form" ng-show="ctrl.panel.repeat && ctrl.panel.repeatDirection == 'h'"> |
||||||
|
<span class="gf-form-label width-9">Min width</span> |
||||||
|
<select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]"> |
||||||
|
<option value=""></option> |
||||||
|
</select> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="panel-option-section"> |
||||||
|
<div class="panel-option-section__header">Drildown Links</div> |
||||||
|
<div class="panel-option-section__body"> |
||||||
|
<panel-links-editor panel="ctrl.panel"></panel-links-editor> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
Loading…
Reference in new issue