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

Iplementing rjsf form, rewriting to functional components #3043

Merged
merged 9 commits into from
Nov 2, 2023
Merged

Iplementing rjsf form, rewriting to functional components #3043

merged 9 commits into from
Nov 2, 2023

Conversation

alexcottner
Copy link
Contributor

@alexcottner alexcottner commented Oct 27, 2023

This pull request:

  1. Implements the @rjsf/bootstrap4 package in place of kinto-admin-form. See Swap kinto-admin-form with rjsf/bootstrap-x? #2118
  2. Rewrites our components to be functional components. See Migrate away from container components in favor of functional compomnents with react-redux hooks #3026 .
  3. Refactors some of the larger components to be smaller and more manageable for those of us with bad short term memories.
  4. Fixes a few unit tests, mainly around the AuthForm component. But I still would like to do a larger rewrite for this and all unit tests in the near future.

There are also a couple freebies we get:

  1. Validate collection JSON schema. See Validate provided collection JSON schema #75
  2. Refactor JSONEditor component. See Refactor the JSONEditor component #237

This PR does not completely move away from redux or untangle us from the other dependencies we want to remove. However I think this is a good foundation to start with that should make that work easier in the near future.

This PR is huge but I would like to see it implemented as a whole if we can. I have tried to organize the commits into manageable pieces that can be reviewed independently. Requests for additional changes will be made in new commits for easy tracking.

Copy link
Contributor

@leplatrem leplatrem left a comment

Choose a reason for hiding this comment

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

Niiiiice!
Are there areas where you need us to concentrate more?

I made a first pass, could not review every detail thoroughly because my brain tends to scroll changes too fast, but I ran it on https://remote-settings-dev.allizom.org/v1/ and found a few glitches

Screenshot 2023-10-30 at 14 00 01
  • title of Authentication method field is not bold
  • Drop down is a bit higher than form line
Screenshot 2023-10-30 at 14 01 08 Screenshot 2023-10-30 at 14 07 21
  • Pop up after login success/error
Screenshot 2023-10-30 at 14 03 18
  • records list seems broken
A non-serializable value was detected in an action, in the path: `next`. Value: 
BoundFunctionObject { … }
 
Take a look at the logic that dispatched this action:  
Object { type: "COLLECTION_HISTORY_SUCCESS", entries: (1) […], hasNextPage: false, next: BoundFunctionObject, … }
Screenshot 2023-10-30 at 14 14 30
  • in DEV we can create collections. The permission seems to "lost" with this PR

},
server: {
...schema.properties.server,
default: serverURL,
},
},
};
return foo;
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: form?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should have removed that, I was debugging something and wanted to look at the object structure before it was returned. Removing in next commit.


