Move through svg icons and add aria labels

This commit is contained in:
Cassie Tarakajian 2020-05-05 19:03:58 -04:00
parent 8bf4008c51
commit cee22c3277
28 changed files with 194 additions and 101 deletions

View file

@ -228,11 +228,11 @@ class Nav extends React.PureComponent {
return ( return (
<ul className="nav__items-left"> <ul className="nav__items-left">
<li className="nav__item-logo"> <li className="nav__item-logo">
<LogoIcon title="p5.js Logo" className="svg__logo" /> <LogoIcon role="img" aria-label="p5.js Logo" focusable="false" className="svg__logo" />
</li> </li>
<li className="nav__item nav__item--no-icon"> <li className="nav__item nav__item--no-icon">
<Link to="/" className="nav__back-link"> <Link to="/" className="nav__back-link">
<CaretLeftIcon className="nav__back-icon" /> <CaretLeftIcon className="nav__back-icon" focusable="false" aria-hidden="true" />
<span className="nav__item-header"> <span className="nav__item-header">
Back to Editor Back to Editor
</span> </span>
@ -246,7 +246,7 @@ class Nav extends React.PureComponent {
return ( return (
<ul className="nav__items-left"> <ul className="nav__items-left">
<li className="nav__item-logo"> <li className="nav__item-logo">
<LogoIcon title="p5.js Logo" className="svg__logo" /> <LogoIcon role="img" aria-label="p5.js Logo" focusable="false" className="svg__logo" />
</li> </li>
<li className={navDropdownState.file}> <li className={navDropdownState.file}>
<button <button
@ -260,7 +260,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">File</span> <span className="nav__item-header">File</span>
<TriangleIcon className="nav__item-header-triangle" /> <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
@ -362,7 +362,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">Edit</span> <span className="nav__item-header">Edit</span>
<TriangleIcon className="nav__item-header-triangle" /> <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
</button> </button>
<ul className="nav__dropdown" > <ul className="nav__dropdown" >
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
@ -422,7 +422,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">Sketch</span> <span className="nav__item-header">Sketch</span>
<TriangleIcon className="nav__item-header-triangle" /> <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
@ -497,7 +497,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">Help</span> <span className="nav__item-header">Help</span>
<TriangleIcon className="nav__item-header-triangle" /> <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
@ -574,7 +574,7 @@ class Nav extends React.PureComponent {
}} }}
> >
My Account My Account
<TriangleIcon className="nav__item-header-triangle" /> <TriangleIcon className="nav__item-header-triangle" focusable="false" aria-hidden="true" />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">

View file

@ -14,13 +14,13 @@ class NavBasic extends React.PureComponent {
<nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}> <nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}>
<ul className="nav__items-left"> <ul className="nav__items-left">
<li className="nav__item-logo"> <li className="nav__item-logo">
<LogoIcon title="p5.js Logo" className="svg__logo" /> <LogoIcon role="img" aria-label="p5.js Logo" focusable="false" className="svg__logo" />
</li> </li>
{ this.props.onBack && ( { this.props.onBack && (
<li className="nav__item"> <li className="nav__item">
<button onClick={this.props.onBack}> <button onClick={this.props.onBack}>
<span className="nav__item-header"> <span className="nav__item-header">
<ArrowIcon title="Left arrow" /> <ArrowIcon focusable="false" aria-hidden="true" />
</span> </span>
Back to the editor Back to the editor
</button> </button>

View file

@ -9,15 +9,15 @@ const PreviewNav = ({ owner, project }) => (
<nav className="nav preview-nav"> <nav className="nav preview-nav">
<div className="nav__items-left"> <div className="nav__items-left">
<div className="nav__item-logo"> <div className="nav__item-logo">
<LogoIcon title="p5.js Logo" className="svg__logo" /> <LogoIcon role="img" aria-label="p5.js Logo" focusable="false" className="svg__logo" />
</div> </div>
<Link className="nav__item" to={`/${owner.username}/sketches/${project.id}`}>{project.name}</Link> <Link className="nav__item" to={`/${owner.username}/sketches/${project.id}`}>{project.name}</Link>
<p className="toolbar__project-owner">by</p> <p className="toolbar__project-owner">by</p>
<Link className="nav__item" to={`/${owner.username}/sketches/`}>{owner.username}</Link> <Link className="nav__item" to={`/${owner.username}/sketches/`}>{owner.username}</Link>
</div> </div>
<div className="nav__items-right"> <div className="nav__items-right">
<Link to={`/${owner.username}/sketches/${project.id}`}> <Link to={`/${owner.username}/sketches/${project.id}`} aria-label="Edit Sketch" >
<CodeIcon className="preview-nav__editor-svg" /> <CodeIcon className="preview-nav__editor-svg" focusable="false" aria-hidden="true" />
</Link> </Link>
</div> </div>
</nav> </nav>

View file

@ -80,8 +80,8 @@ class Overlay extends React.Component {
<h2 className="overlay__title">{title}</h2> <h2 className="overlay__title">{title}</h2>
<div className="overlay__actions"> <div className="overlay__actions">
{actions} {actions}
<button className="overlay__close-button" onClick={this.close} > <button className="overlay__close-button" onClick={this.close} aria-label={`Close ${title} overlay`} >
<ExitIcon title="close overlay" /> <ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
</header> </header>

View file

@ -12,7 +12,7 @@ function About(props) {
<title>p5.js Web Editor | About</title> <title>p5.js Web Editor | About</title>
</Helmet> </Helmet>
<div className="about__content-column"> <div className="about__content-column">
<SquareLogoIcon className="about__logo" title="p5.js Square Logo" /> <SquareLogoIcon className="about__logo" role="img" aria-label="p5.js Logo" focusable="false" />
{/* Video button to hello p5 video page */} {/* Video button to hello p5 video page */}
{/* <p className="about__play-video"> {/* <p className="about__play-video">
<a <a
@ -32,7 +32,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<AsteriskIcon className="about__content-column-asterisk" title="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Examples Examples
</a> </a>
</p> </p>
@ -42,7 +42,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<AsteriskIcon className="about__content-column-asterisk" title="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Learn Learn
</a> </a>
</p> </p>
@ -55,7 +55,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<AsteriskIcon className="about__content-column-asterisk" title="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Libraries Libraries
</a> </a>
</p> </p>
@ -65,7 +65,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<AsteriskIcon className="about__content-column-asterisk" title="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Reference Reference
</a> </a>
</p> </p>
@ -75,7 +75,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<AsteriskIcon className="about__content-column-asterisk" title="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Forum Forum
</a> </a>
</p> </p>

View file

@ -85,8 +85,9 @@ class AssetListRowBase extends React.Component {
onClick={this.toggleOptions} onClick={this.toggleOptions}
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
aria-label="Toggle Open/Close Asset Options"
> >
<DownFilledTriangleIcon title="Menu" /> <DownFilledTriangleIcon focusable="false" aria-hidden="true" />
</button> </button>
{optionsOpen && {optionsOpen &&
<ul <ul

View file

@ -82,21 +82,43 @@ class CollectionList extends React.Component {
return null; return null;
} }
_getButtonLabel = (fieldName, displayName) => {
const { field, direction } = this.props.sorting;
let buttonLabel;
if (field !== fieldName) {
if (field === 'name') {
buttonLabel = `Sort by ${displayName} ascending.`;
} else {
buttonLabel = `Sort by ${displayName} descending.`;
}
} else if (direction === SortingActions.DIRECTION.ASC) {
buttonLabel = `Sort by ${displayName} descending.`;
} else {
buttonLabel = `Sort by ${displayName} ascending.`;
}
return buttonLabel;
}
_renderFieldHeader = (fieldName, displayName) => { _renderFieldHeader = (fieldName, displayName) => {
const { field, direction } = this.props.sorting; const { field, direction } = this.props.sorting;
const headerClass = classNames({ const headerClass = classNames({
'sketches-table__header': true, 'sketches-table__header': true,
'sketches-table__header--selected': field === fieldName 'sketches-table__header--selected': field === fieldName
}); });
const buttonLabel = this._getButtonLabel(fieldName, displayName);
return ( return (
<th scope="col"> <th scope="col">
<button className="sketch-list__sort-button" onClick={() => this.props.toggleDirectionForField(fieldName)}> <button
className="sketch-list__sort-button"
onClick={() => this.props.toggleDirectionForField(fieldName)}
aria-label={buttonLabel}
>
<span className={headerClass}>{displayName}</span> <span className={headerClass}>{displayName}</span>
{field === fieldName && direction === SortingActions.DIRECTION.ASC && {field === fieldName && direction === SortingActions.DIRECTION.ASC &&
<ArrowUpIcon /> <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" />
} }
{field === fieldName && direction === SortingActions.DIRECTION.DESC && {field === fieldName && direction === SortingActions.DIRECTION.DESC &&
<ArrowDownIcon /> <ArrowDownIcon role="img" aria-label="Descending" focusable="false" />
} }
</button> </button>
</th> </th>

View file

@ -128,6 +128,7 @@ class CollectionListRowBase extends React.Component {
onClick={this.toggleOptions} onClick={this.toggleOptions}
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
aria-label="Toggle Open/Close collection options"
> >
<DownFilledTriangleIcon title="Menu" /> <DownFilledTriangleIcon title="Menu" />
</button> </button>

View file

@ -90,18 +90,18 @@ class Console extends React.Component {
<div className="preview-console__header"> <div className="preview-console__header">
<h2 className="preview-console__header-title">Console</h2> <h2 className="preview-console__header-title">Console</h2>
<div className="preview-console__header-buttons"> <div className="preview-console__header-buttons">
<button className="preview-console__clear" onClick={this.props.clearConsole} aria-label="clear console"> <button className="preview-console__clear" onClick={this.props.clearConsole} aria-label="Clear console">
Clear Clear
</button> </button>
<button <button
className="preview-console__collapse" className="preview-console__collapse"
onClick={this.props.collapseConsole} onClick={this.props.collapseConsole}
aria-label="collapse console" aria-label="Close console"
> >
<DownArrowIcon /> <DownArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
<button className="preview-console__expand" onClick={this.props.expandConsole} aria-label="expand console"> <button className="preview-console__expand" onClick={this.props.expandConsole} aria-label="Open console" >
<UpArrowIcon /> <UpArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
</div> </div>

View file

@ -69,8 +69,9 @@ class CopyableInput extends React.Component {
rel="noopener noreferrer" rel="noopener noreferrer"
href={value} href={value}
className="copyable-input__preview" className="copyable-input__preview"
aria-label={`Open ${label} view in new tab`}
> >
<ShareIcon title={`open ${label} view in new tab`} /> <ShareIcon focusable="false" aria-hidden="true" />
</a> </a>
} }
</div> </div>

View file

@ -3,6 +3,7 @@ import React from 'react';
import EditIcon from '../../../images/pencil.svg'; import EditIcon from '../../../images/pencil.svg';
// TODO I think this needs a description prop so that it's accessible
function EditableInput({ function EditableInput({
validate, value, emptyPlaceholder, InputComponent, inputProps, onChange validate, value, emptyPlaceholder, InputComponent, inputProps, onChange
}) { }) {
@ -47,9 +48,13 @@ function EditableInput({
return ( return (
<span className={classes}> <span className={classes}>
<button className="editable-input__label" onClick={beginEditing}> <button
className="editable-input__label"
onClick={beginEditing}
aria-label={`Edit ${displayValue} value`}
>
<span>{displayValue}</span> <span>{displayValue}</span>
<EditIcon className="editable-input__icon" /> <EditIcon className="editable-input__icon" focusable="false" aria-hidden="true" />
</button> </button>
<InputComponent <InputComponent

View file

@ -320,24 +320,26 @@ class Editor extends React.Component {
> >
<header className="editor__header"> <header className="editor__header">
<button <button
aria-label="collapse file navigation" aria-label="Open Sketch files navigation"
className="sidebar__contract" className="sidebar__contract"
onClick={this.props.collapseSidebar} onClick={this.props.collapseSidebar}
> >
<LeftArrowIcon /> <LeftArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
<button <button
aria-label="expand file navigation" aria-label="Close sketch files navigation"
className="sidebar__expand" className="sidebar__expand"
onClick={this.props.expandSidebar} onClick={this.props.expandSidebar}
> >
<RightArrowIcon /> <RightArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
<div className="editor__file-name"> <div className="editor__file-name">
<span> <span>
{this.props.file.name} {this.props.file.name}
<span className="editor__unsaved-changes"> <span className="editor__unsaved-changes">
{this.props.unsavedChanges ? <UnsavedChangesDotIcon /> : null} {this.props.unsavedChanges ?
<UnsavedChangesDotIcon role="img" aria-label="Sketch has unsaved changes" focusable="false" /> :
null}
</span> </span>
</span> </span>
<Timer <Timer

View file

@ -23,7 +23,7 @@ function Feedback(props) {
className="feedback__github-link" className="feedback__github-link"
> >
Go to Github Go to Github
<GitHubLogo className="feedback__github-logo" /> <GitHubLogo className="feedback__github-logo" focusable="false" aria-hidden="true" />
</a> </a>
</p> </p>
</div> </div>

View file

@ -184,7 +184,7 @@ export class FileNode extends React.Component {
<span className="file-item__spacer"></span> <span className="file-item__spacer"></span>
{ isFile && { isFile &&
<span className="sidebar__file-item-icon"> <span className="sidebar__file-item-icon">
<FileIcon /> <FileIcon focusable="false" aria-hidden="true" />
</span> </span>
} }
{ isFolder && { isFolder &&
@ -192,14 +192,16 @@ export class FileNode extends React.Component {
<button <button
className="sidebar__file-item-closed" className="sidebar__file-item-closed"
onClick={this.showFolderChildren} onClick={this.showFolderChildren}
aria-label="Open folder contents"
> >
<FolderRightIcon className="folder-right" /> <FolderRightIcon className="folder-right" focusable="false" aria-hidden="true" />
</button> </button>
<button <button
className="sidebar__file-item-open" className="sidebar__file-item-open"
onClick={this.hideFolderChildren} onClick={this.hideFolderChildren}
aria-label="Close file contents"
> >
<FolderDownIcon className="folder-down" /> <FolderDownIcon className="folder-down" focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
} }
@ -221,14 +223,14 @@ export class FileNode extends React.Component {
/> />
<button <button
className="sidebar__file-item-show-options" className="sidebar__file-item-show-options"
aria-label="view file options" aria-label="Toggle open/close file options"
ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }} ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }}
tabIndex="0" tabIndex="0"
onClick={this.toggleFileOptions} onClick={this.toggleFileOptions}
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
> >
<DownArrowIcon /> <DownArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
<div className="sidebar__file-item-options"> <div className="sidebar__file-item-options">
<ul title="file options"> <ul title="file options">

View file

@ -34,8 +34,12 @@ class NewFileModal extends React.Component {
<div className="modal-content"> <div className="modal-content">
<div className="modal__header"> <div className="modal__header">
<h2 className="modal__title">Create File</h2> <h2 className="modal__title">Create File</h2>
<button className="modal__exit-button" onClick={this.props.closeNewFileModal}> <button
<ExitIcon title="Close New File Modal" /> className="modal__exit-button"
onClick={this.props.closeNewFileModal}
aria-label="Close New File Modal"
>
<ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
<NewFileForm <NewFileForm

View file

@ -16,8 +16,12 @@ class NewFolderModal extends React.Component {
<div className="modal-content-folder"> <div className="modal-content-folder">
<div className="modal__header"> <div className="modal__header">
<h2 className="modal__title">Create Folder</h2> <h2 className="modal__title">Create Folder</h2>
<button className="modal__exit-button" onClick={this.props.closeModal}> <button
<ExitIcon title="Close New Folder Modal" /> className="modal__exit-button"
onClick={this.props.closeModal}
aria-label="Close New Folder Modal"
>
<ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
<NewFolderForm {...this.props} /> <NewFolderForm {...this.props} />

View file

@ -149,7 +149,7 @@ class Preferences extends React.Component {
aria-label="decrease font size" aria-label="decrease font size"
disabled={this.state.fontSize <= 8} disabled={this.state.fontSize <= 8}
> >
<MinusIcon title="Decrease Font Size" /> <MinusIcon focusable="false" aria-hidden="true" />
<h6 className="preference__label">Decrease</h6> <h6 className="preference__label">Decrease</h6>
</button> </button>
<form onSubmit={this.onFontInputSubmit}> <form onSubmit={this.onFontInputSubmit}>
@ -170,7 +170,7 @@ class Preferences extends React.Component {
aria-label="increase font size" aria-label="increase font size"
disabled={this.state.fontSize >= 36} disabled={this.state.fontSize >= 36}
> >
<PlusIcon title="Increase Font Size" /> <PlusIcon focusable="false" aria-hidden="true" />
<h6 className="preference__label">Increase</h6> <h6 className="preference__label">Increase</h6>
</button> </button>
</div> </div>

View file

@ -12,9 +12,9 @@ const Icons = ({ isAdded }) => {
return ( return (
<div className={classes}> <div className={classes}>
<CloseIcon className="quick-add__remove-icon" title="Remove from collection" /> <CloseIcon className="quick-add__remove-icon" role="img" aria-label="Descending" focusable="false" />
<CheckIcon className="quick-add__in-icon" title="In collection" /> <CheckIcon className="quick-add__in-icon" role="img" aria-label="Descending" focusable="false" />
<CloseIcon className="quick-add__add-icon" title="Add to collection" /> <CloseIcon className="quick-add__add-icon" role="img" aria-label="Descending" focusable="false" />
</div> </div>
); );
}; };

View file

@ -6,9 +6,11 @@ import Icons from './Icons';
const Item = ({ const Item = ({
isAdded, onSelect, name, url isAdded, onSelect, name, url
}) => ( }) => {
const buttonLabel = isAdded ? 'Remove from collection' : 'Add to collection';
return (
<li className="quick-add__item" onClick={onSelect}> { /* eslint-disable-line */ } <li className="quick-add__item" onClick={onSelect}> { /* eslint-disable-line */ }
<button className="quick-add__item-toggle" onClick={onSelect}> <button className="quick-add__item-toggle" onClick={onSelect} aria-label={buttonLabel}>
<Icons isAdded={isAdded} /> <Icons isAdded={isAdded} />
</button> </button>
<span className="quick-add__item-name">{name}</span> <span className="quick-add__item-name">{name}</span>
@ -22,6 +24,7 @@ const Item = ({
</Link> </Link>
</li> </li>
); );
};
const ItemType = PropTypes.shape({ const ItemType = PropTypes.shape({
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,

View file

@ -45,7 +45,7 @@ class Searchbar extends React.Component {
return ( return (
<div className={`searchbar ${searchValue === '' ? 'searchbar--is-empty' : ''}`}> <div className={`searchbar ${searchValue === '' ? 'searchbar--is-empty' : ''}`}>
<div className="searchbar__button"> <div className="searchbar__button">
<SearchIcon className="searchbar__icon" /> <SearchIcon className="searchbar__icon" focusable="false" aria-hidden="true" />
</div> </div>
<input <input
className="searchbar__input" className="searchbar__input"

View file

@ -75,7 +75,7 @@ class Sidebar extends React.Component {
</h3> </h3>
<div className="sidebar__icons"> <div className="sidebar__icons">
<button <button
aria-label="add file or folder" aria-label="Toggle open/close sketch file options"
className="sidebar__add" className="sidebar__add"
tabIndex="0" tabIndex="0"
ref={(element) => { this.sidebarOptions = element; }} ref={(element) => { this.sidebarOptions = element; }}
@ -83,7 +83,7 @@ class Sidebar extends React.Component {
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
> >
<DownArrowIcon /> <DownArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
<ul className="sidebar__project-options"> <ul className="sidebar__project-options">
<li> <li>

View file

@ -167,8 +167,9 @@ class SketchListRowBase extends React.Component {
onClick={this.toggleOptions} onClick={this.toggleOptions}
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
aria-label="Toggle Open/Close Sketch Options"
> >
<DownFilledTriangleIcon title="Menu" /> <DownFilledTriangleIcon focusable="false" aria-hidden="true" />
</button> </button>
{optionsOpen && {optionsOpen &&
<ul <ul
@ -325,7 +326,6 @@ class SketchList extends React.Component {
super(props); super(props);
this.props.getProjects(this.props.username); this.props.getProjects(this.props.username);
this.props.resetSorting(); this.props.resetSorting();
this._renderFieldHeader = this._renderFieldHeader.bind(this);
this.state = { this.state = {
isInitialDataLoad: true, isInitialDataLoad: true,
@ -368,21 +368,43 @@ class SketchList extends React.Component {
return null; return null;
} }
_renderFieldHeader(fieldName, displayName) { _getButtonLabel = (fieldName, displayName) => {
const { field, direction } = this.props.sorting;
let buttonLabel;
if (field !== fieldName) {
if (field === 'name') {
buttonLabel = `Sort by ${displayName} ascending.`;
} else {
buttonLabel = `Sort by ${displayName} descending.`;
}
} else if (direction === SortingActions.DIRECTION.ASC) {
buttonLabel = `Sort by ${displayName} descending.`;
} else {
buttonLabel = `Sort by ${displayName} ascending.`;
}
return buttonLabel;
}
_renderFieldHeader = (fieldName, displayName) => {
const { field, direction } = this.props.sorting; const { field, direction } = this.props.sorting;
const headerClass = classNames({ const headerClass = classNames({
'sketches-table__header': true, 'sketches-table__header': true,
'sketches-table__header--selected': field === fieldName 'sketches-table__header--selected': field === fieldName
}); });
const buttonLabel = this._getButtonLabel(fieldName, displayName);
return ( return (
<th scope="col"> <th scope="col">
<button className="sketch-list__sort-button" onClick={() => this.props.toggleDirectionForField(fieldName)}> <button
className="sketch-list__sort-button"
onClick={() => this.props.toggleDirectionForField(fieldName)}
aria-label={buttonLabel}
>
<span className={headerClass}>{displayName}</span> <span className={headerClass}>{displayName}</span>
{field === fieldName && direction === SortingActions.DIRECTION.ASC && {field === fieldName && direction === SortingActions.DIRECTION.ASC &&
<ArrowUpIcon /> <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" />
} }
{field === fieldName && direction === SortingActions.DIRECTION.DESC && {field === fieldName && direction === SortingActions.DIRECTION.DESC &&
<ArrowDownIcon /> <ArrowDownIcon role="img" aria-label="Descending" focusable="false" />
} }
</button> </button>
</th> </th>

View file

@ -12,8 +12,8 @@ function Toast(props) {
<p> <p>
{props.text} {props.text}
</p> </p>
<button className="toast__close" onClick={props.hideToast}> <button className="toast__close" onClick={props.hideToast} aria-label="Close Alert" >
<ExitIcon title="Close Keyboard Shortcuts Overlay" /> <ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</section> </section>
); );

View file

@ -59,6 +59,8 @@ class Toolbar extends React.Component {
'toolbar__project-name-container--editing': this.props.project.isEditingName 'toolbar__project-name-container--editing': this.props.project.isEditingName
}); });
const canEditProjectName = this.canEditProjectName();
return ( return (
<div className="toolbar"> <div className="toolbar">
<button <button
@ -68,25 +70,25 @@ class Toolbar extends React.Component {
this.props.setTextOutput(true); this.props.setTextOutput(true);
this.props.setGridOutput(true); this.props.setGridOutput(true);
}} }}
aria-label="play sketch" aria-label="Play sketch"
disabled={this.props.infiniteLoop} disabled={this.props.infiniteLoop}
> >
<PlayIcon title="Play Sketch" /> <PlayIcon focusable="false" aria-hidden="true" />
</button> </button>
<button <button
className={playButtonClass} className={playButtonClass}
onClick={this.props.startSketch} onClick={this.props.startSketch}
aria-label="play only visual sketch" aria-label="Play only visual sketch"
disabled={this.props.infiniteLoop} disabled={this.props.infiniteLoop}
> >
<PlayIcon title="Play only visual Sketch" /> <PlayIcon focusable="false" aria-hidden="true" />
</button> </button>
<button <button
className={stopButtonClass} className={stopButtonClass}
onClick={this.props.stopSketch} onClick={this.props.stopSketch}
aria-label="stop sketch" aria-label="Stop sketch"
> >
<StopIcon alt="Stop Sketch" /> <StopIcon focusable="false" aria-hidden="true" />
</button> </button>
<div className="toolbar__autorefresh"> <div className="toolbar__autorefresh">
<input <input
@ -102,24 +104,28 @@ class Toolbar extends React.Component {
</label> </label>
</div> </div>
<div className={nameContainerClass}> <div className={nameContainerClass}>
<a <button
className="toolbar__project-name" className="toolbar__project-name"
href={this.props.owner ? `/${this.props.owner.username}/sketches/${this.props.project.id}` : ''} onClick={() => {
onClick={(e) => { if (canEditProjectName) {
if (this.canEditProjectName()) {
e.preventDefault();
this.originalProjectName = this.props.project.name; this.originalProjectName = this.props.project.name;
this.props.showEditProjectName(); this.props.showEditProjectName();
setTimeout(() => this.projectNameInput.focus(), 0); setTimeout(() => this.projectNameInput.focus(), 0);
} }
}} }}
disabled={!canEditProjectName}
aria-label="Edit sketch name"
> >
<span>{this.props.project.name}</span> <span>{this.props.project.name}</span>
{ {
this.canEditProjectName() && canEditProjectName &&
<EditProjectNameIcon className="toolbar__edit-name-button" title="Edit Project Name" /> <EditProjectNameIcon
className="toolbar__edit-name-button"
focusable="false"
aria-hidden="true"
/>
} }
</a> </button>
<input <input
type="text" type="text"
maxLength="128" maxLength="128"
@ -149,9 +155,9 @@ class Toolbar extends React.Component {
<button <button
className={preferencesButtonClass} className={preferencesButtonClass}
onClick={this.props.openPreferences} onClick={this.props.openPreferences}
aria-label="preferences" aria-label="Open Preferences"
> >
<PreferencesIcon role="img" /> <PreferencesIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
); );

View file

@ -32,8 +32,12 @@ class UploadFileModal extends React.Component {
<div className="modal-content"> <div className="modal-content">
<div className="modal__header"> <div className="modal__header">
<h2 className="modal__title">Upload File</h2> <h2 className="modal__title">Upload File</h2>
<button className="modal__exit-button" onClick={this.props.closeModal}> <button
<ExitIcon title="Close New File Modal" /> className="modal__exit-button"
onClick={this.props.closeModal}
aria-label="Close upload file modal"
>
<ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
{ this.props.reachedTotalSizeLimit && { this.props.reachedTotalSizeLimit &&

View file

@ -85,7 +85,7 @@ class APIKeyForm extends React.Component {
disabled={this.state.keyLabel === ''} disabled={this.state.keyLabel === ''}
type="submit" type="submit"
> >
<PlusIcon className="api-key-form__create-icon" /> <PlusIcon className="api-key-form__create-icon" focusable="false" aria-hidden="true" />
Create Create
</button> </button>
</form> </form>

View file

@ -31,8 +31,12 @@ function APIKeyList({ apiKeys, onRemove }) {
<td>{format(new Date(key.createdAt), 'MMM D, YYYY h:mm A')}</td> <td>{format(new Date(key.createdAt), 'MMM D, YYYY h:mm A')}</td>
<td>{lastUsed}</td> <td>{lastUsed}</td>
<td className="api-key-list__action"> <td className="api-key-list__action">
<button className="api-key-list__delete-button" onClick={() => onRemove(key)}> <button
<TrashCanIcon title="Delete Key" /> className="api-key-list__delete-button"
onClick={() => onRemove(key)}
aria-label="Delete API Key"
>
<TrashCanIcon focusable="false" aria-hidden="true" />
</button> </button>
</td> </td>
</tr> </tr>

View file

@ -96,3 +96,15 @@ textarea:focus {
outline: none; outline: none;
box-shadow: 0 0 0 1px $outline-color; box-shadow: 0 0 0 1px $outline-color;
} }
// screen reader only class
// from https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html#hiding-content-visually
.sr-only:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}