Verified Commit 8e56f384 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"
parent a220b008
Loading
Loading
Loading
Loading
Loading
+4 −9
Original line number Diff line number Diff line
@@ -83,15 +83,18 @@
  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 {
.ck.ck-media-alternative-text-form .ck-button-save,
.ck.ck-media-alternative-text-form .ck-button-cancel {
  width: 50%;
  margin: var(--ck-spacing-large) 0 0 0;
  padding: var(--ck-spacing-standard);
@@ -99,11 +102,3 @@
  border-top: 1px solid var(--ck-color-base-border);
  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;
}
+1 −1

File changed.

Preview size limit exceeded, changes collapsed.

+19 −3
Original line number Diff line number Diff line
@@ -105,7 +105,13 @@ export default class MediaImageTextAlternativeUi extends Plugin {

    this.listenTo(this._form, 'submit', () => {
      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);
@@ -149,6 +155,7 @@ export default class MediaImageTextAlternativeUi extends Plugin {
    }
    const editor = this.editor;
    const command = editor.commands.get('mediaImageTextAlternative');
    const decorativeToggle = this._form.decorativeToggle;
    const metadataRepository = editor.plugins.get(
      'DrupalMediaMetadataRepository',
    );
@@ -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
    // 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
@@ -183,6 +198,9 @@ export default class MediaImageTextAlternativeUi extends Plugin {
          this._form.defaultAltText = metadata.imageSourceMetadata
            ? metadata.imageSourceMetadata.alt
            : '';
          labeledInput.infoText = Drupal.t(
            `Leave blank to use the default alternative text: "${this._form.defaultAltText}".`,
          );
        })
        .catch((e) => {
          // There isn't any UI indication for errors because this should be
@@ -193,8 +211,6 @@ export default class MediaImageTextAlternativeUi extends Plugin {
        });
    }

    this._form.labeledInput.fieldView.select();

    this._form.enableCssTransitions();
  }

+43 −63
Original line number Diff line number Diff line
/* eslint-disable import/no-extraneous-dependencies */

// cspell:ignore focusables
/* cspell:ignore focusables switchbuttonview */

import {
  ButtonView,
  FocusCycler,
  LabeledFieldView,
  SwitchButtonView,
  View,
  ViewCollection,
  createLabeledInputText,
@@ -16,8 +16,6 @@ import {
import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils';
import { icons } from 'ckeditor5/src/core';

// cspell:ignore focusables

export default class TextAlternativeFormView extends View {
  /**
   * @inheritdoc
@@ -36,25 +34,16 @@ export default class TextAlternativeFormView extends View {
    this.keystrokes = new KeystrokeHandler();

    /**
     * An input with a label.
     */
    this.labeledInput = this._createLabeledInputView();

    /**
     * The default alt text.
     *
     * @observable
     * A toggle for marking the image as decorative.
     *
     * @member {string} #defaultAltText
     * @member {module:ui/button/switchbuttonview~SwitchButtonView} #decorativeToggle
     */
    this.set('defaultAltText', undefined);
    this.decorativeToggle = this._decorativeToggleView();

    /**
     * The default alt text view.
     *
     * @type {module:ui/template~Template}
     * An input with a label.
     */
    this.defaultAltTextView = this._createDefaultAltTextView();
    this.labeledInput = this._createLabeledInputView();

    /**
     * A button used to submit the form.
@@ -106,7 +95,10 @@ export default class TextAlternativeFormView extends View {
      },

      children: [
        this.defaultAltTextView,
        {
          tag: 'div',
          children: [this.decorativeToggle],
        },
        this.labeledInput,
        this.saveButtonView,
        this.cancelButtonView,
@@ -126,15 +118,18 @@ export default class TextAlternativeFormView extends View {

    submitHandler({ view: this });

    [this.labeledInput, this.saveButtonView, this.cancelButtonView].forEach(
      (v) => {
    [
      this.decorativeToggle,
      this.labeledInput,
      this.saveButtonView,
      this.cancelButtonView,
    ].forEach((v) => {
      // 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 {
      createLabeledInputText,
    );

    labeledInput
      .bind('class')
      .to(this.decorativeToggle, 'isOn', (value) => (value ? 'ck-hidden' : ''));
    labeledInput.label = Drupal.t('Alternative text override');

    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
   */
  _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')],
            },
          ],
        },
      ],
  _decorativeToggleView() {
    const decorativeToggle = new SwitchButtonView(this.locale);
    decorativeToggle.set({
      withText: true,
      label: Drupal.t('Decorative image'),
    });
    decorativeToggle.on('execute', () => {
      if (decorativeToggle.isOn) {
        // Clear value when decorative alt is turned off.
        this.labeledInput.fieldView.element.value = '';
      }
      decorativeToggle.set('isOn', !decorativeToggle.isOn);
    });
    return decorativeToggle;
  }
}
+12 −1
Original line number Diff line number Diff line
@@ -10,9 +10,9 @@
use Drupal\filter\Entity\FilterFormat;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\media\Entity\Media;
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Symfony\Component\Validator\ConstraintViolation;

// cspell:ignore arrakis complote détruire harkonnen
@@ -309,6 +309,17 @@ public function testAlt() {
    $xpath = new \DOMXPath($this->getEditorDataAsDom());
    $drupal_media = $xpath->query('//drupal-media')[0];
    $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'));
  }

}
Loading