feat: add accessibility toggle

Users can now press ctrl/cmd+f1 to toggle the screen reader mode, in
addition to using the accessibility tooltip.  The mode now persists
between challenges.

If screen reader mode is on, the instructions are focused by default so
the user is not required to navigate to them before reading starts.
This commit is contained in:
Oliver Eyton-Williams
2019-11-14 09:18:08 +01:00
committed by mrugesh
parent 265e383705
commit c5d4bedda5
2 changed files with 48 additions and 3 deletions

View File

@ -6,7 +6,9 @@ import { createSelector } from 'reselect';
import {
canFocusEditorSelector,
executeChallenge,
inAccessibilityModeSelector,
setEditorFocusability,
setAccessibilityMode,
updateFile
} from '../redux';
import { userSelector, isDonationModalOpenSelector } from '../../../redux';
@ -22,6 +24,8 @@ const propTypes = {
executeChallenge: PropTypes.func.isRequired,
ext: PropTypes.string,
fileKey: PropTypes.string,
inAccessibilityMode: PropTypes.bool.isRequired,
setAccessibilityMode: PropTypes.func.isRequired,
setEditorFocusability: PropTypes.func,
theme: PropTypes.string,
updateFile: PropTypes.func.isRequired
@ -29,16 +33,19 @@ const propTypes = {
const mapStateToProps = createSelector(
canFocusEditorSelector,
inAccessibilityModeSelector,
isDonationModalOpenSelector,
userSelector,
(canFocus, open, { theme = 'default' }) => ({
(canFocus, accessibilityMode, open, { theme = 'default' }) => ({
canFocus: open ? false : canFocus,
inAccessibilityMode: accessibilityMode,
theme
})
);
const mapDispatchToProps = {
setEditorFocusability,
setAccessibilityMode,
executeChallenge,
updateFile
};
@ -114,7 +121,12 @@ class Editor extends Component {
editorDidMount = (editor, monaco) => {
this._editor = editor;
if (this.props.canFocus) {
this._editor.updateOptions({
accessibilitySupport: this.props.inAccessibilityMode ? 'on' : 'auto'
});
// Users who are using screen readers should not have to move focus from
// the editor to the description every time they open a challenge.
if (this.props.canFocus && !this.props.inAccessibilityMode) {
this._editor.focus();
} else this.focusOnHotkeys();
this._editor.addAction({
@ -135,9 +147,33 @@ class Editor extends Component {
this.props.setEditorFocusability(false);
}
});
this._editor.addAction({
id: 'toggle-accessibility',
label: 'Toggle Accessibility Mode',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F1],
run: () => {
const currentAccessibility = this.props.inAccessibilityMode;
// The store needs to be updated first, as onDidChangeConfiguration is
// called before updateOptions returns
this.props.setAccessibilityMode(!currentAccessibility);
this._editor.updateOptions({
accessibilitySupport: currentAccessibility ? 'auto' : 'on'
});
}
});
this._editor.onDidFocusEditorWidget(() =>
this.props.setEditorFocusability(true)
);
// This is to persist changes caused by the accessibility tooltip.
// Unfortunately it relies on Monaco's implementation details
this._editor.onDidChangeConfiguration(() => {
if (
this._editor.getConfiguration().accessibilitySupport === 2 &&
!this.props.inAccessibilityMode
) {
this.props.setAccessibilityMode(true);
}
});
};
focusOnHotkeys() {

View File

@ -29,6 +29,7 @@ const initialState = {
},
challengeTests: [],
consoleOut: '',
inAccessibilityMode: false,
isCodeLocked: false,
isBuildEnabled: true,
modal: {
@ -78,7 +79,8 @@ export const types = createTypes(
'moveToTab',
'setEditorFocusability'
'setEditorFocusability',
'setAccessibilityMode'
],
ns
);
@ -152,6 +154,7 @@ export const submitChallenge = createAction(types.submitChallenge);
export const moveToTab = createAction(types.moveToTab);
export const setEditorFocusability = createAction(types.setEditorFocusability);
export const setAccessibilityMode = createAction(types.setAccessibilityMode);
export const currentTabSelector = state => state[ns].currentTab;
export const challengeFilesSelector = state => state[ns].challengeFiles;
@ -223,6 +226,8 @@ export const challengeDataSelector = state => {
};
export const canFocusEditorSelector = state => state[ns].canFocusEditor;
export const inAccessibilityModeSelector = state =>
state[ns].inAccessibilityMode;
const MAX_LOGS_SIZE = 64 * 1024;
@ -359,6 +364,10 @@ export const reducer = handleActions(
[types.setEditorFocusability]: (state, { payload }) => ({
...state,
canFocusEditor: payload
}),
[types.setAccessibilityMode]: (state, { payload }) => ({
...state,
inAccessibilityMode: payload
})
},
initialState