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

UI update: regex checking for Project Name/Tag inputs in edit metadata modal #319

Merged
merged 16 commits into from
Jun 26, 2024

Conversation

sanghoonio
Copy link
Member

Inputs for Project Name and Tag will only allow alphanumeric characters, '_', and '-'. If a user attempts to type an invalid character or paste a string with an invalid character into these inputs, the input will not let them and some helper text will appear to let the user know which characters are supported.

Someone can work around this by going into browser inspect element or call the API endpoint itself with invalid characters, so we should still have some API level check for these strings.

khoroshevskyi and others added 5 commits February 8, 2024 23:10
updated hands on table
Integrate views into the user interface
…nd tag for metadata modal. added conditional helper text that appears if user enters invalid character.
Copy link
Member

@nleroy917 nleroy917 left a comment

Choose a reason for hiding this comment

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

Nice start. I totally agree with introducing another state variable to verify that the name and tag are correct. A couple thoughts and ideas...

  1. Instead of using onKeyDown and onPaste callbacks, could we tie the check directly to the onChange callback? It gets messy with react-hook-form? Here is an idea:

We are already "watching" the new name and the new tag. Could we add an effect? Something like:

useEffect(() => {
   checkName(newName)
   checktag(newTag)
}, [newName, newTag])

Does that make sense? We could go over it together if needed. Lot of React quirks here.

  1. As I think you rightly pointed out, this needed to be validated on the API too. Do you feel comfortable tackling that too? Hows your Python? Moreover, we need to run this validation when someone submits a new PEP too.

I believe there a lot in the src/components/forms/ folder that need this logic too.

Let me know if that all made sense!

Here is some reading on uesEffect. With great power, though, comes great responsibility. useEffect is often called useFootGun and I try to use it as little as possible. I really like this article by Dan Abromov, one of the creators of React.

web/src/components/forms/edit-project-meta.tsx Outdated Show resolved Hide resolved
web/src/components/forms/edit-project-meta.tsx Outdated Show resolved Hide resolved
web/src/components/forms/edit-project-meta.tsx Outdated Show resolved Hide resolved
web/src/components/forms/edit-project-meta.tsx Outdated Show resolved Hide resolved
@sanghoonio
Copy link
Member Author

sanghoonio commented Jun 18, 2024

Changes addressed:

  1. onKeyDown and onPaste removed for useEffect. Users can now input or paste invalid characters into the input field, which will trigger helper text to display due to change in state variable. If the invalid characters are removed from the inputs, the state variable will revert to false and helper text will clear.
  2. Modal save button now tracks state variable and is deactivated if name or tag contain an invalid character.
  3. Use some bootstrap classes for the helper text. Bootstrap by default goes down to fs-6 minimum which is 16px or 1em. It looked too big to me so I switched it from 12px to 0.75em, which should help it scale if needed.

Thanks for the pointers on useEffect(). It was pretty confusing at first because it tracks the input value itself and not the key down or paste event but it seems to work pretty well.

Copy link
Member

@nleroy917 nleroy917 left a comment

Choose a reason for hiding this comment

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

🤦🏻‍♂️ I love how I sent a link to an article telling you not to use useEffect, then proceeded to say lets use useEffect.

I think I was totally wrong now that I am looking at it. Instead of the effect, I think we should just define the variable:

export const ProjectMetaEditForm = () => {

  const nameIsBad =  /[^0-9a-zA-Z_-]/.test(newName);
  const tagIsBad = /[^0-9a-zA-Z_-]/.test(newTag);
  
  return (
    <div>
    { nameIsBad ? <p className = 'text-secondary pt-1' style = {{fontSize: '.75em'}}>Invalid name. Project names should only contain letters, numbers, etc etc etc </p> : null }
    </div>
  )
}

Because useState triggers a rerender anyways, there's no need to tie all the to a useEffect, so that's my bad for giving bad advice there.

Finally... do you think you could implement something similar on the other components?

