From 118b87ee8feaa2488a4ae696753559045185ba47 Mon Sep 17 00:00:00 2001 From: Adela Almasan <88068998+adela-almasan@users.noreply.github.com> Date: Wed, 30 Mar 2022 09:41:13 -0500 Subject: [PATCH] Geomap: Display legend (#46886) * Display legend for fixed colors and field; Hide tooltip on base layer; --- .../app/plugins/panel/geomap/GeomapPanel.tsx | 31 ++++++++-- .../panel/geomap/editor/layerEditor.tsx | 16 ++--- .../geomap/layers/data/MarkersLegend.tsx | 59 +++++++++++++++---- .../panel/geomap/layers/data/markersLayer.tsx | 5 +- .../plugins/panel/geomap/layers/registry.ts | 2 +- 5 files changed, 88 insertions(+), 25 deletions(-) diff --git a/public/app/plugins/panel/geomap/GeomapPanel.tsx b/public/app/plugins/panel/geomap/GeomapPanel.tsx index cbd6febaa7f..758cc4443e2 100644 --- a/public/app/plugins/panel/geomap/GeomapPanel.tsx +++ b/public/app/plugins/panel/geomap/GeomapPanel.tsx @@ -46,6 +46,7 @@ type Props = PanelProps; interface State extends OverlayProps { ttip?: GeomapHoverPayload; ttipOpen: boolean; + legends: ReactNode[]; } export interface GeomapLayerActions { @@ -82,7 +83,7 @@ export class GeomapPanel extends Component { constructor(props: Props) { super(props); - this.state = { ttipOpen: false }; + this.state = { ttipOpen: false, legends: [] }; this.subs.add( this.props.eventBus.subscribe(PanelEditExitedEvent, (evt) => { if (this.mapDiv && this.props.id === evt.payload) { @@ -114,7 +115,7 @@ export class GeomapPanel extends Component { return true; // always? } - /** This funciton will actually update the JSON model */ + /** This function will actually update the JSON model */ private doOptionsUpdate(selected: number) { const { options, onOptionsChange } = this.props; const layers = this.layers; @@ -124,7 +125,7 @@ export class GeomapPanel extends Component { layers: layers.slice(1).map((v) => v.options), }); - // Notify the the panel editor + // Notify the panel editor if (this.panelContext.onInstanceStateChange) { this.panelContext.onInstanceStateChange({ map: this.map, @@ -133,6 +134,8 @@ export class GeomapPanel extends Component { actions: this.actions, }); } + + this.setState({ legends: this.getLegends() }); } getNextLayerName = () => { @@ -308,6 +311,8 @@ export class GeomapPanel extends Component { actions: this.actions, }); } + + this.setState({ legends: this.getLegends() }); }; clearTooltip = () => { @@ -447,6 +452,9 @@ export class GeomapPanel extends Component { return false; } + // Just to trigger a state update + this.setState({ legends: [] }); + this.layers = layers; this.doOptionsUpdate(layerIndex); return true; @@ -481,6 +489,7 @@ export class GeomapPanel extends Component { if (!options.name) { options.name = this.getNextLayerName(); } + const UID = options.name; const state: MapLayerState = { // UID, // unique name when added to the map (it may change and will need special handling) @@ -496,6 +505,7 @@ export class GeomapPanel extends Component { this.updateLayer(UID, cfg); }, }; + this.byName.set(UID, state); (state.layer as any).__state = state; return state; @@ -597,15 +607,26 @@ export class GeomapPanel extends Component { this.setState({ topRight }); } + getLegends() { + const legends: ReactNode[] = []; + for (const state of this.layers) { + if (state.handler.legend) { + legends.push(
{state.handler.legend}
); + } + } + + return legends; + } + render() { - const { ttip, ttipOpen, topRight, bottomLeft } = this.state; + const { ttip, ttipOpen, topRight, legends } = this.state; return ( <>
- +
diff --git a/public/app/plugins/panel/geomap/editor/layerEditor.tsx b/public/app/plugins/panel/geomap/editor/layerEditor.tsx index 4ae3c1cd85e..22103c678ac 100644 --- a/public/app/plugins/panel/geomap/editor/layerEditor.tsx +++ b/public/app/plugins/panel/geomap/editor/layerEditor.tsx @@ -4,7 +4,7 @@ import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/O import { defaultMarkersConfig } from '../layers/data/markersLayer'; import { hasAlphaPanels } from 'app/core/config'; import { MapLayerState } from '../types'; -import { get as lodashGet } from 'lodash'; +import { get as lodashGet, isEqual } from 'lodash'; import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils'; import { addLocationFields } from 'app/features/geo/editor/locationEditor'; @@ -93,12 +93,14 @@ export function getLayerEditor(opts: LayerEditorOptions): NestedPanelOptions; size?: DimensionSupplier; + layerName?: string; + styleConfig?: StyleConfigState; } export function MarkersLegend(props: MarkersLegendProps) { - const { color } = props; + const { layerName, styleConfig } = props; const theme = useTheme2(); + const style = getStyles(theme); - if (!color || (!color.field && color.fixed)) { + if (!styleConfig) { + return <>; + } + const { color, opacity} = styleConfig?.base ?? {}; + const symbol = styleConfig?.config.symbol?.fixed; + + const colorField = styleConfig.dims?.color?.field; + + if (color && symbol && !colorField) { + return ( +
+
+ + {layerName} +
+
+ ) + } + + if (!colorField) { return <>; } - const style = getStyles(theme); - const fmt = (v: any) => `${formattedValueToString(color.field!.display!(v))}`; - const colorMode = getFieldColorModeForField(color!.field!); + const fmt = (v: any) => `${formattedValueToString(colorField.display!(v))}`; + const colorMode = getFieldColorModeForField(colorField); if (colorMode.isContinuous && colorMode.getColors) { const colors = colorMode.getColors(config.theme2); - const colorRange = getMinMaxAndDelta(color.field!); + const colorRange = getMinMaxAndDelta(colorField); // TODO: explore showing mean on the gradiant scale // const stats = reduceField({ // field: color.field!, @@ -40,7 +67,7 @@ export function MarkersLegend(props: MarkersLegendProps) { return ( <> - +
c).join(', ')}` }} @@ -52,12 +79,12 @@ export function MarkersLegend(props: MarkersLegendProps) { ); } - const thresholds = color.field?.config?.thresholds; + const thresholds = colorField?.config?.thresholds; if (!thresholds || thresholds.steps.length < 2) { return
; // don't show anything in the legend } - const items = getThresholdItems(color.field!.config, config.theme2); + const items = getThresholdItems(colorField!.config, config.theme2); return (
@@ -95,6 +122,16 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => ({ legendItem: css` white-space: nowrap; `, + fixedColorContainer: css` + min-width: 80px; + font-size: ${theme.typography.bodySmall.fontSize}; + `, + legendSymbol: css` + height: 10px; + width: 10px; + margin: auto; + margin-right: 4px; + `, gradientContainer: css` min-width: 200px; display: flex; diff --git a/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx b/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx index 1f428b3c7bb..64dcb32ab0d 100644 --- a/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx +++ b/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx @@ -56,7 +56,9 @@ export const markersLayer: MapLayerRegistryItem = { /** * Function that configures transformation and returns a transformer + * @param map * @param options + * @param theme */ create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { // Assert default values @@ -137,8 +139,9 @@ export const markersLayer: MapLayerRegistryItem = { // Post updates to the legend component if (legend) { legendProps.next({ - color: style.dims?.color, + styleConfig: style, size: style.dims?.size, + layerName: options.name, }); } diff --git a/public/app/plugins/panel/geomap/layers/registry.ts b/public/app/plugins/panel/geomap/layers/registry.ts index de99df94e5f..123828a9de1 100644 --- a/public/app/plugins/panel/geomap/layers/registry.ts +++ b/public/app/plugins/panel/geomap/layers/registry.ts @@ -22,7 +22,7 @@ export const defaultBaseLayer: MapLayerRegistryItem = { if (serverLayerType) { const layer = geomapLayerRegistry.getIfExists(serverLayerType); if (!layer) { - throw new Error('Invalid basemap configuraiton on server'); + throw new Error('Invalid basemap configuration on server'); } return layer.create(map, config.geomapDefaultBaseLayerConfig!, theme); }