Skip to content

Commit

Permalink
Added AdminCredentials component & fixed TextInput component clear state
Browse files Browse the repository at this point in the history
Implemented the AdminCredentials component and copied its ErrorResponse component styling to the Login component, which appropriately displays an "Invalid Credentials" error message outside of the relevant TextInput components group.  Also, added a SetNativeValue utility function to dispatch "change" events on TextInput components when their values are cleared by updates to their state.  For performance reasons React does not dispatch onChange or onInput events when a value of an HTMLElement is updated by state (RE: facebook/react#10135).
  • Loading branch information
gmattie committed Feb 26, 2020
1 parent bfec713 commit 24d0d88
Show file tree
Hide file tree
Showing 25 changed files with 442 additions and 165 deletions.
142 changes: 142 additions & 0 deletions client/src/components/AdminCredentials.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* @description AdminCredentials component
*
* @requires Collapsible
* @requires constants
* @requires ErrorResponse
* @requires InputPassword
* @requires InputText
* @requires prop-types
* @requires react
* @requires useInputText
* @requires utilities
* @public
* @module
*
*/
import { concatClassNames } from "../support/utilities";
import * as C from "../support/constants";
import Collapsible from "./Collapsible";
import ErrorResponse from "./ErrorResponse";
import InputPassword from "./InputPassword";
import InputText from "./InputText";
import PropTypes from "prop-types";
import React, { useEffect, useRef } from "react";

/**
* @description The AdminCredentials component contains UI elements that are required to enter admin credentials.
*
* @param {object} props - Immutable properties populated by the parent component.
* @returns {object} JSX markup.
* @public
* @function
*
*/
const AdminCredentials = ({

bindAdminUsername,
bindAdminPassword,
isLoading,
errorMessage
}) => {

/**
* Refs
*
*/
const showErrorMessage = useRef(true);

/**
* @description Reset the "showErrorMessage" reference when the "errorMessage" prop is updated.
*
* @private
* @function
*
*/
useEffect(() => {

showErrorMessage.current = true;
}, [errorMessage]);

/**
* @description Callback for a dispatched "change" event for either the "adminUsername" or "adminPassword" HTMLInputElement.
* Intercepts the target's "onChange" binding from its UseInputText hook.
*
* @param {object} target - The object that dispatched the event.
* @param {object} event - The event object.
*
* @private
* @function
*
*/
const adminCredentialsChangeHandler = (target, event) => {

if (showErrorMessage.current) {

showErrorMessage.current = false;
}

target.onChange(event);
};

/**
* JSX markup
*
*/
return (

<Collapsible
title={`${C.Label.ADMIN_CREDENTIALS} ${C.Label.OPTIONAL}`}
headerStyle={C.Style.COLLAPSIBLE_HEADER_SECTION}
>
<div className={C.Style.ADMIN_CREDENTIALS}>
<div className={
concatClassNames(
C.Style.ADMIN_CREDENTIALS_ERROR,
(errorMessage && showErrorMessage.current && C.Style.ADMIN_CREDENTIALS_ERROR_SHOW)
)
}>
{errorMessage &&
<ErrorResponse message={errorMessage} />
}
</div>

<div className={C.Style.ADMIN_CREDENTIALS_USERNAME}>
<InputText
name={C.ID.NAME_ADMIN_USERNAME}
disabled={isLoading}
{...bindAdminUsername}
onChange={adminCredentialsChangeHandler.bind(null, bindAdminUsername)}
/>
</div>

<div className={C.Style.ADMIN_CREDENTIALS_PASSWORD}>
<InputPassword
name={C.ID.NAME_ADMIN_PASSWORD}
disabled={isLoading}
{...bindAdminPassword}
onChange={adminCredentialsChangeHandler.bind(null, bindAdminPassword)}
/>
</div>
</div>
</Collapsible>
);
};

/**
* Prop Types
*
*/
AdminCredentials.propTypes = {

bindAdminUsername: PropTypes.object.isRequired,
bindAdminPassword: PropTypes.object.isRequired,
isLoading: PropTypes.bool.isRequired,
errorMessage: PropTypes.string,
};

/**
* Export module
*
*/
export default AdminCredentials;
38 changes: 35 additions & 3 deletions client/src/components/InputPassword.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @requires EyeSlash.svg
* @requires InputText
* @requires react
* @requires prop-types
* @public
* @module
*
Expand All @@ -15,6 +16,7 @@ import Eye from "../assets/Eye.svg";
import EyeSlash from "../assets/EyeSlash.svg";
import InputText from "./InputText";
import React, { memo, useState } from "react";
import PropTypes from "prop-types";

/**
* @description The InputPassword component extends the functionality of InputText component with an additional button for toggling between plain and obscured text.
Expand All @@ -26,7 +28,15 @@ import React, { memo, useState } from "react";
* @function
*
*/
const InputPassword = (props) => {
const InputPassword = ({

placeholder,
errorMessage,
value,
disabled,
onChange,
onKeyPress,
}) => {

/**
* State
Expand All @@ -52,6 +62,7 @@ const InputPassword = (props) => {
* Preventing default behavior on the button ensures that it will not intercept focus from the input element.
*
* @param {object} event - The event object.
*
*/
const buttonMouseDownHandler = (event) => {

Expand All @@ -69,24 +80,45 @@ const InputPassword = (props) => {
type={(isVisible)
? C.HTMLElement.InputType.TEXT
: C.HTMLElement.InputType.PASSWORD}
{...props}
placeholder={placeholder}
errorMessage={errorMessage}
value={value}
disabled={disabled}
onChange={onChange}
onKeyPress={onKeyPress}
/>

<img
<input
className={C.Style.INPUT_PASSWORD_BUTTON}
type={C.HTMLElement.InputType.IMAGE}
src={(isVisible)
? Eye
: EyeSlash}
alt={(isVisible)
? C.Label.HIDE
: C.Label.SHOW}
disabled={disabled}
onClick={toggleInputTypeHandler}
onMouseDown={buttonMouseDownHandler}
/>
</div>
);
};

/**
* Prop Types
*
*/
InputPassword.propTypes = {

placeholder: PropTypes.string,
errorMessage: PropTypes.string,
value: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func,
onKeyPress: PropTypes.func,
};

/**
* Export module
*
Expand Down
3 changes: 3 additions & 0 deletions client/src/components/InputText.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const InputText = ({
placeholder,
errorMessage,
value,
disabled,
onChange,
onKeyPress,
}) => {
Expand Down Expand Up @@ -104,6 +105,7 @@ const InputText = ({
}
type={type || C.HTMLElement.InputType.TEXT}
value={value}
disabled={disabled}
onChange={inputChangeHandler}
onKeyPress={onKeyPress}
onFocus={() => setIsFocused(true)}
Expand Down Expand Up @@ -149,6 +151,7 @@ InputText.propTypes = {
placeholder: PropTypes.string,
errorMessage: PropTypes.string,
value: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func,
onKeyPress: PropTypes.func,
};
Expand Down
42 changes: 11 additions & 31 deletions client/src/components/route/protected/Edit.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/**
* @description Edit component
*
* @requires Collapsible
* @requires AdminCredentials
* @requires constants
* @requires Dialog
* @requires ErrorResponse
* @requires InputPassword
* @requires InputText
* @requires prop-types
Expand All @@ -17,9 +16,8 @@
*
*/
import * as C from "../../../support/constants";
import Collapsible from "../../Collapsible";
import AdminCredentials from "../../AdminCredentials";
import Dialog from "../../modal/Dialog";
import ErrorResponse from "../../ErrorResponse";
import InputPassword from "../../InputPassword";
import InputText from "../../InputText";
import PropTypes from "prop-types";
Expand Down Expand Up @@ -110,7 +108,7 @@ const Edit = ({ logout }) => {

/**
* Set isEditable flag
* Determines if the present state of text field data is sufficient for submitting to the server.
* Determines if the present state of text data is sufficient for submitting to the server.
*
*/
isEditable.current = (
Expand Down Expand Up @@ -266,6 +264,8 @@ const Edit = ({ logout }) => {
clearPasswordConfirm();
clearAdminUsername();
clearAdminPassword();

setInvalidAdminCredentials(null);
}

/**
Expand Down Expand Up @@ -330,32 +330,12 @@ const Edit = ({ logout }) => {

{!usersSelf[C.Model.USER][C.Model.ADMIN] &&
<div className={C.Style.EDIT_ADMIN}>
{invalidAdminCredentials &&
<div className={C.Style.EDIT_ADMIN_ERROR}>
<ErrorResponse message={invalidAdminCredentials} />
</div>
}

<Collapsible
title={`${C.Label.ADMIN_CREDENTIALS} ${C.Label.OPTIONAL}`}
headerStyle={C.Style.COLLAPSIBLE_HEADER_SECTION}
>
<div className={C.Style.EDIT_ADMIN_USERNAME}>
<InputText
name={C.ID.NAME_ADMIN_USERNAME}
disabled={isLoading}
{...bindAdminUsername}
/>
</div>

<div className={C.Style.EDIT_ADMIN_PASSWORD}>
<InputPassword
name={C.ID.NAME_ADMIN_PASSWORD}
disabled={isLoading}
{...bindAdminPassword}
/>
</div>
</Collapsible>
<AdminCredentials
bindAdminUsername={bindAdminUsername}
bindAdminPassword={bindAdminPassword}
isLoading={isLoading}
errorMessage={invalidAdminCredentials}
/>
</div>
}

Expand Down
2 changes: 1 addition & 1 deletion client/src/components/route/protected/admin/AddItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const AddItem = ({ logout }) => {

/**
* Set isSubmittable flag
* Determines if the present state of text field data is sufficient for submitting to the server.
* Determines if the present state of text data is sufficient for submitting to the server.
*
*/
isSubmittable.current = name;
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/route/protected/admin/ManagePolls.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const ManagePolls = ({ logout }) => {

/**
* Set isSubmittable flag
* Determines if the present state of text field data is sufficient for submitting to the server.
* Determines if the present state of text data is sufficient for submitting to the server.
*
*/
isSubmittable.current = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const EditItem = ({

/**
* Set isEditable flag
* Determines if the present state of text field data is sufficient for submitting to the server.
* Determines if the present state of text data is sufficient for submitting to the server.
*
*/
isEditable.current = (
Expand Down

0 comments on commit 24d0d88

Please sign in to comment.