Skip to content
Snippets Groups Projects
Verified Commit a770ef7b authored by Théodore Biadala's avatar Théodore Biadala
Browse files

Issue #3313616 by mark_fullmer, Sandeep_k, nessthehero, bnjmnm, smustgrave,...

Issue #3313616 by mark_fullmer, Sandeep_k, nessthehero, bnjmnm, smustgrave, Wim Leers, itmaybejj, alexpott, Charles Belov, simohell, AaronMcHale: [drupalMedia] Ability to mark image media as "decorative"

(cherry picked from commit 8e56f384)
parent b41bf7c9
No related branches found
No related tags found
2 merge requests!11185Issue #3477324 by andypost, alexpott: Fix usage of str_getcsv() and fgetcsv() for PHP 8.4,!9944Issue #3483353: Consider making the createCopy config action optionally fail...
Pipeline #200628 passed
...@@ -83,15 +83,18 @@ ...@@ -83,15 +83,18 @@
max-width: 600px; max-width: 600px;
padding: 0; padding: 0;
} }
.ck.ck-media-alternative-text-form .ck-labeled-field-view, .ck.ck-media-alternative-text-form .ck-labeled-field-view,
.ck.ck-media-alternative-text-form .ck-media-alternative-text-form__default-alt-text { .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); 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 { .ck.ck-media-alternative-text-form .ck-labeled-field-view .ck-input-text {
width: 100%; width: 100%;
} }
.ck.ck-media-alternative-text-form .ck-button { .ck.ck-media-alternative-text-form .ck-button-save,
.ck.ck-media-alternative-text-form .ck-button-cancel {
width: 50%; width: 50%;
margin: var(--ck-spacing-large) 0 0 0; margin: var(--ck-spacing-large) 0 0 0;
padding: var(--ck-spacing-standard); padding: var(--ck-spacing-standard);
...@@ -99,11 +102,3 @@ ...@@ -99,11 +102,3 @@
border-top: 1px solid var(--ck-color-base-border); border-top: 1px solid var(--ck-color-base-border);
border-radius: 0; border-radius: 0;
} }
.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;
}
This diff is collapsed.
...@@ -105,7 +105,13 @@ export default class MediaImageTextAlternativeUi extends Plugin { ...@@ -105,7 +105,13 @@ export default class MediaImageTextAlternativeUi extends Plugin {
this.listenTo(this._form, 'submit', () => { this.listenTo(this._form, 'submit', () => {
editor.execute('mediaImageTextAlternative', { editor.execute('mediaImageTextAlternative', {
newValue: this._form.labeledInput.fieldView.element.value, // The "decorative toggle" allows users to opt-in to empty alt
// attributes for the very rare edge cases where that is valid. This is
// indicated by specifying two double quotes as the alternative text.
// See https://www.w3.org/WAI/tutorials/images/decorative .
newValue: this._form.decorativeToggle.isOn
? '""'
: this._form.labeledInput.fieldView.element.value,
}); });
this._hideForm(true); this._hideForm(true);
...@@ -149,6 +155,7 @@ export default class MediaImageTextAlternativeUi extends Plugin { ...@@ -149,6 +155,7 @@ export default class MediaImageTextAlternativeUi extends Plugin {
} }
const editor = this.editor; const editor = this.editor;
const command = editor.commands.get('mediaImageTextAlternative'); const command = editor.commands.get('mediaImageTextAlternative');
const decorativeToggle = this._form.decorativeToggle;
const metadataRepository = editor.plugins.get( const metadataRepository = editor.plugins.get(
'DrupalMediaMetadataRepository', 'DrupalMediaMetadataRepository',
); );
...@@ -163,6 +170,14 @@ export default class MediaImageTextAlternativeUi extends Plugin { ...@@ -163,6 +170,14 @@ export default class MediaImageTextAlternativeUi extends Plugin {
}); });
} }
// This implementation, populating double quotes, differs from drupalImage.
// In drupalImage, an image either has alt text or it is decorative, so the
// 'decorative' state can be represented by an empty string. In drupalMedia,
// an image can inherit alt text from the media entity (represented by an
// empty string), can have overridden alt text (represented by user-entered
// text), or can be designated decorative (represented by double quotes).
decorativeToggle.isOn = command.value === '""';
// Make sure that each time the panel shows up, the field remains in sync with the value of // Make sure that each time the panel shows up, the field remains in sync with the value of
// the command. If the user typed in the input, then canceled the balloon (`labeledInput#value` // the command. If the user typed in the input, then canceled the balloon (`labeledInput#value`
// stays unaltered) and re-opened it without changing the value of the command, they would see the // stays unaltered) and re-opened it without changing the value of the command, they would see the
...@@ -183,6 +198,9 @@ export default class MediaImageTextAlternativeUi extends Plugin { ...@@ -183,6 +198,9 @@ export default class MediaImageTextAlternativeUi extends Plugin {
this._form.defaultAltText = metadata.imageSourceMetadata this._form.defaultAltText = metadata.imageSourceMetadata
? metadata.imageSourceMetadata.alt ? metadata.imageSourceMetadata.alt
: ''; : '';
labeledInput.infoText = Drupal.t(
`Leave blank to use the default alternative text: "${this._form.defaultAltText}".`,
);
}) })
.catch((e) => { .catch((e) => {
// There isn't any UI indication for errors because this should be // There isn't any UI indication for errors because this should be
...@@ -193,8 +211,6 @@ export default class MediaImageTextAlternativeUi extends Plugin { ...@@ -193,8 +211,6 @@ export default class MediaImageTextAlternativeUi extends Plugin {
}); });
} }
this._form.labeledInput.fieldView.select();
this._form.enableCssTransitions(); this._form.enableCssTransitions();
} }
......
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
/* cspell:ignore focusables switchbuttonview */
// cspell:ignore focusables
import { import {
ButtonView, ButtonView,
FocusCycler, FocusCycler,
LabeledFieldView, LabeledFieldView,
SwitchButtonView,
View, View,
ViewCollection, ViewCollection,
createLabeledInputText, createLabeledInputText,
...@@ -16,8 +16,6 @@ import { ...@@ -16,8 +16,6 @@ import {
import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils'; import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
import { icons } from 'ckeditor5/src/core'; import { icons } from 'ckeditor5/src/core';
// cspell:ignore focusables
export default class TextAlternativeFormView extends View { export default class TextAlternativeFormView extends View {
/** /**
* @inheritdoc * @inheritdoc
...@@ -36,25 +34,16 @@ export default class TextAlternativeFormView extends View { ...@@ -36,25 +34,16 @@ export default class TextAlternativeFormView extends View {
this.keystrokes = new KeystrokeHandler(); this.keystrokes = new KeystrokeHandler();
/** /**
* An input with a label. * A toggle for marking the image as decorative.
*/
this.labeledInput = this._createLabeledInputView();
/**
* The default alt text.
* *
* @observable * @member {module:ui/button/switchbuttonview~SwitchButtonView} #decorativeToggle
*
* @member {string} #defaultAltText
*/ */
this.set('defaultAltText', undefined); this.decorativeToggle = this._decorativeToggleView();
/** /**
* The default alt text view. * An input with a label.
*
* @type {module:ui/template~Template}
*/ */
this.defaultAltTextView = this._createDefaultAltTextView(); this.labeledInput = this._createLabeledInputView();
/** /**
* A button used to submit the form. * A button used to submit the form.
...@@ -106,7 +95,10 @@ export default class TextAlternativeFormView extends View { ...@@ -106,7 +95,10 @@ export default class TextAlternativeFormView extends View {
}, },
children: [ children: [
this.defaultAltTextView, {
tag: 'div',
children: [this.decorativeToggle],
},
this.labeledInput, this.labeledInput,
this.saveButtonView, this.saveButtonView,
this.cancelButtonView, this.cancelButtonView,
...@@ -126,15 +118,18 @@ export default class TextAlternativeFormView extends View { ...@@ -126,15 +118,18 @@ export default class TextAlternativeFormView extends View {
submitHandler({ view: this }); submitHandler({ view: this });
[this.labeledInput, this.saveButtonView, this.cancelButtonView].forEach( [
(v) => { this.decorativeToggle,
// Register the view as focusable. this.labeledInput,
this._focusables.add(v); this.saveButtonView,
this.cancelButtonView,
// Register the view in the focus tracker. ].forEach((v) => {
this.focusTracker.add(v.element); // Register the view as focusable.
}, this._focusables.add(v);
);
// Register the view in the focus tracker.
this.focusTracker.add(v.element);
});
} }
/** /**
...@@ -185,49 +180,34 @@ export default class TextAlternativeFormView extends View { ...@@ -185,49 +180,34 @@ export default class TextAlternativeFormView extends View {
createLabeledInputText, createLabeledInputText,
); );
labeledInput
.bind('class')
.to(this.decorativeToggle, 'isOn', (value) => (value ? 'ck-hidden' : ''));
labeledInput.label = Drupal.t('Alternative text override'); labeledInput.label = Drupal.t('Alternative text override');
return labeledInput; return labeledInput;
} }
/** /**
* Creates a default alt text view. * Creates a decorative image toggle view.
*
* @return {module:ui/button/switchbuttonview~SwitchButtonView}
* Decorative image toggle view instance.
* *
* @return {module:ui/template~Template}
* A template for default alt text view.
* @private * @private
*/ */
_createDefaultAltTextView() { _decorativeToggleView() {
const bind = Template.bind(this, this); const decorativeToggle = new SwitchButtonView(this.locale);
return new Template({ decorativeToggle.set({
tag: 'div', withText: true,
attributes: { label: Drupal.t('Decorative image'),
class: [ });
'ck-media-alternative-text-form__default-alt-text', decorativeToggle.on('execute', () => {
bind.if('defaultAltText', 'ck-hidden', (value) => !value), if (decorativeToggle.isOn) {
], // Clear value when decorative alt is turned off.
}, this.labeledInput.fieldView.element.value = '';
children: [ }
{ decorativeToggle.set('isOn', !decorativeToggle.isOn);
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')],
},
],
},
],
}); });
return decorativeToggle;
} }
} }
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
use Drupal\filter\Entity\FilterFormat; use Drupal\filter\Entity\FilterFormat;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\media\Entity\Media; use Drupal\media\Entity\Media;
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait; use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait; use Drupal\Tests\TestFileCreationTrait;
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolation;
// cspell:ignore arrakis complote détruire harkonnen // cspell:ignore arrakis complote détruire harkonnen
...@@ -306,6 +306,17 @@ public function testAlt() { ...@@ -306,6 +306,17 @@ public function testAlt() {
$xpath = new \DOMXPath($this->getEditorDataAsDom()); $xpath = new \DOMXPath($this->getEditorDataAsDom());
$drupal_media = $xpath->query('//drupal-media')[0]; $drupal_media = $xpath->query('//drupal-media')[0];
$this->assertEquals($test_alt, $drupal_media->getAttribute('alt')); $this->assertEquals($test_alt, $drupal_media->getAttribute('alt'));
// Test that the media item can be designated 'decorative'.
// Click the "Override media image text alternative" button.
$this->getBalloonButton('Override media image alternative text')->click();
$page->pressButton('Decorative image');
$this->getBalloonButton('Save')->click();
$xpath = new \DOMXPath($this->getEditorDataAsDom());
$drupal_media = $xpath->query('//drupal-media')[0];
// The alt text in CKEditor displays alt="""", indicating
// decorative image (https://www.w3.org/WAI/tutorials/images/decorative/).
$this->assertEquals('""', $drupal_media->getAttribute('alt'));
} }
} }
...@@ -383,7 +383,7 @@ public function testAlt() { ...@@ -383,7 +383,7 @@ public function testAlt() {
$this->getBalloonButton('Override media image alternative text')->click(); $this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form'); $this->assertVisibleBalloon('.ck-media-alternative-text-form');
// Assert that the default alt text is visible in the UI. // 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_session->elementTextEquals('css', '.ck-media-alternative-text-form .ck-labeled-field-view__status', 'Leave blank to use the default alternative text: "default alt".');
// Assert that the value is currently empty. // Assert that the value is currently empty.
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-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()); $this->assertSame('', $alt_override_input->getValue());
...@@ -413,7 +413,7 @@ public function testAlt() { ...@@ -413,7 +413,7 @@ public function testAlt() {
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]'); $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()); $this->assertSame($who_is_zartan, $alt_override_input->getValue());
// Assert that the default alt text is still visible in the UI. // 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'); $assert_session->elementTextEquals('css', '.ck-media-alternative-text-form .ck-labeled-field-view__status', 'Leave blank to use the default alternative text: "default alt".');
// Test the process again with a different alt text to make sure it works // Test the process again with a different alt text to make sure it works
// the second time around. // the second time around.
...@@ -456,6 +456,9 @@ public function testAlt() { ...@@ -456,6 +456,9 @@ public function testAlt() {
// media item. // media item.
$this->getBalloonButton('Override media image alternative text')->click(); $this->getBalloonButton('Override media image alternative text')->click();
$this->assertVisibleBalloon('.ck-media-alternative-text-form'); $this->assertVisibleBalloon('.ck-media-alternative-text-form');
// The 'decorative image' toggle is enabled because the alt was set to `""`.
// Set the toggle to "off" to override the alt text value.
$page->pressButton('Decorative image');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]'); $alt_override_input = $page->find('css', '.ck-balloon-panel .ck-media-alternative-text-form input[type=text]');
$alt_override_input->setValue(''); $alt_override_input->setValue('');
$this->getBalloonButton('Save')->click(); $this->getBalloonButton('Save')->click();
...@@ -545,7 +548,7 @@ public function testTranslationAlt() { ...@@ -545,7 +548,7 @@ public function testTranslationAlt() {
// Assert that the default alt on the UI is the default alt text from the // Assert that the default alt on the UI is the default alt text from the
// media entity. // media entity.
// cSpell:disable-next-line // cSpell:disable-next-line
$assert_session->elementTextEquals('css', '.ck-media-alternative-text-form__default-alt-text-value', 'texte alternatif par défaut'); $assert_session->elementTextEquals('css', '.ck-media-alternative-text-form .ck-labeled-field-view__status', 'Leave blank to use the default alternative text: "texte alternatif par défaut".');
// Fill in the alt field in the balloon form. // Fill in the alt field in the balloon form.
// cSpell:disable-next-line // cSpell:disable-next-line
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment