Canvas: Add actions support (#90677)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
co-authored by
Leon Sorokin
parent
b89f3f8115
commit
af48d3db1e
@@ -607,7 +607,7 @@ export function useFieldOverrides(
|
||||
/**
|
||||
* Clones the existing dataContext or creates a new one
|
||||
*/
|
||||
function getFieldDataContextClone(frame: DataFrame, field: Field, fieldScopedVars: ScopedVars) {
|
||||
export function getFieldDataContextClone(frame: DataFrame, field: Field, fieldScopedVars: ScopedVars) {
|
||||
if (fieldScopedVars?.__dataContext) {
|
||||
return {
|
||||
value: {
|
||||
|
||||
@@ -145,6 +145,7 @@ export {
|
||||
validateFieldConfig,
|
||||
applyRawFieldOverrides,
|
||||
useFieldOverrides,
|
||||
getFieldDataContextClone,
|
||||
} from './field/fieldOverrides';
|
||||
export { getFieldDisplayValuesProxy } from './field/getFieldDisplayValuesProxy';
|
||||
export {
|
||||
@@ -800,6 +801,14 @@ export {
|
||||
VariableSuggestionsScope,
|
||||
OneClickMode,
|
||||
} from './types/dataLink';
|
||||
export {
|
||||
type Action,
|
||||
type ActionModel,
|
||||
HttpRequestMethod,
|
||||
defaultActionConfig,
|
||||
contentTypeOptions,
|
||||
httpMethodOptions,
|
||||
} from './types/action';
|
||||
export { DataFrameType } from './types/dataFrameTypes';
|
||||
export {
|
||||
FieldType,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { ScopedVars } from './ScopedVars';
|
||||
import { DataFrame, Field, ValueLinkConfig } from './dataFrame';
|
||||
import { InterpolateFunction } from './panel';
|
||||
import { SelectableValue } from './select';
|
||||
|
||||
export interface Action<T = ActionType.Fetch, TOptions = FetchOptions> {
|
||||
type: T;
|
||||
title: string;
|
||||
options: TOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processed Action Model. The values are ready to use
|
||||
*/
|
||||
export interface ActionModel<T = any> {
|
||||
title: string;
|
||||
onClick: (event: any, origin?: any) => void;
|
||||
}
|
||||
|
||||
interface FetchOptions {
|
||||
method: HttpRequestMethod;
|
||||
url: string;
|
||||
body?: string;
|
||||
queryParams?: Array<[string, string]>;
|
||||
headers?: Array<[string, string]>;
|
||||
}
|
||||
|
||||
export enum ActionType {
|
||||
Fetch = 'fetch',
|
||||
}
|
||||
|
||||
export enum HttpRequestMethod {
|
||||
POST = 'POST',
|
||||
PUT = 'PUT',
|
||||
GET = 'GET',
|
||||
}
|
||||
|
||||
export const httpMethodOptions: SelectableValue[] = [
|
||||
{ label: HttpRequestMethod.POST, value: HttpRequestMethod.POST },
|
||||
{ label: HttpRequestMethod.PUT, value: HttpRequestMethod.PUT },
|
||||
{ label: HttpRequestMethod.GET, value: HttpRequestMethod.GET },
|
||||
];
|
||||
|
||||
export const contentTypeOptions: SelectableValue[] = [
|
||||
{ label: 'application/json', value: 'application/json' },
|
||||
{ label: 'text/plain', value: 'text/plain' },
|
||||
{ label: 'application/xml', value: 'application/xml' },
|
||||
{ label: 'application/x-www-form-urlencoded', value: 'application/x-www-form-urlencoded' },
|
||||
];
|
||||
|
||||
export const defaultActionConfig: Action = {
|
||||
type: ActionType.Fetch,
|
||||
title: '',
|
||||
options: {
|
||||
url: '',
|
||||
method: HttpRequestMethod.POST,
|
||||
body: '{}',
|
||||
queryParams: [],
|
||||
headers: [['Content-Type', 'application/json']],
|
||||
},
|
||||
};
|
||||
|
||||
export type ActionsArgs = {
|
||||
frame: DataFrame;
|
||||
field: Field;
|
||||
fieldScopedVars: ScopedVars;
|
||||
replaceVariables: InterpolateFunction;
|
||||
actions: Action[];
|
||||
config: ValueLinkConfig;
|
||||
};
|
||||
@@ -51,7 +51,6 @@ export interface DataLink<T extends DataQuery = any> {
|
||||
internal?: InternalDataLink<T>;
|
||||
|
||||
origin?: DataLinkConfigOrigin;
|
||||
sortIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +130,7 @@ export enum VariableSuggestionsScope {
|
||||
}
|
||||
|
||||
export enum OneClickMode {
|
||||
Action = 'action',
|
||||
Link = 'link',
|
||||
Off = 'off',
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ export interface FeatureToggles {
|
||||
autoMigrateXYChartPanel?: boolean;
|
||||
disableAngular?: boolean;
|
||||
canvasPanelNesting?: boolean;
|
||||
vizActions?: boolean;
|
||||
disableSecretsCompatibility?: boolean;
|
||||
logRequestsInstrumentedAsUnknown?: boolean;
|
||||
topnav?: boolean;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ActionModel, Field } from '@grafana/data';
|
||||
|
||||
import { Button, ButtonProps } from '../Button';
|
||||
|
||||
type ActionButtonProps = ButtonProps & {
|
||||
action: ActionModel<Field>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function ActionButton({ action, ...buttonProps }: ActionButtonProps) {
|
||||
return (
|
||||
<Button variant="primary" size="sm" onClick={action.onClick} {...buttonProps}>
|
||||
{action.title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +1,50 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { Field, GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
import { ActionModel, Field, GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
|
||||
import { Button, ButtonProps, DataLinkButton, Stack } from '..';
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { ActionButton } from '../Actions/ActionButton';
|
||||
|
||||
interface VizTooltipFooterProps {
|
||||
dataLinks: Array<LinkModel<Field>>;
|
||||
actions?: Array<ActionModel<Field>>;
|
||||
annotate?: () => void;
|
||||
}
|
||||
|
||||
export const ADD_ANNOTATION_ID = 'add-annotation-button';
|
||||
|
||||
export const VizTooltipFooter = ({ dataLinks, annotate }: VizTooltipFooterProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const renderDataLinks = () => {
|
||||
const buttonProps: ButtonProps = {
|
||||
variant: 'secondary',
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="column" justifyContent="flex-start">
|
||||
{dataLinks.map((link, i) => (
|
||||
<DataLinkButton key={i} link={link} buttonProps={buttonProps} />
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
const renderDataLinks = (dataLinks: LinkModel[]) => {
|
||||
const buttonProps: ButtonProps = {
|
||||
variant: 'secondary',
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="column" justifyContent="flex-start">
|
||||
{dataLinks.map((link, i) => (
|
||||
<DataLinkButton key={i} link={link} buttonProps={buttonProps} />
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const renderActions = (actions: ActionModel[]) => {
|
||||
return (
|
||||
<Stack direction="column" justifyContent="flex-start">
|
||||
{actions.map((action, i) => (
|
||||
<ActionButton key={i} action={action} variant="secondary" />
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const VizTooltipFooter = ({ dataLinks, actions, annotate }: VizTooltipFooterProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{dataLinks.length > 0 && <div className={styles.dataLinks}>{renderDataLinks()}</div>}
|
||||
{dataLinks.length > 0 && <div className={styles.dataLinks}>{renderDataLinks(dataLinks)}</div>}
|
||||
{actions && actions.length > 0 && <div className={styles.dataLinks}>{renderActions(actions)}</div>}
|
||||
{annotate != null && (
|
||||
<div className={styles.addAnnotations}>
|
||||
<Button icon="comment-alt" variant="secondary" size="sm" id={ADD_ANNOTATION_ID} onClick={annotate}>
|
||||
|
||||
Reference in New Issue
Block a user