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

refactor(components): Expose Reusable hooks and components used in FormField [JOB-110812] #2184

Merged
merged 17 commits into from
Dec 4, 2024

Conversation

MichaelParadis
Copy link
Contributor

@MichaelParadis MichaelParadis commented Dec 2, 2024

Copied from: #2105

Motivations

We have an upcoming need to decouple from react-hook-form. We still want to leverage the library, but we no longer want our form components to be directly coupled, instead we want them to support being used in that environment but not use the library directly.

This PR is the first step towards that future.

In this PR, we've broken down all the requisite parts of FormField and FormFieldWrapper into either smaller bite-sized components, or hooks.

This allows FormFieldWrapper to now just be collating a variety of smaller pieces into a cohesive whole. The purpose of the first part of this refactor was to ensure that nothing changed for the main control flow, which can be verified by our passing test suite (with no changes to tests in this PR. I want to verify passing tests still in our downstream applications).

With FormFieldWrapper/FormField broken down into chunks, we could now leverage those chunks directly in a new version of InputText that bypasses the Form and react-hook-form specific FormField components by leveraging FormFieldWrapper directly, and providing the input html element directly.

You can verify this new component is working through the next step in this refactor which is using the potential pattern for an Atlantis Shim through the use of a version prop on the input to control if the original implementation of the Input or a new version

Testing

