WordPress.org

Make WordPress Core

Changeset 48375


Ignore:
Timestamp:
07/07/2020 01:43:43 PM (3 months ago)
Author:
afercia
Message:

Accessibility: Media: Improve accessibility of the status and error messages in the Image Editor.

  • improves focus management by moving focus to the notices, if any, or to the first "tabbable" element
  • this avoids a focus loss and helps Braille-only and screen magnification users to be aware of the messages
  • adds an ARIA role alert to all the notices
  • uses wp.a11y.speak() to announce messages to assistive technology
  • this way, all visual users will see the messages while assistive technology users will get an audible message
  • uses wp.i18n for translatable strings in wp-admin/js/image-edit.js

Props anevins, ryanshoover, antpb, SergeyBiryukov, afercia.
See #20491.
Fixes #47147.

Location:
trunk/src
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/lib/image-edit.js

    r48265 r48375  
    66 */
    77
    8  /* global imageEditL10n, ajaxurl, confirm */
     8 /* global ajaxurl, confirm */
    99
    1010(function($) {
     11    var __ = wp.i18n.__;
    1112
    1213    /**
     
    138139        });
    139140
    140         $( document ).on( 'image-editor-image-loaded', this.focusManager );
     141        $( document ).on( 'image-editor-ui-ready', this.focusManager );
    141142    },
    142143
     
    145146     *
    146147     * @since 2.9.0
    147      *
    148      * @memberof imageEdit
    149      *
    150      * @param {number} postid The post ID.
    151      * @param {number} toggle Is 0 or 1, fades the icon in then 1 and out when 0.
     148     * @since 5.5.0 Added the triggerUIReady parameter.
     149     *
     150     * @memberof imageEdit
     151     *
     152     * @param {number}  postid         The post ID.
     153     * @param {number}  toggle         Is 0 or 1, fades the icon in when 1 and out when 0.
     154     * @param {boolean} triggerUIReady Whether to trigger a custom event when the UI is ready. Default false.
    152155     *
    153156     * @return {void}
    154157     */
    155     toggleEditor : function(postid, toggle) {
     158    toggleEditor: function( postid, toggle, triggerUIReady ) {
    156159        var wait = $('#imgedit-wait-' + postid);
    157160
     
    159162            wait.fadeIn( 'fast' );
    160163        } else {
    161             wait.fadeOut('fast');
     164            wait.fadeOut( 'fast', function() {
     165                if ( triggerUIReady ) {
     166                    $( document ).trigger( 'image-editor-ui-ready' );
     167                }
     168            } );
    162169        }
    163170    },
     
    403410                t.toggleEditor(postid, 0);
    404411            })
    405             .on('error', function() {
    406                 $('#imgedit-crop-' + postid).empty().append('<div class="error"><p>' + imageEditL10n.error + '</p></div>');
    407                 t.toggleEditor(postid, 0);
    408             })
     412            .on( 'error', function() {
     413                var errorMessage = __( 'Could not load the preview image. Please reload the page and try again.' );
     414
     415                $( '#imgedit-crop-' + postid )
     416                    .empty()
     417                    .append( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' );
     418
     419                t.toggleEditor( postid, 0, true );
     420                wp.a11y.speak( errorMessage, 'assertive' );
     421            } )
    409422            .attr('src', ajaxurl + '?' + $.param(data));
    410423    },
     
    467480
    468481        t.toggleEditor(postid, 1);
    469         $.post(ajaxurl, data, function(r) {
    470             $('#image-editor-' + postid).empty().append(r);
    471             t.toggleEditor(postid, 0);
     482        $.post( ajaxurl, data, function( response ) {
     483            $( '#image-editor-' + postid ).empty().append( response.data.html );
     484            t.toggleEditor( postid, 0, true );
    472485            // Refresh the attachment model so that changes propagate.
    473486            if ( t._view ) {
    474487                t._view.refresh();
    475488            }
    476         });
     489        } ).done( function( response ) {
     490            // Whether the executed action was `scale` or `restore`, the response does have a message.
     491            if ( response && response.data.message.msg ) {
     492                wp.a11y.speak( response.data.message.msg );
     493                return;
     494            }
     495
     496            if ( response && response.data.message.error ) {
     497                wp.a11y.speak( response.data.message.error );
     498            }
     499        } );
    477500    },
    478501
     
    512535        };
    513536        // Post the image edit data to the backend.
    514         $.post(ajaxurl, data, function(r) {
    515             // Read the response.
    516             var ret = JSON.parse(r);
    517 
     537        $.post( ajaxurl, data, function( response ) {
    518538            // If a response is returned, close the editor and show an error.
    519             if ( ret.error ) {
    520                 $('#imgedit-response-' + postid).html('<div class="error"><p>' + ret.error + '</p></div>');
     539            if ( response.data.error ) {
     540                $( '#imgedit-response-' + postid )
     541                    .html( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + response.data.error + '</p></div>' );
     542
    521543                imageEdit.close(postid);
     544                wp.a11y.speak( response.data.error );
    522545                return;
    523546            }
    524547
    525             if ( ret.fw && ret.fh ) {
    526                 $('#media-dims-' + postid).html( ret.fw + ' &times; ' + ret.fh );
    527             }
    528 
    529             if ( ret.thumbnail ) {
    530                 $('.thumbnail', '#thumbnail-head-' + postid).attr('src', ''+ret.thumbnail);
    531             }
    532 
    533             if ( ret.msg ) {
    534                 $('#imgedit-response-' + postid).html('<div class="updated"><p>' + ret.msg + '</p></div>');
     548            if ( response.data.fw && response.data.fh ) {
     549                $( '#media-dims-' + postid ).html( response.data.fw + ' &times; ' + response.data.fh );
     550            }
     551
     552            if ( response.data.thumbnail ) {
     553                $( '.thumbnail', '#thumbnail-head-' + postid ).attr( 'src', '' + response.data.thumbnail );
     554            }
     555
     556            if ( response.data.msg ) {
     557                $( '#imgedit-response-' + postid )
     558                    .html( '<div class="notice notice-success" tabindex="-1" role="alert"><p>' + response.data.msg + '</p></div>' );
     559
     560                wp.a11y.speak( response.data.msg );
    535561            }
    536562
     
    560586        this._view = view;
    561587
    562         var dfd, data, elem = $('#image-editor-' + postid), head = $('#media-head-' + postid),
    563             btn = $('#imgedit-open-btn-' + postid), spin = btn.siblings('.spinner');
     588        var dfd, data,
     589            elem = $( '#image-editor-' + postid ),
     590            head = $( '#media-head-' + postid ),
     591            btn = $( '#imgedit-open-btn-' + postid ),
     592            spin = btn.siblings( '.spinner' );
    564593
    565594        /*
     
    580609        };
    581610
    582         dfd = $.ajax({
     611        dfd = $.ajax( {
    583612            url:  ajaxurl,
    584613            type: 'post',
     
    587616                btn.addClass( 'button-activated' );
    588617            }
    589         }).done(function( html ) {
    590             elem.html( html );
    591             head.fadeOut('fast', function(){
    592                 elem.fadeIn('fast');
     618        } ).done( function( response ) {
     619            var errorMessage;
     620
     621            if ( '-1' === response ) {
     622                errorMessage = __( 'Could not load the preview image.' );
     623                elem.html( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' );
     624            }
     625
     626            if ( response.data && response.data.html ) {
     627                elem.html( response.data.html );
     628            }
     629
     630            head.fadeOut( 'fast', function() {
     631                elem.fadeIn( 'fast', function() {
     632                    if ( errorMessage ) {
     633                        $( document ).trigger( 'image-editor-ui-ready' );
     634                    }
     635                } );
    593636                btn.removeClass( 'button-activated' );
    594637                spin.removeClass( 'is-active' );
    595             });
     638            } );
    596639            // Initialise the Image Editor now that everything is ready.
    597640            imageEdit.init( postid );
    598         });
     641        } );
    599642
    600643        return dfd;
     
    623666        this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0, 'width': img.innerWidth(), 'height': img.innerHeight() } );
    624667
    625         this.toggleEditor(postid, 0);
    626 
    627         $( document ).trigger( 'image-editor-image-loaded' );
     668        this.toggleEditor( postid, 0, true );
    628669    },
    629670
     
    637678    focusManager: function() {
    638679        /*
    639          * Editor is ready, move focus to the first focusable element. Since the
    640          * DOM update is pretty large, the timeout helps browsers update their
     680         * Editor is ready. Move focus to one of the admin alert notices displayed
     681         * after a user action or to the first focusable element. Since the DOM
     682         * update is pretty large, the timeout helps browsers update their
    641683         * accessibility tree to better support assistive technologies.
    642684         */
    643685        setTimeout( function() {
    644             $( '.imgedit-wrap' ).find( ':tabbable:first' ).focus();
     686            var elementToSetFocusTo = $( '.notice[role="alert"]' );
     687
     688            if ( ! elementToSetFocusTo.length ) {
     689                elementToSetFocusTo = $( '.imgedit-wrap' ).find( ':tabbable:first' );
     690            }
     691
     692            elementToSetFocusTo.focus();
    645693        }, 100 );
    646694    },
  • trunk/src/wp-admin/includes/ajax-actions.php

    r48373 r48375  
    26082608        case 'save':
    26092609            $msg = wp_save_image( $attachment_id );
    2610             $msg = wp_json_encode( $msg );
    2611             wp_die( $msg );
     2610            if ( $msg->error ) {
     2611                wp_send_json_error( $msg );
     2612            }
     2613
     2614            wp_send_json_success( $msg );
    26122615            break;
    26132616        case 'scale':
     
    26192622    }
    26202623
     2624    ob_start();
    26212625    wp_image_editor( $attachment_id, $msg );
    2622     wp_die();
     2626    $html = ob_get_clean();
     2627
     2628    if ( $msg->error ) {
     2629        wp_send_json_error(
     2630            array(
     2631                'message' => $msg,
     2632                'html'    => $html,
     2633            )
     2634        );
     2635    }
     2636
     2637    wp_send_json_success(
     2638        array(
     2639            'message' => $msg,
     2640            'html'    => $html,
     2641        )
     2642    );
    26232643}
    26242644
  • trunk/src/wp-admin/includes/image-edit.php

    r48265 r48375  
    3939    if ( $msg ) {
    4040        if ( isset( $msg->error ) ) {
    41             $note = "<div class='error'><p>$msg->error</p></div>";
     41            $note = "<div class='notice notice-error' tabindex='-1' role='alert'><p>$msg->error</p></div>";
    4242        } elseif ( isset( $msg->msg ) ) {
    43             $note = "<div class='updated'><p>$msg->msg</p></div>";
     43            $note = "<div class='notice notice-success' tabindex='-1' role='alert'><p>$msg->msg</p></div>";
    4444        }
    4545    }
     
    104104    <div class="imgedit-group-top">
    105105        <h2><?php _e( 'Scale Image' ); ?></h2>
    106         <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Scale Image Help' ); ?></span></button>
     106        <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Scale Image Help' ); ?></span></button>
    107107        <div class="imgedit-help">
    108108        <p><?php _e( 'You can proportionally scale the original image. For best results, scaling should be done before you crop, flip, or rotate. Images can only be scaled down, not up.' ); ?></p>
     
    142142    <div class="imgedit-group">
    143143    <div class="imgedit-group-top">
    144         <h2><button type="button" onclick="imageEdit.toggleHelp(this);" class="button-link"><?php _e( 'Restore Original Image' ); ?> <span class="dashicons dashicons-arrow-down imgedit-help-toggle"></span></button></h2>
     144        <h2><button type="button" onclick="imageEdit.toggleHelp(this);" class="button-link" aria-expanded="false"><?php _e( 'Restore original image' ); ?> <span class="dashicons dashicons-arrow-down imgedit-help-toggle"></span></button></h2>
    145145        <div class="imgedit-help imgedit-restore">
    146146        <p>
     
    165165    <div class="imgedit-group-top">
    166166        <h2><?php _e( 'Image Crop' ); ?></h2>
    167         <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Image Crop Help' ); ?></span></button>
     167        <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Image Crop Help' ); ?></span></button>
    168168
    169169        <div class="imgedit-help">
     
    210210    <div class="imgedit-group-top">
    211211        <h2><?php _e( 'Thumbnail Settings' ); ?></h2>
    212         <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Thumbnail Settings Help' ); ?></span></button>
     212        <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Thumbnail Settings Help' ); ?></span></button>
    213213        <div class="imgedit-help">
    214214        <p><?php _e( 'You can edit the image while preserving the thumbnail. For example, you may wish to have a square thumbnail that displays just a section of the image.' ); ?></p>
  • trunk/src/wp-includes/css/media-views.css

    r48368 r48375  
    19991999}
    20002000
    2001 .media-modal .imgedit-wrap div.updated {
     2001.media-modal .imgedit-wrap div.updated, /* Back-compat for pre-5.5 */
     2002.media-modal .imgedit-wrap .notice {
    20022003    margin: 0;
    20032004    margin-bottom: 16px;
  • trunk/src/wp-includes/script-loader.php

    r48373 r48375  
    13771377        );
    13781378
    1379         $scripts->add( 'image-edit', "/wp-admin/js/image-edit$suffix.js", array( 'jquery', 'jquery-ui-core', 'json2', 'imgareaselect' ), false, 1 );
    1380         did_action( 'init' ) && $scripts->localize(
    1381             'image-edit',
    1382             'imageEditL10n',
    1383             array(
    1384                 'error' => __( 'Could not load the preview image. Please reload the page and try again.' ),
    1385             )
    1386         );
     1379        $scripts->add( 'image-edit', "/wp-admin/js/image-edit$suffix.js", array( 'jquery', 'jquery-ui-core', 'json2', 'imgareaselect', 'wp-a11y' ), false, 1 );
     1380        $scripts->set_translations( 'image-edit' );
    13871381
    13881382        $scripts->add( 'set-post-thumbnail', "/wp-admin/js/set-post-thumbnail$suffix.js", array( 'jquery' ), false, 1 );
Note: See TracChangeset for help on using the changeset viewer.