Skip to content

Commit

Permalink
Field: getFieldTitle as field / series display identity and use it in…
Browse files Browse the repository at this point in the history
… all field name matchers & field / series name displays (grafana#24024)

* common title handling

* show labels

* update comment

* Update changelog for v7.0.0-beta1 (grafana#24007)

Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com>
Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>

* verify-repo-update: Fix Dockerfile.deb (grafana#24030)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* CircleCI: Upgrade build pipeline tool (grafana#24021)

* CircleCI: Upgrade build pipeline tool

* Devenv: ignore enterprise (grafana#24037)

* Add header icon to Add data source page (grafana#24033)

* latest.json: Update testing version (grafana#24038)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix login page redirected from password reset (grafana#24032)

* Storybook: Rewrite stories to CSF (grafana#23989)

* ColorPicker to CSF format

* Convert stories to CSF

* Do not export ClipboardButton

* Update ConfirmButton

* Remove unused imports

* Fix feedback

* changelog enterprise 7.0.0-beta1 (grafana#24039)

* CircleCI: Bump grafana/build-container revision (grafana#24043)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Changelog: Updates changelog with more feature details (grafana#24040)

* Changelog: Updates changelog with more feature details

* spell fix

* spell fix

* Updates

* Readme update

* Updates

* Select: fixes so component loses focus on selecting value or pressing outside of input. (grafana#24008)

* changed the value container to a class component to get it to work with focus (maybe something with context?).

* added e2e tests to verify that the select focus is working as it should.

* fixed according to feedback.

* updated snapshot.

* Devenv: add remote renderer to grafana (grafana#24050)

* NewPanelEditor: minor UI twekas (grafana#24042)

* Forward ref for tabs, use html props

* Inspect:  add inspect label to drawer title

* Add tooltips to sidebar pane tabs, copy changes

* Remove unused import

* Place tooltips over tabs

* Inspector: dont show transformations select if there is only one data frame

* Review

* Changelog: Add a breaking change (grafana#24051)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* CircleCI: Unpin grafana/docs-base (grafana#24054)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Search: close overlay on Esc press (grafana#24003)

* Search: Close on Esc

* Search: Increase bottom padding for the last item in section

* Search: Move closing search to keybindingsSrv

* Search: Fix folder view

* Search: Do not move folders if already in folder

* Docs: Adds deprecation notice to changelog and docs for scripted dashboards (grafana#24060)

* Update CHANGELOG.md (grafana#24047)

Fix typo

Co-authored-by: Daniel Lee <dan.limerick@gmail.com>

* Documentation: Alternative Team Sync Wording (grafana#23960)

* Alternative wording for team sync docs

Signed-off-by: Joe Elliott <number101010@gmail.com>

* Update docs/sources/auth/team-sync.md

Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

* Fix misspell issues (grafana#23905)

* Fix misspell issues

See,
$ golangci-lint run --timeout 10m --disable-all -E misspell ./...

Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>

* Fix codespell issues

See,
$ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm'

Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>

* ci please?

* non-empty commit - ci?

* Trigger build

Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>

* fix compile error

* better series display

* better display

* now with prometheus and loki

* a few more tests

* Improvements and tests

* thinking

* More advanced and smart default title generation

* Another fix

* Progress but dam this will be hard

* Reverting the time series Value field name change

* revert revert going in circles

* add a field state object

* Use state title when converting back to legacy format

* Improved the join (series to columsn) transformer

* Got tests running again

* Rewrite of seriesToColums that simplifies and fixing tests

* Fixed the tricky problem of multiple time field when not used in join

* Prometheus: Restoring prometheus formatting

* Graphite: Disable Grafana's series naming

* fixed imports

* Fixed tests and made rename transform change title instead

* Fixing more tests

* fix more tests

* fixed import issue

* Fixed more circular dependencies

* Renamed to getFieldTitle

* More rename

* Review feedback

* Fix for showing field title in calculate field transformer

* fieldOverride: Make it clear that state title after applying defaults & overrides

* Fixed ts issue

* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com>
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
Co-authored-by: Joe Elliott <joe.elliott@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Mario Trangoni <mario@mariotrangoni.de>
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
  • Loading branch information
20 people authored and pull[bot] committed May 7, 2020
1 parent 12ec9e2 commit 39a6cd5
Show file tree
Hide file tree
Showing 47 changed files with 910 additions and 366 deletions.
21 changes: 20 additions & 1 deletion packages/grafana-data/src/dataframe/processDataFrame.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe('toDataFrame', () => {
],
};
let series = toDataFrame(input1);
expect(series.fields[1].name).toBe(input1.target);
expect(series.name).toBe(input1.target);
expect(series.fields[1].name).toBe('Value');

const v0 = series.fields[0].values;
const v1 = series.fields[1].values;
Expand Down Expand Up @@ -182,6 +183,24 @@ describe('SerisData backwards compatibility', () => {
expect(roundtrip.target).toBe(timeseries.target);
});

it('can convert TimeSeries to series and back again with tags should render name with tags', () => {
const timeseries = {
target: 'Series A',
tags: { server: 'ServerA', job: 'app' },
datapoints: [
[100, 1],
[200, 2],
],
};
const series = toDataFrame(timeseries);
expect(isDataFrame(timeseries)).toBeFalsy();
expect(isDataFrame(series)).toBeTruthy();

const roundtrip = toLegacyResponseData(series) as TimeSeries;
expect(isDataFrame(roundtrip)).toBeFalsy();
expect(roundtrip.target).toBe('{job="app", server="ServerA"}');
});

it('can convert empty table to DataFrame then back to legacy', () => {
const table = {
columns: [],
Expand Down
45 changes: 27 additions & 18 deletions packages/grafana-data/src/dataframe/processDataFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import {
TimeSeriesValue,
FieldDTO,
DataFrameDTO,
TIME_SERIES_FIELD_NAME,
} from '../types/index';
import { isDateTime } from '../datetime/moment_wrapper';
import { ArrayVector } from '../vector/ArrayVector';
import { MutableDataFrame } from './MutableDataFrame';
import { SortedVector } from '../vector/SortedVector';
import { ArrayDataFrame } from './ArrayDataFrame';
import { getFieldTitle } from '../field/fieldState';

function convertTableToDataFrame(table: TableData): DataFrame {
const fields = table.columns.map(c => {
Expand Down Expand Up @@ -61,6 +63,7 @@ function convertTableToDataFrame(table: TableData): DataFrame {
function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {
const times: number[] = [];
const values: TimeSeriesValue[] = [];

for (const point of timeSeries.datapoints) {
values.push(point[0]);
times.push(point[1] as number);
Expand All @@ -74,7 +77,7 @@ function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {
values: new ArrayVector<number>(times),
},
{
name: timeSeries.target || 'Value',
name: TIME_SERIES_FIELD_NAME,
type: FieldType.number,
config: {
unit: timeSeries.unit,
Expand All @@ -84,6 +87,10 @@ function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {
},
];

if (timeSeries.title) {
(fields[1].config as FieldConfig).title = timeSeries.title;
}

return {
name: timeSeries.target,
refId: timeSeries.refId,
Expand Down Expand Up @@ -111,7 +118,7 @@ function convertGraphSeriesToDataFrame(graphSeries: GraphSeriesXY): DataFrame {
name: graphSeries.label,
fields: [
{
name: graphSeries.label || 'Value',
name: graphSeries.label || TIME_SERIES_FIELD_NAME,
type: FieldType.number,
config: {},
values: x,
Expand Down Expand Up @@ -312,18 +319,20 @@ export const toLegacyResponseData = (frame: DataFrame): TimeSeries | TableData =
const { timeField, timeIndex } = getTimeField(frame);
if (timeField) {
const valueIndex = timeIndex === 0 ? 1 : 0;
const valueField = fields[valueIndex];
const timeField = fields[timeIndex!];

// Make sure it is [value,time]
for (let i = 0; i < rowCount; i++) {
rows.push([
fields[valueIndex].values.get(i), // value
fields[timeIndex!].values.get(i), // time
valueField.values.get(i), // value
timeField.values.get(i), // time
]);
}

return {
alias: fields[valueIndex].name || frame.name,
target: fields[valueIndex].name || frame.name,
alias: frame.name,
target: getFieldTitle(valueField, frame),
datapoints: rows,
unit: fields[0].config ? fields[0].config.unit : undefined,
refId: frame.refId,
Expand Down Expand Up @@ -432,18 +441,6 @@ export function reverseDataFrame(data: DataFrame): DataFrame {
};
}

export const getTimeField = (series: DataFrame): { timeField?: Field; timeIndex?: number } => {
for (let i = 0; i < series.fields.length; i++) {
if (series.fields[i].type === FieldType.time) {
return {
timeField: series.fields[i],
timeIndex: i,
};
}
}
return {};
};

/**
* Wrapper to get an array from each field value
*/
Expand Down Expand Up @@ -487,3 +484,15 @@ export function toDataFrameDTO(data: DataFrame): DataFrameDTO {
name: data.name,
};
}

export const getTimeField = (series: DataFrame): { timeField?: Field; timeIndex?: number } => {
for (let i = 0; i < series.fields.length; i++) {
if (series.fields[i].type === FieldType.time) {
return {
timeField: series.fields[i],
timeIndex: i,
};
}
}
return {};
};
40 changes: 13 additions & 27 deletions packages/grafana-data/src/field/fieldDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,17 @@ export interface ReduceDataOptions {
// TODO: use built in variables, same as for data links?
export const VAR_SERIES_NAME = '__series.name';
export const VAR_FIELD_NAME = '__field.name';
export const VAR_FIELD_LABELS = '__field.labels';
export const VAR_CALC = '__calc';
export const VAR_CELL_PREFIX = '__cell_'; // consistent with existing table templates

function getTitleTemplate(title: string | undefined, stats: string[], data?: DataFrame[]): string {
// If the title exists, use it as a template variable
if (title) {
return title;
}
if (!data || !data.length) {
return 'No Data';
}

let fieldCount = 0;
for (const field of data[0].fields) {
if (field.type === FieldType.number) {
fieldCount++;
}
}

function getTitleTemplate(stats: string[]): string {
const parts: string[] = [];
if (stats.length > 1) {
parts.push('${' + VAR_CALC + '}');
}
if (data.length > 1) {
parts.push('${' + VAR_SERIES_NAME + '}');
}
if (fieldCount > 1 || !parts.length) {
parts.push('${' + VAR_FIELD_NAME + '}');
}

parts.push('${' + VAR_FIELD_NAME + '}');

return parts.join(' ');
}
Expand Down Expand Up @@ -108,8 +90,8 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
const data = options.data;
let hitLimit = false;
const limit = reduceOptions.limit ? reduceOptions.limit : DEFAULT_FIELD_DISPLAY_VALUES_LIMIT;
const defaultTitle = getTitleTemplate(fieldConfig.defaults.title, calcs, data);
const scopedVars: ScopedVars = {};
const defaultTitle = getTitleTemplate(calcs);

for (let s = 0; s < data.length && !hitLimit; s++) {
const series = data[s]; // Name is already set
Expand All @@ -120,11 +102,14 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
for (let i = 0; i < series.fields.length && !hitLimit; i++) {
const field = series.fields[i];
const fieldLinksSupplier = field.getLinks;
// Show all number fields

// To filter out time field, need an option for this
if (field.type !== FieldType.number) {
continue;
}

const config = field.config; // already set by the prepare task
const title = field.config.title ?? defaultTitle;

const display =
field.display ??
Expand All @@ -134,7 +119,6 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
timeZone,
});

const title = config.title ? config.title : defaultTitle;
// Show all rows
if (reduceOptions.values) {
const usesCellValues = title.indexOf(VAR_CELL_PREFIX) >= 0;
Expand All @@ -151,9 +135,10 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
};
}
}

const displayValue = display(field.values.get(j));
displayValue.title = replaceVariables(title, {
...field.config.scopedVars, // series and field scoped vars
...field.state?.scopedVars, // series and field scoped vars
...scopedVars,
});

Expand Down Expand Up @@ -197,9 +182,10 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
scopedVars[VAR_CALC] = { value: calc, text: calc };
const displayValue = display(results[calc]);
displayValue.title = replaceVariables(title, {
...field.config.scopedVars, // series and field scoped vars
...field.state?.scopedVars, // series and field scoped vars
...scopedVars,
});

values.push({
name: calc,
field: config,
Expand Down
30 changes: 19 additions & 11 deletions packages/grafana-data/src/field/fieldOverrides.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Registry } from '../utils';
import { mockStandardProperties } from '../utils/tests/mockStandardProperties';
import { FieldMatcherID } from '../transformations';
import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
import { getFieldTitle } from './fieldState';

const property1 = {
id: 'custom.property1', // Match field properties
Expand Down Expand Up @@ -111,12 +112,14 @@ describe('applyFieldOverrides', () => {
fieldConfigRegistry: new FieldConfigOptionsRegistry(),
});

expect(withOverrides[0].fields[0].config.scopedVars).toMatchInlineSnapshot(`
expect(withOverrides[0].fields[0].state!.scopedVars).toMatchInlineSnapshot(`
Object {
"__field": Object {
"text": "Field",
"value": Object {
"name": "message",
"label": undefined,
"labels": "",
"name": "A message",
},
},
"__series": Object {
Expand All @@ -128,12 +131,14 @@ describe('applyFieldOverrides', () => {
}
`);

expect(withOverrides[1].fields[0].config.scopedVars).toMatchInlineSnapshot(`
expect(withOverrides[1].fields[0].state!.scopedVars).toMatchInlineSnapshot(`
Object {
"__field": Object {
"text": "Field",
"value": Object {
"name": "info",
"label": undefined,
"labels": "",
"name": "B info",
},
},
"__series": Object {
Expand All @@ -152,16 +157,19 @@ describe('applyFieldOverrides', () => {
min: 0,
max: 100,
};

const f1 = {
unit: 'ms',
dateFormat: '', // should be ignored
max: parseFloat('NOPE'), // should be ignored
min: null, // should alo be ignored!
title: 'newTitle',
};

const f: DataFrame = toDataFrame({
fields: [{ type: FieldType.number, name: 'x', config: field, values: [] }],
});

const processed = applyFieldOverrides({
data: [f],
fieldConfig: {
Expand All @@ -172,11 +180,13 @@ describe('applyFieldOverrides', () => {
replaceVariables: v => v,
theme: {} as GrafanaTheme,
})[0];
const out = processed.fields[0].config;

expect(out.min).toEqual(0);
expect(out.max).toEqual(100);
expect(out.unit).toEqual('ms');
const outField = processed.fields[0];

expect(outField.config.min).toEqual(0);
expect(outField.config.max).toEqual(100);
expect(outField.config.unit).toEqual('ms');
expect(getFieldTitle(outField, f)).toEqual('newTitle');
});

it('will apply field overrides', () => {
Expand Down Expand Up @@ -300,10 +310,8 @@ describe('setDynamicConfigValue', () => {
it('applies dynamic config values', () => {
const config = {
title: 'test',
// custom: {
// property1: 1,
// },
};

setDynamicConfigValue(
config,
{
Expand Down
Loading

0 comments on commit 39a6cd5

Please sign in to comment.