Merge pull request #1420 from processing/aria-labels

Make SVG icons web accessible
This commit is contained in:
Cassie Tarakajian 2020-05-06 17:21:32 -04:00 committed by GitHub
commit ad11d2b89c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 1529 additions and 408 deletions

View file

@ -1,17 +1,20 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import InlineSVG from 'react-inlinesvg';
const addIcon = require('../images/plus.svg'); import AddIcon from '../images/plus.svg';
const removeIcon = require('../images/minus.svg'); import RemoveIcon from '../images/minus.svg';
const AddRemoveButton = ({ type, onClick }) => { const AddRemoveButton = ({ type, onClick }) => {
const alt = type === 'add' ? 'add to collection' : 'remove from collection'; const alt = type === 'add' ? 'Add to collection' : 'Remove from collection';
const icon = type === 'add' ? addIcon : removeIcon; const Icon = type === 'add' ? AddIcon : RemoveIcon;
return ( return (
<button className="overlay__close-button" onClick={onClick}> <button
<InlineSVG src={icon} alt={alt} /> className="overlay__close-button"
onClick={onClick}
aria-label={alt}
>
<Icon focusable="false" aria-hidden="true" />
</button> </button>
); );
}; };

View file

@ -3,7 +3,6 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { Link } from 'react-router'; import { Link } from 'react-router';
import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames'; import classNames from 'classnames';
import * as IDEActions from '../modules/IDE/actions/ide'; import * as IDEActions from '../modules/IDE/actions/ide';
import * as toastActions from '../modules/IDE/actions/toast'; import * as toastActions from '../modules/IDE/actions/toast';
@ -12,10 +11,10 @@ import { setAllAccessibleOutput } from '../modules/IDE/actions/preferences';
import { logoutUser } from '../modules/User/actions'; import { logoutUser } from '../modules/User/actions';
import { metaKeyName, } from '../utils/metaKey'; import { metaKeyName, } from '../utils/metaKey';
import caretLeft from '../images/left-arrow.svg';
const triangleUrl = require('../images/down-filled-triangle.svg'); import CaretLeftIcon from '../images/left-arrow.svg';
const logoUrl = require('../images/p5js-logo-small.svg'); import TriangleIcon from '../images/down-filled-triangle.svg';
import LogoIcon from '../images/p5js-logo-small.svg';
const __process = (typeof global !== 'undefined' ? global : window).process; const __process = (typeof global !== 'undefined' ? global : window).process;
@ -229,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">
<InlineSVG src={logoUrl} alt="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">
<InlineSVG src={caretLeft} 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>
@ -247,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">
<InlineSVG src={logoUrl} alt="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
@ -261,7 +260,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">File</span> <span className="nav__item-header">File</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <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">
@ -363,7 +362,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">Edit</span> <span className="nav__item-header">Edit</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <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">
@ -423,7 +422,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">Sketch</span> <span className="nav__item-header">Sketch</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <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">
@ -498,7 +497,7 @@ class Nav extends React.PureComponent {
}} }}
> >
<span className="nav__item-header">Help</span> <span className="nav__item-header">Help</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <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">
@ -575,7 +574,7 @@ class Nav extends React.PureComponent {
}} }}
> >
My Account My Account
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <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

