From 78232d21fcdca41282cff9314ff99c5d9b45ebed Mon Sep 17 00:00:00 2001 From: Redjue Date: Fri, 3 Feb 2023 17:14:27 +0800 Subject: [PATCH 1/4] fix: segmented not work in RTL mode(#40128) --- assets/index.less | 8 ++++++-- docs/demo/rtl.md | 3 +++ docs/examples/rtl.tsx | 42 ++++++++++++++++++++++++++++++++++++++++++ src/MotionThumb.tsx | 26 ++++++++++++++++++++++++-- src/index.tsx | 1 + tests/index.spec.tsx | 24 ++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 docs/demo/rtl.md create mode 100644 docs/examples/rtl.tsx diff --git a/assets/index.less b/assets/index.less index 0f93795..801039c 100644 --- a/assets/index.less +++ b/assets/index.less @@ -79,8 +79,8 @@ .segmented-item-selected(); position: absolute; - top: 0; - left: 0; + // top: 0; + // left: 0; width: 0; height: 100%; padding: 4px 0; @@ -93,4 +93,8 @@ width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); will-change: transform, width; } + + &-rtl { + direction: rtl; + } } diff --git a/docs/demo/rtl.md b/docs/demo/rtl.md new file mode 100644 index 0000000..6a9e988 --- /dev/null +++ b/docs/demo/rtl.md @@ -0,0 +1,3 @@ +## rtl + + diff --git a/docs/examples/rtl.tsx b/docs/examples/rtl.tsx new file mode 100644 index 0000000..91c3761 --- /dev/null +++ b/docs/examples/rtl.tsx @@ -0,0 +1,42 @@ +import '../../assets/style.less'; +import React, { useState } from 'react'; +import Segmented from 'rc-segmented'; + +export default function App() { + const [direction, setDirection] = useState<'rtl' | 'ltr'>('rtl'); + return ( +
+ + +

+ console.log(value, typeof value)} + direction={direction} + /> +

+ ); +} diff --git a/src/MotionThumb.tsx b/src/MotionThumb.tsx index 9527e24..5e004b1 100644 --- a/src/MotionThumb.tsx +++ b/src/MotionThumb.tsx @@ -7,6 +7,7 @@ import type { SegmentedValue } from '.'; type ThumbReact = { left: number; + right: number; width: number; } | null; @@ -18,6 +19,7 @@ export interface MotionThumbInterface { motionName: string; onMotionStart: VoidFunction; onMotionEnd: VoidFunction; + direction?: 'ltr' | 'rtl'; } const calcThumbStyle = ( @@ -26,6 +28,10 @@ const calcThumbStyle = ( targetElement ? { left: targetElement.offsetLeft, + right: + targetElement.parentElement?.clientWidth! - + targetElement.clientWidth - + targetElement.offsetLeft, width: targetElement.clientWidth, } : null; @@ -42,6 +48,7 @@ export default function MotionThumb(props: MotionThumbInterface) { motionName, onMotionStart, onMotionEnd, + direction, } = props; const thumbRef = React.useRef(null); @@ -81,6 +88,21 @@ export default function MotionThumb(props: MotionThumbInterface) { } }, [value]); + const thumbStart = React.useMemo( + () => + direction === 'rtl' + ? toPX(-(prevStyle?.right as number)) + : toPX(prevStyle?.left as number), + [direction, prevStyle], + ); + const thumbActive = React.useMemo( + () => + direction === 'rtl' + ? toPX(-(nextStyle?.right as number)) + : toPX(nextStyle?.left as number), + [direction, nextStyle], + ); + // =========================== Motion =========================== const onAppearStart = () => { return { @@ -118,9 +140,9 @@ export default function MotionThumb(props: MotionThumbInterface) { {({ className: motionClassName, style: motionStyle }, ref) => { const mergedStyle = { ...motionStyle, - '--thumb-start-left': toPX(prevStyle?.left), + '--thumb-start-left': thumbStart, '--thumb-start-width': toPX(prevStyle?.width), - '--thumb-active-left': toPX(nextStyle?.left), + '--thumb-active-left': thumbActive, '--thumb-active-width': toPX(nextStyle?.width), } as React.CSSProperties; diff --git a/src/index.tsx b/src/index.tsx index 117e43e..37933eb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -184,6 +184,7 @@ const Segmented = React.forwardRef( value={rawValue} containerRef={containerRef} motionName={`${prefixCls}-${motionName}`} + direction={direction} getValueIndex={(val) => segmentedOptions.findIndex((n) => n.value === val) } diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index ec9ea27..dfe33e3 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -477,4 +477,28 @@ describe('rc-segmented', () => { expectMatchChecked(container, [true, false, false]); }); + + it('click can work as expected with rtl', () => { + const handleValueChange = jest.fn(); + const { container } = render( + handleValueChange(value)} + />, + ); + + fireEvent.click(container.querySelectorAll('.rc-segmented-item-input')[1]); + expectMatchChecked(container, [false, true, false]); + + // Motion to active + act(() => { + jest.runAllTimers(); + }); + + exceptThumbHaveStyle(container, { + '--thumb-active-left': '-22px', + '--thumb-active-width': '118px', + }); + }); }); From b5084b86893f02ae7c6098e77bd04e38c84c271f Mon Sep 17 00:00:00 2001 From: Redjue Date: Sat, 4 Feb 2023 21:54:21 +0800 Subject: [PATCH 2/4] fix: lint issue and remove rtl.md --- docs/demo/rtl.md | 3 --- src/MotionThumb.tsx | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 docs/demo/rtl.md diff --git a/docs/demo/rtl.md b/docs/demo/rtl.md deleted file mode 100644 index 6a9e988..0000000 --- a/docs/demo/rtl.md +++ /dev/null @@ -1,3 +0,0 @@ -## rtl - - diff --git a/src/MotionThumb.tsx b/src/MotionThumb.tsx index 1c3d2e0..d7f01e6 100644 --- a/src/MotionThumb.tsx +++ b/src/MotionThumb.tsx @@ -1,8 +1,8 @@ -import * as React from 'react'; -import CSSMotion from 'rc-motion'; import classNames from 'classnames'; +import CSSMotion from 'rc-motion'; import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; import { composeRef } from 'rc-util/lib/ref'; +import * as React from 'react'; import type { SegmentedValue } from '.'; type ThumbReact = { @@ -29,7 +29,7 @@ const calcThumbStyle = ( ? { left: targetElement.offsetLeft, right: - targetElement.parentElement?.clientWidth! - + (targetElement.parentElement?.clientWidth as number) - targetElement.clientWidth - targetElement.offsetLeft, width: targetElement.clientWidth, From e8b07e1c8a152caa35d9cd12d154f686bf6d0e79 Mon Sep 17 00:00:00 2001 From: Redjue Date: Sun, 5 Feb 2023 00:50:44 +0800 Subject: [PATCH 3/4] fix: test code --- tests/index.test.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/index.test.tsx b/tests/index.test.tsx index a7bfa3d..59fb1b1 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -27,7 +27,7 @@ describe('rc-segmented', () => { const styleText = container .querySelector('.rc-segmented-thumb') ?.getAttribute('data-test-style'); - const style = JSON.parse(styleText!) || {}; + const style = styleText ? JSON.parse(styleText!) : {}; expect(style).toMatchObject(matchStyle); } @@ -525,17 +525,23 @@ describe('rc-segmented', () => { }); it('click can work as expected with rtl', () => { + const offsetParentSpy = jest + .spyOn(HTMLElement.prototype, 'offsetParent', 'get') + .mockImplementation(() => { + return container; + }); const handleValueChange = jest.fn(); const { container } = render( handleValueChange(value)} />, ); fireEvent.click(container.querySelectorAll('.rc-segmented-item-input')[1]); expectMatchChecked(container, [false, true, false]); + expect(handleValueChange).toBeCalledWith('Android'); // Motion to active act(() => { @@ -546,5 +552,7 @@ describe('rc-segmented', () => { '--thumb-active-left': '-22px', '--thumb-active-width': '118px', }); + + offsetParentSpy.mockRestore(); }); }); From 5d7fe201f3598158cbda20ba7068d0c2e650869e Mon Sep 17 00:00:00 2001 From: Redjue Date: Mon, 6 Feb 2023 11:18:58 +0800 Subject: [PATCH 4/4] fix: test cover problem --- src/MotionThumb.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MotionThumb.tsx b/src/MotionThumb.tsx index d7f01e6..384aeb8 100644 --- a/src/MotionThumb.tsx +++ b/src/MotionThumb.tsx @@ -29,7 +29,7 @@ const calcThumbStyle = ( ? { left: targetElement.offsetLeft, right: - (targetElement.parentElement?.clientWidth as number) - + (targetElement.parentElement!.clientWidth as number) - targetElement.clientWidth - targetElement.offsetLeft, width: targetElement.clientWidth,