@@ -93,4 +93,173 @@ describe('useField - Dynamic Name (Issue #869)', () => {
9393 expect ( getByTestId ( 'name' ) ) . toHaveTextContent ( 'b' )
9494 expect ( getByTestId ( 'value' ) ) . toHaveTextContent ( 'value-b' )
9595 } )
96+
97+ it ( 'should keep name and checked in sync when checkbox field name changes' , ( ) => {
98+ const renderSpy = jest . fn ( )
99+
100+ const TestComponent = ( { fieldName } ) => {
101+ return (
102+ < Form
103+ onSubmit = { ( ) => { } }
104+ initialValues = { { a : true , b : false } }
105+ >
106+ { ( ) => (
107+ < Field name = { fieldName } type = "checkbox" >
108+ { ( { input } ) => {
109+ // Log every render to track name/checked sync
110+ renderSpy ( input . name , input . checked )
111+ return < input { ...input } data-testid = "field" />
112+ } }
113+ </ Field >
114+ ) }
115+ </ Form >
116+ )
117+ }
118+
119+ const { rerender } = render ( < TestComponent fieldName = "a" /> )
120+
121+ // Initial render - field 'a' checked
122+ expect ( renderSpy ) . toHaveBeenCalledWith ( 'a' , true )
123+
124+ renderSpy . mockClear ( )
125+
126+ // Change field name from 'a' to 'b'
127+ act ( ( ) => {
128+ rerender ( < TestComponent fieldName = "b" /> )
129+ } )
130+
131+ // Verify all renders after name change have name='b' and checked=false
132+ const calls = renderSpy . mock . calls
133+
134+ // Ensure Field actually rendered
135+ expect ( calls . length ) . toBeGreaterThan ( 0 )
136+
137+ // After rerender with fieldName="b", ALL calls should be for field 'b'
138+ calls . forEach ( call => {
139+ const [ name , checked ] = call
140+ expect ( name ) . toBe ( 'b' )
141+ expect ( checked ) . toBe ( false )
142+ } )
143+ } )
144+
145+ it ( 'should have correct checked immediately after checkbox name change' , ( ) => {
146+ const TestComponent = ( { fieldName } ) => {
147+ return (
148+ < Form
149+ onSubmit = { ( ) => { } }
150+ initialValues = { { a : true , b : false } }
151+ >
152+ { ( ) => (
153+ < Field name = { fieldName } type = "checkbox" >
154+ { ( { input } ) => (
155+ < div >
156+ < span data-testid = "name" > { input . name } </ span >
157+ < span data-testid = "checked" > { String ( input . checked ) } </ span >
158+ </ div >
159+ ) }
160+ </ Field >
161+ ) }
162+ </ Form >
163+ )
164+ }
165+
166+ const { rerender, getByTestId } = render ( < TestComponent fieldName = "a" /> )
167+
168+ expect ( getByTestId ( 'name' ) ) . toHaveTextContent ( 'a' )
169+ expect ( getByTestId ( 'checked' ) ) . toHaveTextContent ( 'true' )
170+
171+ // Change field name
172+ act ( ( ) => {
173+ rerender ( < TestComponent fieldName = "b" /> )
174+ } )
175+
176+ // Immediately after rerender, name and checked should be in sync
177+ expect ( getByTestId ( 'name' ) ) . toHaveTextContent ( 'b' )
178+ expect ( getByTestId ( 'checked' ) ) . toHaveTextContent ( 'false' )
179+ } )
180+
181+ it ( 'should keep name and checked in sync when radio field name changes' , ( ) => {
182+ const renderSpy = jest . fn ( )
183+
184+ const TestComponent = ( { fieldName } ) => {
185+ return (
186+ < Form
187+ onSubmit = { ( ) => { } }
188+ initialValues = { { a : 'option1' , b : 'option2' } }
189+ >
190+ { ( ) => (
191+ < Field name = { fieldName } type = "radio" value = "option2" >
192+ { ( { input } ) => {
193+ // Log every render to track name/checked sync
194+ renderSpy ( input . name , input . checked )
195+ return < input { ...input } data-testid = "field" />
196+ } }
197+ </ Field >
198+ ) }
199+ </ Form >
200+ )
201+ }
202+
203+ const { rerender } = render ( < TestComponent fieldName = "a" /> )
204+
205+ // Initial render - field 'a' has value 'option1', not checked for 'option2'
206+ expect ( renderSpy ) . toHaveBeenCalledWith ( 'a' , false )
207+
208+ renderSpy . mockClear ( )
209+
210+ // Change field name from 'a' to 'b'
211+ act ( ( ) => {
212+ rerender ( < TestComponent fieldName = "b" /> )
213+ } )
214+
215+ // Verify all renders after name change have name='b' and checked=true
216+ const calls = renderSpy . mock . calls
217+
218+ // Ensure Field actually rendered
219+ expect ( calls . length ) . toBeGreaterThan ( 0 )
220+
221+ // After rerender with fieldName="b", ALL calls should be for field 'b'
222+ // Field 'b' has value 'option2', so radio with value="option2" should be checked
223+ calls . forEach ( call => {
224+ const [ name , checked ] = call
225+ expect ( name ) . toBe ( 'b' )
226+ expect ( checked ) . toBe ( true )
227+ } )
228+ } )
229+
230+ it ( 'should have correct checked immediately after radio name change' , ( ) => {
231+ const TestComponent = ( { fieldName } ) => {
232+ return (
233+ < Form
234+ onSubmit = { ( ) => { } }
235+ initialValues = { { a : 'option1' , b : 'option2' } }
236+ >
237+ { ( ) => (
238+ < Field name = { fieldName } type = "radio" value = "option2" >
239+ { ( { input } ) => (
240+ < div >
241+ < span data-testid = "name" > { input . name } </ span >
242+ < span data-testid = "checked" > { String ( input . checked ) } </ span >
243+ </ div >
244+ ) }
245+ </ Field >
246+ ) }
247+ </ Form >
248+ )
249+ }
250+
251+ const { rerender, getByTestId } = render ( < TestComponent fieldName = "a" /> )
252+
253+ expect ( getByTestId ( 'name' ) ) . toHaveTextContent ( 'a' )
254+ expect ( getByTestId ( 'checked' ) ) . toHaveTextContent ( 'false' )
255+
256+ // Change field name
257+ act ( ( ) => {
258+ rerender ( < TestComponent fieldName = "b" /> )
259+ } )
260+
261+ // Immediately after rerender, name and checked should be in sync
262+ expect ( getByTestId ( 'name' ) ) . toHaveTextContent ( 'b' )
263+ expect ( getByTestId ( 'checked' ) ) . toHaveTextContent ( 'true' )
264+ } )
96265} )
0 commit comments