fix: [AA-1087] defer goals widget for proctoring (#727)

fix: [AA-1087] defer goals widget for proctoring

- pause showing goals widget while proctor info panel awaits callback.

- Implement tests to check that goals show up when
  proctoring does not

- small refactoring of LearningGoalButton to eliminate test warnings
This commit is contained in:
Chris Deery
2021-11-09 10:22:20 -05:00
committed by GitHub
parent c19f21d257
commit 9c2c1427e1
7 changed files with 75 additions and 21 deletions

View File

@@ -453,6 +453,7 @@ Object {
"datesWidget": Object {
"courseDateBlocks": Array [],
},
"enableProctoredExams": undefined,
"enrollAlert": Object {
"canEnroll": true,
"extraText": "Contact the administrator.",

View File

@@ -343,6 +343,7 @@ export async function getOutlineTabData(courseId) {
const courseTools = camelCaseObject(data.course_tools);
const datesBannerInfo = camelCaseObject(data.dates_banner_info);
const datesWidget = camelCaseObject(data.dates_widget);
const enableProctoredExams = data.enable_proctored_exams;
const enrollAlert = camelCaseObject(data.enroll_alert);
const enrollmentMode = data.enrollment_mode;
const handoutsHtml = data.handouts_html;
@@ -366,6 +367,7 @@ export async function getOutlineTabData(courseId) {
datesWidget,
enrollAlert,
enrollmentMode,
enableProctoredExams,
handoutsHtml,
hasScheduledContent,
hasEnded,

View File

@@ -62,10 +62,11 @@ function OutlineTab({ intl }) {
datesWidget: {
courseDateBlocks,
},
enableProctoredExams,
offer,
resumeCourse: {
url: resumeCourseUrl,
},
offer,
timeOffsetMillis,
verifiedMode,
} = useModel('outline', courseId);
@@ -73,6 +74,9 @@ function OutlineTab({ intl }) {
const [deprecatedCourseGoalToDisplay, setDeprecatedCourseGoalToDisplay] = useState(selectedGoal);
const [goalToastHeader, setGoalToastHeader] = useState('');
const [expandAll, setExpandAll] = useState(false);
// Defer showing the goal widget until the ProctoringInfoPanel is either shown or determined as not showing
// to avoid components bouncing around too much as screen is displayed
const [proctorPanelResolved, setProctorPanelResolved] = useState(!enableProctoredExams);
const eventProperties = {
org_key: org,
@@ -200,6 +204,7 @@ function OutlineTab({ intl }) {
<ProctoringInfoPanel
courseId={courseId}
username={username}
isResolved={() => setProctorPanelResolved(true)}
/>
{deprecatedCourseGoalToDisplay && goalOptions && goalOptions.length > 0 && (
<UpdateGoalSelector
@@ -210,7 +215,7 @@ function OutlineTab({ intl }) {
setGoalToastHeader={(newHeader) => { setGoalToastHeader(newHeader); }}
/>
)}
{weeklyLearningGoalEnabled && (
{proctorPanelResolved && weeklyLearningGoalEnabled && (
<WeeklyLearningGoalCard
daysPerWeek={selectedGoal && 'daysPerWeek' in selectedGoal ? selectedGoal.daysPerWeek : null}
subscribedToReminders={selectedGoal && 'subscribedToReminders' in selectedGoal ? selectedGoal.subscribedToReminders : false}

View File

@@ -513,19 +513,55 @@ describe('Outline Tab', () => {
expect(screen.queryByText(messages.goalReminderDetail.defaultMessage)).not.toBeInTheDocument();
});
});
describe('weekly learning goal is already set', () => {
beforeEach(async () => {
setTabData({
course_goals: {
weekly_learning_goal_enabled: true,
selected_goal: {
subscribed_to_reminders: true,
days_per_week: 3,
},
it('has button for weekly learning goal selected', async () => {
setTabData({
course_goals: {
weekly_learning_goal_enabled: true,
selected_goal: {
subscribed_to_reminders: true,
days_per_week: 3,
},
});
await fetchAndRender();
},
});
await fetchAndRender();
const button = await screen.queryByTestId('weekly-learning-goal-input-Regular');
expect(button).toBeInTheDocument();
expect(button).toHaveClass('flag-button-selected');
});
it('renders weekly learning goal card if ProctoringInfoPanel is not shown', async () => {
setTabData({
course_goals: {
weekly_learning_goal_enabled: true,
},
});
axiosMock.onGet(proctoringInfoUrl).reply(404);
await fetchAndRender();
expect(screen.queryByTestId('weekly-learning-goal-card')).toBeInTheDocument();
});
it('renders weekly learning goal card if ProctoringInfoPanel is not enabled', async () => {
setTabData({
course_goals: {
weekly_learning_goal_enabled: true,
enableProctoredExams: false,
},
});
await fetchAndRender();
expect(screen.queryByTestId('weekly-learning-goal-card')).toBeInTheDocument();
});
it('renders weekly learning goal card if ProctoringInfoPanel is enabled', async () => {
setTabData({
course_goals: {
weekly_learning_goal_enabled: true,
enableProctoredExams: true,
},
});
await fetchAndRender();
expect(screen.queryByTestId('weekly-learning-goal-card')).toBeInTheDocument();
});
});
@@ -1172,6 +1208,7 @@ describe('Outline Tab', () => {
it('does not appear for 404', async () => {
axiosMock.onGet(proctoringInfoUrl).reply(404);
await fetchAndRender();
expect(screen.queryByRole('link', { name: 'Review instructions and system requirements' })).not.toBeInTheDocument();
});

View File

@@ -10,7 +10,7 @@ import messages from '../messages';
function LearningGoalButton({
level,
currentGoal,
isSelected,
handleSelect,
intl,
}) {
@@ -43,14 +43,14 @@ function LearningGoalButton({
title={intl.formatMessage(values.title)}
text={intl.formatMessage(values.text)}
handleSelect={() => handleSelect(values.daysPerWeek)}
isSelected={currentGoal === values.daysPerWeek}
isSelected={isSelected}
/>
);
}
LearningGoalButton.propTypes = {
level: PropTypes.string.isRequired,
currentGoal: PropTypes.number.isRequired,
isSelected: PropTypes.bool.isRequired,
handleSelect: PropTypes.func.isRequired,
intl: intlShape.isRequired,
};

View File

@@ -8,7 +8,9 @@ import { Button } from '@edx/paragon';
import messages from '../messages';
import { getProctoringInfoData } from '../../data/api';
function ProctoringInfoPanel({ courseId, username, intl }) {
function ProctoringInfoPanel({
courseId, username, intl, isResolved,
}) {
const [link, setLink] = useState('');
const [onboardingPastDue, setOnboardingPastDue] = useState(false);
const [showInfoPanel, setShowInfoPanel] = useState(false);
@@ -95,7 +97,13 @@ function ProctoringInfoPanel({ courseId, username, intl }) {
setOnboardingPastDue(response.onboarding_past_due);
}
},
);
)
.catch(() => {
/* Do nothing. API throws 404 when class does not have proctoring */
})
.finally(() => {
isResolved();
});
}, []);
let onboardingExamButton = null;
@@ -186,6 +194,7 @@ ProctoringInfoPanel.propTypes = {
courseId: PropTypes.string.isRequired,
username: PropTypes.string,
intl: intlShape.isRequired,
isResolved: PropTypes.func.isRequired,
};
ProctoringInfoPanel.defaultProps = {

View File

@@ -63,17 +63,17 @@ function WeeklyLearningGoalCard({
>
<LearningGoalButton
level="casual"
currentGoal={daysPerWeekGoal}
isSelected={daysPerWeekGoal === 1}
handleSelect={handleSelect}
/>
<LearningGoalButton
level="regular"
currentGoal={daysPerWeekGoal}
isSelected={daysPerWeekGoal === 3}
handleSelect={handleSelect}
/>
<LearningGoalButton
level="intense"
currentGoal={daysPerWeekGoal}
isSelected={daysPerWeekGoal === 5}
handleSelect={handleSelect}
/>
</div>