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

Fixes #36295 - Auto activation key selection if there's only one #10518

Merged
merged 1 commit into from
Aug 23, 2023

Conversation

girijaasoni
Copy link
Contributor

@girijaasoni girijaasoni commented Apr 14, 2023

This PR is blocked by Foreman PR:
theforeman/foreman#9688

This PR has changes t
o auto select an activation key when there's just one and to remove the "No activation key selected error" on User's arrival to the register form. This pr also has better validation formatting like the exclamation icon on the left of carter and underline on the Select dropdown when there's an error! Screenshots of the changes are attached below:

Screenshot from 2023-04-18 17-10-21

Screenshot from 2023-04-18 17-09-57

@theforeman-bot
Copy link

Can one of the admins verify this patch?

@theforeman-bot
Copy link

Issues: #36295

@girijaasoni girijaasoni changed the title Fixes #36295 - Auto activation key selection in case of one activatio… Fixes #36295 - Auto activation key selection if there's only one Apr 14, 2023
Copy link

@Ron-Lavi Ron-Lavi left a comment

Choose a reason for hiding this comment

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

Thanks @girijaasoni !



useEffect(() => {
if(activationKeys.length==1){

Choose a reason for hiding this comment

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

Suggested change
if(activationKeys.length==1){
if (activationKeys.length === 1) {

Copy link
Contributor

Choose a reason for hiding this comment

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

@girijaasoni npm run lint -- --fix is your friend

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @stejskalleos


useEffect(() => {
if(activationKeys.length==1){
onChange({ activationKeys: [activationKeys[0]?.name]})

Choose a reason for hiding this comment

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

maybe you could reuse the onSelect function above instead by passing the value of the activation key?

if(activationKeys.length==1){
onChange({ activationKeys: [activationKeys[0]?.name]})
}
}, [activationKeys]);

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @Ron-Lavi

Copy link
Contributor

@stejskalleos stejskalleos left a comment

Choose a reason for hiding this comment

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

Two nits, otherwise code looks good, but haven't tested it yet.

@@ -38,10 +38,16 @@ const ActivationKeys = ({
};

// Validate field when hostgroup is changed (host group may have some keys)
useEffect(() => {
useEffect(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Trailing space, check the configuration of VS code, Trim Trailing Whitespace should be enabled by default if I am not mistaken

handleInvalidField('Activation Keys', akHasValidValue(hostGroupId, pluginValues?.activationKeys, hostGroupActivationKeys));
}, [handleInvalidField, hostGroupId, hostGroupActivationKeys, pluginValues]);

useEffect(() => {
if (activationKeys?.length === 1) {
updatePluginValues([activationKeys[0]?.name]);
Copy link
Contributor

Choose a reason for hiding this comment

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

If activationKeys is not empty, we can safely assume that the key has a name attribute:
updatePluginValues([activationKeys[0].name]);

@@ -88,7 +94,7 @@ ActivationKeys.propTypes = {
};

ActivationKeys.defaultProps = {
activationKeys: undefined,
activationKeys: [],
Copy link
Contributor Author

@girijaasoni girijaasoni Apr 18, 2023

Choose a reason for hiding this comment

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

activationKeys is set to "[]" in the useEffect of indexJs

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah I know, but it was supposed to be [] from the beginning ...

Copy link
Contributor

Choose a reason for hiding this comment

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

undefined is for first form load so the field is not marked as invalid

@@ -88,7 +94,7 @@ ActivationKeys.propTypes = {
};

ActivationKeys.defaultProps = {
activationKeys: undefined,
activationKeys: [],
Copy link
Contributor Author

@girijaasoni girijaasoni Apr 18, 2023

Choose a reason for hiding this comment

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

activationKeys is set to "[]" in the useEffect of indexJs

Copy link

@Ron-Lavi Ron-Lavi left a comment

Choose a reason for hiding this comment

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

Nice work @girijaasoni !

  • When there is only one activation key available during host registration, the Activation Key is automatically selected.

  • When multiple activation keys are available during host registration, the Activation Key is not automatically selected.

  • The auto-selection of the Activation Key does not interfere with the ability to manually select or deselect the option.

  • There's a warning alert shown initially when user's have multiple AK (not sure if you are going to address it in this PR):
    Screenshot - 2023-04-19T093224 456

@girijaasoni
Copy link
Contributor Author

Nice work @girijaasoni !

  • When there is only one activation key available during host registration, the Activation Key is automatically selected.
  • When multiple activation keys are available during host registration, the Activation Key is not automatically selected.
  • The auto-selection of the Activation Key does not interfere with the ability to manually select or deselect the option.
  • There's a warning alert shown initially when user's have multiple AK (not sure if you are going to address it in this PR):
    Screenshot - 2023-04-19T093224 456

That is done in the foreman pr which blocks this pr theforeman/foreman#9688

return (
<FormGroup
label={__('Activation Keys')}
fieldId="reg_katello_ak"
helperText={hostGroupActivationKeys && sprintf('From host group: %s', hostGroupActivationKeys)}
helperTextInvalid={activationKeys?.length === 0 ? <a href="/activation_keys/new">{__('Create new activation key')}</a> : __('No Activation Keys selected')}
validated={validateAKField(hostGroupId, pluginValues?.activationKeys, hostGroupActivationKeys)}
helperTextInvalidIcon={<ExclamationCircleIcon />}

Choose a reason for hiding this comment

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

Not sure if we should remove the {activationKeys?.length === 0 ? <a href="/activation_keys/new">{__('Create new activation key')}</a>

Copy link
Contributor

Choose a reason for hiding this comment

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

Why remove it?

Copy link
Member

Choose a reason for hiding this comment

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

I don't see the "Create new activation key" link any more. I see no reason to remove it; can you please put it back?

Comment on lines 47 to 72
if (activationKeys?.length === 1) {
updatePluginValues([activationKeys[0].name]);
}
}, [activationKeys?.length]);

Choose a reason for hiding this comment

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

not entirely against using optional chaining (?.)
though proptypes should protect us in this case and we can expect it to be an array,
I think we would be ok not using it in this case

Copy link
Contributor

@stejskalleos stejskalleos Apr 20, 2023

Choose a reason for hiding this comment

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

Optional chaining is required for the case when activationKeys is undefined (on form load)

Copy link
Contributor

@stejskalleos stejskalleos left a comment

Choose a reason for hiding this comment

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

One nit about PropTypes otherwise works fine.

@@ -88,7 +94,7 @@ ActivationKeys.propTypes = {
};

ActivationKeys.defaultProps = {
activationKeys: undefined,
activationKeys: [],
Copy link
Contributor

Choose a reason for hiding this comment

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

undefined is for first form load so the field is not marked as invalid

@@ -73,7 +73,7 @@ export const RegistrationActivationKeys = ({
isLoading,
}) => {
useEffect(() => {
onChange({ activationKeys: [] });
onChange({ activationKeys: undefined });
Copy link
Contributor

Choose a reason for hiding this comment

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

PropTypes.oneOfType([PropTypes.undefined, PropTypes.array])

Choose a reason for hiding this comment

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

imho it's not a best practice to pass the value as undefined
if we are doing this in order to hide/display proper error on the field, e.g:
hide the error when the user opens the page,
and show it upon interaction with the field,

I propose to add a const [ wasInteracted, setInteraction ] = useState(false)
and then use the wasInteracted in the right places.

This should give us more control over what we are trying to achieve.

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 not against it, it makes sense, just didn't know that it's a bad practice.

@jeremylenz @MariaAga How do you handle touched / untouched form fields in React components?

Copy link
Contributor

Choose a reason for hiding this comment

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

I used onBlur={() => setWasFocus(true) here: theforeman/foreman_remote_execution#784

Copy link

@Ron-Lavi Ron-Lavi left a comment

Choose a reason for hiding this comment

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

@girijaasoni snapshot test is failing, could you update it?
npm test -- -u

@girijaasoni girijaasoni force-pushed the 36295 branch 2 times, most recently from 2f87ffd to ecc90cb Compare May 24, 2023 13:08
Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

Hi @girijaasoni, I left a couple code comments below :)

Comment on lines 65 to 72
useEffect(() => {
if (activationKeys?.length === 1) {
updatePluginValues([activationKeys[0].name]);
}
if (activationKeys?.length === 0) {
setHasInteraction(true);
}
}, [activationKeys?.length]);
Copy link
Member

Choose a reason for hiding this comment

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

In general, setting state in effects should be avoided if possible.

Rather, setting state in event handlers is much better. For hasInteraction, I think we can achieve the same thing by adding an onFocus to the <FormGroup>:

<FormGroup
      onFocus={() => setHasInteraction(true)}

And for the pluginValues, I would strongly prefer if that could be updated outside an effect as well, but it seems slot and fill makes that too difficult. So I'm ok with keeping that part.

Either way, this is really good reading: https://react.dev/learn/you-might-not-need-an-effect

Comment on lines 65 to 72
useEffect(() => {
if (activationKeys?.length === 1) {
updatePluginValues([activationKeys[0].name]);
}
if (activationKeys?.length === 0) {
setHasInteraction(true);
}
}, [activationKeys?.length]);
Copy link
Member

Choose a reason for hiding this comment

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

However this effect ends up, the dependency array needs to be correct :)

hostGroupActivationKeys: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
]),
hostGroupId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
pluginValues: PropTypes.objectOf(PropTypes.shape({})),
Copy link
Member

Choose a reason for hiding this comment

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

I got this console error; seems like this needs an update:

Warning: Failed prop type: Invalid prop `pluginValues.activationKeys` of type `array` supplied to `ActivationKeys`, expected `object`

@jeremylenz
Copy link
Member

@girijaasoni any update here? This is still on our review board.

@girijaasoni
Copy link
Contributor Author

@girijaasoni any update here? This is still on our review board.

Since it was for 6.15, I paused the work on it, back on it now again :D

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

The useEffect code is looking much better and the page is mostly working well! 👍 Just a couple more minor comments below.

@@ -2,7 +2,7 @@

exports[`ActivationKeys renders 1`] = `
<FormGroup
fieldId="activation_keys_field"
fieldId="reg_katello_ak"
Copy link
Member

Choose a reason for hiding this comment

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

I thought QE wanted us to remove "katello" from these? Or did I have that switched around?

}
helperTextInvalidIcon={<ExclamationCircleIcon />}
labelIcon={
<LabelIcon text={__('Activation key(s) for Subscription Manager.')} />
Copy link
Member

Choose a reason for hiding this comment

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

Since subscription-manager is going away at some point, this might be a good time to update the wording here.

Suggested change
<LabelIcon text={__('Activation key(s) for Subscription Manager.')} />
<LabelIcon text={__('Activation key(s) to use during registration.')} />

return (
<FormGroup
label={__('Activation Keys')}
fieldId="reg_katello_ak"
helperText={hostGroupActivationKeys && sprintf('From host group: %s', hostGroupActivationKeys)}
helperTextInvalid={activationKeys?.length === 0 ? <a href="/activation_keys/new">{__('Create new activation key')}</a> : __('No Activation Keys selected')}
validated={validateAKField(hostGroupId, pluginValues?.activationKeys, hostGroupActivationKeys)}
helperTextInvalidIcon={<ExclamationCircleIcon />}
Copy link
Member

Choose a reason for hiding this comment

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

I don't see the "Create new activation key" link any more. I see no reason to remove it; can you please put it back?

Copy link
Member

@jeremylenz jeremylenz left a comment

Choose a reason for hiding this comment

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

Tested all three scenarios

✔️ No AKs available
✔️ Only one AK
✔️ Multiple AKs

All works great. Nice work! APJ 👍

@jeremylenz
Copy link
Member

[test katello]

@jeremylenz
Copy link
Member

Test failures unrelated

@jeremylenz
Copy link
Member

@girijaasoni If you rebase to current master the test failures should go away now :)

@girijaasoni
Copy link
Contributor Author

[test katello]

1 similar comment
@jeremylenz
Copy link
Member

[test katello]

@jeremylenz jeremylenz merged commit e6e766a into Katello:master Aug 23, 2023
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants