Skip to content

Commit 3101f37

Browse files
committed
feat(Form): add isDirty flag
1 parent 97ecb40 commit 3101f37

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

.changeset/famous-cars-do.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@cube-dev/ui-kit': patch
3+
---
4+
5+
Add `isDirty` flag to Form instances.

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,4 +369,92 @@ describe('<Form />', () => {
369369

370370
expect(() => getByText('Field is required')).toThrow();
371371
});
372+
373+
it('should update isTouched when an input is interacted with', async () => {
374+
const onSubmit = jest.fn(() => Promise.resolve());
375+
const { getByRole, formInstance } = renderWithForm(
376+
<>
377+
<Form.Item name="test" label="Test">
378+
<TextInput />
379+
</Form.Item>
380+
<SubmitButton>Submit</SubmitButton>
381+
</>,
382+
);
383+
384+
// Initially, isTouched should be false
385+
expect(formInstance.isTouched).toBeFalsy();
386+
387+
// Simulate user interaction
388+
const input = getByRole('textbox');
389+
await act(async () => {
390+
await userEvents.type(input, 'hello');
391+
});
392+
393+
// After typing, isTouched should be true
394+
expect(formInstance.isTouched).toBeTruthy();
395+
});
396+
397+
it('should update isDirty when input value differs from the initial value', async () => {
398+
const onSubmit = jest.fn(() => Promise.resolve());
399+
const defaultValues = { test: 'initial' };
400+
const { getByRole, formInstance } = renderWithForm(
401+
<>
402+
<TextInput name="test" label="Test" />
403+
<SubmitButton>Submit</SubmitButton>
404+
</>,
405+
{ formProps: { onSubmit, defaultValues } },
406+
);
407+
408+
// Initially, isDirty should be false because the value is same as initial
409+
expect(formInstance.isDirty).toBeFalsy();
410+
411+
// Change the input value
412+
const input = getByRole('textbox');
413+
await act(async () => {
414+
await userEvents.clear(input);
415+
await userEvents.type(input, 'changed');
416+
});
417+
418+
// After change, isDirty should be true
419+
expect(formInstance.isDirty).toBeTruthy();
420+
});
421+
422+
it('should maintain isTouched true but set isDirty false when input value reverts to initial', async () => {
423+
const onSubmit = jest.fn(() => Promise.resolve());
424+
const initialValue = { test: 'initial' };
425+
const { getByRole, formInstance } = renderWithForm(
426+
<>
427+
<Form.Item name="test" label="Test">
428+
<TextInput />
429+
</Form.Item>
430+
<SubmitButton>Submit</SubmitButton>
431+
</>,
432+
{ formProps: { onSubmit, defaultValues: initialValue } },
433+
);
434+
435+
// Initially, both isTouched and isDirty should be false
436+
expect(formInstance.isTouched).toBeFalsy();
437+
expect(formInstance.isDirty).toBeFalsy();
438+
439+
// Change the input to a different value
440+
const input = getByRole('textbox');
441+
await act(async () => {
442+
await userEvents.clear(input);
443+
await userEvents.type(input, 'changed');
444+
});
445+
446+
// After change, both isTouched and isDirty should be true
447+
expect(formInstance.isTouched).toBeTruthy();
448+
expect(formInstance.isDirty).toBeTruthy();
449+
450+
// Revert the input back to its initial value
451+
await act(async () => {
452+
await userEvents.clear(input);
453+
await userEvents.type(input, 'initial');
454+
});
455+
456+
// After reverting, isTouched remains true, but isDirty should be false
457+
expect(formInstance.isTouched).toBeTruthy();
458+
expect(formInstance.isDirty).toBeFalsy();
459+
});
372460
});

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,15 @@ export class CubeFormInstance<
341341
return Object.values(this.fields).some((field) => field?.touched);
342342
}
343343

344+
get isDirty(): boolean {
345+
return Object.values(this.fields).some((field) => {
346+
return field && field.name
347+
? JSON.stringify(field?.value) !==
348+
JSON.stringify(this.defaultValues[field?.name])
349+
: false;
350+
});
351+
}
352+
344353
/**
345354
* True if all fields are verified and valid
346355
* IMPORTANT: This is not the same as `!isInvalid`, because it also checks if all fields are verified.

0 commit comments

Comments
 (0)