From bb4ab0368fa7e00174db039b15bf30f5a14c2dec Mon Sep 17 00:00:00 2001 From: alangsto <46360176+alangsto@users.noreply.github.com> Date: Mon, 10 Aug 2020 10:27:21 -0400 Subject: [PATCH] improved focus management and help text (#32) --- .../ProctoredExamSettings.jsx | 13 ++++-- .../ProctoredExamSettings.test.jsx | 41 +++++++++++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/proctored-exam-settings/ProctoredExamSettings.jsx b/src/proctored-exam-settings/ProctoredExamSettings.jsx index bd685db33..4096826f8 100644 --- a/src/proctored-exam-settings/ProctoredExamSettings.jsx +++ b/src/proctored-exam-settings/ProctoredExamSettings.jsx @@ -38,6 +38,7 @@ function ExamSettings(props) { }); const alertRef = React.createRef(); + const saveStatusAlertRef = React.createRef(); const proctoringEscalationEmailInputRef = useRef(null); function onEnableProctoredExamsChange(event) { @@ -260,8 +261,7 @@ function ExamSettings(props) { id="authoring.examsettings.allowoptout.help" defaultMessage={` If this value is "Yes", learners can choose to take proctored exams without proctoring. - If this value is "No", all learners must take the exam with proctoring. - This setting only applies if proctored exams are enabled for the course. + If this value is "No", all learners must take the exam with proctoring. `} description="Help text for proctored exam opt out radio selection" /> @@ -443,6 +443,8 @@ function ExamSettings(props) { variant="success" dismissible data-test-id="saveSuccess" + tabIndex="-1" + ref={saveStatusAlertRef} onClose={() => setSaveSuccess(false)} > setSaveError(false)} > { + if ((saveSuccess || saveError) && !!saveStatusAlertRef.current) { + saveStatusAlertRef.current.focus(); + } if (!formStatus.isValid && !!alertRef.current) { alertRef.current.focus(); } - }, [formStatus]); + }, [formStatus, saveSuccess, saveError]); return (
diff --git a/src/proctored-exam-settings/ProctoredExamSettings.test.jsx b/src/proctored-exam-settings/ProctoredExamSettings.test.jsx index 961cee8a2..69b19f4dd 100644 --- a/src/proctored-exam-settings/ProctoredExamSettings.test.jsx +++ b/src/proctored-exam-settings/ProctoredExamSettings.test.jsx @@ -234,9 +234,14 @@ describe('ProctoredExamSettings validation with invalid escalation email', () => fireEvent.click(selectButton); }); - // verify there is no alert and focus is maintained on the button + // verify there is no escalation email alert, and focus has been set on save success alert expect(screen.queryByTestId('proctortrackEscalationEmailError')).toBeNull(); - expect(document.activeElement).toEqual(selectButton); + + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); }); it('Escalation Email field hidden when proctoring backend is not Proctortrack', async () => { @@ -526,6 +531,7 @@ describe('ProctoredExamSettings save settings tests', () => { expect(errorAlert.textContent).toEqual( expect.stringContaining('Proctored exam settings saved successfully.'), ); + expect(document.activeElement).toEqual(errorAlert); }); it('Makes API call successfully without proctoring_escalation_email if not proctortrack', async () => { @@ -555,12 +561,12 @@ describe('ProctoredExamSettings save settings tests', () => { expect(errorAlert.textContent).toEqual( expect.stringContaining('Proctored exam settings saved successfully.'), ); + expect(document.activeElement).toEqual(errorAlert); }); it('Makes API call generated error', async () => { const mockedFunctions = mockAPI(mockGetData, false); await act(async () => render(intlWrapper())); - // Make a change to the provider to proctortrack and set the email const submitButton = screen.getByTestId('submissionButton'); await act(async () => { fireEvent.click(submitButton); @@ -570,6 +576,35 @@ describe('ProctoredExamSettings save settings tests', () => { expect(errorAlert.textContent).toEqual( expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), ); + expect(document.activeElement).toEqual(errorAlert); + }); + + it('Manages focus correctly after different save statuses', async () => { + // first make a call that will cause a save error + const mockedFunctionsBadCall = mockAPI(mockGetData, false); + await act(async () => render(intlWrapper())); + const submitButton = screen.getByTestId('submissionButton'); + await act(async () => { + fireEvent.click(submitButton); + }); + expect(mockedFunctionsBadCall.mockClientPost).toHaveBeenCalled(); + const errorAlert = screen.getByTestId('saveError'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), + ); + expect(document.activeElement).toEqual(errorAlert); + + // now make a call that will allow for a successful save + const mockedFunctionsGoodCall = mockAPI(mockGetData, { data: 'success' }); + await act(async () => { + fireEvent.click(submitButton); + }); + expect(mockedFunctionsGoodCall.mockClientPost).toHaveBeenCalled(); + const successAlert = screen.getByTestId('saveSuccess'); + expect(successAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(successAlert); }); afterEach(() => {