Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed : Disable Submit Button #9526

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from

Conversation

Jeffrin2005
Copy link
Contributor

@Jeffrin2005 Jeffrin2005 commented Dec 21, 2024

Proposed Changes

S_Button.mp4

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate bug / test a new feature.
  • Update product documentation.
  • Ensure that UI text is kept in I18n files.
  • Prep screenshot or demo video for changelog entry, and attach it to issue.
  • Request for Peer Reviews
  • Completion of QA

Summary by CodeRabbit

  • Chores
    • Updated dependencies to ensure compatibility and access to the latest features.
  • New Features
    • Enhanced form state management with a modification tracking mechanism.
    • Updated form submission and cancellation logic for improved user experience.
    • Introduced new form handling components for better state and validation management.
    • Translated button labels for internationalization.

@Jeffrin2005 Jeffrin2005 requested a review from a team as a code owner December 21, 2024 16:55
Copy link
Contributor

coderabbitai bot commented Dec 21, 2024

Walkthrough

The pull request introduces updates to the package.json and Form.tsx files. The package.json file includes new dependencies, specifically @hookform/resolvers and react-hook-form, along with an updated version of zod. In the Form component, a new state variable isEdited is added to track form modifications, and the submission logic is updated to disable the submit button when no changes have been made.

Changes

File Change Summary
package.json Added dependencies: @hookform/resolvers ^3.9.1, react-hook-form ^7.54.2; updated zod from ^3.23.8 to ^3.24.1.
src/components/Form/Form.tsx Added state variable isEdited and updated submission logic to disable the submit button when no changes are made.
src/components/ui/form.tsx Introduced new form handling context and components for improved state management and validation.

Assessment against linked issues

Objective Addressed Explanation
Disable Submit Button When No Changes [#9501]

Possibly related PRs

  • fix: fixed enter key issue #9396: This PR modifies the Form component to add an event handler for the "Enter" key, enhancing user interaction in a similar context to the changes made in the main PR regarding form state management.

Suggested labels

needs testing, reviewed, P1

Suggested reviewers

  • Jacobjeevan
  • rithviknishad

Poem

🐰 In the garden where forms take flight,
A rabbit hops with changes in sight.
With buttons that wait for a tweak or a turn,
Each little modification, a lesson to learn!
So hop along, submit with glee,
For forms now dance, wild and free! 🌼


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c88392 and 9193e17.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Dec 21, 2024

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit 9193e17
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/676a9693bf75700008f44a09
😎 Deploy Preview https://deploy-preview-9526--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/components/Form/Form.tsx (2)

1-1: Consider using deep-import alternative

The current import import isEqual from "lodash/isEqual" is good for tree-shaking, but consider using the more modern deep-import syntax:

-import isEqual from "lodash/isEqual";
+import { isEqual } from "lodash/isEqual";

130-142: Optimize form change detection performance

The current implementation creates a new form object on every change. Consider memoizing the comparison or using a more efficient change detection strategy.

 onChange: ({ name, value }: FieldChangeEvent<T[keyof T]>) => {
-  const newForm = {
-    ...state.form,
-    [name]: value,
-  };
-  setIsFormModified(!isEqual(newForm, formVals.current));
+  const hasChanged = !isEqual(value, formVals.current[name]);
+  if (hasChanged !== isFormModified) {
+    setIsFormModified(hasChanged);
+  }
   dispatch({
     type: "set_field",
     name,
     value,
     error: validate?.(value),
   });
 },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a14155 and 3438758.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json (2 hunks)
  • src/components/Form/Form.tsx (5 hunks)
🔇 Additional comments (1)
src/components/Form/Form.tsx (1)

162-162: Verify submit button behavior in edge cases

The submit button disabled state implementation looks correct. However, please verify the behavior in the following scenarios:

  1. Form initialized with non-default values
  2. Form reset after submission
  3. Form modified and then restored to initial values

package.json Outdated
@@ -70,6 +70,7 @@
"@sentry/browser": "^8.45.1",
"@tanstack/react-query": "^5.62.3",
"@tanstack/react-query-devtools": "^5.62.7",
"@types/lodash": "^4.17.13",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect @types/lodash version

The version ^4.17.13 for @types/lodash appears to be incorrect. The latest version series is 4.14.x, and version 4.17.x doesn't exist in the npm registry.

Update the version to the latest stable:

-    "@types/lodash": "^4.17.13",
+    "@types/lodash": "^4.14.202",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@types/lodash": "^4.17.13",
"@types/lodash": "^4.14.202",

Comment on lines 86 to 88
dispatch({ type: "set_form", form: formVals.current });
setIsFormModified(false);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for form submission failures

The form modified state is reset only on successful submission. Consider handling submission failures to maintain consistent state.

 } else if (props.resetFormValsOnSubmit) {
   dispatch({ type: "set_form", form: formVals.current });
   setIsFormModified(false);
+} else {
+  // Keep isFormModified true if submission fails
+  setIsFormModified(true);
 }

Also applies to: 93-95

@Jeffrin2005 Jeffrin2005 reopened this Dec 21, 2024
@github-actions github-actions bot added the Deploy-Failed Deplyment is not showing preview label Dec 21, 2024
Copy link
Member

