Skip to content

Commit

Permalink
MDL-71674 editor_atto: more assessible errors
Browse files Browse the repository at this point in the history
  • Loading branch information
dcai committed Sep 1, 2021
1 parent 6259988 commit 1da0e42
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 80 deletions.
Expand Up @@ -21,8 +21,11 @@ Feature: Atto accessibility checker
And I click on ".moodle-dialogue-focused .closebutton" "css_element"
And I select the text in the "Description" Atto editor
And I click on "Insert or edit image" "button"
And I set the field "Enter URL" to "/decorative-image.png"
And I set the field "Describe this image for someone who cannot see it" to ""
And I set the field "This image is decorative only" to "1"
And I set the field "Width" to "1"
And I set the field "Height" to "1"
And I click on "This image is decorative only" "checkbox"
And I press "Save image"
And I press "Accessibility checker"
And I should see "Congratulations, no accessibility problems found!"
Expand Down
1 change: 1 addition & 0 deletions lib/editor/atto/plugins/image/lang/en/atto_image.php
Expand Up @@ -39,6 +39,7 @@
$string['presentation'] = 'This image is decorative only';
$string['pluginname'] = 'Insert or edit image';
$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
$string['imageurlrequired'] = 'An image must have a URL.';
$string['preview'] = 'Preview';
$string['saveimage'] = 'Save image';
$string['size'] = 'Size';
Expand Down
1 change: 1 addition & 0 deletions lib/editor/atto/plugins/image/lib.php
Expand Up @@ -47,6 +47,7 @@ function atto_image_strings_for_js() {
'height',
'presentation',
'presentationoraltrequired',
'imageurlrequired',
'size',
'width',
'uploading',
Expand Down
24 changes: 23 additions & 1 deletion lib/editor/atto/plugins/image/tests/behat/image.feature
Expand Up @@ -58,7 +58,8 @@ Feature: Add images to Atto
When I click on "Insert or edit image" "button"
Then the field "Enter URL" matches value "/nothing/here"
And I set the field "Describe this image for someone who cannot see it" to "Something"
And I set the field "Enter URL" to ""
And I set the field "Width" to "1"
And I set the field "Height" to "1"
And I press "Save image"
And I set the field "Description" to "<p>Image: <img src='/nothing/again' width='123' height='456' alt='Awesome!'>.</p>"
And I press "Update profile"
Expand All @@ -69,3 +70,24 @@ Feature: Add images to Atto
And the field "Width" matches value "123"
And the field "Height" matches value "456"
And the field "Describe this image" matches value "Awesome!"

@javascript
Scenario: Error handling when inserting an image manually
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Image: <img src='/nothing/here'>.</p>"
And I select the text in the "Description" Atto editor
When I click on "Insert or edit image" "button"
Then the field "Enter URL" matches value "/nothing/here"
And I set the field "Describe this image for someone who cannot see it" to ""
And I take focus off "Describe this image for someone who cannot see it" "field"
And I should see "An image must have a description, unless it is marked as decorative only."
And I set the field "Describe this image for someone who cannot see it" to "Something"
And I set the field "Enter URL" to ""
And I press "Save image"
And I should see "An image must have a URL."
And I set the field "Enter URL" to "/nothing/here"
And I set the field "Width" to "1"
And I set the field "Height" to "1"
And I press "Save image"
And I press "Update profile"
Expand Up @@ -43,6 +43,7 @@ var CSS = {
INPUTSIZE: 'atto_image_size',
INPUTWIDTH: 'atto_image_widthentry',
IMAGEALTWARNING: 'atto_image_altwarning',
IMAGEURLWARNING: 'atto_image_urlwarning',
IMAGEBROWSER: 'openimagebrowser',
IMAGEPRESENTATION: 'atto_image_presentation',
INPUTCONSTRAIN: 'atto_image_constrain',
Expand All @@ -51,6 +52,10 @@ var CSS = {
IMAGEPREVIEWBOX: 'atto_image_preview_box',
ALIGNSETTINGS: 'atto_image_button'
},
FORMNAMES = {
URL: 'urlentry',
ALT: 'altentry'
},
SELECTORS = {
INPUTURL: '.' + CSS.INPUTURL
},
Expand Down Expand Up @@ -96,13 +101,17 @@ var CSS = {

TEMPLATE = '' +
'<form class="atto_form">' +

// Add the repository browser button.
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEURLWARNING}}">' +
'<label for="{{elementid}}_{{CSS.INPUTURL}}">' +
'{{get_string "imageurlrequired" component}}' +
'</label>' +
'</div>' +
'{{#if showFilepicker}}' +
'<div class="mb-1">' +
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
'<div class="input-group input-append w-100">' +
'<input class="form-control {{CSS.INPUTURL}}" type="url" ' +
'<input name="{{FORMNAMES.URL}}" class="form-control {{CSS.INPUTURL}}" type="url" ' +
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
'<span class="input-group-append">' +
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
Expand All @@ -113,19 +122,21 @@ var CSS = {
'{{else}}' +
'<div class="mb-1">' +
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
'<input class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
'<input name="{{FORMNAMES.URL}}" class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
'</div>' +
'{{/if}}' +

// Add the Alt box.
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
'{{get_string "presentationoraltrequired" component}}' +
'</label>' +
'</div>' +
// Add the Alt box.
'<div class="mb-1">' +
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
'id="{{elementid}}_{{CSS.INPUTALT}}" maxlength="125"></textarea>' +
'id="{{elementid}}_{{CSS.INPUTALT}}" name="{{FORMNAMES.ALT}}" maxlength="125"></textarea>' +

// Add the character count.
'<div id="the-count" class="d-flex justify-content-end small">' +
Expand Down Expand Up @@ -252,7 +263,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
this.editor.on('paste', this._handlePaste, this);
this.editor.on('drop', this._handleDragDrop, this);

// e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
// ...e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
this.editor.on('dragover', function(e) {
e.preventDefault();
}, this);
Expand Down Expand Up @@ -602,6 +613,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
content = Y.Node.create(template({
elementid: this.get('host').get('elementid'),
CSS: CSS,
FORMNAMES: FORMNAMES,
component: COMPONENTNAME,
showFilepicker: canShowFilepicker,
alignments: ALIGNMENTS
Expand All @@ -613,16 +625,16 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
this._applyImageProperties(this._form);

this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._hasErrorAltField, this);
this._form.one('.' + CSS.INPUTALT).on('blur', this._hasErrorAltField, this);
this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
if (event.target.get('checked')) {
this._autoAdjustSize(event);
}
}, this);
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);

if (canShowFilepicker) {
Expand Down Expand Up @@ -1049,6 +1061,38 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
return CSS.ALIGNSETTINGS + '_' + alignment;
},

_toggleVisibility: function(selector, predicate) {
var form = this._form;
var element = form.all(selector);
element.setStyle('display', predicate ? 'block' : 'none');
},

_toggleAriaInvalid: function(selectors, predicate) {
var form = this._form;
selectors.forEach(function(selector) {
var element = form.all(selector);
element.setAttribute('aria-invalid', predicate);
});
},

_hasErrorUrlField: function() {
var form = this._form;
var url = form.one('.' + CSS.INPUTURL).get('value');
var urlerror = url === '';
this._toggleVisibility('.' + CSS.IMAGEURLWARNING, urlerror);
this._toggleAriaInvalid(['.' + CSS.INPUTURL], urlerror);
return urlerror;
},

_hasErrorAltField: function() {
var form = this._form;
var alt = form.one('.' + CSS.INPUTALT).get('value');
var presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
var imagealterror = alt === '' && !presentation;
this._toggleVisibility('.' + CSS.IMAGEALTWARNING, imagealterror);
this._toggleAriaInvalid(['.' + CSS.INPUTALT, '.' + CSS.IMAGEPRESENTATION], imagealterror);
return imagealterror;
},
/**
* Update the alt text warning live.
*
Expand All @@ -1057,23 +1101,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
* @private
*/
_updateWarning: function() {
var form = this._form,
state = true,
alt = form.one('.' + CSS.INPUTALT).get('value'),
presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
if (alt === '' && !presentation) {
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'block');
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', true);
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', true);
state = true;
} else {
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'none');
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', false);
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', false);
state = false;
}
var urlerror = this._hasErrorUrlField();
var imagealterror = this._hasErrorAltField();
var haserrors = urlerror || imagealterror;
this.getDialogue().centerDialogue();
return state;
return haserrors;
},

/**
Expand Down

0 comments on commit 1da0e42

Please sign in to comment.