-
Notifications
You must be signed in to change notification settings - Fork 20
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
O3-1409: Support for users changing passwords #43
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jnsereko! Getting much closer. A bunch of nit-picking remarks.
if (passwordInput.confirmPassword !== '') { | ||
handleValidation(passwordInput.confirmPassword, 'confirmPassword'); | ||
} | ||
}, [passwordInput]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleValidation
is also a dependency here.
const handlePasswordChange = (event) => { | ||
const passwordInputValue = event.target.value.trim(); | ||
const passwordInputFieldName = event.target.name; | ||
const NewPasswordInput = { ...passwordInput, [passwordInputFieldName]: passwordInputValue }; | ||
setPasswordInput(NewPasswordInput); | ||
}; | ||
|
||
const handleValidation = (passwordInputValue, passwordInputFieldName) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably wrap both of these in useCallback()
try { | ||
setIsSavingPassword(true); | ||
await performPasswordChange(passwordInput.oldPassword, passwordInput.confirmPassword).then(() => { | ||
close(); | ||
navigate({ to: `\${openmrsSpaBase}/logout` }); | ||
showToast({ | ||
title: t('userPassword', 'User password'), | ||
description: t('userPasswordUpdated', 'User password updated successfully'), | ||
kind: 'success', | ||
}); | ||
}); | ||
} catch (error) { | ||
setIsSavingPassword(false); | ||
setErrorMessage(`${t('invalidPasswordCredentials', 'Invalid password provided')}: ${error.message}`); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This construction is weird...
performPasswordChange(passwordInput.oldPassword, passwordInput.confirmPassword).then(() => {
close();
navigate({ to: `\${openmrsSpaBase}/logout` });
showToast({
title: t('userPassword', 'User password'),
description: t('userPasswordUpdated', 'User password updated successfully'),
kind: 'success',
});
}).catch((err) => {
setIsSavingPassword(false);
setErrorMessage(`${t('invalidPasswordCredentials', 'Invalid password provided')}: ${error.message}`);
});
Reads better to me. (Basically either use await
and no then()
or else just use it as a promise).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The success message should be a snackbar, not a toast. Also, we should probably show a notification when it submits but fails at the backend.
evt.preventDefault(); | ||
evt.stopPropagation(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
setErrorMessage(`${t('invalidPasswordCredentials', 'Invalid password provided')}: ${error.message}`); | ||
} | ||
|
||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also why are we returning false?
onClick={handleSubmit} | ||
disabled={isSavingPassword || isNewPasswordInvalid || isConfirmPasswordInvalid || isOldPasswordInvalid} | ||
> | ||
{t('apply', 'Apply')} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{t('apply', 'Apply')} | |
{t('updatePassword', 'Update Password')} |
} | ||
|
||
.errorMessage { | ||
position: absolute; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolute positioning for an error message feels wrong.
options, | ||
); | ||
|
||
export const ChangePasswordModal = getAsyncLifecycle( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const ChangePasswordModal = getAsyncLifecycle( | |
export const changePasswordModal = getAsyncLifecycle( |
}, | ||
{ | ||
"name": "change-password-modal", | ||
"component": "ChangePasswordModal", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"component": "ChangePasswordModal", | |
"component": "changePasswordModal", |
<Tile> | ||
<form onSubmit={handleSubmit} ref={formRef}> | ||
<div className={styles['input-group']}> | ||
<PasswordInput |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's likely we need a Carbon Layer
component in here.
packages/esm-system-admin-app/src/change-password-modal/change-password-modal.component.tsx
Outdated
Show resolved
Hide resolved
packages/esm-system-admin-app/src/change-password-modal/change-password-modal.component.tsx
Outdated
Show resolved
Hide resolved
packages/esm-system-admin-app/src/change-password-modal/change-password-modal.component.tsx
Outdated
Show resolved
Hide resolved
packages/esm-system-admin-app/src/change-password-modal/change-password-modal.component.tsx
Outdated
Show resolved
Hide resolved
packages/esm-system-admin-app/src/change-password-modal/change-password-modal.component.tsx
Outdated
Show resolved
Hide resolved
<> | ||
<ModalHeader closeModal={close} title={t('changePassword', 'Change Password')} /> | ||
<ModalBody> | ||
<Tile> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for Tile here. It adds unnecessary margin to the modal body.
packages/esm-system-admin-app/src/change-password-modal/change-password-modal.component.tsx
Outdated
Show resolved
Hide resolved
:global(.cds--text-input) { | ||
background-color: white; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Layer instead of this override.
import userEvent from '@testing-library/user-event'; | ||
|
||
describe(`Change Password Modal`, () => { | ||
it('should change user locale', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be something about changing the password, not the locale?
onChange={handlePasswordChange} | ||
ref={newPasswordInputRef} | ||
required | ||
showPasswordLabel="Show new password" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider providing detailed helper text below the fields listing any requirements related to the data format, such as types of characters allowed per the password input usage guidelines.
useEffect(() => { | ||
if (passwordInput.oldPassword !== '') { | ||
handleValidation(passwordInput.oldPassword, 'oldPassword'); | ||
} | ||
if (passwordInput.newPassword !== '') { | ||
handleValidation(passwordInput.newPassword, 'newPassword'); | ||
} | ||
if (passwordInput.confirmPassword !== '') { | ||
handleValidation(passwordInput.confirmPassword, 'confirmPassword'); | ||
} | ||
}, [handleValidation, passwordInput]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the best client-side validation experience, we should probably use RHF and Zod here similar to how we're validating the Vitals and Biometrics form (and various other vanilla React forms in O3).
…-password-modal.component.tsx Co-authored-by: Dennis Kigen <[email protected]>
…-password-modal.component.tsx Co-authored-by: Dennis Kigen <[email protected]>
…-password-modal.component.tsx Co-authored-by: Dennis Kigen <[email protected]>
…-password-modal.component.tsx Co-authored-by: Dennis Kigen <[email protected]>
…-password-modal.component.tsx Co-authored-by: Dennis Kigen <[email protected]>
…-password-modal.component.tsx Co-authored-by: Dennis Kigen <[email protected]>
Requirements
Summary
O3 screen to allow users to change their password as a follow-up for openmrs/openmrs-esm-core#902
cc @ibacher @michaelbontyes @vasharma05 @denniskigen
Screenshots
change-password.mov
None.
Issue
https://openmrs.atlassian.net/browse/O3-1409
None.
Other
None.