From 37e9a293c5acd630d052c63358993052db41668e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 12 May 2026 16:54:39 +0800 Subject: [PATCH] fix slider lint issues --- docs/examples/components/TooltipSlider.tsx | 2 +- docs/examples/slider.tsx | 2 +- src/Handles/Handle.tsx | 6 +-- src/Handles/index.tsx | 4 +- src/Marks/index.tsx | 2 +- src/Slider.tsx | 43 +++++++++++---------- src/Tracks/index.tsx | 2 +- src/hooks/useDrag.ts | 44 ++++++++++++++-------- src/hooks/useOffset.ts | 7 +++- src/hooks/useRange.ts | 2 +- tests/Range.test.tsx | 24 ++++++------ 11 files changed, 80 insertions(+), 58 deletions(-) diff --git a/docs/examples/components/TooltipSlider.tsx b/docs/examples/components/TooltipSlider.tsx index 97a14517f..9a3240455 100644 --- a/docs/examples/components/TooltipSlider.tsx +++ b/docs/examples/components/TooltipSlider.tsx @@ -16,7 +16,7 @@ interface HandleTooltipProps { const HandleTooltip: React.FC = (props) => { const { value, children, visible, tipFormatter = (val) => `${val} %`, ...restProps } = props; - const tooltipRef = React.useRef(); + const tooltipRef = React.useRef(null); const rafRef = React.useRef(null); function cancelKeepAlign() { diff --git a/docs/examples/slider.tsx b/docs/examples/slider.tsx index 2de9ce50c..cfb0625e9 100644 --- a/docs/examples/slider.tsx +++ b/docs/examples/slider.tsx @@ -60,7 +60,7 @@ class NullableSlider extends React.Component { } const NullableRangeSlider = () => { - const [value, setValue] = React.useState(null); + const [value, setValue] = React.useState(null); return (
diff --git a/src/Handles/Handle.tsx b/src/Handles/Handle.tsx index 903d7233a..edd7f1149 100644 --- a/src/Handles/Handle.tsx +++ b/src/Handles/Handle.tsx @@ -87,7 +87,7 @@ const Handle = React.forwardRef((props, ref) => { // =========================== Keyboard =========================== const onKeyDown: React.KeyboardEventHandler = (e) => { if (!disabled && keyboard) { - let offset: number | 'min' | 'max' = null; + let offset: number | 'min' | 'max' | undefined; // Change the value switch (e.which || e.keyCode) { @@ -131,7 +131,7 @@ const Handle = React.forwardRef((props, ref) => { break; } - if (offset !== null) { + if (offset !== undefined) { e.preventDefault(); onOffsetChange(offset, valueIndex); } @@ -161,7 +161,7 @@ const Handle = React.forwardRef((props, ref) => { if (valueIndex !== null) { divProps = { - tabIndex: disabled ? null : getIndex(tabIndex, valueIndex), + tabIndex: disabled ? undefined : getIndex(tabIndex, valueIndex) ?? undefined, role: 'slider', 'aria-valuemin': min, 'aria-valuemax': max, diff --git a/src/Handles/index.tsx b/src/Handles/index.tsx index 89fa2b205..52f418f34 100644 --- a/src/Handles/index.tsx +++ b/src/Handles/index.tsx @@ -120,12 +120,12 @@ const Handles = React.forwardRef((props, ref) => { key="a11y" {...handleProps} value={values[activeIndex]} - valueIndex={null} + valueIndex={null!} dragging={draggingIndex !== -1} draggingDelete={draggingDelete} render={activeHandleRender} style={{ pointerEvents: 'none' }} - tabIndex={null} + tabIndex={undefined} aria-hidden /> )} diff --git a/src/Marks/index.tsx b/src/Marks/index.tsx index 441e1bc95..ea75343df 100644 --- a/src/Marks/index.tsx +++ b/src/Marks/index.tsx @@ -17,7 +17,7 @@ export interface MarksProps { } const Marks: React.FC = (props) => { - const { prefixCls, marks, onClick } = props; + const { prefixCls, marks = [], onClick } = props; const markPrefixCls = `${prefixCls}-mark`; diff --git a/src/Slider.tsx b/src/Slider.tsx index 448a8bf5b..21b846084 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -185,8 +185,8 @@ const Slider = React.forwardRef>((prop ariaValueTextFormatterForHandle, } = props; - const handlesRef = React.useRef(null); - const containerRef = React.useRef(null); + const handlesRef = React.useRef(null); + const containerRef = React.useRef(null); const direction = React.useMemo(() => { if (vertical) { @@ -214,9 +214,11 @@ const Slider = React.forwardRef>((prop // ============================ Marks ============================= const markList = React.useMemo(() => { - return Object.keys(marks || {}) + const markRecord = marks || {}; + + return Object.keys(markRecord) .map((key) => { - const mark = marks[key]; + const mark = markRecord[key]; const markObj: InternalMarkObj = { value: Number(key), }; @@ -243,10 +245,10 @@ const Slider = React.forwardRef>((prop const [formatValue, offsetValues] = useOffset( mergedMin, mergedMax, - mergedStep, + mergedStep as number, markList, allowCross, - mergedPush, + mergedPush as false | number, ); // ============================ Values ============================ @@ -269,7 +271,7 @@ const Slider = React.forwardRef>((prop // When count provided or value is `undefined`, we fill values if (count || mergedValue === undefined) { - const pointCount = count >= 0 ? count + 1 : 2; + const pointCount = count !== undefined && count >= 0 ? count + 1 : 2; returnValues = returnValues.slice(0, pointCount); // Fill with count @@ -308,7 +310,7 @@ const Slider = React.forwardRef>((prop const finishChange = useEvent((draggingDelete?: boolean) => { // Trigger from `useDrag` will tell if it's a delete action if (draggingDelete) { - handlesRef.current.hideHelp(); + handlesRef.current!.hideHelp(); } const finishValue = getTriggerValue(rawValues); @@ -332,8 +334,8 @@ const Slider = React.forwardRef>((prop triggerChange(cloneNextValues); const nextFocusIndex = Math.max(0, index - 1); - handlesRef.current.hideHelp(); - handlesRef.current.focus(nextFocusIndex); + handlesRef.current!.hideHelp(); + handlesRef.current!.focus(nextFocusIndex); }; const [draggingIndex, draggingValue, draggingDelete, cacheValues, onStartDrag] = useDrag( @@ -395,7 +397,7 @@ const Slider = React.forwardRef>((prop if (e) { (document.activeElement as HTMLElement)?.blur?.(); - handlesRef.current.focus(focusIndex); + handlesRef.current!.focus(focusIndex); onStartDrag(e, focusIndex, cloneNextValues); } else { // https://github.com/ant-design/ant-design/issues/49997 @@ -414,7 +416,7 @@ const Slider = React.forwardRef>((prop e.preventDefault(); const { width, height, left, top, bottom, right } = - containerRef.current.getBoundingClientRect(); + containerRef.current!.getBoundingClientRect(); const { clientX, clientY } = e; let percent: number; @@ -440,7 +442,7 @@ const Slider = React.forwardRef>((prop }; // =========================== Keyboard =========================== - const [keyboardValue, setKeyboardValue] = React.useState(null); + const [keyboardValue, setKeyboardValue] = React.useState(null!); const onHandleOffsetChange = (offset: number | 'min' | 'max', valueIndex: number) => { if (!disabled) { @@ -457,11 +459,12 @@ const Slider = React.forwardRef>((prop if (keyboardValue !== null) { const valueIndex = rawValues.indexOf(keyboardValue); if (valueIndex >= 0) { - handlesRef.current.focus(valueIndex); + handlesRef.current!.focus(valueIndex); } } - setKeyboardValue(null); + setKeyboardValue(null!); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [keyboardValue]); // ============================= Drag ============================= @@ -486,8 +489,9 @@ const Slider = React.forwardRef>((prop React.useEffect(() => { if (!dragging) { const valueIndex = rawValues.lastIndexOf(draggingValue); - handlesRef.current.focus(valueIndex); + handlesRef.current!.focus(valueIndex); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [dragging]); // =========================== Included =========================== @@ -509,7 +513,7 @@ const Slider = React.forwardRef>((prop // ============================= Refs ============================= React.useImperativeHandle(ref, () => ({ focus: () => { - handlesRef.current.focus(0); + handlesRef.current!.focus(0); }, blur: () => { const { activeElement } = document; @@ -520,9 +524,10 @@ const Slider = React.forwardRef>((prop })); // ========================== Auto Focus ========================== + const autoFocusRef = React.useRef(autoFocus); React.useEffect(() => { - if (autoFocus) { - handlesRef.current.focus(0); + if (autoFocusRef.current) { + handlesRef.current!.focus(0); } }, []); diff --git a/src/Tracks/index.tsx b/src/Tracks/index.tsx index 4242bb86c..1b11e3301 100644 --- a/src/Tracks/index.tsx +++ b/src/Tracks/index.tsx @@ -49,7 +49,7 @@ const Tracks: React.FC = (props) => { const tracksNode = trackList?.length && (classNames.tracks || styles.tracks) ? ( , + containerRef: React.RefObject, direction: Direction, rawValues: number[], min: number, @@ -33,15 +33,15 @@ function useDrag( returnValues: number[], onStartMove: OnStartMove, ] { - const [draggingValue, setDraggingValue] = React.useState(null); + const [draggingValue, setDraggingValue] = React.useState(null!); const [draggingIndex, setDraggingIndex] = React.useState(-1); const [draggingDelete, setDraggingDelete] = React.useState(false); const [cacheValues, setCacheValues] = React.useState(rawValues); const [originValues, setOriginValues] = React.useState(rawValues); - const mouseMoveEventRef = React.useRef<(event: MouseEvent) => void>(null); - const mouseUpEventRef = React.useRef<(event: MouseEvent) => void>(null); - const touchEventTargetRef = React.useRef(null); + const mouseMoveEventRef = React.useRef(null); + const mouseUpEventRef = React.useRef(null); + const touchEventTargetRef = React.useRef(null); const { onDragStart, onDragChange } = React.useContext(UnstableContext); @@ -54,11 +54,19 @@ function useDrag( // Clean up event React.useEffect( () => () => { - document.removeEventListener('mousemove', mouseMoveEventRef.current); - document.removeEventListener('mouseup', mouseUpEventRef.current); + if (mouseMoveEventRef.current) { + document.removeEventListener('mousemove', mouseMoveEventRef.current); + } + if (mouseUpEventRef.current) { + document.removeEventListener('mouseup', mouseUpEventRef.current); + } if (touchEventTargetRef.current) { - touchEventTargetRef.current.removeEventListener('touchmove', mouseMoveEventRef.current); - touchEventTargetRef.current.removeEventListener('touchend', mouseUpEventRef.current); + if (mouseMoveEventRef.current) { + touchEventTargetRef.current.removeEventListener('touchmove', mouseMoveEventRef.current); + } + if (mouseUpEventRef.current) { + touchEventTargetRef.current.removeEventListener('touchend', mouseUpEventRef.current); + } } }, [], @@ -82,7 +90,7 @@ function useDrag( rawValues: nextValues, deleteIndex: deleteMark ? draggingIndex : -1, draggingIndex, - draggingValue: nextValue, + draggingValue: nextValue as number, }); } }; @@ -149,14 +157,14 @@ function useDrag( } // Moving - const onMouseMove = (event: MouseEvent | TouchEvent) => { + const onMouseMove: EventListener = (event) => { event.preventDefault(); - const { pageX: moveX, pageY: moveY } = getPosition(event); + const { pageX: moveX, pageY: moveY } = getPosition(event as MouseEvent | TouchEvent); const offsetX = moveX - startX; const offsetY = moveY - startY; - const { width, height } = containerRef.current.getBoundingClientRect(); + const { width, height } = containerRef.current!.getBoundingClientRect(); let offSetPercent: number; let removeDist: number; @@ -192,14 +200,18 @@ function useDrag( }; // End - const onMouseUp = (event: MouseEvent | TouchEvent) => { + const onMouseUp: EventListener = (event) => { event.preventDefault(); document.removeEventListener('mouseup', onMouseUp); document.removeEventListener('mousemove', onMouseMove); if (touchEventTargetRef.current) { - touchEventTargetRef.current.removeEventListener('touchmove', mouseMoveEventRef.current); - touchEventTargetRef.current.removeEventListener('touchend', mouseUpEventRef.current); + if (mouseMoveEventRef.current) { + touchEventTargetRef.current.removeEventListener('touchmove', mouseMoveEventRef.current); + } + if (mouseUpEventRef.current) { + touchEventTargetRef.current.removeEventListener('touchend', mouseUpEventRef.current); + } } mouseMoveEventRef.current = null; mouseUpEventRef.current = null; diff --git a/src/hooks/useOffset.ts b/src/hooks/useOffset.ts index 3c54d06be..83fa3c57e 100644 --- a/src/hooks/useOffset.ts +++ b/src/hooks/useOffset.ts @@ -52,9 +52,9 @@ export default function useOffset( const maxDecimal = Math.max(getDecimal(step), getDecimal(max), getDecimal(min)); const fixedValue = Number(stepValue.toFixed(maxDecimal)); - return min <= fixedValue && fixedValue <= max ? fixedValue : null; + return min <= fixedValue && fixedValue <= max ? fixedValue : null!; } - return null; + return null!; }, [step, min, max, formatRangeValue], ); @@ -168,6 +168,9 @@ export default function useOffset( } else if (offset === 'max') { return max; } + + // Unreachable since `offset` is typed as number | 'min' | 'max'. + return max; }; /** Same as `offsetValue` but return `changed` mark to tell value changed */ diff --git a/src/hooks/useRange.ts b/src/hooks/useRange.ts index f1478bfb6..4867b77ca 100644 --- a/src/hooks/useRange.ts +++ b/src/hooks/useRange.ts @@ -22,6 +22,6 @@ export default function useRange( warning(!editable || !draggableTrack, '`editable` can not work with `draggableTrack`.'); } - return [true, editable, !editable && draggableTrack, minCount || 0, maxCount]; + return [true, !!editable, !editable && !!draggableTrack, minCount || 0, maxCount]; }, [range]); } diff --git a/tests/Range.test.tsx b/tests/Range.test.tsx index 3bf6e8973..9b767162f 100644 --- a/tests/Range.test.tsx +++ b/tests/Range.test.tsx @@ -140,7 +140,9 @@ describe('Range', () => { }); it('should render Range without tabIndex (equal null) correctly', () => { - const { container } = render(); + const { container } = render( + , + ); expect(container.getElementsByClassName('rc-slider-handle')[0]).not.toHaveAttribute('tabIndex'); expect(container.getElementsByClassName('rc-slider-handle')[1]).not.toHaveAttribute('tabIndex'); }); @@ -557,15 +559,15 @@ describe('Range', () => { it('focus()', () => { const handleFocus = jest.fn(); const { container } = render(); - container.querySelector('.rc-slider-handle').focus(); + container.querySelector('.rc-slider-handle')!.focus(); expect(handleFocus).toBeCalled(); }); it('blur()', () => { const handleBlur = jest.fn(); const { container } = render(); - container.querySelector('.rc-slider-handle').focus(); - container.querySelector('.rc-slider-handle').blur(); + container.querySelector('.rc-slider-handle')!.focus(); + container.querySelector('.rc-slider-handle')!.blur(); expect(handleBlur).toHaveBeenCalled(); }); }); @@ -697,7 +699,7 @@ describe('Range', () => { expect(container.querySelectorAll('.rc-slider-track')).toHaveLength(1); // Fire mouse up - fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); + fireEvent.mouseUp(container.querySelector('.rc-slider-handle')!); expect(onChangeComplete).toHaveBeenCalledWith([50, 100]); }); @@ -722,7 +724,7 @@ describe('Range', () => { expect(onChange).toHaveBeenCalledWith([0, 50]); // Fire mouse up - fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); + fireEvent.mouseUp(container.querySelector('.rc-slider-handle')!); expect(onChangeComplete).toHaveBeenCalledWith([0, 50]); }); @@ -753,7 +755,7 @@ describe('Range', () => { expect(onChange).toHaveBeenCalledWith([50, 100]); // Fire mouse up - fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); + fireEvent.mouseUp(container.querySelector('.rc-slider-handle')!); expect(onChangeComplete).toHaveBeenCalledWith([50, 100]); }); }); @@ -783,10 +785,10 @@ describe('Range', () => { expect(onChange).toHaveBeenCalledWith([0, 100]); // Clear all - fireEvent.keyDown(container.querySelector('.rc-slider-handle'), { + fireEvent.keyDown(container.querySelector('.rc-slider-handle')!, { keyCode: keyCode.DELETE, }); - fireEvent.keyDown(container.querySelector('.rc-slider-handle'), { + fireEvent.keyDown(container.querySelector('.rc-slider-handle')!, { keyCode: keyCode.DELETE, }); expect(onChange).toHaveBeenCalledWith([]); @@ -812,8 +814,8 @@ describe('Range', () => { const handle = container.querySelector('.rc-slider-handle'); // Key - fireEvent.mouseEnter(handle); - fireEvent.keyDown(handle, { + fireEvent.mouseEnter(handle!); + fireEvent.keyDown(handle!, { keyCode: keyCode.DELETE, }); expect(onChange).not.toHaveBeenCalled();