Skip to content

Commit

Permalink
fix: Image block preserves alt text from media library (#41839)
Browse files Browse the repository at this point in the history
* fix: Image block preserves alt text from media library

When the alt text for a media item is present in the media library, that
value should be copied into the Image block when inserted. This behavior
avoids the need to re-enter the alt text for each image inserted into
the post content.

* fix: Allow Media with and without alt text

Previous code required an alt text value, which broken existing code.
This overrides the method to support both contexts. It also updates
existing code to use the same method of generating media throughout the
source.

* test: Update alt text for demo editor test data

Consistently setting the alt text for all platforms will likely help
avoid confusion.

* test: Update outdated test fixture data

The demo editor now returns an alt text for the test image media.

* docs: Update changelog
  • Loading branch information
dcalhoun authored Jun 23, 2022
1 parent ea8590e commit caba4dc
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/block-library/src/image/edit.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ export class ImageEdit extends Component {
id: media.id,
url: media.url,
caption: media.caption,
alt: media.alt,
};

let additionalAttributes;
Expand Down
44 changes: 38 additions & 6 deletions packages/block-library/src/image/test/edit.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Clipboard from '@react-native-clipboard/clipboard';
*/
import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks';
import {
requestMediaPicker,
setFeaturedImage,
sendMediaUpload,
subscribeMediaUpload,
Expand Down Expand Up @@ -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( '' );
Expand Down Expand Up @@ -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' };
Expand Down Expand Up @@ -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 = `
<!-- wp:image -->
<figure class="wp-block-image">
<img alt="" />
</figure>
<!-- /wp:image -->`;
const screen = await initializeEditor( { initialHtml } );

fireEvent.press( screen.getByText( 'ADD IMAGE' ) );
fireEvent.press( screen.getByText( 'WordPress Media Library' ) );

const expectedHtml = `<!-- wp:image {"id":${ IMAGE.id },"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img src="${ IMAGE.url }" alt="${ IMAGE.alt }" class="wp-image-${ IMAGE.id }"/></figure>
<!-- /wp:image -->`;
expect( getEditorHtml() ).toBe( expectedHtml );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ interface RNMedia {
val type: String
val caption: String
val title: String
val alt: String
fun toMap(): WritableMap
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,20 @@ 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)
putString("url", url)
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
}
Expand All @@ -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 ?: "")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/react-native-editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ exports.imageCompletehtml = `<!-- wp:image {"id":1,"sizeslug":"large"} -->
<!-- /wp:paragraph -->`;

exports.imageShorteHtml = `<!-- wp:image {"id":1,"sizeslug":"large"} -->
<figure class="wp-block-image size-large"><img src="https://cldup.com/cXyG__fTLN.jpg" alt="" class="wp-image-1"/><figcaption>C'est la vie my friends</figcaption></figure>
<figure class="wp-block-image size-large"><img src="https://cldup.com/cXyG__fTLN.jpg" alt="A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground" class="wp-image-1"/><figcaption>C'est la vie my friends</figcaption></figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -145,7 +147,7 @@ public void getOtherMediaPickerOptions(OtherMediaOptionsReceivedCallback otherMe
public void requestMediaPickFrom(String mediaSource, MediaSelectedCallback mediaSelectedCallback, Boolean allowMultipleSelection) {
if (mediaSource.equals("1")) {
List<RNMedia> 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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit caba4dc

Please sign in to comment.