diff --git a/[refs] b/[refs] index 4c48138855304..a106993ade6ea 100644 --- a/[refs] +++ b/[refs] @@ -562,7 +562,7 @@ refs/heads/sub-400-fixes: 2e94cff18bc022b66839ba9d66fe937112efdaff refs/heads/webpack-perf-improvements: 16fc8e415e6c2310d1166388bd8776f67cd07883 refs/tags/v7.0.6: e8c57c79d3c6922bee1118ce3da10be3ac8fa35e refs/tags/v7.1.0-beta3: f3bef580fe8539bf5e8227e79af6ac7eaa04d20a -refs/heads/chore/arve-oauth-orgs: 1bff2fdeea613fdcbe7b6dd0842d781550b859d8 +refs/heads/chore/arve-oauth-orgs: 85a04794aca05b9d591c60eafdd60a4745b622bc refs/heads/chore/cloudwatch-dataframes: 68af58f75a192e53fd775889f9e9144e011b8a8c refs/heads/chore/cloudwatch-tests: c0d121e2aee43970b6fe037ef099063cbd601174 refs/heads/chore/dashboardErr: f6386bf405bafe3f2239b43497bcd8ac314609f0 diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-data/src/field/overrides/processors.ts b/branches/chore/arve-oauth-orgs/packages/grafana-data/src/field/overrides/processors.ts index a50da40520e80..8152659c9ff26 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-data/src/field/overrides/processors.ts +++ b/branches/chore/arve-oauth-orgs/packages/grafana-data/src/field/overrides/processors.ts @@ -24,6 +24,12 @@ export const numberOverrideProcessor = ( return parseFloat(value); }; +export interface SliderFieldConfigSettings { + min: number; + max: number; + step?: number; +} + export interface DataLinksFieldConfigSettings {} export const dataLinksOverrideProcessor = ( diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts b/branches/chore/arve-oauth-orgs/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts index b636a0f202936..291239469c37d 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts +++ b/branches/chore/arve-oauth-orgs/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts @@ -1,6 +1,11 @@ import { ComponentType } from 'react'; import { RegistryItem, Registry } from '../utils/Registry'; -import { NumberFieldConfigSettings, SelectFieldConfigSettings, StringFieldConfigSettings } from '../field'; +import { + NumberFieldConfigSettings, + SliderFieldConfigSettings, + SelectFieldConfigSettings, + StringFieldConfigSettings, +} from '../field'; /** * Option editor registry item @@ -71,6 +76,10 @@ export interface OptionsUIRegistryBuilderAPI< config: OptionEditorConfig ): this; + addSliderInput?( + config: OptionEditorConfig + ): this; + addTextInput?( config: OptionEditorConfig ): this; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-data/src/utils/OptionsUIBuilders.ts b/branches/chore/arve-oauth-orgs/packages/grafana-data/src/utils/OptionsUIBuilders.ts index 98138e6b8869d..8fa49d92b3cab 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-data/src/utils/OptionsUIBuilders.ts +++ b/branches/chore/arve-oauth-orgs/packages/grafana-data/src/utils/OptionsUIBuilders.ts @@ -12,6 +12,7 @@ import { StandardEditorProps, StringFieldConfigSettings, NumberFieldConfigSettings, + SliderFieldConfigSettings, ColorFieldConfigSettings, identityOverrideProcessor, UnitFieldConfigSettings, @@ -39,6 +40,18 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } + addSliderInput(config: FieldConfigEditorConfig) { + return this.addCustomEditor({ + ...config, + id: config.path, + override: standardEditorsRegistry.get('slider').editor as any, + editor: standardEditorsRegistry.get('slider').editor as any, + process: numberOverrideProcessor, + shouldApply: config.shouldApply ? config.shouldApply : field => field.type === FieldType.number, + settings: config.settings || {}, + }); + } + addTextInput(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, @@ -136,6 +149,14 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } + addSliderInput(config: PanelOptionsEditorConfig) { + return this.addCustomEditor({ + ...config, + id: config.path, + editor: standardEditorsRegistry.get('slider').editor as any, + }); + } + addTextInput(config: PanelOptionsEditorConfig) { return this.addCustomEditor({ ...config, diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/OptionsUI/slider.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/OptionsUI/slider.tsx new file mode 100644 index 0000000000000..f5403f5b96ae2 --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/OptionsUI/slider.tsx @@ -0,0 +1,27 @@ +import React, { useCallback } from 'react'; +import { FieldConfigEditorProps, SliderFieldConfigSettings } from '@grafana/data'; +import { Slider } from '../Slider/Slider'; + +export const SliderValueEditor: React.FC> = ({ + value, + onChange, + item, +}) => { + const { settings } = item; + const onValueAfterChange = useCallback( + (value?: number) => { + onChange(value); + }, + [onChange] + ); + const initialValue = typeof value === 'number' ? value : typeof value === 'string' ? +value : 0; + return ( + + ); +}; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.mdx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.mdx new file mode 100644 index 0000000000000..2df51ccae284a --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.mdx @@ -0,0 +1,12 @@ +import { Meta, Props } from '@storybook/addon-docs/blocks'; +import { RangeSliderProps } from './types'; + + + +# Range-slider + +The `Range-slider` component is an input element where users can manipulate two values on a one-dimensional axis. + +`Range-slider` can be implemented in horizontal or vertical orientation. You can set the default starting values for the slider with the `value` prop. + + diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.story.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.story.tsx new file mode 100644 index 0000000000000..f3bc6ba4e1a05 --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.story.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { RangeSlider } from '@grafana/ui'; +import { select, number, boolean } from '@storybook/addon-knobs'; + +export default { + title: 'Forms/Slider/Range', + component: RangeSlider, +}; + +const getKnobs = () => { + return { + min: number('min', 0), + max: number('max', 100), + step: boolean('enable step', false), + orientation: select('orientation', ['horizontal', 'vertical'], 'horizontal'), + reverse: boolean('reverse', false), + }; +}; + +const SliderWrapper = () => { + const { min, max, orientation, reverse, step } = getKnobs(); + const stepValue = step ? 10 : undefined; + return ( +
+ +
+ ); +}; + +export const basic = () => ; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.test.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.test.tsx new file mode 100644 index 0000000000000..6011cbff16111 --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.test.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { RangeSlider } from './RangeSlider'; +import { RangeSliderProps } from './types'; +import { render } from '@testing-library/react'; + +const sliderProps: RangeSliderProps = { + min: 10, + max: 20, +}; + +describe('RangeSlider', () => { + it('renders without error', () => { + expect(() => { + render(); + }); + }); +}); diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.tsx new file mode 100644 index 0000000000000..75b26a98b9235 --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/RangeSlider.tsx @@ -0,0 +1,53 @@ +import React, { FunctionComponent } from 'react'; +import { Range as RangeComponent, createSliderWithTooltip } from 'rc-slider'; +import { cx } from 'emotion'; +import { Global } from '@emotion/core'; +import { useTheme } from '../../themes/ThemeContext'; +import { getStyles } from './styles'; +import { RangeSliderProps } from './types'; + +/** + * @public + * + * RichHistoryQueriesTab uses this Range Component + */ +export const RangeSlider: FunctionComponent = ({ + min, + max, + onChange, + onAfterChange, + orientation = 'horizontal', + reverse, + step, + formatTooltipResult, + value, + tooltipAlwaysVisible = true, +}) => { + const isHorizontal = orientation === 'horizontal'; + const theme = useTheme(); + const styles = getStyles(theme, isHorizontal); + const RangeWithTooltip = createSliderWithTooltip(RangeComponent); + return ( +
+ {/** Slider tooltip's parent component is body and therefore we need Global component to do css overrides for it. */} + + (formatTooltipResult ? formatTooltipResult(value) : value)} + onChange={onChange} + onAfterChange={onAfterChange} + vertical={!isHorizontal} + reverse={reverse} + /> +
+ ); +}; + +RangeSlider.displayName = 'Range'; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.mdx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.mdx index b1cf782034167..7a40f484391fb 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.mdx +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.mdx @@ -1,12 +1,12 @@ import { Meta, Props } from '@storybook/addon-docs/blocks'; -import { Slider } from './Slider'; +import { SliderProps } from './types'; # Slider -The `Slider` component is an input element where users can manipulate one or two values on a one-dimensional axis. +The `Slider` component is an input element where users can manipulate one value on a one-dimensional axis. -`Slider` can be implemented in horizontal or vertical orientation. You can set the default starting value(s) for the slider with the `value` prop. +`Slider` can be implemented in horizontal or vertical orientation. You can set the default starting value(s) for the slider with the `value` prop. - + diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.story.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.story.tsx index d659092e86bce..931eba9f337cd 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.story.tsx +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.story.tsx @@ -13,24 +13,16 @@ const getKnobs = () => { max: number('max', 100), step: boolean('enable step', false), orientation: select('orientation', ['horizontal', 'vertical'], 'horizontal'), - reverse: boolean('reverse', true), - singleValue: boolean('single value', false), + reverse: boolean('reverse', false), }; }; const SliderWrapper = () => { - const { min, max, orientation, reverse, singleValue, step } = getKnobs(); + const { min, max, orientation, reverse, step } = getKnobs(); const stepValue = step ? 10 : undefined; return (
- +
); }; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.test.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.test.tsx index 277a905c15fa3..f688f546508a1 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.test.tsx +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.test.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import { Slider, Props } from './Slider'; +import { Slider } from './Slider'; +import { SliderProps } from './types'; import { mount } from 'enzyme'; -const sliderProps: Props = { +const sliderProps: SliderProps = { min: 10, max: 20, }; @@ -17,11 +18,10 @@ describe('Slider', () => { expect(wrapper.html()).toContain('aria-valuemin="10"'); expect(wrapper.html()).toContain('aria-valuemax="20"'); expect(wrapper.html()).toContain('aria-valuenow="10"'); - expect(wrapper.html()).toContain('aria-valuenow="20"'); }); it('renders correct contents with a value', () => { - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.html()).toContain('aria-valuenow="15"'); expect(wrapper.html()).not.toContain('aria-valuenow="20"'); expect(wrapper.html()).not.toContain('aria-valuenow="10"'); diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.tsx index f0095930b3b68..de9058010bd98 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.tsx +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/Slider.tsx @@ -1,104 +1,15 @@ -import React, { FunctionComponent } from 'react'; -import { Range, createSliderWithTooltip } from 'rc-slider'; -import { cx, css } from 'emotion'; -import { Global, css as cssCore } from '@emotion/core'; -import { stylesFactory } from '../../themes'; -import { GrafanaTheme } from '@grafana/data'; +import React, { useState, useCallback, ChangeEvent, FunctionComponent } from 'react'; +import SliderComponent from 'rc-slider'; +import { cx } from 'emotion'; +import { Global } from '@emotion/core'; import { useTheme } from '../../themes/ThemeContext'; -import { Orientation } from '../../types/orientation'; +import { getStyles } from './styles'; +import { SliderProps } from './types'; -export interface Props { - min: number; - max: number; - orientation?: Orientation; - /** Set current positions of handle(s). If only 1 value supplied, only 1 handle displayed. */ - value?: number[]; - reverse?: boolean; - step?: number; - tooltipAlwaysVisible?: boolean; - formatTooltipResult?: (value: number) => number | string; - onChange?: (values: number[]) => void; - onAfterChange?: (values: number[]) => void; -} - -const getStyles = stylesFactory((theme: GrafanaTheme, isHorizontal: boolean) => { - const trackColor = theme.isLight ? theme.palette.gray5 : theme.palette.dark6; - const container = isHorizontal - ? css` - width: 100%; - margin: ${theme.spacing.lg} ${theme.spacing.sm} ${theme.spacing.sm} ${theme.spacing.sm}; - ` - : css` - height: 100%; - margin: ${theme.spacing.sm} ${theme.spacing.lg} ${theme.spacing.sm} ${theme.spacing.sm}; - `; - - return { - container, - slider: css` - .rc-slider-vertical .rc-slider-handle { - margin-top: -10px; - } - .rc-slider-handle { - border: solid 2px ${theme.palette.blue77}; - background-color: ${theme.palette.blue77}; - } - .rc-slider-handle:hover { - border-color: ${theme.palette.blue77}; - } - .rc-slider-handle:focus { - border-color: ${theme.palette.blue77}; - box-shadow: none; - } - .rc-slider-handle:active { - border-color: ${theme.palette.blue77}; - box-shadow: none; - } - .rc-slider-handle-click-focused:focus { - border-color: ${theme.palette.blue77}; - } - .rc-slider-dot-active { - border-color: ${theme.palette.blue77}; - } - .rc-slider-track { - background-color: ${theme.palette.blue77}; - } - .rc-slider-rail { - background-color: ${trackColor}; - border: 1px solid ${trackColor}; - } - `, - /** Global component from @emotion/core doesn't accept computed classname string returned from css from emotion. - * It accepts object containing the computed name and flattened styles returned from css from @emotion/core - * */ - tooltip: cssCore` - body { - .rc-slider-tooltip { - cursor: grab; - user-select: none; - z-index: ${theme.zIndex.tooltip}; - } - - .rc-slider-tooltip-inner { - color: ${theme.colors.text}; - background-color: transparent !important; - border-radius: 0; - box-shadow: none; - } - - .rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow { - display: none; - } - - .rc-slider-tooltip-placement-top { - padding: 0; - } - } - `, - }; -}); - -export const Slider: FunctionComponent = ({ +/** + * @public + */ +export const Slider: FunctionComponent = ({ min, max, onChange, @@ -106,33 +17,63 @@ export const Slider: FunctionComponent = ({ orientation = 'horizontal', reverse, step, - formatTooltipResult, value, - tooltipAlwaysVisible = true, }) => { const isHorizontal = orientation === 'horizontal'; const theme = useTheme(); const styles = getStyles(theme, isHorizontal); - const RangeWithTooltip = createSliderWithTooltip(Range); + const SliderWithTooltip = SliderComponent; + const [slidervalue, setSliderValue] = useState(value || min); + const onSliderChange = useCallback((v: number) => { + setSliderValue(v); + + if (onChange) { + onChange(v); + } + }, []); + const onSliderInputChange = useCallback((e: ChangeEvent) => { + let v = +e.target.value; + + v > max && (v = max); + v < min && (v = min); + + setSliderValue(v); + + if (onChange) { + onChange(v); + } + + if (onAfterChange) { + onAfterChange(v); + } + }, []); + const sliderInputClassNames = !isHorizontal ? [styles.sliderInputVertical] : []; + const sliderInputFieldClassNames = !isHorizontal ? [styles.sliderInputFieldVertical] : []; return (
{/** Slider tooltip's parent component is body and therefore we need Global component to do css overrides for it. */} - (formatTooltipResult ? formatTooltipResult(value) : value)} - onChange={onChange} - onAfterChange={onAfterChange} - vertical={!isHorizontal} - reverse={reverse} - /> +
); }; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/styles.ts b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/styles.ts new file mode 100644 index 0000000000000..b43399de6335a --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/styles.ts @@ -0,0 +1,122 @@ +import { stylesFactory } from '../../themes'; +import { GrafanaTheme } from '@grafana/data'; +import { focusCss } from '../../themes/mixins'; +import { css as cssCore } from '@emotion/core'; +import { css } from 'emotion'; + +export const getFocusStyle = (theme: GrafanaTheme) => css` + &:focus { + ${focusCss(theme)} + } +`; + +export const getStyles = stylesFactory((theme: GrafanaTheme, isHorizontal: boolean) => { + const trackColor = theme.isLight ? theme.palette.gray5 : theme.palette.dark6; + const container = isHorizontal + ? css` + width: 100%; + ` + : css` + height: 100%; + margin: ${theme.spacing.sm} ${theme.spacing.lg} ${theme.spacing.sm} ${theme.spacing.sm}; + `; + + return { + container, + slider: css` + .rc-slider { + display: flex; + flex-grow: 1; + margin-left: 7px; // half the size of the handle to align handle to the left on 0 value + } + .rc-slider-vertical .rc-slider-handle { + margin-top: -10px; + } + .rc-slider-handle { + border: solid 2px ${theme.palette.blue77}; + background-color: ${theme.palette.blue77}; + } + .rc-slider-handle:hover { + border-color: ${theme.palette.blue77}; + } + .rc-slider-handle:focus { + border-color: ${theme.palette.blue77}; + box-shadow: none; + } + .rc-slider-handle:active { + border-color: ${theme.palette.blue77}; + box-shadow: none; + } + .rc-slider-handle-click-focused:focus { + border-color: ${theme.palette.blue77}; + } + .rc-slider-dot-active { + border-color: ${theme.palette.blue77}; + } + .rc-slider-track { + background-color: ${theme.palette.blue77}; + } + .rc-slider-rail { + background-color: ${trackColor}; + border: 1px solid ${trackColor}; + } + `, + /** Global component from @emotion/core doesn't accept computed classname string returned from css from emotion. + * It accepts object containing the computed name and flattened styles returned from css from @emotion/core + * */ + tooltip: cssCore` + body { + .rc-slider-tooltip { + cursor: grab; + user-select: none; + z-index: ${theme.zIndex.tooltip}; + } + + .rc-slider-tooltip-inner { + color: ${theme.colors.text}; + background-color: transparent !important; + border-radius: 0; + box-shadow: none; + } + + .rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow { + display: none; + } + + .rc-slider-tooltip-placement-top { + padding: 0; + } + } + `, + sliderInput: css` + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + `, + sliderInputVertical: css` + flex-direction: column; + height: 100%; + + .rc-slider { + margin: 0; + order: 2; + } + `, + sliderInputField: css` + display: flex; + flex-grow: 0; + flex-basis: 50px; + margin-left: ${theme.spacing.lg}; + height: ${theme.spacing.formInputHeight}px; + text-align: center; + border-radius: ${theme.border.radius.sm}; + border: 1px solid ${theme.colors.formInputBorder}; + ${getFocusStyle(theme)}; + `, + sliderInputFieldVertical: css` + margin: 0 0 ${theme.spacing.lg} 0; + order: 1; + `, + }; +}); diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/types.ts b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/types.ts new file mode 100644 index 0000000000000..7838465b79c62 --- /dev/null +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/Slider/types.ts @@ -0,0 +1,29 @@ +import { Orientation } from '../../types/orientation'; + +export interface SliderProps { + min: number; + max: number; + orientation?: Orientation; + /** Set current positions of handle(s). If only 1 value supplied, only 1 handle displayed. */ + value?: number; + reverse?: boolean; + step?: number; + tooltipAlwaysVisible?: boolean; + formatTooltipResult?: (value: number) => number; + onChange?: (value: number) => void; + onAfterChange?: (value?: number) => void; +} + +export interface RangeSliderProps { + min: number; + max: number; + orientation?: Orientation; + /** Set current positions of handle(s). If only 1 value supplied, only 1 handle displayed. */ + value?: number[]; + reverse?: boolean; + step?: number; + tooltipAlwaysVisible?: boolean; + formatTooltipResult?: (value: number) => number | string; + onChange?: (value: number[]) => void; + onAfterChange?: (value: number[]) => void; +} diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/index.ts b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/index.ts index a42b11cbf90a7..0c6d24213750d 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/index.ts +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/components/index.ts @@ -126,11 +126,13 @@ export { default as Chart } from './Chart'; export { TooltipContainer } from './Chart/TooltipContainer'; export { Drawer } from './Drawer/Drawer'; export { Slider } from './Slider/Slider'; +export { RangeSlider } from './Slider/RangeSlider'; // TODO: namespace!! export { StringValueEditor } from './OptionsUI/string'; export { StringArrayEditor } from './OptionsUI/strings'; export { NumberValueEditor } from './OptionsUI/number'; +export { SliderValueEditor } from './OptionsUI/slider'; export { SelectValueEditor } from './OptionsUI/select'; export { FieldConfigItemHeaderTitle } from './FieldConfigs/FieldConfigItemHeaderTitle'; diff --git a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/utils/standardEditors.tsx b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/utils/standardEditors.tsx index c70a279478a3f..8c45c5d0a418e 100644 --- a/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/utils/standardEditors.tsx +++ b/branches/chore/arve-oauth-orgs/packages/grafana-ui/src/utils/standardEditors.tsx @@ -25,6 +25,7 @@ import { import { Switch } from '../components/Switch/Switch'; import { NumberValueEditor, + SliderValueEditor, RadioButtonGroup, StringValueEditor, StringArrayEditor, @@ -229,6 +230,13 @@ export const getStandardOptionEditors = () => { editor: NumberValueEditor as any, }; + const slider: StandardEditorsRegistryItem = { + id: 'slider', + name: 'Slider', + description: 'Allows numeric values input', + editor: SliderValueEditor as any, + }; + const text: StandardEditorsRegistryItem = { id: 'text', name: 'Text', @@ -323,6 +331,7 @@ export const getStandardOptionEditors = () => { return [ text, number, + slider, boolean, radio, select, diff --git a/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.test.tsx b/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.test.tsx index fe74db6ca76fc..9d0c92c7d036c 100644 --- a/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.test.tsx +++ b/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.test.tsx @@ -3,7 +3,7 @@ import { mount } from 'enzyme'; import { ExploreId } from '../../../types/explore'; import { SortOrder } from 'app/core/utils/richHistory'; import { RichHistoryQueriesTab, Props } from './RichHistoryQueriesTab'; -import { Slider } from '@grafana/ui'; +import { RangeSlider } from '@grafana/ui'; jest.mock('../state/selectors', () => ({ getExploreDatasources: jest.fn() })); @@ -30,7 +30,7 @@ describe('RichHistoryQueriesTab', () => { describe('slider', () => { it('should render slider', () => { const wrapper = setup(); - expect(wrapper.find(Slider)).toHaveLength(1); + expect(wrapper.find(RangeSlider)).toHaveLength(1); }); it('should render slider with correct timerange', () => { const wrapper = setup(); diff --git a/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx b/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx index fad75758906a2..e286915a1233d 100644 --- a/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx +++ b/branches/chore/arve-oauth-orgs/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx @@ -20,7 +20,7 @@ import { // Components import RichHistoryCard from './RichHistoryCard'; import { sortOrderOptions } from './RichHistory'; -import { Slider, Select } from '@grafana/ui'; +import { RangeSlider, Select } from '@grafana/ui'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; export interface Props { @@ -186,7 +186,7 @@ export function RichHistoryQueriesTab(props: Props) {
Filter history
{mapNumbertoTimeInSlider(timeFilter[0])}
- (GraphPane description: '', defaultValue: true, }) - .addSelect({ + .addSliderInput({ path: 'line.width', name: 'Line width', defaultValue: 1, settings: { - options: [ - { value: 1, label: '1 • thin' }, - { value: 2, label: '2' }, - { value: 3, label: '3' }, - { value: 4, label: '4' }, - { value: 5, label: '5' }, - { value: 6, label: '6' }, - { value: 7, label: '7' }, - { value: 8, label: '8' }, - { value: 9, label: '9' }, - { value: 10, label: '10 • thick' }, - ], + min: 1, + max: 10, + step: 1, }, showIf: c => { return c.line.show; @@ -52,23 +43,14 @@ export const plugin = new PanelPlugin(GraphPane description: '', defaultValue: false, }) - .addSelect({ + .addSliderInput({ path: 'points.radius', name: 'Point radius', defaultValue: 4, settings: { - options: [ - { value: 1, label: '1 • thin' }, - { value: 2, label: '2' }, - { value: 3, label: '3' }, - { value: 4, label: '4' }, - { value: 5, label: '5' }, - { value: 6, label: '6' }, - { value: 7, label: '7' }, - { value: 8, label: '8' }, - { value: 9, label: '9' }, - { value: 10, label: '10 • thick' }, - ], + min: 1, + max: 10, + step: 1, }, showIf: c => c.points.show, }) @@ -78,24 +60,14 @@ export const plugin = new PanelPlugin(GraphPane description: '', defaultValue: false, }) - .addSelect({ + .addSliderInput({ path: 'fill.alpha', name: 'Fill area opacity', defaultValue: 0.1, settings: { - options: [ - { value: 0, label: 'No Fill' }, - { value: 0.1, label: '10% • transparent' }, - { value: 0.2, label: '20%' }, - { value: 0.3, label: '30%' }, - { value: 0.4, label: '40% ' }, - { value: 0.5, label: '50%' }, - { value: 0.6, label: '60%' }, - { value: 0.7, label: '70%' }, - { value: 0.8, label: '80%' }, - { value: 0.9, label: '90%' }, - { value: 1, label: '100% • opaque' }, - ], + min: 0, + max: 1, + step: 0.1, }, }) .addTextInput({