@@ -67,6 +67,56 @@ describe('type()', () => {
6767 expect ( events ) . toMatchSnapshot ( 'input: "abc"' ) ;
6868 } ) ;
6969
70+ it ( 'includes caret selection in the change event nativeEvent' , async ( ) => {
71+ const { events } = await renderTextInputWithToolkit ( ) ;
72+
73+ const user = userEvent . setup ( ) ;
74+ await user . type ( screen . getByTestId ( 'input' ) , 'Hello' ) ;
75+
76+ const changeEvents = events . filter ( ( event ) => event . name === 'change' ) ;
77+ const lastChangeEvent = changeEvents [ changeEvents . length - 1 ] ;
78+
79+ // React Native (>=0.85) reports the caret position via `onChange`'s
80+ // `nativeEvent.selection` on all platforms.
81+ expect ( lastChangeEvent . payload . nativeEvent . selection ) . toEqual ( {
82+ start : 'Hello' . length ,
83+ end : 'Hello' . length ,
84+ } ) ;
85+ } ) ;
86+
87+ it ( 'drives components that read the caret from change event (regression)' , async ( ) => {
88+ const onChange = jest . fn ( ) ;
89+
90+ function MaskedInput ( ) {
91+ const [ value , setValue ] = React . useState ( '' ) ;
92+ return (
93+ < TextInput
94+ testID = "masked-input"
95+ value = { value }
96+ onChange = { ( event ) => {
97+ // RN (>=0.85) reports the caret position via `nativeEvent.selection`
98+ // on all platforms; the bundled RN types may not declare it yet.
99+ const { selection, text } = event . nativeEvent as typeof event . nativeEvent & {
100+ selection : { start : number ; end : number } ;
101+ } ;
102+ onChange ( selection ) ;
103+ // Mimic an input-mask library that relies on the caret position
104+ // reported inside the `change` event to update the value.
105+ setValue ( text . slice ( 0 , selection . end ) ) ;
106+ } }
107+ />
108+ ) ;
109+ }
110+
111+ await render ( < MaskedInput /> ) ;
112+
113+ const user = userEvent . setup ( ) ;
114+ await user . type ( screen . getByTestId ( 'masked-input' ) , 'abc' ) ;
115+
116+ expect ( onChange ) . toHaveBeenLastCalledWith ( { start : 3 , end : 3 } ) ;
117+ expect ( screen . getByTestId ( 'masked-input' ) . props . value ) . toBe ( 'abc' ) ;
118+ } ) ;
119+
70120 it . each ( [ 'modern' , 'legacy' ] ) ( 'works with %s fake timers' , async ( type ) => {
71121 jest . useFakeTimers ( { legacyFakeTimers : type === 'legacy' } ) ;
72122 const { events } = await renderTextInputWithToolkit ( ) ;
0 commit comments