Browse Source

Merge branch 'more-options' into 'main'

UI for all options

See merge request chartwerk/grafana-chartwerk-panel!6
merge-requests/7/merge
Alexander Velikiy 3 years ago
parent
commit
8b50017393
  1. 30
      src/components/editors/IconsEditor.tsx
  2. 5
      src/components/editors/NotSupportedText.tsx
  3. 88
      src/components/editors/ThresholdsEditor.tsx
  4. 62
      src/components/editors/UseMetricEditor.tsx
  5. 85
      src/module.ts

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

88
src/components/editors/ThresholdsEditor.tsx

@ -1,21 +1,25 @@
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker'; import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker';
import { StandardEditorProps } from '@grafana/data'; import { GrafanaTheme, StandardEditorProps } from '@grafana/data';
import { import {
Button, Button,
ColorPicker, ColorPicker,
HorizontalGroup, HorizontalGroup,
IconButton, IconButton,
InlineField, InlineField,
InlineLabel,
InlineSwitch, InlineSwitch,
Input, Input,
stylesFactory,
ThemeContext, ThemeContext,
} from '@grafana/ui'; } from '@grafana/ui';
import React from 'react'; import React from 'react';
import { css } from 'emotion';
import * as _ from 'lodash'; import * as _ from 'lodash';
interface Props { interface Props {
defaultColor: string; defaultColor: string;
arcBackground: string; arcBackground: string;
@ -65,61 +69,39 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
return ( return (
<ThemeContext.Consumer> <ThemeContext.Consumer>
{() => { {(theme) => {
// const styles = getStyles(theme.v1); const styles = getStyles(theme.v1);
return ( return (
<div> <div>
<Input <InlineField label="Default Gauge Color">
type="text" <InlineLabel>
value={'Default Gauge Color'}
disabled
prefix={
<div>
<ColorPicker <ColorPicker
color={config.defaultColor} color={config.defaultColor}
onChange={(val) => onFieldChange('defaultColor', val)} onChange={(val) => onFieldChange('defaultColor', val)}
enableNamedColors={true} enableNamedColors={false}
/>
</div>
}
/> />
<Input </InlineLabel>
type="text" </InlineField>
value={'Arc Background'}
disabled <InlineField label="Arc Background">
prefix={ <InlineLabel>
<div>
<ColorPicker <ColorPicker
color={config.arcBackground} color={config.arcBackground}
onChange={(val) => onFieldChange('arcBackground', val)} onChange={(val) => onFieldChange('arcBackground', val)}
enableNamedColors={true} enableNamedColors={false}
/>
</div>
}
/> />
</InlineLabel>
</InlineField>
{config.thresholds.map((threshold, thresholdIdx) => { {config.thresholds.map((threshold, thresholdIdx) => {
return ( return (
<HorizontalGroup key={thresholdIdx}> <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"> <InlineField label="Use metric">
<InlineSwitch <InlineSwitch
value={threshold.useMetric} value={threshold.useMetric}
onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'useMetric', (evt.target as any).checked)} onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'useMetric', (evt.target as any).checked)}
/> />
</InlineField> </InlineField>
<InlineField>
{threshold.useMetric ? ( {threshold.useMetric ? (
<FieldNamePicker <FieldNamePicker
context={context} context={context}
@ -134,7 +116,25 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'value', (evt.target as any).value)} onChange={(evt) => onThresholdFieldChange(thresholdIdx, 'value', (evt.target as any).value)}
/> />
)} )}
<IconButton name="trash-alt" onClick={() => removeThreshold(thresholdIdx)}></IconButton> </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> </HorizontalGroup>
); );
})} })}
@ -147,3 +147,15 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
</ThemeContext.Consumer> </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 { PanelOptions, Pod } from './types';
import { Panel } from './components/Panel'; import { Panel } from './components/Panel';
import { IconsEditor } from './components/editors/IconsEditor'; import { IconsEditor } from './components/editors/IconsEditor';
import { ThresholdsEditor } from './components/editors/ThresholdsEditor'; import { ThresholdsEditor } from './components/editors/ThresholdsEditor';
import { NotSupportedText } from './components/editors/NotSupportedText';
import { UseMetricEditor } from './components/editors/UseMetricEditor';
import { PanelPlugin } from '@grafana/data'; import { PanelPlugin } from '@grafana/data';
export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((builder) => { export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((builder) => {
return builder return builder
.addRadio({ .addRadio({
@ -18,21 +22,26 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
{ {
label: 'Gauge', label: 'Gauge',
value: Pod.GAUGE, value: Pod.GAUGE,
description: 'Enable gauge pod',
}, },
{ {
label: 'Line', label: 'Line',
value: Pod.LINE, value: Pod.LINE,
description: 'Enable line pod',
}, },
{ {
label: 'Bar', label: 'Bar',
value: Pod.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({ .addFieldNamePicker({
name: 'Value', name: 'Value',
@ -40,46 +49,55 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
category: ['Extremum'], category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE, showIf: (config) => config.visualizationType === Pod.GAUGE,
}) })
// TODO: defaults?
.addNumberInput({ .addCustomEditor({
path: 'gauge.min.value', id: 'min',
name: 'Min',
category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE && !config.gauge.min.useMetric,
})
.addFieldNamePicker({
name: 'Min', name: 'Min',
path: 'gauge.min.metricName', path: 'gauge.min',
category: ['Extremum'], category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE && config.gauge.min.useMetric, showIf: (config) => config.visualizationType === Pod.GAUGE,
}) editor: UseMetricEditor as any,
.addBooleanSwitch({ }).
path: 'gauge.min.useMetric', addCustomEditor({
name: 'Use metric', id: 'max',
defaultValue: false, name: 'Max',
path: 'gauge.max',
category: ['Extremum'], category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE, showIf: (config) => config.visualizationType === Pod.GAUGE,
editor: UseMetricEditor as any,
}) })
.addNumberInput({ // note: `gauge.unit` will contain unit name, not it's string representation
path: 'gauge.max.value', // to format value with unit, use `getValueFormat` function from `@grafana/data`
name: 'Max', .addUnitPicker({
category: ['Extremum'], path: 'gauge.unit',
showIf: (config) => config.visualizationType === Pod.GAUGE && !config.gauge.max.useMetric, name: 'Unit',
category: ['Value Format'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
}) })
.addFieldNamePicker({ .addNumberInput({
name: 'Max', path: 'gauge.decimals',
path: 'gauge.max.metricName', name: 'Decimals',
category: ['Extremum'], settings: {
showIf: (config) => config.visualizationType === Pod.GAUGE && config.gauge.max.useMetric, placeholder: 'auto',
min: 0,
max: 5,
},
category: ['Value Format'],
showIf: (config) => config.visualizationType === Pod.GAUGE,
}) })
.addBooleanSwitch({ .addSliderInput({
path: 'gauge.max.useMetric', path: 'gauge.size',
name: 'Use metric', defaultValue: 20,
defaultValue: false, name: 'Size (px)',
category: ['Extremum'], settings: {
min: 1,
max: 50,
},
category: ['Value Format'],
showIf: (config) => config.visualizationType === Pod.GAUGE, showIf: (config) => config.visualizationType === Pod.GAUGE,
}) })
.addBooleanSwitch({ .addBooleanSwitch({
path: 'gauge.reversed', path: 'gauge.reversed',
name: 'Reversed', name: 'Reversed',
@ -87,6 +105,7 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
category: ['Direction'], category: ['Direction'],
showIf: (config) => config.visualizationType === Pod.GAUGE, showIf: (config) => config.visualizationType === Pod.GAUGE,
}) })
.addCustomEditor({ .addCustomEditor({
id: 'icons', id: 'icons',
path: 'gauge.icons', path: 'gauge.icons',

Loading…
Cancel
Save