Browse Source

Merge branch 'more-options' into 'main'

UI for all options

See merge request chartwerk/grafana-chartwerk-panel!6
merge-requests/6/merge
rozetko 2 years ago
parent
commit
68fbe0f47d
  1. 102
      src/components/editors/IconsEditor.tsx
  2. 5
      src/components/editors/NotSupportedText.tsx
  3. 132
      src/components/editors/ThresholdsEditor.tsx
  4. 62
      src/components/editors/UseMetricEditor.tsx
  5. 85
      src/module.ts

102
src/components/editors/IconsEditor.tsx

@ -121,7 +121,6 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ic
};
const onConditionChange = (iconIdx: number, conditionIdx: number, field: keyof IconConfig, value: any) => {
console.log(value);
// @ts-ignore
icons[iconIdx][field][conditionIdx] = value;
@ -138,52 +137,63 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ic
{icons.map((icon, iconIdx) => {
return (
<div key={iconIdx} className={styles.icon}>
<IconButton name="trash-alt" onClick={() => removeIcon(iconIdx)}></IconButton>
<Input
type="url"
placeholder="Image URL"
value={icon.url}
onChange={(evt) => onIconFieldChange(iconIdx, 'url', (evt.target as any).value)}
/>
<IconButton
name="trash-alt"
onClick={() => removeIcon(iconIdx)}
tooltip="Delete Icon"
></IconButton>
<div className={styles.row}>
<Input
type="url"
placeholder="Image URL"
value={icon.url}
onChange={(evt) => onIconFieldChange(iconIdx, 'url', (evt.target as any).value)}
/>
</div>
<RadioButtonGroup
value={icon.position}
options={positionOptions}
onChange={(newVal) => onIconFieldChange(iconIdx, 'position', newVal)}
/>
<Slider
value={icon.size}
min={1}
max={100}
step={1}
onAfterChange={(newVal) => onIconFieldChange(iconIdx, 'size', newVal)}
/>
<div className={styles.slider}>
<Slider
value={icon.size}
min={1}
max={100}
step={1}
onAfterChange={(newVal) => onIconFieldChange(iconIdx, 'size', newVal)}
/>
</div>
{icon.conditions.map((condition, conditionIdx) => {
return (
<HorizontalGroup key={conditionIdx}>
<FieldNamePicker
context={context}
value={icon.metrics[conditionIdx]}
onChange={(newVal: any) => onConditionChange(iconIdx, conditionIdx, 'metrics', newVal)}
item={fieldNamePickerSettings}
/>
<RadioButtonGroup
value={icon.conditions[conditionIdx]}
options={conditionOptions}
onChange={(newVal) => onConditionChange(iconIdx, conditionIdx, 'conditions', newVal)}
/>
<Input
placeholder="value"
value={icon.values[conditionIdx]}
onChange={(evt) =>
onConditionChange(iconIdx, conditionIdx, 'values', (evt.target as any).value)
}
/>
<IconButton
name="trash-alt"
onClick={() => removeCondition(iconIdx, conditionIdx)}
></IconButton>
</HorizontalGroup>
<div className={styles.condition}>
<HorizontalGroup key={conditionIdx} >
<FieldNamePicker
context={context}
value={icon.metrics[conditionIdx]}
onChange={(newVal: any) => onConditionChange(iconIdx, conditionIdx, 'metrics', newVal)}
item={fieldNamePickerSettings}
/>
<RadioButtonGroup
value={icon.conditions[conditionIdx]}
options={conditionOptions}
onChange={(newVal) => onConditionChange(iconIdx, conditionIdx, 'conditions', newVal)}
/>
<Input
placeholder="value"
value={icon.values[conditionIdx]}
onChange={(evt) =>
onConditionChange(iconIdx, conditionIdx, 'values', (evt.target as any).value)
}
/>
<IconButton
name="trash-alt"
onClick={() => removeCondition(iconIdx, conditionIdx)}
tooltip="Delete Condition"
></IconButton>
</HorizontalGroup>
</div>
);
})}
<Button variant="secondary" onClick={() => addCondition(iconIdx)}>
@ -206,6 +216,9 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ic
interface IconsEditorStyles {
icons: string;
icon: string;
condition: string;
slider: string;
row: string;
}
const getStyles = stylesFactory((theme: GrafanaTheme): IconsEditorStyles => {
@ -221,9 +234,18 @@ const getStyles = stylesFactory((theme: GrafanaTheme): IconsEditorStyles => {
padding-bottom: ${theme.spacing.formSpacingBase * 2}px;
&:last-child {
border: none;
margin-bottom: 0;
}
`,
condition: css`
margin-bottom: ${theme.spacing.xxs};
`,
slider: css`
padding-bottom: ${theme.spacing.xxs};
`,
row: css`
margin-bottom: ${theme.spacing.sm};
`,
};
});

5
src/components/editors/NotSupportedText.tsx

@ -0,0 +1,5 @@
import React from 'react';
export function NotSupportedText() {
return <div>To be supported soon...</div>
}

132
src/components/editors/ThresholdsEditor.tsx

@ -1,21 +1,25 @@
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker';
import { StandardEditorProps } from '@grafana/data';
import { GrafanaTheme, StandardEditorProps } from '@grafana/data';
import {
Button,
ColorPicker,
HorizontalGroup,
IconButton,
InlineField,
InlineLabel,
InlineSwitch,
Input,
stylesFactory,
ThemeContext,
} from '@grafana/ui';
import React from 'react';
import { css } from 'emotion';
import * as _ from 'lodash';
interface Props {
defaultColor: string;
arcBackground: string;
@ -65,76 +69,72 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
return (
<ThemeContext.Consumer>
{() => {
// const styles = getStyles(theme.v1);
{(theme) => {
const styles = getStyles(theme.v1);
return (
<div>
<Input
type="text"
value={'Default Gauge Color'}
disabled
prefix={
<div>
<ColorPicker
color={config.defaultColor}
onChange={(val) => onFieldChange('defaultColor', val)}
enableNamedColors={true}
/>
</div>
}
/>
<Input
type="text"
value={'Arc Background'}
disabled
prefix={
<div>
<ColorPicker
color={config.arcBackground}
onChange={(val) => onFieldChange('arcBackground', val)}
enableNamedColors={true}
/>
</div>
}
/>
<InlineField label="Default Gauge Color">
<InlineLabel>
<ColorPicker
color={config.defaultColor}
onChange={(val) => onFieldChange('defaultColor', val)}
enableNamedColors={false}
/>
</InlineLabel>
</InlineField>
<InlineField label="Arc Background">
<InlineLabel>
<ColorPicker
color={config.arcBackground}
onChange={(val) => onFieldChange('arcBackground', val)}
enableNamedColors={false}
/>
</InlineLabel>
</InlineField>
{config.thresholds.map((threshold, thresholdIdx) => {
return (
<HorizontalGroup key={thresholdIdx}>
<Input
type="text"
value={'Color'}
disabled
prefix={
<div>
<ColorPicker
color={threshold.color}
onChange={(newVal) => onThresholdFieldChange(thresholdIdx, 'color', newVal)}
enableNamedColors={true}
/>
</div>
}
/>
<InlineField label="Use metric">
<InlineSwitch
value={threshold.useMetric}
onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'useMetric', (evt.target as any).checked)}
/>
</InlineField>
{threshold.useMetric ? (
<FieldNamePicker
context={context}
value={threshold.metricName as string}
onChange={(newVal: any) => onThresholdFieldChange(thresholdIdx, 'metricName', newVal)}
item={fieldNamePickerSettings}
/>
) : (
<Input
placeholder="value"
value={threshold.value}
onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'value', (evt.target as any).value)}
/>
)}
<IconButton name="trash-alt" onClick={() => removeThreshold(thresholdIdx)}></IconButton>
<InlineField>
{threshold.useMetric ? (
<FieldNamePicker
context={context}
value={threshold.metricName as string}
onChange={(newVal: any) => onThresholdFieldChange(thresholdIdx, 'metricName', newVal)}
item={fieldNamePickerSettings}
/>
) : (
<Input
placeholder="value"
value={threshold.value}
onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'value', (evt.target as any).value)}
/>
)}
</InlineField>
<InlineField label="Color">
<InlineLabel>
<ColorPicker
color={threshold.color}
onChange={(newVal) => onThresholdFieldChange(thresholdIdx, 'color', newVal)}
enableNamedColors={false}
/>
</InlineLabel>
</InlineField>
<InlineField>
<InlineLabel width={0} className={styles.deleteButton}>
<IconButton
name="trash-alt"
onClick={() => removeThreshold(thresholdIdx)}
tooltip="Delete Threshold"
></IconButton>
</InlineLabel>
</InlineField>
</HorizontalGroup>
);
})}
@ -147,3 +147,15 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
</ThemeContext.Consumer>
);
}
interface ThresholdsEditorStyles {
deleteButton: string;
}
const getStyles = stylesFactory((theme: GrafanaTheme): ThresholdsEditorStyles => {
return {
deleteButton: css`
margin-right: 0px!important;
`,
};
});

62
src/components/editors/UseMetricEditor.tsx

@ -0,0 +1,62 @@
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker';
import { StandardEditorProps } from '@grafana/data';
import {
HorizontalGroup,
InlineField,
InlineSwitch,
Input,
} from '@grafana/ui';
import React from 'react';
import * as _ from 'lodash';
type UseMetricConfig = {
useMetric: boolean;
value?: number;
metricName?: string;
};
const fieldNamePickerSettings = {
settings: { width: 24 },
} as any;
export function UseMetricEditor({ onChange, value, context }: StandardEditorProps<UseMetricConfig>) {
const config = value;
const onFieldChange = (field: keyof UseMetricConfig, value: any) => {
// @ts-ignore
config[field] = value;
onChange(config);
}
return (
<HorizontalGroup>
<InlineField label="Use metric">
<InlineSwitch
value={config.useMetric}
onChange={(evt) => onFieldChange('useMetric', (evt.target as any).checked)}
/>
</InlineField>
<InlineField>
{config.useMetric ? (
<FieldNamePicker
context={context}
value={config.metricName as string}
onChange={(newVal: any) => onFieldChange('metricName', newVal)}
item={fieldNamePickerSettings}
/>
) : (
<Input
placeholder="value"
value={config.value}
onChange={(evt) => onFieldChange('value', (evt.target as any).value)}
/>
)}
</InlineField>
</HorizontalGroup>
)
}

85
src/module.ts

@ -1,11 +1,15 @@
import { PanelOptions, Pod } from './types';
import { Panel } from './components/Panel';
import { IconsEditor } from './components/editors/IconsEditor';
import { ThresholdsEditor } from './components/editors/ThresholdsEditor';
import { NotSupportedText } from './components/editors/NotSupportedText';
import { UseMetricEditor } from './components/editors/UseMetricEditor';
import { PanelPlugin } from '@grafana/data';
export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((builder) => {
return builder
.addRadio({
@ -18,21 +22,26 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
{
label: 'Gauge',
value: Pod.GAUGE,
description: 'Enable gauge pod',
},
{
label: 'Line',
value: Pod.LINE,
description: 'Enable line pod',
},
{
label: 'Bar',
value: Pod.BAR,
description: 'Enable bar pod',
},
],
},
})
.addCustomEditor({
id: 'notSupportedText',
name: 'This visualization is not supported',
category: ['Visualization'],
path: '',
showIf: (config) => config.visualizationType !== Pod.GAUGE,
editor: NotSupportedText as any,
})
.addFieldNamePicker({
name: 'Value',
@ -40,46 +49,55 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
})
.addNumberInput({
path: 'gauge.min.value',
name: 'Min',
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE && !config.gauge.min.useMetric,
})
.addFieldNamePicker({
// TODO: defaults?
.addCustomEditor({
id: 'min',
name: 'Min',
path: 'gauge.min.metricName',
path: 'gauge.min',
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE && config.gauge.min.useMetric,
})
.addBooleanSwitch({
path: 'gauge.min.useMetric',
name: 'Use metric',
defaultValue: false,
showIf: (config) => config.visualizationType === Pod.GAUGE,
editor: UseMetricEditor as any,
}).
addCustomEditor({
id: 'max',
name: 'Max',
path: 'gauge.max',
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
editor: UseMetricEditor as any,
})
.addNumberInput({
path: 'gauge.max.value',
name: 'Max',
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE && !config.gauge.max.useMetric,
// note: `gauge.unit` will contain unit name, not it's string representation
// to format value with unit, use `getValueFormat` function from `@grafana/data`
.addUnitPicker({
path: 'gauge.unit',
name: 'Unit',
category: ['Value Format'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
})
.addFieldNamePicker({
name: 'Max',
path: 'gauge.max.metricName',
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE && config.gauge.max.useMetric,
.addNumberInput({
path: 'gauge.decimals',
name: 'Decimals',
settings: {
placeholder: 'auto',
min: 0,
max: 5,
},
category: ['Value Format'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
})
.addBooleanSwitch({
path: 'gauge.max.useMetric',
name: 'Use metric',
defaultValue: false,
category: ['Extremum'],
.addSliderInput({
path: 'gauge.size',
defaultValue: 20,
name: 'Size (px)',
settings: {
min: 1,
max: 50,
},
category: ['Value Format'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
})
.addBooleanSwitch({
path: 'gauge.reversed',
name: 'Reversed',
@ -87,6 +105,7 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
category: ['Direction'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
})
.addCustomEditor({
id: 'icons',
path: 'gauge.icons',

Loading…
Cancel
Save