Verify styling of the base inputs matches master (you can revert 6643db603a804552d6c46fbd4ed16e3f83509fbe for a comparison story on InputText

Changes

  • Refactored FormFieldWrapper and FormField to be broken down into smaller chunks, but no actual code changes.
  • Added a potential pattern for an Atlantis Shim to allow existing users of a component to get the legacy version of the component, and consumers wishing to use the new pattern can apply an additional prop version:2 to the prop list.

No visual Changes

After (this branch):

No value

image

With value (clearable)

image

With value (not clearable)

image

Before (Master)

No value

image

With value (clearable)

image

With value (not clearable)

image


In Atlantis we use Github's built in pull request reviews.

Random photo of Atlantis

Copy link

cloudflare-workers-and-pages bot commented Dec 2, 2024

Deploying atlantis with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6d4ace1
Status: ✅  Deploy successful!
Preview URL: https://62de306c.atlantis.pages.dev
Branch Preview URL: https://mike-pull-scotts-rfh-formfie.atlantis.pages.dev

View logs

@MichaelParadis MichaelParadis force-pushed the MIKE/pull-scotts-rfh-formfield-changes branch from 174fb2e to dfa93ca Compare December 2, 2024 17:49
@MichaelParadis MichaelParadis force-pushed the MIKE/pull-scotts-rfh-formfield-changes branch from dfa93ca to 7c4b2fa Compare December 2, 2024 17:52
export function InputValidation({ message }: InputValidationProps) {
export function InputValidation({
message,
visible = true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a breaking change. If people use InputValidation it will render as before but they can now toggle with visible to use it in a new way. See: https://github.com/GetJobber/atlantis/pull/2184/files#diff-2dd45bcb75b2965ffe0b5fceb22778aaea0eb9af7c9912de07dae06ff3f7bafeR122-R127

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we update the storybook examples, add some tests? (I'm open to that being a followup PR! Let's just not forget, lol.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can push some tests and update the storybook example. The commit should be very small

}

export function FormFieldDescription({
id,
description,
visible = true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -19,6 +19,7 @@ export function AffixLabel({
const affixLabelClass = classnames(styles.affixLabel, {
[styles.suffix]: variation === "suffix",
});
if (!label) return null;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This might actually break things. confirm this

Copy link
Contributor Author

@MichaelParadis MichaelParadis Dec 2, 2024

Choose a reason for hiding this comment

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

There was a breaking change but I fixed it in d2db62b

image

After (this branch):

No value

image

With value (clearable)

image

With value (not clearable)

image

Before (Master)

No value

image

With value (clearable)

image

With value (not clearable)

image

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for catching these!

@@ -229,7 +229,7 @@ describe("FormField", () => {
describe("with validation errors", () => {
it("should trigger onValidation with error message", async () => {
const validationHandler = jest.fn();
const validate = val => (val == "Bob" ? "message" : "foo");
const validate = (val: string) => (val == "Bob" ? "message" : "foo");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typescript was upset so I added this

@MichaelParadis MichaelParadis changed the title feat(components): RHF formfield changes refactor(components): Expose Reusable hooks and components used in FormField Dec 2, 2024
@MichaelParadis MichaelParadis removed the request for review from JeffMarvin December 2, 2024 22:07
Copy link

github-actions bot commented Dec 2, 2024

Published Pre-release for 7d365c4 with versions:

  - @jobber/[email protected]+7d365c4c

To install the new version(s) for Web run:

npm install @jobber/[email protected]+7d365c4c

Copy link
Contributor

@scotttjob scotttjob left a comment

Choose a reason for hiding this comment

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

I know this is still in Draft, but I wanted to give it an approval to give you the go ahead for this work.

Ultimately, the idea is we're changing absolutely nothing. Just re-arranging the code a bit, before we actually start changing things in the V2 versions of some of our form components.

So as long as you get the green light in all our downstream CI, I think we can move forward with this change. We might want to put out a bit of communication before we roll this out, but it doesn't necessitate a major version bump by any means.

@@ -26,7 +20,6 @@ type FormFieldInternalProps = FormFieldProps & {
readonly id: string;
};

// eslint-disable-next-line max-statements
Copy link
Contributor

Choose a reason for hiding this comment

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

This makes me happy right out of the gate.

setValue(name, newValue, { shouldValidate: true });
},
}));
const { name } = useAtlantisFormFieldName({ id, nameProp });
Copy link
Contributor

Choose a reason for hiding this comment

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

:)

},
fieldState: { error },
} = useController({
errorMessage,
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks long, but ultimately there's so much power being unlocked here! :)

onKeyDown: handleKeyDown,
};
const inputRefs = mergeRefs([inputRef, fieldRef]);
const { textFieldProps, fieldProps, descriptionIdentifier } =
Copy link
Contributor

Choose a reason for hiding this comment

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

I know we're just shuffling code around ultimately, but it LOOKS nicer on top of being more extensible. :)

@@ -19,6 +19,7 @@ export function AffixLabel({
const affixLabelClass = classnames(styles.affixLabel, {
[styles.suffix]: variation === "suffix",
});
if (!label) return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for catching these!

placeholder={placeholder}
identifier={identifier}
style={
prefixRef?.current || suffixRef?.current
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the only part of this I'm not huge on, we can probably pull that into one of these hooks, but it's not a big enough deal to hold up this particular train. Let's keep rolling!

/>
)}
{error && !inline && <InputValidation message={error} />}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

A V2 comment: Is there an opportunity to get rid of these Divs and leverage our own components instead?

autoFocus: autofocus,
onKeyDown: handleKeyDown,
};
useEffect(() => handleValidation(errorMessage), [errorMessage]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks great!

@MichaelParadis MichaelParadis changed the title refactor(components): Expose Reusable hooks and components used in FormField refactor(components): Expose Reusable hooks and components used in FormField [JOB-110812] Dec 3, 2024
@MichaelParadis MichaelParadis marked this pull request as ready for review December 3, 2024 15:04
@MichaelParadis
Copy link
Contributor Author

@scotttjob I noticed that the clearing isn't focusing the input once it is clicked. I think I know why but I am going to spend some time fixing that

@MichaelParadis
Copy link
Contributor Author

It looks like I figured out the issue

Copy link
Contributor

@JeffMarvin JeffMarvin left a comment

Choose a reason for hiding this comment

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

Great stuff here! I had a couple of comments so I thought I would provide those now.

I've checked the components in storybook and they all seem to be behaving. I want to run a bit of QA on the JO PR you have and on the shim behavior.

export function InputValidation({ message }: InputValidationProps) {
export function InputValidation({
message,
visible = true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we update the storybook examples, add some tests? (I'm open to that being a followup PR! Let's just not forget, lol.)

@MichaelParadis MichaelParadis merged commit d0c4be1 into master Dec 4, 2024
13 checks passed
@MichaelParadis MichaelParadis deleted the MIKE/pull-scotts-rfh-formfield-changes branch December 4, 2024 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants