From e612d7a2f936e13e1afe015d1b8bb573c632d59e Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 11 Feb 2020 13:48:36 +0100 Subject: [PATCH] New panel edit: data links edit (#22077) * Move data links suggestions to grafana-data * Data links - field config and overrides * Lint * Fix test * Add variable suggestions to field override context * Revert "Move data links suggestions to grafana-data" This reverts commit 5d8d01a65eeda8db2379fddfc1223a9ec9a00c1d. * Move FieldConfigEditor to core --- packages/grafana-data/src/types/dataLink.ts | 20 ++ .../grafana-data/src/types/fieldOverrides.ts | 15 +- .../components/DataLinks/DataLinkEditor.tsx | 4 +- .../components/DataLinks/DataLinkInput.tsx | 4 +- .../DataLinks/DataLinkSuggestions.tsx | 18 +- .../components/DataLinks/DataLinksEditor.tsx | 4 +- .../FieldConfigs/FieldConfigEditor.story.tsx | 49 ---- .../src/components/FieldConfigs/links.tsx | 225 ++++++++++++++++++ .../standardFieldConfigEditorRegistry.test.ts | 1 + .../standardFieldConfigEditorRegistry.tsx | 23 +- packages/grafana-ui/src/components/index.ts | 4 +- .../PanelEditor}/FieldConfigEditor.tsx | 32 ++- .../components/PanelEditor/PanelEditor.tsx | 36 +-- .../dashboard/components/PanelEditor/utils.ts | 26 ++ .../app/features/panel/panellinks/link_srv.ts | 35 ++- .../elasticsearch/configuration/DataLink.tsx | 3 +- .../elasticsearch/configuration/DataLinks.tsx | 4 +- .../loki/configuration/DerivedField.tsx | 3 +- .../loki/configuration/DerivedFields.tsx | 4 +- public/app/plugins/panel/graph/module.ts | 10 +- 20 files changed, 378 insertions(+), 142 deletions(-) delete mode 100644 packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.story.tsx create mode 100644 packages/grafana-ui/src/components/FieldConfigs/links.tsx rename {packages/grafana-ui/src/components/FieldConfigs => public/app/features/dashboard/components/PanelEditor}/FieldConfigEditor.tsx (89%) create mode 100644 public/app/features/dashboard/components/PanelEditor/utils.ts diff --git a/packages/grafana-data/src/types/dataLink.ts b/packages/grafana-data/src/types/dataLink.ts index 30f7ca60b03..0e6fcb6769b 100644 --- a/packages/grafana-data/src/types/dataLink.ts +++ b/packages/grafana-data/src/types/dataLink.ts @@ -52,3 +52,23 @@ export interface LinkModel { export interface LinkModelSupplier { getLinks(scopedVars?: any): Array>; } + +export enum VariableOrigin { + Series = 'series', + Field = 'field', + Fields = 'fields', + Value = 'value', + BuiltIn = 'built-in', + Template = 'template', +} + +export interface VariableSuggestion { + value: string; + label: string; + documentation?: string; + origin: VariableOrigin; +} + +export enum VariableSuggestionsScope { + Values = 'values', +} diff --git a/packages/grafana-data/src/types/fieldOverrides.ts b/packages/grafana-data/src/types/fieldOverrides.ts index f9849f98790..b02fe3b57ad 100644 --- a/packages/grafana-data/src/types/fieldOverrides.ts +++ b/packages/grafana-data/src/types/fieldOverrides.ts @@ -1,8 +1,7 @@ -import { MatcherConfig, FieldConfig, Field } from '../types'; -import { Registry, RegistryItem } from '../utils'; import { ComponentType } from 'react'; +import { MatcherConfig, FieldConfig, Field, DataFrame, VariableSuggestion, VariableSuggestionsScope } from '../types'; +import { Registry, RegistryItem } from '../utils'; import { InterpolateFunction } from './panel'; -import { DataFrame } from 'apache-arrow'; export interface DynamicConfigValue { prop: string; @@ -26,18 +25,20 @@ export interface FieldConfigSource { export interface FieldConfigEditorProps { item: FieldPropertyEditorItem; // The property info value: TValue; + context: FieldOverrideContext; onChange: (value?: TValue) => void; } export interface FieldOverrideContext { - field: Field; - data: DataFrame; - replaceVariables: InterpolateFunction; + data: DataFrame[]; + field?: Field; + replaceVariables?: InterpolateFunction; + getSuggestions?: (scope?: VariableSuggestionsScope) => VariableSuggestion[]; } export interface FieldOverrideEditorProps { item: FieldPropertyEditorItem; - value: any; + value: TValue; context: FieldOverrideContext; onChange: (value?: any) => void; } diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx index dda63d4c8d7..ac1d57190d5 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx @@ -1,11 +1,9 @@ import React, { ChangeEvent, useContext } from 'react'; -import { DataLink } from '@grafana/data'; +import { DataLink, VariableSuggestion, GrafanaTheme } from '@grafana/data'; import { FormField, Switch } from '../index'; -import { VariableSuggestion } from './DataLinkSuggestions'; import { css } from 'emotion'; import { ThemeContext, stylesFactory } from '../../themes/index'; import { DataLinkInput } from './DataLinkInput'; -import { GrafanaTheme } from '@grafana/data'; interface DataLinkEditorProps { index: number; diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx index 82d4a6892cc..c2903e5b307 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useContext, useRef, RefObject, memo, useEffect } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; -import { VariableSuggestion, VariableOrigin, DataLinkSuggestions } from './DataLinkSuggestions'; +import { DataLinkSuggestions } from './DataLinkSuggestions'; import { ThemeContext, DataLinkBuiltInVars, makeValue } from '../../index'; import { SelectionReference } from './SelectionReference'; import { Portal } from '../index'; @@ -14,7 +14,7 @@ import { css, cx } from 'emotion'; import { SlatePrism } from '../../slate-plugins'; import { SCHEMA } from '../../utils/slate'; import { stylesFactory } from '../../themes'; -import { GrafanaTheme } from '@grafana/data'; +import { GrafanaTheme, VariableSuggestion, VariableOrigin } from '@grafana/data'; const modulo = (a: number, n: number) => a - n * Math.floor(a / n); diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinkSuggestions.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinkSuggestions.tsx index 7295143a326..fe2806172ee 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinkSuggestions.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinkSuggestions.tsx @@ -1,5 +1,5 @@ import { selectThemeVariant, ThemeContext } from '../../index'; -import { GrafanaTheme } from '@grafana/data'; +import { GrafanaTheme, VariableSuggestion } from '@grafana/data'; import { css, cx } from 'emotion'; import _ from 'lodash'; import React, { useRef, useContext, useMemo } from 'react'; @@ -8,22 +8,6 @@ import { List } from '../index'; import tinycolor from 'tinycolor2'; import { stylesFactory } from '../../themes'; -export enum VariableOrigin { - Series = 'series', - Field = 'field', - Fields = 'fields', - Value = 'value', - BuiltIn = 'built-in', - Template = 'template', -} - -export interface VariableSuggestion { - value: string; - label: string; - documentation?: string; - origin: VariableOrigin; -} - interface DataLinkSuggestionsProps { suggestions: VariableSuggestion[]; activeIndex: number; diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksEditor.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksEditor.tsx index 8ac6c69feba..34278b9f6a3 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksEditor.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksEditor.tsx @@ -4,10 +4,10 @@ import React, { FC } from 'react'; import Prism from 'prismjs'; // Components import { css } from 'emotion'; -import { DataLink } from '@grafana/data'; +import { DataLink, VariableSuggestion } from '@grafana/data'; import { Button } from '../index'; import { DataLinkEditor } from './DataLinkEditor'; -import { VariableSuggestion } from './DataLinkSuggestions'; + import { useTheme } from '../../themes/ThemeContext'; interface DataLinksEditorProps { diff --git a/packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.story.tsx b/packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.story.tsx deleted file mode 100644 index c0b35bd9d23..00000000000 --- a/packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.story.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { storiesOf } from '@storybook/react'; -import FieldConfigEditor from './FieldConfigEditor'; -import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; -import { FieldConfigSource, FieldConfigEditorRegistry, FieldPropertyEditorItem, Registry } from '@grafana/data'; -import { NumberFieldConfigSettings, NumberValueEditor, NumberOverrideEditor, numberOverrideProcessor } from './number'; -import { renderComponentWithTheme } from '../../utils/storybook/withTheme'; - -const FieldConfigStories = storiesOf('UI/FieldConfig', module); - -FieldConfigStories.addDecorator(withCenteredStory); - -const cfg: FieldConfigSource = { - defaults: { - title: 'Hello', - decimals: 3, - }, - overrides: [], -}; - -const columWidth: FieldPropertyEditorItem = { - id: 'width', // Match field properties - name: 'Column Width', - description: 'column width (for table)', - - editor: NumberValueEditor, - override: NumberOverrideEditor, - process: numberOverrideProcessor, - - settings: { - placeholder: 'auto', - min: 20, - max: 300, - }, -}; - -export const customEditorRegistry: FieldConfigEditorRegistry = new Registry(() => { - return [columWidth]; -}); - -FieldConfigStories.add('default', () => { - return renderComponentWithTheme(FieldConfigEditor, { - config: cfg, - data: [], - custom: customEditorRegistry, - onChange: (config: FieldConfigSource) => { - console.log('Data', config); - }, - }); -}); diff --git a/packages/grafana-ui/src/components/FieldConfigs/links.tsx b/packages/grafana-ui/src/components/FieldConfigs/links.tsx new file mode 100644 index 00000000000..5b711668247 --- /dev/null +++ b/packages/grafana-ui/src/components/FieldConfigs/links.tsx @@ -0,0 +1,225 @@ +import { + FieldOverrideContext, + FieldConfigEditorProps, + DataLink, + FieldOverrideEditorProps, + DataFrame, +} from '@grafana/data'; +import React, { FC, useState } from 'react'; +import { css, cx } from 'emotion'; +import Forms from '../Forms'; +import { Modal } from '../Modal/Modal'; +import { DataLinkEditor } from '../DataLinks/DataLinkEditor'; +import cloneDeep from 'lodash/cloneDeep'; +import { VariableSuggestion } from '@grafana/data'; + +export interface DataLinksFieldConfigSettings {} + +export const dataLinksOverrideProcessor = ( + value: any, + context: FieldOverrideContext, + _settings: DataLinksFieldConfigSettings +) => { + return value as DataLink[]; +}; + +export const DataLinksValueEditor: React.FC> = ({ + value, + onChange, + context, +}) => { + const onDataLinkChange = (index: number, link: DataLink) => { + const links = cloneDeep(value); + links[index] = link; + onChange(links); + }; + + const onDataLinkAdd = () => { + const links = cloneDeep(value); + + links.push({ + title: '', + url: '', + }); + onChange(links); + }; + + return ( + <> + {value.map((l, i) => { + return ( + + ); + })} + + + Create data link + + + ); +}; + +export const DataLinksOverrideEditor: React.FC> = ({ + value, + onChange, + context, + item, +}) => { + const onDataLinkChange = (index: number, link: DataLink) => { + const links = cloneDeep(value); + links[index] = link; + onChange(links); + }; + + const onDataLinkAdd = () => { + let links = cloneDeep(value); + if (links) { + links.push({ + title: '', + url: '', + }); + } else { + links = [ + { + title: '', + url: '', + }, + ]; + } + onChange(links); + }; + + return ( + <> + {value && + value.map((l, i) => { + return ( + + ); + })} + + + Create data link + + + ); +}; + +interface DataLinksListItemProps { + index: number; + link: DataLink; + data: DataFrame[]; + onChange: (index: number, link: DataLink) => void; + suggestions: VariableSuggestion[]; +} + +const DataLinksListItem: FC = ({ index, link, data, onChange, suggestions }) => { + const [isEditing, setIsEditing] = useState(false); + + const style = () => { + return { + wrapper: css` + display: flex; + justify-content: space-between; + `, + action: css` + flex-shrink: 0; + flex-grow: 0; + `, + noTitle: css` + font-style: italic; + `, + }; + }; + + const styles = style(); + const hasTitle = link.title.trim() !== ''; + + return ( + <> +
+
{hasTitle ? link.title : 'Edit data link'}
+
+ setIsEditing(true)} /> +
+
+ + {isEditing && ( + { + setIsEditing(false); + }} + > + setIsEditing(false)} + suggestions={suggestions} + /> + + )} + + ); +}; + +interface DataLinkEditorModalContentProps { + link: DataLink; + index: number; + data: DataFrame[]; + suggestions: VariableSuggestion[]; + onChange: (index: number, ink: DataLink) => void; + onClose: () => void; +} + +const DataLinkEditorModalContent: FC = ({ + link, + index, + data, + suggestions, + onChange, + onClose, +}) => { + const [dirtyLink, setDirtyLink] = useState(link); + + return ( + <> + { + setDirtyLink(link); + }} + onRemove={() => {}} + /> + { + onChange(index, dirtyLink); + onClose(); + }} + > + Save + + onClose()}>Cancel + + ); +}; diff --git a/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.test.ts b/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.test.ts index 5589ba29c7d..89569bc96e7 100644 --- a/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.test.ts +++ b/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.test.ts @@ -10,6 +10,7 @@ describe('standardFieldConfigEditorRegistry', () => { thresholds: {} as any, noValue: 'no value', unit: 'km/s', + links: {} as any, }; it('make sure all fields have a valid name', () => { diff --git a/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.tsx b/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.tsx index f70799f65e4..fcca46d4566 100644 --- a/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.tsx +++ b/packages/grafana-ui/src/components/FieldConfigs/standardFieldConfigEditorRegistry.tsx @@ -1,4 +1,10 @@ -import { FieldConfigEditorRegistry, Registry, FieldPropertyEditorItem, ThresholdsConfig } from '@grafana/data'; +import { + FieldConfigEditorRegistry, + Registry, + FieldPropertyEditorItem, + ThresholdsConfig, + DataLink, +} from '@grafana/data'; import { StringValueEditor, StringOverrideEditor, stringOverrideProcessor, StringFieldConfigSettings } from './string'; import { NumberValueEditor, NumberOverrideEditor, numberOverrideProcessor, NumberFieldConfigSettings } from './number'; import { UnitValueEditor, UnitOverrideEditor } from './units'; @@ -8,6 +14,7 @@ import { thresholdsOverrideProcessor, ThresholdsFieldConfigSettings, } from './thresholds'; +import { DataLinksValueEditor, DataLinksOverrideEditor, dataLinksOverrideProcessor } from './links'; const title: FieldPropertyEditorItem = { id: 'title', // Match field properties @@ -110,8 +117,20 @@ const noValue: FieldPropertyEditorItem = { }, }; +const links: FieldPropertyEditorItem = { + id: 'links', // Match field properties + name: 'DataLinks', + description: 'Manage date links', + editor: DataLinksValueEditor, + override: DataLinksOverrideEditor, + process: dataLinksOverrideProcessor, + settings: { + placeholder: '-', + }, +}; + export const standardFieldConfigEditorRegistry: FieldConfigEditorRegistry = new Registry( () => { - return [title, unit, min, max, decimals, thresholds, noValue]; + return [title, unit, min, max, decimals, thresholds, noValue, links]; } ); diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index eb3e8095570..50e0f07a21b 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -95,7 +95,6 @@ export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper'; export * from './SingleStatShared/index'; export { CallToActionCard } from './CallToActionCard/CallToActionCard'; export { ContextMenu, ContextMenuItem, ContextMenuGroup, ContextMenuProps } from './ContextMenu/ContextMenu'; -export { VariableSuggestion, VariableOrigin } from './DataLinks/DataLinkSuggestions'; export { DataLinksEditor } from './DataLinks/DataLinksEditor'; export { DataLinkInput } from './DataLinks/DataLinkInput'; export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu'; @@ -118,7 +117,6 @@ export { Icon } from './Icon/Icon'; export { Drawer } from './Drawer/Drawer'; // TODO: namespace!! -export { FieldConfigEditor } from './FieldConfigs/FieldConfigEditor'; export { StringValueEditor, StringOverrideEditor, @@ -135,3 +133,5 @@ export { // Next-gen forms export { default as Forms } from './Forms'; export { ValuePicker } from './ValuePicker/ValuePicker'; +export { fieldMatchersUI } from './MatchersUI/fieldMatchersUI'; +export { standardFieldConfigEditorRegistry } from './FieldConfigs/standardFieldConfigEditorRegistry'; diff --git a/packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx similarity index 89% rename from packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.tsx rename to public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx index 16b51367a29..bd7a8acc355 100644 --- a/packages/grafana-ui/src/components/FieldConfigs/FieldConfigEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx @@ -6,13 +6,16 @@ import { DataFrame, FieldPropertyEditorItem, DynamicConfigValue, + VariableSuggestionsScope, } from '@grafana/data'; -import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry'; -import Forms from '../Forms'; -import { fieldMatchersUI } from '../MatchersUI/fieldMatchersUI'; -import { ControlledCollapse } from '../Collapse/Collapse'; -import { ValuePicker } from '../ValuePicker/ValuePicker'; - +import { + standardFieldConfigEditorRegistry, + Forms, + fieldMatchersUI, + ControlledCollapse, + ValuePicker, +} from '@grafana/ui'; +import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv'; interface Props { config: FieldConfigSource; custom?: FieldConfigEditorRegistry; // custom fields @@ -89,12 +92,21 @@ export class FieldConfigEditor extends React.PureComponent { }; renderEditor(item: FieldPropertyEditorItem, custom: boolean) { + const { data } = this.props; const config = this.props.config.defaults; const value = custom ? (config.custom ? config.custom[item.id] : undefined) : (config as any)[item.id]; return ( - this.setDefaultValue(item.id, v, custom)} /> + this.setDefaultValue(item.id, v, custom)} + context={{ + data, + getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope), + }} + /> ); } @@ -169,7 +181,11 @@ export class FieldConfigEditor extends React.PureComponent { this.onDynamicConfigValueChange(i, j, value); }} item={item} - context={{} as any} + context={{ + data, + getSuggestions: (scope?: VariableSuggestionsScope) => + getDataLinksVariableSuggestions(data, scope), + }} /> ); diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx index 4f98435abba..e28b1d2e3c6 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent, CSSProperties } from 'react'; +import React, { PureComponent } from 'react'; import { GrafanaTheme, FieldConfigSource, @@ -9,18 +9,10 @@ import { SelectableValue, TimeRange, } from '@grafana/data'; -import { - stylesFactory, - Forms, - FieldConfigEditor, - CustomScrollbar, - selectThemeVariant, - ControlledCollapse, -} from '@grafana/ui'; +import { stylesFactory, Forms, CustomScrollbar, selectThemeVariant, ControlledCollapse } from '@grafana/ui'; import { css, cx } from 'emotion'; import config from 'app/core/config'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants'; import { PanelModel } from '../../state/PanelModel'; import { DashboardModel } from '../../state/DashboardModel'; @@ -36,6 +28,8 @@ import { DisplayMode, displayModes } from './types'; import { PanelEditorTabs } from './PanelEditorTabs'; import { DashNavTimeControls } from '../DashNav/DashNavTimeControls'; import { LocationState, CoreEvents } from 'app/types'; +import { calculatePanelSize } from './utils'; +import { FieldConfigEditor } from './FieldConfigEditor'; const getStyles = stylesFactory((theme: GrafanaTheme) => { const handleColor = selectThemeVariant( @@ -407,28 +401,6 @@ export class PanelEditor extends PureComponent { } } -function calculatePanelSize(mode: DisplayMode, width: number, height: number, panel: PanelModel): CSSProperties { - if (mode === DisplayMode.Fill) { - return { width, height }; - } - const colWidth = (window.innerWidth - GRID_CELL_VMARGIN * 4) / GRID_COLUMN_COUNT; - const pWidth = colWidth * panel.gridPos.w; - const pHeight = GRID_CELL_HEIGHT * panel.gridPos.h; - const scale = Math.min(width / pWidth, height / pHeight); - - if (mode === DisplayMode.Exact && pWidth <= width && pHeight <= height) { - return { - width: pWidth, - height: pHeight, - }; - } - - return { - width: pWidth * scale, - height: pHeight * scale, - }; -} - const mapStateToProps = (state: StoreState) => ({ location: state.location, }); diff --git a/public/app/features/dashboard/components/PanelEditor/utils.ts b/public/app/features/dashboard/components/PanelEditor/utils.ts new file mode 100644 index 00000000000..fe36d462fde --- /dev/null +++ b/public/app/features/dashboard/components/PanelEditor/utils.ts @@ -0,0 +1,26 @@ +import { CSSProperties } from 'react'; +import { PanelModel } from '../../state/PanelModel'; +import { GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, GRID_CELL_HEIGHT } from 'app/core/constants'; +import { DisplayMode } from './types'; + +export function calculatePanelSize(mode: DisplayMode, width: number, height: number, panel: PanelModel): CSSProperties { + if (mode === DisplayMode.Fill) { + return { width, height }; + } + const colWidth = (window.innerWidth - GRID_CELL_VMARGIN * 4) / GRID_COLUMN_COUNT; + const pWidth = colWidth * panel.gridPos.w; + const pHeight = GRID_CELL_HEIGHT * panel.gridPos.h; + const scale = Math.min(width / pWidth, height / pHeight); + + if (mode === DisplayMode.Exact && pWidth <= width && pHeight <= height) { + return { + width: pWidth, + height: pHeight, + }; + } + + return { + width: pWidth * scale, + height: pHeight * scale, + }; +} diff --git a/public/app/features/panel/panellinks/link_srv.ts b/public/app/features/panel/panellinks/link_srv.ts index 7807c20b9f4..b24437366c6 100644 --- a/public/app/features/panel/panellinks/link_srv.ts +++ b/public/app/features/panel/panellinks/link_srv.ts @@ -6,7 +6,7 @@ import { appendQueryToUrl, toUrlParams } from 'app/core/utils/url'; import { sanitizeUrl } from 'app/core/utils/text'; import { getConfig } from 'app/core/config'; import locationUtil from 'app/core/utils/location_util'; -import { VariableSuggestion, VariableOrigin, DataLinkBuiltInVars } from '@grafana/ui'; +import { DataLinkBuiltInVars } from '@grafana/ui'; import { DataLink, KeyValue, @@ -16,6 +16,9 @@ import { ScopedVars, FieldType, Field, + VariableSuggestion, + VariableOrigin, + VariableSuggestionsScope, } from '@grafana/data'; const timeRangeVars = [ @@ -180,21 +183,33 @@ const getDataFrameVars = (dataFrames: DataFrame[]) => { return suggestions; }; -export const getDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => { +export const getDataLinksVariableSuggestions = ( + dataFrames: DataFrame[], + scope?: VariableSuggestionsScope +): VariableSuggestion[] => { const valueTimeVar = { value: `${DataLinkBuiltInVars.valueTime}`, label: 'Time', documentation: 'Time value of the clicked datapoint (in ms epoch)', origin: VariableOrigin.Value, }; - return [ - ...seriesVars, - ...getFieldVars(dataFrames), - ...valueVars, - valueTimeVar, - ...getDataFrameVars(dataFrames), - ...getPanelLinksVariableSuggestions(), - ]; + const includeValueVars = scope === VariableSuggestionsScope.Values; + + return includeValueVars + ? [ + ...seriesVars, + ...getFieldVars(dataFrames), + ...valueVars, + valueTimeVar, + ...getDataFrameVars(dataFrames), + ...getPanelLinksVariableSuggestions(), + ] + : [ + ...seriesVars, + ...getFieldVars(dataFrames), + ...getDataFrameVars(dataFrames), + ...getPanelLinksVariableSuggestions(), + ]; }; export const getCalculationValueDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => { diff --git a/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx b/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx index 37adb02470b..a006e684119 100644 --- a/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx +++ b/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { css } from 'emotion'; -import { Button, FormField, VariableSuggestion, DataLinkInput, stylesFactory } from '@grafana/ui'; +import { VariableSuggestion } from '@grafana/data'; +import { Button, FormField, DataLinkInput, stylesFactory } from '@grafana/ui'; import { DataLinkConfig } from '../types'; const getStyles = stylesFactory(() => ({ diff --git a/public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx b/public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx index 7800955209c..b6364bf2cd7 100644 --- a/public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx +++ b/public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { css } from 'emotion'; -import { Button, DataLinkBuiltInVars, stylesFactory, useTheme, VariableOrigin } from '@grafana/ui'; -import { GrafanaTheme } from '@grafana/data'; +import { Button, DataLinkBuiltInVars, stylesFactory, useTheme } from '@grafana/ui'; +import { GrafanaTheme, VariableOrigin } from '@grafana/data'; import { DataLinkConfig } from '../types'; import { DataLink } from './DataLink'; diff --git a/public/app/plugins/datasource/loki/configuration/DerivedField.tsx b/public/app/plugins/datasource/loki/configuration/DerivedField.tsx index 9498e80eb86..27e4d7a202f 100644 --- a/public/app/plugins/datasource/loki/configuration/DerivedField.tsx +++ b/public/app/plugins/datasource/loki/configuration/DerivedField.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { css } from 'emotion'; -import { Button, FormField, VariableSuggestion, DataLinkInput, stylesFactory } from '@grafana/ui'; +import { Button, FormField, DataLinkInput, stylesFactory } from '@grafana/ui'; +import { VariableSuggestion } from '@grafana/data'; import { DerivedFieldConfig } from '../types'; const getStyles = stylesFactory(() => ({ diff --git a/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx b/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx index e634ab364ff..1e947c60dda 100644 --- a/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx +++ b/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { css } from 'emotion'; -import { Button, DataLinkBuiltInVars, stylesFactory, useTheme, VariableOrigin } from '@grafana/ui'; -import { GrafanaTheme } from '@grafana/data'; +import { Button, DataLinkBuiltInVars, stylesFactory, useTheme } from '@grafana/ui'; +import { GrafanaTheme, VariableOrigin } from '@grafana/data'; import { DerivedFieldConfig } from '../types'; import { DerivedField } from './DerivedField'; import { DebugSection } from './DebugSection'; diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index ab0169bb543..f7fc9aa986e 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -11,9 +11,15 @@ import { DataProcessor } from './data_processor'; import { axesEditorComponent } from './axes_editor'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; -import { VariableSuggestion } from '@grafana/ui'; import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest'; -import { getColorFromHexRgbOrName, PanelEvents, DataFrame, DataLink, DateTimeInput } from '@grafana/data'; +import { + getColorFromHexRgbOrName, + PanelEvents, + DataFrame, + DataLink, + DateTimeInput, + VariableSuggestion, +} from '@grafana/data'; import { GraphContextMenuCtrl } from './GraphContextMenuCtrl'; import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';