From f75bdba43bd68a2eca7226bf594e528bb805fa63 Mon Sep 17 00:00:00 2001 From: Michel Paiva Date: Mon, 26 Aug 2024 15:39:57 +0200 Subject: [PATCH] Fix input validation and dirty state after re-enabling disabled fields --- packages/ra-core/src/form/useInput.spec.tsx | 75 ++++++++++++++++++++- packages/ra-core/src/form/useInput.ts | 4 -- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/packages/ra-core/src/form/useInput.spec.tsx b/packages/ra-core/src/form/useInput.spec.tsx index 8fef8d4a666..318955a4653 100644 --- a/packages/ra-core/src/form/useInput.spec.tsx +++ b/packages/ra-core/src/form/useInput.spec.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { FunctionComponent, ReactElement } from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { useFormContext, useWatch } from 'react-hook-form'; +import { useForm, useFormContext, useWatch } from 'react-hook-form'; import { CoreAdminContext, SourceContextProvider } from '../core'; import { testDataProvider } from '../dataProvider'; import { Form } from './Form'; @@ -566,5 +566,78 @@ describe('useInput', () => { ); }); }); + + it('should validate and be dirty for inputs that were disabled and re-enabled', async () => { + const InputWithToggle = () => { + const form = useForm({ mode: 'all' }); + const [disabled, setDisabled] = React.useState(false); + const { field, fieldState } = useInput({ + source: 'title', + resource: 'posts', + validate: required(), + disabled: disabled, + control: form.control, + }); + + return ( +
+ + +

Dirty {fieldState.isDirty ? 'true' : 'false'}

+

Disabled {field.disabled ? 'true' : 'false'}

+

Invalid {fieldState.invalid ? 'true' : 'false'}

+
+ ); + }; + + render( + +
+ + +
+ ); + + // start enabled + expect(screen.getByText('Dirty false')).not.toBeNull(); + expect(screen.getByText('Disabled false')).not.toBeNull(); + + // disable + fireEvent.click(screen.getByText('Toggle')); + expect(screen.getByText('Dirty false')).not.toBeNull(); + expect(screen.getByText('Disabled true')).not.toBeNull(); + + // enable + fireEvent.click(screen.getByText('Toggle')); + expect(screen.getByText('Dirty false')).not.toBeNull(); + expect(screen.getByText('Disabled false')).not.toBeNull(); + + // type + fireEvent.change(screen.getByTestId('title-input'), { + target: { value: 'A title' }, + }); + await waitFor(() => { + expect(screen.getByText('Dirty true')).not.toBeNull(); + expect(screen.getByText('Invalid false')).not.toBeNull(); + }); + + // clear + fireEvent.change(screen.getByTestId('title-input'), { + target: { value: '' }, + }); + await waitFor(() => { + expect(screen.getByText('Dirty true')).not.toBeNull(); + expect(screen.getByText('Invalid true')).not.toBeNull(); + }); + }); }); }); diff --git a/packages/ra-core/src/form/useInput.ts b/packages/ra-core/src/form/useInput.ts index 3f1de19fdb3..30cef57a6c0 100644 --- a/packages/ra-core/src/form/useInput.ts +++ b/packages/ra-core/src/form/useInput.ts @@ -104,10 +104,6 @@ export const useInput = ( }, }, ...options, - // Workaround for https://github.com/react-hook-form/react-hook-form/issues/10907 - // FIXME - remove when fixed - // @ts-ignore - only exists since react-hook-form 7.46.0 - disabled: options.disabled || undefined, }); // Because our forms may receive an asynchronously loaded record for instance,