From 5c0ca03fea456630556ca357a1e4fb20b4b3b1e0 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Thu, 23 Apr 2020 14:08:48 -0700 Subject: [PATCH 1/2] feat(forms): allow multi-thumb range to allow track mouse interaction --- packages/forms/.size-snapshot.json | 16 ++--- .../src/elements/MultiThumbRange.spec.tsx | 34 ++++++++++- .../forms/src/elements/MultiThumbRange.tsx | 61 ++++++++++++++++--- 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/packages/forms/.size-snapshot.json b/packages/forms/.size-snapshot.json index 67692bb2f01..88e703d7ce5 100644 --- a/packages/forms/.size-snapshot.json +++ b/packages/forms/.size-snapshot.json @@ -1,20 +1,20 @@ { "dist/index.cjs.js": { - "bundled": 102434, - "minified": 68435, - "gzipped": 13063 + "bundled": 103453, + "minified": 68750, + "gzipped": 13144 }, "dist/index.esm.js": { - "bundled": 98758, - "minified": 64827, - "gzipped": 12916, + "bundled": 99765, + "minified": 65130, + "gzipped": 12999, "treeshaked": { "rollup": { - "code": 51603, + "code": 51886, "import_statements": 614 }, "webpack": { - "code": 57765 + "code": 58088 } } } diff --git a/packages/forms/src/elements/MultiThumbRange.spec.tsx b/packages/forms/src/elements/MultiThumbRange.spec.tsx index 390fdfe4781..9256d90de91 100644 --- a/packages/forms/src/elements/MultiThumbRange.spec.tsx +++ b/packages/forms/src/elements/MultiThumbRange.spec.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { KEY_CODES } from '@zendeskgarden/container-utilities'; -import { render, renderRtl, fireEvent } from 'garden-test-utils'; +import { render, renderRtl, fireEvent, createEvent } from 'garden-test-utils'; import MultiThumbRange from './MultiThumbRange'; jest.mock('lodash.debounce'); @@ -79,6 +79,38 @@ describe('MultiThumbRange', () => { }); }); + describe('Track selection', () => { + it('updates min value if track is clicked near min thumb', () => { + const onChangeSpy = jest.fn(); + const { getByTestId } = render( + + ); + + const track = getByTestId('track'); + const mouseEvent = createEvent.mouseDown(track); + + (mouseEvent as any).pageX = 45; + fireEvent(track, mouseEvent); + + expect(onChangeSpy).toHaveBeenCalledWith({ minValue: 25, maxValue: 75 }); + }); + + it('updates max value if track is clicked near max thumb', () => { + const onChangeSpy = jest.fn(); + const { getByTestId } = render( + + ); + + const track = getByTestId('track'); + const mouseEvent = createEvent.mouseDown(track); + + (mouseEvent as any).pageX = 100; + fireEvent(track, mouseEvent); + + expect(onChangeSpy).toHaveBeenCalledWith({ minValue: 15, maxValue: 80 }); + }); + }); + describe('Min Thumb', () => { it('applies correct accessibility values', () => { const { getAllByTestId } = render(); diff --git a/packages/forms/src/elements/MultiThumbRange.tsx b/packages/forms/src/elements/MultiThumbRange.tsx index ce19a7a67f5..f8f98c54efe 100644 --- a/packages/forms/src/elements/MultiThumbRange.tsx +++ b/packages/forms/src/elements/MultiThumbRange.tsx @@ -167,13 +167,10 @@ const MultiThumbRange: React.FC [max, minValue, onRangeValuesChange] ); - /** - * Calculates the update thumb position based on current mouse position - */ - const onDocumentMouseMove = useCallback( - e => { + const calculateUpdatedValue = useCallback( + (e: React.MouseEvent) => { if (!trackRailRef.current) { - return; + return undefined; } const trackOffsetLeft = trackRailRef.current.getBoundingClientRect().left; @@ -186,11 +183,21 @@ const MultiThumbRange: React.FC diffX *= -1; } - let newValue = + const newValue = min! + parseInt(((((max! - min!) * diffX) / trackWidth) as unknown) as string, 10); // Reduce updated value to align with step size - newValue = Math.floor(newValue / step!) * step!; + return Math.floor(newValue / step!) * step!; + }, + [max, min, step, theme.rtl] + ); + + /** + * Calculates the update thumb position based on current mouse position + */ + const onDocumentMouseMove = useCallback( + e => { + const newValue = calculateUpdatedValue(e); if (isMinThumbFocused) { updateMinThumbSlider(newValue); @@ -198,7 +205,7 @@ const MultiThumbRange: React.FC updateMaxThumbSlider(newValue); } }, - [isMinThumbFocused, max, min, theme, step, updateMaxThumbSlider, updateMinThumbSlider] + [calculateUpdatedValue, isMinThumbFocused, updateMinThumbSlider, updateMaxThumbSlider] ); const removeDragEvents = useCallback(() => { @@ -210,6 +217,41 @@ const MultiThumbRange: React.FC setIsMousedDown(false); }, [onDocumentMouseMove, themedDocument]); + const onTrackMouseDown = useCallback( + (e: React.MouseEvent) => { + if (e.button !== 0 || disabled) { + return; + } + + e.preventDefault(); + + const valueAtMouseDown = calculateUpdatedValue(e); + + if (valueAtMouseDown === undefined || minValue === undefined || maxValue === undefined) { + return; + } + + const distanceFromMinThumb = Math.abs(minValue - valueAtMouseDown); + const distanceFromMaxThumb = Math.abs(maxValue - valueAtMouseDown); + + if (distanceFromMinThumb <= distanceFromMaxThumb) { + minThumbRef.current && minThumbRef.current.focus(); + updateMinThumbSlider(valueAtMouseDown); + } else { + maxThumbRef.current && maxThumbRef.current.focus(); + updateMaxThumbSlider(valueAtMouseDown); + } + }, + [ + calculateUpdatedValue, + disabled, + maxValue, + minValue, + updateMaxThumbSlider, + updateMinThumbSlider + ] + ); + useEffect(() => { if (isMousedDown && themedDocument) { themedDocument.addEventListener('mousemove', onDocumentMouseMove); @@ -316,6 +358,7 @@ const MultiThumbRange: React.FC backgroundPosition={theme.rtl ? railWidth - maxPosition : minPosition} data-test-id="track" isDisabled={disabled} + onMouseDown={onTrackMouseDown} > Date: Fri, 24 Apr 2020 09:39:21 -0700 Subject: [PATCH 2/2] Move mouse event to slider --- packages/forms/.size-snapshot.json | 16 ++++++++-------- .../src/elements/MultiThumbRange.spec.tsx | 18 +++++++++--------- .../forms/src/elements/MultiThumbRange.tsx | 13 ++++++++++--- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/forms/.size-snapshot.json b/packages/forms/.size-snapshot.json index 88e703d7ce5..47cec340c30 100644 --- a/packages/forms/.size-snapshot.json +++ b/packages/forms/.size-snapshot.json @@ -1,20 +1,20 @@ { "dist/index.cjs.js": { - "bundled": 103453, - "minified": 68750, - "gzipped": 13144 + "bundled": 103605, + "minified": 68827, + "gzipped": 13165 }, "dist/index.esm.js": { - "bundled": 99765, - "minified": 65130, - "gzipped": 12999, + "bundled": 99898, + "minified": 65188, + "gzipped": 13019, "treeshaked": { "rollup": { - "code": 51886, + "code": 51925, "import_statements": 614 }, "webpack": { - "code": 58088 + "code": 58156 } } } diff --git a/packages/forms/src/elements/MultiThumbRange.spec.tsx b/packages/forms/src/elements/MultiThumbRange.spec.tsx index 9256d90de91..ab1c4767846 100644 --- a/packages/forms/src/elements/MultiThumbRange.spec.tsx +++ b/packages/forms/src/elements/MultiThumbRange.spec.tsx @@ -79,33 +79,33 @@ describe('MultiThumbRange', () => { }); }); - describe('Track selection', () => { - it('updates min value if track is clicked near min thumb', () => { + describe('Slider selection', () => { + it('updates min value if slider is clicked near min thumb', () => { const onChangeSpy = jest.fn(); const { getByTestId } = render( ); - const track = getByTestId('track'); - const mouseEvent = createEvent.mouseDown(track); + const slider = getByTestId('slider'); + const mouseEvent = createEvent.mouseDown(slider); (mouseEvent as any).pageX = 45; - fireEvent(track, mouseEvent); + fireEvent(slider, mouseEvent); expect(onChangeSpy).toHaveBeenCalledWith({ minValue: 25, maxValue: 75 }); }); - it('updates max value if track is clicked near max thumb', () => { + it('updates max value if slider is clicked near max thumb', () => { const onChangeSpy = jest.fn(); const { getByTestId } = render( ); - const track = getByTestId('track'); - const mouseEvent = createEvent.mouseDown(track); + const slider = getByTestId('slider'); + const mouseEvent = createEvent.mouseDown(slider); (mouseEvent as any).pageX = 100; - fireEvent(track, mouseEvent); + fireEvent(slider, mouseEvent); expect(onChangeSpy).toHaveBeenCalledWith({ minValue: 15, maxValue: 80 }); }); diff --git a/packages/forms/src/elements/MultiThumbRange.tsx b/packages/forms/src/elements/MultiThumbRange.tsx index f8f98c54efe..ffede9b9033 100644 --- a/packages/forms/src/elements/MultiThumbRange.tsx +++ b/packages/forms/src/elements/MultiThumbRange.tsx @@ -16,7 +16,7 @@ import React, { } from 'react'; import PropTypes from 'prop-types'; import debounce from 'lodash.debounce'; -import { KEY_CODES } from '@zendeskgarden/container-utilities'; +import { KEY_CODES, composeEventHandlers } from '@zendeskgarden/container-utilities'; import { withTheme, useDocument, DEFAULT_THEME } from '@zendeskgarden/react-theming'; import { StyledSlider, @@ -55,6 +55,7 @@ const MultiThumbRange: React.FC step, onChange, theme, + onMouseDown, ...props }) => { const themedDocument = useDocument(theme); @@ -351,14 +352,20 @@ const MultiThumbRange: React.FC const maxPosition = calculateMaxPosition(MIN_DISTANCE); const sliderBackgroundSize = Math.abs(maxPosition) - Math.abs(minPosition); + const onSliderMouseDown = composeEventHandlers(onMouseDown, onTrackMouseDown); + return ( - +