Problem/Motivation

From #2962110-102: Add the Media Library module to Drupal core:

Grid vs. table? Why do we still have both? Seems like a stable blocker to unify this, but not a blocker for beta.

This was replied to in #2962110-115: Add the Media Library module to Drupal core:

From accessibility/UX review we found that the table is always going to be preferred for users using screen readers that can better parse table markup, and for more technical users that want to show more metadata than the Grid is able to nicely handle. We could make this configurable and only show the Grid by default on new installs in a follow up, if showing both isn't desirable.

Let's get this hashed out.

Proposed resolution

Allow users to switch between the grid and table display of the media library results.
Use the new views area plugin for display links added in #3025657: Add views area plugin to display a link to another view display within the same view.

Remaining tasks

Review patch.
Commit.

User interface changes

Screenshots and video of #66:

https://www.drupal.org/files/issues/2019-02-27/media-library-grid-table-...

Grid display
Grid display with links to switch between grid/table.

Grid display (RTL)
Grid display with links to switch between grid/table (RTL).

Table display
Table display with links to switch between grid/table.

Table display (RTL)
Table display with links to switch between grid/table (RTL).

API changes

None.

Data model changes

None.

CommentFileSizeAuthor
#82 2981044-77-reroll.patch100.73 KBseanb
#77 2981044-77.patch100.72 KBseanb
#77 interdiff-69-77.txt8.74 KBseanb
#69 2981044-69.patch100.77 KBseanb
#69 interdiff-68-69.txt404 bytesseanb
#68 2981044-68.patch100.77 KBseanb
#68 interdiff-66-68.txt8.89 KBseanb
#66 media-library-grid-table-66.mp42.22 MBseanb
#66 table-rtl.png220.61 KBseanb
#66 table.png283.46 KBseanb
#66 grid-rtl.png738.83 KBseanb
#66 grid.png771.46 KBseanb
#66 2981044-66.patch100.69 KBseanb
#66 interdiff-61-66.txt19.57 KBseanb
#63 Screen Shot 2019-02-26 at 17.21.21.png19.24 KBlauriii
#63 Screen Shot 2019-02-26 at 17.31.57.png1.86 KBlauriii
#63 Screen Shot 2019-02-26 at 17.39.20.png4.12 KBlauriii
#62 2981044-61-reroll.patch96.1 KBseanb
#61 2981044-61.patch96.36 KBseanb
#61 interdiff-58-61.txt5.93 KBseanb
#58 2981044-58.patch95.77 KBseanb
#56 2981044-56.patch415.18 KBseanb
#56 interdiff-51-56.txt695 bytesseanb
#52 2981044-51.patch95.89 KBseanb
#52 interdiff-46-51.txt12.92 KBseanb
#46 2981044-46.patch91.08 KBseanb
#46 interdiff-43-46.txt30.38 KBseanb
#45 media-library-aria-polite-on-entire-view-container.png360.97 KBandrewmacpherson
#43 2981044-43.patch83.01 KBseanb
#43 interdiff-41-43.txt1.56 KBseanb
#41 2981044-41.patch82.89 KBseanb
#41 interdiff-39-41.txt15.45 KBseanb
#39 2981044-39.patch78.3 KBseanb
#39 interdiff-37-39.txt649 bytesseanb
#37 2981044-37.patch78.28 KBseanb
#37 interdiff-35-37.txt13.9 KBseanb
#35 2981044-35.patch68.61 KBseanb
#35 interdiff-34-35.txt2.39 KBseanb
#34 grid-vs-table.mp41.4 MBseanb
#34 2981044-34.patch68.41 KBseanb
#34 interdiff-31-34.txt6.12 KBseanb
#34 table-rtl.png296.76 KBseanb
#34 table.png298.05 KBseanb
#34 grid-rtl.png819.52 KBseanb
#34 grid.png818.54 KBseanb
#31 2981044-31.patch68.09 KBseanb
#31 interdiff-29-31.txt5.49 KBseanb
#29 table-rtl.png264.18 KBseanb
#29 table.png276.66 KBseanb
#29 grid-rtl.png786.9 KBseanb
#29 grid.png801.13 KBseanb
#29 2981044-29.patch63.92 KBseanb
#29 interdiff-27-29.txt2.74 KBseanb
#27 table-rtl.png298.56 KBseanb
#27 grid-rtl.png792.82 KBseanb
#27 2981044-27.patch63.96 KBseanb
#27 interdiff-23-27.txt20.36 KBseanb
#23 table.png278.01 KBseanb
#23 grid.png809.48 KBseanb
#23 2981044-23.patch46.42 KBseanb
#23 interdiff-17-23.txt50.21 KBseanb
#17 grid-vs-table.gif8.22 MBseanb
#17 2981044-17.patch21.94 KBseanb
#4 google-table-grid.png610.94 KBseanb
#4 ebay-table-grid.png422.67 KBseanb
#4 amazon-table-grid.png1.13 MBseanb
#4 aliexpress-table-grid.png1.47 MBseanb

Comments

phenaproxima created an issue. See original summary.

phenaproxima’s picture

Title: [PP-1] Unify the grid/table views of the media library » Unify the grid/table views of the media library
Status: Postponed » Active

The blocker is in.

phenaproxima’s picture

Copying relevant accessibility feedback from #2962110-127: Add the Media Library module to Drupal core:

Re: #115 (emphasis mine):

From accessibility/UX review we found that the table is always going to be preferred for users using screen readers that can better parse table markup, and for more technical users that want to show more metadata than the Grid is able to nicely handle. We could make this configurable and only show the Grid by default on new installs in a follow up, if showing both isn't desirable.

It's already configurable: site builders can disable the media table view.

I'm dead set against having the table view disabled on new installs. Of all the accessibility aspects looked at in this issue, keeping the table view is by far the biggest win. It's not just for screen reader users: it provides useful metadata for everyone, and offering multiple ways to manage things is a principle of inclusive design. If the table view was off by default, my hunch is that few site builders would ever think to turn it on for their users. If it's enabled by default, my hope is that few site builders will ever turn it off :-)

Edit: Other benefits of keeping the table view...

  • Doesn't have to to deal with truncated titles. Long titles get wrapped in their table cell, and make the row a bit row taller. The table design accommodates this easily.
  • Lets you see the full title, without obscuring the thumbnails.
  • Lets you see all titles in full, none truncated. The card grid just shows one expanded title at a time.
  • Has room for the entity operations links/dropbutton.
  • Easier for site builders to extend, allowing any columns known to Views. For example, license/rights, video length, a media tags field. The card grid can't accommodate much more information.

The choice of grid / list is quite common. A quick look at my phone apps show that Google Drive, Dropbox, the Android file manager, and my RSS reader all have a button to swap between a list and a card/grid view.

seanb’s picture

StatusFileSize
new1.47 MB
new1.13 MB
new422.67 KB
new610.94 KB

It would be nice if you could link 2 views displays as a grid/table display and handle this through views in a generic way. There are a lot of views besides the media library that could benefit from this. Below are some examples from major shopping sites.

The media library view is now separate from the regular media overview. That might be a problem, but I guess in the future it would make sense to merge the 2 views together.

Google grid/table example

Amazon grid/table example

Ebay grid/table example

Ali Express grid/table example

marcoscano’s picture

As per the accessibility feedback received above, it looks like having both options available by default is a good solution (which I personally agree with). In this case, is there anything else this issue is trying to solve?

#4 mentioned the fact that the two views are separate right now, but I don't really see an issue with that. If we want to avoid the tabs as a mechanism to toggle between grid/table view, we can easily add a Text area header to both views, containing the two links, and style them to look like the toggle icons that users are familiar with (such as in the screenshots above). But I'm not sure there is a strong need to remove the tabs, IMHO.

