diff --git a/packages/grafana-ui/src/components/Slider/Slider.test.tsx b/packages/grafana-ui/src/components/Slider/Slider.test.tsx index f688f546508..d4274a8dc4b 100644 --- a/packages/grafana-ui/src/components/Slider/Slider.test.tsx +++ b/packages/grafana-ui/src/components/Slider/Slider.test.tsx @@ -26,4 +26,29 @@ describe('Slider', () => { expect(wrapper.html()).not.toContain('aria-valuenow="20"'); expect(wrapper.html()).not.toContain('aria-valuenow="10"'); }); + + it('allows for custom values to be set in the input', () => { + const wrapper = mount(); + const sliderInput = wrapper.find('input'); + sliderInput.simulate('focus'); + sliderInput.simulate('change', { target: { value: 50 } }); + sliderInput.simulate('blur'); + expect(wrapper.html()).toContain('aria-valuenow="50"'); + }); + + it('defaults after blur if input value is outside of range', () => { + const wrapper = mount(); + const sliderInput = wrapper.find('input'); + sliderInput.simulate('focus'); + sliderInput.simulate('change', { target: { value: 200 } }); + // re-grab to check value is out of range before blur + const sliderInputIncorrect = wrapper.find('input'); + expect(sliderInputIncorrect.get(0).props.value).toEqual('200'); + + sliderInput.simulate('blur'); + expect(wrapper.html()).toContain('aria-valuenow="100"'); + // re-grab to check value is back inside range + const sliderInputCorrect = wrapper.find('input'); + expect(sliderInputCorrect.get(0).props.value).toEqual('100'); + }); }); diff --git a/packages/grafana-ui/src/components/Slider/Slider.tsx b/packages/grafana-ui/src/components/Slider/Slider.tsx index 329105ff912..4cc36a31426 100644 --- a/packages/grafana-ui/src/components/Slider/Slider.tsx +++ b/packages/grafana-ui/src/components/Slider/Slider.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, ChangeEvent, FunctionComponent } from 'react'; +import React, { useState, useCallback, ChangeEvent, FunctionComponent, FocusEvent } from 'react'; import SliderComponent from 'rc-slider'; import { cx } from '@emotion/css'; import { Global } from '@emotion/react'; @@ -46,9 +46,6 @@ export const Slider: FunctionComponent = ({ v = 0; } - v > max && (v = max); - v < min && (v = min); - setSliderValue(v); if (onChange) { @@ -59,7 +56,22 @@ export const Slider: FunctionComponent = ({ onAfterChange(v); } }, - [max, min, onChange, onAfterChange] + [onChange, onAfterChange] + ); + + // Check for min/max on input blur so user is able to enter + // custom values that might seem above/below min/max on first keystroke + const onSliderInputBlur = useCallback( + (e: FocusEvent) => { + const v = +e.target.value; + + if (v > max) { + setSliderValue(max); + } else if (v < min) { + setSliderValue(min); + } + }, + [max, min] ); const sliderInputClassNames = !isHorizontal ? [styles.sliderInputVertical] : []; @@ -88,6 +100,7 @@ export const Slider: FunctionComponent = ({ className={cx(styles.sliderInputField, ...sliderInputFieldClassNames)} value={`${sliderValue}`} // to fix the react leading zero issue onChange={onSliderInputChange} + onBlur={onSliderInputBlur} min={min} max={max} />