Problem/Motivation

Followup of #3023802: Show media add form directly on media type tab in media library

Lauri found a few major UX problems with that patch (#3023802-21: Show media add form directly on media type tab in media library):

  1. Media entities selected prior to uploading new media file are lost. I would have expected those to remain attached.
  2. I found the second step of the upload confusing; I wasn't sure if the file was already saved or not. This was relevant because I accidentally first uploaded a wrong file. I'm wondering also if the text on the submit button should be changed from "Save" to something like "Save and attach" to be more clear of the action.
  3. There's no way to cancel upload without losing selected files in the media listing. This is again relevant because I uploaded a wrong file, and the only way to restart the process was to close the dialog.

Proposed resolution

  • Point 1 and 2 should be addressed in this issue by adding a second button the metadata screen, which will give the user two options:
    "Save and select" and "Save and insert"
  • We will add an area to the metadata screen which will display small thumbnails of all the items you have already selected from the library. This is important from both an accessibility and usability perspective. @ckrina has promised to create a design for this area.
  • Point 3 will be addressed by #3023801: Allow newly uploaded files to be deleted from the media library without saving them

Remaining tasks

User interface changes

Video of the changes in #15: https://www.drupal.org/files/issues/2019-02-18/media-library-save-insert...

Current screenshots as of #77:

Button changed to 'Insert selected' in media library, instead of 'Select media'
Button changed to 'Insert selected' in media library, instead of 'Select media'

Renamed 'Save' button in second step to 'Save and select' (returning to the media library) and added a 'Save and insert' button to directly insert the items in the widget. Added the button_type 'primary' for the primary user flow, to make it easier for new users to follow the recommended path.

Just adding new files:

Only local images are allowed.

Adding new files, along with some already existing ones selected (details collapsed by default):

Only local images are allowed.

And expanded:

Same, but with details element expanded, which includes two images with checkboxes, same as the main media library.

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#83 3023797-83.patch65.27 KBseanB
#83 interdiff-80-83.txt1.2 KBseanB
#81 3023797-80.patch65.26 KBseanB
#81 interdiff-76-80.txt1.81 KBseanB
#77 MediaLibrary-NewPlusExistingFilesExpanded.png509.7 KBwebchick
#77 MediaLibrary-NewPlusExistingFilesCollapsed.png278.62 KBwebchick
#77 MediaLibrary-JustNewFiles.png248.42 KBwebchick
#76 3023797-76.patch65.18 KBseanB
#76 interdiff-74-76.txt5.9 KBseanB
#74 3023797-74.patch64.37 KBseanB
#74 interdiff-72-74.txt1.02 KBseanB
#72 3023797-72.patch64.11 KBseanB
#72 interdiff-70-72.txt6.61 KBseanB
#70 3023797-70.patch61.59 KBseanB
#70 interdiff-62-70.txt2.53 KBseanB
#63 3023797-62.patch61.29 KBseanB
#63 interdiff-60-62.txt11.5 KBseanB
#60 3023797-60.patch60.72 KBseanB
#60 interdiff-54-60.txt6.7 KBseanB
#55 3023797-55.patch62.47 KBseanB
#55 interdiff-54-55.txt7.23 KBseanB
#54 3023797-54.patch59.24 KBseanB
#54 interdiff-52-54.txt8.82 KBseanB
#52 3023797-52.patch59.14 KBseanB
#52 interdiff-50-52.txt2.19 KBseanB
#50 3023797-50.patch59.24 KBseanB
#50 interdiff-47-50.txt5.87 KBseanB
#47 3023797-47.patch57.41 KBseanB
#47 interdiff-43-47.txt1.01 KBseanB
#45 3023797-43-reroll.patch55.65 KBseanB
#43 3023797-43.patch55.54 KBseanB
#43 interdiff-40-43.txt9.91 KBseanB
#40 3023797-40.patch46.57 KBseanB
#40 interdiff-37-40.txt4.38 KBseanB
#37 3023797-37.patch46.57 KBseanB
#37 interdiff-35-37.txt4.46 KBseanB
#35 3023797-35.patch44.64 KBseanB
#35 interdiff-33-35.txt2.84 KBseanB
#33 3023797-33.patch44.32 KBseanB
#33 interdiff-31-33.txt4.75 KBseanB
#31 save-and-insert.png632.56 KBseanB
#31 3023797-31.patch43.83 KBseanB
#31 interdiff-24-31.txt8.9 KBseanB
#30 Screen Shot 2019-02-22 at 22.05.28.png29.98 KBlauriii
#24 3023797-24.patch42.58 KBseanB
#24 interdiff-22-24.txt4.84 KBseanB
#22 3023797-22.patch41.86 KBseanB
#22 interdiff-17-22.txt11.54 KBseanB
#17 save-and-insert.png620.76 KBseanB
#17 3023797-17.patch40.2 KBseanB
#17 interdiff-15-17.txt723 bytesseanB
#16 media-small-grid.png147.29 KBckrina
#15 save-and-insert.png851.32 KBseanB
#15 insert-selected.png808.94 KBseanB
#15 media-library-save-insert-persistant-selection.mp41.95 MBseanB
#15 3023797-15.patch39.98 KBseanB
#15 interdiff-13-15.txt31.08 KBseanB
#13 media-library-select-insert.mp4907.89 KBseanB
#13 save-and-insert.png629 KBseanB
#13 insert-selected.png769.26 KBseanB
#13 3023797-13-3023802-55.patch90.35 KBseanB
#13 3023797-13-do-not-test.patch11.73 KBseanB
#9 3023797-9-3023802-12-3020716-64.patch210.62 KBseanB
#9 3023797-9-do-not-test.patch31.42 KBseanB
#9 interdiff-5-9.txt8.61 KBseanB
#5 3023797-5-3023802-10-3020716-64.patch209.86 KBseanB
#5 3023797-5-do-not-test.patch24.04 KBseanB
#2 3023797-2.patch16.05 KBseanB
#2 3023797-2-3023802-4-3020716-14-3020707-4-combined.patch335.83 KBseanB
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

seanB created an issue. See original summary.

seanB’s picture

Attached is a full patch built on top of #3023802-4: Show media add form directly on media type tab in media library (which is built on #3020707: Streamline buttons in the media library widget and #3020716: Add vertical tabs style menu to media library).
Also a patch with just the changes for this issue.

Obviously, this is blocked on the other issues for now.

Status: Needs review » Needs work

The last submitted patch, 2: 3023797-2.patch, failed testing. View results

Berdir’s picture

making it configurable seems like a good idea.

> You need extra clicks to upload new and reuse existing items at the same time

Seems like a < 10% (or less ;) use case to need a mix of existing and new media on the same field at the same time?

seanB’s picture

A bunch of things have been changed in the issues we are building on top of. Rerolled based on #3020716-64: Add vertical tabs style menu to media library and #3023802-10: Show media add form directly on media type tab in media library.
Too much changed for an interdiff and no reviews yet anyway.

Status: Needs review » Needs work

The last submitted patch, 5: 3023797-5-3023802-10-3020716-64.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dww’s picture

Adding #3009114: [Regression] Editing a media entity now always redirects you to a media overview list as related. I'm maintaining a site where the editors use the canonical view of media entities, and I've had to work-around the change that always forces them back to the media library after every edit. Please continue to consider this use case as you make changes / assumptions about how all this should work.

I know *this* issue is about "add" not "edit", and I'm okay with add forcing you back to the library (in their world, they add via an entity browser and IEF, so they never would hit this), but still...

Thanks,
-Derek

seanB’s picture

Thanks @dww.

This is indeed specifically about adding media, and also more specifically about the media library widget for entity reference fields. We have discussed this while designing the new media library and we all came to the conclusion that untrained/inexperienced users benefit from actually seeing the created media items being added to the library.

That being said, trained site editors that spend a lot of time adding new media items would benefit from an optimised workflow, so I really think it is important that site builders can change this if needed.

seanB’s picture

Status: Needs work » Needs review
FileSize
8.61 KB
31.42 KB
210.62 KB

This should fix the tests.

seanB’s picture

Copying this over from #3023802-21: Show media add form directly on media type tab in media library

Media entities selected prior to uploading new media file are lost. I would have expected those to remain attached.

The patch in this issue should make sure that selected items stay selected when adding new media.

phenaproxima’s picture

At @lauriii's behest, I need to cross-update this issue with some feedback discovered in #3023802-21: Show media add form directly on media type tab in media library, which was deferred to this issue to be actually solved.

Lauri found a few major UX problems with that patch:

  1. Media entities selected prior to uploading new media file are lost. I would have expected those to remain attached.
  2. I found the second step of the upload confusing; I wasn't sure if the file was already saved or not. This was relevant because I accidentally first uploaded a wrong file. I'm wondering also if the text on the submit button should be changed from "Save" to something like "Save and attach" to be more clear of the action.
  3. There's no way to cancel upload without losing selected files in the media listing. This is again relevant because I uploaded a wrong file, and the only way to restart the process was to close the dialog.

@seanB replied in #3023802-22: Show media add form directly on media type tab in media library to say that all of those issues will (probably) be addressed by this issue. But I raise it here so that, whatever solution we come up with here, we remember to test all of these cases.

phenaproxima’s picture

Status: Needs review » Needs work
Issue tags: +Needs design

As per #3023802-28: Show media add form directly on media type tab in media library, this is now a follow-up for that issue based on feedback from the UX team.

Once it's in, this issue will:

add a second button the metadata screen, which will give the user two options (actual wording may vary): "Save and return to library", and "Save and insert selected items". ... As part of that work, we will add an area to the metadata screen which will display small thumbnails of all the items you have already selected from the library. This is important from both an accessibility and usability perspective. @ckrina has promised to create a design for this area.

Kicking this back for designs.

seanB’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
11.73 KB
90.35 KB
769.26 KB
629 KB
907.89 KB

Updated the IS and added a video an screenshots of the progress. Here is a first patch to implement to updated buttons. Will work on remembering the selection next.

seanB’s picture

Issue summary: View changes
seanB’s picture

#3023802: Show media add form directly on media type tab in media library landed! Yay!

Here is a new patch implementing persistent selection when uploading, and an area to show the selected items below the new ones. Also updating the screenshots and video in the IS.

ckrina’s picture

FileSize
147.29 KB

Sorry it took me longer than expected! I've been giving a thought to the current proposed solution with the selected items below in a grid and it's a really good and unobstructive way to solve it, so I don't think more design efforts to find something completely different are needed.

- But the items on the grid look huge and take a lot of space. Would it be possible to make them smaller? It's a problem for longer names, thought. But maybe the hover info and the possibility to review selection ("Save and select" button) would be enough to compensate?

- I feel the difference between the text for the 2 buttons is not big enough. I know it's too long, but I would expect to be able to deduce from the primary one that I'll "Save and select more items" or I'll "Save and go back to library" or I'll "Save and modify selection".

- Also, would it be possible/make sense to add a hover or active background when you're editing an item?

seanB’s picture

We discussed this patch in the UX call today. The general feedback was very positive. The following this were agreed upon:

  1. The selected items shown in the media add form should be smaller.
  2. The terminology of the buttons could be improved, but this could be done in a follow up.
  3. It is not yet clear what to do with the uploaded items for the 'X of Y items selected' text. Should they be included or not? What should the actual wording be for this? This could be discussed and improved in a follow up.
  4. The media library could use a cancel button. Should we add this for every step or only when new items are added? This could also be discussed and improved in a follow up.

Attached is a patch to make the selected items smaller for the selection shown in the media add form. Updated the screenshot in the IS.

Status: Needs review » Needs work

The last submitted patch, 17: 3023797-17.patch, failed testing. View results

seanB’s picture

Status: Needs work » Needs review

That was a testbot issue, patch is green. Back to needs review.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/css/media_library.module.css
    @@ -81,7 +82,8 @@
    -.media-library-view.view-display-id-widget .media-library-item__name a {
    +.media-library-view.view-display-id-widget .media-library-item__name a,
    +.media-library-add-form-selection .media-library-item__name a {
    

    I wonder why we wouldn't just reduce this selector to .media-library-item__name a?

  2. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -96,6 +96,20 @@
    +.media-library-add-form-selection .media-library-item {
    +  width: 120px;
    +}
    +
    +.media-library-add-form-selection .media-library-item .field--name-thumbnail img {
    +  height: 100px;
    +}
    

    Should these maybe be max-width and max-height?

  3. +++ b/core/modules/media_library/js/media_library.click_to_select.es6.js
    @@ -33,4 +33,44 @@
    +  Drupal.behaviors.ClickToSelectHover = {
    +    attach(context) {
    +      $('.js-click-to-select-trigger, .js-click-to-select-checkbox', context)
    +        .once('media-library-item-hover')
    +        .on('mouseover mouseout', ({ currentTarget, type }) => {
    +          $(currentTarget)
    +            .closest('.media-library-item')
    +            .toggleClass('is-hover', type === 'mouseover');
    +        });
    +    },
    +  };
    +
    +  /**
    +   * Adds focus effect to media items.
    +   *
    +   * @type {Drupal~behavior}
    +   *
    +   * @prop {Drupal~behaviorAttach} attach
    +   *   Attaches behavior to add a focus effect to media items.
    +   */
    +  Drupal.behaviors.ClickToSelectFocus = {
    +    attach(context) {
    +      $('.js-click-to-select-checkbox input', context)
    +        .once('media-library-item-focus')
    +        .on('focus blur', ({ currentTarget, type }) => {
    +          $(currentTarget)
    +            .closest('.media-library-item')
    +            .toggleClass('is-focus', type === 'focus');
    +        });
    +    },
    +  };
    

    I think these should both be added to the main click-to-select behavior, if it can be done generically, rather than be implemented as additional behaviors.

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -123,6 +126,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         $form['#attributes']['class'][] = 'media-library-add-form';
    +    $form['#attributes']['class'][] = 'js-media-library-add-form';
    

    These can be combined into a single line. Also, I don't understand why we're using the same class name twice, just prefixed with 'js-' the second time. That could maybe use a comment.

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -139,8 +144,22 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +      $form['selection'] = $this->buildSelection($form, $form_state);
    

    I think this should be renamed buildCurrentSelectionArea().

  6. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,6 +266,67 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +    $media_ids = array_filter(explode(',', $form_state->getValue('current_selection')));
    

    I think we should pass the array of loaded media items into this method directly, as the first argument: buildCurrentSelectionArea($media_items, $form, $form_state).

  7. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,6 +266,67 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +      $selection[$media_id] = [
    +        '#type' => 'container',
    +        '#attributes' => [
    +          'class' => [
    +            'media-library-item',
    +            'js-media-library-item',
    +            'js-click-to-select',
    +          ],
    +        ],
    +        'select' => [
    +          '#type' => 'container',
    +          '#attributes' => [
    +            'class' => [
    +              'js-click-to-select-checkbox',
    +            ],
    +          ],
    +          'select_checkbox' => [
    +            '#type' => 'checkbox',
    +            '#title' => $this->t('Select @name', ['@name' => $media->label()]),
    +            '#title_display' => 'invisible',
    +            '#return_value' => $media_id,
    +          ],
    +        ],
    +        'rendered_entity' => $view_builder->view($media, 'media_library'),
    +      ];
    

    This should all be moved into a single protected method, for easy overriding. Something like $this->buildSelectedItemElement($media_item);

  8. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -429,6 +518,41 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +    $selected_media_ids = array_filter(explode(',', $form_state->getValue('current_selection')));
    +    $added_media = $form_state->get('media') ?: [];
    +    $added_media_ids = array_map(function (MediaInterface $media) {
    +      return $media->id();
    +    }, $added_media);
    +    $media_ids = array_merge($selected_media_ids, $added_media_ids);
    

    I think this should be moved to a new protected method. Something like $this->getAllSelectedMediaIds($form_state).

seanB’s picture

Status: Needs work » Needs review
FileSize
11.54 KB
41.86 KB

#21

1. We are stopping people from clicking the title since that would take them out of the library. We only want that for the widget and not for the general admin/content/media overview.
2. The media items are currently not scaling and are all fixed width. I personally would like to improve that, but I guess that should be a separate issue.
3. Fixed.
4. Fixed. About the comment, I believe it is a common convention to add a js- specific class when the element needs to be targeted in javascript. Do we really need to document that here? One class is for styling and the other for JS.
5. Fixed.
6. Fixed.
7. Fixed.
8. Fixed.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -139,8 +154,27 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +    // Allow the current selection to be set in a hidden field so the selection
    +    // can be passed between different states of the form.
    +    $form['current_selection'] = [
    

    Can we add a @see here referencing the relevant JavaScript?

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,6 +281,71 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +    $selection = [
    +      '#type' => 'container',
    +      '#attributes' => [
    +        'class' => [
    +          'media-library-add-form-selection',
    +        ],
    +      ],
    +    ];
    

    I wonder if this should be an open details element, so that users can collapse it if they want to. (Assuming such a thing would be kosher from an accessibility standpoint.)

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,6 +281,71 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +      $selection[$media_id] = $this->buildSelectedItemElement($media);
    

    I wonder if we should also pass $form and $form_state to this method?

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,6 +281,71 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +  protected function buildSelectedItemElement($media) {
    

    $media needs to be type hinted.

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -429,6 +537,33 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +   *   The form array when there are form errors or a AJAX response to select
    

    Should be "The form array if there are validation errors, or an AJAX response to add the created items to the current selection."

  6. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -444,4 +579,24 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +  protected function getAllSelectedMediaIds(array &$form, FormStateInterface $form_state) {
    

    $form isn't used for anything, so I think we can remove it.

  7. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -176,6 +176,7 @@ public function validateUploadElement(array $element, FormStateInterface $form_s
         $element['upload_button']['#submit'] = ['::uploadButtonSubmit'];
    +    $element['upload_button']['#limit_validation_errors'] = FALSE;
    

    This seems questionable; won't this disable validation for the upload entirely? Will extensions, sizes, etc. still be checked? If they will, we have tests to assert that, right?

seanB’s picture

Status: Needs work » Needs review
FileSize
4.84 KB
42.58 KB

#23
1. Fixed.
2. Leaving this for now. Not really sure if that would add anything, since all the forms are above this element and there is nothing below it.
3. Fixed.
4. Fixed.
5. Fixed.
6. Fixed.
7. Added a comment to explain why this is needed, and added specific field names for #limit_validation_errors. Validation still works on the upload field though, and we have tests for that in MediaLibraryTest::testWidgetUpload().

    // Assert media type four should only allow jpg files by trying a png file
    // first.
andrewmacpherson’s picture

Issue tags: +accessibility, +Needs accessibility review

point 3 in #21-22

I don't really follow what this issue is about, however if you are changing JS code which involves focus, please tag for an accessibility review.

I think this looks like re-arranging code to be neater, rather than intending to change any behaviour, is that right?

All the same, in the past we have had some very serious keyboard accessibility regressions from well-meaning refactorings, and keyboard accessibilty has been broken for many releases. Eventually I'd like to get more functional javascript tests in place for keyboard behahiours, but for the time being please request an accessibility review if messing with focus in JS.

seanB’s picture

Issue tags: -accessibility +Accessibility

I think this looks like re-arranging code to be neater, rather than intending to change any behaviour, is that right?

That is correct, we are merging 3 behaviors into 1 to optimize how often jQuery has to search the context for the same elements. No functional changes.

Eventually I'd like to get more functional javascript tests in place for keyboard behahiours, but for the time being please request an accessibility review if messing with focus in JS.

In this case we are adding a class when an element receives focus, not changing the actual focus. Not sure if that changes things?

andrewmacpherson’s picture

Applying a class when focus happens is something that could still potentially fail WCAG "focus visible" (at level AA) if it broke, even though it still passed keyboard-operable at level A. Hence worth reviewing.

phenaproxima’s picture

Tagging for front-end framework manager review due to CSS and JavaScript changes.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/js/media_library.click_to_select.es6.js
    @@ -24,12 +24,29 @@
    +        // Adds focus effect to media items.
    +        .on('focus blur', ({ currentTarget, type }) => {
    +          $(currentTarget)
    +            .closest('.media-library-item')
    +            .toggleClass('is-focus', type === 'focus');
    +        });
    

    So, question -- in this event handler, we are targeting the closest .media-library-item, but in the 'change' handler, we're targeting the closest .js-click-to-select. Surely we want to make those the same? If not, can we add a comment explaining why?

  2. +++ b/core/modules/media_library/js/media_library.click_to_select.es6.js
    @@ -24,12 +24,29 @@
    +      $('.js-click-to-select-trigger, .js-click-to-select-checkbox', context)
    +        .once('media-library-item-hover')
    +        .on('mouseover mouseout', ({ currentTarget, type }) => {
    +          $(currentTarget)
    +            .closest('.media-library-item')
    +            .toggleClass('is-hover', type === 'mouseover');
    

    Same here. The click-to-select stuff was originally meant to be generic, so why are we targeting the closest .media-library-item? Can we use a more generic selector?

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -139,8 +154,28 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +      $media_ids = array_filter(explode(',', $form_state->getValue('current_selection')));
    

    I think we should also put this into its own method, and have getAllSelectedMediaIds() call it. That way, we can confine all the somewhat fiddly $form_state stuff to individual methods.

    So my suggestion for the method names are getPreSelectedMediaItems() -- to get the stuff that was already selected before the add form was used -- and getAllCurrentMediaItems(), which returns the pre-selected ones _and_ the ones being added by the form. Both of these methods should return an array of fully loaded media entities, keyed by ID.

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,6 +282,75 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +  protected function buildCurrentSelectionArea($media_items, array $form, FormStateInterface $form_state) {
    

    If we follow my previous suggestion, we will no longer need to pass $media_items here; this method can just call $this->getPreSelectedMediaItems($form_state) and loop over them.

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -444,4 +584,22 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +  protected function getAllSelectedMediaIds(FormStateInterface $form_state) {
    

    If my renaming suggestion is followed, this will become getCurrentMediaItems(). We'll also need to change the doc comment to account for the fact that this will return the combination of the pre-selected ones and the added ones.

  6. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -176,6 +176,14 @@ public function validateUploadElement(array $element, FormStateInterface $form_s
    +    // Limit the validation errors to make sure
    +    // FormValidator::handleErrorsWithLimitedValidation doesn't remove the
    +    // current selection from the form state.
    +    // @see Drupal\Core\Form\FormValidator::handleErrorsWithLimitedValidation()
    +    $element['upload_button']['#limit_validation_errors'] = [
    +      ['upload'],
    +      ['current_selection'],
    +    ];
    

    Very nice! This is a lot clearer.

  7. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -606,36 +604,31 @@ public function testWidgetUpload() {
    +    $assert_session->elementNotExists('css', '.media-library-add-form-selection');
    

    I think we can use $assert_session->hiddenFieldNotExists() here.

lauriii’s picture

+++ b/core/modules/media_library/js/media_library.click_to_select.es6.js
@@ -24,12 +24,29 @@
+            .closest('.media-library-item')
...
+            .closest('.media-library-item')

We should use a js- prefixed class here.

Other than that, the CSS and the JS changes look fine to me so I'm removing the tag.

This is a big UX improvement and makes the current state clearer. I feel like we could be even clearer about the state on this screen when user has pre-selected media. The problem with this screen is that it combines entities that haven't been saved with entities that have been saved without a clear distinction. When I saw the UX improvements, I checked again if the functionality had been changed so that the media was already saved at that stage since it was unclear to me what the state was. Potential solutions to this could be a title that describes the purpose of that screen, or a message / description with instructions.

seanB’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
8.9 KB
43.83 KB
632.56 KB

#29
1. Fixed.
2. Fixed.
3. Fixed.
4. Fixed.
5. Fixed.
6. Yay!
7. This is not the hidden field but the div that would contain the pre-selected items after the users adds new media.

#30 Refactored it to use .js-click-to-select'

Potential solutions to this could be a title that describes the purpose of that screen, or a message / description with instructions.

Added a description above the form now. Hopefully this helps. Open to other suggestions though.

Renamed 'Save' button in second step to 'Save and select' (returning to the media library) and added a 'Save and insert' button to directly insert the items in the widget.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -146,20 +146,23 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +        '#value' => $this->formatPlural(count($added_media), 'The media item has been created. Fill in any required fields and save to make it available in the media library.', 'The media items have been added. Fill in any required fields and save to make them available in the media library.'),
    

    Why does the singular string say "created", but the plural string say "added"?

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -585,21 +593,38 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +   * @return |Drupal\media\MediaInterface[]
    +   *   An array containing the pre-selected media IDs.
    +   */
    +  protected function getPreSelectedMediaItems(FormStateInterface $form_state) {
    

    There's a rogue | here, and the comment is not accurate -- this is returning media entities keyed by ID, not IDs only.

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -585,21 +593,38 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +  protected function getCurrentMediaItems(FormStateInterface $form_state) {
    

    I think this should be returning media entities too, not just IDs.

seanB’s picture

Status: Needs work » Needs review
FileSize
4.75 KB
44.32 KB

#32 Fixed.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -149,7 +149,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +        '#value' => $this->formatPlural(count($added_media), 'The media item has been created. Fill in any required fields and save to make it available in the media library.', 'The media items have been created. Fill in any required fields and save to make them available in the media library.'),
    

    "Make them available in" is a bit unwieldy. I think we could just say "...to add it/them to the media library."

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -570,8 +570,11 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +      $current_media_ids = array_map(function (MediaInterface $media) {
    +        return $media->id();
    +      }, $this->getCurrentMediaItems($form_state));
    

    This could be a simple array_keys() call if getCurrentMediaItems() returned the media items keyed by their ID. Is that possible?

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -598,8 +601,8 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +   * @return \Drupal\media\MediaInterface[]
    +   *   An array containing the pre-selected media items.
        */
       protected function getPreSelectedMediaItems(FormStateInterface $form_state) {
    

    Let's return them keyed by their ID, and mention that in the doc comment.

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -615,16 +618,13 @@ protected function getPreSelectedMediaItems(FormStateInterface $form_state) {
    +   * @return \Drupal\media\MediaInterface[]
    +   *   An array containing all pre-selected and added media items.
        */
       protected function getCurrentMediaItems(FormStateInterface $form_state) {
    

    Same here.

seanB’s picture

Status: Needs work » Needs review
FileSize
2.84 KB
44.64 KB

#34
1. Fixed.
2. The added media items in the form state are keyed by delta and only get an actual media ID on save. So I guess we can't do that.
3. Fixed.
4. See 2. Added a comment to make this clear.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -570,6 +570,9 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +      // The added media items get an ID  when they are saved in ::submitForm().
    

    This is the nittiest nitpick of all freaking time: There is an extra space after "ID" which shouldn't be there.

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -619,7 +622,9 @@ protected function getPreSelectedMediaItems(FormStateInterface $form_state) {
    +   *   An array containing all pre-selected and added media items. The
    +   *   pre-selected media items are keyed by ID and the added items are keyed by
    +   *   delta.
        */
       protected function getCurrentMediaItems(FormStateInterface $form_state) {
    

    So, I don't think we should combine IDs and deltas. It really needs to be one or the other. The scenario I'm envisioning is this: you pre-select a media item whose ID is 1, and you create two media items, the second of which will have the delta of 1. Which will win? Something will get clobbered, or at the very least, this method becomes harder to read and understand.

    I suggest that getPreSelectedMediaItems() always key by ID, and getCurrentMediaItems() is a simple numerically indexed array -- it should not key by either ID or delta. If someone calls getCurrentMediaItems(), they can filter out the pre-selected or created ones by calling $media->isNew().

    The doc comments of each method should make clear how each method keys its return value.

seanB’s picture

Status: Needs work » Needs review
FileSize
4.46 KB
46.57 KB

Fixed #36 and the broken test.

phenaproxima’s picture

+++ b/core/modules/media_library/src/Form/AddFormBase.php
@@ -247,6 +285,80 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
+  /**
+   * Returns a render array containing the current selection.
+   *
+   * @param \Drupal\media\MediaInterface $media
+   *   The media item.
+   * @param array $form
+   *   The complete form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current form state.
+   *
+   * @return array
+   *   A render array of a selected media item.
+   */
+  protected function buildSelectedItemElement(MediaInterface $media, array $form, FormStateInterface $form_state) {

The doc comment is not accurate. It should be "Returns a render array for a single pre-selected media item."

That's all I found. I think, pending accessibility sign-off, this is ready.

Status: Needs review » Needs work

The last submitted patch, 37: 3023797-37.patch, failed testing. View results

seanB’s picture

Status: Needs work » Needs review
FileSize
4.38 KB
46.57 KB

Fixed #38. Replaced deprecated function calls in the test.

Status: Needs review » Needs work

The last submitted patch, 40: 3023797-40.patch, failed testing. View results

phenaproxima’s picture

Issue tags: +Needs reroll
seanB’s picture

Rerolled and added extra tests for the oEmbed form.

seanB’s picture

Status: Needs work » Needs review
seanB’s picture

phenaproxima’s picture

Read the interdiff. Extra test coverage is always good!

From my perspective, this is RTBC once @andrewmacpherson signs off.

seanB’s picture

Rerolled now #2981044: Unify the grid/table views of the media library landed. While doing the reroll I looked at the a11y implications of this patch. We are basically touching the buttons of the modal and adding the selected items as a grid below the add form.

The buttons currently still have know a11y issues, for which we have separate issues:
#3016807: Improve refocus on submit buttons of Media Library Widget modals
#3035446: Inform assistive tech users about the outcome of using the MediaLibraryWidget dialog

The questions is, do we start merging the work from the other patches in this patch, or can we work on that separately?

rainbreaw’s picture

seanB and phenaproxima demo'd this with me for accessibility review today, as well. What is working well:

  • The DOM is well ordered and the screen reader will announce the move from element to element.
  • The keyboard navigates between all items in a logical flow, including properly understanding the use of the space bar to select or deselect the checkboxes on items in the previously selected section

One suggestion that I made:

Include aria-labels on the wrappers that will receive focus so that the user understands the element they are on. This means that the area where media is being added would need an aria-label presenting the section as "Add new media" or "Media to add," while the div containing the pre-selected items would need an aria-label presenting the section as "Preselected Media."

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

seanB’s picture

Thanks @rainbreaw for the review! I have added the aria labels and we now shift the focus to the wrapper of the added items.
Removing the tag 'needs accessibility review'.

phenaproxima’s picture

+++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
@@ -670,7 +670,9 @@ public function testWidgetUpload() {
+    $this->assertSame('Added media items', $assert_session->elementExists('css', '.media-library-add-form-media')->getAttribute('aria-label'));

This seems like a good place for $assert_session->elementAttributeContains().

Otherwise, I think this looks pretty damn good. RTBC once that's done.

seanB’s picture

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +backport

Thanks!

I'm also tagging this for backport to 8.7.x, since Media Library will hugely benefit from this UX improvement and, as an experimental module, this patch may be eligible to land in alpha.

seanB’s picture

Based on the feedback in #3023801-58: Allow newly uploaded files to be deleted from the media library without saving them I noticed we were not using the BEM convention correctly here. Updated the classnames for the new elements.

seanB’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
7.23 KB
62.47 KB

Reroll now #3035316: ‘Add media’ in MediaLibraryWidget should be a button, not a link has landed. Found an AJAX issue because of the tests we have added here, so I guess this needs another review.

Status: Needs review » Needs work

The last submitted patch, 55: 3023797-55.patch, failed testing. View results

seanB’s picture

The cause of the AJAX issue was in #3035316: ‘Add media’ in MediaLibraryWidget should be a button, not a link which has been reverted. So forget #55. Once #3035316: ‘Add media’ in MediaLibraryWidget should be a button, not a link is fixed, reroll again from #54.

Gábor Hojtsy’s picture

Let's retitle to reflect what actually happened here and any updates to the issue summary needed, so it makes sense in a commit log, etc :)

phenaproxima’s picture

Title: Determine what happens after you've added things to the media library » Let users choose what to do after selecting and/or adding items in the media library

How's this for a re-title?

seanB’s picture

Rerolled now #3035316: ‘Add media’ in MediaLibraryWidget should be a button, not a link and #3037767: Improve responsive styling of grid items in the media library landed.

Changed the selected items to also be responsive. Instead of 4 items for bigger screens, 3 for medium and 2 for smaller screens, the selection area uses 6, 4 and 3 items (which make them smaller than the regular grid).

phenaproxima’s picture

Found nitpicks, but overall this looks pretty good. I think we need a demo video, though -- or at least screenshots -- demonstrating the functionality at different screen sizes.

  1. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -321,7 +360,7 @@
    -.media-library-item--grid.checked {
    +.media-library-item--grid.checked:before {
       border-color: #0076c0;
     }
    

    Why did this change?

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -139,14 +154,47 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +    // Allow the current selection to be set in a hidden field so the selection
    +    // can be passed between different states of the form.
    +    // @see Drupal.behaviors.MediaLibraryItemSelection
    +    $form['current_selection'] = [
    +      '#type' => 'hidden',
    +      '#default_value' => '',
    +      '#attributes' => [
    +        'class' => [
    +          'js-media-library-add-form-current-selection',
    +        ],
    +      ],
    +    ];
    

    The comment should explain why no default value is set here, and mention how/where the value is updated.

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -253,6 +301,82 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +      'select' => [
    +        '#type' => 'container',
    +        '#attributes' => [
    +          'class' => [
    +            'js-click-to-select-checkbox',
    +          ],
    +        ],
    +        'select_checkbox' => [
    +          '#type' => 'checkbox',
    +          '#title' => $this->t('Select @name', ['@name' => $media->label()]),
    +          '#title_display' => 'invisible',
    +          '#return_value' => $media->id(),
    +        ],
    +      ],
    

    Not a big deal, but I think this could potentially be refactored to use #theme_wrappers to create the container around it. It might make this code a bit more succinct.

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -267,9 +391,18 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +      'save_submit' => [
    +        '#type' => 'submit',
    +        '#button_type' => 'primary',
    +        '#value' => $this->t('Save and select'),
    +        '#ajax' => [
    +          'callback' => '::updateLibrary',
    +          'wrapper' => 'media-library-add-form-wrapper',
    +        ],
    +      ],
    +      'insert_submit' => [
             '#type' => 'submit',
    

    I don't know if we need the '_submit' suffix for these buttons, or the word 'save' (both operations save the media items, after all). 'select' and 'insert' might be clearer.

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -344,25 +477,31 @@ protected function prepareMediaEntityForSave(MediaInterface $media) {
    +    $response->addCommand(new InvokeCommand('#media-library-add-form-wrapper .media-library-add-form__added-media', 'focus'));
    

    Not sure we need the '#media-library-add-form-wrapper' part of this selector.

  6. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -472,4 +642,51 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +    $pre_selected_media = $this->getPreSelectedMediaItems($form_state);
    +    $added_media = $form_state->get('media') ?: [];
    +    return array_merge($pre_selected_media, $added_media);
    

    $pre_selected_media_items are keyed by ID, so we probably need to do array_merge(array_values($pre_selected_media), $added_media). Additionally, a comment mentioning why we're doing that might be useful.

  7. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -676,38 +676,38 @@ public function testWidgetUpload() {
    +    $this->assertJsCondition('jQuery("#media-library-add-form-wrapper .media-library-add-form__added-media").is(":focus")');
    

    I don't think we need #media-library-add-form-wrapper in this selector.

  8. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -759,69 +782,101 @@ public function testWidgetUpload() {
    +    $media_storage = $this->container->get('entity_type.manager')->getStorage('media');
    +    $media_items = $media_storage->loadMultiple();
    

    Nit: Let's just use Media::loadMultiple() here.

  9. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -906,21 +963,101 @@ public function testWidgetOEmbed() {
    +    $media_storage = $this->container->get('entity_type.manager')->getStorage('media');
    +    $media_items = $media_storage->loadMultiple();
    

    Same here.

phenaproxima’s picture

Status: Needs review » Needs work
Issue tags: +Needs usability review, +Needs accessibility review

@seanB and I demoed this patch to the UX team again today. @jhodgdon was there and provided some very interesting feedback, from which we have some good news and some bad news.

The good news is that this patch is not far from done. The bad news is that the UI is still somewhat confusing -- the separation of newly added media items vs. preselected items isn't very clear. So we need to improve that. We decided to do two things:

  1. We need to slightly improve the text above the new media items so that it's clear that they haven't been saved yet, and will be as soon as the required fields are filled in.
  2. We need to make it more obvious that the pre-selected items are a secondary concern. To do this, we will wrap them in a collapsed details element with a heading like "Additionally selected items".

These changes are going to necessitate another round of UX and accessibility review, but once we have that, I think we can get this in. So let's do that.

seanB’s picture

Status: Needs work » Needs review
FileSize
11.5 KB
61.29 KB

Fixed the things mentioned in #62 discussed in the UX meeting.

In the meeting there were 2 additional comments, for which we already have separate issues:

  1. Being able to deselect the previously selected items could be useful, but it seemed to be confusing you can't deselect added items. We have #3023801: Allow newly uploaded files to be deleted from the media library without saving them to remove added items to solve that. Hiding the selected items and their checkboxes in a details element also improves this.
  2. The text X of Y items selected does not take the added items in to account. We have #3034243: Improve selection text in Media Library to address that specific issue.

Also fixed #61.

1. For some reason the checked state was broken. We could have a separate issue for this, but not sure if that is worth it.
2. Fixed.
3. Left this for now.
4. Fixed. Changed to 'save_select' / 'save_insert'.
5. Fixed.
6. The array_merge already renumbers numeric keys, so I don't think we need to do that? Added a comment though.
7. Fixed.
8. Fixed.
9. Fixed.

lauriii’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/css/media_library.module.css
    @@ -81,7 +82,8 @@
    +.media-library-view--widget .media-library-item__name a,
    +.media-library-add-form__selected-media .media-library-item__name a {
       pointer-events: none;
     }
    

    Using pointer-events: none; isn't a sufficient way to disable links since it only works for pointer devices. The link is still considered as a link for other input methods, such as screen readers.

  2. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -95,6 +95,10 @@
    +.media-library-add-form__added-media {
    +  outline: none;
    +}
    
    +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -139,14 +154,48 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +          'tabindex' => -1,
    

    What do we need this for? Especially given that we later remove the outline.

seanB’s picture

Status: Needs work » Needs review

1. Created #3039829: Remove link to media item from media library view.. This was already mentioned in #2988431: [Meta] Accessibility plan for Media Library Widget and has been classified as a should have. So I don't think this is a blocker at the moment and we should probably not make the current patch more complicated to fix this existing issue.
2. We need the tabindex so we are able to set focus to the div. We remove the outline because from what I understood, an outline on the div is not expected. We have that same pattern right now for the .media-library-content div when clicking a vertical tab.

lauriii’s picture

Status: Needs review » Needs work

#65.1 +1 for not fixing it here!
#65.2 Thanks for the explanation. I can see why it's needed now. Could we add documentation that explains this in the CSS and in the render array? This would make it easier for anyone doing maintenance to understand why this is needed.

+++ b/core/modules/media_library/css/media_library.theme.css
@@ -129,6 +137,20 @@
+.media-library-add-form__selected-media .media-library-item .field--name-thumbnail img {

@@ -263,16 +285,26 @@
+.media-library-add-form__selected-media .media-library-item--grid {
...
+  .media-library-add-form__selected-media .media-library-item--grid {

@@ -280,23 +312,33 @@
+  .media-library-add-form__selected-media .media-library-item--grid {
...
+  .media-library-add-form__selected-media .media-library-item--grid {

Could we add another variation to the .media-library-item element? Something like .media-library-item--small?

seanB’s picture

seanB’s picture

rainbreaw’s picture

@seanB and @phenaproxima demo'd this with me this morning:

Making the label of the selected media section visible to all and collapsing it is a great solution! This looks good from UX standpoint. We verified the following a11y considerations:

  1. The label (which is a button) is read by the screen reader
  2. The button is set to aria-expanded="false" when the section is collapsed
  3. The button resets to aria-expanded="true" when the section is expanded
  4. The tab functionality progresses in the expected order, from the add new media form, to the selected media section, to the items in the selected media, where they can be toggled with the expected keyboard commands and are read as expected

I raised the same concern that has apparently been raised a few times about the selection text (x of y selected) from issue #3034243: Improve selection text in Media Library and will add a separate comment on that related issue.

seanB’s picture

Fixed #66. Added the class .media-library-item--small but we still need to make sure it's specific enough.

Thanks @rainbreaw for helping us! I think we can remove the 'needs accessibility review' tag per #69.

seanB’s picture

Status: Needs work » Needs review
seanB’s picture

Rerolled now #3016807: Improve refocus on submit buttons of Media Library Widget modals has landed. I found an issue while testing. The pre-selected items were not shown after removing an item. Added a fix and a test for that now we can test multiple file uploads (YAY!).

phenaproxima’s picture

+++ b/core/modules/media_library/src/Form/AddFormBase.php
@@ -422,6 +422,9 @@ protected function buildSelectedItemElement(MediaInterface $media, array $form,
+          // Force disable the checkbox. It should only be enabled via
+          // JavaScript.
+          '#value' => FALSE,

After a quick chat with Sean, I understand what we're doing here. The actual selected media IDs are compiled by JavaScript into a hidden field, and that is submitted to the server; ever thus has it been since we rebuilt the media library UI in 8.7.x. From a form processing standpoint, these checkboxes are purely decorative (although they serve an important purpose for usability and accessibility). So setting #value => FALSE is okay here, but let's have a comment explaining why we do it.

seanB’s picture

Updated the comment.

phenaproxima’s picture

+++ b/core/modules/media_library/src/Form/AddFormBase.php
@@ -422,8 +422,11 @@ protected function buildSelectedItemElement(MediaInterface $media, array $form,
+          // The checkbox’s value is never processed by this form. It is present
+          // for usability and accessibility reasons, and only used by
+          // JavaScript to track whether or not this media item is selected. The
+          // hidden ‘current_selection’ field is used to store the actual IDs of
+          // selected media items.

Nit: These are "smart" apostrophes. I wonder if they should be ASCII?

seanB’s picture

webchick’s picture

@SeanB walked me through this patch today, we tested all of the interactions (WebchickTestCase FTW ;)):

- Inserting Media directly from the library
- Selecting Media and adding new at the same time.
- Only selecting new files
- Inserting new media into the library
- Inserting new media directly into the post
- Canceling out and inserting nothing
- Deleting new media before it's uploaded
- Unselecting existing media to remove it during the interstitial

Here are some updated screenshots (also updated in issue summary) of how it's working now.

Just adding new files:

Only local images are allowed.

Adding new files, along with some already existing ones selected (details collapsed by default):

Only local images are allowed.

And expanded:

Same, but with details element expanded, which includes two images with checkboxes, same as the main media library.

I can confirm addresses all of the (great) feedback in UX call last week. Therefore, removing the "Needs usability review" tag.

Way to knock it out of the park!! :D SO excited for this patch to land!!

webchick’s picture

Oh, one thing we could use a "normal"-level follow-up for (if one doesn't already exist), and do some "real world" asking of users at DrupalCon...

If you select, say, 3 media, then go back and select, say, 2 media... the media library will only show you the two you just selected, not the 5 you've selected altogether. My spidey sense says that might be confusing for people, and could also cause them to accidentally select the same image twice, since they can't see the already-inserted images when the dialog is up in their face. (In fairness, @SeanB did point out that there could be a use case for inserting the same media item twice, such as filling up a carousel, or promoting a particular ad multiple times... but it feels like it's more often you'd do this by accident, and maybe we move the ability to select an image twice to a contrib module for those who need it).

phenaproxima’s picture

Random small nits. Please send this issue directly to RTBC once these are addressed.

  1. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -504,7 +542,7 @@
     .media-library-add-form__media {
       position: relative;
       display: flex;
    -  padding: 20px 0 20px 0;
    +  padding: 1em 0 1em 0;
    

    Nit: We can use the shorthand 1em 0 here.

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -309,6 +350,90 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +          // The checkbox’s value is never processed by this form. It is present
    

    "checkbox's" is still using a smart apostrophe.

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -572,6 +707,39 @@ protected function buildMediaLibraryUi(FormStateInterface $form_state) {
    +        ->addCommand(new InvokeCommand("[data-media-library-widget-value=\"$field_id\"]", 'val', [implode(',', $current_media_ids)]))
    +        ->addCommand(new InvokeCommand("[data-media-library-widget-update=\"$field_id\"]", 'trigger', ['mousedown']))
    

    I kinda wish that these things were their own AJAX command. However, we can leave that for another time.

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -607,4 +775,52 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +    $media_ids = $form_state->get('current_selection');
    

    Can we add a @see, referencing where in this class the current_selection value is added to $form_state?

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -607,4 +775,52 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
    +    $pre_selected_media = $this->getPreSelectedMediaItems($form_state);
    +    $added_media = $form_state->get('media') ?: [];
    +    // Using array_merge will renumber the numeric keys.
    +    return array_merge($pre_selected_media, $added_media);
    

    This function could be reduced to one line: return array_merge($this->getPreSelectedMediaItems($form_state), $this->getAddedMediaItems($form_state));. Obviously we'd want to keep the comment; :)

phenaproxima’s picture

Opened #3041147: Preserve the selected items across multiple uses of the media library to discuss @webchick's spidey-sense from #78.

seanB’s picture

#79.
1. Fixed.
2. Fixed.
3. I personally want to refactor this to use events in the future, but that's a discussion for another time :)
4. Fixed.
5. I think the way it is right now is a lot easier to read and understand. Can we keep it?

phenaproxima’s picture

+++ b/core/modules/media_library/src/Form/AddFormBase.php
@@ -785,6 +785,8 @@ protected function getSourceFieldName(MediaTypeInterface $media_type) {
+    // Get the current select from the form state.

This needs to be "Get the current selection..."

I think the way it is right now is a lot easier to read and understand. Can we keep it?

We're duplicating the exact logic of getAddedMediaItems() -- we shouldn't do that. We can keep it on three lines, but let's at least call getAddedMediaItems().

seanB’s picture

Sorry, you are totally right :). Fixed #82.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Thanks. This is beautiful. Completely RTBC once green.

  • webchick committed c91f236 on 8.8.x
    Issue #3023797 by seanB, webchick, lauriii, ckrina, phenaproxima,...

  • webchick committed 780d02b on 8.7.x
    Issue #3023797 by seanB, webchick, lauriii, ckrina, phenaproxima,...

webchick credited jhodgdon.

webchick’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs usability review

Ok, @seanB and @phenaproxima walked me through the patch today. Front-end framework manager review tag was already removed and @lauriii's feedback looks to be addressed, mostly skimmed over the CSS/JS parts. (A lot of the JS changes are related to consolidating logic since the display logic is now called in two places.)

+    // Allow the current selection to be set in a hidden field so the selection
+    // can be passed between different states of the form. This filled is filled
+    // via JavaScript so the default value should be empty.

(nitpick) "filled is filled" ;) Should be "field is filled" ... fixed on commit.

+    // Allow the current selection to be set in a hidden field so the selection
+    // can be passed between different states of the form. This filled is filled
+    // via JavaScript so the default value should be empty.
+    // @see Drupal.behaviors.MediaLibraryItemSelection
+    $form['current_selection'] = [
+      '#type' => 'hidden',
+      '#default_value' => '',
+      '#attributes' => [
+        'class' => [
+          'js-media-library-add-form-current-selection',
+        ],
+      ],
+    ];

We spent some time on this, since normally these types of internal properties would be passed around via #type => value, but in this case we need them exposed in the source so the JS can manipulate them. There should be no security issue here (e.g. concerns with value tampering), because basically access to media items is currently "all or nothing" ... if you can see the media library at all, you can see everything in it. However, something to add explicit test coverage for once we get to more access control stuff.

+        'select_checkbox' => [
+          '#type' => 'checkbox',
+          '#title' => $this->t('Select @name', ['@name' => $media->label()]),
+          '#title_display' => 'invisible',
+          '#return_value' => $media->id(),
+          // The checkbox's value is never processed by this form. It is present
+          // for usability and accessibility reasons, and only used by
+          // JavaScript to track whether or not this media item is selected. The
+          // hidden 'current_selection' field is used to store the actual IDs of
+          // selected media items.
+          '#value' => FALSE,
+        ],
+      ],

The extra comments here were helpful, because at a glance it seems like this would be an error.

   public function processUploadElement(array $element, FormStateInterface $form_state) {
     $element['upload_button']['#submit'] = ['::uploadButtonSubmit'];
+    // Limit the validation errors to make sure
+    // FormValidator::handleErrorsWithLimitedValidation doesn't remove the
+    // current selection from the form state.
+    // @see Drupal\Core\Form\FormValidator::handleErrorsWithLimitedValidation()
+    $element['upload_button']['#limit_validation_errors'] = [
+      ['upload'],
+      ['current_selection'],
+    ];

Also spent a bit of time here, since #limit_validation_errors is normally one of those "eeek" properties. In this case we are oddly using it to include more data than would otherwise happen by default, in order to preserve the keys. Unconventional, but it works, and it's commented as such.

KUDOS for all of the great test coverage!! Basically everything enumerated in #77 is covered, in addition to some other things (like multiple file uploads, testing the oEmbed flow as well, etc.)

Not the fault of this patch, but the "type Three / type Five" stuff in tests would be vastly helped by using actual names of things (such as the default media types in Standard profile), vs. placeholder names like this. :P Follow-up material, however.

Overall, all of my potential concerns were addressed, the flow works, the a11y team and frontend framework managers signed off, and it's the last big UX patch the Media team was targeting for 8.7... LET'S DO IT!

Adding credit for people from recent UX meetings who have helped review this functionality. And actually removing the tag I meant to back in #77. :P

Committed and pushed to 8.8! WOOHOO!! And, because this code is against an experimental module, also backported to 8.7.x.

GREAT work, everyone!!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.