Skip to content
This repository has been archived by the owner on Jul 19, 2023. It is now read-only.

feat: Save Generator Form State for a Session #185

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Laurin-W
Copy link
Contributor

@Laurin-W Laurin-W commented Nov 25, 2022

This PR makes the generator UI store its state with the sessionStore API so that when re-visiting the form after going to the validator, for example, the entered form values are not lost.

This is rather a WIP since I will probably refactor a bit of the formik state management to make this less ugly.

TODO:

  • refactor
  • write e2e tests

@vercel
Copy link

vercel bot commented Nov 25, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
client-identifier-helper ✅ Ready (Inspect) Visit Preview Dec 21, 2022 at 10:20PM (UTC)

@Laurin-W
Copy link
Contributor Author

Heyho, I finally got some time, added an e2e test and refactored the code a bit.

The form now uses a modified version of useFormik which packs the fields' state manipulation (setting info, warning, etc. messages) and the field value manipulation at one place, the formik object. Also, modifying form array elements (i.e. the redirect URIs and contacts fields) got a bit better in that regard.

Happy for any feedback and improvement suggestions! I hope you have a good christmas time!

Copy link
Contributor

@ThisIsMissEm ThisIsMissEm left a comment

Choose a reason for hiding this comment

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

I'm a little surprised by this PR, but thanks! There are some issues with the safety of certain operations (json.parse and setItem), which should be fixed; Additionally I think it only makes sense to restore the state if it's within a given timeframe, instead of being indefinite, so you'd want to track a "last modified" value and compare that to see if it's current data to restore, otherwise this could potentially leak information.


// Expect clientId error state to have remained.
expect(
await page.locator(`.MuiFormHelperText-root.Mui-error`).count()
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be a good element to add a data-testid attribute to, potentially

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed in d5425f1.

);
};

return { ...modifiedFormik, addChild, removeChild };
Copy link
Contributor

Choose a reason for hiding this comment

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

you'll want to useMemo 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.

I'm not quite sure how I would employ this here and I couldn't quite make sense of the documentation I found.

Wrapping the whole function makes the linter say: React Hook "useFieldStates" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
If only wrapping parts, the linter comoplains: React Hook useMemo has missing dependencies

Could you give me a hint here?

*
* @returns formik object
*/
export default function useVerboseFormik<FormParameters extends FormikValues>({
Copy link
Contributor

Choose a reason for hiding this comment

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

How's this "verbose" ? Perhaps extended?

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 used "verbose" since the extension allows the form to hold not only error but also warning, info and success messages and I used the "verbose" in VerboseTextField etc.

But I guess, it's not really that clear and I'll change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed in 8770679

@@ -87,10 +75,36 @@ export default function ClientIdentifierGenerator() {
defaultMaxAge: undefined,
};

/** Helper to load form state and values from storage or defaults. */
const getSessionStorageStateOrDefault: () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

it'd be an idea to add a max-age to this, such that coming back after a day doesn't restore data, it's really only for "I changed pages and oops I actually wanted that data"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in ec0e242

Comment on lines 86 to 92
const initialValues = storedFormValues
? JSON.parse(storedFormValues)
: initialFormValues;
const initialStates =
formValidationState && storedFormValues
? JSON.parse(formValidationState)
: getEmptyFormState();
Copy link
Contributor

Choose a reason for hiding this comment

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

JSON.parse can throw, make sure you do this safely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in ec0e242.

Comment on lines 216 to 221
// Save state of the form.
sessionStorage.setItem("formValues", JSON.stringify(formik.values));
sessionStorage.setItem(
"formValidationState",
JSON.stringify(formik.states.all)
);
Copy link
Contributor

Choose a reason for hiding this comment

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

These operations can throw, make sure they're handled safely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in ec0e242

Comment on lines -231 to +266
form.setFieldTouched(field, true)
formik.setFieldTouched(field, true)
Copy link
Contributor

Choose a reason for hiding this comment

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

why the rename?

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 formik references call the object formik as well so I figured, it'd be better to stick to that convention.

@ThisIsMissEm
Copy link
Contributor

Hey Laurin, sorry for the delay here, but it's going to take a while for us to get to this (it's been pushed into our backlog to review), I had been hoping to review this over christmas for you, but I caught covid and was out for 2 solid weeks.

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

Successfully merging this pull request may close these issues.

2 participants