@@ -252,7 +248,7 @@ export const ProjectMetaEditForm: FC<Props> = ({
<button
onClick={() => mutation.mutate()}
id="metadata-save-btn"
disabled={(!isDirty && isValid) || mutation.isPending}
disabled={(!isDirty && isValid || badName || badTag) || mutation.isPending}
Copy link
Member

Choose a reason for hiding this comment

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

Great! This is perfect.

…other components and updated error message contents and formatting. added mode: 'onChange' for formState instantation for these 3 other components.
@sanghoonio
Copy link
Member Author

Done, updated checks in other components

Copy link
Member

@nleroy917 nleroy917 left a comment

Choose a reason for hiding this comment

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

This is great -- I love that you found those react-hook-form APIs and used them. Thats a great solution. Could we emulate that over on the edit-project-meta form? I think thats the only one using the old system.

@@ -119,6 +119,9 @@ export const ProjectMetaEditForm: FC<Props> = ({
}
}, [newPop]);

const isBadName = newName ? (/[^0-9a-zA-Z_-]/.test(newName) ? "Project Name must contain only alphanumeric characters, '-', or '_'." : null) : "Project Name must not be empty."
const isBadTag = newTag ? (/[^0-9a-zA-Z_-]/.test(newTag) ? "Project Tag must contain only alphanumeric characters, '-', or '_'." : null) : "Project Tag must not be empty."
Copy link
Member

Choose a reason for hiding this comment

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

Actually, the project tag can be empty.

Copy link
Member

Choose a reason for hiding this comment

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

I really loved what you did in the blank-project-form component, using build in react-hook-form API's to validate -- should we do that here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, the project tag can be empty.

I tried submitting a blank tag edit on the deployed UI and it doesn't let you so I assumed we weren't supposed to allow it. Is it supposed to default to 'default' if empty?

Comment on lines 123 to 124
value: /^[a-zA-Z0-9_-]+$/,
message: "Project Name must contain only alphanumeric characters, '-', or '_'.",
Copy link
Member

Choose a reason for hiding this comment

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

This is awesome, I didn't know this was possible with react-hook-form!

Copy link
Member Author

Choose a reason for hiding this comment

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

// instantiate form
const {
    reset: resetForm,
    register,
    control,
    watch,
    setValue,
    formState: { isValid, errors },
  } = useForm<FromFileInputs>({
    mode: 'onChange',  <<<---------- had to add this for it to work
    defaultValues: {
      is_private: false,
      pep_schema: 'pep/2.1.0',
      namespace: defaultNamespace || user?.login || '',
    },
  });

So I added that mode: 'onChange' line to make it work but wasn't sure if it would cause lag if it started checking after every single input. Is this negligible with react/our use case or is it something we have to worry about?

Comment on lines 141 to 142
<ErrorMessage errors={errors} name="project_name" render={({ message }) => message ? (<p className='text-secondary pt-1' style={{fontSize: '.75em'}}>{message}</p>) : null} />
<ErrorMessage errors={errors} name="tag" render={({ message }) => message ? (<p className='text-secondary pt-1' style={{fontSize: '.75em'}}>{message}</p>) : null} />
Copy link
Member

Choose a reason for hiding this comment

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

Feel like I've tried to use this before with mixed results. Have you tried?

Copy link
Member

Choose a reason for hiding this comment

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

Just tried it myself and it looks great! What do you think about making the error message text red? (danger)

web/src/components/forms/pop-form.tsx Outdated Show resolved Hide resolved
web/src/components/forms/project-upload-form.tsx Outdated Show resolved Hide resolved
Copy link
Member Author

@sanghoonio sanghoonio left a comment

Choose a reason for hiding this comment

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

Changes reviewed

…r message for when both name and tag are bad for all components. added sample count to project page. cleaned up project page slightly (borders, padding, etc).
Copy link
Member

@nleroy917 nleroy917 left a comment

Choose a reason for hiding this comment

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

Looks great! We should probably change the base to merge into dev instead of master

@khoroshevskyi khoroshevskyi changed the base branch from master to dev June 26, 2024 15:08
@sanghoonio sanghoonio merged commit 5811ce4 into dev Jun 26, 2024
1 check passed
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