Commit c7f4fe4d authored by bnjmnm's avatar bnjmnm
Browse files

Issue #3246365 by lauriii, ankithashetty, Wim Leers: [drupalMedia] Show the...

Issue #3246365 by lauriii, ankithashetty, Wim Leers: [drupalMedia] Show the Image Media's default alt text that is being overridden
parent 00e831d1
......@@ -75,3 +75,37 @@
display: block;
width: 240px;
}
.ck.ck-media-alternative-text-form {
min-width: 300px;
max-width: 600px;
padding: 0;
}
.ck.ck-media-alternative-text-form .ck-labeled-field-view,
.ck.ck-media-alternative-text-form .ck-media-alternative-text-form__default-alt-text {
margin: var(--ck-spacing-large) var(--ck-spacing-large) var(--ck-spacing-small);
}
.ck.ck-media-alternative-text-form .ck-labeled-field-view .ck-input-text {
width: 100%;
}
.ck.ck-media-alternative-text-form .ck-button {
width: 50%;
margin: var(--ck-spacing-large) 0 0 0;
padding: var(--ck-spacing-standard);
border: 0;
border-top: 1px solid var(--ck-color-base-border);
border-radius: 0;
}
.ck.ck-media-alternative-text-form .ck-button:last-of-type {
border-right: 1px solid var(--ck-color-base-border);
}
.ck.ck .ck-media-alternative-text-form__default-alt-text-label {
font-weight: bold;
}
.ck.ck .ck-media-alternative-text-form__default-alt-text-label,
.ck.ck .ck-media-alternative-text-form__default-alt-text-value {
white-space: normal;
}
......@@ -8,6 +8,10 @@ import DrupalMediaMetadataRepository from '../drupalmediametadatarepository';
import { isDrupalMedia } from '../utils';
import { METADATA_ERROR } from './utils';
/**
* @module drupalMedia/mediaimagetextalternative/mediaimagetextalternativeediting
*/
/**
* The media image text alternative editing plugin.
*/
......
......@@ -8,7 +8,7 @@ import {
clickOutsideHandler,
} from 'ckeditor5/src/ui';
import { getSelectedDrupalMediaWidget } from '../utils';
import { getSelectedDrupalMediaWidget, isDrupalMedia } from '../utils';
import {
getBalloonPositionData,
repositionContextualBalloon,
......@@ -64,7 +64,7 @@ export default class MediaImageTextAlternativeUi extends Plugin {
const view = new ButtonView(locale);
view.set({
label: Drupal.t('Override media image text alternative'),
label: Drupal.t('Override media image alternative text'),
icon: icons.lowVision,
tooltip: true,
});
......@@ -146,9 +146,11 @@ export default class MediaImageTextAlternativeUi extends Plugin {
if (this._isVisible) {
return;
}
const editor = this.editor;
const command = editor.commands.get('mediaImageTextAlternative');
const metadataRepository = editor.plugins.get(
'DrupalMediaMetadataRepository',
);
const labeledInput = this._form.labeledInput;
this._form.disableCssTransitions();
......@@ -168,6 +170,28 @@ export default class MediaImageTextAlternativeUi extends Plugin {
labeledInput.fieldView.element.value = command.value || '';
labeledInput.fieldView.value = labeledInput.fieldView.element.value;
this._form.defaultAltText = '';
const modelElement = editor.model.document.selection.getSelectedElement();
// Make sure that each time the panel shows up, the default alt text remains
// in sync with the value from the metadata repository.
if (isDrupalMedia(modelElement)) {
metadataRepository
.getMetadata(modelElement)
.then((metadata) => {
this._form.defaultAltText = metadata.imageSourceMetadata
? metadata.imageSourceMetadata.alt
: '';
})
.catch((e) => {
// There isn't any UI indication for errors because this should be
// always called after the Drupal Media has been upcast, which would
// already display an error in the UI.
// @see module:drupalMedia/mediaimagetextalternative/mediaimagetextalternativeediting~MediaImageTextAlternativeEditing
console.warn(e.toString());
});
}
this._form.labeledInput.fieldView.select();
this._form.enableCssTransitions();
......
......@@ -11,6 +11,7 @@ import {
createLabeledInputText,
injectCssTransitionDisabler,
submitHandler,
Template,
} from 'ckeditor5/src/ui';
import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
import { icons } from 'ckeditor5/src/core';
......@@ -39,6 +40,22 @@ export default class TextAlternativeFormView extends View {
*/
this.labeledInput = this._createLabeledInputView();
/**
* The default alt text.
*
* @observable
*
* @member {string} #defaultAltText
*/
this.set('defaultAltText', undefined);
/**
* The default alt text view.
*
* @type {module:ui/template~Template}
*/
this.defaultAltTextView = this._createDefaultAltTextView();
/**
* A button used to submit the form.
*/
......@@ -84,11 +101,16 @@ export default class TextAlternativeFormView extends View {
tag: 'form',
attributes: {
class: ['ck', 'ck-text-alternative-form', 'ck-responsive-form'],
class: ['ck', 'ck-media-alternative-text-form', 'ck-vertical-form'],
tabindex: '-1',
},
children: [this.labeledInput, this.saveButtonView, this.cancelButtonView],
children: [
this.defaultAltTextView,
this.labeledInput,
this.saveButtonView,
this.cancelButtonView,
],
});
injectCssTransitionDisabler(this);
......@@ -163,8 +185,49 @@ export default class TextAlternativeFormView extends View {
createLabeledInputText,
);
labeledInput.label = Drupal.t('Override text alternative');
labeledInput.label = Drupal.t('Alternative text override');
return labeledInput;
}
/**
* Creates a default alt text view.
*
* @return {module:ui/template~Template}
* A template for default alt text view.
* @private
*/
_createDefaultAltTextView() {
const bind = Template.bind(this, this);
return new Template({
tag: 'div',
attributes: {
class: [
'ck-media-alternative-text-form__default-alt-text',
bind.if('defaultAltText', 'ck-hidden', (value) => !value),
],
},
children: [
{
tag: 'strong',
attributes: {
class: 'ck-media-alternative-text-form__default-alt-text-label',
},
children: [Drupal.t('Default alternative text:')],
},
' ',
{
tag: 'span',
attributes: {
class: 'ck-media-alternative-text-form__default-alt-text-value',
},
children: [
{
text: [bind.to('defaultAltText')],
},
],
},
],
});
}
}
......@@ -104,7 +104,7 @@ public function mediaEntityMetadata(Request $request) {
$response = [];
if ($image_field) {
$response['imageSourceMetadata'] = [
'alt' => $media->{$image_field}->alt,
'alt' => $this->entityRepository->getTranslationFromContext($media)->{$image_field}->alt,
];
}
......
......@@ -6,6 +6,8 @@
use Drupal\editor\Entity\Editor;
use Drupal\file\Entity\File;
use Drupal\filter\Entity\FilterFormat;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Entity\ContentLanguageSettings;
use Drupal\media\Entity\Media;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\ckeditor5\Traits\SynchronizeCsrfTokenSeedTrait;
......@@ -209,4 +211,47 @@ public function testApi() {
$this->assertSession()->statusCodeEquals(403);
}
/**
* Tests the media entity metadata API with translations.
*/
public function testApiTranslation(): void {
$this->container->get('module_installer')->install(['language', 'content_translation']);
$this->resetAll();
ConfigurableLanguage::create(['id' => 'fi'])->save();
$this->container->get('config.factory')->getEditable('language.negotiation')
->set('url.source', 'path_prefix')
->set('url.prefixes.fi', 'fi')
->save();
$this->rebuildContainer();
ContentLanguageSettings::loadByEntityTypeBundle('media', 'image')
->setDefaultLangcode('en')
->setLanguageAlterable(TRUE)
->save();
$media_fi = Media::load($this->mediaImage->id())->addTranslation('fi');
$media_fi->field_media_image->setValue([
[
'target_id' => '1',
// cSpell:disable-next-line
'alt' => 'oletus alt-teksti kuvalle',
],
]);
$media_fi->save();
$uuid = $this->mediaImage->uuid();
$path = '/ckeditor5/filtered_html/media-entity-metadata';
$token = $this->container->get('csrf_token')->get(ltrim($path, '/'));
// Ensure that translation is returned when language is specified.
$this->drupalGet($path, ['query' => ['uuid' => $uuid, 'token' => $token], 'language' => $media_fi->language()]);
$this->assertSession()->statusCodeEquals(200);
// cSpell:disable-next-line
$this->assertSame(json_encode(['imageSourceMetadata' => ['alt' => 'oletus alt-teksti kuvalle']]), $this->getSession()->getPage()->getContent());
// Ensure that default translation is returned when no language is
// specified.
$this->drupalGet($path, ['query' => ['uuid' => $uuid, 'token' => $token]]);
$this->assertSession()->statusCodeEquals(200);
$this->assertSame(json_encode(['imageSourceMetadata' => ['alt' => 'default alt']]), $this->getSession()->getPage()->getContent());
}
}
......@@ -286,10 +286,10 @@ public function testAlt() {
$this->click('.ck-widget.drupal-media');
$this->assertVisibleBalloon('[aria-label="Drupal Media toolbar"]');
// Click the "Override media image text alternative" button.
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
// Assert that the value is currently empty.
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$this->assertSame('', $alt_override_input->getValue());
$test_alt = 'Alt text override';
......
......@@ -6,11 +6,15 @@
use Drupal\file\Entity\File;
use Drupal\filter\Entity\FilterFormat;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Entity\ContentLanguageSettings;
use Drupal\media\Entity\Media;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use Symfony\Component\Validator\ConstraintViolation;
// cspell:ignore layercake
......@@ -320,10 +324,10 @@ public function testEditableCaption() {
// button still exists.
$this->click('.ck-widget.drupal-media');
$this->assertVisibleBalloon('[aria-label="Drupal Media toolbar"]');
// Click the "Override media image text alternative" button.
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
// Click the "Override media image alternative text" button.
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
// Fill in the alt field and submit.
$alt_override_input->setValue('Gold star for robot boy.');
......@@ -423,12 +427,13 @@ public function testAlt() {
// with a single button to override the alt text.
$this->click('.ck-widget.drupal-media');
$this->assertVisibleBalloon('[aria-label="Drupal Media toolbar"]');
// Click the "Override media image text alternative" button.
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
// Click the "Override media image alternative text" button.
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
// Assert that the default alt text is visible in the UI.
$assert_session->elementTextEquals('css', '.ck-media-alternative-text-form__default-alt-text-value', 'default alt');
// Assert that the value is currently empty.
// @todo Consider changing this in https://www.drupal.org/project/ckeditor5/issues/3246365.
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$this->assertSame('', $alt_override_input->getValue());
// Fill in the alt field and submit.
......@@ -451,10 +456,12 @@ public function testAlt() {
$this->assertSourceAttributeSame('alt', $who_is_zartan);
// The alt field should now display the override instead of the default.
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$this->assertSame($who_is_zartan, $alt_override_input->getValue());
// Assert that the default alt text is still visible in the UI.
$assert_session->elementTextEquals('css', '.ck-media-alternative-text-form__default-alt-text-value', 'default alt');
// Test the process again with a different alt text to make sure it works
// the second time around.
......@@ -472,9 +479,9 @@ public function testAlt() {
// The default value of the alt field should now display the override
// instead of the value on the media image field.
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$this->assertSame($cobra_commander_bio, $alt_override_input->getValue());
// Test that setting alt value to two double quotes will signal to the
......@@ -497,9 +504,9 @@ public function testAlt() {
// Test that setting alt to back to an empty string within the balloon will
// restore the default alt value saved in to the media image field of the
// media item.
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$alt_override_input->setValue('');
$this->getBalloonButton('Save')->click();
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media img[alt*="default alt"]'));
......@@ -513,8 +520,96 @@ public function testAlt() {
* Tests the CKEditor 5 media plugin loads the translated alt attribute.
*/
public function testTranslationAlt() {
// @todo Port in https://www.drupal.org/project/ckeditor5/issues/3246365
$this->markTestSkipped('Blocked on https://www.drupal.org/project/ckeditor5/issues/3246365.');
\Drupal::service('module_installer')->install(['language', 'content_translation']);
$this->resetAll();
ConfigurableLanguage::create(['id' => 'fr'])->save();
ContentLanguageSettings::loadByEntityTypeBundle('media', 'image')
->setDefaultLangcode('en')
->setLanguageAlterable(TRUE)
->save();
$media = Media::create([
'bundle' => 'image',
'name' => 'Screaming hairy armadillo',
'field_media_image' => [
[
'target_id' => 1,
'alt' => 'default alt',
'title' => 'default title',
],
],
]);
$media->save();
$media_fr = $media->addTranslation('fr');
$media_fr->name = "Tatou poilu hurlant";
$media_fr->field_media_image->setValue([
[
'target_id' => '1',
'alt' => "texte alternatif par défaut",
'title' => "titre alternatif par défaut",
],
]);
$media_fr->save();
ContentLanguageSettings::loadByEntityTypeBundle('node', 'blog')
->setDefaultLangcode('en')
->setLanguageAlterable(TRUE)
->save();
$host = $this->createNode([
'type' => 'blog',
'title' => 'Animals with strange names',
'body' => [
'value' => '<drupal-media data-caption="baz" data-entity-type="media" data-entity-uuid="' . $media->uuid() . '"></drupal-media>',
'format' => 'test_format',
],
]);
$host->save();
$translation = $host->addTranslation('fr');
// cSpell:disable-next-line
$translation->title = 'Animaux avec des noms étranges';
$translation->body->value = $host->body->value;
$translation->body->format = $host->body->format;
$translation->save();
Role::load(RoleInterface::AUTHENTICATED_ID)
->grantPermission('translate any entity')
->save();
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$this->drupalGet('/fr/node/' . $host->id() . '/edit');
$this->waitForEditor();
// Test that the default alt attribute displays without an override.
// cSpell:disable-next-line
$this->assertNotEmpty($assert_session->waitForElementVisible('xpath', '//img[contains(@alt, "texte alternatif par défaut")]'));
// Test `aria-label` attribute appears on the preview wrapper.
// cSpell:disable-next-line
$assert_session->elementExists('css', '[data-drupal-media-preview][aria-label="Tatou poilu hurlant"]');
$this->click('.ck-widget.drupal-media');
$this->assertVisibleBalloon('[aria-label="Drupal Media toolbar"]');
// Click the "Override media image alternative text" button.
$this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form');
// Assert that the default alt on the UI is the default alt text from the
// media entity.
// cSpell:disable-next-line
$assert_session->elementTextEquals('css', '.ck-media-alternative-text-form__default-alt-text-value', 'texte alternatif par défaut');
// Fill in the alt field in the balloon form.
// cSpell:disable-next-line
$qui_est_zartan = 'Zartan est le chef des Dreadnoks.';
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$alt_override_input->setValue($qui_est_zartan);
$this->getBalloonButton('Save')->click();
// Assert that the img within the media embed within CKEditor 5 contains
// the overridden alt text set in CKEditor 5.
$this->assertNotEmpty($assert_session->waitForElementVisible('xpath', '//img[contains(@alt, "' . $qui_est_zartan . '")]'));
$this->getSession()->switchToIFrame();
$page->pressButton('Save');
$assert_session->elementExists('xpath', '//img[contains(@alt, "' . $qui_est_zartan . '")]');
}
/**
......
......@@ -31,6 +31,20 @@ protected function drupalLogin(AccountInterface $account) {
$this->container->get('session_manager.metadata_bag')->setCsrfTokenSeed($csrf_token_seed);
}
/**
* {@inheritdoc}
*/
protected function rebuildContainer() {
parent::rebuildContainer();
// Ensure that the CSRF token seed is reset on container rebuild.
if ($this->loggedInUser) {
$current_user = $this->loggedInUser;
$this->drupalLogout();
$this->drupalLogin($current_user);
}
}
/**
* {@inheritdoc}
*/
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment