From 1b82cbf757fbce04f21f2cf317d7367581e706e4 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 6 Jul 2023 23:34:34 +1000 Subject: [PATCH] [RNMobile] Add media inserter blocks to toolbar (#51827) * Add media button to native header toolbar * Add other media button types * Display media inserter buttons when no block is selected * Add insertBlocks function to Header Toolbar * Use const instead of function for renderMediaButtons * Remove inline function for onInsertBlock * Update header toolbar media block insertion method * Update media toolbar insertion tests * Update media toolbar insertion tests * Update visual-editor test with Media Toolbar insertion tests * Fix typo in Gallery block test * Update visual editor tests to use toBeVisible when selecting Media Toolbar buttons * Update Visual Editor gallery block selector * Wrap Media Inserter toolbar buttons in ToolbarGroup * Remove iOS ActionSheet conditional from Visual Editor test * Update CHANGELOG --------- Co-authored-by: Gerardo --- .../src/gallery/test/index.native.js | 4 +- .../header/header-toolbar/index.native.js | 66 +++++++++- .../test/__snapshots__/index.native.js.snap | 15 +++ .../visual-editor/test/index.native.js | 119 +++++++++++++++++- packages/react-native-editor/CHANGELOG.md | 1 + 5 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 packages/edit-post/src/components/visual-editor/test/__snapshots__/index.native.js.snap diff --git a/packages/block-library/src/gallery/test/index.native.js b/packages/block-library/src/gallery/test/index.native.js index a64b8bf403281..70b05870b5f30 100644 --- a/packages/block-library/src/gallery/test/index.native.js +++ b/packages/block-library/src/gallery/test/index.native.js @@ -161,7 +161,7 @@ describe( 'Gallery block', () => { // This case is disabled until the issue (https://github.com/WordPress/gutenberg/issues/38444) // is addressed. - it.skip( 'block remains selected after dimissing the media options picker', async () => { + it.skip( 'block remains selected after dismissing the media options picker', async () => { // Initialize with an empty gallery const { getByLabelText, getByText, getByTestId } = await initializeEditor( { @@ -175,7 +175,7 @@ describe( 'Gallery block', () => { expect( getByText( 'Choose images' ) ).toBeVisible(); expect( getByText( 'WordPress Media Library' ) ).toBeVisible(); - // Dimiss the picker + // Dismiss the picker if ( Platform.isIOS ) { fireEvent.press( getByText( 'Cancel' ) ); } else { diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index b9b91c8d4f558..948d37fe76ff5 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -19,10 +19,15 @@ import { import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; import { keyboardClose, + audio as audioIcon, + media as imageIcon, + video as videoIcon, + gallery as galleryIcon, undo as undoIcon, redo as redoIcon, } from '@wordpress/icons'; import { store as editorStore } from '@wordpress/editor'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies @@ -38,6 +43,7 @@ function HeaderToolbar( { showInserter, showKeyboardHideButton, getStylesFromColorScheme, + insertBlock, onHideKeyboard, isRTL, noContentSelected, @@ -55,6 +61,7 @@ function HeaderToolbar( { scrollViewRef.current.scrollTo( { x: 0 } ); } }; + const renderHistoryButtons = () => { const buttons = [ /* TODO: replace with EditorHistoryRedo and EditorHistoryUndo. */ @@ -83,6 +90,60 @@ function HeaderToolbar( { return isRTL ? buttons.reverse() : buttons; }; + const onInsertBlock = useCallback( + ( blockType ) => () => { + insertBlock( createBlock( blockType ), undefined, undefined, true, { + source: 'inserter_menu', + } ); + }, + [ insertBlock ] + ); + + const renderMediaButtons = ( + + + + + + + ); + const onToggleInserter = useCallback( ( isOpen ) => { if ( isOpen ) { @@ -131,6 +192,7 @@ function HeaderToolbar( { useExpandedMode={ useExpandedMode } onToggle={ onToggleInserter } /> + { noContentSelected && renderMediaButtons } { renderHistoryButtons() } @@ -181,7 +243,8 @@ export default compose( [ }; } ), withDispatch( ( dispatch ) => { - const { clearSelectedBlock } = dispatch( blockEditorStore ); + const { clearSelectedBlock, insertBlock } = + dispatch( blockEditorStore ); const { togglePostTitleSelection } = dispatch( editorStore ); return { @@ -191,6 +254,7 @@ export default compose( [ clearSelectedBlock(); togglePostTitleSelection( false ); }, + insertBlock, }; } ), withViewportMatch( { isLargeViewport: 'medium' } ), diff --git a/packages/edit-post/src/components/visual-editor/test/__snapshots__/index.native.js.snap b/packages/edit-post/src/components/visual-editor/test/__snapshots__/index.native.js.snap new file mode 100644 index 0000000000000..de7091fa94735 --- /dev/null +++ b/packages/edit-post/src/components/visual-editor/test/__snapshots__/index.native.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`when nothing is selected media buttons and picker display correctly 1`] = ` +" +

First example paragraph.

+ + + +

Second example paragraph.

+ + + + +" +`; diff --git a/packages/edit-post/src/components/visual-editor/test/index.native.js b/packages/edit-post/src/components/visual-editor/test/index.native.js index af07e4309ab69..8c6e041880a83 100644 --- a/packages/edit-post/src/components/visual-editor/test/index.native.js +++ b/packages/edit-post/src/components/visual-editor/test/index.native.js @@ -1,11 +1,12 @@ /** * External dependencies */ -import { initializeEditor, fireEvent } from 'test/helpers'; +import { initializeEditor, getEditorHtml, fireEvent } from 'test/helpers'; /** * WordPress dependencies */ +import { Platform } from '@wordpress/element'; import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks'; import { registerCoreBlocks } from '@wordpress/block-library'; @@ -21,6 +22,12 @@ afterAll( () => { } ); } ); +const MEDIA_OPTIONS = [ + 'Choose from device', + 'Take a Photo', + 'WordPress Media Library', +]; + const initialHtml = `

First example paragraph.

@@ -63,6 +70,38 @@ describe( 'when title is focused', () => { screen.getAllByLabelText( /Paragraph Block. Row 3/ )[ 0 ] ).toBeDefined(); } ); + + it( 'media blocks should be displayed', async () => { + const screen = await initializeEditor( { + initialHtml, + } ); + + // Focus first block + fireEvent.press( + screen.getAllByLabelText( /Paragraph Block. Row 1/ )[ 0 ] + ); + + // Focus title + fireEvent( + screen.getAllByLabelText( 'Post title. test' )[ 0 ], + 'select' + ); + + // Assert that the media buttons are visible + const imageButton = await screen.findByTestId( 'insert-image-button' ); + expect( imageButton ).toBeVisible(); + + const videoButton = await screen.findByTestId( 'insert-video-button' ); + expect( videoButton ).toBeVisible(); + + const galleryButton = await screen.findByTestId( + 'insert-gallery-button' + ); + expect( galleryButton ).toBeVisible(); + + const audioButton = await screen.findByTestId( 'insert-audio-button' ); + expect( audioButton ).toBeVisible(); + } ); } ); describe( 'when title is no longer focused', () => { @@ -101,4 +140,82 @@ describe( 'when title is no longer focused', () => { screen.getAllByLabelText( /Heading Block. Row 3/ )[ 0 ] ).toBeDefined(); } ); + + it( 'media blocks should not be displayed', async () => { + const screen = await initializeEditor( { + initialHtml, + } ); + + // Focus first block + fireEvent.press( + screen.getAllByLabelText( /Paragraph Block. Row 1/ )[ 0 ] + ); + + // Focus title + fireEvent( + screen.getAllByLabelText( 'Post title. test' )[ 0 ], + 'select' + ); + + // Focus last block + fireEvent.press( + screen.getAllByLabelText( /Paragraph Block. Row 2/ )[ 0 ] + ); + + // Assert that the media buttons are not visible + const imageButton = screen.queryByTestId( 'insert-image-button' ); + expect( imageButton ).toBeNull(); + + const videoButton = screen.queryByTestId( 'insert-video-button' ); + expect( videoButton ).toBeNull(); + + const galleryButton = screen.queryByTestId( 'insert-gallery-button' ); + expect( galleryButton ).toBeNull(); + + const audioButton = screen.queryByTestId( 'insert-audio-button' ); + expect( audioButton ).toBeNull(); + } ); +} ); + +describe( 'when nothing is selected', () => { + it( 'media buttons and picker display correctly', async () => { + const screen = await initializeEditor( { + initialHtml, + } ); + + const { getByText, getByTestId } = screen; + + // Check that the gallery button is visible within the toolbar + const galleryButton = await screen.queryByTestId( + 'insert-gallery-button' + ); + expect( galleryButton ).toBeVisible(); + + // Press the toolbar Gallery button + fireEvent.press( galleryButton ); + + // Expect the block to be created + expect( + screen.getAllByLabelText( /Gallery Block. Row 3/ )[ 0 ] + ).toBeDefined(); + + expect( getByText( 'Choose images' ) ).toBeVisible(); + MEDIA_OPTIONS.forEach( ( option ) => + expect( getByText( option ) ).toBeVisible() + ); + + // Dismiss the picker + if ( Platform.isIOS ) { + fireEvent.press( getByText( 'Cancel' ) ); + } else { + fireEvent( getByTestId( 'media-options-picker' ), 'backdropPress' ); + } + + // Expect the Gallery block to remain + expect( + screen.getAllByLabelText( /Gallery Block. Row 3/ )[ 0 ] + ).toBeDefined(); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index d43144db54722..add4d4c780483 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -12,6 +12,7 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [*] Rename "Reusable blocks" to "Synced patterns", aligning with the web editor. [#51704] - [**] Fix a crash related to Reanimated when closing the editor [#52320] +- [**] Add media inserter buttons to editor toolbar [#51827] ## 1.98.1 - [*] fix: Display heading level dropdown icons and labels [#52004]