mirror of https://github.com/grafana/grafana
Loki: Adds options to the new query builder (#46783)
* Initial commit for loki builder options * Loki query options * Added some basic tests * Update public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> * Updated options * All option changes should trigger query * Fixed ts issue Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>pull/46959/head
parent
3516821012
commit
1c648cb52c
@ -0,0 +1,51 @@ |
||||
import React from 'react'; |
||||
import { fireEvent, render, screen } from '@testing-library/react'; |
||||
import { LokiQuery, LokiQueryType } from '../../types'; |
||||
import { LokiQueryBuilderOptions } from './LokiQueryBuilderOptions'; |
||||
import userEvent from '@testing-library/user-event'; |
||||
|
||||
describe('LokiQueryBuilderOptions', () => { |
||||
it('Can change query type', async () => { |
||||
const { props } = setup(); |
||||
|
||||
screen.getByTitle('Click to edit options').click(); |
||||
expect(screen.getByLabelText('Range')).toBeChecked(); |
||||
|
||||
screen.getByLabelText('Instant').click(); |
||||
|
||||
expect(props.onChange).toHaveBeenCalledWith({ |
||||
...props.query, |
||||
queryType: LokiQueryType.Instant, |
||||
}); |
||||
}); |
||||
|
||||
it('Can change legend format', async () => { |
||||
const { props } = setup(); |
||||
|
||||
screen.getByTitle('Click to edit options').click(); |
||||
|
||||
const element = screen.getByLabelText('Legend'); |
||||
userEvent.type(element, 'asd'); |
||||
fireEvent.keyDown(element, { key: 'Enter', code: 'Enter', charCode: 13 }); |
||||
|
||||
expect(props.onChange).toHaveBeenCalledWith({ |
||||
...props.query, |
||||
legendFormat: 'asd', |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
function setup(queryOverrides: Partial<LokiQuery> = {}) { |
||||
const props = { |
||||
query: { |
||||
refId: 'A', |
||||
expr: '', |
||||
...queryOverrides, |
||||
}, |
||||
onRunQuery: jest.fn(), |
||||
onChange: jest.fn(), |
||||
}; |
||||
|
||||
const { container } = render(<LokiQueryBuilderOptions {...props} />); |
||||
return { container, props }; |
||||
} |
||||
@ -0,0 +1,117 @@ |
||||
import React from 'react'; |
||||
import { EditorRow, EditorField } from '@grafana/experimental'; |
||||
import { SelectableValue } from '@grafana/data'; |
||||
import { RadioButtonGroup, Select } from '@grafana/ui'; |
||||
import { LokiQuery, LokiQueryType } from '../../types'; |
||||
import { QueryOptionGroup } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryOptionGroup'; |
||||
import { preprocessMaxLines, queryTypeOptions, RESOLUTION_OPTIONS } from '../../components/LokiOptionFields'; |
||||
import { getLegendModeLabel } from 'app/plugins/datasource/prometheus/querybuilder/components/PromQueryLegendEditor'; |
||||
import { AutoSizeInput } from 'app/plugins/datasource/prometheus/querybuilder/shared/AutoSizeInput'; |
||||
import { isMetricsQuery } from '../../datasource'; |
||||
|
||||
export interface Props { |
||||
query: LokiQuery; |
||||
onChange: (update: LokiQuery) => void; |
||||
onRunQuery: () => void; |
||||
} |
||||
|
||||
export const LokiQueryBuilderOptions = React.memo<Props>(({ query, onChange, onRunQuery }) => { |
||||
const onQueryTypeChange = (value: LokiQueryType) => { |
||||
onChange({ ...query, queryType: value }); |
||||
onRunQuery(); |
||||
}; |
||||
|
||||
const onResolutionChange = (option: SelectableValue<number>) => { |
||||
onChange({ ...query, resolution: option.value }); |
||||
onRunQuery(); |
||||
}; |
||||
|
||||
const onLegendFormatChanged = (evt: React.FormEvent<HTMLInputElement>) => { |
||||
onChange({ ...query, legendFormat: evt.currentTarget.value }); |
||||
onRunQuery(); |
||||
}; |
||||
|
||||
function onMaxLinesChange(e: React.SyntheticEvent<HTMLInputElement>) { |
||||
const newMaxLines = preprocessMaxLines(e.currentTarget.value); |
||||
if (query.maxLines !== newMaxLines) { |
||||
onChange({ ...query, maxLines: newMaxLines }); |
||||
onRunQuery(); |
||||
} |
||||
} |
||||
|
||||
let queryType = query.queryType ?? (query.instant ? LokiQueryType.Instant : LokiQueryType.Range); |
||||
let showMaxLines = !isMetricsQuery(query.expr); |
||||
|
||||
return ( |
||||
<EditorRow> |
||||
<QueryOptionGroup title="Options" collapsedInfo={getCollapsedInfo(query, queryType, showMaxLines)}> |
||||
<EditorField |
||||
label="Legend" |
||||
tooltip="Series name override or template. Ex. {{hostname}} will be replaced with label value for hostname." |
||||
> |
||||
<AutoSizeInput |
||||
placeholder="{{label}}" |
||||
id="loki-query-editor-legend-format" |
||||
type="string" |
||||
minWidth={14} |
||||
defaultValue={query.legendFormat} |
||||
onCommitChange={onLegendFormatChanged} |
||||
/> |
||||
</EditorField> |
||||
<EditorField label="Type"> |
||||
<RadioButtonGroup |
||||
id="options.query.type" |
||||
options={queryTypeOptions} |
||||
value={queryType} |
||||
onChange={onQueryTypeChange} |
||||
/> |
||||
</EditorField> |
||||
{showMaxLines && ( |
||||
<EditorField label="Line limit" tooltip="Upper limit for number of log lines returned by query."> |
||||
<AutoSizeInput |
||||
className="width-4" |
||||
placeholder="auto" |
||||
type="number" |
||||
min={0} |
||||
defaultValue={query.maxLines?.toString() ?? ''} |
||||
onCommitChange={onMaxLinesChange} |
||||
/> |
||||
</EditorField> |
||||
)} |
||||
<EditorField label="Resolution"> |
||||
<Select |
||||
isSearchable={false} |
||||
onChange={onResolutionChange} |
||||
options={RESOLUTION_OPTIONS} |
||||
value={query.resolution || 1} |
||||
aria-label="Select resolution" |
||||
menuShouldPortal |
||||
/> |
||||
</EditorField> |
||||
</QueryOptionGroup> |
||||
</EditorRow> |
||||
); |
||||
}); |
||||
|
||||
function getCollapsedInfo(query: LokiQuery, queryType: LokiQueryType, showMaxLines: boolean): string[] { |
||||
const queryTypeLabel = queryTypeOptions.find((x) => x.value === queryType); |
||||
const resolutionLabel = RESOLUTION_OPTIONS.find((x) => x.value === (query.resolution ?? 1)); |
||||
|
||||
const items: string[] = []; |
||||
|
||||
items.push(`Legend: ${getLegendModeLabel(query.legendFormat)}`); |
||||
|
||||
if (query.resolution) { |
||||
items.push(`Resolution: ${resolutionLabel?.label}`); |
||||
} |
||||
|
||||
items.push(`Type: ${queryTypeLabel?.label}`); |
||||
|
||||
if (showMaxLines && query.maxLines) { |
||||
items.push(`Line limit: ${query.maxLines}`); |
||||
} |
||||
|
||||
return items; |
||||
} |
||||
|
||||
LokiQueryBuilderOptions.displayName = 'LokiQueryBuilderOptions'; |
||||
@ -0,0 +1,38 @@ |
||||
import React from 'react'; |
||||
import { testIds } from '../../components/LokiQueryEditor'; |
||||
import { useStyles2 } from '@grafana/ui'; |
||||
import { css } from '@emotion/css'; |
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
import { LokiQueryEditorProps } from '../../components/types'; |
||||
import { LokiQueryField } from '../../components/LokiQueryField'; |
||||
|
||||
export function LokiQueryCodeEditor({ query, datasource, range, onRunQuery, onChange, data }: LokiQueryEditorProps) { |
||||
const styles = useStyles2(getStyles); |
||||
|
||||
return ( |
||||
<div className={styles.wrapper}> |
||||
<LokiQueryField |
||||
datasource={datasource} |
||||
query={query} |
||||
range={range} |
||||
onRunQuery={onRunQuery} |
||||
onChange={onChange} |
||||
history={[]} |
||||
data={data} |
||||
data-testid={testIds.editor} |
||||
/> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => { |
||||
return { |
||||
// This wrapper styling can be removed after the old PromQueryEditor is removed.
|
||||
// This is removing margin bottom on the old legacy inline form styles
|
||||
wrapper: css` |
||||
.gf-form { |
||||
margin-bottom: 0; |
||||
} |
||||
`,
|
||||
}; |
||||
}; |
||||
Loading…
Reference in new issue