From dd34d327c2fca10658ace8e97884b8a11193664c Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Thu, 25 Jul 2019 17:45:44 +0200 Subject: [PATCH] Move the post title selection state to the store and update PostTitle (#16704) * Move the post title selection state to the store and update PostTitle * Fix jsdoc type returned for postTitle reducer * Update native version of VisualEditor to use the store to know if the post title is selected * Update doc * Remove unused clearSelectedBlock props in PostTitle web and bring back focus in native version * Fork core/editor store for native instead of modifying it * Move tests to native * Update docs * Revert changes to web PostTitle * Fix post title blur on block selection * Update the isPostTitleSelected state if any other block is selected --- .../components/visual-editor/index.native.js | 37 +---------- .../src/components/post-title/index.native.js | 64 +++++++++++++------ packages/editor/src/store/actions.native.js | 16 +++++ packages/editor/src/store/reducer.native.js | 64 +++++++++++++++++++ packages/editor/src/store/selectors.native.js | 13 ++++ .../editor/src/store/test/actions.native.js | 17 +++++ .../editor/src/store/test/reducer.native.js | 34 ++++++++++ .../editor/src/store/test/selectors.native.js | 28 ++++++++ 8 files changed, 220 insertions(+), 53 deletions(-) create mode 100644 packages/editor/src/store/actions.native.js create mode 100644 packages/editor/src/store/reducer.native.js create mode 100644 packages/editor/src/store/selectors.native.js create mode 100644 packages/editor/src/store/test/actions.native.js create mode 100644 packages/editor/src/store/test/reducer.native.js create mode 100644 packages/editor/src/store/test/selectors.native.js diff --git a/packages/edit-post/src/components/visual-editor/index.native.js b/packages/edit-post/src/components/visual-editor/index.native.js index 708b96c8289aea..092ea638f68026 100644 --- a/packages/edit-post/src/components/visual-editor/index.native.js +++ b/packages/edit-post/src/components/visual-editor/index.native.js @@ -15,33 +15,6 @@ import { ReadableContentView } from '@wordpress/components'; import styles from './style.scss'; class VisualEditor extends Component { - constructor() { - super( ...arguments ); - - this.onPostTitleSelect = this.onPostTitleSelect.bind( this ); - this.onPostTitleUnselect = this.onPostTitleUnselect.bind( this ); - - this.state = { - isPostTitleSelected: false, - }; - } - - static getDerivedStateFromProps( props ) { - if ( props.isAnyBlockSelected ) { - return { isPostTitleSelected: false }; - } - return null; - } - - onPostTitleSelect() { - this.setState( { isPostTitleSelected: true } ); - this.props.clearSelectedBlock(); - } - - onPostTitleUnselect() { - this.setState( { isPostTitleSelected: false } ); - } - renderHeader() { const { editTitle, @@ -55,9 +28,6 @@ class VisualEditor extends Component { innerRef={ setTitleRef } title={ title } onUpdate={ editTitle } - onSelect={ this.onPostTitleSelect } - onUnselect={ this.onPostTitleUnselect } - isSelected={ this.state.isPostTitleSelected } placeholder={ __( 'Add title' ) } borderStyle={ this.props.isFullyBordered ? @@ -93,7 +63,7 @@ class VisualEditor extends Component { isFullyBordered={ isFullyBordered } rootViewHeight={ rootViewHeight } safeAreaBottomInset={ safeAreaBottomInset } - isPostTitleSelected={ this.state.isPostTitleSelected } + isPostTitleSelected={ this.props.isPostTitleSelected } onBlockTypeSelected={ this.onPostTitleUnselect } /> @@ -106,14 +76,13 @@ export default compose( [ const { getEditorBlocks, getEditedPostAttribute, + isPostTitleSelected, } = select( 'core/editor' ); - const { getSelectedBlockClientId } = select( 'core/block-editor' ); - return { blocks: getEditorBlocks(), title: getEditedPostAttribute( 'title' ), - isAnyBlockSelected: !! getSelectedBlockClientId(), + isPostTitleSelected: isPostTitleSelected(), }; } ), withDispatch( ( dispatch ) => { diff --git a/packages/editor/src/components/post-title/index.native.js b/packages/editor/src/components/post-title/index.native.js index d91d0220753dfe..ce845b8fb630f6 100644 --- a/packages/editor/src/components/post-title/index.native.js +++ b/packages/editor/src/components/post-title/index.native.js @@ -10,7 +10,7 @@ import { isEmpty } from 'lodash'; import { Component } from '@wordpress/element'; import { RichText } from '@wordpress/rich-text'; import { decodeEntities } from '@wordpress/html-entities'; -import { withDispatch } from '@wordpress/data'; +import { withDispatch, withSelect } from '@wordpress/data'; import { withFocusOutside } from '@wordpress/components'; import { withInstanceId, compose } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; @@ -22,6 +22,13 @@ import { pasteHandler } from '@wordpress/blocks'; import styles from './style.scss'; class PostTitle extends Component { + componentDidUpdate( prevProps ) { + // Unselect if any other block is selected + if ( this.props.isSelected && ! prevProps.isAnyBlockSelected && this.props.isAnyBlockSelected ) { + this.props.onUnselect(); + } + } + componentDidMount() { if ( this.props.innerRef ) { this.props.innerRef( this ); @@ -91,27 +98,46 @@ class PostTitle extends Component { } } -const applyWithDispatch = withDispatch( ( dispatch ) => { - const { - undo, - redo, - } = dispatch( 'core/editor' ); +export default compose( + withSelect( ( select ) => { + const { + isPostTitleSelected, + } = select( 'core/editor' ); - const { - insertDefaultBlock, - } = dispatch( 'core/block-editor' ); + const { getSelectedBlockClientId } = select( 'core/block-editor' ); - return { - onEnterPress() { - insertDefaultBlock( undefined, undefined, 0 ); - }, - onUndo: undo, - onRedo: redo, - }; -} ); + return { + isAnyBlockSelected: !! getSelectedBlockClientId(), + isSelected: isPostTitleSelected(), + }; + } ), + withDispatch( ( dispatch ) => { + const { + undo, + redo, + togglePostTitleSelection, + } = dispatch( 'core/editor' ); -export default compose( - applyWithDispatch, + const { + clearSelectedBlock, + insertDefaultBlock, + } = dispatch( 'core/block-editor' ); + + return { + onEnterPress() { + insertDefaultBlock( undefined, undefined, 0 ); + }, + onUndo: undo, + onRedo: redo, + onSelect() { + togglePostTitleSelection( true ); + clearSelectedBlock(); + }, + onUnselect() { + togglePostTitleSelection( false ); + }, + }; + } ), withInstanceId, withFocusOutside )( PostTitle ); diff --git a/packages/editor/src/store/actions.native.js b/packages/editor/src/store/actions.native.js new file mode 100644 index 00000000000000..3d638cbc2be2cf --- /dev/null +++ b/packages/editor/src/store/actions.native.js @@ -0,0 +1,16 @@ + +export * from './actions.js'; + +/** + * Returns an action object that enables or disables post title selection. + * + * @param {boolean} [isSelected=true] Whether post title is currently selected. + + * @return {Object} Action object. + */ +export function togglePostTitleSelection( isSelected = true ) { + return { + type: 'TOGGLE_POST_TITLE_SELECTION', + isSelected, + }; +} diff --git a/packages/editor/src/store/reducer.native.js b/packages/editor/src/store/reducer.native.js new file mode 100644 index 00000000000000..82b3689a98ead0 --- /dev/null +++ b/packages/editor/src/store/reducer.native.js @@ -0,0 +1,64 @@ +/** + * External dependencies + */ +import optimist from 'redux-optimist'; + +/** + * WordPress dependencies + */ +import { combineReducers } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { + editor, + initialEdits, + currentPost, + preferences, + saving, + postLock, + reusableBlocks, + template, + previewLink, + postSavingLock, + isReady, + editorSettings, +} from './reducer.js'; + +export * from './reducer.js'; + +/** + * Reducer returning the post title state. + * + * @param {PostTitleState} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export const postTitle = combineReducers( { + isSelected( state = false, action ) { + switch ( action.type ) { + case 'TOGGLE_POST_TITLE_SELECTION': + return action.isSelected; + } + + return state; + }, +} ); + +export default optimist( combineReducers( { + editor, + initialEdits, + currentPost, + preferences, + saving, + postLock, + reusableBlocks, + template, + previewLink, + postSavingLock, + isReady, + editorSettings, + postTitle, +} ) ); diff --git a/packages/editor/src/store/selectors.native.js b/packages/editor/src/store/selectors.native.js new file mode 100644 index 00000000000000..8c6ead8e97ba2f --- /dev/null +++ b/packages/editor/src/store/selectors.native.js @@ -0,0 +1,13 @@ + +export * from './selectors.js'; + +/** + * Returns true if post title is selected. + * + * @param {Object} state Global application state. + * + * @return {boolean} Whether current post title is selected. + */ +export function isPostTitleSelected( state ) { + return state.postTitle.isSelected; +} diff --git a/packages/editor/src/store/test/actions.native.js b/packages/editor/src/store/test/actions.native.js new file mode 100644 index 00000000000000..b39086244d98de --- /dev/null +++ b/packages/editor/src/store/test/actions.native.js @@ -0,0 +1,17 @@ + +/** + * Internal dependencies + */ +import { togglePostTitleSelection } from '../actions'; + +describe( 'Editor actions', () => { + describe( 'togglePostTitleSelection', () => { + it( 'should return the TOGGLE_POST_TITLE_SELECTION action', () => { + const result = togglePostTitleSelection( true ); + expect( result ).toEqual( { + type: 'TOGGLE_POST_TITLE_SELECTION', + isSelected: true, + } ); + } ); + } ); +} ); diff --git a/packages/editor/src/store/test/reducer.native.js b/packages/editor/src/store/test/reducer.native.js new file mode 100644 index 00000000000000..d97ca0d9e5c9df --- /dev/null +++ b/packages/editor/src/store/test/reducer.native.js @@ -0,0 +1,34 @@ +/** + * Internal dependencies + */ +import { + postTitle, +} from '../reducer'; + +describe( 'state native', () => { + describe( 'postTitle', () => { + describe( 'isSelected()', () => { + it( 'should not be selected by default', () => { + expect( postTitle( undefined, {} ).isSelected ).toBe( false ); + } ); + + it( 'should return false if not selecting the post title', () => { + const action = { + type: 'TOGGLE_POST_TITLE_SELECTION', + isSelected: false, + }; + + expect( postTitle( { isSelected: true }, action ).isSelected ).toBe( false ); + } ); + + it( 'should return true if selecting the post title', () => { + const action = { + type: 'TOGGLE_POST_TITLE_SELECTION', + isSelected: true, + }; + + expect( postTitle( { isSelected: false }, action ).isSelected ).toBe( true ); + } ); + } ); + } ); +} ); diff --git a/packages/editor/src/store/test/selectors.native.js b/packages/editor/src/store/test/selectors.native.js new file mode 100644 index 00000000000000..958a68e651f743 --- /dev/null +++ b/packages/editor/src/store/test/selectors.native.js @@ -0,0 +1,28 @@ +/** + * Internal dependencies + */ +import { isPostTitleSelected } from '../selectors'; + +describe( 'selectors native', () => { + describe( 'isPostTitleSelected', () => { + it( 'should return true if the post title is selected', () => { + const state = { + postTitle: { + isSelected: true, + }, + }; + + expect( isPostTitleSelected( state ) ).toBe( true ); + } ); + + it( 'should return false if the post title is not selected', () => { + const state = { + postTitle: { + isSelected: false, + }, + }; + + expect( isPostTitleSelected( state ) ).toBe( false ); + } ); + } ); +} );