make nav keyboard accessible

This commit is contained in:
Cassie Tarakajian 2017-08-28 17:54:39 -04:00
parent 7d449e63b9
commit 087f319a8a
2 changed files with 108 additions and 16 deletions

View file

@ -12,10 +12,15 @@ class Nav extends React.PureComponent {
this.state = { this.state = {
dropdownOpen: 'none' dropdownOpen: 'none'
}; };
this.handleFocus = this.handleFocus.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.clearHideTimeout = this.clearHideTimeout.bind(this);
} }
isUserOwner() { setDropdown(dropdown) {
return this.props.project.owner && this.props.project.owner.id === this.props.user.id; this.setState({
dropdownOpen: dropdown
});
} }
toggleDropdown(dropdown) { toggleDropdown(dropdown) {
@ -30,6 +35,26 @@ class Nav extends React.PureComponent {
} }
} }
isUserOwner() {
return this.props.project.owner && this.props.project.owner.id === this.props.user.id;
}
handleFocus(dropdown) {
this.clearHideTimeout();
this.setDropdown(dropdown);
}
clearHideTimeout() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
handleBlur() {
this.timer = setTimeout(this.setDropdown.bind(this, 'none'), 10);
}
render() { render() {
const navDropdownState = { const navDropdownState = {
file: classNames({ file: classNames({
@ -62,7 +87,8 @@ class Nav extends React.PureComponent {
<li className={navDropdownState.file}> <li className={navDropdownState.file}>
<button <button
onClick={this.toggleDropdown.bind(this, 'file')} onClick={this.toggleDropdown.bind(this, 'file')}
onBlur={this.toggleDropdown.bind(this, 'none')} onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
> >
<span className="nav__item-header">File</span> <span className="nav__item-header">File</span>
<InlineSVG src={triangleUrl} /> <InlineSVG src={triangleUrl} />
@ -81,6 +107,8 @@ class Nav extends React.PureComponent {
this.props.newProject(); this.props.newProject();
} }
}} }}
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
> >
New New
</button> </button>
@ -95,37 +123,57 @@ class Nav extends React.PureComponent {
this.props.showErrorModal('forceAuthentication'); this.props.showErrorModal('forceAuthentication');
} }
}} }}
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
> >
Save Save
</button> </button>
</li> } </li> }
{ this.props.project.id && this.props.user.authenticated && { this.props.project.id && this.props.user.authenticated &&
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button onClick={this.props.cloneProject}> <button
onClick={this.props.cloneProject}
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
>
Duplicate Duplicate
</button> </button>
</li> } </li> }
{ this.props.project.id && { this.props.project.id &&
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button onClick={this.props.showShareModal}> <button
onClick={this.props.showShareModal}
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
>
Share Share
</button> </button>
</li> } </li> }
{ this.props.project.id && { this.props.project.id &&
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button onClick={() => this.props.exportProjectAsZip(this.props.project.id)}> <button
onClick={() => this.props.exportProjectAsZip(this.props.project.id)}
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
>
Download Download
</button> </button>
</li> } </li> }
{ this.props.user.authenticated && { this.props.user.authenticated &&
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link to={`/${this.props.user.username}/sketches`}> <Link
to={`/${this.props.user.username}/sketches`}
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
>
Open Open
</Link> </Link>
</li> } </li> }
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link <Link
to="/p5/sketches" to="/p5/sketches"
onFocus={this.handleFocus.bind(this, 'file')}
onBlur={this.handleBlur}
> >
Examples Examples
</Link> </Link>
@ -133,7 +181,11 @@ class Nav extends React.PureComponent {
</ul> </ul>
</li> </li>
<li className={navDropdownState.edit}> <li className={navDropdownState.edit}>
<button onClick={this.toggleDropdown.bind(this, 'edit')}> <button
onClick={this.toggleDropdown.bind(this, 'edit')}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
>
<span className="nav__item-header">Edit</span> <span className="nav__item-header">Edit</span>
<InlineSVG src={triangleUrl} /> <InlineSVG src={triangleUrl} />
</button> </button>
@ -151,7 +203,11 @@ class Nav extends React.PureComponent {
</ul> </ul>
</li> </li>
<li className={navDropdownState.sketch}> <li className={navDropdownState.sketch}>
<button onClick={this.toggleDropdown.bind(this, 'sketch')}> <button
onClick={this.toggleDropdown.bind(this, 'sketch')}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
>
<span className="nav__item-header">Sketch</span> <span className="nav__item-header">Sketch</span>
<InlineSVG src={triangleUrl} /> <InlineSVG src={triangleUrl} />
</button> </button>
@ -169,7 +225,11 @@ class Nav extends React.PureComponent {
</ul> </ul>
</li> </li>
<li className={navDropdownState.help}> <li className={navDropdownState.help}>
<button onClick={this.toggleDropdown.bind(this, 'help')}> <button
onClick={this.toggleDropdown.bind(this, 'help')}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
>
<span className="nav__item-header">Help</span> <span className="nav__item-header">Help</span>
<InlineSVG src={triangleUrl} /> <InlineSVG src={triangleUrl} />
</button> </button>
@ -186,10 +246,16 @@ class Nav extends React.PureComponent {
href="https://p5js.org/reference/" href="https://p5js.org/reference/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
onFocus={this.handleFocus.bind(this, 'help')}
onBlur={this.handleBlur}
>Reference</a> >Reference</a>
</li> </li>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link to="/about"> <Link
to="/about"
onFocus={this.handleFocus.bind(this, 'help')}
onBlur={this.handleBlur}
>
About About
</Link> </Link>
</li> </li>
@ -216,6 +282,8 @@ class Nav extends React.PureComponent {
<button <button
className="nav__item-header" className="nav__item-header"
onClick={this.toggleDropdown.bind(this, 'account')} onClick={this.toggleDropdown.bind(this, 'account')}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
> >
My Account My Account
</button> </button>
@ -226,22 +294,38 @@ class Nav extends React.PureComponent {
<InlineSVG src={triangleUrl} /> <InlineSVG src={triangleUrl} />
</li> </li>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link to={`/${this.props.user.username}/sketches`}> <Link
to={`/${this.props.user.username}/sketches`}
onFocus={this.handleFocus.bind(this, 'account')}
onBlur={this.handleBlur}
>
My sketches My sketches
</Link> </Link>
</li> </li>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link to={`/${this.props.user.username}/assets`}> <Link
to={`/${this.props.user.username}/assets`}
onFocus={this.handleFocus.bind(this, 'account')}
onBlur={this.handleBlur}
>
My assets My assets
</Link> </Link>
</li> </li>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link to={`/${this.props.user.username}/account`}> <Link
to={`/${this.props.user.username}/account`}
onFocus={this.handleFocus.bind(this, 'account')}
onBlur={this.handleBlur}
>
Settings Settings
</Link> </Link>
</li> </li>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button onClick={this.props.logoutUser} > <button
onClick={this.props.logoutUser}
onFocus={this.handleFocus.bind(this, 'account')}
onBlur={this.handleBlur}
>
Log out Log out
</button> </button>
</li> </li>

View file

@ -152,6 +152,14 @@ class PreviewFrame extends React.Component {
doc.srcDoc = ''; doc.srcDoc = '';
} }
addLoopProtect(sketchDoc) {
const scriptsInHTML = sketchDoc.getElementsByTagName('script');
const scriptsInHTMLArray = Array.prototype.slice.call(scriptsInHTML);
scriptsInHTMLArray.forEach((script) => {
script.innerHTML = loopProtect(script.innerHTML); // eslint-disable-line
});
}
injectLocalFiles() { injectLocalFiles() {
const htmlFile = this.props.htmlFile.content; const htmlFile = this.props.htmlFile.content;
let scriptOffs = []; let scriptOffs = [];
@ -227,7 +235,7 @@ class PreviewFrame extends React.Component {
scriptOffs = getAllScriptOffsets(sketchDocString); scriptOffs = getAllScriptOffsets(sketchDocString);
const consoleErrorsScript = sketchDoc.createElement('script'); const consoleErrorsScript = sketchDoc.createElement('script');
consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs)); consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
// sketchDoc.head.appendChild(consoleErrorsScript); this.addLoopProtect(sketchDoc);
sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement); sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement);
return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`; return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`;