mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Fields: __field.name as field name and __field.displayName as displayName (#26531)
* name vs displayName * name vs displayName * add __values * add docs for displayName expressions * Update docs/sources/panels/field-configuration-options.md Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Update docs/sources/panels/field-configuration-options.md Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Update docs/sources/panels/field-configuration-options.md Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Update docs/sources/panels/field-configuration-options.md Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Update docs/sources/panels/field-configuration-options.md Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> * Update docs/sources/panels/field-configuration-options.md Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> Co-authored-by: kyle <kyle@grafana.com> Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
This commit is contained in:
parent
9e5fe8dbdb
commit
ec783fbff4
@ -139,7 +139,19 @@ For more information and instructions, refer to [Data links]({{< relref "../link
|
|||||||
|
|
||||||
Lets you set the display title of all fields. You can use [variables]({{< relref "../variables/templates-and-variables.md" >}}) in the field title.
|
Lets you set the display title of all fields. You can use [variables]({{< relref "../variables/templates-and-variables.md" >}}) in the field title.
|
||||||
|
|
||||||
When multiple stats are shown, this field controls the title in each stat. By default this is the series name and field name. You can use expressions like ${__series.name} or ${**field.name} to use only series name or field name in title or \${**cell_2} to refer to other fields (2 being field/column with index 2).
|
When multiple stats, fields, or series are shown, this field controls the title in each stat. You can use expressions like `${__field.name}` to use only the series name or the field name in title.
|
||||||
|
|
||||||
|
Given a field with a name of Temp, and labels of {"Loc"="PBI", "Sensor"="3"}
|
||||||
|
|
||||||
|
| Expression syntax | Example | Renders to | Explanation |
|
||||||
|
| ---------------------------- | ---------------------- | --------------------------------- | ----------- |
|
||||||
|
| `${__field.displayName}` | Same as syntax | `Temp {Loc="PBI", Sensor="3"}` | Displays the field name, and labels in `{}` if they are present. If there is only one label key in the response, then for the label portion, Grafana displays the value of the label without the enclosing braces. |
|
||||||
|
| `${__field.name}` | Same as syntax | `Temp` | Displays the name of the field (without labels). |
|
||||||
|
| `${__field.labels}` | Same as syntax | `Loc="PBI", Sensor="3"` | Displays the labels without the name. |
|
||||||
|
| `${__field.labels.X}` | `${__field.labels.Loc}` | `PBI` | Displays the value of the specified label key. |
|
||||||
|
| `${__field.labels.__values}` | Same as Syntax | `PBI, 3` | Displays the values of the labels separated by a comma (without label keys). |
|
||||||
|
|
||||||
|
If the value is an empty string after rendering the expression for a particular field, then the default display method is used.
|
||||||
|
|
||||||
### Max
|
### Max
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ export interface ReduceDataOptions {
|
|||||||
|
|
||||||
// TODO: use built in variables, same as for data links?
|
// TODO: use built in variables, same as for data links?
|
||||||
export const VAR_SERIES_NAME = '__series.name';
|
export const VAR_SERIES_NAME = '__series.name';
|
||||||
export const VAR_FIELD_NAME = '__field.name';
|
export const VAR_FIELD_NAME = '__field.displayName'; // Includes the rendered tags and naming strategy
|
||||||
export const VAR_FIELD_LABELS = '__field.labels';
|
export const VAR_FIELD_LABELS = '__field.labels';
|
||||||
export const VAR_CALC = '__calc';
|
export const VAR_CALC = '__calc';
|
||||||
export const VAR_CELL_PREFIX = '__cell_'; // consistent with existing table templates
|
export const VAR_CELL_PREFIX = '__cell_'; // consistent with existing table templates
|
||||||
|
@ -127,11 +127,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
Object {
|
Object {
|
||||||
"__field": Object {
|
"__field": Object {
|
||||||
"text": "Field",
|
"text": "Field",
|
||||||
"value": Object {
|
"value": Object {},
|
||||||
"formattedLabels": "",
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "A message",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"__series": Object {
|
"__series": Object {
|
||||||
"text": "Series",
|
"text": "Series",
|
||||||
@ -146,11 +142,7 @@ describe('applyFieldOverrides', () => {
|
|||||||
Object {
|
Object {
|
||||||
"__field": Object {
|
"__field": Object {
|
||||||
"text": "Field",
|
"text": "Field",
|
||||||
"value": Object {
|
"value": Object {},
|
||||||
"formattedLabels": "",
|
|
||||||
"labels": undefined,
|
|
||||||
"name": "B info",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"__series": Object {
|
"__series": Object {
|
||||||
"text": "Series",
|
"text": "Series",
|
||||||
|
@ -32,10 +32,10 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
|
|||||||
import { DataLinkBuiltInVars, locationUtil } from '../utils';
|
import { DataLinkBuiltInVars, locationUtil } from '../utils';
|
||||||
import { formattedValueToString } from '../valueFormats';
|
import { formattedValueToString } from '../valueFormats';
|
||||||
import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
|
import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
|
||||||
import { formatLabels } from '../utils/labels';
|
|
||||||
import { getFrameDisplayName, getFieldDisplayName } from './fieldState';
|
import { getFrameDisplayName, getFieldDisplayName } from './fieldState';
|
||||||
import { getTimeField } from '../dataframe/processDataFrame';
|
import { getTimeField } from '../dataframe/processDataFrame';
|
||||||
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
||||||
|
import { getTemplateProxyForField } from './templateProxies';
|
||||||
|
|
||||||
interface OverrideProps {
|
interface OverrideProps {
|
||||||
match: FieldMatcher;
|
match: FieldMatcher;
|
||||||
@ -113,11 +113,7 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
|
|||||||
|
|
||||||
fieldScopedVars['__field'] = {
|
fieldScopedVars['__field'] = {
|
||||||
text: 'Field',
|
text: 'Field',
|
||||||
value: {
|
value: getTemplateProxyForField(field, frame, options.data),
|
||||||
name: displayName, // Generally appropriate (may include the series name if useful)
|
|
||||||
formattedLabels: formatLabels(field.labels!),
|
|
||||||
labels: field.labels,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
field.state = {
|
field.state = {
|
||||||
|
32
packages/grafana-data/src/field/templateProxies.test.ts
Normal file
32
packages/grafana-data/src/field/templateProxies.test.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { getTemplateProxyForField } from './templateProxies';
|
||||||
|
import { toDataFrame } from '../dataframe';
|
||||||
|
|
||||||
|
describe('Template proxies', () => {
|
||||||
|
it('supports name and displayName', () => {
|
||||||
|
const frames = [
|
||||||
|
toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: '🔥',
|
||||||
|
config: { displayName: '✨' },
|
||||||
|
labels: {
|
||||||
|
b: 'BBB',
|
||||||
|
a: 'AAA',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const f = getTemplateProxyForField(frames[0].fields[0], frames[0], frames);
|
||||||
|
|
||||||
|
expect(f.name).toEqual('🔥');
|
||||||
|
expect(f.displayName).toEqual('✨');
|
||||||
|
expect(`${f.labels}`).toEqual('a="AAA", b="BBB"');
|
||||||
|
expect(f.labels.__values).toEqual('AAA, BBB');
|
||||||
|
expect(f.labels.a).toEqual('AAA');
|
||||||
|
|
||||||
|
// Deprecated syntax
|
||||||
|
expect(`${f.formattedLabels}`).toEqual('a="AAA", b="BBB"');
|
||||||
|
});
|
||||||
|
});
|
41
packages/grafana-data/src/field/templateProxies.ts
Normal file
41
packages/grafana-data/src/field/templateProxies.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { DataFrame, Field } from '../types';
|
||||||
|
import { getFieldDisplayName } from './fieldState';
|
||||||
|
import { formatLabels } from '../utils/labels';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is created often, and only used when tmplates exist. Using a proxy lets us delay
|
||||||
|
* calculations of the more complex structures (label names) until they are actually used
|
||||||
|
*/
|
||||||
|
export function getTemplateProxyForField(field: Field, frame?: DataFrame, frames?: DataFrame[]): any {
|
||||||
|
return new Proxy(
|
||||||
|
{} as any, // This object shows up in test snapshots
|
||||||
|
{
|
||||||
|
get: (obj: Field, key: string, reciever: any) => {
|
||||||
|
if (key === 'name') {
|
||||||
|
return field.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'displayName') {
|
||||||
|
return getFieldDisplayName(field, frame, frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'labels' || key === 'formattedLabels') {
|
||||||
|
// formattedLabels deprecated
|
||||||
|
if (!field.labels) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...field.labels,
|
||||||
|
__values: Object.values(field.labels)
|
||||||
|
.sort()
|
||||||
|
.join(', '),
|
||||||
|
toString: () => {
|
||||||
|
return formatLabels(field.labels!, '', true);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined; // (field as any)[key]; // any property?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
@ -62,11 +62,14 @@ export function findUniqueLabels(labels: Labels | undefined, commonLabels: Label
|
|||||||
/**
|
/**
|
||||||
* Serializes the given labels to a string.
|
* Serializes the given labels to a string.
|
||||||
*/
|
*/
|
||||||
export function formatLabels(labels: Labels, defaultValue = ''): string {
|
export function formatLabels(labels: Labels, defaultValue = '', withoutBraces?: boolean): string {
|
||||||
if (!labels || Object.keys(labels).length === 0) {
|
if (!labels || Object.keys(labels).length === 0) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
const labelKeys = Object.keys(labels).sort();
|
const labelKeys = Object.keys(labels).sort();
|
||||||
const cleanSelector = labelKeys.map(key => `${key}="${labels[key]}"`).join(', ');
|
const cleanSelector = labelKeys.map(key => `${key}="${labels[key]}"`).join(', ');
|
||||||
|
if (withoutBraces) {
|
||||||
|
return cleanSelector;
|
||||||
|
}
|
||||||
return ['{', cleanSelector, '}'].join('');
|
return ['{', cleanSelector, '}'].join('');
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user