From b65bd0ff4407610ed88306d0f227f80a7b6db77f Mon Sep 17 00:00:00 2001 From: Michael Terry Date: Fri, 21 Aug 2020 12:49:12 -0400 Subject: [PATCH] AA-305: Update Masquerade Widget UI (#188) * AA-305: Update Masquerade Widget UI This will start showing the user being masqueraded as in the Toolbar and will show any error messages next to the Toolbar as well. * Further masquerade widget fixes Co-authored-by: Dillon Dumesnil --- src/instructor-toolbar/InstructorToolbar.jsx | 61 ++++++--- .../MasqueradeUserNameInput.jsx | 15 ++- .../masquerade-widget/MasqueradeWidget.jsx | 127 ++++++++---------- .../masquerade-widget/messages.js | 31 ++--- 4 files changed, 114 insertions(+), 120 deletions(-) diff --git a/src/instructor-toolbar/InstructorToolbar.jsx b/src/instructor-toolbar/InstructorToolbar.jsx index a4759e6a..42335157 100644 --- a/src/instructor-toolbar/InstructorToolbar.jsx +++ b/src/instructor-toolbar/InstructorToolbar.jsx @@ -1,8 +1,10 @@ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { getConfig } from '@edx/frontend-platform'; +import { ALERT_TYPES } from '../generic/user-messages'; +import Alert from '../generic/user-messages/Alert'; import MasqueradeWidget from './masquerade-widget'; function getInsightsUrl(courseId) { @@ -48,28 +50,47 @@ export default function InstructorToolbar(props) { return activeUnit ? activeUnit.lmsWebUrl : undefined; }); const urlStudio = getStudioUrl(courseId, unitId); + const [masqueradeErrorMessage, showMasqueradeError] = useState(null); return ( -
-
-
- +
+
+
+
+ +
+ {(urlLms || urlStudio || urlInsights) && ( + <> +
+ View course in: + + )} + {urlLms && ( + + Existing experience + + )} + {urlStudio && ( + + Studio + + )} + {urlInsights && ( + + Insights + + )}
- {urlLms && ( - - )} - {urlStudio && ( - - )} - {urlInsights && ( - - )}
+ {masqueradeErrorMessage && ( +
+ + {masqueradeErrorMessage} + +
+ )}
); } diff --git a/src/instructor-toolbar/masquerade-widget/MasqueradeUserNameInput.jsx b/src/instructor-toolbar/masquerade-widget/MasqueradeUserNameInput.jsx index d9fc3e75..5b65b232 100644 --- a/src/instructor-toolbar/masquerade-widget/MasqueradeUserNameInput.jsx +++ b/src/instructor-toolbar/masquerade-widget/MasqueradeUserNameInput.jsx @@ -32,22 +32,25 @@ class MasqueradeUserNameInput extends Component { this.onError(error); } }).catch(() => { - const message = this.props.intl.formatMessage(messages['userName.error.generic']); + const message = this.props.intl.formatMessage(messages.genericError); this.onError(message); }); return true; } render() { + const { + intl, + onError, + onSubmit, + ...rest + } = this.props; return ( this.onKeyPress(event)} - placeholder={this.props.intl.formatMessage(messages['userName.input.placeholder'])} type="text" + {...rest} /> ); } diff --git a/src/instructor-toolbar/masquerade-widget/MasqueradeWidget.jsx b/src/instructor-toolbar/masquerade-widget/MasqueradeWidget.jsx index a1349820..0de94cbc 100644 --- a/src/instructor-toolbar/masquerade-widget/MasqueradeWidget.jsx +++ b/src/instructor-toolbar/masquerade-widget/MasqueradeWidget.jsx @@ -5,10 +5,7 @@ import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Dropdown } from '@edx/paragon'; -import { - ALERT_TYPES, - UserMessagesContext, -} from '../../generic/user-messages'; +import { UserMessagesContext } from '../../generic/user-messages'; import MasqueradeUserNameInput from './MasqueradeUserNameInput'; import MasqueradeWidgetOption from './MasqueradeWidgetOption'; @@ -23,8 +20,11 @@ class MasqueradeWidget extends Component { super(props); this.courseId = props.courseId; this.state = { + autoFocus: false, + masquerade: 'Staff', options: [], shouldShowUserNameInput: false, + masqueradeUsername: null, }; } @@ -48,18 +48,11 @@ class MasqueradeWidget extends Component { } onError(message) { - if (message) { - this.errorAlertId = this.context.add({ - text: message, - topic: 'course', - type: ALERT_TYPES.ERROR, - dismissible: false, - }); - } + this.props.onError(message); } async onSubmit(payload) { - this.context.remove(this.errorAlertId); + this.clearError(); const options = await postMasqueradeOptions(this.courseId, payload); return options; } @@ -69,47 +62,18 @@ class MasqueradeWidget extends Component { this.setState({ options, }); - const active = data.active || {}; - const message = this.getStatusMessage(active); - if (message) { - this.context.add({ - text: message, - topic: 'course', - type: ALERT_TYPES.INFO, - dismissible: false, - }); - } } - getStatusMessage(active) { - const { - groupName, - } = active; - let message = ''; - if (active.userName) { - message = this.props.intl.formatMessage(messages['status.userName'], { - userName: active.userName, - }); - } else if (groupName) { - message = this.props.intl.formatMessage(messages['status.groupName'], { - groupName, - }); - } else if (active.role === 'student') { - message = this.props.intl.formatMessage(messages['status.learner']); - } - return message; + clearError() { + this.props.onError(''); } toggle(show) { - let shouldShow; - if (show === undefined) { - shouldShow = !this.state.shouldShowUserNameInput; - } else { - shouldShow = show; - } - this.setState({ - shouldShowUserNameInput: shouldShow, - }); + this.setState(prevState => ({ + autoFocus: true, + masquerade: 'Specific Student...', + shouldShowUserNameInput: show === undefined ? !prevState.shouldShowUserNameInput : show, + })); } parseAvailableOptions(postData) { @@ -125,47 +89,68 @@ class MasqueradeWidget extends Component { selected={active} userName={group.userName} userPartitionId={group.userPartitionId} - userNameInput={this.userNameInput} userNameInputToggle={(...args) => this.toggle(...args)} onSubmit={(payload) => this.onSubmit(payload)} /> )); + if (active.userName) { + this.setState({ + autoFocus: false, + masquerade: 'Specific Student...', + masqueradeUsername: active.userName, + shouldShowUserNameInput: true, + }); + } else if (active.groupName) { + this.setState({ masquerade: active.groupName }); + } else if (active.role === 'student') { + this.setState({ masquerade: 'Learner' }); + } return options; } render() { const { + autoFocus, + masquerade, options, + shouldShowUserNameInput, + masqueradeUsername, } = this.state; + const specificLearnerInputText = this.props.intl.formatMessage(messages.placeholder); return ( - <> - - - View this course as - - - {options} - - - {this.state.shouldShowUserNameInput && ( - this.onError(errorMessage)} - onSubmit={(payload) => this.onSubmit(payload)} - ref={(input) => { this.userNameInput = input; }} - /> +
+
+ View this course as: + + + {masquerade} + + + {options} + + +
+ {shouldShowUserNameInput && ( +
+ {`${specificLearnerInputText}:`} + this.onError(errorMessage)} + onSubmit={(payload) => this.onSubmit(payload)} + /> +
)} - +
); } } MasqueradeWidget.propTypes = { courseId: PropTypes.string.isRequired, intl: intlShape.isRequired, + onError: PropTypes.func.isRequired, }; MasqueradeWidget.contextType = UserMessagesContext; export default injectIntl(MasqueradeWidget); diff --git a/src/instructor-toolbar/masquerade-widget/messages.js b/src/instructor-toolbar/masquerade-widget/messages.js index ca6358e7..4621534c 100644 --- a/src/instructor-toolbar/masquerade-widget/messages.js +++ b/src/instructor-toolbar/masquerade-widget/messages.js @@ -1,36 +1,21 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ - 'status.groupName': { - id: 'masquerade-widget.status.groupName', - defaultMessage: 'You are masquerading as a learner in the {groupName} group.', - description: 'Message when masquerading as a generic user in a specific track', - }, - 'status.learner': { - id: 'masquerade-widget.status.learner', - defaultMessage: 'You are masquerading as a learner.', - description: 'Message when masquerading as a specific user', - }, - 'status.userName': { - id: 'masquerade-widget.status.userName', - defaultMessage: 'You are masquerading as the following user: {userName}', - description: 'Message when masquerading as a specific user', - }, - 'userName.input.label': { - id: 'masquerade-widget.userName.input.label', - defaultMessage: 'Masquerade as this user', - description: 'Label for the masquerade user input', - }, - 'userName.error.generic': { + genericError: { id: 'masquerade-widget.userName.error.generic', defaultMessage: 'An error has occurred; please try again.', description: 'Message shown after a general error when attempting to masquerade', }, - 'userName.input.placeholder': { + placeholder: { id: 'masquerade-widget.userName.input.placeholder', - defaultMessage: 'username or email', + defaultMessage: 'Username or email', description: 'Placeholder text to prompt for a user to masquerade as', }, + userNameLabel: { + id: 'masquerade-widget.userName.input.label', + defaultMessage: 'Masquerade as this user', + description: 'Label for the masquerade user input', + }, }); export default messages;