improved focus management and help text (#32)

This commit is contained in:
alangsto
2020-08-10 10:27:21 -04:00
committed by GitHub
parent 9a5542c1ed
commit bb4ab0368f
2 changed files with 48 additions and 6 deletions

View File

@@ -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)}
>
<FormattedMessage
@@ -463,6 +465,8 @@ function ExamSettings(props) {
variant="danger"
dismissible
data-test-id="saveError"
tabIndex="-1"
ref={saveStatusAlertRef}
onClose={() => setSaveError(false)}
>
<FormattedMessage
@@ -520,10 +524,13 @@ function ExamSettings(props) {
);
useEffect(() => {
if ((saveSuccess || saveError) && !!saveStatusAlertRef.current) {
saveStatusAlertRef.current.focus();
}
if (!formStatus.isValid && !!alertRef.current) {
alertRef.current.focus();
}
}, [formStatus]);
}, [formStatus, saveSuccess, saveError]);
return (
<div className="container">

View File

@@ -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(<IntlProctoredExamSettings {...defaultProps} />)));
// 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(<IntlProctoredExamSettings {...defaultProps} />)));
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(() => {