Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocks: Add automatic handling of focus for RichText component #6419

Merged
merged 7 commits into from
May 7, 2018
7 changes: 7 additions & 0 deletions blocks/block-edit/context.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
/**
* External dependencies
*/
import { noop } from 'lodash';

/**
* WordPress dependencies
*/
import { createContext, createHigherOrderComponent } from '@wordpress/element';

const { Consumer, Provider } = createContext( {
name: '',
isSelected: false,
focusedElement: null,
setFocusedElement: noop,
} );

export { Provider as BlockEditContextProvider };
Expand Down
16 changes: 15 additions & 1 deletion blocks/block-edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ import { BlockEditContextProvider } from './context';
export class BlockEdit extends Component {
constructor( props ) {
super( props );
this.state = {};
this.setFocusedElement = this.setFocusedElement.bind( this );
this.state = {
focusedElement: null,
setFocusedElement: this.setFocusedElement,
};
}

getChildContext() {
Expand All @@ -38,6 +42,15 @@ export class BlockEdit extends Component {
};
}

setFocusedElement( focusedElement ) {
this.setState( ( prevState ) => {
if ( prevState.focusedElement === focusedElement ) {
return null;
}
return { focusedElement };
} );
}

static getDerivedStateFromProps( { name, isSelected }, prevState ) {
if (
name === prevState.name &&
Expand All @@ -47,6 +60,7 @@ export class BlockEdit extends Component {
}

return {
...prevState,
name,
isSelected,
};
Expand Down
34 changes: 28 additions & 6 deletions blocks/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
getScrollContainer,
deprecated,
} from '@wordpress/utils';
import { withSafeTimeout, Slot } from '@wordpress/components';
import { withInstanceId, withSafeTimeout, Slot } from '@wordpress/components';
import { withSelect } from '@wordpress/data';

/**
Expand Down Expand Up @@ -121,6 +121,7 @@ export class RichText extends Component {
this.onPastePreProcess = this.onPastePreProcess.bind( this );
this.onPaste = this.onPaste.bind( this );
this.onCreateUndoLevel = this.onCreateUndoLevel.bind( this );
this.setFocusedElement = this.setFocusedElement.bind( this );

this.state = {
formats: {},
Expand Down Expand Up @@ -195,6 +196,12 @@ export class RichText extends Component {
}
}

setFocusedElement() {
if ( this.props.setFocusedElement ) {
this.props.setFocusedElement( this.props.instanceId );
}
}

/**
* Allows prop event handlers to handle an event.
*
Expand Down Expand Up @@ -850,7 +857,10 @@ export class RichText extends Component {
);

return (
<div className={ classes } ref={ this.containerRef }>
<div className={ classes }
ref={ this.containerRef }
onFocus={ this.setFocusedElement }
>
{ isSelected && ! inlineToolbar && (
<BlockFormatControls>
{ formatToolbar }
Expand Down Expand Up @@ -912,17 +922,29 @@ RichText.defaultProps = {
};

const RichTextContainer = compose( [
withBlockEditContext( ( { isSelected } ) => {
withInstanceId,
withBlockEditContext( ( context, ownProps ) => {
// When explicitly set as not selected, do nothing.
if ( ownProps.isSelected === false ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a deprecation message about the isSelected prop in RichText components?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might, but as soon as we remove all occurrences in core blocks. Otherwise, we would flood the console with the deprecation messages. We should also make sure there aren't any cases when this possibility would still make sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love having focus handled automatically by default. I'd like to be able to optionally control it myself by setting that prop using my own logic.

return {};
}
// When explicitly set as selected, use the value stored in the context instead.
if ( ownProps.isSelected === true ) {
return {
isSelected: context.isSelected,
};
}
// Ensures that only one RichText component can be focused.
return {
isBlockSelected: isSelected,
isSelected: context.isSelected && context.focusedElement === ownProps.instanceId,
setFocusedElement: context.setFocusedElement,
};
} ),
withSelect( ( select, { isSelected, isBlockSelected } ) => {
withSelect( ( select ) => {
const { isViewportMatch = identity } = select( 'core/viewport' ) || {};

return {
isViewportSmall: isViewportMatch( '< small' ),
isSelected: isSelected !== false && isBlockSelected,
};
} ),
withSafeTimeout,
Expand Down
12 changes: 2 additions & 10 deletions core-blocks/pullquote/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { map } from 'lodash';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { withState } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import {
BlockControls,
Expand Down Expand Up @@ -68,12 +67,9 @@ export const settings = {
}
},

edit: withState( {
editable: 'content',
} )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => {
edit( { attributes, setAttributes, isSelected, className } ) {
const { value, citation, align } = attributes;
const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } );
const onSetActiveEditable = ( newEditable ) => () => setState( { editable: newEditable } );

return (
<Fragment>
Expand All @@ -95,8 +91,6 @@ export const settings = {
/* translators: the text of the quotation */
placeholder={ __( 'Write quote…' ) }
wrapperClassName="blocks-pullquote__content"
isSelected={ isSelected && editable === 'content' }
onFocus={ onSetActiveEditable( 'content' ) }
/>
{ ( citation || isSelected ) && (
<RichText
Expand All @@ -109,14 +103,12 @@ export const settings = {
citation: nextCitation,
} )
}
isSelected={ isSelected && editable === 'cite' }
onFocus={ onSetActiveEditable( 'cite' ) }
/>
) }
</blockquote>
</Fragment>
);
} ),
},

save( { attributes } ) {
const { value, citation, align } = attributes;
Expand Down
15 changes: 3 additions & 12 deletions core-blocks/quote/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Toolbar, withState } from '@wordpress/components';
import { Toolbar } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import {
createBlock,
Expand Down Expand Up @@ -175,14 +175,9 @@ export const settings = {
],
},

edit: withState( {
editable: 'content',
} )( ( { attributes, setAttributes, isSelected, mergeBlocks, onReplace, className, editable, setState } ) => {
edit( { attributes, setAttributes, isSelected, mergeBlocks, onReplace, className } ) {
const { align, value, citation, style } = attributes;
const containerClassname = classnames( className, style === 2 ? 'is-large' : '' );
const onSetActiveEditable = ( newEditable ) => () => {
setState( { editable: newEditable } );
};

return (
<Fragment>
Expand Down Expand Up @@ -223,8 +218,6 @@ export const settings = {
} }
/* translators: the text of the quotation */
placeholder={ __( 'Write quote…' ) }
isSelected={ isSelected && editable === 'content' }
onFocus={ onSetActiveEditable( 'content' ) }
/>
{ ( ( citation && citation.length > 0 ) || isSelected ) && (
<RichText
Expand All @@ -237,14 +230,12 @@ export const settings = {
}
/* translators: the individual or entity quoted */
placeholder={ __( 'Write citation…' ) }
isSelected={ isSelected && editable === 'cite' }
onFocus={ onSetActiveEditable( 'cite' ) }
/>
) }
</blockquote>
</Fragment>
);
} ),
},

save( { attributes } ) {
const { align, value, citation, style } = attributes;
Expand Down
40 changes: 19 additions & 21 deletions core-blocks/spacer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const settings = {
},
},

edit( { attributes, setAttributes, isSelected, toggleSelection } ) {
edit( { attributes, setAttributes, toggleSelection } ) {
const { height } = attributes;

return (
Expand Down Expand Up @@ -68,26 +68,24 @@ export const settings = {
toggleSelection( false );
} }
/>
{ isSelected &&
<InspectorControls>
<PanelBody title={ __( 'Spacer Settings' ) }>
<BaseControl label={ __( 'Height in pixels' ) }>
<input
type="number"
onChange={ ( event ) => {
setAttributes( {
height: parseInt( event.target.value, 10 ),
} );
} }
aria-label={ __( 'Height for the spacer element in pixels.' ) }
value={ height }
min="20"
step="10"
/>
</BaseControl>
</PanelBody>
</InspectorControls>
}
<InspectorControls>
<PanelBody title={ __( 'Spacer Settings' ) }>
<BaseControl label={ __( 'Height in pixels' ) }>
<input
type="number"
onChange={ ( event ) => {
setAttributes( {
height: parseInt( event.target.value, 10 ),
} );
} }
aria-label={ __( 'Height for the spacer element in pixels.' ) }
value={ height }
min="20"
step="10"
/>
</BaseControl>
</PanelBody>
</InspectorControls>
</Fragment>
);
},
Expand Down
20 changes: 6 additions & 14 deletions core-blocks/text-columns/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/**
* External dependencies
*/
import { times } from 'lodash';
import { get, times } from 'lodash';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { PanelBody, RangeControl, withState } from '@wordpress/components';
import { PanelBody, RangeControl } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import {
BlockControls,
Expand Down Expand Up @@ -61,13 +61,8 @@ export const settings = {
}
},

edit: withState( {
editable: 'column-1',
} )( ( { attributes, setAttributes, className, isSelected, editable, setState } ) => {
edit: ( ( { attributes, setAttributes, className } ) => {
const { width, content, columns } = attributes;
const onSetActiveEditable = ( newEditable ) => () => {
setState( { editable: newEditable } );
};

return (
<Fragment>
Expand All @@ -91,12 +86,11 @@ export const settings = {
</InspectorControls>
<div className={ `${ className } align${ width } columns-${ columns }` }>
{ times( columns, ( index ) => {
const key = `column-${ index }`;
return (
<div className="wp-block-column" key={ key }>
<div className="wp-block-column" key={ `column-${ index }` }>
<RichText
tagName="p"
value={ content && content[ index ] && content[ index ].children }
value={ get( content, [ index, 'children' ] ) }
onChange={ ( nextContent ) => {
setAttributes( {
content: [
Expand All @@ -107,8 +101,6 @@ export const settings = {
} );
} }
placeholder={ __( 'New Column' ) }
isSelected={ isSelected && editable === key }
onFocus={ onSetActiveEditable( key ) }
/>
</div>
);
Expand All @@ -124,7 +116,7 @@ export const settings = {
<div className={ `align${ width } columns-${ columns }` }>
{ times( columns, ( index ) =>
<div className="wp-block-column" key={ `column-${ index }` }>
<RichText.Content tagName="p" value={ content && content[ index ].children } />
<RichText.Content tagName="p" value={ get( content, [ index, 'children' ] ) } />
</div>
) }
</div>
Expand Down