Skip to content

Commit

Permalink
Editable: Pass all aria-* props to TinyMCE
Browse files Browse the repository at this point in the history
With this change, Editable expects ARIA attributes to be provided as
individual props: `<Editable aria-label="foo" />`. This replaces the
approach whereby a single `aria` object prop would be passed.

This allows Editable's interface to be kept similar to any native
element's interface. One benefit we now get for free is that the linter
is able to warn the author if they pass an invalid ARIA attribute:

    aria-foo: This attribute is an invalid ARIA attribute.
    (jsx-a11y/aria-props) [eslint]
  • Loading branch information
mcsf committed Oct 26, 2017
1 parent d0cfaa4 commit 700f2a1
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 13 deletions.
17 changes: 17 additions & 0 deletions blocks/editable/aria.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* External dependencies
*/

import {
pickBy,
startsWith,
} from 'lodash';

const isAriaPropName = ( name ) =>
startsWith( name, 'aria-' );

export const getAriaKeys = ( props ) =>
Object.keys( props ).filter( isAriaPropName );

export const pickAriaProps = ( props ) =>
pickBy( props, ( value, key ) => isAriaPropName( key ) );
7 changes: 5 additions & 2 deletions blocks/editable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import './style.scss';
import { pasteHandler } from '../api';
import FormatToolbar from './format-toolbar';
import TinyMCE from './tinymce';
import { pickAriaProps } from './aria';
import patterns from './patterns';
import { EVENTS } from './constants';

Expand Down Expand Up @@ -609,11 +610,12 @@ export default class Editable extends Component {
inlineToolbar = false,
formattingControls,
placeholder,
aria,
multiline: MultilineTag,
keepPlaceholderOnFocus = false,
} = this.props;

const ariaProps = pickAriaProps( this.props );

// Generating a key that includes `tagName` ensures that if the tag
// changes, we unmount and destroy the previous TinyMCE element, then
// mount and initialize a new child element in its place.
Expand Down Expand Up @@ -650,7 +652,8 @@ export default class Editable extends Component {
style={ style }
defaultValue={ value }
isPlaceholderVisible={ isPlaceholderVisible }
aria={ { label: placeholder, ...aria } }
aria-label={ placeholder }
{ ...ariaProps }
className={ className }
key={ key }
/>
Expand Down
30 changes: 19 additions & 11 deletions blocks/editable/tinymce.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
* External dependencies
*/
import tinymce from 'tinymce';
import { difference, filter, forEach, isEqual, keys, mapKeys } from 'lodash';
import { difference, isEqual } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { Component, Children, createElement } from '@wordpress/element';

/**
* Internal dependencies
*/
import { getAriaKeys, pickAriaProps } from './aria';

export default class TinyMCE extends Component {
componentDidMount() {
this.initialize();
Expand Down Expand Up @@ -40,14 +45,16 @@ export default class TinyMCE extends Component {
this.editorNode.className = classnames( nextProps.className, 'blocks-editable__tinymce' );
}

if ( ! isEqual( this.props.aria, nextProps.aria ) ) {
const before = keys( this.props.aria );
const after = keys( nextProps.aria );
const removed = difference( before, after );
const updated = filter( after, ( key ) => ! isEqual( this.props.aria[ key ], nextProps.aria[ key ] ) );
forEach( removed, ( key ) => this.editorNode.removeAttribute( 'aria-' + key ) );
forEach( updated, ( key ) => this.editorNode.setAttribute( 'aria-' + key, nextProps.aria[ key ] ) );
}
const prevAriaKeys = getAriaKeys( this.props );
const nextAriaKeys = getAriaKeys( nextProps );
const removedKeys = difference( prevAriaKeys, nextAriaKeys );
const updatedKeys = nextAriaKeys.filter( ( key ) =>
! isEqual( this.props[ key ], nextProps[ key ] ) );

removedKeys.forEach( ( key ) =>
this.editorNode.removeAttribute( key ) );
updatedKeys.forEach( ( key ) =>
this.editorNode.setAttribute( key, nextProps[ key ] ) );
}

componentWillUnmount() {
Expand Down Expand Up @@ -97,7 +104,8 @@ export default class TinyMCE extends Component {
}

render() {
const { tagName = 'div', style, defaultValue, aria, className } = this.props;
const { tagName = 'div', style, defaultValue, className } = this.props;
const ariaProps = pickAriaProps( this.props );

// If a default value is provided, render it into the DOM even before
// TinyMCE finishes initializing. This avoids a short delay by allowing
Expand All @@ -113,7 +121,7 @@ export default class TinyMCE extends Component {
suppressContentEditableWarning: true,
className: classnames( className, 'blocks-editable__tinymce' ),
style,
...( mapKeys( aria, ( value, key ) => 'aria-' + key ) ),
...ariaProps,
}, children );
}
}

0 comments on commit 700f2a1

Please sign in to comment.