diff --git a/package.json b/package.json index 8c9b8ca3..7ed752a6 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "@babel/runtime": "^7.10.1", "@rc-component/mini-decimal": "^1.0.1", "classnames": "^2.2.5", - "rc-input": "~1.4.0", - "rc-util": "^5.28.0" + "rc-input": "~1.5.0", + "rc-util": "^5.40.1" }, "devDependencies": { "@rc-component/father-plugin": "^1.0.1", diff --git a/src/InputNumber.tsx b/src/InputNumber.tsx index 6dc7e23b..1169f2a3 100644 --- a/src/InputNumber.tsx +++ b/src/InputNumber.tsx @@ -9,18 +9,24 @@ import getMiniDecimal, { import clsx from 'classnames'; import { BaseInput } from 'rc-input'; import { useLayoutUpdateEffect } from 'rc-util/lib/hooks/useLayoutEffect'; +import proxyObject from 'rc-util/lib/proxyObject'; import { composeRef } from 'rc-util/lib/ref'; import * as React from 'react'; import useCursor from './hooks/useCursor'; import StepHandler from './StepHandler'; import { getDecupleSteps } from './utils/numberUtil'; +import type { HolderRef } from 'rc-input/lib/BaseInput'; +import { BaseInputProps } from 'rc-input/lib/interface'; import { InputFocusOptions, triggerFocus } from 'rc-input/lib/utils/commonUtils'; import useFrame from './hooks/useFrame'; -import { BaseInputProps } from 'rc-input/lib/interface'; export type { ValueType }; +export interface InputNumberRef extends HTMLInputElement { + nativeElement: HTMLElement; +} + /** * We support `stringMode` which need handle correct type when user call in onChange * format max or min value @@ -100,12 +106,14 @@ export interface InputNumberProps changeOnBlur?: boolean; } -type InternalInputNumberProps = Omit; +type InternalInputNumberProps = Omit & { + domRef: React.Ref; +}; const InternalInputNumber = React.forwardRef( (props: InternalInputNumberProps, ref: React.Ref) => { const { - prefixCls = 'rc-input-number', + prefixCls, className, style, min, @@ -136,6 +144,8 @@ const InternalInputNumber = React.forwardRef( changeOnBlur = true, + domRef, + ...inputProps } = props; @@ -572,6 +582,7 @@ const InternalInputNumber = React.forwardRef( // ============================ Render ============================ return (
) => { - const { - disabled, - style, - prefixCls, - value, - prefix, - suffix, - addonBefore, - addonAfter, - className, - classNames, - ...rest - } = props; - - const inputFocusRef = React.useRef(null); - - const focus = (option?: InputFocusOptions) => { - if (inputFocusRef.current) { - triggerFocus(inputFocusRef.current, option); - } - }; +const InputNumber = React.forwardRef((props, ref) => { + const { + disabled, + style, + prefixCls = 'rc-input-number', + value, + prefix, + suffix, + addonBefore, + addonAfter, + className, + classNames, + ...rest + } = props; + + const holderRef = React.useRef(null); + const inputNumberDomRef = React.useRef(null); + const inputFocusRef = React.useRef(null); + + const focus = (option?: InputFocusOptions) => { + if (inputFocusRef.current) { + triggerFocus(inputFocusRef.current, option); + } + }; - return ( - + proxyObject(inputFocusRef.current, { + nativeElement: holderRef.current.nativeElement || inputNumberDomRef.current, + }), + ); + + return ( + + - - - ); - }, -) as (( + ref={inputFocusRef} + domRef={inputNumberDomRef} + className={classNames?.input} + {...rest} + /> + + ); +}) as (( props: React.PropsWithChildren> & { ref?: React.Ref; }, ) => React.ReactElement) & { displayName?: string }; -InputNumber.displayName = 'InputNumber'; +if (process.env.NODE_ENV !== 'production') { + InputNumber.displayName = 'InputNumber'; +} export default InputNumber; diff --git a/src/index.ts b/src/index.ts index 0e60b192..34c1fc02 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -import type { InputNumberProps, ValueType } from './InputNumber'; +import type { InputNumberProps, ValueType, InputNumberRef } from './InputNumber'; import InputNumber from './InputNumber'; -export type { InputNumberProps, ValueType }; +export type { InputNumberProps, ValueType, InputNumberRef }; export default InputNumber; diff --git a/tests/github.test.tsx b/tests/github.test.tsx index 6739d5b2..1edcb35c 100644 --- a/tests/github.test.tsx +++ b/tests/github.test.tsx @@ -1,8 +1,7 @@ import KeyCode from 'rc-util/lib/KeyCode'; import React from 'react'; -import { act } from 'react-dom/test-utils'; import InputNumber from '../src'; -import { fireEvent, render, screen, waitFor } from './util/wrapper'; +import { act, fireEvent, render, screen, waitFor } from './util/wrapper'; // Github issues describe('InputNumber.Github', () => { diff --git a/tests/input.test.tsx b/tests/input.test.tsx index 3a77f6bb..095aa11d 100644 --- a/tests/input.test.tsx +++ b/tests/input.test.tsx @@ -1,6 +1,6 @@ import KeyCode from 'rc-util/lib/KeyCode'; import React from 'react'; -import InputNumber, { InputNumberProps } from '../src'; +import InputNumber, { InputNumberProps, InputNumberRef } from '../src'; import { fireEvent, render } from './util/wrapper'; describe('InputNumber.Input', () => { @@ -223,4 +223,20 @@ describe('InputNumber.Input', () => { fireEvent.blur(container.querySelector('input')); expect(onChange).not.toHaveBeenCalled(); }); + + describe('nativeElement', () => { + it('basic', () => { + const ref = React.createRef(); + const { container } = render(); + expect(ref.current.nativeElement).toBe(container.querySelector('.rc-input-number')); + }); + + it('wrapper', () => { + const ref = React.createRef(); + const { container } = render(); + expect(ref.current.nativeElement).toBe( + container.querySelector('.rc-input-number-affix-wrapper'), + ); + }); + }); }); diff --git a/tests/longPress.test.tsx b/tests/longPress.test.tsx index df8fe995..ccb83e09 100644 --- a/tests/longPress.test.tsx +++ b/tests/longPress.test.tsx @@ -1,7 +1,5 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { render, fireEvent, waitFor } from './util/wrapper'; import InputNumber from '../src'; +import { act, fireEvent, render, waitFor } from './util/wrapper'; // Jest will mass of advanceTimersByTime if other test case not use fakeTimer. // Let's create a pure file here for test. diff --git a/tests/util/wrapper.ts b/tests/util/wrapper.ts index 0d87212a..dd31921f 100644 --- a/tests/util/wrapper.ts +++ b/tests/util/wrapper.ts @@ -1,13 +1,12 @@ -import type { ReactElement } from 'react'; -import { act } from 'react-dom/test-utils'; import type { RenderOptions } from '@testing-library/react'; -import { render } from '@testing-library/react'; +import { act, render } from '@testing-library/react'; +import type { ReactElement } from 'react'; const globalTimeout = global.setTimeout; export const sleep = async (timeout = 0) => { await act(async () => { - await new Promise(resolve => { + await new Promise((resolve) => { globalTimeout(resolve, timeout); }); }); @@ -16,6 +15,5 @@ export const sleep = async (timeout = 0) => { const customRender = (ui: ReactElement, options?: Omit) => render(ui, { ...options }); -export { customRender as render }; - export * from '@testing-library/react'; +export { customRender as render };