@rithviknishad rithviknishad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not use lodash. we just got rid of it from care last month.

also do use shadcn's form component instead. that'll tell if you the form is dirty or not.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/components/Form/Form.tsx (1)

84-86: ⚠️ Potential issue

Add error handling for form submission failures

The form modified state is reset only on successful submission and cancellation. Consider handling submission failures to maintain consistent state.

Also applies to: 91-93

🧹 Nitpick comments (2)
src/components/Form/Form.tsx (2)

128-140: Consider memoizing form state comparison for better performance.

While the implementation is functionally correct, creating a new form object and performing deep comparison on every change might impact performance for large forms.

Consider this optimization:

 onChange: ({ name, value }: FieldChangeEvent<T[keyof T]>) => {
-  const newForm = {
-    ...state.form,
-    [name]: value,
-  };
-  setIsFormModified(!isEqual(newForm, formVals.current));
+  const isModified = !isEqual(value, formVals.current[name]);
+  if (isModified !== isFormModified) {
+    setIsFormModified(isModified);
+  }
   dispatch({
     type: "set_field",
     name,
     value,
     error: validate?.(value),
   });
 },

160-160: Consider adding a tooltip to explain why the submit button is disabled.

While the disabled state logic is correct, users might not understand why the button is disabled. Consider adding a tooltip to improve user experience.

 <Submit
   data-testid="submit-button"
   type="submit"
   disabled={disabled || !isFormModified}
+  title={!isFormModified ? "Make changes to enable submission" : disabled ? "Form is currently disabled" : ""}
   label={props.submitLabel ?? "Submit"}
 />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d3405f and 6a8092f.

📒 Files selected for processing (1)
  • src/components/Form/Form.tsx (5 hunks)
🔇 Additional comments (1)
src/components/Form/Form.tsx (1)

1-1: LGTM! Good choice of dependencies and state management.

The use of lodash/isEqual for deep comparison and the state initialization look correct. Using the specific lodash import is good for bundle size optimization.

Also applies to: 52-53

Copy link
Member

@bodhish bodhish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed the review from Rithvik.

@Jeffrin2005
Copy link
Contributor Author

@rithviknishad Implemented without using lodash

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a8092f and 96903ce.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json (3 hunks)
  • src/components/Form/Form.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🔇 Additional comments (3)
src/components/Form/Form.tsx (3)

2-2: LGTM: Proper internationalization implementation

The translation integration is well-implemented using react-i18next, following i18n best practices.

Also applies to: 47-47


Line range hint 93-102: Add error handling for form submission failures

The form modified state is reset only on successful submission. Consider handling submission failures to maintain consistent state.


154-169: Verify test coverage for button disabled state

The button disabled state implementation looks good, but let's ensure adequate test coverage exists.