Is anything else needed here?

phenaproxima’s picture

Issue tags: +Needs usability review

Good question; marking for usability review so we can get a canonical answer here.

phenaproxima’s picture

Oh, and accessibility review.

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.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

Just closed #2973162: allow users to toggle between the media library grid and the old table-based list as a duplicate. This is a copy of the IS that contains important information from @andrewmacpherson.
----------------
Problem/Motivation
Follow-up from comments #80 and #97 in #2834729-87: [META] Roadmap to stabilize Media Library.

We might want to allow users to toggle between the media library grid, and the old table-based list.

Misc benefits...

  • Offering users a choice of tools is a recognized principle of inclusive design. If you find one interface awkward, try the other.
  • Keeping the familiar interface available while they're still leaning the new one, a good strategy to support users with cognitive/learning impairments.
  • Reduce frustration for users who might otherwise be annoyed that you took their old tool away, when it worked just fine.
  • Many screen readers provide some power tools for navigating table content. Users who are confident with these can save a lot of time with things like "jump to end of row", or quickly going down the title column while ignoring the other fields.
  • Sorting the list can easier to understand with a table.
  • Reviewing selections (before running bulk-operations) might be easier with a table, too.
  • You can fit more items in the viewport with a table than with a card grid. Less scrolling needed for speech control users.

Proposed resolution
Provide a way to swap between the table and card-grid views of the media library.

MVP could just be a pair of views displays configured using the secondary tabs functionality.
A more advanced approach could preserve filters and bulk-operations selections when switching displays.

andrewmacpherson’s picture

Issue tags: +Accessibility
seanb’s picture

Just discussed this with phenaproxima and lendude on slack. We came up with the following solution.

We are going to create a new area plugin where you can link view displays together. We only allow a display to be linked if:

  • It is a page display
  • It has the same filters
  • It has the same arguments
  • It has the same pager settings

We render links to the other displays in the view area and pass the filters/arguments/pager when a link is clicked. This would mean you get the exact same result, but showed differently.

andrewmacpherson’s picture

Re: #11...

It has the same pager settings

To clarify, does this mean:

  • The exact same views pager plugin (full, mini, ...)?
  • Or, just the same number of results per page?

What problems would arise if they:

  • Didn't use the same pager plugin?
  • Showed a different number of results per page?

For bulk operations selections, can a user have selections spanning more than one page's worth of results?

I can imagine that a table (with small thumbnail images) may be able to accommodate more results in a page/viewport than a card-grid (with larger images). It depends on the particular dispay settings, but in principle I don't see why a listing couldn't swap between a 4-column grid of 20 results per page, and a table of 50 results per page. That may be the feature that makes switching displays useful to a user.

phenaproxima’s picture

To clarify, does this mean:

The exact same views pager plugin (full, mini, ...)?
Or, just the same number of results per page?

Yes. Same plugin and configuration. If they use a different plugin on the other display, or change the settings, we will simply not render the link, and we'll display or log an error/warning about it.

seanb’s picture

I can imagine that a table (with small thumbnail images) may be able to accommodate more results in a page/viewport than a card-grid (with larger images). It depends on the particular display settings, but in principle I don't see why a listing couldn't swap between a 4-column grid of 20 results per page, and a table of 50 results per page.

While I agree that a table display is suited to show more results, what should happen when a user navigates to page number 3 and the displays are showing a different number of items? The first/top result for those displays would be different and probably confusing.

The only way we can guarantee both displays are showing the exact same results is when the filters/arguments and pager settings are equal.

For bulk operations selections, can a user have selections spanning more than one page's worth of results?

Core currently doesn't support that.

seanb’s picture

phenaproxima’s picture

Title: Unify the grid/table views of the media library » [PP-1] Unify the grid/table views of the media library
Status: Active » Postponed
Related issues: +#3025657: Add views area plugin to display a link to another view display within the same view
seanb’s picture

Issue tags: -Accessibility +accessibility
StatusFileSize
new21.94 KB
new8.22 MB
dww’s picture

+++ b/core/modules/media_library/js/media_library.ui.es6.js
@@ -120,6 +120,45 @@
+   * @todo Remove when the AJAX system adds support for replacing a specific
+   *   selector via a link.
+   *   https://www.drupal.org/project/drupal/issues/3026636

nit: @see https://...

Otherwise, all looks good to a quick skim. I haven't applied locally and tested manually, but the screenshot looks nice. ;) Good to have code for the original use-case in core for #3020716: Add vertical tabs style menu to media library.

Thanks,
-Derek

phenaproxima’s picture

Title: [PP-1] Unify the grid/table views of the media library » Unify the grid/table views of the media library
Status: Postponed » Needs work

The blocker is in! We can have this with or without vertical tabs, so let's get 'er done.

phenaproxima’s picture

Status: Needs work » Needs review

Now that #3020716: Add vertical tabs style menu to media library is in, I've queued the patch for re-testing. I think we can get this done.

Status: Needs review » Needs work

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

phenaproxima’s picture

Issue tags: +Needs reroll

Okay, then!

seanb’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new50.21 KB
new46.42 KB
new809.48 KB
new278.01 KB

Rerolled, plus the following changes:

Interdiff is probably not super helpful but added it anyways.

Updated and added screenshots to the IS.

seanb’s picture

