Make WordPress Core

Changeset 50829

Timestamp:
05/07/2021 11:17:33 PM (3 years ago)
Author:
joedolson
Message:

Media: Remove infinite scroll from media library and modal.

Replace infinitely autoloading behavior on scroll with a user-controlled load more button. Fix a long standing accessibility issue in the media library. Infinite scroll poses a wide range of problems for accessibility, usability, and performance.

This change modifies the library to load 40 items in the initial view, with a load more button to load the next 40 items and a button to move focus from the load more region to the first of the most recently added items.

The text for communicating the jump target was broadly discussed, agreeing that the text incorporated here would most concisely and clearly convey the purpose of the button, and any further detail is learnable from use.

Props afercia, adamsilverstein, joedolson, audrasjb, francina
Fixes #50105. See #40330.

Location:
trunk/src
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/media/models/attachments.js

    r50068 r50829  
    349349    },
    350350    /**
     351
     352
     353
     354
     355
     356
     357
     358
     359
     360
     361
     362
     363
     364
     365
     366
     367
     368
    351369     * A custom Ajax-response parser.
    352370     *
    353371     * See trac ticket #24753
    354372     *
    355      * @param {Object|Array} resp The raw response Object/Array.
     373     * Called automatically by Backbone whenever a collection's models are returned
     374     * by the server, in fetch. The default implementation is a no-op, simply
     375     * passing through the JSON response. We override this to add attributes to
     376     * the collection items.
     377     *
     378     * Since WordPress 5.5, the response returns the attachments under `response.attachments`
     379     * and `response.totalAttachments` holds the total number of attachments found.
     380     *
     381     * @param {Object|Array} response The raw response Object/Array.
    356382     * @param {Object} xhr
    357383     * @return {Array} The array of model attributes to be added to the collection
    358384     */
    359     parse: function( resp, xhr ) {
    360         if ( ! _.isArray( resp ) ) {
    361             resp = [resp];
    362         }
    363 
    364         return _.map( resp, function( attrs ) {
     385    parse: function( response, xhr ) {
     386        if ( ! _.isArray( response.attachments ) ) {
     387            response = [response.attachments];
     388        }
     389
     390        this.totalAttachments = parseInt( response.totalAttachments, 10 );
     391
     392        return _.map( response.attachments, function( attrs ) {
    365393            var id, attachment, newAttributes;
    366394
  • trunk/src/js/media/models/query.js

    r50067 r50829  
    113113        options.remove = false;
    114114
    115         return this._more = this.fetch( options ).done( function( resp ) {
    116             if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
     115        return this._more = this.fetch( options ).done( function( response ) {
     116            // Since WordPress 5.5, the response returns the attachments under `response.attachments`.
     117            var attachments = response.attachments;
     118
     119            if ( _.isEmpty( attachments ) || -1 === this.args.posts_per_page || attachments.length < this.args.posts_per_page ) {
    117120                query._hasMore = false;
    118121            }
  • trunk/src/js/media/views/attachments.js

    r47122 r50829  
    11var View = wp.media.View,
    22    $ = jQuery,
    3     Attachments;
     3    Attachments,
     4    infiniteScrolling = wp.media.view.settings.infiniteScrolling;
    45
    56Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{
     
    3637
    3738        /**
     39
     40
    3841         * @param refreshSensitivity The time in milliseconds to throttle the scroll
    3942         *                           handler.
     
    5053         */
    5154        _.defaults( this.options, {
     55
    5256            refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
    5357            refreshThreshold:   3,
     
    8589        this.controller.on( 'library:selection:add', this.attachmentFocus, this );
    8690
    87         // Throttle the scroll handler and bind this.
    88         this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
    89 
    90         this.options.scrollElement = this.options.scrollElement || this.el;
    91         $( this.options.scrollElement ).on( 'scroll', this.scroll );
     91        if ( this.options.infiniteScrolling ) {
     92            // Throttle the scroll handler and bind this.
     93            this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
     94
     95            this.options.scrollElement = this.options.scrollElement || this.el;
     96            $( this.options.scrollElement ).on( 'scroll', this.scroll );
     97        }
    9298
    9399        this.initSortable();
     
    388394        } else {
    389395            this.views.unset();
    390             this.collection.more().done( this.scroll );
     396            if ( this.options.infiniteScrolling ) {
     397                this.collection.more().done( this.scroll );
     398            }
    391399        }
    392400    },
     
    401409     */
    402410    ready: function() {
    403         this.scroll();
     411        if ( this.options.infiniteScrolling ) {
     412            this.scroll();
     413        }
    404414    },
    405415
  • trunk/src/js/media/views/attachments/browser.js

    r49539 r50829  
    33    l10n = wp.media.view.l10n,
    44    $ = jQuery,
    5     AttachmentsBrowser;
     5    AttachmentsBrowser,
     6    infiniteScrolling = wp.media.view.settings.infiniteScrolling,
     7    __ = wp.i18n.__,
     8    sprintf = wp.i18n.sprintf;
    69
    710/**
     
    6972        }
    7073
    71 
    7274        // Add a heading before the attachments list.
    7375        this.createAttachmentsHeading();
    7476
    75         // Create the list of attachments.
    76         this.createAttachments();
     77        // Create the attachments wrapper view.
     78        this.createAttachmentsWrapperView();
     79
     80        if ( ! infiniteScrolling ) {
     81            this.$el.addClass( 'has-load-more' );
     82            this.createLoadMoreView();
     83        }
    7784
    7885        // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909.
     
    9299
    93100        this.collection.on( 'add remove reset', this.updateContent, this );
     101
     102
     103
     104
    94105
    95106        // The non-cached or cached attachments query has completed.
     
    107118     */
    108119    announceSearchResults: _.debounce( function() {
    109         var count;
     120        var count,
     121            /* translators: Accessibility text. %d: Number of attachments found in a search. */
     122            mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' );
     123
     124        if ( infiniteScrolling ) {
     125            /* translators: Accessibility text. %d: Number of attachments found in a search. */
     126            mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' );
     127        }
    110128
    111129        if ( this.collection.mirroring.args.s ) {
     
    118136
    119137            if ( this.collection.hasMore() ) {
    120                 wp.a11y.speak( l10n.mediaFoundHasMoreResults.replace( '%d', count ) );
     138                wp.a11y.speak( .replace( '%d', count ) );
    121139                return;
    122140            }
     
    393411
    394412        if ( this.controller.isModeActive( 'grid' ) ) {
     413
    395414            noItemsView = view.attachmentsNoResults;
    396415        } else {
     416
    397417            noItemsView = view.uploader;
    398418        }
     
    434454    },
    435455
     456
     457
     458
     459
     460
     461
     462
     463
     464
     465
     466
     467
     468
     469
     470
     471
     472
    436473    createAttachments: function() {
    437474        this.attachments = new wp.media.view.Attachments({
     
    452489        this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) );
    453490
    454         this.views.add( this.attachments );
    455 
     491        this.views.add( '.attachments-wrapper', this.attachments );
    456492
    457493        if ( this.controller.isModeActive( 'grid' ) ) {
     
    466502            this.views.add( this.attachmentsNoResults );
    467503        }
     504
     505
     506
     507
     508
     509
     510
     511
     512
     513
     514
     515
     516
     517
     518
     519
     520
     521
     522
     523
     524
     525
     526
     527
     528
     529
     530
     531
     532
     533
     534
     535
     536
     537
     538
     539
     540
     541
     542
     543
     544
     545
     546
     547
     548
     549
     550
     551
     552
     553
     554
     555
     556
     557
     558
     559
     560
     561
     562
     563
     564
     565
     566
     567
     568
     569
     570
     571
     572
     573
     574
     575
     576
     577
     578
     579
     580
     581
     582
     583
     584
     585
     586
     587
     588
     589
     590
     591
     592
     593
     594
     595
     596
     597
     598
     599
     600
     601
     602
     603
     604
     605
     606
     607
     608
     609
     610
     611
     612
     613
     614
     615
     616
     617
     618
     619
     620
     621
     622
     623
     624
     625
     626
     627
     628
     629
     630
     631
     632
     633
     634
     635
     636
     637
     638
     639
     640
     641
     642
     643
     644
     645
     646
     647
     648
     649
     650
     651
     652
     653
     654
    468655    },
    469656
  • trunk/src/wp-admin/css/media.css

    r50025 r50829  
    421421.media-frame.mode-grid,
    422422.media-frame.mode-grid .media-frame-content,
    423 .media-frame.mode-grid .attachments-browser .attachments,
     423.media-frame.mode-grid .attachments-browser:not(.has-load-more) .attachments,
     424.media-frame.mode-grid .attachments-browser.has-load-more .attachments-wrapper,
    424425.media-frame.mode-grid .uploader-inline-content {
    425426    position: static;
     
    499500}
    500501
    501 .media-frame.mode-select .attachments-browser.fixed .attachments {
     502.media-frame.mode-select .attachments-browser.fixed:not(.has-load-more) .attachments,
     503.media-frame.mode-select .attachments-browser.has-load-more.fixed .attachments-wrapper {
    502504    position: relative;
    503505    top: 94px; /* prevent jumping up when the toolbar becomes fixed */
  • trunk/src/wp-admin/includes/ajax-actions.php

    r50556 r50829  
    29942994    $posts = array_filter( $posts );
    29952995
    2996     wp_send_json_success( $posts );
     2996    $result = array(
     2997        'attachments'      => $posts,
     2998        'totalAttachments' => $query->found_posts,
     2999    );
     3000
     3001    wp_send_json_success( $result );
    29973002}
    29983003
  • trunk/src/wp-includes/css/media-views.css

    r50784 r50829  
    11891189}
    11901190
    1191 .attachments-browser .attachments,
     1191.attachments-browser:not(.has-load-more) .attachments,
     1192.attachments-browser.has-load-more .attachments-wrapper,
    11921193.attachments-browser .uploader-inline {
    11931194    position: absolute;
     
    12661267.attachments-browser .no-media {
    12671268    padding: 2em 0 0 2em;
     1269
     1270
     1271
     1272
     1273
     1274
     1275
     1276
     1277
     1278
     1279
     1280
     1281
     1282
     1283
     1284
     1285
     1286
     1287
     1288
     1289
     1290
     1291
     1292
     1293
     1294
     1295
     1296
     1297
     1298
     1299
     1300
     1301
     1302
     1303
     1304
     1305
     1306
     1307
     1308
     1309
     1310
     1311
     1312
     1313
     1314
     1315
     1316
     1317
     1318
     1319
     1320
     1321
     1322
     1323
     1324
     1325
     1326
     1327
     1328
     1329
     1330
     1331
     1332
     1333
     1334
     1335
     1336
     1337
     1338
     1339
     1340
     1341
     1342
     1343
     1344
     1345
     1346
     1347
     1348
     1349
     1350
     1351
     1352
     1353
     1354
     1355
     1356
     1357
     1358
    12681359}
    12691360
     
    28192910        display: none;
    28202911    }
     2912
     2913
     2914
     2915
     2916
     2917
    28212918}
    28222919
     
    28272924        padding-top: 44px;
    28282925    }
     2926
     2927
     2928
     2929
     2930
     2931
    28292932}
    28302933
  • trunk/src/wp-includes/media.php

    r50820 r50829  
    43074307    }
    43084308
     4309
     4310
     4311
     4312
     4313
     4314
     4315
     4316
     4317
    43094318    $settings = array(
    4310         'tabs'             => $tabs,
    4311         'tabUrl'           => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
    4312         'mimeTypes'        => wp_list_pluck( get_post_mime_types(), 0 ),
     4319        'tabs'             => $tabs,
     4320        'tabUrl'           => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
     4321        'mimeTypes'        => wp_list_pluck( get_post_mime_types(), 0 ),
    43134322        /** This filter is documented in wp-admin/includes/media.php */
    4314         'captions'         => ! apply_filters( 'disable_captions', '' ),
    4315         'nonce'            => array(
     4323        'captions'         => ! apply_filters( 'disable_captions', '' ),
     4324        'nonce'            => array(
    43164325            'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ),
    43174326        ),
    4318         'post'             => array(
     4327        'post'             => array(
    43194328            'id' => 0,
    43204329        ),
    4321         'defaultProps'     => $props,
    4322         'attachmentCounts' => array(
     4330        'defaultProps'     => $props,
     4331        'attachmentCounts' => array(
    43234332            'audio' => ( $show_audio_playlist ) ? 1 : 0,
    43244333            'video' => ( $show_video_playlist ) ? 1 : 0,
    43254334        ),
    4326         'oEmbedProxyUrl'   => rest_url( 'oembed/1.0/proxy' ),
    4327         'embedExts'        => $exts,
    4328         'embedMimes'       => $ext_mimes,
    4329         'contentWidth'     => $content_width,
    4330         'months'           => $months,
    4331         'mediaTrash'       => MEDIA_TRASH ? 1 : 0,
     4335        'oEmbedProxyUrl'    => rest_url( 'oembed/1.0/proxy' ),
     4336        'embedExts'         => $exts,
     4337        'embedMimes'        => $ext_mimes,
     4338        'contentWidth'      => $content_width,
     4339        'months'            => $months,
     4340        'mediaTrash'        => MEDIA_TRASH ? 1 : 0,
     4341        'infiniteScrolling' => ( $infinite_scrolling ) ? 1 : 0,
    43324342    );
    43334343
     
    44134423        'searchMediaLabel'            => __( 'Search media' ),          // Backward compatibility pre-5.3.
    44144424        'searchMediaPlaceholder'      => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3.
     4425
    44154426        'mediaFound'                  => __( 'Number of media items found: %d' ),
    4416         'mediaFoundHasMoreResults'    => __( 'Number of media items displayed: %d. Scroll the page for more results.' ),
    44174427        'noMedia'                     => __( 'No media items found.' ),
    44184428        'noMediaTryNewSearch'         => __( 'No media items found. Try a different search.' ),
Note: See TracChangeset for help on using the changeset viewer.