FieldOverride: Support data links via field overrides (#23590)

* Move xss and sanitize packages to grafana-data

* Move text, url and location utils to grafana-data

* Move grafana config types to grafana-data

* Move field display value proxy to grafana-data

* Fix

* Move data links built in vars to grafana-data

* Attach links supplier to when applying field overrides

* Prep tests

* Use links suppliers attached via field overrides

* locationUtil dependencies type

* Move sanitize-url declaration to grafana-data

* Revert "Move sanitize-url declaration to grafana-data"

This reverts commit 11db9f5e55.

* Fix typo

* fix ts vol1

* Remove import from runtime in data.... Make TS happy at the same time ;)

* Lovely TS, please shut up

* Lovely TS, please shut up vol2

* fix tests

* Fixes

* minor refactor

* Attach get links to FieldDisplayValue for seamless usage

* Update packages/grafana-data/src/field/fieldOverrides.ts

* Make storybook build
This commit is contained in:
Dominik Prokop
2020-04-20 07:37:38 +02:00
committed by GitHub
parent e6c9b1305e
commit d2a13c4715
87 changed files with 659 additions and 337 deletions

View File

@@ -6,5 +6,5 @@
},
"exclude": ["../dist", "../node_modules"],
"extends": "../tsconfig.json",
"include": ["../src/**/*.ts", "../src/**/*.tsx"]
"include": ["../src/**/*.ts", "../src/**/*.tsx", "../../../public/app/types/sanitize-url.d.ts"]
}

View File

@@ -1,7 +1,7 @@
import React, { useState, useMemo, useContext, useRef, RefObject, memo, useEffect } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
import { DataLinkSuggestions } from './DataLinkSuggestions';
import { ThemeContext, DataLinkBuiltInVars, makeValue } from '../../index';
import { ThemeContext, makeValue } from '../../index';
import { SelectionReference } from './SelectionReference';
import { Portal } from '../index';
@@ -14,7 +14,7 @@ import { css } from 'emotion';
import { SlatePrism } from '../../slate-plugins';
import { SCHEMA } from '../../utils/slate';
import { stylesFactory } from '../../themes';
import { GrafanaTheme, VariableSuggestion, VariableOrigin } from '@grafana/data';
import { GrafanaTheme, VariableSuggestion, VariableOrigin, DataLinkBuiltInVars } from '@grafana/data';
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);

View File

@@ -1,12 +1,12 @@
import React from 'react';
import { WithContextMenu } from '../ContextMenu/WithContextMenu';
import { LinkModelSupplier } from '@grafana/data';
import { LinkModel } from '@grafana/data';
import { linkModelToContextMenuItems } from '../../utils/dataLinks';
import { css } from 'emotion';
interface DataLinksContextMenuProps {
children: (props: { openMenu?: React.MouseEventHandler<HTMLElement>; targetClassName?: string }) => JSX.Element;
links?: LinkModelSupplier<any>;
links?: () => LinkModel[];
}
export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links }) => {

View File

@@ -1,5 +1,10 @@
import React from 'react';
import { FieldConfigEditorProps, DataLink, DataLinksFieldConfigSettings } from '@grafana/data';
import {
DataLink,
DataLinksFieldConfigSettings,
FieldConfigEditorProps,
VariableSuggestionsScope,
} from '@grafana/data';
import { DataLinksInlineEditor } from '../DataLinks/DataLinksInlineEditor/DataLinksInlineEditor';
export const DataLinksValueEditor: React.FC<FieldConfigEditorProps<DataLink[], DataLinksFieldConfigSettings>> = ({
@@ -12,7 +17,7 @@ export const DataLinksValueEditor: React.FC<FieldConfigEditorProps<DataLink[], D
links={value}
onChange={onChange}
data={context.data}
suggestions={context.getSuggestions ? context.getSuggestions() : []}
suggestions={context.getSuggestions ? context.getSuggestions(VariableSuggestionsScope.Values) : []}
/>
);
};

View File

@@ -1,14 +1,33 @@
import React, { FC } from 'react';
import { TableCellProps } from './types';
import { formattedValueToString } from '@grafana/data';
import { formattedValueToString, LinkModel } from '@grafana/data';
export const DefaultCell: FC<TableCellProps> = props => {
const { field, cell, tableStyles } = props;
const { field, cell, tableStyles, row } = props;
let link: LinkModel<any> | undefined;
if (!field.display) {
return null;
}
const displayValue = field.display(cell.value);
return <div className={tableStyles.tableCell}>{formattedValueToString(displayValue)}</div>;
if (field.getLinks) {
link = field.getLinks({
valueRowIndex: row.index,
})[0];
}
const value = formattedValueToString(displayValue);
return (
<div className={tableStyles.tableCell}>
{link ? (
<a href={link.href} target={link.target} title={link.title}>
{value}
</a>
) : (
value
)}
</div>
);
};

View File

@@ -1,26 +1,11 @@
import { ContextMenuItem } from '../components/ContextMenu/ContextMenu';
import { LinkModelSupplier } from '@grafana/data';
export const DataLinkBuiltInVars = {
keepTime: '__url_time_range',
timeRangeFrom: '__from',
timeRangeTo: '__to',
includeVars: '__all_variables',
seriesName: '__series.name',
fieldName: '__field.name',
valueTime: '__value.time',
valueNumeric: '__value.numeric',
valueText: '__value.text',
valueRaw: '__value.raw',
// name of the calculation represented by the value
valueCalc: '__value.calc',
};
import { LinkModel } from '@grafana/data';
/**
* Delays creating links until we need to open the ContextMenu
*/
export const linkModelToContextMenuItems: (links: LinkModelSupplier<any>) => ContextMenuItem[] = links => {
return links.getLinks().map(link => {
export const linkModelToContextMenuItems: (links: () => LinkModel[]) => ContextMenuItem[] = links => {
return links().map(link => {
return {
label: link.title,
// TODO: rename to href

View File

@@ -11,5 +11,5 @@
},
"exclude": ["dist", "node_modules"],
"extends": "@grafana/tsconfig",
"include": ["src/**/*.ts*"]
"include": ["src/**/*.ts*", "../../public/app/types/sanitize-url.d.ts"]
}