diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index d9d0ecbdd28b6..06cb9c80deadf 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -699,6 +699,27 @@ export function isCaretWithinFormattedText( state = false, action ) { } const BLOCK_SELECTION_EMPTY_OBJECT = {}; + +/** + * Initial state object for block selection. + * + * @property {Object} start Block anchor from which the selection + * begins. + * @property {Object} end Block extent to which the selection + * ends. + * @property {boolean} isMultiSelecting Flag representing whether a multi- + * selection interaction is in progress. + * @property {boolean} isEnabled Flag representing whether multi- + * selection is currently allowed. + * @property {number?} initialPosition For a changed selection, the position + * at which the caret should be placed. + * Either null (default position) or -1 + * (at the end of the block). + * + * @typedef {WPBlockSelectionState} + * + * @type {Object} + */ const BLOCK_SELECTION_INITIAL_STATE = { start: BLOCK_SELECTION_EMPTY_OBJECT, end: BLOCK_SELECTION_EMPTY_OBJECT, @@ -710,10 +731,10 @@ const BLOCK_SELECTION_INITIAL_STATE = { /** * Reducer returning the block selection's state. * - * @param {Object} state Current state. + * @param {WPBlockSelectionState} state Current state. * @param {Object} action Dispatched action. * - * @return {Object} Updated state. + * @return {WPBlockSelectionState} Updated state. */ export function blockSelection( state = BLOCK_SELECTION_INITIAL_STATE, action ) { switch ( action.type ) { @@ -809,8 +830,11 @@ export function blockSelection( state = BLOCK_SELECTION_INITIAL_STATE, action ) } case 'TOGGLE_SELECTION': return { - ...BLOCK_SELECTION_INITIAL_STATE, + ...state, isEnabled: action.isSelectionEnabled, + isMultiSelecting: action.isSelectionEnabled ? + state.isMultiSelecting : + false, }; case 'SELECTION_CHANGE': return { diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 7543fdad57dd8..9ab02b1714c4b 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -1727,6 +1727,68 @@ describe( 'state', () => { expect( state1 ).toBe( original ); } ); + it( 'should maintain selection when toggling multi-selection', () => { + const original = deepFreeze( { + start: { clientId: 'ribs' }, + end: { clientId: 'ribs' }, + isMultiSelecting: false, + } ); + + const state = blockSelection( original, { + type: 'TOGGLE_SELECTION', + isSelectionEnabled: false, + } ); + + expect( state ).toEqual( { + start: { clientId: 'ribs' }, + end: { clientId: 'ribs' }, + isMultiSelecting: false, + isEnabled: false, + } ); + } ); + + it( 'should cancel multi-selection when disabling multi-selection', () => { + const original = deepFreeze( { + start: { clientId: 'ribs' }, + end: { clientId: 'ribs' }, + isMultiSelecting: true, + } ); + + const state = blockSelection( original, { + type: 'TOGGLE_SELECTION', + isSelectionEnabled: false, + } ); + + expect( state ).toEqual( { + start: { clientId: 'ribs' }, + end: { clientId: 'ribs' }, + isEnabled: false, + isMultiSelecting: false, + } ); + } ); + + it( 'should preserve multi-selection when enabling multi-selection', () => { + [ true, false ].forEach( ( isMultiSelecting ) => { + const original = deepFreeze( { + start: { clientId: 'ribs' }, + end: { clientId: 'ribs' }, + isMultiSelecting, + } ); + + const state = blockSelection( original, { + type: 'TOGGLE_SELECTION', + isSelectionEnabled: true, + } ); + + expect( state ).toEqual( { + start: { clientId: 'ribs' }, + end: { clientId: 'ribs' }, + isEnabled: true, + isMultiSelecting, + } ); + } ); + } ); + it( 'should unset multi selection', () => { const original = deepFreeze( { start: { clientId: 'ribs' }, end: { clientId: 'chicken' } } );