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:
@@ -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 > 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 = () =>
|
||||
|
||||
+5
@@ -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>
|
||||
);
|
||||
|
||||
+2
-2
@@ -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 > 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user