fix(client): store accessibility for screenreaders (#42996)

* fix: store accessibility for screenreaders

* Apply suggestions from code review

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* fix: remove old comments

* feat: at short cut announcement when running test

* feat: announce that accesibility mode is turned on

* fix: use Redux values

* Apply suggestions from code review

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* Apply suggestions from Oliver

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* fix: "rip out redux stuff" and use store instead

* Apply suggestions from code review

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* Apply suggestions from code review

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* fix: old use of props

* Update client/src/templates/Challenges/classic/editor.tsx

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Sem Bauke
2021-08-06 14:17:17 +02:00
committed by GitHub
parent cbf553639d
commit eccc642d83
5 changed files with 38 additions and 57 deletions

View File

@@ -8,9 +8,7 @@ import {
canFocusEditorSelector, canFocusEditorSelector,
consoleOutputSelector, consoleOutputSelector,
executeChallenge, executeChallenge,
inAccessibilityModeSelector,
saveEditorContent, saveEditorContent,
setAccessibilityMode,
setEditorFocusability, setEditorFocusability,
visibleEditorsSelector, visibleEditorsSelector,
updateFile updateFile
@@ -31,7 +29,6 @@ const propTypes = {
executeChallenge: PropTypes.func.isRequired, executeChallenge: PropTypes.func.isRequired,
ext: PropTypes.string, ext: PropTypes.string,
fileKey: PropTypes.string, fileKey: PropTypes.string,
inAccessibilityMode: PropTypes.bool.isRequired,
initialEditorContent: PropTypes.string, initialEditorContent: PropTypes.string,
initialExt: PropTypes.string, initialExt: PropTypes.string,
output: PropTypes.arrayOf(PropTypes.string), output: PropTypes.arrayOf(PropTypes.string),
@@ -40,7 +37,6 @@ const propTypes = {
onResize: PropTypes.func onResize: PropTypes.func
}), }),
saveEditorContent: PropTypes.func.isRequired, saveEditorContent: PropTypes.func.isRequired,
setAccessibilityMode: PropTypes.func.isRequired,
setEditorFocusability: PropTypes.func, setEditorFocusability: PropTypes.func,
theme: PropTypes.string, theme: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
@@ -57,21 +53,12 @@ const mapStateToProps = createSelector(
visibleEditorsSelector, visibleEditorsSelector,
canFocusEditorSelector, canFocusEditorSelector,
consoleOutputSelector, consoleOutputSelector,
inAccessibilityModeSelector,
isDonationModalOpenSelector, isDonationModalOpenSelector,
userSelector, userSelector,
( (visibleEditors, canFocus, output, open, { theme = 'default' }) => ({
visibleEditors,
canFocus,
output,
accessibilityMode,
open,
{ theme = 'default' }
) => ({
visibleEditors, visibleEditors,
canFocus: open ? false : canFocus, canFocus: open ? false : canFocus,
output, output,
inAccessibilityMode: accessibilityMode,
theme theme
}) })
); );
@@ -79,7 +66,6 @@ const mapStateToProps = createSelector(
const mapDispatchToProps = { const mapDispatchToProps = {
executeChallenge, executeChallenge,
saveEditorContent, saveEditorContent,
setAccessibilityMode,
setEditorFocusability, setEditorFocusability,
updateFile updateFile
}; };

View File

@@ -16,7 +16,7 @@ import React, {
} from 'react'; } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import store from 'store';
import { Loader } from '../../../components/helpers'; import { Loader } from '../../../components/helpers';
import { userSelector, isDonationModalOpenSelector } from '../../../redux'; import { userSelector, isDonationModalOpenSelector } from '../../../redux';
import { import {
@@ -27,14 +27,13 @@ import {
ResizePropsType, ResizePropsType,
TestType TestType
} from '../../../redux/prop-types'; } from '../../../redux/prop-types';
import { import {
canFocusEditorSelector, canFocusEditorSelector,
consoleOutputSelector, consoleOutputSelector,
executeChallenge, executeChallenge,
inAccessibilityModeSelector,
saveEditorContent, saveEditorContent,
setEditorFocusability, setEditorFocusability,
setAccessibilityMode,
updateFile, updateFile,
challengeTestsSelector, challengeTestsSelector,
submitChallenge submitChallenge
@@ -55,13 +54,11 @@ interface EditorProps {
executeChallenge: (isShouldCompletionModalOpen?: boolean) => void; executeChallenge: (isShouldCompletionModalOpen?: boolean) => void;
ext: ExtTypes; ext: ExtTypes;
fileKey: FileKeyTypes; fileKey: FileKeyTypes;
inAccessibilityMode: boolean;
initialEditorContent: string; initialEditorContent: string;
initialExt: string; initialExt: string;
output: string[]; output: string[];
resizeProps: ResizePropsType; resizeProps: ResizePropsType;
saveEditorContent: () => void; saveEditorContent: () => void;
setAccessibilityMode: (isAccessible: boolean) => void;
setEditorFocusability: (isFocusable: boolean) => void; setEditorFocusability: (isFocusable: boolean) => void;
submitChallenge: () => void; submitChallenge: () => void;
tests: TestType[]; tests: TestType[];
@@ -100,21 +97,18 @@ interface EditorPropertyStore {
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
canFocusEditorSelector, canFocusEditorSelector,
consoleOutputSelector, consoleOutputSelector,
inAccessibilityModeSelector,
isDonationModalOpenSelector, isDonationModalOpenSelector,
userSelector, userSelector,
challengeTestsSelector, challengeTestsSelector,
( (
canFocus: boolean, canFocus: boolean,
output: string[], output: string[],
accessibilityMode: boolean,
open, open,
{ theme = 'default' }: { theme: string }, { theme = 'default' }: { theme: string },
tests: [{ text: string; testString: string }] tests: [{ text: string; testString: string }]
) => ({ ) => ({
canFocus: open ? false : canFocus, canFocus: open ? false : canFocus,
output, output,
inAccessibilityMode: accessibilityMode,
theme, theme,
tests tests
}) })
@@ -125,7 +119,6 @@ const mapStateToProps = createSelector(
const mapDispatchToProps = { const mapDispatchToProps = {
executeChallenge, executeChallenge,
saveEditorContent, saveEditorContent,
setAccessibilityMode,
setEditorFocusability, setEditorFocusability,
updateFile, updateFile,
submitChallenge submitChallenge
@@ -297,12 +290,31 @@ const Editor = (props: EditorProps): JSX.Element => {
// TODO this should *probably* be set on focus // TODO this should *probably* be set on focus
editorRef.current = editor; editorRef.current = editor;
data.editor = editor; data.editor = editor;
const storedAccessibilityMode = () => {
const accessibility = store.get('accessibilityMode') as boolean;
if (!accessibility) {
store.set('accessibilityMode', false);
}
// Only able to set the arialabel when accessibility mode is set to true
// Otherwise it gets overwritten by the monaco default aria-label
if (accessibility) {
editor.updateOptions({
ariaLabel:
'Accessibility mode set to true. Press Ctrl+e to disable or press Alt+F1 for more options'
});
}
return accessibility;
};
const accessibilityMode = storedAccessibilityMode();
editor.updateOptions({ editor.updateOptions({
accessibilitySupport: props.inAccessibilityMode ? 'on' : 'auto' accessibilitySupport: accessibilityMode ? 'on' : 'auto'
}); });
// Users who are using screen readers should not have to move focus from // Users who are using screen readers should not have to move focus from
// the editor to the description every time they open a challenge. // the editor to the description every time they open a challenge.
if (props.canFocus && !props.inAccessibilityMode) { if (props.canFocus && !accessibilityMode) {
// TODO: only one Editor should be calling for focus at once. // TODO: only one Editor should be calling for focus at once.
editor.focus(); editor.focus();
} else focusOnHotkeys(); } else focusOnHotkeys();
@@ -343,28 +355,18 @@ const Editor = (props: EditorProps): JSX.Element => {
editor.addAction({ editor.addAction({
id: 'toggle-accessibility', id: 'toggle-accessibility',
label: 'Toggle Accessibility Mode', label: 'Toggle Accessibility Mode',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F1], keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_E],
run: () => { run: () => {
const currentAccessibility = props.inAccessibilityMode; const currentAccessibility = storedAccessibilityMode();
// The store needs to be updated first, as onDidChangeConfiguration is
// called before updateOptions returns store.set('accessibilityMode', !currentAccessibility);
props.setAccessibilityMode(!currentAccessibility);
editor.updateOptions({ editor.updateOptions({
accessibilitySupport: currentAccessibility ? 'auto' : 'on' accessibilitySupport: storedAccessibilityMode() ? 'on' : 'auto',
}); });
} }
}); });
editor.onDidFocusEditorWidget(() => props.setEditorFocusability(true)); editor.onDidFocusEditorWidget(() => props.setEditorFocusability(true));
// This is to persist changes caused by the accessibility tooltip.
editor.onDidChangeConfiguration(event => {
if (
event.hasChanged(monaco.editor.EditorOption.accessibilitySupport) &&
editor.getRawOptions().accessibilitySupport === 'on' &&
!props.inAccessibilityMode
) {
props.setAccessibilityMode(true);
}
});
const editableBoundaries = getEditableRegion(); const editableBoundaries = getEditableRegion();

View File

@@ -53,7 +53,12 @@ function ToolPanel({
isMobile ? 'tool-panel-group-mobile' : '' isMobile ? 'tool-panel-group-mobile' : ''
}`} }`}
> >
<Button block={true} bsStyle='primary' onClick={handleRunTests}> <Button
aria-label='Run the tests use shortcut Ctrl+enter'
block={true}
bsStyle='primary'
onClick={handleRunTests}
>
{isMobile ? t('buttons.run') : t('buttons.run-test')} {isMobile ? t('buttons.run') : t('buttons.run-test')}
</Button> </Button>
<Button <Button

View File

@@ -41,8 +41,7 @@ export const actionTypes = createTypes(
'moveToTab', 'moveToTab',
'setEditorFocusability', 'setEditorFocusability',
'toggleVisibleEditor', 'toggleVisibleEditor'
'setAccessibilityMode'
], ],
ns ns
); );

View File

@@ -31,7 +31,6 @@ const initialState = {
challengeTests: [], challengeTests: [],
consoleOut: [], consoleOut: [],
hasCompletedBlock: false, hasCompletedBlock: false,
inAccessibilityMode: false,
isCodeLocked: false, isCodeLocked: false,
isBuildEnabled: true, isBuildEnabled: true,
logsOut: [], logsOut: [],
@@ -131,9 +130,6 @@ export const setEditorFocusability = createAction(
export const toggleVisibleEditor = createAction( export const toggleVisibleEditor = createAction(
actionTypes.toggleVisibleEditor actionTypes.toggleVisibleEditor
); );
export const setAccessibilityMode = createAction(
actionTypes.setAccessibilityMode
);
export const currentTabSelector = state => state[ns].currentTab; export const currentTabSelector = state => state[ns].currentTab;
export const challengeFilesSelector = state => state[ns].challengeFiles; export const challengeFilesSelector = state => state[ns].challengeFiles;
@@ -211,9 +207,6 @@ export const challengeDataSelector = state => {
export const canFocusEditorSelector = state => state[ns].canFocusEditor; export const canFocusEditorSelector = state => state[ns].canFocusEditor;
export const visibleEditorsSelector = state => state[ns].visibleEditors; export const visibleEditorsSelector = state => state[ns].visibleEditors;
export const inAccessibilityModeSelector = state =>
state[ns].inAccessibilityMode;
export const reducer = handleActions( export const reducer = handleActions(
{ {
[actionTypes.createFiles]: (state, { payload }) => ({ [actionTypes.createFiles]: (state, { payload }) => ({
@@ -361,11 +354,7 @@ export const reducer = handleActions(
[payload]: !state.visibleEditors[payload] [payload]: !state.visibleEditors[payload]
} }
}; };
}, }
[actionTypes.setAccessibilityMode]: (state, { payload }) => ({
...state,
inAccessibilityMode: payload
})
}, },
initialState initialState
); );