diff --git a/packages/grafana-ui/src/components/SingleStatShared/FieldPropertiesEditor.test.tsx b/packages/grafana-ui/src/components/SingleStatShared/FieldPropertiesEditor.test.tsx deleted file mode 100644 index 6d75fc53d8d..00000000000 --- a/packages/grafana-ui/src/components/SingleStatShared/FieldPropertiesEditor.test.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { FieldPropertiesEditor } from './FieldPropertiesEditor'; -import { FieldConfig } from '@grafana/data'; -import { mount } from 'enzyme'; - -describe('FieldPropertiesEditor', () => { - describe('when bluring min/max field', () => { - it("when title was modified it should persist it's value", () => { - const onChangeHandler = jest.fn(); - const value: FieldConfig = { - title: 'Title set', - }; - const container = mount(); - const minInput = container.find('input[aria-label="Field properties editor min input"]'); - const maxInput = container.find('input[aria-label="Field properties editor max input"]'); - - // Simulating title update provided from PanelModel options - container.setProps({ value: { title: 'Title updated' } }); - - minInput.simulate('blur'); - maxInput.simulate('blur'); - - expect(onChangeHandler).toHaveBeenLastCalledWith({ - title: 'Title updated', - min: undefined, - max: undefined, - decimals: undefined, - }); - }); - }); -}); -// diff --git a/packages/grafana-ui/src/components/SingleStatShared/FieldPropertiesEditor.tsx b/packages/grafana-ui/src/components/SingleStatShared/FieldPropertiesEditor.tsx deleted file mode 100644 index 5b4aa49cdcc..00000000000 --- a/packages/grafana-ui/src/components/SingleStatShared/FieldPropertiesEditor.tsx +++ /dev/null @@ -1,142 +0,0 @@ -// Libraries -import React, { ChangeEvent, useState, useCallback } from 'react'; - -// Components -import { FormField } from '../FormField/FormField'; -import { FormLabel } from '../FormLabel/FormLabel'; -import { UnitPicker } from '../UnitPicker/UnitPicker'; - -// Types -import { - VAR_SERIES_NAME, - VAR_FIELD_NAME, - VAR_CALC, - VAR_CELL_PREFIX, - toIntegerOrUndefined, - FieldConfig, - toFloatOrUndefined, - toNumberString, -} from '@grafana/data'; - -const labelWidth = 6; - -export interface Props { - showMinMax?: boolean; - showTitle?: boolean; - value: FieldConfig; - onChange: (value: FieldConfig, event?: React.SyntheticEvent) => void; -} - -export const FieldPropertiesEditor: React.FC = ({ value, onChange, showMinMax, showTitle }) => { - const { unit, title } = value; - - const [decimals, setDecimals] = useState( - value.decimals !== undefined && value.decimals !== null ? value.decimals.toString() : '' - ); - const [min, setMin] = useState(toNumberString(value.min)); - const [max, setMax] = useState(toNumberString(value.max)); - - const onTitleChange = (event: ChangeEvent) => { - onChange({ ...value, title: event.target.value }); - }; - - const onDecimalChange = useCallback( - (event: ChangeEvent) => { - setDecimals(event.target.value); - }, - [value.decimals, onChange] - ); - - const onMinChange = useCallback( - (event: ChangeEvent) => { - setMin(event.target.value); - }, - [value.min, onChange] - ); - - const onMaxChange = useCallback( - (event: ChangeEvent) => { - setMax(event.target.value); - }, - [value.max, onChange] - ); - - const onUnitChange = (unit?: string) => { - onChange({ ...value, unit }); - }; - - const commitChanges = useCallback(() => { - onChange({ - ...value, - decimals: toIntegerOrUndefined(decimals), - min: toFloatOrUndefined(min), - max: toFloatOrUndefined(max), - }); - }, [min, max, decimals, value]); - - const titleTooltip = ( -
- Template Variables: -
- {'${' + VAR_SERIES_NAME + '}'} -
- {'${' + VAR_FIELD_NAME + '}'} -
- {'$' + VAR_CELL_PREFIX + '{N}'} / {'$' + VAR_CALC} -
- ); - - return ( - <> - {showTitle && ( - - )} - -
- Unit - -
- {showMinMax && ( - <> - - - - )} - - - ); -}; diff --git a/packages/grafana-ui/src/components/SingleStatShared/index.ts b/packages/grafana-ui/src/components/SingleStatShared/index.ts index c66cc532767..9a86c16c8a9 100644 --- a/packages/grafana-ui/src/components/SingleStatShared/index.ts +++ b/packages/grafana-ui/src/components/SingleStatShared/index.ts @@ -1,5 +1,4 @@ export { FieldDisplayEditor } from './FieldDisplayEditor'; -export { FieldPropertiesEditor } from './FieldPropertiesEditor'; export { SingleStatBaseOptions, diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx deleted file mode 100644 index 10b3c4faa4a..00000000000 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { action } from '@storybook/addon-actions'; -import { object } from '@storybook/addon-knobs'; - -import { ThresholdsEditor } from './ThresholdsEditor'; -import { ThresholdsMode } from '@grafana/data'; - -export default { - title: 'Panel/ThresholdsEditor', - component: ThresholdsEditor, -}; - -const getKnobs = () => { - return { - initThresholds: object('Initial thresholds', { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: 'green' }, - { value: 50, color: 'red' }, - ], - }), - }; -}; - -export const basic = () => ; - -export const withThresholds = () => ( - -); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx deleted file mode 100644 index 1b76e062c8b..00000000000 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import React, { ChangeEvent } from 'react'; -import { mount } from 'enzyme'; -import { GrafanaThemeType, ThresholdsMode } from '@grafana/data'; -import { ThresholdsEditor, Props, thresholdsWithoutKey } from './ThresholdsEditor'; -import { colors } from '../../utils/colors'; -import { mockThemeContext } from '../../themes/ThemeContext'; - -const setup = (propOverrides?: Partial) => { - const props: Props = { - onChange: jest.fn(), - thresholds: { mode: ThresholdsMode.Absolute, steps: [] }, - }; - - Object.assign(props, propOverrides); - - const wrapper = mount(); - const instance = wrapper.instance() as ThresholdsEditor; - - return { - instance, - wrapper, - }; -}; - -function getCurrentThresholds(editor: ThresholdsEditor) { - return thresholdsWithoutKey(editor.props.thresholds, editor.state.steps); -} - -describe('Render', () => { - let restoreThemeContext: any; - beforeAll(() => { - restoreThemeContext = mockThemeContext({ type: GrafanaThemeType.Dark }); - }); - - afterAll(() => { - restoreThemeContext(); - }); - - it('should render with base threshold', () => { - const { wrapper } = setup(); - expect(wrapper.find('.thresholds')).toMatchSnapshot(); - }); -}); - -describe('Initialization', () => { - it('should add a base threshold if missing', () => { - const { instance } = setup(); - expect(getCurrentThresholds(instance).steps).toEqual([{ value: -Infinity, color: 'green' }]); - }); -}); - -describe('Add threshold', () => { - it('should add threshold', () => { - const { instance } = setup(); - - instance.onAddThresholdAfter(instance.state.steps[0]); - - expect(getCurrentThresholds(instance).steps).toEqual([ - { value: -Infinity, color: 'green' }, // 0 - { value: 50, color: colors[1] }, // 1 - ]); - }); - - it('should add another threshold above a first', () => { - const { instance } = setup({ - thresholds: { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: colors[0] }, // 0 - { value: 50, color: colors[2] }, // 1 - ], - }, - }); - - instance.onAddThresholdAfter(instance.state.steps[1]); - - expect(getCurrentThresholds(instance).steps).toEqual([ - { value: -Infinity, color: colors[0] }, // 0 - { value: 50, color: colors[2] }, // 1 - { value: 75, color: colors[3] }, // 2 - ]); - }); - - it('should add another threshold between first and second index', () => { - const { instance } = setup({ - thresholds: { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: colors[0] }, - { value: 50, color: colors[2] }, - { value: 75, color: colors[3] }, - ], - }, - }); - - instance.onAddThresholdAfter(instance.state.steps[1]); - - expect(getCurrentThresholds(instance).steps).toEqual([ - { value: -Infinity, color: colors[0] }, - { value: 50, color: colors[2] }, - { value: 62.5, color: colors[4] }, - { value: 75, color: colors[3] }, - ]); - }); -}); - -describe('Remove threshold', () => { - it('should not remove threshold at index 0', () => { - const thresholds = { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: '#7EB26D' }, - { value: 50, color: '#EAB839' }, - { value: 75, color: '#6ED0E0' }, - ], - }; - const { instance } = setup({ thresholds }); - - instance.onRemoveThreshold(instance.state.steps[0]); - - expect(getCurrentThresholds(instance)).toEqual(thresholds); - }); - - it('should remove threshold', () => { - const thresholds = { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: '#7EB26D' }, - { value: 50, color: '#EAB839' }, - { value: 75, color: '#6ED0E0' }, - ], - }; - const { instance } = setup({ thresholds }); - - instance.onRemoveThreshold(instance.state.steps[1]); - - expect(getCurrentThresholds(instance).steps).toEqual([ - { value: -Infinity, color: '#7EB26D' }, - { value: 75, color: '#6ED0E0' }, - ]); - }); -}); - -describe('change threshold value', () => { - it('should not change threshold at index 0', () => { - const thresholds = { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: '#7EB26D' }, - { value: 50, color: '#EAB839' }, - { value: 75, color: '#6ED0E0' }, - ], - }; - const { instance } = setup({ thresholds }); - - const mockEvent = ({ target: { value: '12' } } as any) as ChangeEvent; - - instance.onChangeThresholdValue(mockEvent, instance.state.steps[0]); - - expect(getCurrentThresholds(instance)).toEqual(thresholds); - }); - - it('should update value', () => { - const { instance } = setup(); - const thresholds = { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: '#7EB26D', key: 1 }, - { value: 50, color: '#EAB839', key: 2 }, - { value: 75, color: '#6ED0E0', key: 3 }, - ], - }; - - instance.state = { - steps: thresholds.steps, - }; - - const mockEvent = ({ target: { value: '78' } } as any) as ChangeEvent; - - instance.onChangeThresholdValue(mockEvent, thresholds.steps[1]); - - expect(getCurrentThresholds(instance).steps).toEqual([ - { value: -Infinity, color: '#7EB26D' }, - { value: 78, color: '#EAB839' }, - { value: 75, color: '#6ED0E0' }, - ]); - }); -}); - -describe('on blur threshold value', () => { - it('should resort rows and update indexes', () => { - const { instance } = setup(); - const thresholds = { - mode: ThresholdsMode.Absolute, - steps: [ - { value: -Infinity, color: '#7EB26D', key: 1 }, - { value: 78, color: '#EAB839', key: 2 }, - { value: 75, color: '#6ED0E0', key: 3 }, - ], - }; - - instance.setState({ - steps: thresholds.steps, - }); - - instance.onBlur(); - - expect(getCurrentThresholds(instance).steps).toEqual([ - { value: -Infinity, color: '#7EB26D' }, - { value: 75, color: '#6ED0E0' }, - { value: 78, color: '#EAB839' }, - ]); - }); -}); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx deleted file mode 100644 index e0fb069b721..00000000000 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import React, { PureComponent, ChangeEvent } from 'react'; -import { Threshold, sortThresholds, ThresholdsConfig, ThresholdsMode, SelectableValue } from '@grafana/data'; -import { colors } from '../../utils'; -import { getColorFromHexRgbOrName } from '@grafana/data'; -import { ThemeContext } from '../../themes/ThemeContext'; -import { Input } from '../Forms/Legacy/Input/Input'; -import { ColorPicker } from '../ColorPicker/ColorPicker'; -import { css } from 'emotion'; -import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; - -export interface Props { - thresholds?: ThresholdsConfig; - onChange: (thresholds: ThresholdsConfig) => void; -} - -interface State { - steps: ThresholdWithKey[]; -} - -interface ThresholdWithKey extends Threshold { - key: number; -} - -let counter = 100; - -export class ThresholdsEditor extends PureComponent { - constructor(props: Props) { - super(props); - - const steps = toThresholdsWithKey(props.thresholds); - steps[0].value = -Infinity; - - this.state = { steps }; - } - - onAddThresholdAfter = (threshold: ThresholdWithKey) => { - const { steps } = this.state; - - const maxValue = 100; - const minValue = 0; - - let prev: ThresholdWithKey | undefined = undefined; - let next: ThresholdWithKey | undefined = undefined; - for (const t of steps) { - if (prev && prev.key === threshold.key) { - next = t; - break; - } - prev = t; - } - - const prevValue = prev && isFinite(prev.value) ? prev.value : minValue; - const nextValue = next && isFinite(next.value) ? next.value : maxValue; - - const color = colors.filter(c => !steps.some(t => t.color === c))[1]; - const add = { - value: prevValue + (nextValue - prevValue) / 2.0, - color: color, - key: counter++, - }; - const newThresholds = [...steps, add]; - sortThresholds(newThresholds); - - this.setState( - { - steps: newThresholds, - }, - () => this.onChange() - ); - }; - - onRemoveThreshold = (threshold: ThresholdWithKey) => { - const { steps } = this.state; - if (!steps.length) { - return; - } - // Don't remove index 0 - if (threshold.key === steps[0].key) { - return; - } - this.setState( - { - steps: steps.filter(t => t.key !== threshold.key), - }, - () => this.onChange() - ); - }; - - onChangeThresholdValue = (event: ChangeEvent, threshold: ThresholdWithKey) => { - const cleanValue = event.target.value.replace(/,/g, '.'); - const parsedValue = parseFloat(cleanValue); - const value = isNaN(parsedValue) ? '' : parsedValue; - - const steps = this.state.steps.map(t => { - if (t.key === threshold.key) { - t = { ...t, value: value as number }; - } - return t; - }); - if (steps.length) { - steps[0].value = -Infinity; - } - this.setState({ steps }); - }; - - onChangeThresholdColor = (threshold: ThresholdWithKey, color: string) => { - const { steps } = this.state; - - const newThresholds = steps.map(t => { - if (t.key === threshold.key) { - t = { ...t, color: color }; - } - - return t; - }); - - this.setState( - { - steps: newThresholds, - }, - () => this.onChange() - ); - }; - - onBlur = () => { - const steps = [...this.state.steps]; - sortThresholds(steps); - this.setState( - { - steps, - }, - () => this.onChange() - ); - }; - - onChange = () => { - this.props.onChange(thresholdsWithoutKey(this.props.thresholds, this.state.steps)); - }; - - onModeChanged = (item: SelectableValue) => { - if (item.value) { - this.props.onChange({ - ...getThresholdOrDefault(this.props.thresholds), - mode: item.value, - }); - } - }; - - renderInput = (threshold: ThresholdWithKey) => { - const config = getThresholdOrDefault(this.props.thresholds); - const isPercent = config.mode === ThresholdsMode.Percentage; - - return ( -
- -
- {threshold.color && ( -
- this.onChangeThresholdColor(threshold, color)} - enableNamedColors={true} - /> -
- )} -
- {!isFinite(threshold.value) ? ( -
- -
- ) : ( - <> -
- ) => this.onChangeThresholdValue(event, threshold)} - value={threshold.value} - onBlur={this.onBlur} - /> -
- {isPercent && ( -
- -
- )} -
this.onRemoveThreshold(threshold)}> - -
- - )} -
- ); - }; - - render() { - const { steps } = this.state; - - return ( - - - {theme => ( - <> -
- {steps - .slice(0) - .reverse() - .map(threshold => { - return ( -
-
this.onAddThresholdAfter(threshold)}> - -
-
-
{this.renderInput(threshold)}
-
- ); - })} -
- - )} - - - ); - } -} - -export function thresholdsWithoutKey( - thresholds: ThresholdsConfig | undefined, - steps: ThresholdWithKey[] -): ThresholdsConfig { - thresholds = getThresholdOrDefault(thresholds); - - const mode = thresholds.mode ?? ThresholdsMode.Absolute; - - return { - mode, - steps: steps.map(t => { - const { key, ...rest } = t; - return rest; // everything except key - }), - }; -} - -function getThresholdOrDefault(thresholds?: ThresholdsConfig): ThresholdsConfig { - return thresholds ?? { steps: [], mode: ThresholdsMode.Absolute }; -} - -function toThresholdsWithKey(thresholds?: ThresholdsConfig): ThresholdWithKey[] { - thresholds = getThresholdOrDefault(thresholds); - - let steps: Threshold[] = thresholds.steps || []; - - if (thresholds.steps && thresholds.steps.length === 0) { - steps = [{ value: -Infinity, color: 'green' }]; - } - - return steps.map(t => { - return { - color: t.color, - value: t.value === null ? -Infinity : t.value, - key: counter++, - }; - }); -} diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss deleted file mode 100644 index 4662be0322f..00000000000 --- a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss +++ /dev/null @@ -1,106 +0,0 @@ -.thresholds { - margin-bottom: 20px; -} - -.thresholds-row { - display: flex; - flex-direction: row; - height: 62px; -} - -.thresholds-row:first-child > .thresholds-row-color-indicator { - border-top-left-radius: $border-radius; - border-top-right-radius: $border-radius; - overflow: hidden; -} - -.thresholds-row:last-child > .thresholds-row-color-indicator { - border-bottom-left-radius: $border-radius; - border-bottom-right-radius: $border-radius; - overflow: hidden; -} - -.thresholds-row-add-button { - @include buttonBackground($btn-success-bg, $btn-success-bg-hl, #fff); - - align-self: center; - margin-right: 5px; - height: 24px; - width: 24px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - cursor: pointer; - - &:hover { - color: $white; - } -} - -.thresholds-row-color-indicator { - width: 10px; - flex-shrink: 0; -} - -.thresholds-row-input { - margin-top: 44px; - margin-left: 2px; -} - -.thresholds-row-input-inner { - display: flex; - justify-content: center; - flex-direction: row; -} - -.thresholds-row-input-inner > *:last-child { - border-top-right-radius: $border-radius; - border-bottom-right-radius: $border-radius; -} - -.thresholds-row-input-inner-arrow { - align-self: center; - width: 0; - height: 0; - border-top: 6px solid transparent; - border-bottom: 6px solid transparent; - border-right: 6px solid $input-label-border-color; -} - -.thresholds-row-input-inner-value > input { - height: $input-height; - padding: $input-padding; - width: 150px; - border-top: 1px solid $input-label-border-color; - border-bottom: 1px solid $input-label-border-color; -} - -.thresholds-row-input-inner-color { - width: 42px; - display: flex; - align-items: center; - justify-content: center; - background-color: $input-bg; - border: 1px solid $input-label-border-color; -} - -.thresholds-row-input-inner-color-colorpicker { - border-radius: 10px; - display: flex; - align-items: center; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25); -} - -.thresholds-row-input-inner-remove { - display: flex; - align-items: center; - justify-content: center; - height: $input-height; - padding: $input-padding; - width: 42px; - background-color: $input-label-bg; - border: 1px solid $input-label-border-color; - cursor: pointer; -} diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap deleted file mode 100644 index 99346f17456..00000000000 --- a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap +++ /dev/null @@ -1,156 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Render should render with base threshold 1`] = ` -
-
-
- -
-
-
-
- -
-
- - - - } - hideAfter={300} - > - -
-
-
-
-
- - - - -
-
-
- -
- -
- -
-
-
-
-
-`; diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx deleted file mode 100644 index 127daed718f..00000000000 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/LegacyMappingRow.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import React, { ChangeEvent, PureComponent } from 'react'; - -import { FormField } from '../FormField/FormField'; -import { FormLabel } from '../FormLabel/FormLabel'; -import { Input } from '../Forms/Legacy/Input/Input'; -import { Select } from '../Forms/Legacy/Select/Select'; - -import { MappingType, ValueMapping } from '@grafana/data'; - -export interface Props { - valueMapping: ValueMapping; - updateValueMapping: (valueMapping: ValueMapping) => void; - removeValueMapping: () => void; -} - -interface State { - from?: string; - id: number; - operator: string; - text: string; - to?: string; - type: MappingType; - value?: string; -} - -const mappingOptions = [ - { value: MappingType.ValueToText, label: 'Value' }, - { value: MappingType.RangeToText, label: 'Range' }, -]; - -export default class LegacyMappingRow extends PureComponent { - constructor(props: Props) { - super(props); - - this.state = { ...props.valueMapping }; - } - - onMappingValueChange = (event: ChangeEvent) => { - this.setState({ value: event.target.value }); - }; - - onMappingFromChange = (event: ChangeEvent) => { - this.setState({ from: event.target.value }); - }; - - onMappingToChange = (event: ChangeEvent) => { - this.setState({ to: event.target.value }); - }; - - onMappingTextChange = (event: ChangeEvent) => { - this.setState({ text: event.target.value }); - }; - - onMappingTypeChange = (mappingType: MappingType) => { - this.setState({ type: mappingType }); - }; - - updateMapping = () => { - this.props.updateValueMapping({ ...this.state } as ValueMapping); - }; - - renderRow() { - const { from, text, to, type, value } = this.state; - - if (type === MappingType.RangeToText) { - return ( - <> - - -
- Text - -
- - ); - } - - return ( - <> - -
- Text - -
- - ); - } - - render() { - const { type } = this.state; - - return ( -
-
- Type -