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
This commit is contained in:
RachelElysia
2021-07-06 12:22:51 -04:00
committed by GitHub
parent 498209614c
commit 0834af5bb7
12 changed files with 98 additions and 24 deletions
@@ -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 (
<form onSubmit={handleSubmit} className={baseClass}>
<InputField
{...fields.email}
autofocus
label="Email (required)"
hint={renderEmailHint()}
/>
<div
className="smtp-not-configured"
data-tip
data-for="smtp-tooltip"
data-tip-disable={smtpConfigured}
>
<InputField
{...fields.email}
autofocus
label="Email (required)"
hint={renderEmailHint()}
disabled={!smtpConfigured}
/>
</div>
<ReactTooltip
place="bottom"
type="dark"
effect="solid"
id="smtp-tooltip"
backgroundColor="#3e4771"
data-html
>
<span className={`${baseClass}__tooltip-text`}>
Editing your email address requires that SMTP is <br />
configured in order to send a validation email. <br />
<br />
Users with Admin role can configure SMTP in
<br />
<strong>Settings &gt; Organization settings</strong>.
</span>
</ReactTooltip>
<InputField {...fields.name} label="Full name (required)" />
<InputField {...fields.position} label="Position" />
<div className={`${baseClass}__button-wrap`}>
@@ -89,7 +89,6 @@
.Select-value-label {
font-size: $x-small;
font-weight: $bold;
color: $core-dark-blue-grey;
line-height: 28px;
}
@@ -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 && <FleetIcon name={iconName} className={iconClasses} />}
{renderHint()}
@@ -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;
@@ -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}
/>
</div>
<div className={`${baseClass}__additional body-wrap`}>
@@ -29,10 +29,10 @@ describe("UserSettingsPage - component", () => {
it("contains expected text", () => {
const admin = { ...userStub, admin: true };
const pageWithUser = mount(
<UserSettingsPage dispatch={noop} user={userStub} />
<UserSettingsPage dispatch={noop} user={userStub} config={configStub} />
);
const pageWithAdmin = mount(
<UserSettingsPage dispatch={noop} user={admin} />
<UserSettingsPage dispatch={noop} user={admin} config={configStub} />
);
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(<UserSettingsPage {...props} />).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(<UserSettingsPage {...props} />);
const UserSettingsForm = () => Page.find("UserSettingsForm");
const emailHint = () =>
@@ -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 ? (
@@ -417,6 +417,7 @@ export class UserManagementPage extends Component {
availableTeams={teams}
submitText={"Save"}
isBasicTier={isBasicTier}
smtpConfigured={config.configured}
/>
</Modal>
);
@@ -452,6 +453,7 @@ export class UserManagementPage extends Component {
defaultTeams={[]}
submitText={"Create"}
isBasicTier={isBasicTier}
smtpConfigured={config.configured}
/>
</Modal>
);
@@ -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}
/>
</Modal>
);
@@ -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",
},
];
@@ -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<ICreateUserFormProps, ICreateUserFormState> {
<InfoBanner className={`${baseClass}__user-permissions-info`}>
<p>
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.
</p>
<a
href="https://github.com/fleetdm/fleet/blob/2f42c281f98e39a72ab4a5125ecd26d303a16a6b/docs/1-Using-Fleet/9-Permissions.md#team-member-permissions"
@@ -289,7 +291,7 @@ class UserForm extends Component<ICreateUserFormProps, ICreateUserFormState> {
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<ICreateUserFormProps, ICreateUserFormState> {
placeholder="Full name"
value={name}
/>
<InputFieldWithIcon
error={errors.email}
name="email"
onChange={onInputChange("email")}
placeholder="Email"
value={email}
/>
<div
className="smtp-not-configured"
data-tip
data-for="smtp-tooltip"
data-tip-disable={smtpConfigured}
>
<InputFieldWithIcon
error={errors.email}
name="email"
onChange={onInputChange("email")}
placeholder="Email"
value={email}
disabled={!smtpConfigured}
/>
</div>
<ReactTooltip
place="bottom"
type="dark"
effect="solid"
id="smtp-tooltip"
backgroundColor="#3e4771"
data-html
>
<span className={`${baseClass}__tooltip-text`}>
Editing an email address requires that SMTP is <br />
configured in order to send a validation email. <br />
<br />
Users with Admin role can configure SMTP in
<br />
<strong>Settings &gt; Organization settings</strong>.
</span>
</ReactTooltip>
<div className={`${baseClass}__sso-input`}>
<Checkbox
name="sso_enabled"
@@ -69,4 +69,7 @@
.sublabel {
margin: 0px;
}
&__tooltip-text {
width: 300px;
}
}