diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8f5fd7916af..04fcddc4b15 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -92,6 +92,7 @@ go.sum @grafana/backend-platform /public/app/plugins/panel/timeseries @grafana/grafana-bi-squad /public/app/plugins/panel/geomap @grafana/grafana-edge-squad /public/app/plugins/panel/canvas @grafana/grafana-edge-squad +/public/app/plugins/panel/icon @grafana/grafana-edge-squad /scripts/build/release-packages.sh @grafana/plugins-platform /scripts/circle-release-next-packages.sh @grafana/plugins-platform /scripts/ci-frontend-metrics.sh @grafana/user-essentials @grafana/plugins-platform @grafana/grafana-bi-squad diff --git a/public/app/features/canvas/elements/icon.tsx b/public/app/features/canvas/elements/icon.tsx index b4dedd128ac..8717e60675f 100644 --- a/public/app/features/canvas/elements/icon.tsx +++ b/public/app/features/canvas/elements/icon.tsx @@ -14,7 +14,7 @@ import { isString } from 'lodash'; import { LineConfig } from '../types'; import { DimensionContext } from 'app/features/dimensions/context'; -interface IconConfig { +export interface IconConfig { path?: ResourceDimensionConfig; fill?: ColorDimensionConfig; stroke?: LineConfig; diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 0ea05edba30..87d11ab3b4c 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -71,6 +71,7 @@ import * as alertGroupsPanel from 'app/plugins/panel/alertGroups/module'; // Async loaded panels const geomapPanel = async () => await import(/* webpackChunkName: "geomapPanel" */ 'app/plugins/panel/geomap/module'); const canvasPanel = async () => await import(/* webpackChunkName: "canvasPanel" */ 'app/plugins/panel/canvas/module'); +const iconPanel = async () => await import(/* webpackChunkName: "iconPanel" */ 'app/plugins/panel/icon/module'); const builtInPlugins: any = { 'app/plugins/datasource/graphite/module': graphitePlugin, @@ -102,6 +103,7 @@ const builtInPlugins: any = { 'app/plugins/panel/xychart/module': xyChartPanel, 'app/plugins/panel/geomap/module': geomapPanel, 'app/plugins/panel/canvas/module': canvasPanel, + 'app/plugins/panel/icon/module': iconPanel, 'app/plugins/panel/dashlist/module': dashListPanel, 'app/plugins/panel/pluginlist/module': pluginsListPanel, 'app/plugins/panel/alertlist/module': alertListPanel, diff --git a/public/app/plugins/panel/canvas/CanvasPanel.tsx b/public/app/plugins/panel/canvas/CanvasPanel.tsx index 85a942c5b3a..6b793edcd8a 100644 --- a/public/app/plugins/panel/canvas/CanvasPanel.tsx +++ b/public/app/plugins/panel/canvas/CanvasPanel.tsx @@ -89,7 +89,7 @@ export class CanvasPanel extends Component { }; shouldComponentUpdate(nextProps: Props) { - const { width, height, data, renderCounter } = this.props; + const { width, height, data } = this.props; let changed = false; if (width !== nextProps.width || height !== nextProps.height) { @@ -110,10 +110,6 @@ export class CanvasPanel extends Component { changed = true; } - if (renderCounter !== nextProps.renderCounter) { - changed = true; - } - return changed; } diff --git a/public/app/plugins/panel/icon/IconPanel.tsx b/public/app/plugins/panel/icon/IconPanel.tsx new file mode 100644 index 00000000000..7e4cced6a90 --- /dev/null +++ b/public/app/plugins/panel/icon/IconPanel.tsx @@ -0,0 +1,67 @@ +import React, { Component } from 'react'; +import { PanelProps } from '@grafana/data'; +import { PanelOptions } from './models.gen'; +import { ElementState } from 'app/features/canvas/runtime/element'; +import { iconItem } from 'app/features/canvas/elements/icon'; +import { + ColorDimensionConfig, + DimensionContext, + getColorDimensionFromData, + getResourceDimensionFromData, + getScaleDimensionFromData, + getTextDimensionFromData, + ResourceDimensionConfig, + ScaleDimensionConfig, + TextDimensionConfig, +} from 'app/features/dimensions'; + +interface Props extends PanelProps {} + +export class IconPanel extends Component { + private element: ElementState; + + constructor(props: Props) { + super(props); + this.element = this.initElement(props); + } + + initElement = (props: Props) => { + this.element = new ElementState(iconItem, props.options.root as any); + this.element.updateSize(props.width, props.height); + this.element.updateData(this.dims); + return this.element; + }; + + dims: DimensionContext = { + getColor: (color: ColorDimensionConfig) => getColorDimensionFromData(this.props.data, color), + getScale: (scale: ScaleDimensionConfig) => getScaleDimensionFromData(this.props.data, scale), + getText: (text: TextDimensionConfig) => getTextDimensionFromData(this.props.data, text), + getResource: (res: ResourceDimensionConfig) => getResourceDimensionFromData(this.props.data, res), + }; + + shouldComponentUpdate(nextProps: Props) { + const { width, height, data } = this.props; + let changed = false; + + if (width !== nextProps.width || height !== nextProps.height) { + this.element.updateSize(nextProps.width, nextProps.height); + changed = true; + } + if (data !== nextProps.data) { + this.element.updateData(this.dims); + changed = true; + } + + // Reload the element when options change + if (this.props.options?.root !== nextProps.options?.root) { + this.initElement(nextProps); + changed = true; + } + return changed; + } + + render() { + const { width, height } = this.props; + return
{this.element.render()}
; + } +} diff --git a/public/app/plugins/panel/icon/README.md b/public/app/plugins/panel/icon/README.md new file mode 100644 index 00000000000..6fc6f0b2271 --- /dev/null +++ b/public/app/plugins/panel/icon/README.md @@ -0,0 +1,3 @@ +# Icon panel - Native Plugin + +The icon panel is **included** with Grafana. diff --git a/public/app/plugins/panel/icon/img/icn-icon.svg b/public/app/plugins/panel/icon/img/icn-icon.svg new file mode 100644 index 00000000000..b4ff2621278 --- /dev/null +++ b/public/app/plugins/panel/icon/img/icn-icon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/app/plugins/panel/icon/models.gen.ts b/public/app/plugins/panel/icon/models.gen.ts new file mode 100644 index 00000000000..e5809913201 --- /dev/null +++ b/public/app/plugins/panel/icon/models.gen.ts @@ -0,0 +1,23 @@ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// NOTE: This file will be auto generated from models.cue +// It is currenty hand written but will serve as the target for cuetsy +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +import { CanvasElementOptions } from 'app/features/canvas'; +import { IconConfig } from 'app/features/canvas/elements/icon'; +import { ResourceDimensionMode } from 'app/features/dimensions'; + +export interface PanelOptions { + root: Omit, 'type'>; // type is forced +} + +export const defaultPanelOptions: PanelOptions = { + root: { + config: { + path: { + mode: ResourceDimensionMode.Fixed, + fixed: 'img/icons/unicons/analysis.svg', + }, + }, + }, +}; diff --git a/public/app/plugins/panel/icon/module.tsx b/public/app/plugins/panel/icon/module.tsx new file mode 100644 index 00000000000..af043d9b07f --- /dev/null +++ b/public/app/plugins/panel/icon/module.tsx @@ -0,0 +1,27 @@ +import { PanelPlugin } from '@grafana/data'; + +import { IconPanel } from './IconPanel'; +import { defaultPanelOptions, PanelOptions } from './models.gen'; +import { IconConfig, iconItem } from 'app/features/canvas/elements/icon'; +import { optionBuilder } from '../canvas/editor/options'; +import { CanvasElementOptions } from 'app/features/canvas'; + +export const plugin = new PanelPlugin(IconPanel) + .setNoPadding() // extend to panel edges + .useFieldConfig() + .setPanelOptions((builder) => { + builder.addNestedOptions>({ + category: ['Icon'], + path: 'root', + + // Dynamically fill the selected element + build: (builder, ctx) => { + iconItem.registerOptionsUI!(builder, ctx); + + optionBuilder.addBackground(builder, ctx); + optionBuilder.addBorder(builder, ctx); + }, + + defaultValue: defaultPanelOptions.root as any, + }); + }); diff --git a/public/app/plugins/panel/icon/plugin.json b/public/app/plugins/panel/icon/plugin.json new file mode 100644 index 00000000000..0fa8f63010f --- /dev/null +++ b/public/app/plugins/panel/icon/plugin.json @@ -0,0 +1,18 @@ +{ + "type": "panel", + "name": "Icon", + "id": "icon", + "state": "alpha", + + "info": { + "description": "Show icon based on data", + "author": { + "name": "Grafana Labs", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/icn-icon.svg", + "large": "img/icn-icon.svg" + } + } +}