diff --git a/packages/block-editor/src/components/block-mobile-toolbar/index.native.js b/packages/block-editor/src/components/block-mobile-toolbar/index.native.js index 440137968b5d73..eeff33568d1d96 100644 --- a/packages/block-editor/src/components/block-mobile-toolbar/index.native.js +++ b/packages/block-editor/src/components/block-mobile-toolbar/index.native.js @@ -8,7 +8,7 @@ import { View } from 'react-native'; */ import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { useState } from '@wordpress/element'; +import { useState, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -53,6 +53,14 @@ const BlockMobileToolbar = ( { blockWidth <= BREAKPOINTS.wrapMover || appenderWidth - spacingValue <= BREAKPOINTS.wrapMover; + const BlockSettingsButtonFill = ( fillProps ) => { + useEffect( + () => fillProps.onChangeFillsLength( fillProps.fillsLength ), + [ fillProps.fillsLength ] + ); + return fillProps.children ?? null; + }; + return ( { /* Render only one settings icon even if we have more than one fill - need for hooks with controls */ } - { ( fills = [ null ] ) => { - setFillsLength( fills.length ); - return wrapBlockSettings ? null : fills[ 0 ]; - } } + { ( fills = [ null ] ) => ( + // The purpose of BlockSettingsButtonFill component is only to provide a way + // to pass data upstream from the slot rendering + + { wrapBlockSettings ? null : fills[ 0 ] } + + ) } { } ); describe( 'Reusable block', () => { + it( 'inserts a reusable block', async () => { + // We have to use different ids because entities are cached in memory. + const reusableBlockMock1 = getMockedReusableBlock( 1 ); + const reusableBlockMock2 = getMockedReusableBlock( 2 ); + + // Return mocked responses for the block endpoints. + fetchRequest.mockImplementation( ( { path } ) => { + let response = {}; + if ( path.startsWith( '/wp/v2/blocks?' ) ) { + response = [ reusableBlockMock1, reusableBlockMock2 ]; + } else if ( path.startsWith( '/wp/v2/blocks/1' ) ) { + response = reusableBlockMock1; + } + return Promise.resolve( response ); + } ); + + const { + getByA11yLabel, + getByTestId, + getByText, + } = await initializeEditor( { + initialHtml: '', + capabilities: { reusableBlock: true }, + } ); + + // Open the inserter menu + fireEvent.press( await waitFor( () => getByA11yLabel( 'Add block' ) ) ); + + // Navigate to reusable tab + const reusableSegment = getByText( 'Reusable' ); + // onLayout event is required by Segment component + fireEvent( reusableSegment, 'layout', { + nativeEvent: { + layout: { + width: 100, + }, + }, + } ); + fireEvent.press( reusableSegment ); + + const reusableBlockList = getByTestId( 'InserterUI-ReusableBlocks' ); + // onScroll event used to force the FlatList to render all items + fireEvent.scroll( reusableBlockList, { + nativeEvent: { + contentOffset: { y: 0, x: 0 }, + contentSize: { width: 100, height: 100 }, + layoutMeasurement: { width: 100, height: 100 }, + }, + } ); + + // Insert a reusable block + fireEvent.press( + await waitFor( () => getByText( `Reusable block - 1` ) ) + ); + + // Get the reusable block + const reusableBlock = await waitFor( () => + getByA11yLabel( /Reusable block Block\. Row 1/ ) + ); + + expect( reusableBlock ).toBeDefined(); + expect( getEditorHtml() ).toBe( '' ); + } ); + it( 'renders warning when the block does not exist', async () => { // We have to use different ids because entities are cached in memory. - const id = 1; + const id = 3; const initialHtml = ``; - const endpoint = `/wp/v2/blocks/${ id }`; const { getByA11yLabel } = await initializeEditor( { initialHtml, @@ -72,33 +135,29 @@ describe( 'Reusable block', () => { getByA11yLabel( /Reusable block Block\. Row 1/ ) ); - const reusableBlockRequest = fetchRequest.mock.calls.find( ( call ) => - call[ 0 ].path.startsWith( endpoint ) - ); - const blockDeleted = await waitFor( () => within( reusableBlock ).getByText( 'Block has been deleted or is unavailable.' ) ); - expect( reusableBlockRequest ).toBeDefined(); expect( reusableBlock ).toBeDefined(); expect( blockDeleted ).toBeDefined(); - expect( getEditorHtml() ).toBe( initialHtml ); } ); it( 'renders block content', async () => { // We have to use different ids because entities are cached in memory. - const id = 2; + const id = 4; const initialHtml = ``; const endpoint = `/wp/v2/blocks/${ id }`; // Return mocked response for the block endpoint. fetchRequest.mockImplementation( ( { path } ) => { + let response = {}; if ( path.startsWith( endpoint ) ) { - return Promise.resolve( getMockedReusableBlock( id ) ); + response = getMockedReusableBlock( id ); } + return Promise.resolve( response ); } ); const { getByA11yLabel } = await initializeEditor( { @@ -109,10 +168,6 @@ describe( 'Reusable block', () => { getByA11yLabel( /Reusable block Block\. Row 1/ ) ); - const reusableBlockRequest = fetchRequest.mock.calls.find( ( call ) => - call[ 0 ].path.startsWith( endpoint ) - ); - const innerBlockListWrapper = await waitFor( () => within( reusableBlock ).getByTestId( 'block-list-wrapper' ) ); @@ -133,9 +188,7 @@ describe( 'Reusable block', () => { ) ); - expect( reusableBlockRequest ).toBeDefined(); expect( reusableBlock ).toBeDefined(); expect( headingInnerBlock ).toBeDefined(); - expect( getEditorHtml() ).toBe( initialHtml ); } ); } ); diff --git a/test/native/__mocks__/styleMock.js b/test/native/__mocks__/styleMock.js index 46f0d1a2b41079..d369d27b926544 100644 --- a/test/native/__mocks__/styleMock.js +++ b/test/native/__mocks__/styleMock.js @@ -107,9 +107,16 @@ module.exports = { paddingRight: 10, }, 'block-types-list__column': { - padding: 10, + paddingLeft: 10, + paddingRight: 10, }, floatingToolbar: { height: 10, }, + searchFormPlaceholder: { + color: 'gray', + }, + ripple: { + backgroundColor: 'white', + }, }; diff --git a/test/native/helpers.js b/test/native/helpers.js index 9033635d322481..623b5131845411 100644 --- a/test/native/helpers.js +++ b/test/native/helpers.js @@ -33,14 +33,9 @@ provideToNativeHtml.mockImplementation( ( html ) => { serializedHtml = html; } ); -export async function initializeEditor( { initialHtml } ) { +export async function initializeEditor( props ) { const renderResult = render( - + ); const { getByTestId } = renderResult;