Skip to content

Commit

Permalink
feat: updated a11y component (#738)
Browse files Browse the repository at this point in the history
* feat: updated a11y component
* fix: allow note to be focusable
* fix: remove dead hotkey code
  • Loading branch information
liamross authored Jul 10, 2020
1 parent feee676 commit a8d1847
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 86 deletions.
12 changes: 12 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"printWidth": 120,
"quoteProps": "preserve",
"singleQuote": true,
"trailingComma": "all"
}
90 changes: 49 additions & 41 deletions src/components/Accessibility/Accessibility.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import selectors from 'selectors';

import './Accessibility.scss';
import classNames from 'classnames';
import actions from 'actions';

class Accessibility extends React.PureComponent {
static propTypes = {
isAccessibleMode: PropTypes.bool,
isNotesPanelDisabled: PropTypes.bool,
}
function Accessibility() {
const dispatch = useDispatch();
const [isVisible, setIsVisible] = useState(false);

state = {
isVisible: false,
}
const isAccessibleMode = useSelector(selectors.isAccessibleMode);

onFocus = () => {
this.setState({ isVisible: true });
}
const isNotesPanelOpen = useSelector(state => selectors.isElementOpen(state, 'notesPanel'));
const isNotesPanelDisabled = useSelector(state => selectors.isElementDisabled(state, 'notesPanel'));

onBlur = () => {
this.setState({ isVisible: false });
}
const isSearchPanelOpen = useSelector(state => selectors.isElementOpen(state, 'searchPanel'));
const isSearchPanelDisabled = useSelector(state => selectors.isElementDisabled(state, 'searchPanel'));

render() {
const { isAccessibleMode, isNotesPanelDisabled } = this.props;
const { isVisible } = this.state;
const onFocus = () => setIsVisible(true);
const onBlur = () => setIsVisible(false);

if (!isAccessibleMode) {
return null;
const onSkipToDocument = () => {
document.getElementById('pageText1').focus();
};

const onSkipToSearch = () => {
if (isSearchPanelOpen) {
const searchEl = document.getElementById('SearchPanel__input');
searchEl && searchEl.focus();
} else {
dispatch(actions.openElement('searchPanel'));
}
};

const onSkipToNotes = () => {
if (isNotesPanelOpen) {
const noteEl = document.getElementById('NotesPanel__input');
noteEl && noteEl.focus();
} else {
dispatch(actions.openElement('notesPanel'));
}
};

const skiptoNotes = isNotesPanelDisabled ? null : <div className="skip-to-notes" onFocus={this.onFocus} onBlur={this.onBlur} tabIndex={0}>Notes</div>;
const className = `Accessibility ${isVisible ? 'visible' : 'hidden'}`;

return (
<div className={className} data-element="accessibility">
<div>Skip to: </div>
<input className="skip-to-hack" tabIndex={-1}></input>
<div className="skip-to-document" onFocus={this.onFocus} onBlur={this.onBlur} tabIndex={0}>Document</div>
{skiptoNotes}
</div>
);
if (!isAccessibleMode) {
return null;
}
}

const mapStateToProps = state => ({
isAccessibleMode: selectors.isAccessibleMode(state),
isNotesPanelDisabled: selectors.isElementDisabled(state, 'notesPanel')
});
const a11yClass = classNames('Accessibility', isVisible ? 'visible' : 'hidden');

return (
<div className={a11yClass} data-element="accessibility" onFocus={onFocus} onBlur={onBlur}>
<div>Skip to: </div>

<button onClick={onSkipToDocument}>Document</button>
{isSearchPanelDisabled ? null : <button onClick={onSkipToSearch}>Search</button>}
{isNotesPanelDisabled ? null : <button onClick={onSkipToNotes}>Notes</button>}
</div>
);
}

export default connect(mapStateToProps)(Accessibility);
export default Accessibility;
32 changes: 14 additions & 18 deletions src/components/Accessibility/Accessibility.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,33 @@

.Accessibility {
position: fixed;
left: 50%;
transform: translate(-50%);
z-index: 30;
z-index: 9999;

display: flex;
align-items: center;
overflow: hidden;

background: white;
color: $pdftron-blue;
font-style: italic;
color: black;
font-size: 1.2em;
border-bottom: 1px solid var(--border);
border: 1px solid var(--border);

height: $top-bar-height;
padding: 0 10px;

&.visible {
height: $top-bar-height;
padding: 10px;
transform: translateY(0%);
}

&.hidden {
height: 0;
padding: 0;
transform: translateY(-100%);
}

div {
margin: 2px 7px;
padding: 1px;
}

.skip-to-hack {
width: 0;
height: 0;
button {
border: none;
background-color: transparent;
color: black;
margin: 0 7px;
padding: 2px 6px;
}
}
18 changes: 13 additions & 5 deletions src/components/Note/Note.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const Note = ({ annotation }) => {
const handleNoteClick = e => {
// stop bubbling up otherwise the note will be closed
// due to annotation deselection
e.stopPropagation();
e && e.stopPropagation();

if (!isSelected) {
core.deselectAllAnnotations();
Expand All @@ -105,10 +105,11 @@ const Note = ({ annotation }) => {
const showReplyArea = !Object.values(isEditingMap).some(val => val);

const handleNoteKeydown = e => {
// Stop enter/space key on Note from being submitted into field.
// Click if enter or space is pressed and is current target.
const isNote = e.target === e.currentTarget;
if (isNote && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault();
e.preventDefault(); // Stop from being entered in field
handleNoteClick();
}
};

Expand All @@ -123,7 +124,14 @@ const Note = ({ annotation }) => {
);

return (
<div ref={containerRef} className={noteClass} onClick={handleNoteClick} onKeyDown={handleNoteKeydown}>
<div
role="button"
tabIndex={0}
ref={containerRef}
className={noteClass}
onClick={handleNoteClick}
onKeyDown={handleNoteKeydown}
>
<NoteContent
noteIndex={0}
annotation={annotation}
Expand All @@ -137,7 +145,7 @@ const Note = ({ annotation }) => {
<div className={repliesClass}>
{replies.map((reply, i) => (
<NoteContent
noteIndex={i+1}
noteIndex={i + 1}
key={reply.Id}
annotation={reply}
setIsEditing={setIsEditing}
Expand Down
1 change: 1 addition & 0 deletions src/components/NotesPanel/NotesPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ const NotesPanel = () => {
placeholder={t('message.searchCommentsPlaceholder')}
onChange={handleInputChange}
ref={inputRef}
id="NotesPanel__input"
/>
</div>
<div className="divider" />
Expand Down
1 change: 1 addition & 0 deletions src/components/SearchOverlay/SearchOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ class SearchOverlay extends React.PureComponent {
onKeyDown={this.onKeyDown}
value={searchValue}
placeholder={t('message.searchDocumentPlaceholder')}
id="SearchPanel__input"
/>
<button
className="input-button"
Expand Down
22 changes: 0 additions & 22 deletions src/helpers/hotkeysManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,19 +330,6 @@ WebViewer(...)

print(dispatch, selectors.isEmbedPrintSupported(getState()));
},
[`${Keys.ENTER}`]: () => {
if (document.activeElement.className.includes('Note')) {
document.activeElement.click();
} else if (document.activeElement.className === 'skip-to-document') {
document.getElementById('pageText0').focus();
} else if (document.activeElement.className === 'skip-to-notes') {
dispatch(actions.openElement('notesPanel'));
const noteEl = document.querySelector('.Note');
if (noteEl) {
noteEl.focus();
}
}
},
[`${Keys.PAGE_UP}`]: e => {
e.preventDefault();

Expand Down Expand Up @@ -401,15 +388,6 @@ WebViewer(...)
e.preventDefault();
setToolModeAndGroup(store, 'AnnotationEdit', '');

const el = document.activeElement;
if (el?.tabIndex === 0) {
const hackEl = document.querySelector('.skip-to-hack');
if (hackEl) {
hackEl.focus();
hackEl.blur();
}
}

dispatch(
actions.closeElements([
'annotationPopup',
Expand Down

0 comments on commit a8d1847

Please sign in to comment.