Transformations: Add "Auto" mode to Organize Fields (#103055)

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
pull/106596/head
Kristina 7 months ago committed by GitHub
parent 96f1582c36
commit 5e9bb9f506
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      docs/sources/panels-visualizations/query-transform-data/transform-data/index.md
  2. 8
      packages/grafana-data/src/internal/index.ts
  3. 170
      packages/grafana-data/src/transformations/transformers/order.test.ts
  4. 96
      packages/grafana-data/src/transformations/transformers/order.ts
  5. 4
      packages/grafana-data/src/transformations/transformers/organize.test.ts
  6. 1
      public/app/features/transformers/docs/content.ts
  7. 332
      public/app/features/transformers/editors/OrganizeFieldsTransformerEditor.tsx
  8. 8
      public/locales/en-US/grafana.json

@ -1131,6 +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.
- **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.

@ -63,7 +63,13 @@ export { LabelsToFieldsMode, type LabelsToFieldsOptions } from '../transformatio
export { type LimitTransformerOptions } from '../transformations/transformers/limit';
export { type MergeTransformerOptions } from '../transformations/transformers/merge';
export { ReduceTransformerMode, type ReduceTransformerOptions } from '../transformations/transformers/reduce';
export { createOrderFieldsComparer } from '../transformations/transformers/order';
export {
createOrderFieldsComparer,
Order,
OrderByMode,
OrderByType,
type OrderByItem,
} from '../transformations/transformers/order';
export { type RenameByRegexTransformerOptions } from '../transformations/transformers/renameByRegex';
export { type OrganizeFieldsTransformerOptions } from '../transformations/transformers/organize';
export { compareValues } from '../transformations/matchers/compareValues';

@ -5,7 +5,14 @@ import { mockTransformationsRegistry } from '../../utils/tests/mockTransformatio
import { transformDataFrame } from '../transformDataFrame';
import { DataTransformerID } from './ids';
import { orderFieldsTransformer, OrderFieldsTransformerOptions } from './order';
import {
OrderByMode,
Order,
orderFieldsTransformer,
OrderFieldsTransformerOptions,
OrderByType,
OrderByItem,
} from './order';
describe('Order Transformer', () => {
beforeAll(() => {
@ -25,6 +32,7 @@ describe('Order Transformer', () => {
const cfg: DataTransformerConfig<OrderFieldsTransformerOptions> = {
id: DataTransformerID.order,
options: {
orderByMode: OrderByMode.Manual,
indexByName: {
time: 2,
temperature: 0,
@ -79,6 +87,7 @@ describe('Order Transformer', () => {
id: DataTransformerID.order,
disabled: true,
options: {
orderByMode: OrderByMode.Manual,
indexByName: {
time: 2,
temperature: 0,
@ -143,6 +152,7 @@ describe('Order Transformer', () => {
const cfg: DataTransformerConfig<OrderFieldsTransformerOptions> = {
id: DataTransformerID.order,
options: {
orderByMode: OrderByMode.Manual,
indexByName: {
time: 2,
temperature: 0,
@ -207,6 +217,7 @@ describe('Order Transformer', () => {
const cfg: DataTransformerConfig<OrderFieldsTransformerOptions> = {
id: DataTransformerID.order,
options: {
orderByMode: OrderByMode.Manual,
indexByName: {},
},
};
@ -220,21 +231,178 @@ describe('Order Transformer', () => {
name: 'time',
type: FieldType.time,
values: [3000, 4000, 5000, 6000],
state: {
displayName: 'time',
multipleFrames: false,
},
},
{
config: {},
name: 'pressure',
type: FieldType.number,
values: [10.3, 10.4, 10.5, 10.6],
state: {
displayName: 'pressure',
multipleFrames: false,
},
},
{
config: {},
name: 'humidity',
type: FieldType.number,
values: [10000.3, 10000.4, 10000.5, 10000.6],
state: {
displayName: 'humidity',
multipleFrames: false,
},
},
]);
});
});
});
describe('auto order', () => {
it.each`
labelPodOrder | labelUserOrder | fieldNameOrder | expectedFieldNameOrder
${Order.Off} | ${Order.Off} | ${Order.Off} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Off} | ${Order.Asc} | ${Order.Off} | ${['Series-2', 'Series-1', 'Series-3']}
${Order.Off} | ${Order.Off} | ${Order.Asc} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Off} | ${Order.Desc} | ${Order.Off} | ${['Series-1', 'Series-3', 'Series-2']}
${Order.Off} | ${Order.Off} | ${Order.Desc} | ${['Series-3', 'Series-2', 'Series-1']}
${Order.Off} | ${Order.Desc} | ${Order.Desc} | ${['Series-3', 'Series-1', 'Series-2']}
${Order.Off} | ${Order.Asc} | ${Order.Asc} | ${['Series-2', 'Series-1', 'Series-3']}
${Order.Off} | ${Order.Asc} | ${Order.Desc} | ${['Series-2', 'Series-3', 'Series-1']}
${Order.Off} | ${Order.Desc} | ${Order.Asc} | ${['Series-1', 'Series-3', 'Series-2']}
${Order.Asc} | ${Order.Off} | ${Order.Off} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Asc} | ${Order.Off} | ${Order.Asc} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Asc} | ${Order.Off} | ${Order.Desc} | ${['Series-2', 'Series-1', 'Series-3']}
${Order.Asc} | ${Order.Asc} | ${Order.Off} | ${['Series-2', 'Series-1', 'Series-3']}
${Order.Asc} | ${Order.Desc} | ${Order.Off} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Asc} | ${Order.Desc} | ${Order.Desc} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Asc} | ${Order.Asc} | ${Order.Asc} | ${['Series-2', 'Series-1', 'Series-3']}
${Order.Asc} | ${Order.Asc} | ${Order.Desc} | ${['Series-2', 'Series-1', 'Series-3']}
${Order.Asc} | ${Order.Desc} | ${Order.Asc} | ${['Series-1', 'Series-2', 'Series-3']}
${Order.Desc} | ${Order.Asc} | ${Order.Off} | ${['Series-3', 'Series-2', 'Series-1']}
${Order.Desc} | ${Order.Off} | ${Order.Asc} | ${['Series-3', 'Series-1', 'Series-2']}
${Order.Desc} | ${Order.Off} | ${Order.Desc} | ${['Series-3', 'Series-2', 'Series-1']}
${Order.Desc} | ${Order.Asc} | ${Order.Asc} | ${['Series-3', 'Series-2', 'Series-1']}
${Order.Desc} | ${Order.Asc} | ${Order.Desc} | ${['Series-3', 'Series-2', 'Series-1']}
${Order.Desc} | ${Order.Desc} | ${Order.Asc} | ${['Series-3', 'Series-1', 'Series-2']}
${Order.Desc} | ${Order.Desc} | ${Order.Off} | ${['Series-3', 'Series-1', 'Series-2']}
${Order.Desc} | ${Order.Desc} | ${Order.Desc} | ${['Series-3', 'Series-1', 'Series-2']}
${Order.Desc} | ${Order.Off} | ${Order.Off} | ${['Series-3', 'Series-1', 'Series-2']}
`(
'When the order is (with index order as given) label pod: $labelPodOrder / label user: $labelUserOrder / field name: $fieldNameOrder , then the field order is $expectedFieldNameOrder',
async ({ labelPodOrder, labelUserOrder, fieldNameOrder, expectedFieldNameOrder }) => {
let items: OrderByItem[] = [];
if (labelPodOrder !== Order.Off) {
items.push({ type: OrderByType.Label, name: 'pod', desc: labelPodOrder === Order.Desc });
}
if (labelUserOrder !== Order.Off) {
items.push({ type: OrderByType.Label, name: 'user', desc: labelUserOrder === Order.Desc });
}
if (fieldNameOrder !== Order.Off) {
items.push({ type: OrderByType.Name, desc: fieldNameOrder === Order.Desc });
}
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.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 };
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);
});
}
);
});

@ -1,14 +1,38 @@
import { clone } from 'lodash';
import { map } from 'rxjs/operators';
import { getFieldDisplayName } from '../../field/fieldState';
import { cacheFieldDisplayNames, getFieldDisplayName } from '../../field/fieldState';
import { DataFrame, Field } from '../../types/dataFrame';
import { DataTransformerInfo } from '../../types/transformations';
import { DataTransformerID } from './ids';
export enum OrderByMode {
Manual = 'manual',
Auto = 'auto',
}
export enum Order {
Off = 'off',
Asc = 'asc',
Desc = 'desc',
}
export enum OrderByType {
Name = 'name',
Label = 'label',
}
export interface OrderByItem {
type: OrderByType;
name?: string;
desc?: boolean;
}
export interface OrderFieldsTransformerOptions {
indexByName: Record<string, number>;
indexByName?: Record<string, number>;
orderByMode?: OrderByMode;
orderBy?: OrderByItem[];
}
export const orderFieldsTransformer: DataTransformerInfo<OrderFieldsTransformerOptions> = {
@ -23,36 +47,33 @@ export const orderFieldsTransformer: DataTransformerInfo<OrderFieldsTransformerO
* Return a modified copy of the series. If the transform is not or should not
* be applied, just return the input series
*/
operator: (options) => (source) =>
source.pipe(
map((data) => {
const orderer = createFieldsOrderer(options.indexByName);
if (!Array.isArray(data) || data.length === 0) {
return data;
}
return data.map((frame) => ({
...frame,
fields: orderer(frame.fields, data, frame),
}));
})
),
};
operator:
({ indexByName, orderByMode = OrderByMode.Manual, orderBy = [] }) =>
(source) =>
source.pipe(
map((data) => {
cacheFieldDisplayNames(data);
export const createOrderFieldsComparer = (indexByName: Record<string, number>) => (a: string, b: string) => {
return indexOfField(a, indexByName) - indexOfField(b, indexByName);
const orderer =
orderByMode === OrderByMode.Manual
? createFieldsOrdererManual(indexByName!)
: createFieldsOrdererAuto(orderBy);
return data.map((frame) => ({
...frame,
fields: orderer(frame.fields, data, frame),
}));
})
),
};
const createFieldsOrderer =
export const createOrderFieldsComparer = (indexByName: Record<string, number>) => (a: string, b: string) =>
indexOfField(a, indexByName) - indexOfField(b, indexByName);
const createFieldsOrdererManual =
(indexByName: Record<string, number>) => (fields: Field[], data: DataFrame[], frame: DataFrame) => {
if (!Array.isArray(fields) || fields.length === 0) {
return fields;
}
if (!indexByName || Object.keys(indexByName).length === 0) {
return fields;
}
const comparer = createOrderFieldsComparer(indexByName);
return clone(fields).sort((a, b) =>
comparer(getFieldDisplayName(a, frame, data), getFieldDisplayName(b, frame, data))
);
@ -64,3 +85,24 @@ const indexOfField = (fieldName: string, indexByName: Record<string, number>) =>
}
return Number.MAX_SAFE_INTEGER;
};
const compare = new Intl.Collator(undefined, { sensitivity: 'base', numeric: true }).compare;
/** @internal */
export const createFieldsOrdererAuto = (orderBy: OrderByItem[]) => (fields: Field[]) =>
fields.slice().sort((fieldA, fieldB) => {
for (let i = 0; i < orderBy.length; i++) {
let { type, name = '', desc = false } = orderBy[i];
let aVal = type === OrderByType.Name ? (fieldA.state?.displayName ?? fieldA.name) : (fieldA.labels?.[name] ?? '');
let bVal = type === OrderByType.Name ? (fieldB.state?.displayName ?? fieldB.name) : (fieldB.labels?.[name] ?? '');
let res = compare(aVal, bVal) * (desc ? -1 : 1);
if (res !== 0) {
return res;
}
}
return 0;
});

@ -94,6 +94,10 @@ describe('OrganizeFields Transformer', () => {
name: 'time',
type: FieldType.time,
values: [3000, 4000, 5000, 6000],
state: {
displayName: 'time',
multipleFrames: false,
},
},
]);
});

@ -1152,6 +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.
- **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,6 +1,6 @@
import { css } from '@emotion/css';
import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd';
import { useCallback, useMemo } from 'react';
import { useCallback, useId, useMemo } from 'react';
import {
DataTransformerID,
@ -10,7 +10,14 @@ import {
TransformerUIProps,
TransformerCategory,
} from '@grafana/data';
import { createOrderFieldsComparer, OrganizeFieldsTransformerOptions } from '@grafana/data/internal';
import {
createOrderFieldsComparer,
Order,
OrderByItem,
OrderByMode,
OrderByType,
OrganizeFieldsTransformerOptions,
} from '@grafana/data/internal';
import { Trans, t } from '@grafana/i18n';
import {
Input,
@ -22,18 +29,102 @@ import {
InlineLabel,
Text,
Box,
InlineField,
InlineFieldRow,
RadioButtonGroup,
} from '@grafana/ui';
import { createFieldsOrdererAuto } from '../../../../../packages/grafana-data/src/transformations/transformers/order';
import { getTransformationContent } from '../docs/getTransformationContent';
import { useAllFieldNamesFromDataFrames } from '../utils';
import { getAllFieldNamesFromDataFrames, getDistinctLabels, useAllFieldNamesFromDataFrames } from '../utils';
interface OrganizeFieldsTransformerEditorProps extends TransformerUIProps<OrganizeFieldsTransformerOptions> {}
interface UIOrderByItem {
type: OrderByType;
name?: string;
order: Order;
}
function move(arr: unknown[], from: number, to: number) {
arr.splice(to, 0, arr.splice(from, 1)[0]);
}
const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeFieldsTransformerEditorProps) => {
const { indexByName, excludeByName, renameByName, includeByName } = options;
const { indexByName, excludeByName, renameByName, includeByName, orderBy, orderByMode } = options;
const fieldNames = useAllFieldNamesFromDataFrames(input);
const orderedFieldNames = useMemo(() => orderFieldNamesByIndex(fieldNames, indexByName), [fieldNames, indexByName]);
const orderedFieldNames = useMemo(() => {
if (input.length > 0 && orderByMode === OrderByMode.Auto) {
const autoOrderer = createFieldsOrdererAuto(orderBy ?? []);
return getAllFieldNamesFromDataFrames(
[
{
...input[0],
fields: autoOrderer(input[0].fields),
},
],
false
);
}
return orderFieldNamesByIndex(fieldNames, indexByName);
}, [input, fieldNames, indexByName, orderByMode, orderBy]);
const uiOrderByItems = useMemo(() => {
const uiOrderByItems: UIOrderByItem[] = [];
if (orderByMode === OrderByMode.Auto) {
const foundLabels = getDistinctLabels(input);
let byFieldNameAdded = false;
// add Asc or Desc items
orderBy?.forEach((item, index) => {
let order = item.desc ? Order.Desc : Order.Asc;
// by field name
if (item.type === OrderByType.Name) {
uiOrderByItems.push({
type: OrderByType.Name,
order,
});
byFieldNameAdded = true;
}
// by label
else if (foundLabels.has(item.name!)) {
uiOrderByItems.push({
type: OrderByType.Label,
name: item.name,
order,
});
foundLabels.delete(item.name!);
}
});
// add Off items
if (!byFieldNameAdded) {
uiOrderByItems.push({
type: OrderByType.Name,
order: Order.Off,
});
}
foundLabels.forEach((name) => {
uiOrderByItems.push({
type: OrderByType.Label,
name,
order: Order.Off,
});
});
}
return uiOrderByItems;
}, [input, orderByMode, orderBy]);
const filterType = includeByName && Object.keys(includeByName).length > 0 ? 'include' : 'exclude';
const onToggleVisibility = useCallback(
@ -63,7 +154,7 @@ const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeF
[onChange, options, includeByName]
);
const onDragEnd = useCallback(
const onDragEndFields = useCallback(
(result: DropResult) => {
if (!result || !result.destination) {
return;
@ -97,6 +188,59 @@ const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeF
[onChange, options]
);
const onChangeSort = useCallback(
(item: UIOrderByItem, sortOrder: Order) => {
item.order = sortOrder;
const orderBy: OrderByItem[] = [];
uiOrderByItems.forEach((item) => {
if (item.order !== Order.Off) {
orderBy.push({
type: item.type,
name: item.name,
desc: item.order === Order.Desc,
});
}
});
onChange({ ...options, orderBy });
},
[options, uiOrderByItems, onChange]
);
const onDragEndLabels = useCallback(
(result: DropResult) => {
if (result.destination == null) {
return;
}
const startIndex = result.source.index;
const endIndex = result.destination.index;
if (startIndex === endIndex) {
return;
}
move(uiOrderByItems, startIndex, endIndex);
const orderBy: OrderByItem[] = [];
uiOrderByItems.forEach((item) => {
if (item.order !== Order.Off) {
orderBy.push({
type: item.type,
name: item.name,
desc: item.order === Order.Desc,
});
}
});
onChange({ ...options, orderBy });
},
[options, onChange, uiOrderByItems]
);
// Show warning that we only apply the first frame
if (input.length > 1) {
return (
@ -109,36 +253,84 @@ const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeF
);
}
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="sortable-fields-transformer" direction="vertical">
{(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{orderedFieldNames.map((fieldName, index) => {
const isIncludeFilter = includeByName && fieldName in includeByName ? includeByName[fieldName] : false;
const isVisible = filterType === 'include' ? isIncludeFilter : !excludeByName[fieldName];
const onToggleFunction = filterType === 'include' ? onToggleVisibilityInclude : onToggleVisibility;
const styles = useStyles2(getDraggableStyles);
return (
<>
<InlineFieldRow>
<InlineField label={t('transformers.organize-fields-transformer-editor.field-order', 'Field order')}>
<RadioButtonGroup
options={[
{
label: t('transformers.organize-fields-transformer-editor.field-order-manual', 'Manual'),
value: OrderByMode.Manual,
},
{
label: t('transformers.organize-fields-transformer-editor.field-order-auto', 'Auto'),
value: OrderByMode.Auto,
},
]}
value={options.orderByMode ?? OrderByMode.Manual}
onChange={(v) => onChange({ ...options, orderByMode: v })}
/>
</InlineField>
</InlineFieldRow>
<DragDropContext onDragEnd={onDragEndLabels}>
{options.orderByMode === OrderByMode.Auto && (
<Droppable droppableId="sortable-labels-transformer" direction="vertical">
{(provided) => {
return (
<DraggableFieldName
fieldName={fieldName}
renamedFieldName={renameByName[fieldName]}
index={index}
onToggleVisibility={onToggleFunction}
onRenameField={onRenameField}
visible={isVisible}
key={fieldName}
/>
<>
<div ref={provided.innerRef} className={styles.labelsDraggable} {...provided.droppableProps}>
{uiOrderByItems.map((item, idx) => (
<DraggableUIOrderByItem item={item} index={idx} onChangeSort={onChangeSort} />
))}
</div>
{provided.placeholder}
</>
);
})}
{provided.placeholder}
</div>
}}
</Droppable>
)}
</Droppable>
</DragDropContext>
</DragDropContext>
<DragDropContext onDragEnd={onDragEndFields}>
<Droppable droppableId="sortable-fields-transformer" direction="vertical">
{(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{orderedFieldNames.map((fieldName, index) => {
const isIncludeFilter = includeByName && fieldName in includeByName ? includeByName[fieldName] : false;
const isVisible = filterType === 'include' ? isIncludeFilter : !excludeByName[fieldName];
const onToggleFunction = filterType === 'include' ? onToggleVisibilityInclude : onToggleVisibility;
return (
<DraggableFieldName
fieldName={fieldName}
renamedFieldName={renameByName[fieldName]}
index={index}
onToggleVisibility={onToggleFunction}
onRenameField={onRenameField}
visible={isVisible}
key={fieldName}
isDragDisabled={options.orderByMode === OrderByMode.Auto}
/>
);
})}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</>
);
};
const getDraggableStyles = (theme: GrafanaTheme2) => ({
labelsDraggable: css({
marginBottom: theme.spacing(3),
}),
});
OrganizeFieldsTransformerEditor.displayName = 'OrganizeFieldsTransformerEditor';
interface DraggableFieldProps {
@ -148,6 +340,7 @@ interface DraggableFieldProps {
visible: boolean;
onToggleVisibility: (fieldName: string, isVisible: boolean) => void;
onRenameField: (from: string, to: string) => void;
isDragDisabled: boolean;
}
const DraggableFieldName = ({
@ -157,26 +350,29 @@ const DraggableFieldName = ({
visible,
onToggleVisibility,
onRenameField,
isDragDisabled,
}: DraggableFieldProps) => {
const styles = useStyles2(getFieldNameStyles);
return (
<Draggable draggableId={fieldName} index={index}>
<Draggable draggableId={fieldName} index={index} isDragDisabled={isDragDisabled}>
{(provided) => (
<Box marginBottom={0.5} display="flex" gap={0} ref={provided.innerRef} {...provided.draggableProps}>
<Box display="flex" gap={0} ref={provided.innerRef} {...provided.draggableProps}>
<InlineLabel width={60} as="div">
<Stack gap={0} justifyContent="flex-start" alignItems="center" width="100%">
<span {...provided.dragHandleProps}>
<Icon
name="draggabledots"
title={t(
'transformers.draggable-field-name.title-drag-and-drop-to-reorder',
'Drag and drop to reorder'
)}
size="lg"
className={styles.draggable}
/>
</span>
{!isDragDisabled && (
<span {...provided.dragHandleProps}>
<Icon
name="draggabledots"
title={t(
'transformers.draggable-field-name.title-drag-and-drop-to-reorder',
'Drag and drop to reorder'
)}
size="lg"
className={styles.draggable}
/>
</span>
)}
<IconButton
className={styles.toggle}
size="md"
@ -197,6 +393,7 @@ const DraggableFieldName = ({
defaultValue={renamedFieldName || ''}
placeholder={t('transformers.draggable-field-name.rename-placeholder', 'Rename {{fieldName}}', {
fieldName,
interpolation: { escapeValue: false },
})}
onBlur={(event) => onRenameField(fieldName, event.currentTarget.value)}
/>
@ -208,6 +405,57 @@ const DraggableFieldName = ({
DraggableFieldName.displayName = 'DraggableFieldName';
interface DraggableUIOrderByItemProps {
item: UIOrderByItem;
index: number;
onChangeSort: (item: UIOrderByItem, order: Order) => void;
}
const DraggableUIOrderByItem = ({ index, item, onChangeSort }: DraggableUIOrderByItemProps) => {
const styles = useStyles2(getFieldNameStyles);
const draggableId = useId();
return (
<Draggable draggableId={draggableId} index={index} isDragDisabled={item.order === Order.Off}>
{(provided) => (
<Box marginBottom={0.5} display="flex" gap={0} ref={provided.innerRef} {...provided.draggableProps}>
<InlineLabel width={60} as="div">
<Stack gap={3} justifyContent="flex-start" alignItems="center" width="100%">
<span {...provided.dragHandleProps}>
<Icon
name="draggabledots"
title={t(
'transformers.draggable-field-name.title-drag-and-drop-to-reorder',
'Drag and drop to reorder'
)}
size="lg"
className={styles.draggable}
/>
</span>
<Text truncate={true} element="p" variant="bodySmall" weight="bold">
{item.type === OrderByType.Label ? `Label: ${item.name}` : `Field name`}
</Text>
</Stack>
</InlineLabel>
<RadioButtonGroup
options={[
{ label: t('transformers.draggable-sort-order.off', 'Off'), value: Order.Off },
{ label: t('transformers.draggable-sort-order.asc', 'ASC'), value: Order.Asc },
{ label: t('transformers.draggable-sort-order.desc', 'DESC'), value: Order.Desc },
]}
value={item.order}
onChange={(order) => {
onChangeSort(item, order);
}}
/>
</Box>
)}
</Draggable>
);
};
DraggableUIOrderByItem.displayName = 'DraggableUIOrderByItem';
const getFieldNameStyles = (theme: GrafanaTheme2) => ({
toggle: css({
margin: theme.spacing(0, 1),

@ -10665,6 +10665,11 @@
"tooltip-disable": "Disable",
"tooltip-enable": "Enable"
},
"draggable-sort-order": {
"asc": "ASC",
"desc": "DESC",
"off": "Off"
},
"enum-mapping-editor": {
"add-enum-value": "Add enum value",
"generate-enum-values-from-data": "Generate enum values from data"
@ -10843,6 +10848,9 @@
"time-in-ascending-order": "Time in ascending order"
},
"organize-fields-transformer-editor": {
"field-order": "Field order",
"field-order-auto": "Auto",
"field-order-manual": "Manual",
"first-frame-warning": "Organize fields only works with a single frame. Consider applying a join transformation or filtering the input first."
},
"partion-by-values-editor": {

Loading…
Cancel
Save