Browse Source

linting

merge-requests/6/merge
rozetko 3 years ago
parent
commit
14c7104e5f
  1. 6
      src/components/Panel.tsx
  2. 77
      src/components/editors/IconsEditor.tsx
  3. 70
      src/components/editors/ThresholdsEditor.tsx
  4. 12
      src/models/options.ts
  5. 2
      src/models/series.ts
  6. 7
      src/module.ts
  7. 10
      src/types.ts
  8. 6
      src/utils.ts

6
src/components/Panel.tsx

@ -27,11 +27,7 @@ export function Panel({ options, data, width, height, timeZone, timeRange, onCha
// we request animation frame here because we need an existing DOM-element at the moment we render the pod // we request animation frame here because we need an existing DOM-element at the moment we render the pod
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
// TODO: switch / case pod type // TODO: switch / case pod type
const pod = new ChartwerkGaugePod( const pod = new ChartwerkGaugePod((chartContainer as any).current, series, chartwerkOptions);
(chartContainer as any).current,
series,
chartwerkOptions
);
pod.render(); pod.render();
}); });
return ( return (

77
src/components/editors/IconsEditor.tsx

@ -1,7 +1,16 @@
import { IconPosition } from 'types'; import { IconPosition } from 'types';
import { GrafanaTheme, SelectableValue, StandardEditorProps } from '@grafana/data'; import { GrafanaTheme, SelectableValue, StandardEditorProps } from '@grafana/data';
import { Button, HorizontalGroup, IconButton, Input, RadioButtonGroup, Slider, stylesFactory, ThemeContext } from '@grafana/ui'; import {
Button,
HorizontalGroup,
IconButton,
Input,
RadioButtonGroup,
Slider,
stylesFactory,
ThemeContext,
} from '@grafana/ui';
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker'; import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker';
import { css } from 'emotion'; import { css } from 'emotion';
@ -9,7 +18,6 @@ import React from 'react';
import * as _ from 'lodash'; import * as _ from 'lodash';
type IconConfig = { type IconConfig = {
position: IconPosition; position: IconPosition;
url: string; url: string;
@ -17,7 +25,7 @@ type IconConfig = {
conditions: Condition[]; conditions: Condition[];
values: number[]; values: number[];
size: number; size: number;
} };
enum Condition { enum Condition {
EQUAL = '=', EQUAL = '=',
@ -27,7 +35,6 @@ enum Condition {
LESS_OR_EQUAL = '<=', LESS_OR_EQUAL = '<=',
} }
const positionOptions: Array<SelectableValue<IconPosition>> = [ const positionOptions: Array<SelectableValue<IconPosition>> = [
{ {
label: 'Upper-Left', label: 'Upper-Left',
@ -73,7 +80,7 @@ const DEFAULT_ICON: IconConfig = {
metrics: [], metrics: [],
conditions: [], conditions: [],
values: [], values: [],
} };
const fieldNamePickerSettings = { const fieldNamePickerSettings = {
settings: { width: 24 }, settings: { width: 24 },
@ -83,15 +90,11 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
const icons = value; const icons = value;
const addIcon = () => { const addIcon = () => {
onChange( onChange(_.concat(icons, _.cloneDeep(DEFAULT_ICON)));
_.concat(icons, _.cloneDeep(DEFAULT_ICON))
);
}; };
const removeIcon = (idx: number) => { const removeIcon = (idx: number) => {
onChange( onChange(_.filter(icons, (icon, iconIdx) => iconIdx !== idx));
_.filter(icons, (icon, iconIdx) => iconIdx !== idx)
);
}; };
const addCondition = (iconIdx: number) => { const addCondition = (iconIdx: number) => {
@ -118,7 +121,7 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
}; };
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) console.log(value);
// @ts-ignore // @ts-ignore
icons[iconIdx][field][conditionIdx] = value; icons[iconIdx][field][conditionIdx] = value;
@ -132,8 +135,7 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
return ( return (
<div> <div>
<div className={styles.icons}> <div className={styles.icons}>
{ {icons.map((icon, iconIdx) => {
icons.map((icon, iconIdx) => {
return ( return (
<div className={styles.icon}> <div className={styles.icon}>
<IconButton name="trash-alt" onClick={() => removeIcon(iconIdx)}></IconButton> <IconButton name="trash-alt" onClick={() => removeIcon(iconIdx)}></IconButton>
@ -141,23 +143,22 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
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)}
/> />
<RadioButtonGroup <RadioButtonGroup
value={icon.position} value={icon.position}
options={positionOptions} options={positionOptions}
onChange={newVal => onIconFieldChange(iconIdx, 'position', newVal)} onChange={(newVal) => onIconFieldChange(iconIdx, 'position', newVal)}
/> />
<Slider <Slider
value={icon.size} value={icon.size}
min={1} min={1}
max={100} max={100}
step={1} step={1}
onAfterChange={newVal => onIconFieldChange(iconIdx, 'size', newVal)} onAfterChange={(newVal) => onIconFieldChange(iconIdx, 'size', newVal)}
/> />
{ {icon.conditions.map((condition, conditionIdx) => {
icon.conditions.map((condition, conditionIdx) => {
return ( return (
<HorizontalGroup> <HorizontalGroup>
<FieldNamePicker <FieldNamePicker
@ -169,31 +170,37 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
<RadioButtonGroup <RadioButtonGroup
value={icon.conditions[conditionIdx]} value={icon.conditions[conditionIdx]}
options={conditionOptions} options={conditionOptions}
onChange={newVal => onConditionChange(iconIdx, conditionIdx, 'conditions', newVal)} onChange={(newVal) => onConditionChange(iconIdx, conditionIdx, 'conditions', newVal)}
/> />
<Input <Input
placeholder="value" placeholder="value"
value={icon.values[conditionIdx]} value={icon.values[conditionIdx]}
onChange={evt => onConditionChange(iconIdx, conditionIdx, 'values', (evt.target as any).value)} onChange={(evt) =>
onConditionChange(iconIdx, conditionIdx, 'values', (evt.target as any).value)
}
/> />
<IconButton name="trash-alt" onClick={() => removeCondition(iconIdx, conditionIdx)}></IconButton> <IconButton
name="trash-alt"
onClick={() => removeCondition(iconIdx, conditionIdx)}
></IconButton>
</HorizontalGroup> </HorizontalGroup>
) );
}) })}
} <Button variant="secondary" onClick={() => addCondition(iconIdx)}>
<Button variant="secondary" onClick={() => addCondition(iconIdx)}>Add Condition</Button> Add Condition
</Button>
</div> </div>
) );
}) })}
}
</div> </div>
<Button variant="secondary" onClick={addIcon}>Add Icon</Button> <Button variant="secondary" onClick={addIcon}>
Add Icon
</Button>
</div> </div>
) );
} }}
}
</ThemeContext.Consumer> </ThemeContext.Consumer>
) );
} }
interface IconsEditorStyles { interface IconsEditorStyles {
@ -218,5 +225,5 @@ const getStyles = stylesFactory((theme: GrafanaTheme): IconsEditorStyles => {
margin-bottom: 0; margin-bottom: 0;
} }
`, `,
} };
}); });