const initial = { form: props.defaults, errors: {} };
const [isLoading, setIsLoading] = useState(!!asyncGetDefaults);
const [state, dispatch] = useAutoSaveReducer<T>(formReducer, initial);
const formVals = useRef(props.defaults);
const [isModified, setIsModified] = useState(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider optimizing form state comparison

The current implementation has potential performance and edge case concerns:

  1. Using JSON.stringify for comparison could be performance-intensive for large forms
  2. Edge case: The form might still show as modified even when values are reset to initial state

Consider this optimization:

- const hasChanges =
-   JSON.stringify(state.form) !== JSON.stringify(formVals.current);
- setIsModified(hasChanges);
+ import { isEqual } from 'lodash';
+ const hasChanges = !isEqual(state.form, formVals.current);
+ setIsModified(hasChanges);

Also applies to: 63-67


useEffect(() => {
const hasChanges =
JSON.stringify(state.form) !== JSON.stringify(formVals.current);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not do this, comparing like this isn't always reliable.

Copy link
Member

@rithviknishad rithviknishad Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like already said in previous review, use shadcn's form

https://ui.shadcn.com/docs/components/form

@@ -141,17 +151,22 @@ const Form = <T extends FormDetails>({
</div>
<div className="flex flex-col-reverse justify-end gap-3 sm:flex-row">
{!hideCancelButton && (
<Cancel
<ButtonV2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use shadcn buttons instead (src/components/ui).

Copy link
Contributor

@Jacobjeevan Jacobjeevan Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be in the commit.

How are you committing your merge changes? Cli or GUI 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLI

<Submit
data-testid="submit-button"
<ButtonV2
variant="primary"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure the data-testid is not being used in tests first, before removing them.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (13)
src/components/ui/form.tsx (6)

1-2: Provide a more descriptive file header or component-level comment.

Since this new file introduces a shared form component architecture, a concise top-level comment describing its purpose and usage would be helpful for future maintainers.


28-30: Avoid using empty object type assertions.

React.createContext<FormFieldContextValue>({} as FormFieldContextValue) can lead to runtime errors if used outside its intended scope. Consider initializing it with a safer default or a null check.


32-43: Maintain consistent naming for props in the controller.

Currently, you spread all ControllerProps into <Controller {...props} />. Consider destructuring essential props (like name, control) to make it explicit, which can improve readability and clarity.


130-146: Consistent text size in descriptive text.

text-[0.8rem] for FormDescription may differ from typical app design guidelines. Confirm it matches the design system or consider making it themable.


147-169: Add i18n support for error messages.

FormMessage displays error.message or custom text. If your app uses i18n, ensure these strings are translated or externalized.


171-180: Review default exports vs. named exports for clarity.

Exporting many named components is handy, but verify that import paths and usage across your codebase remain consistent. A single default export of an object containing all subcomponents might simplify imports for consumers.

src/components/Form/Form.tsx (7)

1-4: Document the file's purpose.

Adding a top-level comment explaining this file’s relationship with src/components/ui/form.tsx (e.g., form container vs. form fields) will help developers navigate the codebase.


50-54: Avoid naming collisions with "state" and "dispatch".

Since you already use useAutoSaveReducer, naming your React Hook Form’s reducer or local state differently can reduce confusion between the two sets of form-related data.


71-74: Ensure proper cleanup or error handling for asyncGetDefaults.

If a user navigates away or unmounts the component before asyncGetDefaults() completes, you might encounter a memory leak or an error. Consider canceling the request or checking isMounted before calling dispatch.


103-104: Improve error logging.

Consider capturing error details or custom fields (e.g., unique request ID) to help with debugging. Right now, only the default browser console logs are used.


113-113: Publicly track form cancellation reasons (optional).

When handleCancel is invoked, you simply reset the state. If analytics or other tracking is beneficial, consider logging why or how the user canceled.


120-127: Rename "form" prop to "onSubmit" for clarity.

Your wrapper <form onSubmit={methods.handleSubmit(...)}> is good, but ensure that fully describing the usage avoids confusion between the FormContainer from ui/form.tsx and standard HTML <form> semantics.


167-189: Confirm or unify button styling with design system.

The primary and secondary variants might differ from other styling patterns across the app. Ensure your design team or style guidelines are consistent, especially for critical actions like form submission or cancellation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96903ce and 2c88392.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json (3 hunks)
  • src/components/Form/Form.tsx (3 hunks)
  • src/components/ui/form.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🔇 Additional comments (7)
src/components/ui/form.tsx (5)

19-20: Confirm usage of FormProvider as a component alias.

Using const Form = FormProvider; is valid, but might be confusing. If you plan to rename it to Form, ensure that developers understand this is merely a re-export of FormProvider from React Hook Form, so there's no confusion with the new custom form components introduced here.


45-66: Validate existence of both contexts.

You check if (!fieldContext) {...}, but you also rely on itemContext. Consider a similar guard for itemContext to avoid potential runtime errors if used outside a <FormItem> scope.


76-88: Test interactions with nested FormItem contexts.

FormItem uses an auto-generated ID and provides it via context. It might be beneficial to ensure that multiple nested FormItem components behave as expected (no collision in IDs, etc.).


90-106: Gracefully handle missing error states in FormLabel.

When error is undefined, there is no issue, but if the label is used outside the intended context, a more explicit fallback or guard could preempt confusion.


107-129: Double-check aria attributes.

aria-describedby concatenates form description and message IDs conditionally. Ensure these elements are rendered consistently, preventing potential runtime issues or confusion for assistive technologies.

src/components/Form/Form.tsx (2)

57-64: Validate consistency with default form state.

useForm uses defaultValues: props.defaults, but you also have formVals.current referencing props.defaults. Confirm these remain in sync if the prop changes after component mount.


76-102: Maintain form modified state if submission fails.

Currently, successful submissions reset isEdited to false, but partially validated submissions or network failures keep it true. This is generally correct, but confirm whether you need additional logic to indicate partial success or other states.

Comment on lines +147 to +166
<Provider
value={(name: keyof T, validate?: FieldValidator<T[keyof T]>) => {
return {
name,
value,
error: validate?.(value),
}),
value: state.form[name],
error: state.errors[name],
disabled,
};
}}
>
<div className="my-6">
<Consumer>{props.children}</Consumer>
</div>
<div className="flex flex-col-reverse justify-end gap-3 sm:flex-row">
{!hideCancelButton && (
<Cancel
onClick={handleCancel}
label={props.cancelLabel ?? "Cancel"}
/>
)}
<Submit
data-testid="submit-button"
type="submit"
disabled={disabled}
label={props.submitLabel ?? "Submit"}
/>
</div>
</Provider>
</DraftSection>
</form>
id: name,
onChange: ({ name, value }: FieldChangeEvent<T[keyof T]>) => {
dispatch({
type: "set_field",
name,
value,
error: validate?.(value),
});
setIsEdited(true);
},
value: state.form[name],
error: state.errors[name],
disabled,
};
}}
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Check performance impact of dispatch calls.

onChange triggers dispatch({ type: "set_field", ... }) on every keystroke. While likely fine for small forms, for large forms this may have potential performance overhead.

@Jeffrin2005
Copy link
Contributor Author

@rithviknishad Used Shadcn Form and added form.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changes required Deploy-Failed Deplyment is not showing preview
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Disable Submit Button on Edit Profile Page When No Changes Are Made
4 participants