mirror of https://github.com/grafana/grafana
Loki: Allow aliasing Loki queries in dashboard (#25706)
* Loki: Add Legend field to query editor * Loki: Basic test for legend field * Loki: Mention legend is only for metric queries * Loki: Fix absolute timerange never updatingpull/25996/head^2
parent
73e82af4df
commit
5789f80e14
@ -0,0 +1,66 @@ |
||||
import React from 'react'; |
||||
import { shallow } from 'enzyme'; |
||||
import { toUtc } from '@grafana/data'; |
||||
|
||||
import { LokiQueryEditor } from './LokiQueryEditor'; |
||||
import { LokiDatasource } from '../datasource'; |
||||
import { LokiQuery } from '../types'; |
||||
|
||||
const createMockRequestRange = (from: string, to: string) => { |
||||
return { |
||||
request: { |
||||
range: { |
||||
from: toUtc(from, 'YYYY-MM-DD'), |
||||
to: toUtc(to, 'YYYY-MM-DD'), |
||||
}, |
||||
}, |
||||
}; |
||||
}; |
||||
|
||||
const setup = (propOverrides?: object) => { |
||||
const datasourceMock: unknown = {}; |
||||
const datasource: LokiDatasource = datasourceMock as LokiDatasource; |
||||
const onRunQuery = jest.fn(); |
||||
const onChange = jest.fn(); |
||||
|
||||
const query: LokiQuery = { |
||||
expr: '', |
||||
refId: 'A', |
||||
legendFormat: 'My Legend', |
||||
}; |
||||
|
||||
const data = createMockRequestRange('2020-01-01', '2020-01-02'); |
||||
|
||||
const props: any = { |
||||
datasource, |
||||
onChange, |
||||
onRunQuery, |
||||
query, |
||||
data, |
||||
}; |
||||
|
||||
Object.assign(props, propOverrides); |
||||
|
||||
const wrapper = shallow(<LokiQueryEditor {...props} />); |
||||
const instance = wrapper.instance() as LokiQueryEditor; |
||||
|
||||
return { |
||||
instance, |
||||
wrapper, |
||||
}; |
||||
}; |
||||
|
||||
describe('Render LokiQueryEditor with legend', () => { |
||||
it('should render', () => { |
||||
const { wrapper } = setup(); |
||||
expect(wrapper).toMatchSnapshot(); |
||||
}); |
||||
|
||||
it('should update absolute timerange', () => { |
||||
const { wrapper } = setup(); |
||||
wrapper.setProps({ |
||||
data: createMockRequestRange('2019-01-01', '2020-01-02'), |
||||
}); |
||||
expect(wrapper).toMatchSnapshot(); |
||||
}); |
||||
}); |
@ -1,45 +1,105 @@ |
||||
// Libraries
|
||||
import React, { memo } from 'react'; |
||||
import React, { PureComponent } from 'react'; |
||||
|
||||
// Types
|
||||
import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data'; |
||||
import { AbsoluteTimeRange, QueryEditorProps, PanelData } from '@grafana/data'; |
||||
import { InlineFormLabel } from '@grafana/ui'; |
||||
import { LokiDatasource } from '../datasource'; |
||||
import { LokiQuery } from '../types'; |
||||
import { LokiQueryField } from './LokiQueryField'; |
||||
|
||||
type Props = QueryEditorProps<LokiDatasource, LokiQuery>; |
||||
|
||||
export const LokiQueryEditor = memo(function LokiQueryEditor(props: Props) { |
||||
const { query, data, datasource, onChange, onRunQuery } = props; |
||||
interface State { |
||||
legendFormat: string; |
||||
} |
||||
|
||||
let absolute: AbsoluteTimeRange; |
||||
export class LokiQueryEditor extends PureComponent<Props, State> { |
||||
// Query target to be modified and used for queries
|
||||
query: LokiQuery; |
||||
|
||||
if (data && data.request) { |
||||
const { range } = data.request; |
||||
absolute = { |
||||
from: range.from.valueOf(), |
||||
to: range.to.valueOf(), |
||||
constructor(props: Props) { |
||||
super(props); |
||||
// Use default query to prevent undefined input values
|
||||
const defaultQuery: Partial<LokiQuery> = { expr: '', legendFormat: '' }; |
||||
const query = Object.assign({}, defaultQuery, props.query); |
||||
this.query = query; |
||||
// Query target properties that are fully controlled inputs
|
||||
this.state = { |
||||
// Fully controlled text inputs
|
||||
legendFormat: query.legendFormat, |
||||
}; |
||||
} else { |
||||
absolute = { |
||||
} |
||||
|
||||
calcAbsoluteRange = (data: PanelData): AbsoluteTimeRange => { |
||||
if (data && data.request) { |
||||
const { range } = data.request; |
||||
return { |
||||
from: range.from.valueOf(), |
||||
to: range.to.valueOf(), |
||||
}; |
||||
} |
||||
|
||||
return { |
||||
from: Date.now() - 10000, |
||||
to: Date.now(), |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
onFieldChange = (query: LokiQuery, override?: any) => { |
||||
this.query.expr = query.expr; |
||||
}; |
||||
|
||||
return ( |
||||
<div> |
||||
<LokiQueryField |
||||
datasource={datasource} |
||||
query={query} |
||||
onChange={onChange} |
||||
onRunQuery={onRunQuery} |
||||
history={[]} |
||||
data={data} |
||||
absoluteRange={absolute} |
||||
/> |
||||
</div> |
||||
); |
||||
}); |
||||
onLegendChange = (e: React.SyntheticEvent<HTMLInputElement>) => { |
||||
const legendFormat = e.currentTarget.value; |
||||
this.query.legendFormat = legendFormat; |
||||
this.setState({ legendFormat }); |
||||
}; |
||||
|
||||
onRunQuery = () => { |
||||
const { query } = this; |
||||
this.props.onChange(query); |
||||
this.props.onRunQuery(); |
||||
}; |
||||
|
||||
render() { |
||||
const { datasource, query, data } = this.props; |
||||
const { legendFormat } = this.state; |
||||
|
||||
return ( |
||||
<div> |
||||
<LokiQueryField |
||||
datasource={datasource} |
||||
query={query} |
||||
onChange={this.onFieldChange} |
||||
onRunQuery={this.onRunQuery} |
||||
history={[]} |
||||
data={data} |
||||
absoluteRange={this.calcAbsoluteRange(data)} |
||||
/> |
||||
|
||||
<div className="gf-form-inline"> |
||||
<div className="gf-form"> |
||||
<InlineFormLabel |
||||
width={7} |
||||
tooltip="Controls the name of the time series, using name or pattern. For example |
||||
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries." |
||||
> |
||||
Legend |
||||
</InlineFormLabel> |
||||
<input |
||||
type="text" |
||||
className="gf-form-input" |
||||
placeholder="legend format" |
||||
value={legendFormat} |
||||
onChange={this.onLegendChange} |
||||
onBlur={this.onRunQuery} |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default LokiQueryEditor; |
||||
|
@ -0,0 +1,115 @@ |
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
||||
|
||||
exports[`Render LokiQueryEditor with legend should render 1`] = ` |
||||
<div> |
||||
<Component |
||||
absoluteRange={ |
||||
Object { |
||||
"from": 1577836800000, |
||||
"to": 1577923200000, |
||||
} |
||||
} |
||||
data={ |
||||
Object { |
||||
"request": Object { |
||||
"range": Object { |
||||
"from": "2020-01-01T00:00:00.000Z", |
||||
"to": "2020-01-02T00:00:00.000Z", |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
datasource={Object {}} |
||||
history={Array []} |
||||
onChange={[Function]} |
||||
onRunQuery={[Function]} |
||||
query={ |
||||
Object { |
||||
"expr": "", |
||||
"legendFormat": "My Legend", |
||||
"refId": "A", |
||||
} |
||||
} |
||||
/> |
||||
<div |
||||
className="gf-form-inline" |
||||
> |
||||
<div |
||||
className="gf-form" |
||||
> |
||||
<Component |
||||
tooltip="Controls the name of the time series, using name or pattern. For example |
||||
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries." |
||||
width={7} |
||||
> |
||||
Legend |
||||
</Component> |
||||
<input |
||||
className="gf-form-input" |
||||
onBlur={[Function]} |
||||
onChange={[Function]} |
||||
placeholder="legend format" |
||||
type="text" |
||||
value="My Legend" |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
`; |
||||
|
||||
exports[`Render LokiQueryEditor with legend should update absolute timerange 1`] = ` |
||||
<div> |
||||
<Component |
||||
absoluteRange={ |
||||
Object { |
||||
"from": 1546300800000, |
||||
"to": 1577923200000, |
||||
} |
||||
} |
||||
data={ |
||||
Object { |
||||
"request": Object { |
||||
"range": Object { |
||||
"from": "2019-01-01T00:00:00.000Z", |
||||
"to": "2020-01-02T00:00:00.000Z", |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
datasource={Object {}} |
||||
history={Array []} |
||||
onChange={[Function]} |
||||
onRunQuery={[Function]} |
||||
query={ |
||||
Object { |
||||
"expr": "", |
||||
"legendFormat": "My Legend", |
||||
"refId": "A", |
||||
} |
||||
} |
||||
/> |
||||
<div |
||||
className="gf-form-inline" |
||||
> |
||||
<div |
||||
className="gf-form" |
||||
> |
||||
<Component |
||||
tooltip="Controls the name of the time series, using name or pattern. For example |
||||
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries." |
||||
width={7} |
||||
> |
||||
Legend |
||||
</Component> |
||||
<input |
||||
className="gf-form-input" |
||||
onBlur={[Function]} |
||||
onChange={[Function]} |
||||
placeholder="legend format" |
||||
type="text" |
||||
value="My Legend" |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
`; |
Loading…
Reference in new issue