const select = useCallback(
server => event => {
console.log("server select");
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, removing in next commit.


const onServerChange = useCallback(
event => {
console.log("onServerChange");
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, removing in next commit.

const collectionData = { ...JSON.parse(formData.data), id: formData.id };
this.props.onSubmit({ formData: collectionData });
}) => {
console.log(formInput);
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, removing in next commit.

schema={schema}
uiSchema={_uiSchema}
formData={formDataSerialized}
validator={validator}
Copy link
Contributor

Choose a reason for hiding this comment

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

I imagine that this would fix:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sweet, will add these to the description.

@@ -254,7 +222,7 @@ describe("AuthForm component", () => {
target: { value: "http://test.server/v1" },
});
expect(serverField.value).eql("http://test.server/v1");
expect(authTypeField.value).eql("anonymous");
expect(authTypeField.value).eql("openid-google");
Copy link
Contributor

Choose a reason for hiding this comment

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

out of curiosity? Did some order change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The test was sort-of broken before because of a quirk in how the component worked. In the old authForm it would default to anonymous auth after you changed servers, and an async event would search history (from localStorage) and update the value again if it found a match (through componentDidUpdate). But this test didn't go further to check the updated match was found later.

In the new code this happens in one step. If we have history for the new server we immediately default to the previous auth method. So I had to update the unit test to check the immediate value we're setting.

// Form.defaultProps = {
// ...Form.defaultProps,
// safeRenderCompletion: true,
// };
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good catch. I commented this out and forgot to cleanup later. Deleting in next commit.


import { Gear } from "react-bootstrap-icons";
import { Justify } from "react-bootstrap-icons";
import { ClockHistory } from "react-bootstrap-icons";
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't want to group them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Huh, they used to be like that in BucketCollections.tsx and I didn't fix it when I copied/pasted over. I guess that's what happens when I update 70 components in a few days. Fixing in next commit.

history: { entries, loaded, hasNextPage },
} = collection;
useEffect(() => {
console.log(`collectionHistory useEffect: ${props.location}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, fixing in next commit.

const attributes = JSON.parse(data);
propOnSubmit({
...omit(formData, ["data"]),
...omit(attributes, ["members"]),
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't want to keep the comment about why we omit this field here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Woops, putting it back.

@alexcottner
Copy link
Contributor Author

alexcottner commented Oct 30, 2023

Niiiiice! Are there areas where you need us to concentrate more?

I'm most concerned around things that were hard for me to test locally. The unit tests passed, but I don't have 100% faith in those by any means. Things like:

  1. All the authentication form stuff.
  2. The review/approval processes.
  3. Users/groups/permissions stuff.

I made a first pass, could not review every detail thoroughly because my brain tends to scroll changes too fast, but I ran it on https://remote-settings-dev.allizom.org/v1/ and found a few glitches
Screenshot 2023-10-30 at 14 00 01

Perfect, I will start going through these notes so far. I see you mentioned some styling changes, these are mostly from the @rjsf/bootstrap4 default theming. I only made a few small changes to the default styling so far, but I'm more than happy to make it closer to what we had before.

I'm going to start going through the rest of your feedback today and get things fixed up though. Thanks for looking at it already!

@alexcottner
Copy link
Contributor Author

* records list seems broken
A non-serializable value was detected in an action, in the path: `next`. Value: 
BoundFunctionObject { … }
 
Take a look at the logic that dispatched this action:  
Object { type: "COLLECTION_HISTORY_SUCCESS", entries: (1) […], hasNextPage: false, next: BoundFunctionObject, … }

This is actually happening for me in the current code as well. Can anybody confirm? It doesn't appear to break the records list for me though.

@alexcottner
Copy link
Contributor Author

Fixed most issues, need to dig into the permissions issue some more. I can repro that against the remote-settings-dev test site.

…cks to fix errors with older code. Responding to first round of PR feedback.
@alexcottner
Copy link
Contributor Author

I think I've resolved all known issues. Some issues appeared to exist in the current main branch though. I think we have some older code that isn't playing nice with newer redux, so I've adjusted middle-ware behavior for now.

Copy link
Contributor

@grahamalama grahamalama left a comment

Choose a reason for hiding this comment

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

The unit tests passed, but I don't have 100% faith in those by any means.

Yeah ... that's what makes a PR of this size so concerning.

Our test coverage numbers aren't great for our components and containers:

npx jest --coverage --coverageReporters="text-summary" --collectCoverageFrom="src/{components,containers}/**/*.{ts,tsx}"
...
=============================== Coverage summary ===============================
Statements   : 50.88% ( 546/1073 )
Branches     : 45.05% ( 346/768 )
Functions    : 47.14% ( 157/333 )
Lines        : 50.52% ( 525/1039 )
================================================================================

and there be dragons when converting class components to functional componets, for example when migrating lifecycle methods to useEffect calls.

I would like to see it implemented as a whole if we can

I'll echo @leplatrem by saying that it was a challenge to review a PR of this size thoroughly. Are there maybe specific commits that represent trickier component migrations or conversely, commits that were easier that I don't need to pay too much attention to?

export const Breadcrumbs = ({ separator }: BreadcrumbsProps) => {
export default function Breadcrumbs({ separator }: BreadcrumbsProps) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This change reminds me that we should pick a lane when it comes to default vs named exports. I prefer named exports, personally, but we can bikeshed that at another time 😉.

That being said, was there as reason we changed existing components in this PR? I get going with what you like for new components, but maybe we could leave existing components alone for this PR. That would be one thing we could do to make the overall diff smaller.

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 am indifferent to which pattern we follow for named vs default exports. In this specific case with the Breadcrumbs component, I was just trying to follow the "if you're only exporting one thing, make it default" pattern that we have in most of the code.

Similarly, I made everything export a function rather than declaring a const and exporting it later. in code I don't really have a strong opinion here either, just picked a direction and went with it for consistency. I think exporting the function is slightly cleaner in that it's slightly fewer characters. But I can see the benefit to having one export statement at the end of a file too.

capabilities,
updateBucket,
deleteBucket,
}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised that typescript doesn't complain about us not typing these props, which we were doing with the old component. Can we put that back and look for other places where we might have forgotten to type the props?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Woops, putting that back in.

@alexcottner
Copy link
Contributor Author

First - I'm sorry for the 4k+ line delta. I maybe went a little hard on cleanup, although I'm already finding it easier to work in the repo on my next PR.

We can merge this a bit at a time if that gives us more confidence. I made the commits whole pieces, and I can rewrite history to bring in the bugfixes with them as well if we want to go this route. Although if we don't have a deployment that will immediately use what we've merged this may not be beneficial.

Second - The two commits that I think need the most scrutiny are the 1st one (with AuthForm.tsx) and the 6th one (signoff components). AuthForm had the most difficult to understand change due to componentDidUPdate and all the redux/session state changes. And the sign-off functionality probably (definitely) has some more functionality to it than I think. The rest of the commits felt pretty light in comparison.

Third - The biggest banana peel when converting from class components to functions is rewriting componentDidUpdate functions, at least for me. Everything else is a pretty straight forward conversion (ex: class functions just become normal functions). There are 9 components that used to have a componentDidUpdate function in them that I had to rewrite. Most of these were pretty simple but there is always a chance I missed something in a cut/paste.

@leplatrem
Copy link
Contributor

I made some tests this morning against the DEV server.

Only real issue is this one:

I only saw this apparence issue, which is minor and can be fixed in a follow-up:
Screenshot 2023-11-02 at 10 40 36

As for testing and reviewing, I think we reached an acceptable state in this PR. And since master has a lot more issues than this branch, I would say that we don't too much risk merging (since I could perform most actions successfully).

Before shipping this to PROD, we can ask our users to try it thoroughly on our STAGE instance, making sure it doesn't break their workflow

@alexcottner
Copy link
Contributor Author

Creating a followup on the permission error and merging. Will have some more PR's to follow up on this quickly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants