diff --git a/packages/grafana-ui/src/components/Graph/GraphContextMenu.tsx b/packages/grafana-ui/src/components/Graph/GraphContextMenu.tsx index 7159ae51481..cfc3fc6f4f5 100644 --- a/packages/grafana-ui/src/components/Graph/GraphContextMenu.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphContextMenu.tsx @@ -13,7 +13,7 @@ import { import { useTheme } from '../../themes'; import { HorizontalGroup } from '../Layout/Layout'; import { FormattedValueDisplay } from '../FormattedValueDisplay/FormattedValueDisplay'; -import { SeriesIcon } from '../Legend/SeriesIcon'; +import { SeriesIcon } from '../VizLegend/SeriesIcon'; import { css } from 'emotion'; export type ContextDimensions = { [key in keyof T]: [number, number | undefined] | null }; diff --git a/packages/grafana-ui/src/components/Graph/GraphLegend.story.tsx b/packages/grafana-ui/src/components/Graph/GraphLegend.story.tsx deleted file mode 100644 index 629ecb0e70b..00000000000 --- a/packages/grafana-ui/src/components/Graph/GraphLegend.story.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; -import { GraphLegend } from './GraphLegend'; -import { action } from '@storybook/addon-actions'; -import { select, number } from '@storybook/addon-knobs'; -import { withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory'; -import { generateLegendItems } from '../Legend/Legend'; -import { LegendPlacement, LegendDisplayMode } from '../Legend/Legend'; - -export default { - title: 'Visualizations/Graph/GraphLegend', - component: GraphLegend, - decorators: [withHorizontallyCenteredStory], -}; - -const getStoriesKnobs = (isList = false) => { - const statsToDisplay = select( - 'Stats to display', - { - none: [], - 'single (min)': [{ text: '10ms', title: 'min', numeric: 10 }], - 'multiple (min, max)': [ - { text: '10ms', title: 'min', numeric: 10 }, - { text: '100ms', title: 'max', numeric: 100 }, - ], - }, - [] - ); - - const numberOfSeries = number('Number of series', 3); - - const containerWidth = select( - 'Container width', - { - Small: '200px', - Medium: '500px', - 'Full width': '100%', - }, - '100%' - ); - - const legendPlacement = select( - 'Legend placement', - { - bottom: 'bottom', - right: 'right', - }, - 'bottom' - ); - - return { - statsToDisplay, - numberOfSeries, - containerWidth, - legendPlacement, - }; -}; - -export const list = () => { - const { statsToDisplay, numberOfSeries, containerWidth, legendPlacement } = getStoriesKnobs(true); - return ( -
- { - action('Series label clicked')(item, event); - }} - onSeriesColorChange={(label, color) => { - action('Series color changed')(label, color); - }} - onSeriesAxisToggle={(label, useRightYAxis) => { - action('Series axis toggle')(label, useRightYAxis); - }} - onToggleSort={sortBy => { - action('Toggle legend sort')(sortBy); - }} - placement={legendPlacement} - /> -
- ); -}; - -export const table = () => { - const { statsToDisplay, numberOfSeries, containerWidth, legendPlacement } = getStoriesKnobs(); - return ( -
- { - action('Series label clicked')(item); - }} - onSeriesColorChange={(label, color) => { - action('Series color changed')(label, color); - }} - onSeriesAxisToggle={(label, useRightYAxis) => { - action('Series axis toggle')(label, useRightYAxis); - }} - onToggleSort={sortBy => { - action('Toggle legend sort')(sortBy); - }} - placement={legendPlacement} - /> -
- ); -}; diff --git a/packages/grafana-ui/src/components/Graph/GraphLegend.tsx b/packages/grafana-ui/src/components/Graph/GraphLegend.tsx deleted file mode 100644 index 7110082fb34..00000000000 --- a/packages/grafana-ui/src/components/Graph/GraphLegend.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { useContext } from 'react'; -import { LegendProps, LegendItem, LegendDisplayMode } from '../Legend/Legend'; -import { GraphLegendListItem, GraphLegendTableRow } from './GraphLegendItem'; -import { SeriesColorChangeHandler, SeriesAxisToggleHandler } from './GraphWithLegend'; -import { LegendTable } from '../Legend/LegendTable'; -import { LegendList } from '../Legend/LegendList'; -import union from 'lodash/union'; -import sortBy from 'lodash/sortBy'; -import { ThemeContext } from '../../themes/ThemeContext'; -import { css } from 'emotion'; - -export interface GraphLegendProps extends LegendProps { - displayMode: LegendDisplayMode; - sortBy?: string; - sortDesc?: boolean; - onSeriesColorChange?: SeriesColorChangeHandler; - onSeriesAxisToggle?: SeriesAxisToggleHandler; - onToggleSort?: (sortBy: string) => void; - onLabelClick?: (item: LegendItem, event: React.MouseEvent) => void; -} - -export const GraphLegend: React.FunctionComponent = ({ - items, - displayMode, - sortBy: sortKey, - sortDesc, - onToggleSort, - onSeriesAxisToggle, - placement, - className, - ...graphLegendItemProps -}) => { - const theme = useContext(ThemeContext); - - if (displayMode === LegendDisplayMode.Table) { - const columns = items - .map(item => { - if (item.displayValues) { - return item.displayValues.map(i => i.title); - } - return []; - }) - .reduce( - (acc, current) => { - return union( - acc, - current.filter(item => !!item) - ); - }, - [''] - ) as string[]; - - const sortedItems = sortKey - ? sortBy(items, item => { - if (item.displayValues) { - const stat = item.displayValues.filter(stat => stat.title === sortKey)[0]; - return stat && stat.numeric; - } - return undefined; - }) - : items; - - const legendTableEvenRowBackground = theme.isDark ? theme.palette.dark6 : theme.palette.gray5; - - return ( - ( - { - if (onSeriesAxisToggle) { - onSeriesAxisToggle(item.label, item.yAxis === 1 ? 2 : 1); - } - }} - className={css` - background: ${index % 2 === 0 ? legendTableEvenRowBackground : 'none'}; - `} - {...graphLegendItemProps} - /> - )} - onToggleSort={onToggleSort} - /> - ); - } - return ( - ( - { - if (onSeriesAxisToggle) { - onSeriesAxisToggle(item.label, item.yAxis === 1 ? 2 : 1); - } - }} - {...graphLegendItemProps} - /> - )} - /> - ); -}; - -GraphLegend.displayName = 'GraphLegend'; diff --git a/packages/grafana-ui/src/components/Graph/GraphTooltip/SeriesTable.tsx b/packages/grafana-ui/src/components/Graph/GraphTooltip/SeriesTable.tsx index 22012c1ccd7..70dca4cb07f 100644 --- a/packages/grafana-ui/src/components/Graph/GraphTooltip/SeriesTable.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphTooltip/SeriesTable.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { stylesFactory } from '../../../themes/stylesFactory'; import { GrafanaTheme, GraphSeriesValue } from '@grafana/data'; import { css, cx } from 'emotion'; -import { SeriesIcon } from '../../Legend/SeriesIcon'; +import { SeriesIcon } from '../../VizLegend/SeriesIcon'; import { useTheme } from '../../../themes'; export interface SeriesTableRowProps { diff --git a/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx b/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx index 19a8a1d7fd8..04d9682bfcf 100644 --- a/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx @@ -3,8 +3,7 @@ import React from 'react'; import { select, text } from '@storybook/addon-knobs'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { GraphWithLegend, GraphWithLegendProps } from './GraphWithLegend'; - -import { LegendPlacement, LegendDisplayMode } from '../Legend/Legend'; +import { LegendPlacement, LegendDisplayMode } from '../VizLegend/types'; import { GraphSeriesXY, FieldType, ArrayVector, dateTime, FieldColorModeId } from '@grafana/data'; export default { diff --git a/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx b/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx index c1998365925..e7051af3079 100644 --- a/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx @@ -5,21 +5,19 @@ import { css } from 'emotion'; import { GraphSeriesValue } from '@grafana/data'; import { Graph, GraphProps } from './Graph'; -import { LegendRenderOptions, LegendItem, LegendDisplayMode } from '../Legend/Legend'; -import { GraphLegend } from './GraphLegend'; +import { VizLegendItem, LegendDisplayMode, SeriesColorChangeHandler, LegendPlacement } from '../VizLegend/types'; +import { VizLegend } from '../VizLegend/VizLegend'; import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar'; import { stylesFactory } from '../../themes'; -export type SeriesOptionChangeHandler = (label: string, option: TOption) => void; -export type SeriesColorChangeHandler = SeriesOptionChangeHandler; -export type SeriesAxisToggleHandler = SeriesOptionChangeHandler; - -export interface GraphWithLegendProps extends GraphProps, LegendRenderOptions { +export interface GraphWithLegendProps extends GraphProps { legendDisplayMode: LegendDisplayMode; + placement: LegendPlacement; + hideEmpty?: boolean; + hideZero?: boolean; sortLegendBy?: string; sortLegendDesc?: boolean; onSeriesColorChange?: SeriesColorChangeHandler; - onSeriesAxisToggle?: SeriesAxisToggleHandler; onSeriesToggle?: (label: string, event: React.MouseEvent) => void; onToggleSort: (sortBy: string) => void; } @@ -60,7 +58,6 @@ export const GraphWithLegend: React.FunctionComponent = (p sortLegendDesc, legendDisplayMode, placement, - onSeriesAxisToggle, onSeriesColorChange, onSeriesToggle, onToggleSort, @@ -75,7 +72,7 @@ export const GraphWithLegend: React.FunctionComponent = (p } = props; const { graphContainer, wrapper, legendContainer } = getGraphWithLegendStyles(props); - const legendItems = series.reduce((acc, s) => { + const legendItems = series.reduce((acc, s) => { return shouldHideLegendItem(s.data, hideEmpty, hideZero) ? acc : acc.concat([ @@ -112,7 +109,7 @@ export const GraphWithLegend: React.FunctionComponent = (p {legendDisplayMode !== LegendDisplayMode.Hidden && (
- = (p } }} onSeriesColorChange={onSeriesColorChange} - onSeriesAxisToggle={onSeriesAxisToggle} onToggleSort={onToggleSort} /> diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx index 256e4a3ac0a..65b72fed0ac 100644 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { GraphNG } from './GraphNG'; import { dateTime } from '@grafana/data'; -import { LegendDisplayMode } from '../Legend/Legend'; +import { LegendDisplayMode } from '../VizLegend/types'; import { prepDataForStorybook } from '../../utils/storybook/data'; import { useTheme } from '../../themes'; import { text, select } from '@storybook/addon-knobs'; diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index 64a84201c50..4486e7b2f5f 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -16,8 +16,8 @@ import { PlotProps } from '../uPlot/types'; import { AxisPlacement, DrawStyle, GraphFieldConfig, PointVisibility } from '../uPlot/config'; import { useTheme } from '../../themes'; import { VizLayout } from '../VizLayout/VizLayout'; -import { LegendDisplayMode, LegendItem, LegendOptions } from '../Legend/Legend'; -import { GraphLegend } from '../Graph/GraphLegend'; +import { LegendDisplayMode, VizLegendItem, VizLegendOptions } from '../VizLegend/types'; +import { VizLegend } from '../VizLegend/VizLegend'; import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder'; import { useRevision } from '../uPlot/hooks'; import { GraphNGLegendEvent, GraphNGLegendEventMode } from './types'; @@ -30,7 +30,7 @@ export interface XYFieldMatchers { } export interface GraphNGProps extends Omit { data: DataFrame[]; - legend?: LegendOptions; + legend?: VizLegendOptions; fields?: XYFieldMatchers; // default will assume timeseries data onLegendClick?: (event: GraphNGLegendEvent) => void; } @@ -55,7 +55,7 @@ export const GraphNG: React.FC = ({ }) => { const alignedFrameWithGapTest = useMemo(() => alignDataFrames(data, fields), [data, fields]); const theme = useTheme(); - const legendItemsRef = useRef([]); + const legendItemsRef = useRef([]); const hasLegend = useRef(legend && legend.displayMode !== LegendDisplayMode.Hidden); const alignedFrame = alignedFrameWithGapTest?.frame; const getDataFrameFieldIndex = alignedFrameWithGapTest?.getDataFrameFieldIndex; @@ -68,7 +68,7 @@ export const GraphNG: React.FC = ({ }, []); const onLabelClick = useCallback( - (legend: LegendItem, event: React.MouseEvent) => { + (legend: VizLegendItem, event: React.MouseEvent) => { const { fieldIndex } = legend; if (!onLegendClick || !fieldIndex) { @@ -128,7 +128,7 @@ export const GraphNG: React.FC = ({ }); } - const legendItems: LegendItem[] = []; + const legendItems: VizLegendItem[] = []; for (let i = 0; i < alignedFrame.fields.length; i++) { const field = alignedFrame.fields[i]; @@ -217,7 +217,7 @@ export const GraphNG: React.FC = ({ if (hasLegend && legendItemsRef.current.length > 0) { legendElement = ( - { - const numberOfSeries = number('Number of series', 3); - const containerWidth = select( - 'Container width', - { - Small: '200px', - Medium: '500px', - 'Full width': '100%', - }, - '100%' - ); - - const rawRenderer = (item: LegendItem) => ( - <> - Label: {item.label}, Color: {item.color}, disabled:{' '} - {item.disabled ? 'yes' : 'no'} - - ); - - // eslint-disable-next-line react/display-name - const customRenderer = (component: React.ComponentType) => (item: LegendItem) => - React.createElement(component, { - item, - onLabelClick: action('GraphLegendItem label clicked'), - onSeriesColorChange: action('Series color changed'), - onToggleAxis: action('Y-axis toggle'), - }); - - const typeSpecificRenderer = table - ? { - 'Custom renderer(GraphLegendTablerow)': 'custom-table', - } - : { - 'Custom renderer(GraphLegendListItem)': 'custom-list', - }; - const legendItemRenderer = select( - 'Item rendered', - { - 'Raw renderer': 'raw', - ...typeSpecificRenderer, - }, - 'raw' - ); - - const rightAxisSeries = text('Right y-axis series, i.e. A,C', ''); - - const legendPlacement = select( - 'Legend placement', - { - bottom: 'bottom', - right: 'right', - }, - 'bottom' - ); - - return { - numberOfSeries, - containerWidth, - itemRenderer: - legendItemRenderer === 'raw' - ? rawRenderer - : customRenderer(legendItemRenderer === 'custom-list' ? GraphLegendListItem : GraphLegendTableRow), - rightAxisSeries, - legendPlacement, - }; -}; - -export default { - title: 'Visualizations/Legend', - component: LegendList, - subcomponents: { LegendTable }, -}; - -export const list = () => { - const { numberOfSeries, itemRenderer, containerWidth, rightAxisSeries, legendPlacement } = getStoriesKnobs(); - let items = generateLegendItems(numberOfSeries); - - items = items.map(i => { - if ( - rightAxisSeries - .split(',') - .map(s => s.trim()) - .indexOf(i.label.split('-')[0]) > -1 - ) { - i.yAxis = 2; - } - - return i; - }); - return ( -
- -
- ); -}; - -export const table = () => { - const { numberOfSeries, itemRenderer, containerWidth, rightAxisSeries, legendPlacement } = getStoriesKnobs(true); - let items = generateLegendItems(numberOfSeries); - - items = items.map(i => { - if ( - rightAxisSeries - .split(',') - .map(s => s.trim()) - .indexOf(i.label.split('-')[0]) > -1 - ) { - i.yAxis = 2; - } - - return { - ...i, - info: [ - { title: 'min', text: '14.42', numeric: 14.427101844163694 }, - { title: 'max', text: '18.42', numeric: 18.427101844163694 }, - ], - }; - }); - return ( -
- -
- ); -}; diff --git a/packages/grafana-ui/src/components/Legend/Legend.tsx b/packages/grafana-ui/src/components/Legend/Legend.tsx deleted file mode 100644 index 2df7e6720d2..00000000000 --- a/packages/grafana-ui/src/components/Legend/Legend.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { DataFrameFieldIndex, DisplayValue } from '@grafana/data'; - -import { LegendList } from './LegendList'; -import { LegendTable } from './LegendTable'; -import tinycolor from 'tinycolor2'; - -export const generateLegendItems = (numberOfSeries: number, statsToDisplay?: DisplayValue[]): LegendItem[] => { - const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); - - return [...new Array(numberOfSeries)].map((item, i) => { - return { - label: `${alphabet[i].toUpperCase()}-series`, - color: tinycolor.fromRatio({ h: i / alphabet.length, s: 1, v: 1 }).toHexString(), - yAxis: 1, - displayValues: statsToDisplay || [], - }; - }); -}; - -export enum LegendDisplayMode { - List = 'list', - Table = 'table', - Hidden = 'hidden', -} -export interface LegendBasicOptions { - displayMode: LegendDisplayMode; -} - -export interface LegendRenderOptions { - placement: LegendPlacement; - hideEmpty?: boolean; - hideZero?: boolean; -} - -export type LegendPlacement = 'bottom' | 'right'; - -export interface LegendOptions extends LegendBasicOptions, LegendRenderOptions {} - -export interface LegendItem { - label: string; - color: string; - yAxis: number; - disabled?: boolean; - displayValues?: DisplayValue[]; - fieldIndex?: DataFrameFieldIndex; -} - -export interface LegendComponentProps { - className?: string; - items: LegendItem[]; - placement: LegendPlacement; - // Function to render given item - itemRenderer?: (item: LegendItem, index: number) => JSX.Element; -} - -export interface LegendProps extends LegendComponentProps {} - -export { LegendList, LegendTable }; diff --git a/packages/grafana-ui/src/components/Legend/LegendList.tsx b/packages/grafana-ui/src/components/Legend/LegendList.tsx deleted file mode 100644 index 6808f9fb8cb..00000000000 --- a/packages/grafana-ui/src/components/Legend/LegendList.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useContext } from 'react'; -import { LegendComponentProps, LegendItem } from './Legend'; -import { InlineList } from '../List/InlineList'; -import { List } from '../List/List'; -import { css, cx } from 'emotion'; -import { ThemeContext } from '../../themes/ThemeContext'; -import { stylesFactory } from '../../themes'; -import { GrafanaTheme } from '@grafana/data'; - -const getStyles = stylesFactory((theme: GrafanaTheme) => ({ - item: css` - padding-left: 10px; - display: flex; - font-size: ${theme.typography.size.sm}; - white-space: nowrap; - `, - wrapper: css` - display: flex; - flex-wrap: wrap; - justify-content: space-between; - width: 100%; - `, - section: css` - display: flex; - `, - sectionRight: css` - justify-content: flex-end; - flex-grow: 1; - `, -})); - -export const LegendList: React.FunctionComponent = ({ - items, - itemRenderer, - placement, - className, -}) => { - const theme = useContext(ThemeContext); - const styles = getStyles(theme); - - const renderItem = (item: LegendItem, index: number) => { - return {itemRenderer ? itemRenderer(item, index) : item.label}; - }; - - const getItemKey = (item: LegendItem) => `${item.label}`; - - return placement === 'bottom' ? ( -
-
- item.yAxis === 1)} renderItem={renderItem} getItemKey={getItemKey} /> -
-
- item.yAxis !== 1)} renderItem={renderItem} getItemKey={getItemKey} /> -
-
- ) : ( - - ); -}; - -LegendList.displayName = 'LegendList'; diff --git a/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx b/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx deleted file mode 100644 index 787b818c003..00000000000 --- a/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { css, cx } from 'emotion'; -import { SeriesColorPicker } from '../ColorPicker/ColorPicker'; -import { SeriesIcon, SeriesIconProps } from './SeriesIcon'; - -interface LegendSeriesIconProps { - disabled: boolean; - color: string; - yAxis: number; - onColorChange: (color: string) => void; - onToggleAxis?: () => void; -} - -export const LegendSeriesIcon: React.FunctionComponent = ({ - disabled, - yAxis, - color, - onColorChange, - onToggleAxis, -}) => { - let iconProps: SeriesIconProps = { - color, - }; - - if (!disabled) { - iconProps = { - ...iconProps, - className: 'pointer', - }; - } - - return disabled ? ( - - - - ) : ( - - {({ ref, showColorPicker, hideColorPicker }) => ( - - - - )} - - ); -}; - -LegendSeriesIcon.displayName = 'LegendSeriesIcon'; diff --git a/packages/grafana-ui/src/components/Legend/LegendStatsList.tsx b/packages/grafana-ui/src/components/Legend/LegendStatsList.tsx deleted file mode 100644 index 06b07acfdf9..00000000000 --- a/packages/grafana-ui/src/components/Legend/LegendStatsList.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { InlineList } from '../List/InlineList'; -import { css } from 'emotion'; -import { DisplayValue, formattedValueToString } from '@grafana/data'; -import capitalize from 'lodash/capitalize'; - -const LegendItemStat: React.FunctionComponent<{ stat: DisplayValue }> = ({ stat }) => { - return ( -
- {stat.title && `${capitalize(stat.title)}:`} {formattedValueToString(stat)} -
- ); -}; - -LegendItemStat.displayName = 'LegendItemStat'; - -export const LegendStatsList: React.FunctionComponent<{ stats: DisplayValue[] }> = ({ stats }) => { - if (stats.length === 0) { - return null; - } - return } />; -}; - -LegendStatsList.displayName = 'LegendStatsList'; diff --git a/packages/grafana-ui/src/components/Legend/LegendTable.tsx b/packages/grafana-ui/src/components/Legend/LegendTable.tsx deleted file mode 100644 index ebaf78edfd4..00000000000 --- a/packages/grafana-ui/src/components/Legend/LegendTable.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useContext } from 'react'; -import { css, cx } from 'emotion'; -import { LegendComponentProps } from './Legend'; -import { Icon } from '../Icon/Icon'; -import { ThemeContext } from '../../themes/ThemeContext'; - -export interface LegendTableProps extends LegendComponentProps { - columns: string[]; - sortBy?: string; - sortDesc?: boolean; - onToggleSort?: (sortBy: string) => void; -} - -export const LegendTable: React.FunctionComponent = ({ - items, - columns, - sortBy, - sortDesc, - itemRenderer, - className, - onToggleSort, -}) => { - const theme = useContext(ThemeContext); - - return ( - - - - {columns.map(columnHeader => { - return ( - - ); - })} - - - - {items.map((item, index) => { - return itemRenderer ? ( - itemRenderer(item, index) - ) : ( - - - - ); - })} - -
{ - if (onToggleSort) { - onToggleSort(columnHeader); - } - }} - > - {columnHeader} - {sortBy === columnHeader && ( - - )} -
{item.label}
- ); -}; diff --git a/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx b/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx deleted file mode 100644 index 3bc879780b6..00000000000 --- a/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { Icon } from '../Icon/Icon'; - -export interface SeriesIconProps { - color: string; - className?: string; -} - -export const SeriesIcon: React.FunctionComponent = ({ color, className }) => { - return ; -}; - -SeriesIcon.displayName = 'SeriesIcon'; diff --git a/packages/grafana-ui/src/components/VizLegend/SeriesIcon.tsx b/packages/grafana-ui/src/components/VizLegend/SeriesIcon.tsx new file mode 100644 index 00000000000..950dec6150a --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/SeriesIcon.tsx @@ -0,0 +1,20 @@ +import React, { CSSProperties } from 'react'; + +export interface Props extends React.HTMLAttributes { + color: string; +} + +export const SeriesIcon = React.forwardRef(({ color, className, ...restProps }, ref) => { + const styles: CSSProperties = { + backgroundColor: color, + width: '14px', + height: '4px', + borderRadius: '1px', + display: 'inline-block', + marginRight: '8px', + }; + + return
; +}); + +SeriesIcon.displayName = 'SeriesIcon'; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx new file mode 100644 index 00000000000..1b31300a381 --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx @@ -0,0 +1,184 @@ +import React, { FC, useState } from 'react'; +import { useTheme, VizLegend } from '@grafana/ui'; +import { number, select } from '@storybook/addon-knobs'; +import {} from './VizLegendListItem'; +import { DisplayValue, getColorForTheme, GrafanaTheme } from '@grafana/data'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { LegendDisplayMode, VizLegendItem, LegendPlacement } from './types'; + +const getStoriesKnobs = (table = false) => { + const seriesCount = number('Number of series', 5); + const containerWidth = select( + 'Container width', + { + Small: '200px', + Medium: '500px', + 'Full width': '100%', + }, + '100%' + ); + + return { + seriesCount, + containerWidth, + }; +}; + +export default { + title: 'Visualizations/VizLegend', + component: VizLegend, + decorators: [withCenteredStory], +}; + +interface LegendStoryDemoProps { + name: string; + displayMode: LegendDisplayMode; + placement: LegendPlacement; + seriesCount: number; + stats?: DisplayValue[]; +} + +const LegendStoryDemo: FC = ({ displayMode, seriesCount, name, placement, stats }) => { + const theme = useTheme(); + const [items, setItems] = useState(generateLegendItems(seriesCount, theme, stats)); + + const onSeriesColorChange = (label: string, color: string) => { + setItems( + items.map(item => { + if (item.label === label) { + return { + ...item, + color: color, + }; + } + + return item; + }) + ); + }; + + const onLabelClick = (clickItem: VizLegendItem) => { + setItems( + items.map(item => { + if (item !== clickItem) { + return { + ...item, + disabled: true, + }; + } else { + return { + ...item, + disabled: false, + }; + } + }) + ); + }; + + return ( +

+

{name}

+ +

+ ); +}; + +export const WithNoValues = () => { + const { seriesCount, containerWidth } = getStoriesKnobs(); + + return ( +
+ + + +
+ ); +}; + +export const WithValues = () => { + const { seriesCount, containerWidth } = getStoriesKnobs(); + const stats: DisplayValue[] = [ + { + title: 'Min', + text: '5.00', + numeric: 5, + }, + { + title: 'Max', + text: '10.00', + numeric: 10, + }, + { + title: 'Last', + text: '2.00', + numeric: 2, + }, + ]; + + return ( +
+ + + +
+ ); +}; + +function generateLegendItems( + numberOfSeries: number, + theme: GrafanaTheme, + statsToDisplay?: DisplayValue[] +): VizLegendItem[] { + const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + const colors = ['green', 'blue', 'red', 'purple', 'orange', 'dark-green', 'yellow', 'light-blue'].map(c => + getColorForTheme(c, theme) + ); + + return [...new Array(numberOfSeries)].map((item, i) => { + return { + label: `${alphabet[i].toUpperCase()}-series`, + color: colors[i], + yAxis: 1, + displayValues: statsToDisplay || [], + }; + }); +} diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx new file mode 100644 index 00000000000..3b2180831c7 --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { LegendProps, LegendDisplayMode } from './types'; +import { VizLegendTable } from './VizLegendTable'; +import { VizLegendList } from './VizLegendList'; + +export const VizLegend: React.FunctionComponent = ({ + items, + displayMode, + sortBy: sortKey, + sortDesc, + onToggleSort, + onLabelClick, + onSeriesColorChange, + placement, + className, +}) => { + switch (displayMode) { + case LegendDisplayMode.Table: + return ( + + ); + case LegendDisplayMode.List: + return ( + + ); + default: + return null; + } +}; + +VizLegend.displayName = 'Legend'; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx new file mode 100644 index 00000000000..2f1786368af --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { VizLegendBaseProps, VizLegendItem } from './types'; +import { InlineList } from '../List/InlineList'; +import { List } from '../List/List'; +import { css, cx } from 'emotion'; +import { useStyles } from '../../themes'; +import { GrafanaTheme } from '@grafana/data'; +import { VizLegendListItem } from './VizLegendListItem'; + +export interface Props extends VizLegendBaseProps {} + +export const VizLegendList: React.FunctionComponent = ({ + items, + itemRenderer, + onSeriesColorChange, + onLabelClick, + placement, + className, +}) => { + const styles = useStyles(getStyles); + + if (!itemRenderer) { + /* eslint-disable-next-line react/display-name */ + itemRenderer = item => ( + + ); + } + + const renderItem = (item: VizLegendItem, index: number) => { + return {itemRenderer!(item, index)}; + }; + + const getItemKey = (item: VizLegendItem) => `${item.label}`; + + switch (placement) { + case 'right': + return ( +
+ +
+ ); + case 'bottom': + default: + return ( +
+
+ item.yAxis === 1)} + renderItem={renderItem} + getItemKey={getItemKey} + /> +
+
+ item.yAxis !== 1)} + renderItem={renderItem} + getItemKey={getItemKey} + /> +
+
+ ); + } +}; + +VizLegendList.displayName = 'VizLegendList'; + +const getStyles = (theme: GrafanaTheme) => ({ + item: css` + padding-right: 10px; + display: flex; + font-size: ${theme.typography.size.sm}; + white-space: nowrap; + margin-bottom: ${theme.spacing.xs}; + `, + rightWrapper: css` + margin-left: ${theme.spacing.sm}; + `, + bottomWrapper: css` + display: flex; + flex-wrap: wrap; + justify-content: space-between; + width: 100%; + margin-left: ${theme.spacing.md}; + `, + section: css` + display: flex; + `, + sectionRight: css` + justify-content: flex-end; + flex-grow: 1; + `, +}); diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx new file mode 100644 index 00000000000..ef82fcd26bc --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { css, cx } from 'emotion'; +import { VizLegendSeriesIcon } from './VizLegendSeriesIcon'; +import { VizLegendItem } from './types'; +import { SeriesColorChangeHandler } from './types'; +import { VizLegendStatsList } from './VizLegendStatsList'; +import { useStyles } from '../../themes'; +import { GrafanaTheme } from '@grafana/data'; + +export interface Props { + item: VizLegendItem; + className?: string; + onLabelClick?: (item: VizLegendItem, event: React.MouseEvent) => void; + onSeriesColorChange?: SeriesColorChangeHandler; +} + +export const VizLegendListItem: React.FunctionComponent = ({ item, onSeriesColorChange, onLabelClick }) => { + const styles = useStyles(getStyles); + + return ( +
+ { + if (onSeriesColorChange) { + onSeriesColorChange(item.label, color); + } + }} + yAxis={item.yAxis} + /> +
{ + if (onLabelClick) { + onLabelClick(item, event); + } + }} + className={cx(styles.label, item.disabled && styles.labelDisabled)} + > + {item.label} +
+ + {item.displayValues && } +
+ ); +}; + +VizLegendListItem.displayName = 'VizLegendListItem'; + +const getStyles = (theme: GrafanaTheme) => ({ + label: css` + label: LegendLabel; + cursor: pointer; + white-space: nowrap; + `, + labelDisabled: css` + label: LegendLabelDisabled; + color: ${theme.colors.linkDisabled}; + `, + itemWrapper: css` + display: flex; + white-space: nowrap; + align-items: center; + `, + value: css` + text-align: right; + `, + yAxisLabel: css` + color: ${theme.palette.gray2}; + `, +}); diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendSeriesIcon.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendSeriesIcon.tsx new file mode 100644 index 00000000000..52a7ffb8d74 --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendSeriesIcon.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { SeriesColorPicker } from '../ColorPicker/ColorPicker'; +import { SeriesIcon } from './SeriesIcon'; + +interface Props { + disabled: boolean; + color: string; + yAxis: number; + onColorChange: (color: string) => void; + onToggleAxis?: () => void; +} + +export const VizLegendSeriesIcon: React.FunctionComponent = ({ + disabled, + yAxis, + color, + onColorChange, + onToggleAxis, +}) => { + return disabled ? ( + + ) : ( + + {({ ref, showColorPicker, hideColorPicker }) => ( + + )} + + ); +}; + +VizLegendSeriesIcon.displayName = 'VizLegendSeriesIcon'; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendStatsList.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendStatsList.tsx new file mode 100644 index 00000000000..113e7878edf --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendStatsList.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { InlineList } from '../List/InlineList'; +import { css } from 'emotion'; +import { DisplayValue, formattedValueToString } from '@grafana/data'; +import capitalize from 'lodash/capitalize'; + +const VizLegendItemStat: React.FunctionComponent<{ stat: DisplayValue }> = ({ stat }) => { + const styles = css` + margin-left: 8px; + `; + + return ( +
+ {stat.title && `${capitalize(stat.title)}:`} {formattedValueToString(stat)} +
+ ); +}; + +VizLegendItemStat.displayName = 'VizLegendItemStat'; + +export const VizLegendStatsList: React.FunctionComponent<{ stats: DisplayValue[] }> = ({ stats }) => { + if (stats.length === 0) { + return null; + } + return } />; +}; + +VizLegendStatsList.displayName = 'VizLegendStatsList'; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx new file mode 100644 index 00000000000..7f92396fd76 --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendTable.tsx @@ -0,0 +1,107 @@ +import React, { FC } from 'react'; +import { css, cx } from 'emotion'; +import { VizLegendTableProps } from './types'; +import { Icon } from '../Icon/Icon'; +import { useStyles } from '../../themes/ThemeContext'; +import union from 'lodash/union'; +import sortBy from 'lodash/sortBy'; +import { LegendTableItem } from './VizLegendTableItem'; +import { GrafanaTheme } from '@grafana/data'; + +export const VizLegendTable: FC = ({ + items, + sortBy: sortKey, + sortDesc, + itemRenderer, + className, + onToggleSort, + onLabelClick, + onSeriesColorChange, +}) => { + const styles = useStyles(getStyles); + + const columns = items + .map(item => { + if (item.displayValues) { + return item.displayValues.map(i => i.title); + } + return []; + }) + .reduce( + (acc, current) => { + return union( + acc, + current.filter(item => !!item) + ); + }, + [''] + ) as string[]; + + const sortedItems = sortKey + ? sortBy(items, item => { + if (item.displayValues) { + const stat = item.displayValues.filter(stat => stat.title === sortKey)[0]; + return stat && stat.numeric; + } + return undefined; + }) + : items; + + if (!itemRenderer) { + /* eslint-disable-next-line react/display-name */ + itemRenderer = (item, index) => ( + + ); + } + + return ( + + + + {columns.map(columnHeader => { + return ( + + ); + })} + + + {sortedItems.map(itemRenderer!)} +
{ + if (onToggleSort) { + onToggleSort(columnHeader); + } + }} + > + {columnHeader} + {sortKey === columnHeader && ( + + )} +
+ ); +}; + +const getStyles = (theme: GrafanaTheme) => ({ + table: css` + width: 100%; + margin-left: ${theme.spacing.sm}; + `, + header: css` + color: ${theme.colors.textBlue}; + font-weight: ${theme.typography.weight.semibold}; + border-bottom: 1px solid ${theme.colors.border1}; + padding: ${theme.spacing.xxs} ${theme.spacing.sm}; + text-align: right; + cursor: pointer; + `, + sortIcon: css` + margin-left: ${theme.spacing.sm}; + `, +}); diff --git a/packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx similarity index 51% rename from packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx rename to packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx index d78c2047615..52e31ab81f4 100644 --- a/packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendTableItem.tsx @@ -1,113 +1,40 @@ -import React, { useContext } from 'react'; +import React from 'react'; import { css, cx } from 'emotion'; -import { LegendSeriesIcon } from '../Legend/LegendSeriesIcon'; -import { LegendItem } from '../Legend/Legend'; -import { SeriesColorChangeHandler } from './GraphWithLegend'; -import { LegendStatsList } from '../Legend/LegendStatsList'; -import { ThemeContext } from '../../themes/ThemeContext'; -import { stylesFactory } from '../../themes'; +import { VizLegendSeriesIcon } from './VizLegendSeriesIcon'; +import { VizLegendItem } from './types'; +import { SeriesColorChangeHandler } from './types'; +import { useStyles } from '../../themes/ThemeContext'; +import { styleMixins } from '../../themes'; import { GrafanaTheme, formattedValueToString } from '@grafana/data'; -export interface GraphLegendItemProps { +export interface Props { key?: React.Key; - item: LegendItem; + item: VizLegendItem; className?: string; - onLabelClick?: (item: LegendItem, event: React.MouseEvent) => void; + onLabelClick?: (item: VizLegendItem, event: React.MouseEvent) => void; onSeriesColorChange?: SeriesColorChangeHandler; - onToggleAxis?: () => void; } -export const GraphLegendListItem: React.FunctionComponent = ({ +export const LegendTableItem: React.FunctionComponent = ({ item, onSeriesColorChange, - onToggleAxis, - onLabelClick, -}) => { - const theme = useContext(ThemeContext); - const styles = getStyles(theme); - return ( - <> - { - if (onSeriesColorChange) { - onSeriesColorChange(item.label, color); - } - }} - onToggleAxis={onToggleAxis} - yAxis={item.yAxis} - /> -
{ - if (onLabelClick) { - onLabelClick(item, event); - } - }} - className={cx(styles.label, item.disabled && styles.labelDisabled)} - > - {item.label} -
- - {item.displayValues && } - - ); -}; - -const getStyles = stylesFactory((theme: GrafanaTheme) => { - return { - row: css` - label: LegendRow; - font-size: ${theme.typography.size.sm}; - td { - padding: ${theme.spacing.xxs} ${theme.spacing.sm}; - white-space: nowrap; - } - `, - label: css` - label: LegendLabel; - cursor: pointer; - white-space: nowrap; - `, - labelDisabled: css` - label: LegendLabelDisabled; - color: ${theme.colors.linkDisabled}; - `, - itemWrapper: css` - display: flex; - white-space: nowrap; - `, - value: css` - text-align: right; - `, - yAxisLabel: css` - color: ${theme.palette.gray2}; - `, - }; -}); - -export const GraphLegendTableRow: React.FunctionComponent = ({ - item, - onSeriesColorChange, - onToggleAxis, onLabelClick, className, }) => { - const theme = useContext(ThemeContext); - const styles = getStyles(theme); + const styles = useStyles(getStyles); + return ( - { if (onSeriesColorChange) { onSeriesColorChange(item.label, color); } }} - onToggleAxis={onToggleAxis} yAxis={item.yAxis} />
); }; + +LegendTableItem.displayName = 'LegendTableItem'; + +const getStyles = (theme: GrafanaTheme) => { + const rowHoverBg = styleMixins.hoverColor(theme.colors.bg1, theme); + + return { + row: css` + label: LegendRow; + font-size: ${theme.typography.size.sm}; + border-bottom: 1px solid ${theme.colors.border1}; + td { + padding: ${theme.spacing.xxs} ${theme.spacing.sm}; + white-space: nowrap; + } + + &:hover { + background: ${rowHoverBg}; + } + `, + label: css` + label: LegendLabel; + cursor: pointer; + white-space: nowrap; + `, + labelDisabled: css` + label: LegendLabelDisabled; + color: ${theme.colors.linkDisabled}; + `, + itemWrapper: css` + display: flex; + white-space: nowrap; + align-items: center; + `, + value: css` + text-align: right; + `, + yAxisLabel: css` + color: ${theme.palette.gray2}; + `, + }; +}; diff --git a/packages/grafana-ui/src/components/VizLegend/types.ts b/packages/grafana-ui/src/components/VizLegend/types.ts new file mode 100644 index 00000000000..25ca758fb46 --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/types.ts @@ -0,0 +1,45 @@ +import { DataFrameFieldIndex, DisplayValue } from '@grafana/data'; + +export interface VizLegendBaseProps { + placement: LegendPlacement; + className?: string; + items: VizLegendItem[]; + itemRenderer?: (item: VizLegendItem, index: number) => JSX.Element; + onSeriesColorChange?: SeriesColorChangeHandler; + onLabelClick?: (item: VizLegendItem, event: React.MouseEvent) => void; +} + +export interface VizLegendTableProps extends VizLegendBaseProps { + sortBy?: string; + sortDesc?: boolean; + onToggleSort?: (sortBy: string) => void; +} + +export interface LegendProps extends VizLegendBaseProps, VizLegendTableProps { + displayMode: LegendDisplayMode; +} + +export interface VizLegendItem { + label: string; + color: string; + yAxis: number; + disabled?: boolean; + displayValues?: DisplayValue[]; + fieldIndex?: DataFrameFieldIndex; +} + +export enum LegendDisplayMode { + List = 'list', + Table = 'table', + Hidden = 'hidden', +} + +export type LegendPlacement = 'bottom' | 'right'; + +export interface VizLegendOptions { + displayMode: LegendDisplayMode; + placement: LegendPlacement; +} + +export type SeriesOptionChangeHandler = (label: string, option: TOption) => void; +export type SeriesColorChangeHandler = SeriesOptionChangeHandler; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 5f6c22741dc..7749be6e914 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -69,7 +69,6 @@ export { export { Gauge } from './Gauge/Gauge'; export { Graph } from './Graph/Graph'; -export { GraphLegend } from './Graph/GraphLegend'; export { GraphWithLegend } from './Graph/GraphWithLegend'; export { GraphContextMenu, GraphContextMenuHeader } from './Graph/GraphContextMenu'; export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge'; @@ -77,17 +76,8 @@ export { GraphTooltipOptions } from './Graph/GraphTooltip/types'; export { VizRepeater, VizRepeaterRenderValueProps } from './VizRepeater/VizRepeater'; export { graphTimeFormat, graphTickFormatter } from './Graph/utils'; export { VizLayout, VizLayoutComponentType, VizLayoutLegendProps, VizLayoutProps } from './VizLayout/VizLayout'; - -export { - LegendOptions, - LegendBasicOptions, - LegendRenderOptions, - LegendList, - LegendTable, - LegendItem, - LegendPlacement, - LegendDisplayMode, -} from './Legend/Legend'; +export { VizLegendItem, LegendPlacement, LegendDisplayMode, VizLegendOptions } from './VizLegend/types'; +export { VizLegend } from './VizLegend/VizLegend'; export { Alert, AlertVariant } from './Alert/Alert'; export { GraphSeriesToggler, GraphSeriesTogglerAPI } from './Graph/GraphSeriesToggler'; @@ -109,7 +99,7 @@ export { WithContextMenu } from './ContextMenu/WithContextMenu'; export { DataLinksInlineEditor } from './DataLinks/DataLinksInlineEditor/DataLinksInlineEditor'; export { DataLinkInput } from './DataLinks/DataLinkInput'; export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu'; -export { SeriesIcon } from './Legend/SeriesIcon'; +export { SeriesIcon } from './VizLegend/SeriesIcon'; export { InfoBox } from './InfoBox/InfoBox'; export { FeatureInfoBox } from './InfoBox/FeatureInfoBox'; diff --git a/public/app/features/explore/flotgraph/types.ts b/public/app/features/explore/flotgraph/types.ts index 6e817ac3d91..53421582678 100644 --- a/public/app/features/explore/flotgraph/types.ts +++ b/public/app/features/explore/flotgraph/types.ts @@ -1,4 +1,4 @@ -import { LegendOptions, GraphTooltipOptions, LegendDisplayMode } from '@grafana/ui'; +import { GraphTooltipOptions, LegendDisplayMode, LegendPlacement } from '@grafana/ui'; import { YAxis } from '@grafana/data'; export interface SeriesOptions { @@ -14,7 +14,10 @@ export interface GraphOptions { export interface Options { graph: GraphOptions; - legend: LegendOptions & GraphLegendEditorLegendOptions; + legend: { + displayMode: LegendDisplayMode; + placement: LegendPlacement; + }; series: { [alias: string]: SeriesOptions; }; @@ -35,7 +38,9 @@ export const defaults: Options = { tooltipOptions: { mode: 'single' }, }; -export interface GraphLegendEditorLegendOptions extends LegendOptions { +export interface GraphLegendEditorLegendOptions { + displayMode: LegendDisplayMode; + placement: LegendPlacement; stats?: string[]; decimals?: number; sortBy?: string; diff --git a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx index 448513565e5..31f4a11ff1f 100644 --- a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx +++ b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx @@ -197,7 +197,7 @@ class LegendSeriesIcon extends PureComponent {({ ref, showColorPicker, hideColorPicker }) => ( - + )} diff --git a/public/app/plugins/panel/timeseries/types.ts b/public/app/plugins/panel/timeseries/types.ts index 8a3fd48276b..7051250112e 100644 --- a/public/app/plugins/panel/timeseries/types.ts +++ b/public/app/plugins/panel/timeseries/types.ts @@ -1,4 +1,4 @@ -import { LegendOptions, GraphTooltipOptions } from '@grafana/ui'; +import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui'; export interface GraphOptions { // Redraw as time passes @@ -7,13 +7,6 @@ export interface GraphOptions { export interface Options { graph: GraphOptions; - legend: LegendOptions; + legend: VizLegendOptions; tooltipOptions: GraphTooltipOptions; } - -export interface GraphLegendEditorLegendOptions extends LegendOptions { - stats?: string[]; - decimals?: number; - sortBy?: string; - sortDesc?: boolean; -} diff --git a/public/app/plugins/panel/xychart/types.ts b/public/app/plugins/panel/xychart/types.ts index fcd649f3d9d..21b39893944 100644 --- a/public/app/plugins/panel/xychart/types.ts +++ b/public/app/plugins/panel/xychart/types.ts @@ -1,4 +1,4 @@ -import { LegendOptions, GraphTooltipOptions } from '@grafana/ui'; +import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui'; export interface XYDimensionConfig { frame: number; @@ -9,6 +9,6 @@ export interface XYDimensionConfig { export interface Options { dims: XYDimensionConfig; - legend: LegendOptions; + legend: VizLegendOptions; tooltipOptions: GraphTooltipOptions; }