Merge pull request #7738 from KDSBrowne/closed-captioning-access

Fix accessibility issues in closed captions
This commit is contained in:
Fred Dixon 2019-07-06 22:50:28 -04:00 committed by GitHub
commit aedb653fd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 47 deletions

View File

@ -12,14 +12,30 @@ const DEFAULT_KEY = -1;
const DEFAULT_INDEX = 0;
const FONT_FAMILIES = ['Arial', 'Calibri', 'Times New Roman', 'Sans-serif'];
const FONT_SIZES = ['12px', '14px', '18px', '24px', '32px', '42px'];
// Not using hex values to force the githubPicker UI to display color names on hover
const COLORS = [
'#000000', '#7A7A7A',
'#FF0000', '#FF8800',
'#88FF00', '#FFFFFF',
'#00FFFF', '#0000FF',
'#8800FF', '#FF00FF',
'black', 'grey',
'red', 'orange',
'lime', 'white',
'cyan', 'blue',
'darkviolet', 'magenta',
];
// Used to convert hex values to color names for screen reader aria labels
const HEX_COLOR_NAMES = {
'#000000': 'Black',
'#7a7a7a': 'Grey',
'#ff0000': 'Red',
'#ff8800': 'Orange',
'#88ff00': 'Green',
'#ffffff': 'White',
'#00ffff': 'Cyan',
'#0000ff': 'Blue',
'#8800ff': 'Dark violet',
'#ff00ff': 'Magenta',
};
const intlMessages = defineMessages({
closeLabel: {
id: 'app.captions.menu.closeLabel',
@ -61,6 +77,18 @@ const intlMessages = defineMessages({
id: 'app.captions.menu.previewLabel',
description: 'Preview area label',
},
ariaSelectLang: {
id: 'app.captions.menu.ariaSelect',
description: 'Captions language select aria label',
},
captionsLabel: {
id: 'app.captions.label',
description: 'Used in font / size aria labels',
},
current: {
id: 'app.submenu.application.currentSize',
description: 'Used in text / background color aria labels',
},
});
const propTypes = {
@ -189,6 +217,12 @@ class ReaderMenu extends PureComponent {
} = this.state;
const defaultLocale = locale || DEFAULT_VALUE;
const ariaTextColor = `${intl.formatMessage(intlMessages.fontColor)} ${intl.formatMessage(intlMessages.current, { 0: HEX_COLOR_NAMES[fontColor.toLowerCase()] })}`;
const ariaBackgroundColor = `${intl.formatMessage(intlMessages.backgroundColor)} ${intl.formatMessage(intlMessages.current, { 0: HEX_COLOR_NAMES[backgroundColor.toLowerCase()] })}`;
const ariaFont = `${intl.formatMessage(intlMessages.captionsLabel)} ${intl.formatMessage(intlMessages.fontFamily)}`;
const ariaSize = `${intl.formatMessage(intlMessages.captionsLabel)} ${intl.formatMessage(intlMessages.fontSize)}`;
return (
<Modal
overlayClassName={styles.overlay}
@ -200,34 +234,44 @@ class ReaderMenu extends PureComponent {
<header className={styles.title}>
{intl.formatMessage(intlMessages.title)}
</header>
<div className={styles.selectLanguage}>
<select
className={styles.select}
onChange={this.handleLocaleChange}
defaultValue={defaultLocale}
>
<option
disabled
key={DEFAULT_KEY}
value={DEFAULT_VALUE}
>
{intl.formatMessage(intlMessages.select)}
</option>
{ownedLocales.map(loc => (
<option
key={loc.locale}
value={loc.locale}
>
{loc.name}
</option>))}
</select>
</div>
{!locale ? null : (
<div className={styles.content}>
<div>
<div className={styles.col}>
<div className={styles.row}>
<div className={styles.label}>{intl.formatMessage(intlMessages.fontColor)}</div>
<div aria-hidden className={styles.label}>
{intl.formatMessage(intlMessages.ariaSelectLang)}
</div>
<select
aria-label={intl.formatMessage(intlMessages.ariaSelectLang)}
className={styles.select}
onChange={this.handleLocaleChange}
defaultValue={defaultLocale}
lang={locale}
>
<option
disabled
key={DEFAULT_KEY}
value={DEFAULT_VALUE}
>
{intl.formatMessage(intlMessages.select)}
</option>
{ownedLocales.map(loc => (
<option
key={loc.locale}
value={loc.locale}
lang={loc.locale}
>
{loc.name}
</option>))}
</select>
</div>
<div className={styles.row}>
<div aria-hidden className={styles.label}>
{intl.formatMessage(intlMessages.fontColor)}
</div>
<div
aria-label={ariaTextColor}
tabIndex={DEFAULT_INDEX}
className={styles.swatch}
onClick={this.handleColorPickerClick.bind(this, 'displayFontColorPicker')}
@ -243,7 +287,6 @@ class ReaderMenu extends PureComponent {
/>
<GithubPicker
onChange={this.handleColorChange.bind(this, 'fontColor')}
color={fontColor}
colors={COLORS}
width="140px"
triangle="hide"
@ -254,10 +297,11 @@ class ReaderMenu extends PureComponent {
</div>
<div className={styles.row}>
<div className={styles.label}>
<div aria-hidden className={styles.label}>
{intl.formatMessage(intlMessages.backgroundColor)}
</div>
<div
aria-label={ariaBackgroundColor}
tabIndex={DEFAULT_INDEX}
className={styles.swatch}
onClick={this.handleColorPickerClick.bind(this, 'displayBackgroundColorPicker')}
@ -273,7 +317,6 @@ class ReaderMenu extends PureComponent {
/>
<GithubPicker
onChange={this.handleColorChange.bind(this, 'backgroundColor')}
color={backgroundColor}
colors={COLORS}
width="140px"
triangle="hide"
@ -284,8 +327,11 @@ class ReaderMenu extends PureComponent {
</div>
<div className={styles.row}>
<div className={styles.label}>{intl.formatMessage(intlMessages.fontFamily)}</div>
<div aria-hidden className={styles.label}>
{intl.formatMessage(intlMessages.fontFamily)}
</div>
<select
aria-label={ariaFont}
className={styles.select}
defaultValue={FONT_FAMILIES.indexOf(fontFamily)}
onChange={this.handleSelectChange.bind(this, 'fontFamily', FONT_FAMILIES)}
@ -302,8 +348,11 @@ class ReaderMenu extends PureComponent {
</div>
<div className={styles.row}>
<div className={styles.label}>{intl.formatMessage(intlMessages.fontSize)}</div>
<div aria-hidden className={styles.label}>
{intl.formatMessage(intlMessages.fontSize)}
</div>
<select
aria-label={ariaSize}
className={styles.select}
defaultValue={FONT_SIZES.indexOf(fontSize)}
onChange={this.handleSelectChange.bind(this, 'fontSize', FONT_SIZES)}
@ -321,7 +370,7 @@ class ReaderMenu extends PureComponent {
<div className={styles.row}>
<div className={styles.label}>{intl.formatMessage(intlMessages.preview)}</div>
<span style={this.getPreviewStyle()}>AaBbCc</span>
<span aria-hidden style={this.getPreviewStyle()}>AaBbCc</span>
</div>
</div>
</div>

View File

@ -43,13 +43,6 @@
padding: 1rem;
}
.content {
}
.selectLanguage {
text-align: end;
}
.col {
display: flex;
flex-direction: column;

View File

@ -9,6 +9,7 @@ class Captions extends React.Component {
super(props);
this.state = { initial: true };
this.text = '';
this.ariaText = '';
this.timer = null;
this.settings = CaptionsService.getCaptionsSettings();
@ -27,6 +28,7 @@ class Captions extends React.Component {
} = this.props;
if (padId === nextProps.padId) {
if (this.text !== '') this.ariaText = this.text;
if (revs === nextProps.revs && !nextState.clear) return false;
}
return true;
@ -90,11 +92,30 @@ class Captions extends React.Component {
color: fontColor,
};
const visuallyHidden = {
position: 'absolute',
overflow: 'hidden',
clip: 'rect(0 0 0 0)',
height: '1px',
width: '1px',
margin: '-1px',
padding: '0',
border: '0',
};
return (
<span
style={captionStyles}
dangerouslySetInnerHTML={{ __html: this.text }}
/>
<div>
<div
aria-hidden
style={captionStyles}
dangerouslySetInnerHTML={{ __html: this.text }}
/>
<div
style={visuallyHidden}
aria-live={this.text === '' && this.ariaText !== '' ? 'polite' : 'off'}
dangerouslySetInnerHTML={{ __html: this.ariaText }}
/>
</div>
);
}
}

View File

@ -259,7 +259,7 @@ class ApplicationMenu extends BaseMenu {
>
<option disabled>{intl.formatMessage(intlMessages.languageOptionLabel)}</option>
{availableLocales.map((locale, index) => (
<option key={index} value={locale.locale}>
<option key={index} value={locale.locale} lang={locale.locale}>
{locale.name}
</option>
))}