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
|
// Libraries
|
||||||
import React, { memo } from 'react'; |
import React, { PureComponent } from 'react'; |
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data'; |
import { AbsoluteTimeRange, QueryEditorProps, PanelData } from '@grafana/data'; |
||||||
|
import { InlineFormLabel } from '@grafana/ui'; |
||||||
import { LokiDatasource } from '../datasource'; |
import { LokiDatasource } from '../datasource'; |
||||||
import { LokiQuery } from '../types'; |
import { LokiQuery } from '../types'; |
||||||
import { LokiQueryField } from './LokiQueryField'; |
import { LokiQueryField } from './LokiQueryField'; |
||||||
|
|
||||||
type Props = QueryEditorProps<LokiDatasource, LokiQuery>; |
type Props = QueryEditorProps<LokiDatasource, LokiQuery>; |
||||||
|
|
||||||
export const LokiQueryEditor = memo(function LokiQueryEditor(props: Props) { |
interface State { |
||||||
const { query, data, datasource, onChange, onRunQuery } = props; |
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) { |
constructor(props: Props) { |
||||||
const { range } = data.request; |
super(props); |
||||||
absolute = { |
// Use default query to prevent undefined input values
|
||||||
from: range.from.valueOf(), |
const defaultQuery: Partial<LokiQuery> = { expr: '', legendFormat: '' }; |
||||||
to: range.to.valueOf(), |
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, |
from: Date.now() - 10000, |
||||||
to: Date.now(), |
to: Date.now(), |
||||||
}; |
}; |
||||||
} |
}; |
||||||
|
|
||||||
|
onFieldChange = (query: LokiQuery, override?: any) => { |
||||||
|
this.query.expr = query.expr; |
||||||
|
}; |
||||||
|
|
||||||
return ( |
onLegendChange = (e: React.SyntheticEvent<HTMLInputElement>) => { |
||||||
<div> |
const legendFormat = e.currentTarget.value; |
||||||
<LokiQueryField |
this.query.legendFormat = legendFormat; |
||||||
datasource={datasource} |
this.setState({ legendFormat }); |
||||||
query={query} |
}; |
||||||
onChange={onChange} |
|
||||||
onRunQuery={onRunQuery} |
onRunQuery = () => { |
||||||
history={[]} |
const { query } = this; |
||||||
data={data} |
this.props.onChange(query); |
||||||
absoluteRange={absolute} |
this.props.onRunQuery(); |
||||||
/> |
}; |
||||||
</div> |
|
||||||
); |
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; |
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