70
src/components/editors/ThresholdsEditor.tsx

@ -1,17 +1,25 @@
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker'; import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker';
import { StandardEditorProps } from '@grafana/data'; import { StandardEditorProps } from '@grafana/data';
import { Button, ColorPicker, HorizontalGroup, IconButton, InlineField, InlineSwitch, Input, ThemeContext } from '@grafana/ui'; import {
Button,
ColorPicker,
HorizontalGroup,
IconButton,
InlineField,
InlineSwitch,
Input,
ThemeContext,
} from '@grafana/ui';
import React from 'react'; import React from 'react';
import * as _ from 'lodash'; import * as _ from 'lodash';
interface Props { interface Props {
defaultColor: string, defaultColor: string;
arcBackground: string, arcBackground: string;
thresholds: Array<ThresholdConfig> thresholds: Array<ThresholdConfig>;
} }
type ThresholdConfig = { type ThresholdConfig = {
@ -19,13 +27,13 @@ type ThresholdConfig = {
useMetric: boolean; useMetric: boolean;
value?: number; value?: number;
metricName?: string; metricName?: string;
} };
const DEFAULT_THRESHOLD: ThresholdConfig = { const DEFAULT_THRESHOLD: ThresholdConfig = {
color: '#378372d', color: '#378372d',
useMetric: false, useMetric: false,
value: 0 value: 0,
} };
const fieldNamePickerSettings = { const fieldNamePickerSettings = {
settings: { width: 24 }, settings: { width: 24 },
@ -37,17 +45,17 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
const addThreshold = () => { const addThreshold = () => {
config.thresholds.push(_.cloneDeep(DEFAULT_THRESHOLD)); config.thresholds.push(_.cloneDeep(DEFAULT_THRESHOLD));
onChange(config); onChange(config);
} };
const removeThreshold = (idx: number) => { const removeThreshold = (idx: number) => {
config.thresholds.splice(idx, 1); config.thresholds.splice(idx, 1);
onChange(config); onChange(config);
} };
const onFieldChange = (field: keyof Props, value: any) => { const onFieldChange = (field: keyof Props, value: any) => {
config[field] = value; config[field] = value;
onChange(config); onChange(config);
} };
const onThresholdFieldChange = (thresholdIdx: number, field: keyof ThresholdConfig, value: any) => { const onThresholdFieldChange = (thresholdIdx: number, field: keyof ThresholdConfig, value: any) => {
// @ts-ignore // @ts-ignore
@ -57,8 +65,7 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
return ( return (
<ThemeContext.Consumer> <ThemeContext.Consumer>
{ {() => {
() => {
// const styles = getStyles(theme.v1); // const styles = getStyles(theme.v1);
return ( return (
<div> <div>
@ -70,7 +77,7 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
<div> <div>
<ColorPicker <ColorPicker
color={config.defaultColor} color={config.defaultColor}
onChange={val => onFieldChange('defaultColor', val)} onChange={(val) => onFieldChange('defaultColor', val)}
enableNamedColors={true} enableNamedColors={true}
/> />
</div> </div>
@ -84,14 +91,13 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
<div> <div>
<ColorPicker <ColorPicker
color={config.arcBackground} color={config.arcBackground}
onChange={val => onFieldChange('arcBackground', val) } onChange={(val) => onFieldChange('arcBackground', val)}
enableNamedColors={true} enableNamedColors={true}
/> />
</div> </div>
} }
/> />
{ {config.thresholds.map((threshold, thresholdIdx) => {
config.thresholds.map((threshold, thresholdIdx) => {
return ( return (
<HorizontalGroup> <HorizontalGroup>
<Input <Input
@ -102,7 +108,7 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
<div> <div>
<ColorPicker <ColorPicker
color={threshold.color} color={threshold.color}
onChange={newVal => onThresholdFieldChange(thresholdIdx, 'color', newVal)} onChange={(newVal) => onThresholdFieldChange(thresholdIdx, 'color', newVal)}
enableNamedColors={true} enableNamedColors={true}
/> />
</div> </div>
@ -111,33 +117,33 @@ export function ThresholdsEditor({ onChange, value, context }: StandardEditorPro
<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>
{ {threshold.useMetric ? (
threshold.useMetric ?
<FieldNamePicker <FieldNamePicker
context={context} context={context}
value={threshold.metricName as string} value={threshold.metricName as string}
onChange={(newVal: any) => onThresholdFieldChange(thresholdIdx, 'metricName', newVal)} onChange={(newVal: any) => onThresholdFieldChange(thresholdIdx, 'metricName', newVal)}
item={fieldNamePickerSettings} item={fieldNamePickerSettings}
/> : />
) : (
<Input <Input
placeholder="value" placeholder="value"
value={threshold.value} value={threshold.value}
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> <IconButton name="trash-alt" onClick={() => removeThreshold(thresholdIdx)}></IconButton>
</HorizontalGroup> </HorizontalGroup>
) );
}) })}
} <Button variant="secondary" onClick={addThreshold}>
<Button variant="secondary" onClick={addThreshold}>Add Threshold</Button> Add Threshold
</Button>
</div> </div>
) );
} }}
}
</ThemeContext.Consumer> </ThemeContext.Consumer>
) );
} }

12
src/models/options.ts

@ -17,7 +17,11 @@ export class Options {
this.minValue = this.grafanaOptions.gauge.min.value; this.minValue = this.grafanaOptions.gauge.min.value;
return; return;
} }
const filteredSeries = filterMetricListByAlias(this.grafanaSeriesList, this.grafanaOptions.gauge.min.metricName, 'Min'); const filteredSeries = filterMetricListByAlias(
this.grafanaSeriesList,
this.grafanaOptions.gauge.min.metricName,
'Min'
);
const serie = filteredSeries[0]; const serie = filteredSeries[0];
// Last value for now // Last value for now
const aggregatedValue = getAggregatedValueFromSerie(serie, Aggregation.LAST); const aggregatedValue = getAggregatedValueFromSerie(serie, Aggregation.LAST);
@ -29,7 +33,11 @@ export class Options {
this.maxValue = this.grafanaOptions.gauge.max.value; this.maxValue = this.grafanaOptions.gauge.max.value;
return; return;
} }
const filteredSeries = filterMetricListByAlias(this.grafanaSeriesList, this.grafanaOptions.gauge.max.metricName, 'Max'); const filteredSeries = filterMetricListByAlias(
this.grafanaSeriesList,
this.grafanaOptions.gauge.max.metricName,
'Max'
);
const serie = filteredSeries[0]; const serie = filteredSeries[0];
// Last value for now // Last value for now
const aggregatedValue = getAggregatedValueFromSerie(serie, Aggregation.LAST); const aggregatedValue = getAggregatedValueFromSerie(serie, Aggregation.LAST);

2
src/models/series.ts

@ -1,11 +1,9 @@
import { ValueOptions } from 'types'; import { ValueOptions } from 'types';
import { filterMetricListByAlias } from '../utils'; import { filterMetricListByAlias } from '../utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
// Convert Grafana series into Chartwerk series // Convert Grafana series into Chartwerk series
export class Series { export class Series {
private _seriesList; private _seriesList;

7
src/module.ts

@ -6,7 +6,6 @@ import { ThresholdsEditor } from './components/editors/ThresholdsEditor';
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({
@ -39,7 +38,7 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
name: 'Value', name: 'Value',
path: 'gauge.value.metricName', path: 'gauge.value.metricName',
category: ['Extremum'], category: ['Extremum'],
showIf: (config) => config.visualizationType === Pod.GAUGE showIf: (config) => config.visualizationType === Pod.GAUGE,
}) })
.addNumberInput({ .addNumberInput({
@ -106,9 +105,9 @@ export const plugin = new PanelPlugin<PanelOptions>(Panel).setPanelOptions((buil
defaultValue: { defaultValue: {
defaultColor: '#37872d', defaultColor: '#37872d',
arcBackground: 'rgba(38, 38, 38, 0.1)', arcBackground: 'rgba(38, 38, 38, 0.1)',
thresholds: [] thresholds: [],
}, },
showIf: (config) => config.visualizationType === Pod.GAUGE, showIf: (config) => config.visualizationType === Pod.GAUGE,
editor: ThresholdsEditor as any, editor: ThresholdsEditor as any,
}) });
}); });

10
src/types.ts

@ -12,16 +12,16 @@ export type ExtremumOptions = {
useMetric: false; useMetric: false;
value?: number; value?: number;
metricName?: string; metricName?: string;
} };
export type ValueOptions = { export type ValueOptions = {
metricName?: string metricName?: string;
} };
export enum IconPosition { export enum IconPosition {
UPPER_LEFT = 'Upper left', UPPER_LEFT = 'Upper left',
MIDDLE = 'Middle', MIDDLE = 'Middle',
UPPER_RIGHT = 'Upper right' UPPER_RIGHT = 'Upper right',
} }
export enum Pod { export enum Pod {
@ -33,5 +33,5 @@ export enum Pod {
export enum Aggregation { export enum Aggregation {
MIN = 'min', MIN = 'min',
MAX = 'max', MAX = 'max',
LAST = 'last' LAST = 'last',
} }

6
src/utils.ts

@ -3,7 +3,7 @@ import { Aggregation } from './types';
import * as _ from 'lodash'; import * as _ from 'lodash';
export function filterMetricListByAlias(list: any[], alias: string | undefined, option: string): any[] { export function filterMetricListByAlias(list: any[], alias: string | undefined, option: string): any[] {
const filteredSeries = _.filter(list, serie => serie.alias === alias); const filteredSeries = _.filter(list, (serie) => serie.alias === alias);
if (filteredSeries.length === 0) { if (filteredSeries.length === 0) {
throw new Error(`${option}: Can't find metric for ${alias} name.`); throw new Error(`${option}: Can't find metric for ${alias} name.`);
} }
@ -23,11 +23,11 @@ export function getAggregatedValueFromSerie(serie: any, aggregation = Aggregatio
} }
switch (aggregation) { switch (aggregation) {
case Aggregation.LAST: case Aggregation.LAST:
const lastRow = _.last((serie.datapoints as [number, number][])); const lastRow = _.last(serie.datapoints as [number, number][]);
// [0] because it is Grafan series. So 0 idx for values, 1 idx for timestamps // [0] because it is Grafan series. So 0 idx for values, 1 idx for timestamps
// @ts-ignore // @ts-ignore
return !_.isEmpty(lastRow) ? lastRow[0] : null; return !_.isEmpty(lastRow) ? lastRow[0] : null;
default: default:
throw new Error(`Unknown aggregation type: ${aggregation}`) throw new Error(`Unknown aggregation type: ${aggregation}`);
} }
} }

Loading…
Cancel
Save