XYChart: Add data filter to manual mode (#81115)

* XYChart: Add data filter to manual mode

* Add onChange to data filter for manual editor

* Update placeholder for auto editor for consistency

* Filter x y fields based on frame

* Update frame calc for truthy

* Use display name instead for frame filter

* Update placeholders

* Apply frame filter to series prep

* Re run make gen cue

* Remove old TODO

* Force data filter to be selected

* minor cleanup

---------

Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
This commit is contained in:
Drew Slobodnjak 2024-01-30 15:09:15 -08:00 committed by GitHub
parent be6efd9518
commit 6fc1a6a54f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 87 additions and 25 deletions

View File

@ -214,6 +214,7 @@ It extends [FieldConfig](#fieldconfig).
| `axisSoftMax` | number | No | | *(Inherited from [FieldConfig](#fieldconfig))* |
| `axisSoftMin` | number | No | | *(Inherited from [FieldConfig](#fieldconfig))* |
| `axisWidth` | number | No | | *(Inherited from [FieldConfig](#fieldconfig))* |
| `frame` | number | No | | |
| `hideFrom` | [HideSeriesConfig](#hideseriesconfig) | No | | *(Inherited from [FieldConfig](#fieldconfig))*<br/>TODO docs |
| `labelValue` | [TextDimensionConfig](#textdimensionconfig) | No | | *(Inherited from [FieldConfig](#fieldconfig))* |
| `label` | string | No | | *(Inherited from [FieldConfig](#fieldconfig))*<br/>TODO docs<br/>Possible values are: `auto`, `never`, `always`. |

View File

@ -57,6 +57,7 @@ export const defaultFieldConfig: Partial<FieldConfig> = {
};
export interface ScatterSeriesConfig extends FieldConfig {
frame?: number;
name?: string;
x?: string;
y?: string;

View File

@ -85,7 +85,7 @@ export const AutoEditor = ({ value, onChange, context }: StandardEditorProps<XYD
<Select
isClearable={true}
options={frameNames}
placeholder={frameNames[0].label}
placeholder={'Change filter'}
value={frameNames.find((v) => v.value === value?.frame)}
onChange={(v) => {
onChange({

View File

@ -1,13 +1,14 @@
import { css, cx } from '@emotion/css';
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import {
GrafanaTheme2,
StandardEditorProps,
FieldNamePickerBaseNameMode,
StandardEditorsRegistryItem,
getFrameDisplayName,
} from '@grafana/data';
import { Button, IconButton, useStyles2 } from '@grafana/ui';
import { Button, Field, IconButton, Select, useStyles2 } from '@grafana/ui';
import { LayerName } from 'app/core/components/Layers/LayerName';
import { ScatterSeriesEditor } from './ScatterSeriesEditor';
@ -18,6 +19,16 @@ export const ManualEditor = ({
onChange,
context,
}: StandardEditorProps<ScatterSeriesConfig[], unknown, Options>) => {
const frameNames = useMemo(() => {
if (context?.data?.length) {
return context.data.map((frame, index) => ({
value: index,
label: `${getFrameDisplayName(frame, index)} (index: ${index}, rows: ${frame.length})`,
}));
}
return [{ value: 0, label: 'First result' }];
}, [context.data]);
const [selected, setSelected] = useState(0);
const style = useStyles2(getStyles);
@ -101,23 +112,53 @@ export const ManualEditor = ({
</div>
{selected >= 0 && value[selected] && (
<ScatterSeriesEditor
key={`series/${selected}`}
baseNameMode={FieldNamePickerBaseNameMode.ExcludeBaseNames}
item={{} as StandardEditorsRegistryItem}
context={context}
value={value[selected]}
onChange={(v) => {
<>
{frameNames.length > 1 && (
<Field label={'Data'}>
<Select
isClearable={false}
options={frameNames}
placeholder={'Change filter'}
value={
frameNames.find((v) => {
return v.value === value[selected].frame;
}) ?? 0
}
onChange={(val) => {
onChange(
value.map((obj, i) => {
if (i === selected) {
return v!;
if (val === null) {
return { ...value[i], frame: undefined };
}
return { ...value[i], frame: val?.value!, x: undefined, y: undefined };
}
return obj;
})
);
}}
/>
</Field>
)}
<ScatterSeriesEditor
key={`series/${selected}`}
baseNameMode={FieldNamePickerBaseNameMode.ExcludeBaseNames}
item={{} as StandardEditorsRegistryItem}
context={context}
value={value[selected]}
onChange={(val) => {
onChange(
value.map((obj, i) => {
if (i === selected) {
return val!;
}
return obj;
})
);
}}
frameFilter={value[selected].frame ?? undefined}
/>
</>
)}
</>
);

View File

@ -9,13 +9,16 @@ import { Options, ScatterSeriesConfig } from './panelcfg.gen';
export interface Props extends StandardEditorProps<ScatterSeriesConfig, unknown, Options> {
baseNameMode: FieldNamePickerBaseNameMode;
frameFilter?: number;
}
export const ScatterSeriesEditor = ({ value, onChange, context, baseNameMode }: Props) => {
export const ScatterSeriesEditor = ({ value, onChange, context, baseNameMode, frameFilter = -1 }: Props) => {
const onFieldChange = (val: unknown | undefined, field: string) => {
onChange({ ...value, [field]: val });
};
const frame = context.data && frameFilter > -1 ? context.data[frameFilter] : undefined;
return (
<div>
<Field label={'X Field'}>
@ -27,7 +30,10 @@ export const ScatterSeriesEditor = ({ value, onChange, context, baseNameMode }:
id: 'x',
name: 'x',
settings: {
filter: (field) =>
frame?.fields.some((obj) => obj.state?.displayName === field.state?.displayName) ?? true,
baseNameMode,
placeholderText: 'select X field',
},
}}
/>
@ -38,10 +44,13 @@ export const ScatterSeriesEditor = ({ value, onChange, context, baseNameMode }:
context={context}
onChange={(field) => onFieldChange(field, 'y')}
item={{
id: 'x',
name: 'x',
id: 'y',
name: 'y',
settings: {
filter: (field) =>
frame?.fields.some((obj) => obj.state?.displayName === field.state?.displayName) ?? true,
baseNameMode,
placeholderText: 'select Y field',
},
}}
/>

View File

@ -60,6 +60,7 @@ composableKinds: PanelCfg: {
x?: string
y?: string
name?: string
frame?: number
} @cuetsy(kind="interface")
Options: {

View File

@ -54,6 +54,7 @@ export const defaultFieldConfig: Partial<FieldConfig> = {
};
export interface ScatterSeriesConfig extends FieldConfig {
frame?: number;
name?: string;
x?: string;
y?: string;

View File

@ -224,6 +224,10 @@ function prepSeries(options: Options, frames: DataFrame[]): ScatterSeries[] {
}
for (let frameIndex = 0; frameIndex < frames.length; frameIndex++) {
// When a frame filter is applied, only include matching frame index
if (series.frame !== undefined && series.frame !== frameIndex) {
continue;
}
const frame = frames[frameIndex];
const xIndex = findFieldIndex(series.x, frame, frames);

View File

@ -64,3 +64,7 @@ export interface ExtraFacets {
colorFacetValue: number;
sizeFacetValue: number;
}
export interface DataFilterBySeries {
frame: number;
}