Issue summary: View changes
phenaproxima’s picture

  1. +++ b/core/modules/media_library/css/media_library.module.css
    @@ -67,7 +67,8 @@
    +.media-library-view.view-display-id-widget_table .media-library-select-all {
    

    Why is this only applied to the table display?

  2. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -87,8 +87,33 @@
    +.media-library-view.view-display-id-widget .view-header,
    +.media-library-view.view-display-id-widget_table .view-header {
       margin: 16px 0;
    +  text-align: right;
    +}
    +
    +.media-library-view.view-display-id-widget .view-header .button--primary,
    +.media-library-view.view-display-id-widget_table .view-header .button--primary {
    +  float: left;
    +}
    +
    +.media-library-view.view-display-id-widget .views-display-link,
    +.media-library-view.view-display-id-widget_table .views-display-link {
    +  line-height: 16px;
    +  padding-left: 20px;
    +  color: #333;
    +}
    +
    

    Do we also need RTL styles for these?

  3. +++ b/core/modules/media_library/media_library.module
    @@ -53,18 +53,38 @@ function media_library_theme() {
    +  if (isset($variables['element']['#view_id']) && $variables['element']['#view_id'] === 'media_library' && in_array($variables['element']['#display_id'], ['widget', 'widget_table'])) {
    

    Why are we bothering to check the display ID? I would imagine we'd want to run this logic on all displays of the media library.

  4. +++ b/core/modules/media_library/media_library.module
    @@ -53,18 +53,38 @@ function media_library_theme() {
    +  $is_view_display_link = isset($variables['options']['view'], $variables['options']['target_display_id']);
    

    Can we put a $options = &$variables['options'] at the top of this function, for readability?

  5. +++ b/core/modules/media_library/media_library.module
    @@ -53,18 +53,38 @@ function media_library_theme() {
    +  if ($is_view_display_link && $variables['options']['view']->id() === 'media_library' && in_array($variables['options']['view']->current_display, ['widget', 'widget_table'])) {
    

    Same here -- why bother checking the display ID?

  6. +++ b/core/modules/media_library/media_library.module
    @@ -53,18 +53,38 @@ function media_library_theme() {
    +    $variables['options']['attributes']['class'][] = 'js-media-library-view-display-link';
    

    I'm pretty sure the display_link plugin we added allows us to set a CSS class, can't we use that functionality instead?

  7. +++ b/core/modules/media_library/media_library.module
    @@ -53,18 +53,38 @@ function media_library_theme() {
    +  if ($view->id() === 'media_library' && in_array($view->current_display, ['widget', 'widget_table'])) {
    

    Same here?

Status: Needs review » Needs work

The last submitted patch, 23: 2981044-23.patch, failed testing. View results

seanb’s picture

Issue summary: View changes
StatusFileSize
new20.36 KB
new63.96 KB
new792.82 KB
new298.56 KB

#25
1. I don't think it is?

.media-library-view.view-display-id-widget .media-library-select-all,
.media-library-view.view-display-id-widget_table .media-library-select-all {
  display: none;
}

2. Fixed. We do :). Screenshots in the IS.
3. We only want this for the media library widget displays, not for the admin/content/media display in the same view.
4. Fixed.
5. See 3.
6. That can only be done via a template. And we can't add templates to seven for the media library yet :(
7. See 3.

Also think I fixed the tests.

seanb’s picture

Status: Needs work » Needs review
seanb’s picture

Issue summary: View changes
StatusFileSize
new2.74 KB
new63.92 KB
new801.13 KB
new786.9 KB
new276.66 KB
new264.18 KB

Fixed a small RTL issue and webchick noticed I somehow managed to use the grid icon for the table link and vice versa. Should be fixed now. Also updated screenshots in the IS.

dww’s picture

+++ b/core/modules/media_library/config/install/views.view.media_library.yml
@@ -662,9 +665,268 @@ display:
+            items_per_page: 25

Minor quibble: since the grid is responsive, I've found that multiples of 12 are best for these sorts of things, since that's evenly divisible by 1, 2, 3, 4 and 6. :) As you view at different widths, you basically always get full rows of items. 25 is only evenly divisible by 5, so there's only 1 magic width where you see a "full" grid. What do y'all think about standardizing on 24 for both of these displays? If you have 24 items, and you happen to be at the 5-wide width, you still get a mostly-full last row, and all the other possible widths always give you full rows.

Thoughts?

Thanks!
-Derek

seanb’s picture

StatusFileSize
new5.49 KB
new68.09 KB

While working on a demo, I noticed the .media-library-item class used to style the grid was also applied to the table. We shouldn't do that, so added a separate class for the table. Also made sure the JS uses the js-* class.

Regarding #30: I totally agree! Not sure why this hasn't come up before? Let's do this in a follow up though, since this should get UX sign-off, front-end FM sign-off, and an update path + update path tests.

Also found another issue with the views DisplayLink plugins. Created #3033404: Views DisplayLink area plugin tries to use non-existent theme hook for that.

Status: Needs review » Needs work

The last submitted patch, 31: 2981044-31.patch, failed testing. View results

dww’s picture

seanb’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new818.54 KB
new819.52 KB
new298.05 KB
new296.76 KB
new6.12 KB
new68.41 KB
new1.4 MB

Fixed the tests. Also simplified the CSS selectors a bit and made sure the links are close to the view. That makes a lot more sense.

Updated the screenshots in the IS and added a video.

seanb’s picture

phenaproxima’s picture

  1. +++ b/core/modules/media_library/config/install/views.view.media_library.yml
    @@ -662,9 +665,268 @@ display:
    +  widget_table:
    

    I don't know if I like the use of 'widget' in the display ID or label. I think we should probably just call these 'grid' and 'table', respectively, and have the labels be "Grid" and "Table". However, I'd say this is follow-up material.

  2. +++ b/core/modules/media_library/config/install/views.view.media_library.yml
    @@ -662,9 +665,268 @@ display:
    +        thumbnail__target_id:
    +          id: thumbnail__target_id
    +          label: Thumbnail
    +          table: media_field_data
    +          field: thumbnail__target_id
    +          relationship: none
    +          type: image
    +          entity_type: media
    +          entity_field: thumbnail
    +          plugin_id: field
    +          settings:
    +            image_style: thumbnail
    +            image_link: ''
    

    I'm not sure we should be using the 'thumbnail' image style here, now that we ship with our own dedicated one.

  3. +++ b/core/modules/media_library/css/media_library.module.css
    @@ -67,7 +67,8 @@
    +.media-library-view.view-display-id-widget .media-library-select-all,
    +.media-library-view.view-display-id-widget_table .media-library-select-all {
    

    It seems like just calling this .media-library-select-all is a good idea.

  4. +++ b/core/modules/media_library/media_library.module
    @@ -61,13 +61,33 @@ function media_library_theme() {
    +function media_library_preprocess_container(&$variables) {
    +  if (isset($variables['element']['#view_id']) && $variables['element']['#view_id'] === 'media_library' && in_array($variables['element']['#display_id'], ['widget', 'widget_table'])) {
    +    $variables['attributes']['id'] = 'media-library-view';
    +  }
    +}
    

    Isn't it possible to assign a class to the view using Views itself, rather than a preprocess function? I seem to recall this was possible at some point, if not still...

  5. +++ b/core/modules/media_library/media_library.module
    @@ -61,13 +61,33 @@ function media_library_theme() {
    +function media_library_link_alter(&$variables) {
    +  $options = &$variables['options'];
    +  $is_view_display_link = isset($options['view'], $options['target_display_id']);
    +  if ($is_view_display_link && $options['view']->id() === 'media_library' && in_array($options['view']->current_display, ['widget', 'widget_table'])) {
    +    $options['attributes']['class'][] = 'js-media-library-view-display-link';
    +  }
    +}
    

    This is awkward, but there's no other way to do it. I think it could benefit from a comment, though.

  6. +++ b/core/modules/media_library/media_library.module
    @@ -146,7 +166,7 @@ function media_library_form_alter(array &$form, FormStateInterface $form_state,
    +  if ($form_id === 'views_exposed_form' && in_array($form['#id'], ['views-exposed-form-media-library-widget', 'views-exposed-form-media-library-widget-table'])) {
    

    This almost seems like strpos() would be better here.

seanb’s picture

StatusFileSize
new13.9 KB
new78.28 KB

#36
1. Since the view also contains admin/content/media (and maybe the table display for that as well), I guess having the widget prefix is good to differentiate between the displays.
2. Fixed.
3. Afraid not, the 'select all' checkbox should only be hidden in the widget, not on admin/content/media.
4. We need to force this ID on the view to make AJAX work for the grid/table links. We can only use ID as a selector.
5. Fixed.
6. Fixed.

Also removed media_library_views_post_render(). This code breaks the previews in the views UI, and doesn't really do anything anymore since we have the MediaLibraryUiBuilder using MediaLibraryState.

Status: Needs review » Needs work

The last submitted patch, 37: 2981044-37.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new649 bytes
new78.3 KB

Whoops. Removed just a little too much.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/config/install/views.view.media_library.yml
    @@ -662,9 +665,268 @@ display:
    +        uid:
    +          id: uid
    +          label: Author
    

    "Author" is a strange term for a media item. How about "Creator" or "Created by"?

  2. +++ b/core/modules/media_library/config/install/views.view.media_library.yml
    @@ -662,9 +665,268 @@ display:
    +        changed:
    +          id: changed
    +          label: Updated
    

    How about "Last updated" as a label here?

  3. +++ b/core/modules/media_library/css/media_library.module.css
    @@ -67,7 +67,8 @@
    +.media-library-view.view-display-id-widget .media-library-select-all,
    +.media-library-view.view-display-id-widget_table .media-library-select-all {
    

    I wish we didn't have to go by display ID to create these selectors. Is there some way we could have a generic .media-library-view-display class to assign to both widget displays?

  4. +++ b/core/modules/media_library/media_library.module
    @@ -61,29 +61,34 @@ function media_library_theme() {
    +  if (isset($variables['element']['#view_id']) && $variables['element']['#view_id'] === 'media_library' && in_array($variables['element']['#display_id'], ['widget', 'widget_table'])) {
    +    $variables['attributes']['id'] = 'media-library-view';
    +  }
    

    This could use a comment explaining where the #view_id and #display_id properties comes from. Also, why not use strpos() to check if the display ID begins with 'widget', rather than rely on a hard-coded list of displays?

  5. +++ b/core/modules/media_library/media_library.module
    @@ -61,29 +61,34 @@ function media_library_theme() {
    +  // The only way to find the display links is through the options set on the
    +  // link.
    

    Can we add a @see to the relevant code in the DisplayLink plugin?

  6. +++ b/core/modules/media_library/media_library.module
    @@ -61,29 +61,34 @@ function media_library_theme() {
    +  if ($is_view_display_link && $options['view']->id() === 'media_library' && in_array($options['view']->current_display, ['widget', 'widget_table'])) {
    

    Same question here about strpos().

  7. +++ b/core/modules/media_library/media_library.module
    @@ -146,7 +151,7 @@ function media_library_form_alter(array &$form, FormStateInterface $form_state,
    +  if ($form_id === 'views_exposed_form' && strpos('views-exposed-form-media-library-widget', $form['#id']) === 0) {
    

    Confusingly, strpos() takes its arguments in the order of haystack, needle -- which means this call's arguments are in the wrong order. The fact that we didn't catch this makes me wonder if a test (a kernel test would be fine) is needed.

  8. +++ b/core/modules/media_library/media_library.post_update.php
    @@ -28,3 +29,142 @@ function media_library_post_update_display_modes() {
    +  $view = Views::getView('media_library');
    

    We should bail out of the function if, for some reason, the view is gone. Either that, or we should give the view an enforced dependency on the media_library module.

  9. +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php
    @@ -119,6 +119,11 @@ public function buildUi(MediaLibraryState $state = NULL) {
    +          'drupalSettings' => [
    +            'media_library' => [
    +              'selection_remaining' => $state->getAvailableSlots(),
    +            ],
    +          ],
    

    This could use a comment.

  10. +++ b/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewTableDisplayTest.php
    @@ -0,0 +1,115 @@
    +  /**
    +   * Tests that the table display when the grid display has been changed.
    +   *
    

    This comment seems like it's missing a word or two.

  11. +++ b/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewTableDisplayTest.php
    @@ -0,0 +1,115 @@
    +  public function testMediaLibraryChangedViewTableDisplay() {
    

    What is this method testing? It seems like it's confirming that the Views override system works correctly.

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new15.45 KB
new82.89 KB

#40
1. The view from the media module also uses 'author'. The media edit page uses Authoring information and 'Authored by'. I think we should for now copy what we already have. Maybe a followup to discuss if this should be changed?
2. Same as 1.
3. Fixed. Added an extra CSS class to the grid/table displays.
4. Fixed.
5. Fixed.
6. Fixed.
7. Fixed. Added a test to verify the 'Apply filters' button is not added to the button pane of the dialog for the grid/table displays.
8. Fixed. Added a message to inform users they will probably need to reinstall the module of the view is deleted. I think we should have a followup to remove delete access for this view (and probably the displays as well!).
9. Fixed.
10. Not sure what happened there :p
11. Fixed. In a previous version, the update hook assumed the pager and sorts were still using the default, so they were not updated. Users could have manually overridden the grid display pager/sort settings. In this case we need to verify that the widget_table display copies its settings from the widget display (and does not use the settings from the default display). Added some comments to explain this a bit more.

Besides that, added aria-live="polite" to the views container to make sure the new content is announced when clicking a display link.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/media_library.module
    @@ -65,8 +65,14 @@ function media_library_theme() {
    +    $variables['attributes']['aria-live'] = 'polite';
    

    +1 for this, but I think the addition of this attribute could use a comment.

  2. +++ b/core/modules/media_library/media_library.post_update.php
    @@ -36,12 +36,21 @@ function media_library_post_update_display_modes() {
    +  if (!$view) {
    +    return t('The media_library view could not be updated because it has been deleted. The media library module needs this view to display correctly. Uninstall and reinstall the module so the view will be re-created.');
    +  }
    

    Reading the documentation for hook_post_update_NAME(), I'm wondering if this should not be an UpdateException. Do we consider this an error condition? (I'd say yes.)

  3. +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php
    @@ -117,6 +117,9 @@ public function buildUi(MediaLibraryState $state = NULL) {
    +        // Attach the javascript for the media library UI. The number of
    

    Nit: Should be 'JavaScript'.

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.56 KB
new83.01 KB

#42 Fixed 1 and 3. I think throwing an exception is not the best solution here, since we would also stop other updates from running. This could potentially leave the sites in a state where they are not able to fix the problem. A missing view is not worth doing that imho.

phenaproxima’s picture

Status: Needs review » Needs work
Issue tags: +Needs frontend framework manager review

Minor stuff, then this is ready for manual testing and (probably) RTBC after final review by Lauri. Tagging him in to review the CSS and JavaScript changes here.

  1. +++ b/core/modules/media_library/config/install/views.view.media_library.yml
    @@ -662,9 +666,271 @@ display:
    +      style:
    +        type: table
    +        options:
    +          row_class: 'media-library-item-table js-media-library-item js-click-to-select'
    +          default_row_class: true
    

    Do we have a similar media-library-item-grid class on the items of the grid display? If not, maybe we should just stick with media-library-item here.

  2. +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php
    @@ -117,8 +117,16 @@ public function buildUi(MediaLibraryState $state = NULL) {
    +          'drupalSettings' => [
    +            'media_library' => [
    +              'selection_remaining' => $state->getAvailableSlots(),
    +            ],
    +          ],
    

    I wonder if this should not actually be moved into a method of the MediaLibraryState object, to keep things well-encapsulated. We could make it something like this:

    'drupalSettings' => $state->getJavascriptSettings()
    
  3. +++ b/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewTableDisplayTest.php
    @@ -0,0 +1,141 @@
    +  public function testMediaLibraryViewTableDisplay() {
    

    A lot of the code in this method is kind of hard to read. Can we (should we?) add variables like $grid_prefix = "display.widget" and $table_prefix = "display.widget_table" and use these repeatedly in the test?

  4. +++ b/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewTableDisplayTest.php
    @@ -0,0 +1,141 @@
    +  public function testMediaLibraryChangedViewTableDisplay() {
    

    Same here?

  5. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -279,6 +279,41 @@ public function testWidget() {
    +    $assert_session->elementExists('css', '.media-library-view .media-library-item article');
    

    I'm not sure we should have 'article' as part of this selector.

  6. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -279,6 +279,41 @@ public function testWidget() {
    +    $assert_session->elementExists('named', ['link', 'Show as grid']);
    +    $assert_session->elementExists('named', ['link', 'Show as table'])->click();
    

    These can be $page->clickLink() for readability. Mink is smarter now and will throw exceptions, rather than die with fatal errors, if the links don't exist.

andrewmacpherson’s picture

This issue just changes the content of the MediaLibraryWIdget, right? Is there another issue to do this to the page view at admin/content/media?

Review of manual testing of patch #41, with and without a screen reader.

  1. Besides that, added aria-live="polite" to the views container to make sure the new content is announced when clicking a display link.

    That's way too overblown. This attempts to read the entire content of the view! Confusingly, the first thing it reads is the pair of buttons. one of which was just pressed. Then it reads everything else in the view. The attached screenshot shows the extent of the ARIA live region, using the browser's dev tools layout overlay highlight feature.

    Think about what you actually need to convey:

    • A button was pressed.
    • Then, something happened.
    • So, say what just happened as a result of pressing the button.

    If a blind person asked you "what just happened?", how would you answer in one short sentence? Study the way the toolbar module informs the user of a change in orientation; it doesn't read the entire toolbar.

  2. The tabbing order of the view is messed up. The grid and table buttons come before the name filter in the DOM, but visually they come after the apply-filters button This will confound sighted keyboard users. Make the tabbing order match the visual reading order.

    You can fix this in one of two ways. Both involve ditching the CSS which controls the visual position:

    • Swap the order of these two divs in the DOM, to put the buttons second.
    • Let the buttons appear first like the current the DOM order says.

    WCAG technique C27: Making the DOM order match the visual order has further info about this.

  3. "Show as grid" and "show as table" are links. Are we intending for MediaLibraryWidget to work without JS? If so, they should probably stay as links. If not, they would be better as buttons. Don't fret about this too much though, it's part of a bigger problem across our entire AJAX system. There are a lot of places where an AJAX dialog works without JS by taking you to an intermediate page (for example adding a block in admin/structure/blocks). So there's a situation where they link is the right role when JS is off, but button should be the role when JS is on. I think there might be an issue elsewhere about that. The new views plugin for a link to another display will presumably have uses outside of a dialog, so this could be an awkward one. However I don't think it's a very major accessibility issue, as it doesn't present an outright barrier to using it. Many screen reader users are accustomed to the fact that buttons and links are used interchangeably by developers, and will shrug it off. However some users will be confused by it (for example, tech support tries to direct them to a button, but it isn't in the screen reader's list of buttons).
seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new30.38 KB
new91.08 KB

#44
1. Fixed. We should probably have a special media-library-item--grid and media-library-item--table class. This would also make the CSS a lot more clear since was a little confusing that the media-library-item class adds only grid specific styles.
2. I don't think we should do that. Same reason we decided the dialog options don't belong in the state.
3. Fixed.
4. Fixed.
5. Fixed.
6. Fixed.

#45
1. Fixed. Removed the aria-live attribute and added Drupal.announce() messages.
2. I've added @todo messages to fix this when the media library is stable, since we can't override the views template from a module. We also can't add a template to seven for a experimental module. It will be either a UX / design issue or an a11y issue. Not sure what to do now?

/* @todo Remove order and reorder the views header and filters via a template
     when styles are moved to the seven theme in
     https://www.drupal.org/project/drupal/issues/2980769 */

3. The media library widget doesn't work without JS, but technically the displays are page views that work outside of it. Leaving this for now.

Status: Needs review » Needs work

The last submitted patch, 46: 2981044-46.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

andrewmacpherson’s picture

Re. #46

Focus order:
Hmm, I understand the issue with templates, but that gives us a chicken-or-egg problem. Focus order issue is a WCAG level A problem, and that's a blocker for the accessibility gate. We can't mark it stable.

The other approach is to remove the CSS which hacks the visual order. Do the table/grid buttons really have to appear after the filters in the visual order? I don't think it matters a great deal which one is above the other, but the focus order jumping up and down isn't good.

Announcements:

  1. +    if ($options['target_display_id'] === 'widget') {
    +      $options['attributes']['data-display-announcement'] = t('Media library content changed to grid view.');
    +    }
    +    if ($options['target_display_id'] === 'widget_table') {
    +      $options['attributes']['data-display-announcement'] = t('Media library content changed to table view.');

    Getting there, but these are still a bit verbose. The outcome message is over twice as long as the button text. Can you think of a shorter message?
    Also, what exactly does "content" mean here? I thought these buttons just changed the presentation. This message might be misinterpreted, as meaning that different media items are being shown.

  2. +            } else {
    +              Drupal.announce(Drupal.t('Media library content changed.'));
    +            }

    This message is useless. In what situations does it get used?

  3. + Drupal.announce(Drupal.t('Loading media library content.'));
    This is vague. What situation is it for?
  4. +          // Override the AJAX success callback to announce the updated content
    +          // to screen readers.

    This comment makes it sound like an awkward workaround. Can we use the new AnnounceCommand here, and keep the message logic on the server side?

andrewmacpherson’s picture

Manual testing of patch #46:

  1. After pressing one of the grid/table buttons, focus moves to the start of the dialog. That's not necessary, and is disorientating. Users have to tab past all the vertical tab headers, and the "add files" widget, to recover their position in the dialog.

    TODO: It will be better if focus remains on the button that was just pressed. It seems the view filters and display buttons are all replaced as well as the view results, so it will need some way to remember the button which was pressed, and restore focus to it programatically. Aside, we could do with a generic method to say which element get focus after AJAX responses.

  2. I haven't been able to produce the else-case message from #48.2 ("Media library content changed."). I'm still none the wiser what this is for.
  3. I've heard the message from #48.3 in context. When using the table button, the screen reader announces:
    • "Media library, dialog." I think this happens because focus has moved to the start of the dialog (point 1 here), but I'm not certain. That's actually useful, because the user needs to know about the undesirable focus shift in point 1 here (however we should still fix that). When we programatically maintain focus on the button, this dialog announcement might go away. If it doesn't, that's fine. No action needed here.
    • "Loading media library content." This is queued up just after the AJAX request has been sent. I think you're saying this because we have no idea how long we're going to wait for a response? The message itself could be more succinct.
    • "Media library content changed to table view." This is queued up after the AJAX response has been received. It's OK but it could be more succinct.

    Overall, these messages are OK, but we should aim to reduce the verbosity. When they add up, it's overkill. We hear the phrase "media library" three times as a result of one button press.

    The "loading" message is the one that bothers me most. If the response comes quickly, say within a second, the "loading" message isn't needed, and possibly annoying. But if the response is slow, the message tells the user something is happening. That sounds great in principle, but we aren't doing this for AJAX requests anywhere else. For example, when pressing "apply filters" we don't have any similar "loading" message for screen reader users.

    For now, leave the loading message in place. It's a bit out of place compared to the rest of our AJAX UI, but we can clean it up after we have a generic approach. (Elsewhere, there is an issue to convey AJAX progress messages to assistive technology. I'll dig that out, it has a patch I think.)

andrewmacpherson’s picture

The generic AJAX progress accessibility issue is #2973140: Convey AJAX progress messages to assistive technology..

That's would be a good issue to treat as could-have for Media Library, so I'll add it to the roadmap. By my count there are 3 ajax behaviours in the media library widget that can benefit from this: changing tab, changing filters, changing to grid or table.

In any case, we can update the patch here with a @todo before the "Media library content is loading" message, to change it after the generic approach is ready.

pancho’s picture

Just a comment to the possible followup:

"Author" is a strange term for a media item. How about "Creator" or "Created by"?

I know that for now we settled with Author, which is ok for the moment. However, from the fact that someone uploaded a media item we can‘t infer the user is also the author or creator of the media.

Now you might say that the same argument holds for text content as well. True, however less so. Finally, we‘re providing tools to create new textual content, while our tools don‘t support actually creating AV media. So the chances someone else is the actual author/creator are infinitely higher. Furthermore, the level of originality tends to be higher for AV media.

Therefore, the correct term for media might be „uploader.“

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new12.92 KB
new95.89 KB

#48 / #49 / #50

Hmm, I understand the issue with templates, but that gives us a chicken-or-egg problem. Focus order issue is a WCAG level A problem, and that's a blocker for the accessibility gate. We can't mark it stable.

I'll leave it up to product managers to make a decision on what to do. We already have stable blockers that can only be fixed after/when the module is marked stable. If we need to fix it now, we need to have a new design for the placement of the links until we mark the module stable.

1. Changed the messages to 'Changed to X view'. Hope this helps.
2. Showing a default message if the data attributes are missing might not be helpful at all, so removed it. Site builders can technically add more display links and not add a data attribute with a message, but that might be their responsibiity.
3. Changed the messages a bit and added the todo's.
4. We have #3026636: Allow AJAX links to replace a specific selector to add support for more advanced use cases to AJAX links #3033150: Add feature parity for consistency between AJAX links and the Form AJAX API. Until then we have to do our own thing, since improving the AJAX system in general is not a direct priority. Implementing a custom controller for this to do everything server side adds a lot more complexity, and should ideally not be nessecary.

seanb’s picture

I know that for now we settled with Author, which is ok for the moment. However, from the fact that someone uploaded a media item we can‘t infer the user is also the author or creator of the media.

Now you might say that the same argument holds for text content as well. True, however less so. Finally, we‘re providing tools to create new textual content, while our tools don‘t support actually creating AV media. So the chances someone else is the actual author/creator are infinitely higher. Furthermore, the level of originality tends to be higher for AV media.

Therefore, the correct term for media might be „uploader.“

I think this strongly depends on the context, but I can see how this can be confusing to users. What we actually mean is "user that created the media entity". This is not necessarily an upload (for example, Youtube videos are added via URL). I think the generic term for users that create something in Drupal is currently 'Author'. Introducing different term for different entity types is probably not the best idea. If we want to change this, I think we should look at generic terminology for "user that created an entity" and change it everywhere to avoid confusion.

andrewmacpherson’s picture

#52...

+          // @todo Replace custom announcement when
+          //   https://www.drupal.org/project/drupal/issues/2973140 is in.
+          if (displayAnnouncement) {

The @todo for #2973140: Convey AJAX progress messages to assistive technology. only relates to the loadingAnnouncement I think?

andrewmacpherson’s picture

"Loading grid view. Changed to grid view." and "Loading table view. Changed to table view."

Those are much better messages, call that part done :-)

seanb’s picture

StatusFileSize
new695 bytes
new415.18 KB

The @todo for #2973140: Convey AJAX progress messages to assistive technology. only relates to the loadingAnnouncement I think?

You are right! New patch attached.

Status: Needs review » Needs work

The last submitted patch, 56: 2981044-56.patch, failed testing. View results

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new95.77 KB

Forgot to pull into the branch when making #56. Here is a reroll of that exact patch. Interdiff of #56 is the only change since #51.

seanb’s picture

Mentioned in #45.2 and #48.

The tabbing order of the view is messed up. The grid and table buttons come before the name filter in the DOM, but visually they come after the apply-filters button This will confound sighted keyboard users. Make the tabbing order match the visual reading order.
You can fix this in one of two ways. Both involve ditching the CSS which controls the visual position:

Swap the order of these two divs in the DOM, to put the buttons second.
Let the buttons appear first like the current the DOM order says.
WCAG technique C27: Making the DOM order match the visual order has further info about this.

The response in #46:

I've added @todo messages to fix this when the media library is stable, since we can't override the views template from a module. We also can't add a template to seven for a experimental module. It will be either a UX / design issue or an a11y issue. Not sure what to do now?

Either we fix the DOM order when the library is stable, or we fix the design when the library is stable and implement a different design until then. I think we need a product manager to make a final decision on that one. We unfortunately can't have both.

phenaproxima’s picture

The code looks pretty good to me, honestly. Only a couple of questions remain, then this can go to through the various sign-offs it needs.

  1. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -128,40 +119,148 @@
    +.media-library-item--grid .views-field-operations .dropbutton-wrapper {
    +  position: absolute;
    +  right: 5px;
    +  bottom: 5px;
    +  display: inline-block;
    +}
    

    I'm surprised we even have drop buttons in the grid view. Can this get a comment?

  2. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -252,18 +329,24 @@
    +.media-library-item__remove,
    +.media-library-item__remove:hover,
    +.media-library-item__remove:focus,
    +.media-library-item__remove.button,
    +.media-library-item__remove.button:disabled,
    +.media-library-item__remove.button:disabled:active,
    +.media-library-item__remove.button:hover,
    +.media-library-item__remove.button:focus {
    

    Isn't this stuff part of #3023801: Allow newly uploaded files to be deleted from the media library without saving them?

  3. +++ b/core/modules/media_library/media_library.module
    @@ -61,29 +61,60 @@ function media_library_theme() {
    +      $options['attributes']['data-display-announcement'] = t('Changed to grid view.');
    +      $options['attributes']['data-loading-announcement'] = t('Loading grid view.');
    

    I wonder if these attributes might not be better named 'data-ajax-*-announcement', to clarify that they are specifically there for the AJAX system to use.

  4. +++ b/core/modules/media_library/media_library.module
    @@ -61,29 +61,60 @@ function media_library_theme() {
    +    if ($options['target_display_id'] === 'widget_table') {
    

    This should be elseif.

seanb’s picture

StatusFileSize
new5.93 KB
new96.36 KB

Fixed #60 and some code style things. Also added extra asserts for the announcements.

seanb’s picture

StatusFileSize
new96.1 KB

Rerolled #61.

lauriii’s picture

Issue summary: View changes
Status: Needs review » Needs work
Issue tags: -Needs frontend framework manager review
StatusFileSize
new4.12 KB
new1.86 KB
new19.24 KB
  1. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -161,40 +152,137 @@
    +  padding-left: 24px;
    ...
    +  background: url(../../../misc/icons/333333/grid.svg) 0 0 no-repeat;
    ...
    +  background: url(../../../misc/icons/333333/table.svg) 0 0 no-repeat;
    

    Let's add a comment that these properties are for LTR.

  2. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -161,40 +152,137 @@
    +  background: url(../../../misc/icons/333333/grid.svg) right 0 no-repeat;
    ...
    +  background: url(../../../misc/icons/333333/table.svg) right 0 no-repeat;
    

    We can override the background-position without overriding the image.

  3. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -161,40 +152,137 @@
    +  margin: 2px 16px 16px 2px;
    

    This needs RTL styles.

  4. +++ b/core/modules/media_library/media_library.module
    @@ -63,29 +61,60 @@ function media_library_theme() {
    +    $variables['attributes']['id'] = 'media-library-view';
    ...
    +    $options['attributes']['id'] = 'views-media-library-display-link-' . $options['target_display_id'];
    

    We should ensure these IDs are unique.


  5. There's a regression in the spacing of the field widget grid.

  6. The remove button positioning is not correct in the field widget.

  7. There's sideways scroll on the dialog.

Removing the tag but I'll keep my eyes on this 🧐

phenaproxima’s picture

We went over this in the UX call today, and we have a bunch of useful feedback! Generally, it was well-received, but for a couple of things:

Firstly, the double use of the phrase "Show as" is repetitive. We can just have those links say "Grid" and "Table". @andrewmacpherson agreed that this is okay from an accessibility standpoint ("brevity is good"). Additionally, this keeps the links consistent with the local tasks that are already present at /admin/content/media.

Regarding the tabbing order snafu: this is a tricky chicken-and-egg scenario, and we discussed it with @andrewmacpherson and the product managers. Having the tab order match the visual order is A-level WCAG criteria, but we cannot actually fix this for realsies until Media Library is stable and can therefore add an accessible template to Seven. Although it's an important problem, it doesn't render the media library unusable for users who rely on assistive tech; it just makes it frustrating. So here's the plan: we will commit this issue as-is, but first we will open a postponed major follow-up to add an accessible template to Seven, after Media Library is stable. That issue may become a release-blocking critical for Drupal core, since it addresses a A-level WCAG criteria. @webchick OKed this plan, so I'm removing the "Needs product manager review" tag.

andrewmacpherson’s picture

Yep, the follow-up template plan from #64 has accessibility maintainer sign-off, but I'd like to clarify that we first need to know that the follow-up is actually feasible.

So it would be a good idea to develop the patch in the follow-up issue, while this issue is still being worked on. That way there could be something ready to go as soon as the module is stable, and minimize the chance of encountering a gotcha after marking media library as stable.

I'll add a note in the roadmap issue, about the special handling of the accessibility gate for this issue.

seanb’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs followup
Related issues: +#3035994: Add a Media Library views template to Seven theme to change to order of the views header and exposed filters
StatusFileSize
new19.57 KB
new100.69 KB
new771.46 KB
new738.83 KB
new283.46 KB
new220.61 KB
new2.22 MB

Updated screenshots and video in the IS.

#63
1. Fixed.
2. Fixed.
3. Fixed.
4. If we do that, the IDs will not be the same after the view has loaded and the focus will not be set back on the link. So I guess that is going to be a problem.
5. Fixed.
6. Fixed.
7. Fixed. This was caused by the label for the checkbox having a table display and visually hidden class.

#64 / #65 Changed the link labels and created #3035994: Add a Media Library views template to Seven theme to change to order of the views header and exposed filters

Also added back code in media_library_views_post_render() that I removed in #37. Turns out I was wrong and we need that code when you apply filters and/or sorts. We apparently had no tests that filter a view, select some media from the filtered results and inserts that to the widget. So I added this to make sure this doesn't happen again.

phenaproxima’s picture

Status: Needs review » Needs work

Looks like we have all the required sign-offs and approvals. So all we gotta do is fix these code nits, and I'm ready to call it RTBC.

  1. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -161,40 +152,148 @@
    +.media-library-wrapper .media-library-view .button--primary {
    +  position: absolute;
    +  top: 0;
    +  left: 0; /* LTR */
    +}
    +[dir="rtl"] .media-library-wrapper .media-library-view .button--primary {
    +  right: 0;
    +  left: auto;
    +}
    +
    

    Can we get a comment as to what this is targeting, and why?

  2. +++ b/core/modules/media_library/css/media_library.theme.css
    @@ -161,40 +152,148 @@
    +.media-library-wrapper .views-display-link-widget {
    +  margin-right: 15px;
    +  background: url(../../../misc/icons/333333/grid.svg) 0 0 no-repeat; /* LTR */
    +}
    +[dir="rtl"] .media-library-wrapper .views-display-link-widget {
    +  background-position: right 0;
    +}
    +
    +.media-library-wrapper .views-display-link-widget_table {
    +  background: url(../../../misc/icons/333333/table.svg) 0 0 no-repeat; /* LTR */
    +}
    +[dir="rtl"] .media-library-wrapper .views-display-link-widget_table {
    +  background-position: right 0;
    +}
    

    For consistency, can the LTR styling use 'left 0' for background position, so that the RTL styling echoes that?

  3. +++ b/core/modules/media_library/js/media_library.ui.es6.js
    @@ -140,6 +140,72 @@
    +              // The AJAX link replace the whole view, including the clicked
    

    Should be "The AJAX link replaces..."

  4. +++ b/core/modules/media_library/media_library.module
    @@ -63,14 +63,62 @@ function media_library_theme() {
    +  if (isset($variables['element']['#view_id']) && $variables['element']['#view_id'] === 'media_library' && strpos($variables['element']['#display_id'], 'widget') === 0) {
    

    This isset() call should probably also check for the existence of $variables['element']['#display_id']. To make this more readable, we could add a line above this like $element = &$variables['element'].

  5. +++ b/core/modules/media_library/media_library.module
    @@ -63,14 +63,62 @@ function media_library_theme() {
    +    // The AJAX link replace the whole view, including the clicked link. After
    

    Should be "The AJAX link replaces..."

  6. +++ b/core/modules/media_library/media_library.post_update.php
    @@ -28,3 +29,159 @@ function media_library_post_update_display_modes() {
    +    return t('The media_library view could not be updated because it has been deleted. The media library module needs this view to display correctly. Uninstall and reinstall the module so the view will be re-created.');
    

    Can we rephrase the second sentence a bit? How about "This view must exist for the Media library module to work properly."?

  7. +++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
    @@ -53,6 +53,9 @@ public function viewsForm(array &$form, FormStateInterface $form_state) {
    +    // Add the view to the form state.
    +    $form_state->set('view', $this->view);
    

    Let's add a @see ::updateWidget() here so it's clear where the view is being used elsewhere in this class.

  8. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -208,7 +208,6 @@ public function testWidget() {
    -    $assert_session->assertWaitOnAjaxRequest();
    

    Why were these removed?

  9. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $assert_session->elementNotExists('css', '.media-library-view .media-library-item--table');
    

    This might make more sense with $assert_session->elementsCount().

  10. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $button_pane = $assert_session->elementExists('css', '.ui-dialog-buttonpane');
    +    // Assert the 'Apply filter' button is not moved to the button pane.
    

    The comment should come before the elementExists() call.

  11. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $page->hasLink('Grid');
    

    This should be $assert_session->linkExists().

  12. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $assert_session->waitForText('Loading table view.');
    +    $assert_session->waitForText('Changed to table view.');
    +    $assert_session->waitForElementVisible('css', '.media-library-view .media-library-item--table');
    

    All of these wait* methods return NULL if the thing they are waiting for does not show up. So we need to wrap these calls in $this->assertNotEmpty().

  13. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $page->hasLink('Table');
    

    Should be $assert_session->linkExists().

  14. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $assert_session->waitForText('Loading grid view.');
    +    $assert_session->waitForText('Changed to grid view.');
    +    $assert_session->waitForElementVisible('css', '.media-library-view .media-library-item--grid');
    

    These also need to be wrapped in assertNotEmpty().

  15. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $assert_session->elementNotExists('css', '.media-library-view .media-library-item--table');
    

    Can this also use elementsCount()?

  16. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $page->hasLink('Grid');
    +    $page->hasLink('Table');
    

    linkExists()

  17. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
    @@ -298,6 +293,63 @@ public function testWidget() {
    +    $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media');
    

    I think this can just be $button_pane->pressButton('Select media').

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new8.89 KB
new100.77 KB

#67

1. Fixed, removed it. I honestly don't know why it was there. There is no primary button in the view anywhere, could be a copy/paste thing from #3023797: Let users choose what to do after selecting and/or adding items in the media library (where we introduce primary buttons).
2. Fixed.
3. Fixed.
4. Fixed.
5. Fixed.
6. Fixed.
7. Fixed.
8. Closing the dialog does not start an AJAX request.
9. We want to now if the page contains the table display, isn’t checking for it’s existance clearer than checking of the number of elements with that class is a certain number? elementNotExists is also slighly more optimized.
10. Fixed.
11. Fixed.
12. Fixed.
13. Fixed.
14. Fixed.
15. See 9.
16. Fixed.
17. I guess in this case we can, but we use $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media'); 9 more times in the test. Maybe consistency is a little bit more important here?

seanb’s picture

StatusFileSize
new404 bytes
new100.77 KB

Whoops! Found 1 minor code style thing (somehow CSS Comb adds this newline constantly).

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Okay, that interdiff satisfies me just fine.

I've now read this patch multiple times; it has been demoed to the UX team more than once; @lauriii has signed off on the CSS and JavaScript; we have expanded test coverage; @andrewmacpherson has given extensive accessibility feedback and we have a solid plan in place, with the blessing of the product managers, to address the DOM ordering issue (with a triaged critical follow-up once this module is stable). I don't think there's anything else to do but land this thing.

larowlan’s picture

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

This is looking good, really nice UX...raises the bar for Drupal admin experience.

  1. +++ b/core/modules/media_library/media_library.module
    @@ -63,14 +63,63 @@ function media_library_theme() {
    +function media_library_preprocess_container(&$variables) {
    

    is there a performance cost here...not sure its as bad as on a link (story for another day) - but we should profile this - I think a lot of form elements will end up going through this hook.

    looking at \Drupal\views\ViewExecutable::buildThemeFunctions it adds theme suggestions based on tags on the view - can we explore that?

    And yep, I know \Drupal\views\Element\View::preRenderViewElement is a pita in so far as it adds the 'container' theme wrapper. (our current starter theme at work includes alter stuff to get rid of that by default, because it naffs flexbox and other things)

    I guess first step would be to profile the impact this has before we go too far

  2. +++ b/core/modules/media_library/media_library.module
    @@ -63,14 +63,63 @@ function media_library_theme() {
    +function media_library_link_alter(&$variables) {
    

    oh..speaking of links and performance. This is almost certainly a performance hit - that we need to profile - this runs for every menu link...and on big sites...is a huge drain in my experience

    Options I think we have here -
    1) we could add 'class' and 'id' support to the existing plugin, but it wouldn't help with the announcement data attributes
    2) we could add custom area plugins just for media (and extend from the existing display link)
    3) we could add a new alter hook in display link - ie. more specific than 'link_alter'

    Thoughts?

Status: Needs review » Needs work

The last submitted patch, 69: 2981044-69.patch, failed testing. View results

phenaproxima’s picture

I installed Blackfire tonight and did some light profiling of this patch.

The machine in question was my personal MacBook Pro, which has 16 GB of RAM. I did this using the drush rs web server on PHP 7.3.2 using a SQLite database. I installed Drupal cleanly with the Standard profile after altering my settings to completely disable caching, as per the instructions at https://www.drupal.org/node/2598914. For good measure, I also uninstalled the big_pipe, page_cache, and dynamic_page_cache modules, and then installed admin_toolbar in order to artificially increase the number of links on the page. Then I logged in as admin (so I'd see the toolbar) and ran two profiles.

The first one was without Media or Media Library installed: https://blackfire.io/profiles/e653480b-b9c5-480a-830f-0bbd20feafdc/graph

The second was with this patch applied (minus the rejected hunks, which were just in tests), plus Media Library (and therefore Media) installed: https://blackfire.io/profiles/e27cd020-a164-4587-a6f1-8798b0cffe9a/graph

Here's a comparison of the two: https://blackfire.io/profiles/compare/73be5658-0733-4001-8e47-98f0d16ca0...

TL;DR: I'm very new to profiling, so I don't quite know what to make of this, but one thing that leaps out at me is that the second run took a full .4 seconds longer than the first. If this is the fault of the hook_link_alter implementation, then that seems like something that could very likely cause serious performance problems at scale, if it had so dramatic an effect with a rinky-dink nothing site like the one I tested with...

Tentatively removing the "Needs profiling" tag.

phenaproxima’s picture

If this does turn out to be a performance regression (I leave such judgments to the experts), I want to state for the record that my preferred option is:

we could add custom area plugins just for media (and extend from the existing display link)

To me, this is the least disruptive thing we could do: we just gotta add a new, internal plugin with some hardcoded extra goodies -- don't even need additional tests, since we're basically just changing implementation details -- and do another round of profiling to prove the regression is gone. Sounds pretty good to me.

seanb’s picture

Even though custom area plugins for media would probably be the quickest way to solve this, I'm not sure if users configuring views would understand why they have 2 plugins doing basically the same thing.

I think a custom alter hook for display links would be a better solution. Added #3036694: Add hook_views_display_link_alter() to allow modules to alter view display links. for that. It would also make it easier to target the display links.

The important question now is. Is it really a performance issue at the moment an are we blocked on #3036694: Add hook_views_display_link_alter() to allow modules to alter view display links.?
We are doing 1 isset() call for each link with 2 variables, we could optimize and split them up so we check 1 variable at a time and return faster for most links? That would already make it a bit more efficient.

According to https://phpbench.com/ an isset() call takes 0.01 milliseconds. If a hook_link_alter() that for 99% of the links only does an isset() call is a performance issue, I guess we should definitely document this better on the hook and discourage its use.

phenaproxima’s picture

I discussed the problems with @seanB in Slack. Lo and behold, we came up with solutions!

  1. For the container thing, it looks like it's not strictly necessary for us to add the attribute in a preprocess function. Because we control when and where the view is rendered, we can simply add a pre-render function which will set the attribute for us when we need it. Boom!
  2. The link stuff is trickier, but Sean thinks that everything we are doing in that function can be done with JavaScript instead. I'm 100% in favor of that solution, at least for now. The media library is already entirely reliant on JavaScript anyway, so a little more won't hurt. Doing it in JS removes the need for us to use hook_link_alter(), introduces no new API (i.e., a specialized alter hook) and it confines the solution entirely to Media Library's internals. I love it.
seanb’s picture

StatusFileSize
new8.74 KB
new100.72 KB

#76
1. I tried, but we can't do that because we only control the rendering the first time. After changing filters, sorts etc the view is not rendered by the media library. Also added the ID to the views container in JS now, since the only reason we need that in the first place is because #2821793: Replace #ajax['wrapper'] with #ajax['wrapper_selector'] has not yet landed.
2. Done. We are using classes as selector in a way we normally don't, but I think for now this is fine until we can alter the links properly via #3036694: Add hook_views_display_link_alter() to allow modules to alter view display links..

seanb’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
phenaproxima’s picture

Well, damn. Okay. I think these are acceptable workarounds. This issue is too much of a major usability and accessibility improvement for us to block on limitations of Views (which hails from a whole 'nuther galaxy of crazy) or the AJAX system.

Status: Needs review » Needs work

The last submitted patch, 77: 2981044-77.patch, failed testing. View results

phenaproxima’s picture

Issue tags: +Needs reroll

Sigh.

seanb’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new100.73 KB
phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

The JavaScript workarounds make sense to me. It's really too bad that Views and the AJAX system are forcing our hand in this way, but that's life in Drupal core, I guess. At least we have follow-up and related issues in which to improve things.

Back to RTBC.

tim.plunkett’s picture

Issue tags: -accessibility (duplicate tag) +Accessibility

Fixing tags

larowlan’s picture

The JS workaround is acceptable to me until such time as we resolve the follow-ups (#3036694: Add hook_views_display_link_alter() to allow modules to alter view display links. and #2821793: Replace #ajax['wrapper'] with #ajax['wrapper_selector'])

But I think we need the front end framework manager to sign off on the new JS, so adding that tag - I'll ping @lauriii for another look.

lauriii’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: -Needs frontend framework manager review

The JavaScript workaround is acceptable to me as well. Hopefully, we will be able to make this nicer after resolving the follow-ups.

+++ b/core/modules/media_library/config/install/views.view.media_library.yml
@@ -662,9 +666,271 @@ display:
+          display_id: widget
...
+          display_id: widget

Nitpick: could this be changed into widget_grid? It would make the class names more verbose.

seanb’s picture

Nitpick: could this be changed into widget_grid? It would make the class names more verbose.

The widget display was already in the view for existing sites (added in 8.6). Renaming that could break customisations in existing sites (css, alter hooks etc). I don't think there are a lot of sites that did that, but I'm not sure if this is worth it.

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

I agree with Sean. We could open a follow-up to look into renaming, but it’s out of scope here and the risks far outweigh the benefits at this stage in the 8.7 cycle. Back to RTBC!

  • Gábor Hojtsy committed ce6569f on 8.7.x
    Issue #2981044 by seanB, lauriii, andrewmacpherson, phenaproxima, dww,...
gábor hojtsy’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +8.7.0 highlights

Superb, thanks all. I believe the overall visual coherence of this window needs work (relative placement, visual weights, etc.) but we can refine that later on as well, especially once the dropzone for uploads lands as well.

Status: Fixed » Closed (fixed)

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