diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 8ef7ebeee71532..c155f6cf66a38a 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -428,6 +428,7 @@ export class ImageEdit extends Component { id: media.id, url: media.url, caption: media.caption, + alt: media.alt, }; let additionalAttributes; diff --git a/packages/block-library/src/image/test/edit.native.js b/packages/block-library/src/image/test/edit.native.js index 214c953be323b2..2803060847bb92 100644 --- a/packages/block-library/src/image/test/edit.native.js +++ b/packages/block-library/src/image/test/edit.native.js @@ -16,6 +16,7 @@ import Clipboard from '@react-native-clipboard/clipboard'; */ import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks'; import { + requestMediaPicker, setFeaturedImage, sendMediaUpload, subscribeMediaUpload, @@ -49,6 +50,10 @@ jest.mock( 'lodash', () => { return { ...actual, delay: ( cb ) => cb() }; } ); +function mockGetMedia( media ) { + jest.spyOn( select( coreStore ), 'getMedia' ).mockReturnValue( media ); +} + const apiFetchPromise = Promise.resolve( {} ); const clipboardPromise = Promise.resolve( '' ); @@ -292,12 +297,6 @@ describe( 'Image Block', () => { ); } - function mockGetMedia( media ) { - jest.spyOn( select( coreStore ), 'getMedia' ).mockReturnValueOnce( - media - ); - } - it( 'does not prompt to replace featured image during a new image upload', () => { // Arrange const INITIAL_IMAGE = { id: 1, url: 'mock-url-1' }; @@ -406,4 +405,37 @@ describe( 'Image Block', () => { ); } ); } ); + + it( 'sets src and alt attributes when selecting media', async () => { + const IMAGE = { id: 1, url: 'mock-image', alt: 'A beautiful mountain' }; + requestMediaPicker.mockImplementationOnce( + ( source, filter, multiple, callback ) => { + callback( { + id: IMAGE.id, + url: IMAGE.url, + alt: IMAGE.alt, + } ); + } + ); + mockGetMedia( { + id: IMAGE.id, + source_url: IMAGE.url, + } ); + + const initialHtml = ` + +
+ +
+ `; + const screen = await initializeEditor( { initialHtml } ); + + fireEvent.press( screen.getByText( 'ADD IMAGE' ) ); + fireEvent.press( screen.getByText( 'WordPress Media Library' ) ); + + const expectedHtml = ` +
${ IMAGE.alt }
+`; + expect( getEditorHtml() ).toBe( expectedHtml ); + } ); } ); diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNMedia.kt b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNMedia.kt index 8f1b3bb367e86c..9c3a25a5ed0762 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNMedia.kt +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNMedia.kt @@ -8,5 +8,6 @@ interface RNMedia { val type: String val caption: String val title: String + val alt: String fun toMap(): WritableMap } diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/Media.kt b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/Media.kt index fc835284805ae2..d225593d8eef4d 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/Media.kt +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/Media.kt @@ -14,7 +14,8 @@ data class Media( override val url: String, override val type: String, override val caption: String = "", - override val title: String = "" + override val title: String = "", + override val alt: String = "" ) : RNMedia { override fun toMap(): WritableMap = WritableNativeMap().apply { putInt("id", id) @@ -22,17 +23,11 @@ data class Media( putString("type", type) putString("caption", caption) putString("title", title) + putString("alt", alt) } companion object { - @JvmStatic - fun createRNMediaUsingMimeType( - id: Int, - url: String, - mimeType: String?, - caption: String?, - title: String? - ): Media { + private fun convertToType(mimeType: String?): String { val isMediaType = { mediaType: MediaType -> mimeType?.startsWith(mediaType.name.toLowerCase(Locale.ROOT)) == true } @@ -41,6 +36,30 @@ data class Media( isMediaType(VIDEO) -> VIDEO else -> OTHER }.name.toLowerCase(Locale.ROOT) + return type; + } + + @JvmStatic + fun createRNMediaUsingMimeType( + id: Int, + url: String, + mimeType: String?, + caption: String?, + title: String?, + alt: String?, + ): Media { + val type = convertToType(mimeType) + return Media(id, url, type, caption ?: "", title ?: "", alt ?: "") + } + @JvmStatic + fun createRNMediaUsingMimeType( + id: Int, + url: String, + mimeType: String?, + caption: String?, + title: String?, + ): Media { + val type = convertToType(mimeType) return Media(id, url, type, caption ?: "", title ?: "") } } diff --git a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift index 98b4c721caff1b..ae03570d6f038a 100644 --- a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift +++ b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift @@ -4,13 +4,15 @@ public struct MediaInfo: Encodable { public let type: String? public let title: String? public let caption: String? + public let alt: String? - public init(id: Int32?, url: String?, type: String?, caption: String? = nil, title: String? = nil) { + public init(id: Int32?, url: String?, type: String?, caption: String? = nil, title: String? = nil, alt: String? = nil) { self.id = id self.url = url self.type = type self.caption = caption self.title = title + self.alt = alt } } diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 577348dd63bd0a..cd464e5021adf3 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 - [*] Add 'Insert from URL' option to Video block [#41493] +- [*] Image block copies the alt text from the media library when selecting an item [#41839] ## 1.78.1 diff --git a/packages/react-native-editor/__device-tests__/helpers/test-data.js b/packages/react-native-editor/__device-tests__/helpers/test-data.js index ac225a02403215..9f9a3c4214c976 100644 --- a/packages/react-native-editor/__device-tests__/helpers/test-data.js +++ b/packages/react-native-editor/__device-tests__/helpers/test-data.js @@ -96,7 +96,7 @@ exports.imageCompletehtml = ` `; exports.imageShorteHtml = ` -
C'est la vie my friends
+
A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground
C'est la vie my friends
diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java index b94e581a736cbe..354cb09e953ecd 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java @@ -1,5 +1,7 @@ package com.gutenberg; +import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType; + import android.app.Application; import android.content.Intent; import android.content.res.Configuration; @@ -83,17 +85,17 @@ public void requestMediaPickFromMediaLibrary(MediaSelectedCallback mediaSelected switch (mediaType) { case IMAGE: - rnMediaList.add(new Media(1, "https://cldup.com/cXyG__fTLN.jpg", "image", "Mountain", "")); + rnMediaList.add(createRNMediaUsingMimeType(1, "https://cldup.com/cXyG__fTLN.jpg", "image", "Mountain", "", "A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground")); break; case VIDEO: - rnMediaList.add(new Media(2, "https://i.cloudup.com/YtZFJbuQCE.mov", "video", "Cloudup", "")); + rnMediaList.add(createRNMediaUsingMimeType(2, "https://i.cloudup.com/YtZFJbuQCE.mov", "video", "Cloudup", "")); break; case ANY: case OTHER: - rnMediaList.add(new Media(3, "https://wordpress.org/latest.zip", "zip", "WordPress latest version", "WordPress.zip")); + rnMediaList.add(createRNMediaUsingMimeType(3, "https://wordpress.org/latest.zip", "zip", "WordPress latest version", "WordPress.zip")); break; case AUDIO: - rnMediaList.add(new Media(5, "https://cldup.com/59IrU0WJtq.mp3", "audio", "Summer presto", "")); + rnMediaList.add(createRNMediaUsingMimeType(5, "https://cldup.com/59IrU0WJtq.mp3", "audio", "Summer presto", "")); break; } mediaSelectedCallback.onMediaFileSelected(rnMediaList); @@ -145,7 +147,7 @@ public void getOtherMediaPickerOptions(OtherMediaOptionsReceivedCallback otherMe public void requestMediaPickFrom(String mediaSource, MediaSelectedCallback mediaSelectedCallback, Boolean allowMultipleSelection) { if (mediaSource.equals("1")) { List rnMediaList = new ArrayList<>(); - rnMediaList.add(new Media(1, "https://grad.illinois.edu/sites/default/files/pdfs/cvsamples.pdf", "other", "","cvsamples.pdf")); + rnMediaList.add(createRNMediaUsingMimeType(1, "https://grad.illinois.edu/sites/default/files/pdfs/cvsamples.pdf", "other", "","cvsamples.pdf")); mediaSelectedCallback.onMediaFileSelected(rnMediaList); } } diff --git a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift index 9c722452c19d9b..928310f3f76439 100644 --- a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift +++ b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift @@ -88,9 +88,9 @@ extension GutenbergViewController: GutenbergBridgeDelegate { case .image: if(allowMultipleSelection) { callback([MediaInfo(id: 1, url: "https://cldup.com/cXyG__fTLN.jpg", type: "image"), - MediaInfo(id: 3, url: "https://cldup.com/cXyG__fTLN.jpg", type: "image", caption: "Mountain")]) + MediaInfo(id: 3, url: "https://cldup.com/cXyG__fTLN.jpg", type: "image", caption: "Mountain", alt: "A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground")]) } else { - callback([MediaInfo(id: 1, url: "https://cldup.com/cXyG__fTLN.jpg", type: "image", caption: "Mountain")]) + callback([MediaInfo(id: 1, url: "https://cldup.com/cXyG__fTLN.jpg", type: "image", caption: "Mountain", alt: "A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground")]) } case .video: if(allowMultipleSelection) {