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 d6cc79d08b0..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`] = ` { + // 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, @@ -243,7 +253,7 @@ export class EuiDualRange extends Component { disabled, fullWidth, readOnly, - id, + id: propsId, max, min, name, @@ -261,6 +271,8 @@ export class EuiDualRange extends Component { ...rest } = this.props; + const { id } = this.state; + const digitTolerance = Math.max(String(min).length, String(max).length); const showInputOnly = showInput === 'only'; const canShowDropdown = showInputOnly && !readOnly && !disabled; @@ -281,6 +293,7 @@ export class EuiDualRange extends Component { aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} onFocus={canShowDropdown ? this.onInputFocus : undefined} + onBlur={canShowDropdown ? this.onInputBlur : undefined} readOnly={readOnly} autoSize={!showInputOnly} fullWidth={!!showInputOnly && fullWidth} @@ -306,6 +319,7 @@ export class EuiDualRange extends Component { aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} onFocus={canShowDropdown ? this.onInputFocus : undefined} + onBlur={canShowDropdown ? this.onInputBlur : undefined} readOnly={readOnly} autoSize={!showInputOnly} fullWidth={!!showInputOnly && fullWidth} @@ -424,7 +438,8 @@ export class EuiDualRange extends Component { } fullWidth={fullWidth} isOpen={this.state.isPopoverOpen} - closePopover={this.closePopover}> + closePopover={this.closePopover} + disableFocusTrap={true}> {theRange} ) : ( diff --git a/src/components/form/range/dual_range.test.js b/src/components/form/range/dual_range.test.js index 4b96afebd01..39c9a2cb947 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( diff --git a/src/components/form/range/range.js b/src/components/form/range/range.js index b760ecefec1..7be1b7dcc57 100644 --- a/src/components/form/range/range.js +++ b/src/components/form/range/range.js @@ -4,6 +4,7 @@ import classNames from 'classnames'; import { isWithinRange } from '../../../services/number'; import { EuiInputPopover } from '../../popover'; +import makeId from '../form_row/make_id'; import { EuiRangeHighlight } from './range_highlight'; import { EuiRangeInput } from './range_input'; @@ -17,6 +18,7 @@ export class EuiRange extends Component { constructor(props) { super(props); this.state = { + id: props.id || makeId(), isPopoverOpen: false, }; } @@ -40,6 +42,14 @@ export class EuiRange extends Component { }); }; + 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, @@ -53,7 +63,7 @@ export class EuiRange extends Component { disabled, fullWidth, readOnly, - id, + id: propsId, max, min, name, @@ -75,6 +85,8 @@ export class EuiRange extends Component { ...rest } = this.props; + const { id } = this.state; + const digitTolerance = Math.max(String(min).length, String(max).length); const showInputOnly = showInput === 'only'; const canShowDropdown = showInputOnly && !readOnly && !disabled; @@ -92,6 +104,7 @@ export class EuiRange extends Component { onChange={this.handleOnChange} name={name} onFocus={canShowDropdown ? this.onInputFocus : undefined} + onBlur={canShowDropdown ? this.onInputBlur : undefined} fullWidth={showInputOnly && fullWidth} autoSize={!showInputOnly} {...rest} @@ -180,7 +193,8 @@ export class EuiRange extends Component { input={theInput} fullWidth={fullWidth} isOpen={this.state.isPopoverOpen} - closePopover={this.closePopover}> + closePopover={this.closePopover} + disableFocusTrap={true}> {theRange} ) : ( diff --git a/src/components/form/range/range.test.js b/src/components/form/range/range.test.js index b645f82bcbf..33495ea665d 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( diff --git a/src/components/popover/input_popover.tsx b/src/components/popover/input_popover.tsx index 6659708b0aa..8145f91dfb4 100644 --- a/src/components/popover/input_popover.tsx +++ b/src/components/popover/input_popover.tsx @@ -15,6 +15,7 @@ import { cascadingMenuKeyCodes } from '../../services'; interface EuiInputPopoverProps extends Omit { + disableFocusTrap?: boolean; fullWidth?: boolean; input: EuiPopoverProps['button']; inputRef?: EuiPopoverProps['buttonRef']; @@ -27,6 +28,7 @@ type Props = CommonProps & export const EuiInputPopover: FunctionComponent = ({ children, className, + disableFocusTrap = false, input, fullWidth = false, ...props @@ -68,8 +70,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 +99,7 @@ export const EuiInputPopover: FunctionComponent = ({ panelRef={panelRef} className={classes} {...props}> - +
{children}