diff --git a/src-docs/src/views/range/input_only.js b/src-docs/src/views/range/input_only.js new file mode 100644 index 00000000000..f6e1cd08463 --- /dev/null +++ b/src-docs/src/views/range/input_only.js @@ -0,0 +1,90 @@ +import React, { Component, Fragment } from 'react'; + +import { EuiRange, EuiSpacer, EuiDualRange } from '../../../../src/components'; + +import makeId from '../../../../src/components/form/form_row/make_id'; + +export default class extends Component { + constructor(props) { + super(props); + + this.levels = [ + { + min: 0, + max: 20, + color: 'danger', + }, + { + min: 20, + max: 100, + color: 'success', + }, + ]; + + this.state = { + value: '20', + dualValue: [20, 100], + }; + } + + onChange = e => { + this.setState({ + value: e.target.value, + }); + }; + + onDualChange = value => { + this.setState({ + dualValue: value, + }); + }; + + render() { + return ( + + + + + + + + + + + + + + + + ); + } +} diff --git a/src-docs/src/views/range/range_example.js b/src-docs/src/views/range/range_example.js index b72d93d5bc3..f2d0d37c740 100644 --- a/src-docs/src/views/range/range_example.js +++ b/src-docs/src/views/range/range_example.js @@ -45,6 +45,10 @@ import StatesExample from './states'; const statesSource = require('!!raw-loader!./states'); const statesHtml = renderToHtml(StatesExample); +import InputOnlyExample from './input_only'; +const inputOnlySource = require('!!raw-loader!./input_only'); +const inputOnlyHtml = renderToHtml(InputOnlyExample); + export const RangeControlExample = { title: 'Range sliders', intro: ( @@ -112,16 +116,16 @@ export const RangeControlExample = { EuiRange, }, demo: , - snippet: ` - -// Show tooltip +/>`, + `// Show tooltip - -// Show thickened range and prepend a string to the tooltip +/>`, + `// Show thickened range and prepend a string to the tooltip `, + ], }, { title: 'Dual range', @@ -224,9 +228,7 @@ export const RangeControlExample = { ], demo: , props: { EuiRangeInput }, - snippet: ` - -`, + snippet: ['', ''], }, { title: 'Tick marks', @@ -265,17 +267,17 @@ export const RangeControlExample = { ], demo: , props: { EuiRangeTicks }, - snippet: ` - - - -', + '', + ``, + ], }, { title: 'Levels', @@ -305,21 +307,60 @@ export const RangeControlExample = { ], demo: , props: { EuiRangeLevels }, - snippet: ` - -`, + ``, + ], + }, + { + title: 'Inputs with range in a dropdown', + text: ( + +

+ Passing showInput="inputWithPopover"{' '} + instead of a boolean will only display the inputs until the input is + interacted with in which case a dropdown will appear displaying the + actual slider. +

+
+ ), + source: [ + { + type: GuideSectionTypes.JS, + code: inputOnlySource, + }, + { + type: GuideSectionTypes.HTML, + code: inputOnlyHtml, + }, + ], + demo: , + snippet: [ + ` {}} + showInput="inputWithPopover" +/>`, + ` {}} + showInput="inputWithPopover" +/>`, + ], }, { title: 'Kitchen sink', @@ -327,8 +368,7 @@ export const RangeControlExample = {

Other alterations you can add to the range are{' '} - compressed, fullWidth, and{' '} - disabled. + fullWidth, and disabled.

), @@ -343,11 +383,11 @@ export const RangeControlExample = { }, ], demo: , - snippet: ` {}} - compressed fullWidth disabled showTicks @@ -358,13 +398,11 @@ export const RangeControlExample = { tickInterval={} levels={[]} aria-describedBy={replaceWithID} -/> - -`, + ` {}} - compressed fullWidth disabled showLabels @@ -374,6 +412,7 @@ export const RangeControlExample = { levels={[]} aria-describedBy={replaceWithID} />`, + ], }, ], }; diff --git a/src/components/form/field_number/__snapshots__/field_number.test.js.snap b/src/components/form/field_number/__snapshots__/field_number.test.js.snap index ea8bcf26f2b..a4a346449bb 100644 --- a/src/components/form/field_number/__snapshots__/field_number.test.js.snap +++ b/src/components/form/field_number/__snapshots__/field_number.test.js.snap @@ -25,6 +25,15 @@ exports[`EuiFieldNumber is rendered 1`] = ` `; +exports[`EuiFieldNumber props controlOnly is rendered 1`] = ` + + + +`; + exports[`EuiFieldNumber props fullWidth is rendered 1`] = ` { const classes = classNames('euiFieldNumber', className, { @@ -33,6 +34,28 @@ export const EuiFieldNumber = ({ 'euiFieldNumber-isLoading': isLoading, }); + const control = ( + + + + ); + + if (controlOnly) { + return control; + } + return ( - - - + {control} ); }; @@ -114,6 +123,11 @@ EuiFieldNumber.propTypes = { PropTypes.node, PropTypes.arrayOf(PropTypes.node), ]), + /** + * Completely removes form control layout wrapper and ignores + * icon, prepend, and append. Best used inside EuiFormControlLayoutDelimited. + */ + controlOnly: PropTypes.bool, }; EuiFieldNumber.defaultProps = { diff --git a/src/components/form/field_number/field_number.test.js b/src/components/form/field_number/field_number.test.js index a1082c3c986..939f1b317a3 100644 --- a/src/components/form/field_number/field_number.test.js +++ b/src/components/form/field_number/field_number.test.js @@ -59,6 +59,12 @@ describe('EuiFieldNumber', () => { expect(component).toMatchSnapshot(); }); + test('controlOnly is rendered', () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); + describe('value', () => { test('value is number', () => { const component = render( diff --git a/src/components/form/form_control_layout/_form_control_layout_delimited.scss b/src/components/form/form_control_layout/_form_control_layout_delimited.scss index 824785c0d1d..17a1c7dbafa 100644 --- a/src/components/form/form_control_layout/_form_control_layout_delimited.scss +++ b/src/components/form/form_control_layout/_form_control_layout_delimited.scss @@ -11,8 +11,10 @@ > .euiFormControlLayout__childrenWrapper { display: flex; align-items: center; + width: 100%; } + // Target when the euiFormControlLayout is compressed without specifying the full class name in case it ever changes &[class*='--compressed'] { @include euiFormControlDefaultShadow($borderOnly: true); border-radius: $euiBorderRadius / 2; @@ -31,10 +33,19 @@ } } - &[class*='--fullWidth'] .euiFormControlLayout__childrenWrapper { + // Target when the euiFormControlLayout is fullWidth without specifying the full class name in case it ever changes + &[class*='--fullWidth'] .euiFormControlLayout__childrenWrapper, + &[class*='--fullWidth'] input { width: 100%; + max-width: none; + } + + // Target when the euiFormControlLayout is disabled without specifying the full class name in case it ever changes + &[class*='-isDisabled'] { + @include euiFormControlDisabledStyle; } + // Target when the euiFormControlLayout is readOnly without specifying the full class name in case it ever changes &[class*='--readOnly'] { @include euiFormControlReadOnlyStyle; diff --git a/src/components/form/range/__snapshots__/dual_range.test.js.snap b/src/components/form/range/__snapshots__/dual_range.test.js.snap index 668f64bd7fb..d9bdf8df45b 100644 --- a/src/components/form/range/__snapshots__/dual_range.test.js.snap +++ b/src/components/form/range/__snapshots__/dual_range.test.js.snap @@ -10,6 +10,7 @@ exports[`EuiDualRange allows value prop to accept empty strings 1`] = ` +
+
+
-
-
-
`; @@ -55,6 +57,14 @@ exports[`EuiDualRange is rendered 1`] = `
+
+
+
-
-
-
`; exports[`EuiDualRange props compressed should render 1`] = `
+
+
+
-
-
-
`; @@ -116,23 +119,6 @@ exports[`EuiDualRange props custom ticks should render 1`] = ` class="euiRangeTrack" style="margin-right:0.6em" > - -
-
-
@@ -157,6 +143,24 @@ exports[`EuiDualRange props custom ticks should render 1`] = ` 100kb
+
+
+
+
`; @@ -168,24 +172,25 @@ exports[`EuiDualRange props disabled should render 1`] = `
+
+
+
-
-
-
`; @@ -197,23 +202,24 @@ exports[`EuiDualRange props fullWidth should render 1`] = `
+
+
+
-
-
-
`; @@ -244,6 +250,14 @@ exports[`EuiDualRange props inputs should render 1`] = `
+
+
+
-
-
-
+
+
+
-
-
-
`; @@ -278,14 +318,6 @@ exports[`EuiRange props range should render 1`] = `
-
@@ -294,6 +326,15 @@ exports[`EuiRange props range should render 1`] = ` style="margin-left:0%;width:8%" />
+
`; @@ -306,13 +347,6 @@ exports[`EuiRange props ticks should render 1`] = ` class="euiRangeTrack" style="margin-right:0.6em" > -
+
`; @@ -385,6 +427,7 @@ exports[`EuiRange props value should render 1`] = ` > .euiFormControlLayout { /* 1 */ width: auto; } diff --git a/src/components/form/range/_variables.scss b/src/components/form/range/_variables.scss index ce6a5cfd8e6..623e58acce8 100644 --- a/src/components/form/range/_variables.scss +++ b/src/components/form/range/_variables.scss @@ -12,3 +12,5 @@ $euiRangeTrackBorderColor: $euiRangeTrackColor !default; $euiRangeTrackRadius: $euiBorderRadius !default; $euiRangeDisabledOpacity: .25; + +$euiRangeHighlightHeight: $euiSizeXS; diff --git a/src/components/form/range/dual_range.js b/src/components/form/range/dual_range.js index 3d3a63f4dc6..8e0b3131bbb 100644 --- a/src/components/form/range/dual_range.js +++ b/src/components/form/range/dual_range.js @@ -4,6 +4,9 @@ import classNames from 'classnames'; import { keyCodes } from '../../../services'; import { isWithinRange } from '../../../services/number'; +import { EuiInputPopover } from '../../popover'; +import { EuiFormControlLayoutDelimited } from '../form_control_layout'; +import makeId from '../form_row/make_id'; import { EuiRangeHighlight } from './range_highlight'; import { EuiRangeInput } from './range_input'; @@ -15,15 +18,21 @@ import { EuiRangeWrapper } from './range_wrapper'; export class EuiDualRange extends Component { state = { + id: this.props.id || makeId(), hasFocus: false, rangeSliderRefAvailable: false, + isPopoverOpen: false, + rangeWidth: null, }; + maxNode = null; + minNode = null; rangeSliderRef = null; handleRangeSliderRefUpdate = ref => { this.rangeSliderRef = ref; this.setState({ rangeSliderRefAvailable: !!ref, + rangeWidth: !!ref ? ref.clientWidth : null, }); }; @@ -201,7 +210,7 @@ export class EuiDualRange extends Component { this._handleOnChange(this.lowerValue, upper, e); }; - calculateThumbPositionStyle = value => { + calculateThumbPositionStyle = (value, width) => { // Calculate the left position based on value const decimal = (value - this.props.min) / (this.props.max - this.props.min); @@ -210,7 +219,11 @@ export class EuiDualRange extends Component { valuePosition = valuePosition >= 0 ? valuePosition : 0; const EUI_THUMB_SIZE = 16; - const thumbToTrackRatio = EUI_THUMB_SIZE / this.rangeSliderRef.clientWidth; + const trackWidth = + this.props.showInput === 'only' && !!width + ? width + : this.rangeSliderRef.clientWidth; + const thumbToTrackRatio = EUI_THUMB_SIZE / trackWidth; const trackPositionScale = (1 - thumbToTrackRatio) * 100; return { left: `${valuePosition * trackPositionScale}%` }; }; @@ -221,13 +234,56 @@ export class EuiDualRange extends Component { }); }; + onInputFocus = () => { + this.setState({ + isPopoverOpen: true, + }); + }; + + onInputBlur = e => { + // Firefox returns `relatedTarget` as `null` for security reasons, but provides a proprietary `explicitOriginalTarget` + const relatedTarget = e.relatedTarget || e.explicitOriginalTarget; + if (!relatedTarget || relatedTarget.id !== this.state.id) { + this.closePopover(); + } + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + onResize = width => { + this.setState({ + rangeWidth: width, + }); + }; + + inputRef = (node, ref) => { + if (!this.props.showInput !== 'inputWithPopover') return; + + // IE11 doesn't support the `relatedTarget` event property for blur events + // but does add it for focusout. React doesn't support `onFocusOut` so here we are. + if (this[ref] != null) { + this[ref].removeEventListener('focusout', this.onInputBlur); + } + + this[ref] = node; + + if (this[ref]) { + this[ref].addEventListener('focusout', this.onInputBlur); + } + }; + render() { const { className, compressed, disabled, fullWidth, - id, + readOnly, + id: propsId, max, min, name, @@ -245,34 +301,78 @@ export class EuiDualRange extends Component { ...rest } = this.props; - const classes = classNames('euiDualRange', className); + const { id } = this.state; + const digitTolerance = Math.max(String(min).length, String(max).length); + const showInputOnly = showInput === 'inputWithPopover'; + const canShowDropdown = showInputOnly && !readOnly && !disabled; - return ( - - {showInput && ( - - )} + const minInput = !!showInput ? ( + this.inputRef(node, 'minNode')} + /> + ) : ( + undefined + ); + + const maxInput = !!showInput ? ( + this.inputRef(node, 'maxNode')} + /> + ) : ( + undefined + ); + + const classes = classNames('euiDualRange', className); + const theRange = ( + + {!showInputOnly && minInput} {showLabels && ( {min} )} + {showRange && this.isValid && ( + + )} + this.toggleHasFocus(true)} onBlur={() => this.toggleHasFocus(false)} - style={this.calculateThumbPositionStyle(this.lowerValue || min)} + style={this.calculateThumbPositionStyle( + this.lowerValue || min, + this.state.rangeWidth + )} aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} /> @@ -324,48 +440,50 @@ export class EuiDualRange extends Component { value={this.upperValue} disabled={disabled} showTicks={showTicks} - showInput={showInput} + showInput={!!showInput} onKeyDown={this.handleUpperKeyDown} onFocus={() => this.toggleHasFocus(true)} onBlur={() => this.toggleHasFocus(false)} - style={this.calculateThumbPositionStyle(this.upperValue || max)} + style={this.calculateThumbPositionStyle( + this.upperValue || max, + this.state.rangeWidth + )} aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} /> )} - - {showRange && this.isValid && ( - - )} {showLabels && {max}} - {showInput && ( - + ); + + const thePopover = showInputOnly ? ( + - )} - + } + fullWidth={fullWidth} + isOpen={this.state.isPopoverOpen} + closePopover={this.closePopover} + disableFocusTrap={true} + onPanelResize={this.onResize}> + {theRange} + + ) : ( + undefined ); + + return thePopover || theRange; } } @@ -384,14 +502,16 @@ EuiDualRange.propTypes = { fullWidth: PropTypes.bool, compressed: PropTypes.bool, disabled: PropTypes.bool, + readOnly: PropTypes.bool, /** * Shows static min/max labels on the sides of the range slider */ showLabels: PropTypes.bool, /** - * Displays a input controls for direct manipulation + * Pass `true` to displays an extra input control for direct manipulation. + * Pass `'inputWithPopover'` to only show the input but show the range in a dropdown. */ - showInput: PropTypes.bool, + showInput: PropTypes.oneOf([true, false, 'inputWithPopover']), /** * Shows clickable tick marks and labels at the given interval (`step`/`tickInterval`) */ diff --git a/src/components/form/range/dual_range.test.js b/src/components/form/range/dual_range.test.js index cc010c8b06d..03c9a822023 100644 --- a/src/components/form/range/dual_range.test.js +++ b/src/components/form/range/dual_range.test.js @@ -4,6 +4,8 @@ import { requiredProps } from '../../../test/required_props'; import { EuiDualRange } from './dual_range'; +jest.mock('../form_row/make_id', () => () => 'generated-id'); + describe('EuiDualRange', () => { test('is rendered', () => { const component = render( @@ -86,6 +88,23 @@ describe('EuiDualRange', () => { expect(component).toMatchSnapshot(); }); + test('only input should render', () => { + const component = render( + {}} + showInput="inputWithPopover" + {...requiredProps} + /> + ); + + expect(component).toMatchSnapshot(); + }); + test('levels should render', () => { const component = render( { const isValid = isWithinRange( this.props.min, @@ -26,13 +38,50 @@ export class EuiRange extends Component { return isWithinRange(this.props.min, this.props.max, this.props.value); } + onInputFocus = () => { + this.setState({ + isPopoverOpen: true, + }); + }; + + onInputBlur = e => { + // Firefox returns `relatedTarget` as `null` for security reasons, but provides a proprietary `explicitOriginalTarget` + const relatedTarget = e.relatedTarget || e.explicitOriginalTarget; + if (!relatedTarget || relatedTarget.id !== this.state.id) { + this.closePopover(); + } + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + inputRef = node => { + if (!this.props.showInput !== 'inputWithPopover') return; + + // IE11 and Safar don't support the `relatedTarget` event property for blur events + // but do add it for focusout. React doesn't support `onFocusOut` so here we are. + if (this.inputNode != null) { + this.inputNode.removeEventListener('focusout', this.onInputBlur); + } + + this.inputNode = node; + + if (this.inputNode) { + this.inputNode.addEventListener('focusout', this.onInputBlur); + } + }; + render() { const { className, compressed, disabled, fullWidth, - id, + readOnly, + id: propsId, max, min, name, @@ -54,11 +103,41 @@ export class EuiRange extends Component { ...rest } = this.props; - const classes = classNames('euiRange', className); + const { id } = this.state; + const digitTolerance = Math.max(String(min).length, String(max).length); + const showInputOnly = showInput === 'inputWithPopover'; + const canShowDropdown = showInputOnly && !readOnly && !disabled; + + const theInput = !!showInput ? ( + + ) : ( + undefined + ); - return ( - + const classes = classNames('euiRange', className); + + const theRange = ( + {showLabels && ( {min} @@ -66,6 +145,7 @@ export class EuiRange extends Component { )} + {showRange && this.isValid && ( + + )} + {showValue && !!String(value).length && ( )} - - {showRange && this.isValid && ( - - )} {showLabels && ( {max} )} - {showInput && ( - - )} + {!showInputOnly && theInput} ); + + const thePopover = showInputOnly ? ( + + {theRange} + + ) : ( + undefined + ); + + return thePopover ? thePopover : theRange; } } @@ -152,9 +238,10 @@ EuiRange.propTypes = { */ showLabels: PropTypes.bool, /** - * Displays an extra input control for direct manipulation + * Pass `true` to displays an extra input control for direct manipulation. + * Pass `'inputWithPopover'` to only show the input but show the range in a dropdown. */ - showInput: PropTypes.bool, + showInput: PropTypes.oneOf([true, false, 'inputWithPopover']), /** * Shows clickable tick marks and labels at the given interval (`step`/`tickInterval`) */ diff --git a/src/components/form/range/range.test.js b/src/components/form/range/range.test.js index 652a6c104be..a9c113e1b16 100644 --- a/src/components/form/range/range.test.js +++ b/src/components/form/range/range.test.js @@ -4,6 +4,8 @@ import { requiredProps } from '../../../test/required_props'; import { EuiRange } from './range'; +jest.mock('../form_row/make_id', () => () => 'generated-id'); + describe('EuiRange', () => { test('is rendered', () => { const component = render( @@ -99,6 +101,23 @@ describe('EuiRange', () => { expect(component).toMatchSnapshot(); }); + test('input only should render', () => { + const component = render( + {}} + showInput="inputWithPopover" + {...requiredProps} + /> + ); + + expect(component).toMatchSnapshot(); + }); + test('levels should render', () => { const component = render( = ({ upperValue, max, min, + compressed, }) => { // Calculate the width the range based on value // const rangeWidth = (value - min) / (max - min); @@ -29,6 +31,7 @@ export const EuiRangeHighlight: FunctionComponent = ({ const classes = classNames('euiRangeHighlight', { 'euiRangeHighlight--hasTicks': showTicks, + 'euiRangeHighlight--compressed': compressed, }); const progressClasses = classNames('euiRangeHighlight__progress', { diff --git a/src/components/form/range/range_input.js b/src/components/form/range/range_input.js index 732a28b5541..d7e240f9411 100644 --- a/src/components/form/range/range_input.js +++ b/src/components/form/range/range_input.js @@ -14,12 +14,16 @@ export const EuiRangeInput = ({ name, side, digitTolerance, + fullWidth, + autoSize, ...rest }) => { // Chrome will properly size the input based on the max value, but FF & IE do not. // Calculate the width of the input based on highest number of characters. // Add 2 to accomodate for input stepper - const widthStyle = { width: `${digitTolerance / 1.25 + 2}em` }; + const widthStyle = autoSize + ? { width: `${digitTolerance / 1.25 + 2}em` } + : undefined; return ( ); @@ -48,7 +53,11 @@ EuiRangeInput.propTypes = { name: PropTypes.string, digitTolerance: PropTypes.number.isRequired, side: PropTypes.oneOf(['min', 'max']), + fullWidth: PropTypes.bool, + autoSize: PropTypes.bool, + inputRef: PropTypes.func, }; EuiRangeInput.defaultProps = { side: 'max', + autoSize: true, }; diff --git a/src/components/form/range/range_levels.tsx b/src/components/form/range/range_levels.tsx index b59c63cf852..c607b100aa4 100644 --- a/src/components/form/range/range_levels.tsx +++ b/src/components/form/range/range_levels.tsx @@ -21,6 +21,7 @@ export interface EuiRangeLevelsProps { max: number; min: number; showTicks?: boolean; + compressed?: boolean; } export const EuiRangeLevels: FunctionComponent = ({ @@ -28,6 +29,7 @@ export const EuiRangeLevels: FunctionComponent = ({ max, min, showTicks, + compressed, }) => { const validateLevelIsInRange = (level: EuiRangeLevel) => { if (level.min < min) { @@ -44,6 +46,7 @@ export const EuiRangeLevels: FunctionComponent = ({ const classes = classNames('euiRangeLevels', { 'euiRangeLevels--hasTicks': showTicks, + 'euiRangeLevels--compressed': compressed, }); return ( diff --git a/src/components/form/range/range_slider.tsx b/src/components/form/range/range_slider.tsx index a9120f0fc74..4c16e277000 100644 --- a/src/components/form/range/range_slider.tsx +++ b/src/components/form/range/range_slider.tsx @@ -16,6 +16,7 @@ export type EuiRangeSliderProps = InputHTMLAttributes & min: number; max: number; step?: number; + compressed?: boolean; hasFocus?: boolean; showRange?: boolean; showTicks?: boolean; @@ -43,6 +44,7 @@ export const EuiRangeSlider: FunctionComponent< showTicks, showRange, hasFocus, + compressed, ...rest }, ref: Ref @@ -53,6 +55,7 @@ export const EuiRangeSlider: FunctionComponent< 'euiRangeSlider--hasTicks': showTicks, 'euiRangeSlider--hasFocus': hasFocus, 'euiRangeSlider--hasRange': showRange, + 'euiRangeSlider--compressed': compressed, }, className ); diff --git a/src/components/form/range/range_ticks.tsx b/src/components/form/range/range_ticks.tsx index 545707c5d54..d8d63ffc322 100644 --- a/src/components/form/range/range_ticks.tsx +++ b/src/components/form/range/range_ticks.tsx @@ -24,6 +24,7 @@ export type EuiRangeTicksProps = Omit< value?: number | string | Array; min: number; max: number; + compressed?: boolean; interval?: number; disabled?: boolean; onChange?: MouseEventHandler; @@ -38,6 +39,7 @@ export const EuiRangeTicks: FunctionComponent = ({ max, min, interval = 1, + compressed, }) => { // Calculate the width of each tick mark const percentageWidth = (interval / (max - min + interval)) * 100; @@ -48,8 +50,12 @@ export const EuiRangeTicks: FunctionComponent = ({ ? undefined : { margin: `0 ${percentageWidth / -2}%`, left: 0, right: 0 }; + const classes = classNames('euiRangeTicks', { + 'euiRangeTicks--compressed': compressed, + }); + return ( -
+
{tickSequence.map(tickValue => { const tickStyle: { left?: string; width?: string } = {}; let customTick; diff --git a/src/components/form/range/range_tooltip.tsx b/src/components/form/range/range_tooltip.tsx index 2795deacf3e..73b40f710a9 100644 --- a/src/components/form/range/range_tooltip.tsx +++ b/src/components/form/range/range_tooltip.tsx @@ -9,6 +9,7 @@ export interface EuiRangeTooltipProps { min: number; name?: string; showTicks?: boolean; + compressed?: boolean; } export const EuiRangeTooltip: FunctionComponent = ({ @@ -19,7 +20,12 @@ export const EuiRangeTooltip: FunctionComponent = ({ min, name, showTicks, + compressed, }) => { + const classes = classNames('euiRangeTooltip', { + 'euiRangeTooltip--compressed': compressed, + }); + // Calculate the left position based on value let val = 0; if (typeof value === 'number') { @@ -52,7 +58,7 @@ export const EuiRangeTooltip: FunctionComponent = ({ ); return ( -
+
; + compressed?: boolean; disabled?: boolean; showTicks?: boolean; tickInterval?: number; @@ -117,6 +118,7 @@ export class EuiRangeTrack extends Component { levels, onChange, value, + compressed, } = this.props; // TODO: Move these to only re-calculate if no-value props have changed @@ -147,9 +149,9 @@ export class EuiRangeTrack extends Component { return (
- {children} {levels && !!levels.length && ( { {tickSequence && ( { interval={tickInterval || step} /> )} + {children}
); } diff --git a/src/components/form/range/range_wrapper.tsx b/src/components/form/range/range_wrapper.tsx index 78cb6783f21..8179bab0239 100644 --- a/src/components/form/range/range_wrapper.tsx +++ b/src/components/form/range/range_wrapper.tsx @@ -4,17 +4,20 @@ import classNames from 'classnames'; export interface EuiRangeWrapperProps { className?: string; fullWidth?: boolean; + compressed?: boolean; } export const EuiRangeWrapper: FunctionComponent = ({ children, className, fullWidth, + compressed, }) => { const classes = classNames( 'euiRangeWrapper', { 'euiRangeWrapper--fullWidth': fullWidth, + 'euiRangeWrapper--compressed': compressed, }, className ); diff --git a/src/components/popover/input_popover.tsx b/src/components/popover/input_popover.tsx index 6659708b0aa..a9f4f2df543 100644 --- a/src/components/popover/input_popover.tsx +++ b/src/components/popover/input_popover.tsx @@ -15,9 +15,11 @@ import { cascadingMenuKeyCodes } from '../../services'; interface EuiInputPopoverProps extends Omit { + disableFocusTrap?: boolean; fullWidth?: boolean; input: EuiPopoverProps['button']; inputRef?: EuiPopoverProps['buttonRef']; + onPanelResize?: (width?: number) => void; } type Props = CommonProps & @@ -27,8 +29,10 @@ type Props = CommonProps & export const EuiInputPopover: FunctionComponent = ({ children, className, + disableFocusTrap = false, input, fullWidth = false, + onPanelResize, ...props }) => { const [inputEl, setInputEl] = useState(); @@ -42,6 +46,9 @@ export const EuiInputPopover: FunctionComponent = ({ if (panelEl && (!!inputElWidth || !!width)) { const newWidth = !!width ? width : inputElWidth; panelEl.style.width = `${newWidth}px`; + if (onPanelResize) { + onPanelResize(newWidth); + } } }; const onResize = () => { @@ -68,8 +75,9 @@ export const EuiInputPopover: FunctionComponent = ({ ); }); if ( - tabbableItems.length && - tabbableItems[tabbableItems.length - 1] === document.activeElement + disableFocusTrap || + (tabbableItems.length && + tabbableItems[tabbableItems.length - 1] === document.activeElement) ) { props.closePopover(); } @@ -96,7 +104,7 @@ export const EuiInputPopover: FunctionComponent = ({ panelRef={panelRef} className={classes} {...props}> - +
{children}