Skip to content

Commit

Permalink
feat: improve focus behavior (#681)
Browse files Browse the repository at this point in the history
* feat: improve focus behavior

* feat: add type definition

* test: add test case

* refactor: remove some api

* chore: remove useless code
  • Loading branch information
aojunhao123 authored Nov 1, 2024
1 parent 5b46007 commit c418866
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 0 deletions.
20 changes: 20 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,23 @@ nav:
</tr>
</tbody>
</table>

## inputRef

```tsx | pure
import InputNumber, { InputNumberRef } from 'rc-input-number';

const inputRef = useRef<InputNumberRef>(null);

useEffect(() => {
inputRef.current.focus(); // the input will get focus
inputRef.current.blur(); // the input will lose focus
}, []);
// ....
<InputNumber ref={inputRef} />;
```

| Property | Type | Description |
| -------- | --------------------------------------- | --------------------------------- |
| focus | `(options?: InputFocusOptions) => void` | The input get focus when called |
| blur | `() => void` | The input loses focus when called |
28 changes: 28 additions & 0 deletions docs/demo/focus.tsx
Original file line number Diff line number Diff line change
@@ -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<InputNumberRef>(null);

return (
<div style={{ margin: 10 }}>
<InputNumber aria-label="focus example" value={10} style={{ width: 100 }} ref={inputRef} />
<div style={{ marginTop: 10 }}>
<button type="button" onClick={() => inputRef.current?.focus({ cursor: 'start' })}>
focus at start
</button>
<button type="button" onClick={() => inputRef.current?.focus({ cursor: 'end' })}>
focus at end
</button>
<button type="button" onClick={() => inputRef.current?.focus({ cursor: 'all' })}>
focus to select all
</button>
<button type="button" onClick={() => inputRef.current?.focus({ preventScroll: true })}>
focus prevent scroll
</button>
</div>
</div>
);
};
2 changes: 2 additions & 0 deletions docs/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ nav:

<code src="./demo/wheel.tsx"></code>

## focus

<code src="./demo/focus.tsx"></code>
3 changes: 3 additions & 0 deletions src/InputNumber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -660,6 +662,7 @@ const InputNumber = React.forwardRef<InputNumberRef, InputNumberProps>((props, r

React.useImperativeHandle(ref, () =>
proxyObject(inputFocusRef.current, {
focus,
nativeElement: holderRef.current.nativeElement || inputNumberDomRef.current,
}),
);
Expand Down
66 changes: 66 additions & 0 deletions tests/focus.test.tsx
Original file line number Diff line number Diff line change
@@ -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<InputNumberRef>();
render(<InputNumber ref={ref} defaultValue={12345} />);
return ref;
};

describe('InputNumber.Focus', () => {
let inputSpy: ReturnType<typeof spyElementPrototypes>;
let focus: ReturnType<typeof jest.fn>;
let setSelectionRange: ReturnType<typeof jest.fn>;

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(<InputNumber prefixCls="rc-input-number" />);
const input = container.querySelector('input')!;

fireEvent.focus(input);
expect(container.querySelector('.rc-input-number-focused')).toBeTruthy();

rerender(<InputNumber prefixCls="rc-input-number" disabled />);
fireEvent.blur(input);

expect(container.querySelector('.rc-input-number-focused')).toBeFalsy();
});
});

0 comments on commit c418866

Please sign in to comment.