@ -1,9 +1,8 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
const logoUrl = require('../images/p5js-logo-small.svg'); import LogoIcon from '../images/p5js-logo-small.svg';
const arrowUrl = require('../images/triangle-arrow-left.svg'); import ArrowIcon from '../images/triangle-arrow-left.svg';
class NavBasic extends React.PureComponent { class NavBasic extends React.PureComponent {
static defaultProps = { static defaultProps = {
@ -15,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">
<InlineSVG src={logoUrl} alt="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">
<InlineSVG src={arrowUrl} alt="Left arrow" /> <ArrowIcon focusable="false" aria-hidden="true" />
</span> </span>
Back to the editor Back to the editor
</button> </button>

View file

@ -1,24 +1,23 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import InlineSVG from 'react-inlinesvg';
const logoUrl = require('../images/p5js-logo-small.svg'); import LogoIcon from '../images/p5js-logo-small.svg';
const editorUrl = require('../images/code.svg'); import CodeIcon from '../images/code.svg';
const PreviewNav = ({ owner, project }) => ( 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">
<InlineSVG src={logoUrl} alt="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" >
<InlineSVG className="preview-nav__editor-svg" src={editorUrl} /> <CodeIcon className="preview-nav__editor-svg" focusable="false" aria-hidden="true" />
</Link> </Link>
</div> </div>
</nav> </nav>

View file

@ -11,8 +11,11 @@ exports[`Nav renders correctly 1`] = `
<li <li
className="nav__item-logo" className="nav__item-logo"
> >
<span <test-file-stub
className="isvg loading svg__logo" aria-label="p5.js Logo"
className="svg__logo"
focusable="false"
role="img"
/> />
</li> </li>
<li <li
@ -29,8 +32,10 @@ exports[`Nav renders correctly 1`] = `
> >
File File
</span> </span>
<span <test-file-stub
className="isvg loading nav__item-header-triangle" aria-hidden="true"
className="nav__item-header-triangle"
focusable="false"
/> />
</button> </button>
<ul <ul
@ -108,8 +113,10 @@ exports[`Nav renders correctly 1`] = `
> >
Edit Edit
</span> </span>
<span <test-file-stub
className="isvg loading nav__item-header-triangle" aria-hidden="true"
className="nav__item-header-triangle"
focusable="false"
/> />
</button> </button>
<ul <ul
@ -201,8 +208,10 @@ exports[`Nav renders correctly 1`] = `
> >
Sketch Sketch
</span> </span>
<span <test-file-stub
className="isvg loading nav__item-header-triangle" aria-hidden="true"
className="nav__item-header-triangle"
focusable="false"
/> />
</button> </button>
<ul <ul
@ -282,8 +291,10 @@ exports[`Nav renders correctly 1`] = `
> >
Help Help
</span> </span>
<span <test-file-stub
className="isvg loading nav__item-header-triangle" aria-hidden="true"
className="nav__item-header-triangle"
focusable="false"
/> />
</button> </button>
<ul <ul

View file

@ -1,9 +1,8 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { browserHistory } from 'react-router'; import { browserHistory } from 'react-router';
const exitUrl = require('../../../images/exit.svg'); import ExitIcon from '../../../images/exit.svg';
class Overlay extends React.Component { class Overlay extends React.Component {
constructor(props) { constructor(props) {
@ -81,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`} >
<InlineSVG src={exitUrl} alt="close overlay" /> <ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
</header> </header>

View file

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
const squareLogoUrl = require('../../../images/p5js-square-logo.svg'); import SquareLogoIcon from '../../../images/p5js-square-logo.svg';
// const playUrl = require('../../../images/play.svg'); // import PlayIcon from '../../../images/play.svg';
const asteriskUrl = require('../../../images/p5-asterisk.svg'); import AsteriskIcon from '../../../images/p5-asterisk.svg';
function About(props) { function About(props) {
return ( return (
@ -13,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">
<InlineSVG className="about__logo" src={squareLogoUrl} alt="p5js 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
@ -21,7 +20,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<InlineSVG className="about__play-video-button" src={playUrl} alt="Play Hello Video" /> <PlayIcon className="about__play-video-button" title="Play Hello Video" />
Play hello! video</a> Play hello! video</a>
</p> */} </p> */}
</div> </div>
@ -33,7 +32,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<InlineSVG className="about__content-column-asterisk" src={asteriskUrl} alt="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Examples Examples
</a> </a>
</p> </p>
@ -43,7 +42,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<InlineSVG className="about__content-column-asterisk" src={asteriskUrl} alt="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Learn Learn
</a> </a>
</p> </p>
@ -56,7 +55,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<InlineSVG className="about__content-column-asterisk" src={asteriskUrl} alt="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Libraries Libraries
</a> </a>
</p> </p>
@ -66,7 +65,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<InlineSVG className="about__content-column-asterisk" src={asteriskUrl} alt="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Reference Reference
</a> </a>
</p> </p>
@ -76,7 +75,7 @@ function About(props) {
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<InlineSVG className="about__content-column-asterisk" src={asteriskUrl} alt="p5 asterisk" /> <AsteriskIcon className="about__content-column-asterisk" aria-hidden="true" focusable="false" />
Forum Forum
</a> </a>
</p> </p>

View file

@ -5,11 +5,10 @@ import { bindActionCreators } from 'redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import InlineSVG from 'react-inlinesvg';
import Loader from '../../App/components/loader'; import Loader from '../../App/components/loader';
import * as AssetActions from '../actions/assets'; import * as AssetActions from '../actions/assets';
import downFilledTriangle from '../../../images/down-filled-triangle.svg'; import DownFilledTriangleIcon from '../../../images/down-filled-triangle.svg';
class AssetListRowBase extends React.Component { class AssetListRowBase extends React.Component {
constructor(props) { constructor(props) {
@ -86,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"
> >
<InlineSVG src={downFilledTriangle} alt="Menu" /> <DownFilledTriangleIcon focusable="false" aria-hidden="true" />
</button> </button>
{optionsOpen && {optionsOpen &&
<ul <ul

View file

@ -1,7 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import InlineSVG from 'react-inlinesvg';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import classNames from 'classnames'; import classNames from 'classnames';
@ -19,8 +18,8 @@ import { SketchSearchbar } from '../Searchbar';
import CollectionListRow from './CollectionListRow'; import CollectionListRow from './CollectionListRow';
const arrowUp = require('../../../../images/sort-arrow-up.svg'); import ArrowUpIcon from '../../../../images/sort-arrow-up.svg';
const arrowDown = require('../../../../images/sort-arrow-down.svg'); import ArrowDownIcon from '../../../../images/sort-arrow-down.svg';
class CollectionList extends React.Component { class CollectionList extends React.Component {
constructor(props) { constructor(props) {
@ -83,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 &&
<InlineSVG src={arrowUp} /> <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" />
} }
{field === fieldName && direction === SortingActions.DIRECTION.DESC && {field === fieldName && direction === SortingActions.DIRECTION.DESC &&
<InlineSVG src={arrowDown} /> <ArrowDownIcon role="img" aria-label="Descending" focusable="false" />
} }
</button> </button>
</th> </th>

View file

@ -1,7 +1,6 @@
import format from 'date-fns/format'; import format from 'date-fns/format';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -10,7 +9,7 @@ import * as CollectionsActions from '../../actions/collections';
import * as IdeActions from '../../actions/ide'; import * as IdeActions from '../../actions/ide';
import * as ToastActions from '../../actions/toast'; import * as ToastActions from '../../actions/toast';
const downFilledTriangle = require('../../../../images/down-filled-triangle.svg'); import DownFilledTriangleIcon from '../../../../images/down-filled-triangle.svg';
class CollectionListRowBase extends React.Component { class CollectionListRowBase extends React.Component {
static projectInCollection(project, collection) { static projectInCollection(project, collection) {
@ -129,8 +128,9 @@ 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"
> >
<InlineSVG src={downFilledTriangle} alt="Menu" /> <DownFilledTriangleIcon title="Menu" />
</button> </button>
{optionsOpen && {optionsOpen &&
<ul <ul

View file

@ -1,27 +1,26 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames'; import classNames from 'classnames';
import { Console as ConsoleFeed } from 'console-feed'; import { Console as ConsoleFeed } from 'console-feed';
import { import {
CONSOLE_FEED_WITHOUT_ICONS, CONSOLE_FEED_LIGHT_STYLES, CONSOLE_FEED_WITHOUT_ICONS, CONSOLE_FEED_LIGHT_STYLES,
CONSOLE_FEED_DARK_STYLES, CONSOLE_FEED_CONTRAST_STYLES CONSOLE_FEED_DARK_STYLES, CONSOLE_FEED_CONTRAST_STYLES
} from '../../../styles/components/_console-feed.scss'; } from '../../../styles/components/_console-feed.scss';
import warnLightUrl from '../../../images/console-warn-light.svg'; import warnLightUrl from '../../../images/console-warn-light.svg?byUrl';
import warnDarkUrl from '../../../images/console-warn-dark.svg'; import warnDarkUrl from '../../../images/console-warn-dark.svg?byUrl';
import warnContrastUrl from '../../../images/console-warn-contrast.svg'; import warnContrastUrl from '../../../images/console-warn-contrast.svg?byUrl';
import errorLightUrl from '../../../images/console-error-light.svg'; import errorLightUrl from '../../../images/console-error-light.svg?byUrl';
import errorDarkUrl from '../../../images/console-error-dark.svg'; import errorDarkUrl from '../../../images/console-error-dark.svg?byUrl';
import errorContrastUrl from '../../../images/console-error-contrast.svg'; import errorContrastUrl from '../../../images/console-error-contrast.svg?byUrl';
import debugLightUrl from '../../../images/console-debug-light.svg'; import debugLightUrl from '../../../images/console-debug-light.svg?byUrl';
import debugDarkUrl from '../../../images/console-debug-dark.svg'; import debugDarkUrl from '../../../images/console-debug-dark.svg?byUrl';
import debugContrastUrl from '../../../images/console-debug-contrast.svg'; import debugContrastUrl from '../../../images/console-debug-contrast.svg?byUrl';
import infoLightUrl from '../../../images/console-info-light.svg'; import infoLightUrl from '../../../images/console-info-light.svg?byUrl';
import infoDarkUrl from '../../../images/console-info-dark.svg'; import infoDarkUrl from '../../../images/console-info-dark.svg?byUrl';
import infoContrastUrl from '../../../images/console-info-contrast.svg'; import infoContrastUrl from '../../../images/console-info-contrast.svg?byUrl';
const upArrowUrl = require('../../../images/up-arrow.svg'); import UpArrowIcon from '../../../images/up-arrow.svg';
const downArrowUrl = require('../../../images/down-arrow.svg'); import DownArrowIcon from '../../../images/down-arrow.svg';
class Console extends React.Component { class Console extends React.Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@ -91,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"
> >
<InlineSVG src={downArrowUrl} /> <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" >
<InlineSVG src={upArrowUrl} /> <UpArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
</div> </div>

View file

@ -1,10 +1,9 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Clipboard from 'clipboard'; import Clipboard from 'clipboard';
import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames'; import classNames from 'classnames';
import shareUrl from '../../../images/share.svg'; import ShareIcon from '../../../images/share.svg';
class CopyableInput extends React.Component { class CopyableInput extends React.Component {
constructor(props) { constructor(props) {
@ -70,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`}
> >
<InlineSVG src={shareUrl} alt={`open ${label} view in new tab`} /> <ShareIcon focusable="false" aria-hidden="true" />
</a> </a>
} }
</div> </div>

View file

@ -1,13 +1,9 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
const editIconUrl = require('../../../images/pencil.svg'); import EditIcon from '../../../images/pencil.svg';
function EditIcon() {
return <InlineSVG className="editable-input__icon" src={editIconUrl} alt="Edit" />;
}
// 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
}) { }) {
@ -52,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 /> <EditIcon className="editable-input__icon" focusable="false" aria-hidden="true" />
</button> </button>
<InputComponent <InputComponent

View file

@ -24,7 +24,6 @@ import 'codemirror/addon/edit/matchbrackets';
import { JSHINT } from 'jshint'; import { JSHINT } from 'jshint';
import { CSSLint } from 'csslint'; import { CSSLint } from 'csslint';
import { HTMLHint } from 'htmlhint'; import { HTMLHint } from 'htmlhint';
import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames'; import classNames from 'classnames';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import '../../../utils/htmlmixed'; import '../../../utils/htmlmixed';
@ -36,6 +35,11 @@ import { metaKey, } from '../../../utils/metaKey';
import search from '../../../utils/codemirror-search'; import search from '../../../utils/codemirror-search';
import beepUrl from '../../../sounds/audioAlert.mp3';
import UnsavedChangesDotIcon from '../../../images/unsaved-changes-dot.svg';
import RightArrowIcon from '../../../images/right-arrow.svg';
import LeftArrowIcon from '../../../images/left-arrow.svg';
search(CodeMirror); search(CodeMirror);
const beautifyCSS = beautifyJS.css; const beautifyCSS = beautifyJS.css;
@ -45,11 +49,6 @@ window.JSHINT = JSHINT;
window.CSSLint = CSSLint; window.CSSLint = CSSLint;
window.HTMLHint = HTMLHint; window.HTMLHint = HTMLHint;
const beepUrl = require('../../../sounds/audioAlert.mp3');
const unsavedChangesDotUrl = require('../../../images/unsaved-changes-dot.svg');
const rightArrowUrl = require('../../../images/right-arrow.svg');
const leftArrowUrl = require('../../../images/left-arrow.svg');
const IS_TAB_INDENT = false; const IS_TAB_INDENT = false;
const INDENTATION_AMOUNT = 2; const INDENTATION_AMOUNT = 2;
@ -321,23 +320,27 @@ 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}
> >
<InlineSVG src={leftArrowUrl} /> <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}
> >
<InlineSVG src={rightArrowUrl} /> <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}
{this.props.unsavedChanges ? <InlineSVG src={unsavedChangesDotUrl} /> : null} <span className="editor__unsaved-changes">
{this.props.unsavedChanges ?
<UnsavedChangesDotIcon role="img" aria-label="Sketch has unsaved changes" focusable="false" /> :
null}
</span>
</span> </span>
<Timer <Timer
projectSavedTime={this.props.projectSavedTime} projectSavedTime={this.props.projectSavedTime}

View file

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import githubLogoUrl from '../../../images/github.svg'; import GitHubLogo from '../../../images/github.svg';
function Feedback(props) { function Feedback(props) {
return ( return (
@ -24,7 +23,7 @@ function Feedback(props) {
className="feedback__github-link" className="feedback__github-link"
> >
Go to Github Go to Github
<InlineSVG className="feedback__github-logo" src={githubLogoUrl} /> <GitHubLogo className="feedback__github-logo" focusable="false" aria-hidden="true" />
</a> </a>
</p> </p>
</div> </div>

View file

@ -2,14 +2,13 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import InlineSVG from 'react-inlinesvg';
import classNames from 'classnames'; import classNames from 'classnames';
import * as IDEActions from '../actions/ide'; import * as IDEActions from '../actions/ide';
import * as FileActions from '../actions/files'; import * as FileActions from '../actions/files';
import downArrowUrl from '../../../images/down-filled-triangle.svg'; import DownArrowIcon from '../../../images/down-filled-triangle.svg';
import folderRightUrl from '../../../images/triangle-arrow-right.svg'; import FolderRightIcon from '../../../images/triangle-arrow-right.svg';
import folderDownUrl from '../../../images/triangle-arrow-down.svg'; import FolderDownIcon from '../../../images/triangle-arrow-down.svg';
import fileUrl from '../../../images/file.svg'; import FileIcon from '../../../images/file.svg';
export class FileNode extends React.Component { export class FileNode extends React.Component {
constructor(props) { constructor(props) {
@ -185,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">
<InlineSVG src={fileUrl} /> <FileIcon focusable="false" aria-hidden="true" />
</span> </span>
} }
{ isFolder && { isFolder &&
@ -193,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"
> >
<InlineSVG className="folder-right" src={folderRightUrl} /> <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"
> >
<InlineSVG className="folder-down" src={folderDownUrl} /> <FolderDownIcon className="folder-down" focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
} }
@ -222,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}
> >
<InlineSVG src={downArrowUrl} /> <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

@ -3,13 +3,12 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux'; import { bindActionCreators, compose } from 'redux';
import { reduxForm } from 'redux-form'; import { reduxForm } from 'redux-form';
import InlineSVG from 'react-inlinesvg';
import NewFileForm from './NewFileForm'; import NewFileForm from './NewFileForm';
import { closeNewFileModal } from '../actions/ide'; import { closeNewFileModal } from '../actions/ide';
import { createFile } from '../actions/files'; import { createFile } from '../actions/files';
import { CREATE_FILE_REGEX } from '../../../../server/utils/fileUtils'; import { CREATE_FILE_REGEX } from '../../../../server/utils/fileUtils';
const exitUrl = require('../../../images/exit.svg'); import ExitIcon from '../../../images/exit.svg';
// At some point this will probably be generalized to a generic modal // At some point this will probably be generalized to a generic modal
@ -35,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
<InlineSVG src={exitUrl} alt="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

@ -1,10 +1,9 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { reduxForm } from 'redux-form'; import { reduxForm } from 'redux-form';
import InlineSVG from 'react-inlinesvg';
import NewFolderForm from './NewFolderForm'; import NewFolderForm from './NewFolderForm';
const exitUrl = require('../../../images/exit.svg'); import ExitIcon from '../../../images/exit.svg';
class NewFolderModal extends React.Component { class NewFolderModal extends React.Component {
componentDidMount() { componentDidMount() {
@ -17,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
<InlineSVG src={exitUrl} alt="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

@ -1,15 +1,14 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
// import { bindActionCreators } from 'redux'; // import { bindActionCreators } from 'redux';
// import { connect } from 'react-redux'; // import { connect } from 'react-redux';
// import * as PreferencesActions from '../actions/preferences'; // import * as PreferencesActions from '../actions/preferences';
const plusUrl = require('../../../images/plus.svg'); import PlusIcon from '../../../images/plus.svg';
const minusUrl = require('../../../images/minus.svg'); import MinusIcon from '../../../images/minus.svg';
const beepUrl = require('../../../sounds/audioAlert.mp3'); import beepUrl from '../../../sounds/audioAlert.mp3';
class Preferences extends React.Component { class Preferences extends React.Component {
constructor(props) { constructor(props) {
@ -150,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}
> >
<InlineSVG src={minusUrl} alt="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}>
@ -171,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}
> >
<InlineSVG src={plusUrl} alt="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

@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import InlineSVG from 'react-inlinesvg';
const check = require('../../../../images/check_encircled.svg'); import CheckIcon from '../../../../images/check_encircled.svg';
const close = require('../../../../images/close.svg'); import CloseIcon from '../../../../images/close.svg';
const Icons = ({ isAdded }) => { const Icons = ({ isAdded }) => {
const classes = [ const classes = [
@ -13,9 +12,9 @@ const Icons = ({ isAdded }) => {
return ( return (
<div className={classes}> <div className={classes}>
<InlineSVG className="quick-add__remove-icon" src={close} alt="Remove from collection" /> <CloseIcon className="quick-add__remove-icon" role="img" aria-label="Descending" focusable="false" />
<InlineSVG className="quick-add__in-icon" src={check} alt="In collection" /> <CheckIcon className="quick-add__in-icon" role="img" aria-label="Descending" focusable="false" />
<InlineSVG className="quick-add__add-icon" src={close} alt="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>
@ -21,7 +23,8 @@ const Item = ({
View View
</Link> </Link>
</li> </li>
); );
};
const ItemType = PropTypes.shape({ const ItemType = PropTypes.shape({
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,

View file

@ -1,9 +1,8 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
const searchIcon = require('../../../../images/magnifyingglass.svg'); import SearchIcon from '../../../../images/magnifyingglass.svg';
class Searchbar extends React.Component { class Searchbar extends React.Component {
constructor(props) { constructor(props) {
@ -46,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">
<InlineSVG className="searchbar__icon" src={searchIcon} /> <SearchIcon className="searchbar__icon" focusable="false" aria-hidden="true" />
</div> </div>
<input <input
className="searchbar__input" className="searchbar__input"

View file

@ -1,10 +1,9 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import InlineSVG from 'react-inlinesvg';
import ConnectedFileNode from './FileNode'; import ConnectedFileNode from './FileNode';
const downArrowUrl = require('../../../images/down-filled-triangle.svg'); import DownArrowIcon from '../../../images/down-filled-triangle.svg';
class Sidebar extends React.Component { class Sidebar extends React.Component {
constructor(props) { constructor(props) {
@ -76,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; }}
@ -84,7 +83,7 @@ class Sidebar extends React.Component {
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
> >
<InlineSVG src={downArrowUrl} /> <DownArrowIcon focusable="false" aria-hidden="true" />
</button> </button>
<ul className="sidebar__project-options"> <ul className="sidebar__project-options">
<li> <li>

View file

@ -2,7 +2,6 @@ import format from 'date-fns/format';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import InlineSVG from 'react-inlinesvg';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -19,9 +18,9 @@ import Loader from '../../App/components/loader';
import Overlay from '../../App/components/Overlay'; import Overlay from '../../App/components/Overlay';
import AddToCollectionList from './AddToCollectionList'; import AddToCollectionList from './AddToCollectionList';
const arrowUp = require('../../../images/sort-arrow-up.svg'); import ArrowUpIcon from '../../../images/sort-arrow-up.svg';
const arrowDown = require('../../../images/sort-arrow-down.svg'); import ArrowDownIcon from '../../../images/sort-arrow-down.svg';
const downFilledTriangle = require('../../../images/down-filled-triangle.svg'); import DownFilledTriangleIcon from '../../../images/down-filled-triangle.svg';
class SketchListRowBase extends React.Component { class SketchListRowBase extends React.Component {
constructor(props) { constructor(props) {
@ -168,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"
> >
<InlineSVG src={downFilledTriangle} alt="Menu" /> <DownFilledTriangleIcon focusable="false" aria-hidden="true" />
</button> </button>
{optionsOpen && {optionsOpen &&
<ul <ul
@ -326,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,
@ -369,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 &&
<InlineSVG src={arrowUp} /> <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" />
} }
{field === fieldName && direction === SortingActions.DIRECTION.DESC && {field === fieldName && direction === SortingActions.DIRECTION.DESC &&
<InlineSVG src={arrowDown} /> <ArrowDownIcon role="img" aria-label="Descending" focusable="false" />
} }
</button> </button>
</th> </th>

View file

@ -2,10 +2,9 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import InlineSVG from 'react-inlinesvg';
import * as ToastActions from '../actions/toast'; import * as ToastActions from '../actions/toast';
const exitUrl = require('../../../images/exit.svg'); import ExitIcon from '../../../images/exit.svg';
function Toast(props) { function Toast(props) {
return ( return (
@ -13,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" >
<InlineSVG src={exitUrl} alt="Close Keyboard Shortcuts Overlay" /> <ExitIcon focusable="false" aria-hidden="true" />
</button> </button>
</section> </section>
); );

View file

@ -3,16 +3,14 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import classNames from 'classnames'; import classNames from 'classnames';
import InlineSVG from 'react-inlinesvg';
import * as IDEActions from '../actions/ide'; import * as IDEActions from '../actions/ide';
import * as preferenceActions from '../actions/preferences'; import * as preferenceActions from '../actions/preferences';
import * as projectActions from '../actions/project'; import * as projectActions from '../actions/project';
const playUrl = require('../../../images/play.svg'); import PlayIcon from '../../../images/play.svg';
const stopUrl = require('../../../images/stop.svg'); import StopIcon from '../../../images/stop.svg';
const preferencesUrl = require('../../../images/preferences.svg'); import PreferencesIcon from '../../../images/preferences.svg';
const editProjectNameUrl = require('../../../images/pencil.svg'); import EditProjectNameIcon from '../../../images/pencil.svg';
class Toolbar extends React.Component { class Toolbar extends React.Component {
constructor(props) { constructor(props) {
@ -61,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
@ -70,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}
> >
<InlineSVG src={playUrl} alt="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}
> >
<InlineSVG src={playUrl} alt="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"
> >
<InlineSVG src={stopUrl} alt="Stop Sketch" /> <StopIcon focusable="false" aria-hidden="true" />
</button> </button>
<div className="toolbar__autorefresh"> <div className="toolbar__autorefresh">
<input <input
@ -104,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 &&
<InlineSVG className="toolbar__edit-name-button" src={editProjectNameUrl} alt="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"
@ -151,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"
> >
<InlineSVG src={preferencesUrl} alt="Preferences" /> <PreferencesIcon focusable="false" aria-hidden="true" />
</button> </button>
</div> </div>
); );

View file

@ -2,11 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import InlineSVG from 'react-inlinesvg';
import prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import FileUploader from './FileUploader'; import FileUploader from './FileUploader';
import { getreachedTotalSizeLimit } from '../selectors/users'; import { getreachedTotalSizeLimit } from '../selectors/users';
import exitUrl from '../../../images/exit.svg'; import ExitIcon from '../../../images/exit.svg';
const __process = (typeof global !== 'undefined' ? global : window).process; const __process = (typeof global !== 'undefined' ? global : window).process;
const limit = __process.env.UPLOAD_LIMIT || 250000000; const limit = __process.env.UPLOAD_LIMIT || 250000000;
@ -33,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
<InlineSVG src={exitUrl} alt="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

@ -1,12 +1,11 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import CopyableInput from '../../IDE/components/CopyableInput'; import CopyableInput from '../../IDE/components/CopyableInput';
import APIKeyList from './APIKeyList'; import APIKeyList from './APIKeyList';
const plusIcon = require('../../../images/plus-icon.svg'); import PlusIcon from '../../../images/plus-icon.svg';
export const APIKeyPropType = PropTypes.shape({ export const APIKeyPropType = PropTypes.shape({
id: PropTypes.object.isRequired, id: PropTypes.object.isRequired,
@ -86,7 +85,7 @@ class APIKeyForm extends React.Component {
disabled={this.state.keyLabel === ''} disabled={this.state.keyLabel === ''}
type="submit" type="submit"
> >
<InlineSVG src={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

@ -1,13 +1,12 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import InlineSVG from 'react-inlinesvg';
import format from 'date-fns/format'; import format from 'date-fns/format';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { APIKeyPropType } from './APIKeyForm'; import { APIKeyPropType } from './APIKeyForm';
const trashCan = require('../../../images/trash-can.svg'); import TrashCanIcon from '../../../images/trash-can.svg';
function APIKeyList({ apiKeys, onRemove }) { function APIKeyList({ apiKeys, onRemove }) {
return ( return (
@ -32,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
<InlineSVG src={trashCan} alt="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

@ -2,7 +2,6 @@ import format from 'date-fns/format';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import InlineSVG from 'react-inlinesvg';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -20,11 +19,11 @@ import Overlay from '../../App/components/Overlay';
import AddToCollectionSketchList from '../../IDE/components/AddToCollectionSketchList'; import AddToCollectionSketchList from '../../IDE/components/AddToCollectionSketchList';
import CopyableInput from '../../IDE/components/CopyableInput'; import CopyableInput from '../../IDE/components/CopyableInput';
import { SketchSearchbar } from '../../IDE/components/Searchbar'; import { SketchSearchbar } from '../../IDE/components/Searchbar';
import dropdownArrow from '../../../images/down-arrow.svg';
const arrowUp = require('../../../images/sort-arrow-up.svg'); import DropdownArrowIcon from '../../../images/down-arrow.svg';
const arrowDown = require('../../../images/sort-arrow-down.svg'); import ArrowUpIcon from '../../../images/sort-arrow-up.svg';
const removeIcon = require('../../../images/close.svg'); import ArrowDownIcon from '../../../images/sort-arrow-down.svg';
import RemoveIcon from '../../../images/close.svg';
const ShareURL = ({ value }) => { const ShareURL = ({ value }) => {
const [showURL, setShowURL] = useState(false); const [showURL, setShowURL] = useState(false);
@ -54,9 +53,10 @@ const ShareURL = ({ value }) => {
<button <button
className="collection-share__button" className="collection-share__button"
onClick={() => setShowURL(!showURL)} onClick={() => setShowURL(!showURL)}
aria-label="Show collection share URL"
> >
<span>Share</span> <span>Share</span>
<InlineSVG className="collection-share__arrow" src={dropdownArrow} /> <DropdownArrowIcon className="collection-share__arrow" focusable="false" aria-hidden="true" />
</button> </button>
{ showURL && { showURL &&
<div className="collection__share-dropdown"> <div className="collection__share-dropdown">
@ -98,8 +98,9 @@ class CollectionItemRowBase extends React.Component {
<button <button
className="collection-row__remove-button" className="collection-row__remove-button"
onClick={this.handleSketchRemove} onClick={this.handleSketchRemove}
aria-label="Remove sketch from collection"
> >
<InlineSVG src={removeIcon} alt="Remove" /> <RemoveIcon focusable="false" aria-hidden="true" />
</button> </button>
</td> </td>
</tr>); </tr>);
@ -296,21 +297,43 @@ class Collection 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 &&
<InlineSVG src={arrowUp} /> <ArrowUpIcon role="img" aria-label="Ascending" focusable="false" />
} }
{field === fieldName && direction === SortingActions.DIRECTION.DESC && {field === fieldName && direction === SortingActions.DIRECTION.DESC &&
<InlineSVG src={arrowDown} /> <ArrowDownIcon role="img" aria-label="Descending" focusable="false" />
} }
</button> </button>
</th> </th>

View file

@ -1,8 +1,7 @@
import InlineSVG from 'react-inlinesvg';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
const githubUrl = require('../../../images/github.svg'); import GithubIcon from '../../../images/github.svg';
function GithubButton(props) { function GithubButton(props) {
return ( return (
@ -10,7 +9,7 @@ function GithubButton(props) {
className="github-button" className="github-button"
href="/auth/github" href="/auth/github"
> >
<InlineSVG src={githubUrl} className="github-icon" /> <GithubIcon className="github-icon" role="img" aria-label="GitHub Logo" focusable="false" />
<span>{props.buttonText}</span> <span>{props.buttonText}</span>
</a> </a>
); );

View file

@ -1,8 +1,7 @@
import InlineSVG from 'react-inlinesvg';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import googleUrl from '../../../images/google.svg'; import GoogleIcon from '../../../images/google.svg';
function GoogleButton(props) { function GoogleButton(props) {
return ( return (
@ -10,7 +9,7 @@ function GoogleButton(props) {
className="google-button" className="google-button"
href="/auth/google/" href="/auth/google/"
> >
<InlineSVG src={googleUrl} className="google-icon" /> <GoogleIcon className="google-icon" role="img" aria-label="Google Logo" focusable="false" />
<span>{props.buttonText}</span> <span>{props.buttonText}</span>
</a> </a>
); );

View file

@ -21,13 +21,13 @@
@mixin icon() { @mixin icon() {
@include themify() { @include themify() {
color: getThemifyVariable('icon-color'); color: getThemifyVariable('icon-color');
& g, & polygon { & g, & polygon, & path {
opacity: 1; opacity: 1;
fill: getThemifyVariable('icon-color'); fill: getThemifyVariable('icon-color');
} }
&:hover { &:hover {
color: getThemifyVariable('icon-hover-color'); color: getThemifyVariable('icon-hover-color');
& g, & polygon { & g, & polygon, & path {
opacity: 1; opacity: 1;
fill: getThemifyVariable('icon-hover-color'); fill: getThemifyVariable('icon-hover-color');
} }

View file

@ -5,26 +5,25 @@
width: #{44 / $base-font-size}rem; width: #{44 / $base-font-size}rem;
text-align: center; text-align: center;
border-radius: 100%; border-radius: 100%;
line-height: #{49 / $base-font-size}rem;
cursor: pointer; cursor: pointer;
border: none; border: none;
outline: none; outline: none;
background-color: getThemifyVariable('toolbar-button-background-color'); background-color: getThemifyVariable('toolbar-button-background-color');
color: getThemifyVariable('toolbar-button-color'); color: getThemifyVariable('toolbar-button-color');
& g { & g, & path {
fill: getThemifyVariable('toolbar-button-color'); fill: getThemifyVariable('toolbar-button-color');
} }
&:hover { &:hover {
background-color: getThemifyVariable('button-background-hover-color'); background-color: getThemifyVariable('button-background-hover-color');
color: getThemifyVariable('button-hover-color'); color: getThemifyVariable('button-hover-color');
& g { & g, & path {
fill: getThemifyVariable('button-hover-color'); fill: getThemifyVariable('button-hover-color');
} }
} }
&--selected { &--selected {
background-color: getThemifyVariable('button-background-hover-color'); background-color: getThemifyVariable('button-background-hover-color');
& g { & g, & path {
fill: getThemifyVariable('button-hover-color'); fill: getThemifyVariable('button-hover-color');
} }
} }
@ -34,12 +33,12 @@
%icon-toast{ %icon-toast{
@include themify() { @include themify() {
color: $toast-text-color color: $toast-text-color
& g { & g, & path {
fill: $toast-text-color fill: $toast-text-color
} }
&:hover { &:hover {
color: getThemifyVariable('icon-toast-hover-color'); color: getThemifyVariable('icon-toast-hover-color');
& g { & g, & path {
opacity: 1; opacity: 1;
fill: getThemifyVariable('icon-toast-hover-color'); fill: getThemifyVariable('icon-toast-hover-color');
} }
@ -59,12 +58,12 @@
%none-themify-icon-with-hover { %none-themify-icon-with-hover {
color: $medium-dark; color: $medium-dark;
& g { & g, & path {
fill: $medium-dark; fill: $medium-dark;
} }
&:hover { &:hover {
color: $p5js-pink; color: $p5js-pink;
& g { & g, & path {
opacity: 1; opacity: 1;
fill: $p5js-pink; fill: $p5js-pink;
} }
@ -83,7 +82,7 @@
border: 2px solid getThemifyVariable('button-border-color'); border: 2px solid getThemifyVariable('button-border-color');
border-radius: 2px; border-radius: 2px;
padding: #{10 / $base-font-size}rem #{30 / $base-font-size}rem; padding: #{10 / $base-font-size}rem #{30 / $base-font-size}rem;
& g { & g, & path {
fill: getThemifyVariable('button-color'); fill: getThemifyVariable('button-color');
opacity: 1; opacity: 1;
} }
@ -91,7 +90,7 @@
border-color: getThemifyVariable('button-background-hover-color'); border-color: getThemifyVariable('button-background-hover-color');
background-color: getThemifyVariable('button-background-hover-color'); background-color: getThemifyVariable('button-background-hover-color');
color: getThemifyVariable('button-hover-color'); color: getThemifyVariable('button-hover-color');
& g { & g, & path {
fill: getThemifyVariable('button-hover-color'); fill: getThemifyVariable('button-hover-color');
} }
} }
@ -99,7 +98,7 @@
border-color: getThemifyVariable('button-background-active-color'); border-color: getThemifyVariable('button-background-active-color');
background-color: getThemifyVariable('button-background-active-color'); background-color: getThemifyVariable('button-background-active-color');
color: getThemifyVariable('button-active-color'); color: getThemifyVariable('button-active-color');
& g { & g, & path {
fill: getThemifyVariable('button-active-color'); fill: getThemifyVariable('button-active-color');
} }
} }
@ -114,13 +113,13 @@
padding: 0; padding: 0;
margin-bottom: #{28 / $base-font-size}rem; margin-bottom: #{28 / $base-font-size}rem;
line-height: #{50 / $base-font-size}rem; line-height: #{50 / $base-font-size}rem;
& g { & g, & path {
fill: getThemifyVariable('modal-button-color'); fill: getThemifyVariable('modal-button-color');
} }
&:enabled:hover { &:enabled:hover {
background-color: getThemifyVariable('button-background-hover-color'); background-color: getThemifyVariable('button-background-hover-color');
color: getThemifyVariable('button-hover-color'); color: getThemifyVariable('button-hover-color');
& g { & g, & path {
fill: getThemifyVariable('button-hover-color'); fill: getThemifyVariable('button-hover-color');
} }
} }

View file

@ -88,8 +88,8 @@ $themes: (
nav-border-color: $middle-light, nav-border-color: $middle-light,
error-color: $p5js-pink, error-color: $p5js-pink,
table-row-stripe-color: $medium-light, table-row-stripe-color: $medium-light,
codefold-icon-open: url(../images/triangle-arrow-down.svg), codefold-icon-open: url(../images/triangle-arrow-down.svg?byUrl),
codefold-icon-closed: url(../images/triangle-arrow-right.svg), codefold-icon-closed: url(../images/triangle-arrow-right.svg?byUrl),
primary-button-color: $lightest, primary-button-color: $lightest,
primary-button-background-color: $p5js-pink, primary-button-background-color: $p5js-pink,
@ -163,8 +163,8 @@ $themes: (
nav-border-color: $middle-dark, nav-border-color: $middle-dark,
error-color: $p5js-pink, error-color: $p5js-pink,
table-row-stripe-color: $dark, table-row-stripe-color: $dark,
codefold-icon-open: url(../images/triangle-arrow-down-white.svg), codefold-icon-open: url(../images/triangle-arrow-down-white.svg?byUrl),
codefold-icon-closed: url(../images/triangle-arrow-right-white.svg), codefold-icon-closed: url(../images/triangle-arrow-right-white.svg?byUrl),
primary-button-color: $lightest, primary-button-color: $lightest,
primary-button-background-color: $p5js-pink, primary-button-background-color: $p5js-pink,
@ -236,8 +236,8 @@ $themes: (
nav-border-color: $middle-dark, nav-border-color: $middle-dark,
error-color: $p5-contrast-pink, error-color: $p5-contrast-pink,
table-row-stripe-color: $dark, table-row-stripe-color: $dark,
codefold-icon-open: url(../images/triangle-arrow-down-white.svg), codefold-icon-open: url(../images/triangle-arrow-down-white.svg?byUrl),
codefold-icon-closed: url(../images/triangle-arrow-right-white.svg), codefold-icon-closed: url(../images/triangle-arrow-right-white.svg?byUrl),
primary-button-color: $lightest, primary-button-color: $lightest,
primary-button-background-color: $p5js-pink, primary-button-background-color: $p5js-pink,

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;
}

View file

@ -20,6 +20,8 @@
.api-key-form__create-icon { .api-key-form__create-icon {
display: flex; display: flex;
height: #{12 / $base-font-size}rem;
margin-right: #{3 / $base-font-size}rem;
} }
.api-key-form__create-button .isvg { .api-key-form__create-button .isvg {
@ -73,7 +75,7 @@
position: initial; position: initial;
left: 0; left: 0;
top: 0; top: 0;
& g { & g, & path {
opacity: 1; opacity: 1;
fill: getThemifyVariable('icon-color'); fill: getThemifyVariable('icon-color');
} }
@ -82,7 +84,7 @@
.api-key-list__delete-button:hover { .api-key-list__delete-button:hover {
@include themify() { @include themify() {
& g { & g, & path {
opacity: 1; opacity: 1;
fill: getThemifyVariable('icon-hover-color'); fill: getThemifyVariable('icon-hover-color');
} }

View file

@ -82,7 +82,7 @@
height:#{25 / $base-font-size}rem; height:#{25 / $base-font-size}rem;
@include themify() { @include themify() {
& polygon { & polygon, & path {
fill: getThemifyVariable('inactive-text-color'); fill: getThemifyVariable('inactive-text-color');
} }
} }

View file

@ -158,12 +158,12 @@
@include icon(); @include icon();
@include themify() { @include themify() {
// icon graphic // icon graphic
polygon { path {
fill: getThemifyVariable('table-button-color'); fill: getThemifyVariable('table-button-color');
} }
// icon background circle // icon background circle
path { path:first-child {
fill: getThemifyVariable('table-button-background-color'); fill: getThemifyVariable('table-button-background-color');
} }
@ -174,11 +174,11 @@
&:hover, &:hover,
&:focus { &:focus {
polygon { path {
fill: getThemifyVariable('table-button-hover-color'); fill: getThemifyVariable('table-button-hover-color');
} }
path { path:first-child {
fill: getThemifyVariable('table-button-background-hover-color'); fill: getThemifyVariable('table-button-background-hover-color');
} }
} }

View file

@ -58,7 +58,8 @@
@include icon(); @include icon();
@include themify() { @include themify() {
& g, & g,
& polygon { & polygon,
& path {
fill: getThemifyVariable('secondary-text-color'); fill: getThemifyVariable('secondary-text-color');
} }
} }
@ -72,7 +73,8 @@
@include icon(); @include icon();
@include themify() { @include themify() {
& g, & g,
& polygon { & polygon,
& path {
fill: getThemifyVariable('secondary-text-color'); fill: getThemifyVariable('secondary-text-color');
} }
} }

View file

@ -30,7 +30,7 @@ button.editable-input__label {
width: 100%; width: 100%;
} }
.editable-input__icon svg { .editable-input__icon {
width: 1.5rem; width: 1.5rem;
height: 1.5rem; height: 1.5rem;
} }

View file

@ -211,12 +211,12 @@ pre.CodeMirror-line {
// Previous button // Previous button
.CodeMirror-search-button.prev::after { .CodeMirror-search-button.prev::after {
background-image: url(../images/up-arrow.svg) background-image: url(../images/up-arrow.svg?byUrl)
} }
// Next button // Next button
.CodeMirror-search-button.next::after { .CodeMirror-search-button.next::after {
background-image: url(../images/down-arrow.svg) background-image: url(../images/down-arrow.svg?byUrl)
} }
/* /*
@ -247,7 +247,7 @@ pre.CodeMirror-line {
@include icon(); @include icon();
background: transparent url(../images/exit.svg) no-repeat; background: transparent url(../images/exit.svg?byUrl) no-repeat;
} }
// foldgutter // foldgutter
@ -391,3 +391,7 @@ pre.CodeMirror-line {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.editor__unsaved-changes {
margin-left: #{2 / $base-font-size}rem;
}

View file

@ -48,7 +48,8 @@
padding-left: #{15 / $base-font-size}rem; padding-left: #{15 / $base-font-size}rem;
} }
.nav__item-header-triangle polygon { .nav__item-header-triangle polygon,
.nav__item-header-triangle path {
@include themify() { @include themify() {
fill: getThemifyVariable('icon-color'); fill: getThemifyVariable('icon-color');
} }
@ -67,7 +68,8 @@
} }
} }
.nav__item-header-triangle polygon { .nav__item-header-triangle polygon,
.nav__item-header-triangle path {
@include themify() { @include themify() {
fill: getThemifyVariable('nav-hover-color'); fill: getThemifyVariable('nav-hover-color');
} }

View file

@ -44,12 +44,12 @@
@include icon(); @include icon();
@include themify() { @include themify() {
// icon graphic // icon graphic
polygon { path {
fill: getThemifyVariable('table-button-color'); fill: getThemifyVariable('table-button-color');
} }
// icon background circle // icon background circle
path { path:first-child {
fill: getThemifyVariable('table-button-background-color'); fill: getThemifyVariable('table-button-background-color');
} }
@ -68,15 +68,15 @@
display: inline-block; display: inline-block;
} }
.quick-add__icon--in-collection .quick-add__in-icon svg { .quick-add__icon--in-collection .quick-add__in-icon {
@include themify() { @include themify() {
// icon graphic // icon graphic
polygon { & path {
fill: getThemifyVariable('table-button-active-color'); fill: getThemifyVariable('table-button-active-color');
} }
// icon background circle // icon background circle
path { & path:first-child {
fill: getThemifyVariable('table-button-background-active-color'); fill: getThemifyVariable('table-button-background-active-color');
} }
} }
@ -91,16 +91,14 @@
.quick-add__item-toggle:focus { .quick-add__item-toggle:focus {
cursor: pointer; cursor: pointer;
@include themify() { @include themify() {
.quick-add__icon { & .quick-add__icon path {
polygon {
fill: getThemifyVariable('table-button-hover-color'); fill: getThemifyVariable('table-button-hover-color');
} }
path { & .quick-add__icon path:first-child {
fill: getThemifyVariable('table-button-background-hover-color'); fill: getThemifyVariable('table-button-background-hover-color');
} }
} }
}
& .quick-add__in-icon { & .quick-add__in-icon {
display: none; display: none;

View file

@ -38,6 +38,8 @@ div.searchbar__button {
height: #{27 / $base-font-size}rem; height: #{27 / $base-font-size}rem;
transform: scaleX(-1); transform: scaleX(-1);
padding-top: #{3 / $base-font-size}rem; padding-top: #{3 / $base-font-size}rem;
}
& path {
@include themify() { @include themify() {
fill: getThemifyVariable('input-text-color'); fill: getThemifyVariable('input-text-color');
} }

View file

@ -188,17 +188,17 @@
height: #{25 / $base-font-size}rem; height: #{25 / $base-font-size}rem;
width: #{49 / $base-font-size}rem; width: #{49 / $base-font-size}rem;
border-radius: 2px; border-radius: 2px;
& svg { display: flex;
height: #{25 / $base-font-size}rem; justify-content: center;
} align-items: center;
@include themify() { @include themify() {
background-color: getThemifyVariable("toolbar-button-background-color"); background-color: getThemifyVariable("toolbar-button-background-color");
& polygon { & polygon, & path {
fill: getThemifyVariable("toolbar-button-color"); fill: getThemifyVariable("toolbar-button-color");
} }
&:hover { &:hover {
background-color: getThemifyVariable("button-background-hover-color"); background-color: getThemifyVariable("button-background-hover-color");
& polygon { & polygon, & path {
fill: getThemifyVariable("button-hover-color"); fill: getThemifyVariable("button-hover-color");
} }
} }
@ -211,7 +211,7 @@
cursor: e-resize; cursor: e-resize;
} }
.sidebar--contracted & { .sidebar--contracted & {
display: inline-block; display: flex;
} }
} }
@ -236,7 +236,7 @@
.sidebar__folder-icon { .sidebar__folder-icon {
padding: #{4 / $base-font-size}rem 0; padding: #{4 / $base-font-size}rem 0;
margin-right: #{5 / $base-font-size}rem; margin-right: #{5 / $base-font-size}rem;
& g { & path {
@include themify() { @include themify() {
fill: map-get($theme-map, 'primary-text-color'); fill: map-get($theme-map, 'primary-text-color');
} }
@ -249,7 +249,7 @@
.sidebar__file-item-icon { .sidebar__file-item-icon {
padding: #{4 / $base-font-size}rem 0; padding: #{4 / $base-font-size}rem 0;
margin-right: #{5 / $base-font-size}rem; margin-right: #{5 / $base-font-size}rem;
& g { & path {
@include themify() { @include themify() {
fill: getThemifyVariable('secondary-text-color'); fill: getThemifyVariable('secondary-text-color');
} }

View file

@ -96,7 +96,7 @@
width:#{25 / $base-font-size}rem; width:#{25 / $base-font-size}rem;
height:#{25 / $base-font-size}rem; height:#{25 / $base-font-size}rem;
@include themify() { @include themify() {
& polygon { & polygon, & path {
fill: getThemifyVariable('inactive-text-color'); fill: getThemifyVariable('inactive-text-color');
} }
} }

View file

@ -4,17 +4,18 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 0 0 0 #{3 / $base-font-size}rem;
&--selected { &--selected {
@extend %toolbar-button--selected; @extend %toolbar-button--selected;
} }
&:disabled { &:disabled {
cursor: auto; cursor: auto;
& g { & g, & path {
fill: getThemifyVariable('button-border-color'); fill: getThemifyVariable('button-border-color');
} }
&:hover { &:hover {
background-color: getThemifyVariable('toolbar-button-background-color'); background-color: getThemifyVariable('toolbar-button-background-color');
& g { & g, & path {
fill: getThemifyVariable('button-border-color'); fill: getThemifyVariable('button-border-color');
} }
} }
@ -42,6 +43,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin-right: #{15 / $base-font-size}rem; margin-right: #{15 / $base-font-size}rem;
padding: 0;
&--selected { &--selected {
@extend %toolbar-button--selected; @extend %toolbar-button--selected;
} }
@ -61,13 +63,11 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
line-height: #{52 / $base-font-size}rem; padding: 0;
&--selected { &--selected {
@extend %toolbar-button--selected; @extend %toolbar-button--selected;
line-height: #{52 / $base-font-size}rem;
} }
} }
line-height: #{52 / $base-font-size}rem;
margin-left: auto; margin-left: auto;
& span { & span {
padding-left: #{1 / $base-font-size}rem; padding-left: #{1 / $base-font-size}rem;
@ -82,7 +82,7 @@
.toolbar__logo { .toolbar__logo {
margin-right: #{30 / $base-font-size}rem; margin-right: #{30 / $base-font-size}rem;
@include themify() { @include themify() {
& g { & g, & path {
fill: getThemifyVariable('logo-color'); fill: getThemifyVariable('logo-color');
} }
} }
@ -152,11 +152,8 @@
.toolbar__edit-name-button { .toolbar__edit-name-button {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
height: #{18 / $base-font-size}rem;
& svg {
width: #{18 / $base-font-size}rem; width: #{18 / $base-font-size}rem;
height: #{18 / $base-font-size}rem; height: #{18 / $base-font-size}rem;
}
@include themify() { @include themify() {
& path { & path {
fill: getThemifyVariable('secondary-text-color'); fill: getThemifyVariable('secondary-text-color');

1188
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -69,6 +69,7 @@
"@babel/plugin-transform-react-inline-elements": "^7.8.3", "@babel/plugin-transform-react-inline-elements": "^7.8.3",
"@babel/preset-env": "^7.8.4", "@babel/preset-env": "^7.8.4",
"@babel/preset-react": "^7.8.3", "@babel/preset-react": "^7.8.3",
"@svgr/webpack": "^5.4.0",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^9.0.0", "babel-eslint": "^9.0.0",
"babel-jest": "^24.9.0", "babel-jest": "^24.9.0",
@ -167,7 +168,6 @@
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-helmet": "^5.1.3", "react-helmet": "^5.1.3",
"react-hot-loader": "^4.12.19", "react-hot-loader": "^4.12.19",
"react-inlinesvg": "^0.7.5",
"react-redux": "^5.1.2", "react-redux": "^5.1.2",
"react-router": "^3.2.5", "react-router": "^3.2.5",
"react-split-pane": "^0.1.89", "react-split-pane": "^0.1.89",

View file

@ -69,7 +69,7 @@ module.exports = {
use: ['style-loader', 'css-loader', 'sass-loader'] use: ['style-loader', 'css-loader', 'sass-loader']
}, },
{ {
test: /\.(svg|mp3)$/, test: /\.(mp3)$/,
use: 'file-loader' use: 'file-loader'
}, },
{ {
@ -83,9 +83,30 @@ module.exports = {
} }
}, },
{ {
test: /fonts\/.*\.(eot|svg|ttf|woff|woff2)$/, test: /fonts\/.*\.(eot|ttf|woff|woff2)$/,
use: 'file-loader' use: 'file-loader'
}, },
{
test: /\.svg$/,
oneOf: [
{
resourceQuery: /byUrl/,
use: 'file-loader'
},
{
use: {
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: {
removeViewBox: false
}
}
}
}
}
]
},
{ {
test: /_console-feed.scss/, test: /_console-feed.scss/,
use: { use: {

View file

@ -80,10 +80,6 @@ module.exports = [{
exclude: /node_modules/, exclude: /node_modules/,
use: 'babel-loader' use: 'babel-loader'
}, },
{
test: /\.(svg|mp3)$/,
use: 'file-loader'
},
{ {
test: /\.(png)$/, test: /\.(png)$/,
use: { use: {
@ -95,9 +91,30 @@ module.exports = [{
} }
}, },
{ {
test: /fonts\/.*\.(eot|svg|ttf|woff|woff2)$/, test: /fonts\/.*\.(mp3|eot|ttf|woff|woff2)$/,
use: 'file-loader' use: 'file-loader'
}, },
{
test: /\.svg$/,
oneOf: [
{
resourceQuery: /byUrl/,
use: 'file-loader'
},
{
use: {
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: {
removeViewBox: false
}
}
}
}
}
]
},
{ {
test: /_console-feed.scss/, test: /_console-feed.scss/,
use: { use: {