Skip to content

Commit 7a89b11

Browse files
committed
fix(Form): form buttons handle isDisable properly * 3
1 parent e96fdae commit 7a89b11

File tree

5 files changed

+221
-11
lines changed

5 files changed

+221
-11
lines changed

src/components/form/Form/ResetButton/ResetButton.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ function ResetButton(
2626
const { form, isDisabled, ...otherProps } = props;
2727

2828
const onPress = useEvent(() => {
29-
form?.resetFields();
29+
// Use setTimeout to avoid conflict with onBlur handlers
30+
setTimeout(() => {
31+
form?.resetFields();
32+
});
3033
});
3134

3235
const isSomethingDisabled = isDisabled || isContextDisabled;
@@ -40,10 +43,9 @@ function ResetButton(
4043
{...mergeProps(
4144
{
4245
onPress,
43-
isDisabled:
44-
isSomethingDisabled != null
45-
? isSomethingDisabled
46-
: form?.isSubmitting || !form?.isTouched,
46+
isDisabled: isSomethingDisabled
47+
? true
48+
: form?.isSubmitting || !form?.isTouched,
4749
},
4850
otherProps,
4951
)}

src/components/form/Form/SubmitButton/SubmitButton.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ function SubmitButton(
3333
type="primary"
3434
htmlType="submit"
3535
isLoading={form?.isSubmitting}
36-
isDisabled={
37-
isSomethingDisabled != null ? isSomethingDisabled : form?.isInvalid
38-
}
36+
isDisabled={isSomethingDisabled ? true : form?.isInvalid}
3937
{...otherProps}
4038
/>
4139
);

src/components/form/Form/submit.test.tsx

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import userEvents from '@testing-library/user-event';
22
import { act, waitFor } from '@testing-library/react';
33

44
import { renderWithForm } from '../../../test/index';
5-
import { SubmitButton } from '../../form';
5+
import { ResetButton, SubmitButton } from '../../form';
66
import { TextInput } from '../../fields';
77

88
import { Form } from './index';
@@ -90,4 +90,164 @@ describe('<Form />', () => {
9090
expect(getByText('Internal error')).toBeInTheDocument();
9191
});
9292
});
93+
94+
it('should handle reset button behavior correctly', async () => {
95+
const onSubmit = jest.fn((data) => {});
96+
97+
const { getByRole, getByText } = renderWithForm(
98+
<>
99+
<Form.Item
100+
name="test"
101+
label="Test"
102+
rules={[
103+
{
104+
async validator(rule, value) {
105+
return value ? Promise.resolve() : Promise.reject('Required');
106+
},
107+
},
108+
]}
109+
>
110+
<TextInput />
111+
</Form.Item>
112+
113+
<SubmitButton>Submit</SubmitButton>
114+
<ResetButton>Reset</ResetButton>
115+
<Form.SubmitError />
116+
</>,
117+
{ formProps: { onSubmit } },
118+
);
119+
120+
const input = getByRole('textbox');
121+
const submitButton = getByRole('button', { name: 'Submit' });
122+
const resetButton = getByRole('button', { name: 'Reset' });
123+
124+
// Check initial button states
125+
expect(submitButton).toBeEnabled();
126+
expect(resetButton).toBeDisabled();
127+
128+
// Click submit button and verify state
129+
await userEvents.click(submitButton);
130+
131+
await waitFor(() => {
132+
expect(getByText('Required')).toBeInTheDocument();
133+
});
134+
135+
expect(submitButton).toBeDisabled();
136+
expect(resetButton).toBeDisabled();
137+
138+
// Type into input and verify reset button becomes active
139+
await act(async () => {
140+
await userEvents.type(input, 'test');
141+
});
142+
143+
expect(resetButton).toBeEnabled();
144+
expect(submitButton).toBeEnabled();
145+
146+
// Click reset button and verify states
147+
await userEvents.click(resetButton);
148+
149+
await waitFor(() => {
150+
expect(resetButton).toBeDisabled();
151+
});
152+
153+
expect(input).toHaveValue('');
154+
expect(submitButton).toBeEnabled();
155+
});
156+
157+
it('should respect the isDisabled:true property for ResetButton and SubmitButton', async () => {
158+
const onSubmit = jest.fn(() => Promise.resolve());
159+
const onSubmitFailed = jest.fn(() => {});
160+
161+
const { getByRole } = renderWithForm(
162+
<>
163+
<Form.Item
164+
name="test"
165+
label="Test"
166+
rules={[
167+
{
168+
validator(rule, value) {
169+
return value ? Promise.resolve() : Promise.reject('Required');
170+
},
171+
},
172+
]}
173+
>
174+
<TextInput />
175+
</Form.Item>
176+
177+
{/* Explicitly set isDisabled */}
178+
<SubmitButton isDisabled={true}>Submit</SubmitButton>
179+
<ResetButton isDisabled={true}>Reset</ResetButton>
180+
</>,
181+
{ formProps: { onSubmit, onSubmitFailed } },
182+
);
183+
184+
const input = getByRole('textbox');
185+
const submitButton = getByRole('button', { name: 'Submit' });
186+
const resetButton = getByRole('button', { name: 'Reset' });
187+
188+
// Check that both buttons are disabled because isDisabled is true
189+
expect(submitButton).toBeDisabled();
190+
expect(resetButton).toBeDisabled();
191+
192+
// Try typing in input
193+
await act(async () => {
194+
await userEvents.type(input, 'test');
195+
});
196+
197+
// Buttons should still be disabled because of isDisabled
198+
expect(submitButton).toBeDisabled();
199+
expect(resetButton).toBeDisabled();
200+
});
201+
202+
it('Respect isDisabled:false property for ResetButton and SubmitButton', async () => {
203+
const onSubmit = jest.fn(() => Promise.resolve());
204+
const onSubmitFailed = jest.fn(() => {});
205+
206+
// Render with isDisabled = false for buttons
207+
const { getByRole: getByRoleNew } = renderWithForm(
208+
<>
209+
<Form.Item
210+
name="test"
211+
label="Test"
212+
rules={[
213+
{
214+
validator(rule, value) {
215+
return value ? Promise.resolve() : Promise.reject('Required');
216+
},
217+
},
218+
]}
219+
>
220+
<TextInput />
221+
</Form.Item>
222+
223+
<SubmitButton isDisabled={false}>Submit</SubmitButton>
224+
<ResetButton isDisabled={false}>Reset</ResetButton>
225+
</>,
226+
{ formProps: { onSubmit, onSubmitFailed } },
227+
);
228+
229+
const submitButton = getByRoleNew('button', { name: 'Submit' });
230+
const resetButton = getByRoleNew('button', { name: 'Reset' });
231+
232+
// Verify default behavior without isDisabled=true
233+
expect(submitButton).toBeEnabled();
234+
expect(resetButton).toBeDisabled();
235+
236+
// Type in input and verify reset button becomes enabled
237+
const inputNew = getByRoleNew('textbox');
238+
await act(async () => {
239+
await userEvents.type(inputNew, 'test');
240+
});
241+
242+
expect(resetButton).toBeEnabled();
243+
244+
// Click reset button and verify behavior
245+
await userEvents.click(resetButton);
246+
247+
await waitFor(() => {
248+
expect(inputNew).toHaveValue('');
249+
expect(submitButton).toBeEnabled();
250+
expect(resetButton).toBeDisabled();
251+
});
252+
});
93253
});

