diff --git a/docs/api.md b/docs/api.md index fb173828..9dae0301 100644 --- a/docs/api.md +++ b/docs/api.md @@ -186,3 +186,23 @@ nav: + +## inputRef + +```tsx | pure +import InputNumber, { InputNumberRef } from 'rc-input-number'; + +const inputRef = useRef(null); + +useEffect(() => { + inputRef.current.focus(); // the input will get focus + inputRef.current.blur(); // the input will lose focus +}, []); +// .... +; +``` + +| Property | Type | Description | +| -------- | --------------------------------------- | --------------------------------- | +| focus | `(options?: InputFocusOptions) => void` | The input get focus when called | +| blur | `() => void` | The input loses focus when called | diff --git a/docs/demo/focus.tsx b/docs/demo/focus.tsx new file mode 100644 index 00000000..7b114b63 --- /dev/null +++ b/docs/demo/focus.tsx @@ -0,0 +1,28 @@ +/* eslint no-console:0 */ +import InputNumber, { InputNumberRef } from 'rc-input-number'; +import React from 'react'; +import '../../assets/index.less'; + +export default () => { + const inputRef = React.useRef(null); + + return ( +
+ +
+ + + + +
+
+ ); +}; diff --git a/docs/example.md b/docs/example.md index 72af5617..8252ae2d 100644 --- a/docs/example.md +++ b/docs/example.md @@ -45,4 +45,6 @@ nav: +## focus + diff --git a/src/InputNumber.tsx b/src/InputNumber.tsx index 1169f2a3..78c04b9b 100644 --- a/src/InputNumber.tsx +++ b/src/InputNumber.tsx @@ -24,6 +24,8 @@ import useFrame from './hooks/useFrame'; export type { ValueType }; export interface InputNumberRef extends HTMLInputElement { + focus: (options?: InputFocusOptions) => void; + blur: () => void; nativeElement: HTMLElement; } @@ -660,6 +662,7 @@ const InputNumber = React.forwardRef((props, r React.useImperativeHandle(ref, () => proxyObject(inputFocusRef.current, { + focus, nativeElement: holderRef.current.nativeElement || inputNumberDomRef.current, }), ); diff --git a/tests/focus.test.tsx b/tests/focus.test.tsx new file mode 100644 index 00000000..a71bd577 --- /dev/null +++ b/tests/focus.test.tsx @@ -0,0 +1,66 @@ +import { fireEvent, render } from '@testing-library/react'; +import InputNumber, { InputNumberRef } from 'rc-input-number'; +import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; +import React from 'react'; + +const getInputRef = () => { + const ref = React.createRef(); + render(); + return ref; +}; + +describe('InputNumber.Focus', () => { + let inputSpy: ReturnType; + let focus: ReturnType; + let setSelectionRange: ReturnType; + + beforeEach(() => { + focus = jest.fn(); + setSelectionRange = jest.fn(); + inputSpy = spyElementPrototypes(HTMLInputElement, { + focus, + setSelectionRange, + }); + }); + + afterEach(() => { + inputSpy.mockRestore(); + }); + + it('start', () => { + const input = getInputRef(); + input.current?.focus({ cursor: 'start' }); + + expect(focus).toHaveBeenCalled(); + expect(setSelectionRange).toHaveBeenCalledWith(expect.anything(), 0, 0); + }); + + it('end', () => { + const input = getInputRef(); + input.current?.focus({ cursor: 'end' }); + + expect(focus).toHaveBeenCalled(); + expect(setSelectionRange).toHaveBeenCalledWith(expect.anything(), 5, 5); + }); + + it('all', () => { + const input = getInputRef(); + input.current?.focus({ cursor: 'all' }); + + expect(focus).toHaveBeenCalled(); + expect(setSelectionRange).toHaveBeenCalledWith(expect.anything(), 0, 5); + }); + + it('disabled should reset focus', () => { + const { container, rerender } = render(); + const input = container.querySelector('input')!; + + fireEvent.focus(input); + expect(container.querySelector('.rc-input-number-focused')).toBeTruthy(); + + rerender(); + fireEvent.blur(input); + + expect(container.querySelector('.rc-input-number-focused')).toBeFalsy(); + }); +});