From 0834af5bb7299ef1a046cfe73e0afaaf988e0ee5 Mon Sep 17 00:00:00 2001 From: RachelElysia <71795832+RachelElysia@users.noreply.github.com> Date: Tue, 6 Jul 2021 12:22:51 -0400 Subject: [PATCH] User Settings Page and User Management Page: SMTP config conditionally disables email update (#1309) * Disable Email update field when SMTP is disabled * Render tooltip, not-allowed cursor, greyed font for disabled UI * Update integration tests accordingly * Minor fixes to form styling --- .../UserSettingsForm/UserSettingsForm.jsx | 40 ++++++++++++++--- .../forms/fields/Dropdown/_styles.scss | 1 - .../InputFieldWithIcon/InputFieldWithIcon.jsx | 3 ++ .../fields/InputFieldWithIcon/_styles.scss | 5 +++ .../UserSettingsPage/UserSettingsPage.jsx | 3 +- .../UserSettingsPage.tests.jsx | 8 ++-- .../MembersPagePage/MembersPage.tsx | 5 +++ .../UserManagementPage/UserManagementPage.jsx | 2 + .../EditUserModal/EditUserModal.tsx | 3 ++ .../SelectedTeamsForm/SelectedTeamsForm.tsx | 4 +- .../components/UserForm/UserForm.tsx | 45 +++++++++++++++---- .../components/UserForm/_styles.scss | 3 ++ 12 files changed, 98 insertions(+), 24 deletions(-) diff --git a/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx b/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx index 725e722433..4160f7a5df 100644 --- a/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx +++ b/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx @@ -22,6 +22,7 @@ class UserSettingsForm extends Component { handleSubmit: PropTypes.func.isRequired, pendingEmail: PropTypes.string, onCancel: PropTypes.func.isRequired, + smtpConfigured: PropTypes.bool, }; renderEmailHint = () => { @@ -39,17 +40,42 @@ class UserSettingsForm extends Component { }; render() { - const { fields, handleSubmit, onCancel } = this.props; + const { fields, handleSubmit, onCancel, smtpConfigured } = this.props; const { renderEmailHint } = this; return (
- +
+ +
+ + + Editing your email address requires that SMTP is
+ configured in order to send a validation email.
+
+ Users with Admin role can configure SMTP in +
+ Settings > Organization settings. +
+
diff --git a/frontend/components/forms/fields/Dropdown/_styles.scss b/frontend/components/forms/fields/Dropdown/_styles.scss index d97972eaca..c7915e4067 100644 --- a/frontend/components/forms/fields/Dropdown/_styles.scss +++ b/frontend/components/forms/fields/Dropdown/_styles.scss @@ -89,7 +89,6 @@ .Select-value-label { font-size: $x-small; - font-weight: $bold; color: $core-dark-blue-grey; line-height: 28px; } diff --git a/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx b/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx index d813ac1a0f..9f097fefbf 100644 --- a/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx +++ b/frontend/components/forms/fields/InputFieldWithIcon/InputFieldWithIcon.jsx @@ -19,6 +19,7 @@ class InputFieldWithIcon extends InputField { tabIndex: PropTypes.number, type: PropTypes.string, className: PropTypes.string, + disabled: PropTypes.bool, }; renderHeading = () => { @@ -57,6 +58,7 @@ class InputFieldWithIcon extends InputField { tabIndex, type, value, + disabled, } = this.props; const { onInputChange, renderHint } = this; @@ -89,6 +91,7 @@ class InputFieldWithIcon extends InputField { tabIndex={tabIndex} type={type} value={value} + disabled={disabled} /> {iconName && } {renderHint()} diff --git a/frontend/components/forms/fields/InputFieldWithIcon/_styles.scss b/frontend/components/forms/fields/InputFieldWithIcon/_styles.scss index ae943b6e6a..7c50d19c3d 100644 --- a/frontend/components/forms/fields/InputFieldWithIcon/_styles.scss +++ b/frontend/components/forms/fields/InputFieldWithIcon/_styles.scss @@ -42,6 +42,11 @@ outline: none; } + &:disabled { + color: $ui-fleet-black-50; + cursor: not-allowed; + } + &--error { color: $core-vibrant-red; border: 1px solid $core-vibrant-red; diff --git a/frontend/pages/UserSettingsPage/UserSettingsPage.jsx b/frontend/pages/UserSettingsPage/UserSettingsPage.jsx index 9306a70505..0640cc71bc 100644 --- a/frontend/pages/UserSettingsPage/UserSettingsPage.jsx +++ b/frontend/pages/UserSettingsPage/UserSettingsPage.jsx @@ -318,7 +318,7 @@ export class UserSettingsPage extends Component { renderPasswordModal, renderApiTokenModal, } = this; - const { version, errors, user } = this.props; + const { version, errors, user, config } = this.props; const { pendingEmail } = this.state; if (!user) { @@ -339,6 +339,7 @@ export class UserSettingsPage extends Component { onCancel={onCancel} pendingEmail={pendingEmail} serverErrors={errors} + smtpConfigured={config.configured} />
diff --git a/frontend/pages/UserSettingsPage/UserSettingsPage.tests.jsx b/frontend/pages/UserSettingsPage/UserSettingsPage.tests.jsx index f50de36f8a..2e454c06a9 100644 --- a/frontend/pages/UserSettingsPage/UserSettingsPage.tests.jsx +++ b/frontend/pages/UserSettingsPage/UserSettingsPage.tests.jsx @@ -29,10 +29,10 @@ describe("UserSettingsPage - component", () => { it("contains expected text", () => { const admin = { ...userStub, admin: true }; const pageWithUser = mount( - + ); const pageWithAdmin = mount( - + ); expect(pageWithUser.find(".user-settings__role").text()).toContain("User"); @@ -51,7 +51,7 @@ describe("UserSettingsPage - component", () => { jest.spyOn(authActions, "updateUser"); const dispatch = () => Promise.resolve(); - const props = { dispatch, user: userStub }; + const props = { dispatch, user: userStub, config: configStub }; const pageNode = mount().instance(); const updatedAttrs = { name: "Updated Name" }; const updatedUser = { ...userStub, ...updatedAttrs }; @@ -101,7 +101,7 @@ describe("UserSettingsPage - component", () => { }); it("displays pending email text when the user is pending an email change", () => { - const props = { dispatch: noop, user: userStub }; + const props = { dispatch: noop, user: userStub, config: configStub }; const Page = mount(); const UserSettingsForm = () => Page.find("UserSettingsForm"); const emailHint = () => diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPagePage/MembersPage.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPagePage/MembersPage.tsx index b1d99823b8..b7e16ab64e 100644 --- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPagePage/MembersPage.tsx +++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPagePage/MembersPage.tsx @@ -95,6 +95,10 @@ const MembersPage = (props: IMembersPageProps): JSX.Element => { return member.id; }); + const smtpConfigured = useSelector((state: IRootState) => { + return state.app.config.configured; + }); + const [showAddMemberModal, setShowAddMemberModal] = useState(false); const [showRemoveMemberModal, setShowRemoveMemberModal] = useState(false); const [showEditUserModal, setShowEditUserModal] = useState(false); @@ -265,6 +269,7 @@ const MembersPage = (props: IMembersPageProps): JSX.Element => { availableTeams={teams} validationErrors={[]} isBasicTier={isBasicTier} + smtpConfigured={smtpConfigured} /> ) : null} {showRemoveMemberModal ? ( diff --git a/frontend/pages/admin/UserManagementPage/UserManagementPage.jsx b/frontend/pages/admin/UserManagementPage/UserManagementPage.jsx index 79c7f29e83..cff67d860f 100644 --- a/frontend/pages/admin/UserManagementPage/UserManagementPage.jsx +++ b/frontend/pages/admin/UserManagementPage/UserManagementPage.jsx @@ -417,6 +417,7 @@ export class UserManagementPage extends Component { availableTeams={teams} submitText={"Save"} isBasicTier={isBasicTier} + smtpConfigured={config.configured} /> ); @@ -452,6 +453,7 @@ export class UserManagementPage extends Component { defaultTeams={[]} submitText={"Create"} isBasicTier={isBasicTier} + smtpConfigured={config.configured} /> ); diff --git a/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx b/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx index 47b588c62b..91283ed273 100644 --- a/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx +++ b/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx @@ -16,6 +16,7 @@ interface IEditUserModalProps { availableTeams: ITeam[]; validationErrors: any[]; isBasicTier: boolean; + smtpConfigured: boolean; } const baseClass = "edit-user-modal"; @@ -32,6 +33,7 @@ const EditUserModal = (props: IEditUserModalProps): JSX.Element => { availableTeams, isBasicTier, validationErrors, + smtpConfigured, } = props; return ( @@ -52,6 +54,7 @@ const EditUserModal = (props: IEditUserModalProps): JSX.Element => { availableTeams={availableTeams} submitText={"Save"} isBasicTier={isBasicTier} + smtpConfigured={smtpConfigured} /> ); diff --git a/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx b/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx index 98beb097c6..f51da07eb9 100644 --- a/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx +++ b/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx @@ -22,12 +22,12 @@ const baseClass = "selected-teams-form"; const roles = [ { disabled: false, - label: "observer", + label: "Observer", value: "observer", }, { disabled: false, - label: "maintainer", + label: "Maintainer", value: "maintainer", }, ]; diff --git a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx index ae4973258b..75d0207626 100644 --- a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx +++ b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx @@ -1,4 +1,5 @@ import React, { Component, FormEvent } from "react"; +import ReactTooltip from "react-tooltip"; import { ITeam } from "interfaces/team"; import Button from "components/buttons/Button"; @@ -65,6 +66,7 @@ interface ICreateUserFormProps { defaultTeams?: ITeam[]; isBasicTier: boolean; validationErrors: any[]; // TODO: proper interface for validationErrors + smtpConfigured: boolean; } interface ICreateUserFormState { @@ -263,7 +265,7 @@ class UserForm extends Component {

Users can be members of multiple teams and can only manage or - observe team-sepcific users, entities, and settings in Fleet. + observe team-specific users, entities, and settings in Fleet.

{ formData: { email, name, sso_enabled }, isGlobalUser, } = this.state; - const { onCancel, submitText, isBasicTier } = this.props; + const { onCancel, submitText, isBasicTier, smtpConfigured } = this.props; const { onFormSubmit, onInputChange, @@ -317,13 +319,38 @@ class UserForm extends Component { placeholder="Full name" value={name} /> - +
+ +
+ + + Editing an email address requires that SMTP is
+ configured in order to send a validation email.
+
+ Users with Admin role can configure SMTP in +
+ Settings > Organization settings. +
+