From 4a888d8ca3b7eb9c3ff4fe29078c9c84b2125d00 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Wed, 30 Oct 2019 15:23:17 -0500 Subject: [PATCH 1/2] parent component handles popover state; mousedown triggers popover toggle --- .../color_stops/color_stop_thumb.test.tsx | 15 +++++++ .../color_stops/color_stop_thumb.tsx | 43 +++++++++++++------ .../color_stops/color_stops.test.tsx | 12 +++--- .../color_picker/color_stops/color_stops.tsx | 17 ++++++-- src/components/form/range/range_thumb.tsx | 6 ++- 5 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/components/color_picker/color_stops/color_stop_thumb.test.tsx b/src/components/color_picker/color_stops/color_stop_thumb.test.tsx index 69c4da271fa..c85664395b0 100644 --- a/src/components/color_picker/color_stops/color_stop_thumb.test.tsx +++ b/src/components/color_picker/color_stops/color_stop_thumb.test.tsx @@ -25,6 +25,9 @@ test('renders EuiColorStopThumb', () => { globalMin={0} globalMax={100} colorPickerMode="default" + isPopoverOpen={false} + openPopover={() => {}} + closePopover={() => {}} {...requiredProps} /> ); @@ -42,6 +45,9 @@ test('renders swatch-only EuiColorStopThumb', () => { globalMin={0} globalMax={100} colorPickerMode="swatch" + isPopoverOpen={false} + openPopover={() => {}} + closePopover={() => {}} {...requiredProps} /> ); @@ -59,6 +65,9 @@ test('renders picker-only EuiColorStopThumb', () => { globalMin={0} globalMax={100} colorPickerMode="picker" + isPopoverOpen={false} + openPopover={() => {}} + closePopover={() => {}} {...requiredProps} /> ); @@ -77,6 +86,9 @@ test('renders disabled EuiColorStopThumb', () => { globalMax={100} colorPickerMode="default" disabled={true} + isPopoverOpen={false} + openPopover={() => {}} + closePopover={() => {}} {...requiredProps} /> ); @@ -95,6 +107,9 @@ test('renders readOnly EuiColorStopThumb', () => { globalMax={100} colorPickerMode="default" readOnly={true} + isPopoverOpen={false} + openPopover={() => {}} + closePopover={() => {}} {...requiredProps} /> ); diff --git a/src/components/color_picker/color_stops/color_stop_thumb.tsx b/src/components/color_picker/color_stops/color_stop_thumb.tsx index 5bc33d90efb..848f0b69b62 100644 --- a/src/components/color_picker/color_stops/color_stop_thumb.tsx +++ b/src/components/color_picker/color_stops/color_stop_thumb.tsx @@ -51,7 +51,9 @@ interface EuiColorStopThumbProps extends CommonProps, ColorStop { colorPickerSwatches?: EuiColorPickerProps['swatches']; disabled?: boolean; readOnly?: boolean; - isPopoverOpen?: boolean; + isPopoverOpen: boolean; + openPopover: () => void; + closePopover: () => void; 'data-index'?: string; 'aria-valuetext'?: string; } @@ -76,12 +78,13 @@ export const EuiColorStopThumb: FunctionComponent = ({ colorPickerSwatches, disabled, readOnly, - isPopoverOpen: beginOpen = false, + isPopoverOpen, + openPopover, + closePopover, 'data-index': dataIndex, 'aria-valuetext': ariaValueText, }) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(beginOpen); - const [hasFocus, setHasFocus] = useState(beginOpen); + const [hasFocus, setHasFocus] = useState(isPopoverOpen); const [colorIsInvalid, setColorIsInvalid] = useState(isColorInvalid(color)); const [stopIsInvalid, setStopIsInvalid] = useState(isStopInvalid(stop)); const [numberInputRef, setNumberInputRef] = useState(); @@ -103,10 +106,6 @@ export const EuiColorStopThumb: FunctionComponent = ({ return getPositionFromStop(stop, parentRef!, globalMin, globalMax); }; - const openPopover = () => setIsPopoverOpen(true); - - const closePopover = () => setIsPopoverOpen(false); - const handleOnRemove = () => { if (onRemove) { closePopover(); @@ -171,13 +170,20 @@ export const EuiColorStopThumb: FunctionComponent = ({ const handleKeyDown = (e: React.KeyboardEvent) => { switch (e.keyCode) { + case keyCodes.ENTER: + e.preventDefault(); + openPopover(); + break; + case keyCodes.LEFT: e.preventDefault(); + if (readOnly) return; handleStopChange(stop - 1); break; case keyCodes.RIGHT: e.preventDefault(); + if (readOnly) return; handleStopChange(stop + 1); break; } @@ -187,6 +193,20 @@ export const EuiColorStopThumb: FunctionComponent = ({ handlePointerChange ); + const handleOnMouseDown = (e: React.MouseEvent) => { + if (!readOnly) { + handleMouseDown(e); + } + openPopover(); + }; + + const handleTouchStart = (e: React.TouchEvent) => { + if (!readOnly) { + handleInteraction(e); + } + openPopover(); + }; + const classes = classNames( 'euiColorStopPopover', { @@ -231,14 +251,13 @@ export const EuiColorStopThumb: FunctionComponent = ({ min={localMin} max={localMax} value={stop} - onClick={openPopover} onFocus={handleFocus} onBlur={() => setHasFocus(false)} onMouseOver={() => setHasFocus(true)} onMouseOut={() => setHasFocus(false)} - onKeyDown={readOnly ? undefined : handleKeyDown} - onMouseDown={readOnly ? undefined : handleMouseDown} - onTouchStart={readOnly ? undefined : handleInteraction} + onKeyDown={handleKeyDown} + onMouseDown={handleOnMouseDown} + onTouchStart={handleTouchStart} onTouchMove={readOnly ? undefined : handleInteraction} aria-valuetext={ariaValueText} aria-label={ariaLabel} diff --git a/src/components/color_picker/color_stops/color_stops.test.tsx b/src/components/color_picker/color_stops/color_stops.test.tsx index 9ebacfa9501..1675248ad80 100644 --- a/src/components/color_picker/color_stops/color_stops.test.tsx +++ b/src/components/color_picker/color_stops/color_stops.test.tsx @@ -182,7 +182,7 @@ test('popover color selector is shown when the thumb is clicked', () => { findTestSubject(colorStops, 'euiColorStopThumb') .first() - .simulate('click'); + .simulate('mousedown', { pageX: 0, pageY: 0 }); const colorSelector = findTestSubject(colorStops, 'euiColorStopPopover'); expect(colorSelector.length).toBe(1); }); @@ -201,7 +201,7 @@ test('stop input updates stops', () => { findTestSubject(colorStops, 'euiColorStopThumb') .first() - .simulate('click'); + .simulate('mousedown', { pageX: 0, pageY: 0 }); const event = { target: { value: '10' } }; const inputs = colorStops.find('input[type="number"]'); expect(inputs.length).toBe(1); @@ -231,7 +231,7 @@ test('stop input updates stops with error prevention (reset to bounds)', () => { findTestSubject(colorStops, 'euiColorStopThumb') .first() - .simulate('click'); + .simulate('mousedown', { pageX: 0, pageY: 0 }); const event = { target: { value: '1000' } }; const inputs = colorStops.find('input[type="number"]'); inputs.simulate('change', event); @@ -260,7 +260,7 @@ test('hex input updates stops', () => { findTestSubject(colorStops, 'euiColorStopThumb') .first() - .simulate('click'); + .simulate('mousedown', { pageX: 0, pageY: 0 }); const event = { target: { value: '#FFFFFF' } }; const inputs = colorStops.find('input[type="text"]'); expect(inputs.length).toBe(1); @@ -290,7 +290,7 @@ test('hex input updates stops with error', () => { findTestSubject(colorStops, 'euiColorStopThumb') .first() - .simulate('click'); + .simulate('mousedown', { pageX: 0, pageY: 0 }); const event = { target: { value: '#FFFFF' } }; const inputs = colorStops.find('input[type="text"]'); inputs.simulate('change', event); @@ -319,7 +319,7 @@ test('picker updates stops', () => { findTestSubject(colorStops, 'euiColorStopThumb') .first() - .simulate('click'); + .simulate('mousedown', { pageX: 0, pageY: 0 }); const swatches = colorStops.find('button.euiColorPicker__swatchSelect'); expect(swatches.length).toBe(VISUALIZATION_COLORS.length); swatches.first().simulate('click'); diff --git a/src/components/color_picker/color_stops/color_stops.tsx b/src/components/color_picker/color_stops/color_stops.tsx index e7b498c2e9f..29af34797a0 100644 --- a/src/components/color_picker/color_stops/color_stops.tsx +++ b/src/components/color_picker/color_stops/color_stops.tsx @@ -124,6 +124,7 @@ export const EuiColorStops: FunctionComponent = ({ }, [colorStops, min, rangeMax]); const [hasFocus, setHasFocus] = useState(false); const [focusedStopIndex, setFocusedStopIndex] = useState(null); + const [openedStopId, setOpenedStopId] = useState(null); const [wrapperRef, setWrapperRef] = useState(null); const [addTargetPosition, setAddTargetPosition] = useState(0); const [isHoverDisabled, setIsHoverDisabled] = useState(false); @@ -133,8 +134,12 @@ export const EuiColorStops: FunctionComponent = ({ useEffect(() => { if (focusStopOnUpdate !== null) { - const toFocus = sortedStops.map(el => el.stop).indexOf(focusStopOnUpdate); - onFocusStop(toFocus); + const toFocusIndex = sortedStops + .map(el => el.stop) + .indexOf(focusStopOnUpdate); + const toFocusId = toFocusIndex > -1 ? sortedStops[toFocusIndex].id : null; + onFocusStop(toFocusIndex); + setOpenedStopId(toFocusId); setFocusStopOnUpdate(null); } }, [sortedStops]); @@ -324,7 +329,13 @@ export const EuiColorStops: FunctionComponent = ({ aria-valuetext={`Stop: ${colorStop.stop}, Color: ${ colorStop.color } (${index + 1} of ${colorStops.length})`} - isPopoverOpen={colorStop.stop === focusStopOnUpdate} + isPopoverOpen={colorStop.id === openedStopId} + openPopover={() => { + setOpenedStopId(colorStop.id); + }} + closePopover={() => { + setOpenedStopId(null); + }} /> )); diff --git a/src/components/form/range/range_thumb.tsx b/src/components/form/range/range_thumb.tsx index e1b7c0196ff..bd032725e79 100644 --- a/src/components/form/range/range_thumb.tsx +++ b/src/components/form/range/range_thumb.tsx @@ -15,7 +15,7 @@ interface BaseProps extends CommonProps { interface ButtonLike extends BaseProps, HTMLAttributes {} interface DivLike extends BaseProps, - Omit, 'onClick'> {} + Omit, 'onClick' | 'onMouseDown'> {} export type EuiRangeThumbProps = ExclusiveUnion; @@ -28,6 +28,7 @@ export const EuiRangeThumb: FunctionComponent = ({ showInput, showTicks, onClick, + onMouseDown, tabIndex, ...rest }) => { @@ -47,10 +48,11 @@ export const EuiRangeThumb: FunctionComponent = ({ 'aria-disabled': !!disabled, tabIndex: showInput || !!disabled ? -1 : tabIndex || 0, }; - return onClick ? ( + return onClick || onMouseDown ? (