diff --git a/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json b/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json new file mode 100644 index 00000000000..7a00c1f3fcf --- /dev/null +++ b/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json @@ -0,0 +1,436 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 157, + "links": [], + "liveNow": false, + "panels": [ + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 20 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 22, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "basemap": { + "config": {}, + "name": "Layer 0", + "type": "default" + }, + "controls": { + "mouseWheelZoom": true, + "showAttribution": true, + "showDebug": false, + "showScale": false, + "showZoom": true + }, + "layers": [ + { + "config": { + "rules": [ + { + "check": { + "operation": "eq", + "property": "name", + "value": "Greenland" + }, + "style": { + "color": { + "fixed": "light-blue" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + { + "check": { + "operation": "eq", + "property": "name", + "value": "Antarctica" + }, + "style": { + "color": { + "fixed": "#FCE2DE" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + { + "check": { + "operation": "eq", + "property": "name", + "value": "Canada" + }, + "style": { + "color": { + "fixed": "#37872D" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + { + "check": { + "operation": "eq", + "property": "name", + "value": "Mexico" + }, + "style": { + "color": { + "fixed": "#1F60C4" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + } + ], + "src": "public/maps/countries.geojson", + "style": { + "color": { + "fixed": "dark-green" + }, + "opacity": 0.1, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + "name": "Countries", + "type": "geojson" + }, + { + "config": { + "showLegend": true, + "style": { + "color": { + "fixed": "dark-blue" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/square.svg", + "mode": "fixed" + }, + "text": { + "field": "Count", + "fixed": "", + "mode": "field" + }, + "textConfig": { + "fontSize": 16, + "offsetX": 10, + "offsetY": 10, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + "location": { + "mode": "auto" + }, + "name": "Flights", + "type": "markers" + } + ], + "view": { + "id": "coords", + "lat": 42.826559, + "lon": -96.868893, + "zoom": 3.58 + } + }, + "pluginVersion": "8.3.0-pre", + "targets": [ + { + "csvFileName": "flight_info_by_state.csv", + "refId": "A", + "scenarioId": "csv_file" + } + ], + "title": "Multi layers", + "type": "geomap" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 20 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 22, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "options": { + "basemap": { + "config": {}, + "name": "Layer 0", + "type": "default" + }, + "controls": { + "mouseWheelZoom": true, + "showAttribution": true, + "showDebug": false, + "showScale": false, + "showZoom": true + }, + "layers": [ + { + "config": { + "showLegend": true, + "style": { + "color": { + "fixed": "dark-green" + }, + "opacity": 0.7, + "rotation": { + "field": "Lng", + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "fixed": 10, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/plane.svg", + "mode": "fixed" + }, + "text": { + "field": "Lng", + "fixed": "", + "mode": "field" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 18, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + "name": "Layer 2", + "type": "markers" + } + ], + "view": { + "id": "coords", + "lat": 42.826559, + "lon": -96.868893, + "zoom": 3.58 + } + }, + "pluginVersion": "8.3.0-pre", + "targets": [ + { + "csvFileName": "flight_info_by_state.csv", + "refId": "A", + "scenarioId": "csv_file" + } + ], + "title": "Markers", + "type": "geomap" + } + ], + "schemaVersion": 33, + "style": "dark", + "tags": [ + "gdev", + "geomap", + "panel-tests" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - Geomap Multi Layers", + "uid": "2jFpEvp7z", + "version": 8, + "weekStart": "" +} \ No newline at end of file diff --git a/packages/grafana-ui/src/components/MatchersUI/utils.ts b/packages/grafana-ui/src/components/MatchersUI/utils.ts index 913994a10f0..ccbf9545081 100644 --- a/packages/grafana-ui/src/components/MatchersUI/utils.ts +++ b/packages/grafana-ui/src/components/MatchersUI/utils.ts @@ -68,7 +68,8 @@ export function useFieldDisplayNames(data: DataFrame[], filter?: (field: Field) export function useSelectOptions( displayNames: FrameFieldsDisplayNames, currentName?: string, - firstItem?: SelectableValue + firstItem?: SelectableValue, + fieldType?: string ): Array> { return useMemo(() => { let found = false; @@ -81,11 +82,13 @@ export function useSelectOptions( found = true; } const field = displayNames.fields.get(name); - options.push({ - value: name, - label: name, - icon: field ? getFieldTypeIcon(field) : undefined, - }); + if (!fieldType || fieldType === field?.type) { + options.push({ + value: name, + label: name, + icon: field ? getFieldTypeIcon(field) : undefined, + }); + } } for (const name of displayNames.raw) { if (!displayNames.display.has(name)) { @@ -106,5 +109,5 @@ export function useSelectOptions( }); } return options; - }, [displayNames, currentName, firstItem]); + }, [displayNames, currentName, firstItem, fieldType]); } diff --git a/public/app/features/dimensions/editors/NumberInput.tsx b/public/app/features/dimensions/editors/NumberInput.tsx index 674543e0cf2..da21132a776 100644 --- a/public/app/features/dimensions/editors/NumberInput.tsx +++ b/public/app/features/dimensions/editors/NumberInput.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { Input } from '@grafana/ui'; +import { Field, Input } from '@grafana/ui'; interface Props { value?: number; @@ -13,16 +13,18 @@ interface Props { interface State { text: string; + inputCorrected: boolean; } /** * This is an Input field that will call `onChange` for blur and enter */ export class NumberInput extends PureComponent { - state: State = { text: '' }; + state: State = { text: '', inputCorrected: false }; componentDidMount() { this.setState({ + ...this.state, text: isNaN(this.props.value!) ? '' : `${this.props.value}`, }); } @@ -30,6 +32,7 @@ export class NumberInput extends PureComponent { componentDidUpdate(oldProps: Props) { if (this.props.value !== oldProps.value) { this.setState({ + ...this.state, text: isNaN(this.props.value!) ? '' : `${this.props.value}`, }); } @@ -42,11 +45,30 @@ export class NumberInput extends PureComponent { value = e.currentTarget.valueAsNumber; } this.props.onChange(value); + this.setState({ ...this.state, inputCorrected: false }); }; onChange = (e: React.FocusEvent) => { + let newValue: string | undefined = undefined; + let corrected = false; + const min = this.props.min; + const max = this.props.max; + const currValue = e.currentTarget.valueAsNumber; + if (!Number.isNaN(currValue)) { + if (min != null && currValue < min) { + newValue = min.toString(); + corrected = true; + } else if (max != null && currValue > max) { + newValue = max.toString(); + corrected = true; + } else { + newValue = e.currentTarget.value; + } + } this.setState({ - text: e.currentTarget.value, + ...this.state, + text: newValue ? newValue : '', + inputCorrected: corrected, }); }; @@ -58,20 +80,22 @@ export class NumberInput extends PureComponent { render() { const { placeholder } = this.props; - const { text } = this.state; + const { text, inputCorrected } = this.state; return ( - + + + ); } } diff --git a/public/app/features/dimensions/editors/ScalarDimensionEditor.tsx b/public/app/features/dimensions/editors/ScalarDimensionEditor.tsx new file mode 100644 index 00000000000..4efef4cb0ec --- /dev/null +++ b/public/app/features/dimensions/editors/ScalarDimensionEditor.tsx @@ -0,0 +1,116 @@ +import React, { FC, useCallback } from 'react'; +import { FieldType, GrafanaTheme2, SelectableValue, StandardEditorProps } from '@grafana/data'; +import { ScalarDimensionConfig, ScalarDimensionMode, ScalarDimensionOptions } from '../types'; +import { InlineField, InlineFieldRow, RadioButtonGroup, Select, useStyles2 } from '@grafana/ui'; +import { useFieldDisplayNames, useSelectOptions } from '@grafana/ui/src/components/MatchersUI/utils'; +import { NumberInput } from './NumberInput'; +import { css } from '@emotion/css'; + +const fixedValueOption: SelectableValue = { + label: 'Fixed value', + value: '_____fixed_____', +}; + +const scalarOptions = [ + { label: 'Mod', value: ScalarDimensionMode.Mod, description: 'Use field values, mod from max' }, + { label: 'Clamped', value: ScalarDimensionMode.Clamped, description: 'Use field values, clamped to max and min' }, +]; + +export const ScalarDimensionEditor: FC> = ( + props +) => { + const { value, context, onChange, item } = props; + const { settings } = item; + + const DEFAULT_VALUE = 0; + + const fieldName = value?.field; + const isFixed = Boolean(!fieldName); + const names = useFieldDisplayNames(context.data); + const selectOptions = useSelectOptions(names, fieldName, fixedValueOption, FieldType.number); + + const styles = useStyles2(getStyles); + + const onSelectChange = useCallback( + (selection: SelectableValue) => { + const field = selection.value; + if (field && field !== fixedValueOption.value) { + onChange({ + ...value, + field, + }); + } else { + const fixed = value.fixed ?? DEFAULT_VALUE; + onChange({ + ...value, + field: undefined, + fixed, + }); + } + }, + [onChange, value] + ); + + const onModeChange = useCallback( + (mode) => { + onChange({ + ...value, + mode, + }); + }, + [onChange, value] + ); + + const onValueChange = useCallback( + (v: number | undefined) => { + onChange({ + ...value, + field: undefined, + fixed: v ?? DEFAULT_VALUE, + }); + }, + [onChange, value] + ); + + const val = value ?? {}; + const mode = value?.mode ?? ScalarDimensionMode.Mod; + const selectedOption = isFixed ? fixedValueOption : selectOptions.find((v) => v.value === fieldName); + return ( + <> +
+ + + + + +