src/components/form/Form/use-form.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,13 @@ export class CubeFormInstance<
229229
field.touched = false;
230230
field.status = undefined;
231231
field.inputValue = defaultValue;
232+
233+
// reject all ongoing validations
234+
if (!field.validationId) {
235+
field.validationId = 1;
236+
} else {
237+
field.validationId++;
238+
}
232239
});
233240

234241
if (!skipRender) {
@@ -253,6 +260,7 @@ export class CubeFormInstance<
253260
}
254261

255262
field.validating = true;
263+
field.status = undefined;
256264

257265
if (!field.validationId) {
258266
field.validationId = 1;

src/stories/Form.stories.jsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { Form, Field, SubmitButton, Input } from '../index';
1+
import {
2+
Form,
3+
Field,
4+
SubmitButton,
5+
Input,
6+
ResetButton,
7+
Space,
8+
TextInput,
9+
} from '../index';
210

311
import { DialogFormApp } from './components/DialogFormApp';
412
import { DISABLE_BASE_ARGS } from './FormFieldArgs';
@@ -56,13 +64,47 @@ export const LoginForm = {
5664
>
5765
<Input.Password type="password" />
5866
</Field>
59-
<SubmitButton>Submit</SubmitButton>
67+
<Space>
68+
<SubmitButton>Submit</SubmitButton>
69+
<ResetButton>Reset</ResetButton>
70+
</Space>
6071
</Form>
6172
),
6273

6374
name: 'LoginForm',
6475
};
6576

77+
export const SimpleValidation = {
78+
render: (args) => (
79+
<Form
80+
onSubmit={(data) => {
81+
console.log('! Submit', data);
82+
}}
83+
{...args}
84+
>
85+
<Form.Item
86+
name="test"
87+
label="Test"
88+
rules={[
89+
{
90+
validator(rule, value) {
91+
return value ? Promise.resolve() : Promise.reject('Required');
92+
},
93+
},
94+
]}
95+
>
96+
<TextInput />
97+
</Form.Item>
98+
99+
<Space>
100+
<SubmitButton>Submit</SubmitButton>
101+
<ResetButton>Reset</ResetButton>
102+
</Space>
103+
<Form.SubmitError />
104+
</Form>
105+
),
106+
};
107+
66108
export const HorizontalLoginForm = {
67109
// 1s of submitting
68110
render: (args) => (

0 commit comments

Comments
 (0)