Transformations: Leave first time field out of sort for organize fields (#106862)
* Fix docs * Remove first time field from sorting for consistent indexing, add margin to radio * integrate in with sort * find first * Move lonely test to proper block, add test for logic change, refine logic * bonus points
This commit is contained in:
@@ -201,7 +201,7 @@ Use this transformation to add a new field calculated from two other fields. Eac
|
||||
- **All number fields** - Set the left side of a **Binary operation** to apply the calculation to all number fields.
|
||||
- **As percentile** - If you select **Row index** mode, then the **As percentile** switch appears. This switch allows you to transform the row index as a percentage of the total number of rows.
|
||||
- **Alias** - (Optional) Enter the name of your new field. If you leave this blank, then the field will be named to match the calculation.
|
||||
> **Note:** If a variable will be used in this transformation, the default alias will be interpolated with the value of the variable. Please explicitly define an alias if you would like the alias to not be affected by variable changes.
|
||||
> **Note:** If a variable is used in this transformation, the default alias will be interpolated with the value of the variable. If you want an alias to be unaffected by variable changes, explicitly define the alias.
|
||||
- **Replace all fields** - (Optional) Select this option if you want to hide all other fields and display only your calculated field in the visualization.
|
||||
|
||||
In the example below, we added two fields together and named them Sum.
|
||||
@@ -1131,7 +1131,7 @@ Use this transformation to provide the flexibility to rename, reorder, or hide f
|
||||
|
||||
Grafana displays a list of fields returned by the query, allowing you to perform the following actions:
|
||||
|
||||
- **Set field order mode** - If the mode is **Manual**, you can change the field order by hovering the cursor over a field and dragging the field to its new position. If it's **Auto**, use the **OFF**, **ASC**, and **DESC** options to order by any labels on the field or by the field name. You can then drag the labels and the field name to set the priority of the sorting.
|
||||
- **Set field order mode** - If the mode is **Manual**, you can change the field order by hovering the cursor over a field and dragging the field to its new position. If it's **Auto**, use the **OFF**, **ASC**, and **DESC** options to order by any labels on the field or by the field name. For any field that is sorted **ASC** or **DESC**, you can drag the option to set the priority of the sorting.
|
||||
- **Change field order** - Hover over a field, and when your cursor turns into a hand, drag the field to its new position.
|
||||
- **Hide or show a field** - Use the eye icon next to the field name to toggle the visibility of a specific field.
|
||||
- **Rename fields** - Type a new name in the "Rename <field>" box to customize field names.
|
||||
|
||||
@@ -347,62 +347,119 @@ describe('Order Transformer', () => {
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it.each`
|
||||
labelPodIndex | labelUserIndex | fieldNameIndex | expectedFieldNameOrder
|
||||
${0} | ${1} | ${2} | ${['Series-3', 'Series-1', 'Series-2']}
|
||||
${0} | ${2} | ${1} | ${['Series-3', 'Series-2', 'Series-1']}
|
||||
${1} | ${0} | ${2} | ${['Series-3', 'Series-1', 'Series-2']}
|
||||
${2} | ${0} | ${1} | ${['Series-3', 'Series-1', 'Series-2']}
|
||||
${1} | ${2} | ${0} | ${['Series-3', 'Series-2', 'Series-1']}
|
||||
${2} | ${1} | ${0} | ${['Series-3', 'Series-2', 'Series-1']}
|
||||
`(
|
||||
'When the indexes are label pod: $labelPodIndex / label user: $labelUserIndex / field name: $fieldNameIndex when all of them are sort DESC, then the field order is $expectedFieldNameOrder',
|
||||
async ({ labelPodIndex, labelUserIndex, fieldNameIndex, expectedFieldNameOrder }) => {
|
||||
let items: OrderByItem[] = Array(3);
|
||||
it.each`
|
||||
labelPodIndex | labelUserIndex | fieldNameIndex | expectedFieldNameOrder
|
||||
${0} | ${1} | ${2} | ${['Series-3', 'Series-1', 'Series-2']}
|
||||
${0} | ${2} | ${1} | ${['Series-3', 'Series-2', 'Series-1']}
|
||||
${1} | ${0} | ${2} | ${['Series-3', 'Series-1', 'Series-2']}
|
||||
${2} | ${0} | ${1} | ${['Series-3', 'Series-1', 'Series-2']}
|
||||
${1} | ${2} | ${0} | ${['Series-3', 'Series-2', 'Series-1']}
|
||||
${2} | ${1} | ${0} | ${['Series-3', 'Series-2', 'Series-1']}
|
||||
`(
|
||||
'When the indexes are label pod: $labelPodIndex / label user: $labelUserIndex / field name: $fieldNameIndex when all of them are sort DESC, then the field order is $expectedFieldNameOrder',
|
||||
async ({ labelPodIndex, labelUserIndex, fieldNameIndex, expectedFieldNameOrder }) => {
|
||||
let items: OrderByItem[] = Array(3);
|
||||
|
||||
items[labelPodIndex] = { type: OrderByType.Label, name: 'pod', desc: true };
|
||||
items[labelUserIndex] = { type: OrderByType.Label, name: 'user', desc: true };
|
||||
items[fieldNameIndex] = { type: OrderByType.Name, desc: true };
|
||||
items[labelPodIndex] = { type: OrderByType.Label, name: 'pod', desc: true };
|
||||
items[labelUserIndex] = { type: OrderByType.Label, name: 'user', desc: true };
|
||||
items[fieldNameIndex] = { type: OrderByType.Name, desc: true };
|
||||
|
||||
const cfg: DataTransformerConfig<OrderFieldsTransformerOptions> = {
|
||||
id: DataTransformerID.order,
|
||||
options: {
|
||||
orderByMode: OrderByMode.Auto,
|
||||
orderBy: items,
|
||||
},
|
||||
};
|
||||
|
||||
const data = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{
|
||||
name: 'Series-1',
|
||||
type: FieldType.number,
|
||||
labels: { pod: 123, user: 555 },
|
||||
values: [10.3],
|
||||
},
|
||||
{
|
||||
name: 'Series-2',
|
||||
type: FieldType.number,
|
||||
labels: { pod: 123, user: 312 },
|
||||
values: [100.3],
|
||||
},
|
||||
{
|
||||
name: 'Series-3',
|
||||
labels: { pod: 456, user: 555 },
|
||||
type: FieldType.number,
|
||||
values: [10000.3],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await expect(transformDataFrame([cfg], [data])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const ordered = data[0];
|
||||
expect(ordered.fields.map((f) => f.name)).toEqual(expectedFieldNameOrder);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('should always keep the first time field first', async () => {
|
||||
const data = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [3000] },
|
||||
{ name: 'pressure', type: FieldType.number, values: [10.3] },
|
||||
{ name: 'humidity', type: FieldType.number, values: [10000.3] },
|
||||
],
|
||||
});
|
||||
|
||||
// ascending sort on name means time would be last
|
||||
const cfg: DataTransformerConfig<OrderFieldsTransformerOptions> = {
|
||||
id: DataTransformerID.order,
|
||||
options: {
|
||||
orderByMode: OrderByMode.Auto,
|
||||
orderBy: items,
|
||||
orderBy: [{ type: OrderByType.Name, desc: false }],
|
||||
},
|
||||
};
|
||||
|
||||
const data = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{
|
||||
name: 'Series-1',
|
||||
type: FieldType.number,
|
||||
labels: { pod: 123, user: 555 },
|
||||
values: [10.3],
|
||||
},
|
||||
{
|
||||
name: 'Series-2',
|
||||
type: FieldType.number,
|
||||
labels: { pod: 123, user: 312 },
|
||||
values: [100.3],
|
||||
},
|
||||
{
|
||||
name: 'Series-3',
|
||||
labels: { pod: 456, user: 555 },
|
||||
type: FieldType.number,
|
||||
values: [10000.3],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await expect(transformDataFrame([cfg], [data])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const ordered = data[0];
|
||||
expect(ordered.fields.map((f) => f.name)).toEqual(expectedFieldNameOrder);
|
||||
expect(ordered.fields).toEqual([
|
||||
{
|
||||
config: {},
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: [3000],
|
||||
state: {
|
||||
displayName: 'time',
|
||||
multipleFrames: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
name: 'humidity',
|
||||
type: FieldType.number,
|
||||
values: [10000.3],
|
||||
state: {
|
||||
displayName: 'humidity',
|
||||
multipleFrames: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
config: {},
|
||||
name: 'pressure',
|
||||
type: FieldType.number,
|
||||
values: [10.3],
|
||||
state: {
|
||||
displayName: 'pressure',
|
||||
multipleFrames: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { clone } from 'lodash';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { cacheFieldDisplayNames, getFieldDisplayName } from '../../field/fieldState';
|
||||
import { DataFrame, Field } from '../../types/dataFrame';
|
||||
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
|
||||
import { DataTransformerInfo } from '../../types/transformations';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
@@ -89,8 +89,15 @@ const indexOfField = (fieldName: string, indexByName: Record<string, number>) =>
|
||||
const compare = new Intl.Collator(undefined, { sensitivity: 'base', numeric: true }).compare;
|
||||
|
||||
/** @internal */
|
||||
export const createFieldsOrdererAuto = (orderBy: OrderByItem[]) => (fields: Field[]) =>
|
||||
fields.slice().sort((fieldA, fieldB) => {
|
||||
export const createFieldsOrdererAuto = (orderBy: OrderByItem[]) => (fields: Field[]) => {
|
||||
const firstTimeField = fields.find((f) => f.type === FieldType.time);
|
||||
return fields.slice().sort((fieldA, fieldB) => {
|
||||
if (fieldA === firstTimeField) {
|
||||
return -1;
|
||||
}
|
||||
if (fieldB === firstTimeField) {
|
||||
return 1;
|
||||
}
|
||||
for (let i = 0; i < orderBy.length; i++) {
|
||||
let { type, name = '', desc = false } = orderBy[i];
|
||||
|
||||
@@ -103,6 +110,6 @@ export const createFieldsOrdererAuto = (orderBy: OrderByItem[]) => (fields: Fiel
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1152,7 +1152,7 @@ Use this transformation to provide the flexibility to rename, reorder, or hide f
|
||||
|
||||
Grafana displays a list of fields returned by the query, allowing you to perform the following actions:
|
||||
|
||||
- **Set field order mode** - If the mode is **Manual**, you can change the field order by hovering the cursor over a field and dragging the field to its new position. If it's **Auto**, use the **OFF**, **ASC**, and **DESC** options to order by any labels on the field or by the field name. You can then drag the labels and the field name to set the priority of the sorting.
|
||||
- **Set field order mode** - If the mode is **Manual**, you can change the field order by hovering the cursor over a field and dragging the field to its new position. If it's **Auto**, use the **OFF**, **ASC**, and **DESC** options to order by any labels on the field or by the field name. For any field that is sorted **ASC** or **DESC**, you can drag the option to set the priority of the sorting.
|
||||
- **Change field order** - Hover over a field, and when your cursor turns into a hand, drag the field to its new position.
|
||||
- **Hide or show a field** - Use the eye icon next to the field name to toggle the visibility of a specific field.
|
||||
- **Rename fields** - Type a new name in the "Rename <field>" box to customize field names.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd';
|
||||
import { useCallback, useId, useMemo } from 'react';
|
||||
|
||||
@@ -257,7 +257,7 @@ const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeF
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineFieldRow className={styles.fieldOrderRadio}>
|
||||
<InlineField label={t('transformers.organize-fields-transformer-editor.field-order', 'Field order')}>
|
||||
<RadioButtonGroup
|
||||
options={[
|
||||
@@ -326,6 +326,9 @@ const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeF
|
||||
};
|
||||
|
||||
const getDraggableStyles = (theme: GrafanaTheme2) => ({
|
||||
fieldOrderRadio: css({
|
||||
marginBottom: theme.spacing(1),
|
||||
}),
|
||||
labelsDraggable: css({
|
||||
marginBottom: theme.spacing(3),
|
||||
}),
|
||||
@@ -429,7 +432,7 @@ const DraggableUIOrderByItem = ({ index, item, onChangeSort }: DraggableUIOrderB
|
||||
'Drag and drop to reorder'
|
||||
)}
|
||||
size="lg"
|
||||
className={styles.draggable}
|
||||
className={cx(styles.draggable, { [styles.disabled]: item.order === Order.Off })}
|
||||
/>
|
||||
</span>
|
||||
<Text truncate={true} element="p" variant="bodySmall" weight="bold">
|
||||
@@ -467,6 +470,10 @@ const getFieldNameStyles = (theme: GrafanaTheme2) => ({
|
||||
color: theme.colors.text.maxContrast,
|
||||
},
|
||||
}),
|
||||
disabled: css({
|
||||
color: theme.colors.text.disabled,
|
||||
pointerEvents: 'none',
|
||||
}),
|
||||
});
|
||||
|
||||
const reorderToIndex = (fieldNames: string[], startIndex: number, endIndex: number) => {
|
||||
|
||||
Reference in New Issue
Block a user