Skip to content

Commit

Permalink
Merge pull request #11880 from ckeditor/ck/11837-missing-labels-for-n…
Browse files Browse the repository at this point in the history
…ested-editables

Fix (image,widget): Allowed setting an accessible label in the `toWidgetEditable()` helper. Added missing labels for assistive technologies to image captions. Closes #11837.
  • Loading branch information
oleq authored Jul 11, 2022
2 parents 1c2f249 + d69b194 commit be97052
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 103 deletions.
4 changes: 3 additions & 1 deletion packages/ckeditor5-ckbox/tests/ckboxediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -1017,9 +1017,11 @@ describe( 'CKBoxEditing', () => {
'<img src="/assets/sample.png"></img>' +
'</picture>' +
'<figcaption ' +
'aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" ' +
'data-placeholder="Enter image caption">' +
'data-placeholder="Enter image caption" ' +
'role="textbox">' +
'<a data-ckbox-resource-id="link-id" href="/assets/sample.png">Text of the caption</a>' +
'</figcaption>' +
'</figure>'
Expand Down
4 changes: 3 additions & 1 deletion packages/ckeditor5-image/lang/contexts.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@
"Insert": "The label of the form submit button if the image source URL input has no value.",
"Update": "The label of the form submit button if the image source URL input has a value.",
"Insert image via URL": "The input label for the Insert image via URL form.",
"Update image URL": "The input label for the Insert image via URL form for a pre-existing image."
"Update image URL": "The input label for the Insert image via URL form for a pre-existing image.",
"Caption for the image": "Text used by screen readers do describe an image when the image has no text alternative.",
"Caption for image: %0": "Text used by screen readers do describe an image when there is a text alternative available, e.g. 'Caption for image: this is a description of the image.'"
}
41 changes: 40 additions & 1 deletion packages/ckeditor5-image/src/imagecaption/imagecaptionediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default class ImageCaptionEditing extends Plugin {

this._setupConversion();
this._setupImageTypeCommandsIntegration();
this._registerCaptionReconversion();
}

/**
Expand Down Expand Up @@ -132,7 +133,10 @@ export default class ImageCaptionEditing extends Plugin {
keepOnFocus: true
} );

return toWidgetEditable( figcaptionElement, writer );
const imageAlt = modelElement.parent.getAttribute( 'alt' );
const label = imageAlt ? t( 'Caption for image: %0', [ imageAlt ] ) : t( 'Caption for the image' );

return toWidgetEditable( figcaptionElement, writer, { label } );
}
} );
}
Expand Down Expand Up @@ -242,4 +246,39 @@ export default class ImageCaptionEditing extends Plugin {
_saveCaption( imageModelElement, caption ) {
this._savedCaptionsMap.set( imageModelElement, caption.toJSON() );
}

/**
* Reconverts image caption when image alt attribute changes.
* The change of alt attribute is reflected in caption's aria-label attribute.
*
* @private
*/
_registerCaptionReconversion() {
const editor = this.editor;
const model = editor.model;
const imageUtils = editor.plugins.get( 'ImageUtils' );
const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );

model.document.on( 'change:data', () => {
const changes = model.document.differ.getChanges();

for ( const change of changes ) {
if ( change.attributeKey !== 'alt' ) {
continue;
}

const image = change.range.start.nodeAfter;

if ( imageUtils.isBlockImage( image ) ) {
const caption = imageCaptionUtils.getCaptionFromImageModelElement( image );

if ( !caption ) {
return;
}

editor.editing.reconvertItem( caption );
}
}
} );
}
}
138 changes: 96 additions & 42 deletions packages/ckeditor5-image/tests/imagecaption/imagecaptionediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
'<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'Foo bar baz.' +
'</figcaption>' +
'</figure>'
Expand Down Expand Up @@ -256,8 +256,8 @@ describe( 'ImageCaptionEditing', () => {
'<p>{}foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'foo bar' +
'</figcaption>' +
'</figure>'
Expand All @@ -278,8 +278,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>{}foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'</figcaption>' +
'</figure>'
);
Expand All @@ -299,8 +300,8 @@ describe( 'ImageCaptionEditing', () => {
'<p>{}foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">baz</figcaption>' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">baz</figcaption>' +
'</figure>'
);
} );
Expand Down Expand Up @@ -335,8 +336,9 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
'<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable highlight-yellow" ' +
'contenteditable="true" data-foo="yellow" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable highlight-yellow" ' +
'contenteditable="true" data-foo="yellow" data-placeholder="Enter image caption" role="textbox">' +
'Foo bar baz.' +
'</figcaption>' +
'</figure>'
Expand All @@ -349,8 +351,8 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
'<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'Foo bar baz.' +
'</figcaption>' +
'</figure>'
Expand Down Expand Up @@ -448,8 +450,8 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-widget image" contenteditable="false">' +
'<img alt="" src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'foo bar' +
'</figcaption>' +
'</figure>]' +
Expand Down Expand Up @@ -477,8 +479,9 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-widget image" contenteditable="false">' +
'<img alt="" src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption"></figcaption>' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox"></figcaption>' +
'</figure>]' +
'<p></p>'
);
Expand Down Expand Up @@ -528,8 +531,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>foo</p>' +
'[<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'</figcaption>' +
'</figure>]'
);
Expand All @@ -542,8 +546,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>{}foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'</figcaption>' +
'</figure>'
);
Expand All @@ -558,8 +563,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>foo</p>' +
'[<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'</figcaption>' +
'</figure>]'
);
Expand All @@ -577,9 +583,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption ' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-editor__nested-editable_focused ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">[]' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">[]' +
'</figcaption>' +
'</figure>'
);
Expand All @@ -592,8 +598,8 @@ describe( 'ImageCaptionEditing', () => {
'<p>foo</p>' +
'[<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">foo bar</figcaption>' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">foo bar</figcaption>' +
'</figure>]'
);
} );
Expand All @@ -609,8 +615,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>{}foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'</figcaption>' +
'</figure>'
);
Expand All @@ -626,8 +633,9 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view ) ).to.equal(
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'[]' +
'</figcaption>' +
'</figure>'
Expand All @@ -646,8 +654,9 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption"></figcaption>' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox"></figcaption>' +
'</figure>]'
);
} );
Expand All @@ -665,13 +674,14 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view ) ).to.equal(
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">foo bar</figcaption>' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">foo bar</figcaption>' +
'</figure>' +
'[<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption"></figcaption>' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox"></figcaption>' +
'</figure>]'
);
} );
Expand All @@ -684,8 +694,9 @@ describe( 'ImageCaptionEditing', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-widget image" contenteditable="false">' +
'<img src="img.png"></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="false" data-placeholder="Enter image caption"></figcaption>' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="false" data-placeholder="Enter image caption" role="textbox"></figcaption>' +
'</figure>]'
);
} );
Expand All @@ -709,8 +720,9 @@ describe( 'ImageCaptionEditing', () => {
'<p>{}foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" ' +
'class="ck-editor__editable ck-editor__nested-editable ck-placeholder" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'</figcaption>' +
'</figure>'
);
Expand All @@ -722,8 +734,8 @@ describe( 'ImageCaptionEditing', () => {
'<p>foo</p>' +
'<figure class="ck-widget image" contenteditable="false">' +
'<img src=""></img>' +
'<figcaption class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption">' +
'<figcaption aria-label="Caption for the image" class="ck-editor__editable ck-editor__nested-editable" ' +
'contenteditable="true" data-placeholder="Enter image caption" role="textbox">' +
'{foo bar baz}' +
'</figcaption>' +
'</figure>'
Expand All @@ -748,5 +760,47 @@ describe( 'ImageCaptionEditing', () => {
expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph>' );
} );
} );

it( 'should reflect the change of image\'s alt attribute in caption\'s aria-label attribute', () => {
setModelData( model, '[<imageBlock alt="foo" src="img.png"><caption></caption></imageBlock>]' );

expect( view.document.getRoot().getChild( 0 ).getChild( 1 ).getAttribute( 'aria-label' ) ).to.equal( 'Caption for image: foo' );

const image = doc.getRoot().getChild( 0 );

model.change( writer => {
writer.setAttribute( 'alt', 'bar', image );
} );

expect( view.document.getRoot().getChild( 0 ).getChild( 1 ).getAttribute( 'aria-label' ) ).to.equal( 'Caption for image: bar' );
} );

it( 'should not try to update caption\'s aria-label attribute if the image does not have a caption', () => {
setModelData( model, '[<imageBlock alt="foo" src="img.png"></imageBlock>]' );

const image = doc.getRoot().getChild( 0 );

const spy = sinon.spy( editor.editing, 'reconvertItem' );

model.change( writer => {
writer.setAttribute( 'alt', 'bar', image );
} );

expect( spy.called ).to.be.false;
} );

it( 'should not try to update caption\'s aria-label attribute if the image is not an imageBlock', () => {
setModelData( model, '<paragraph>[<imageInline alt="foo" src="img.png"></imageInline>]</paragraph>' );

const image = doc.getRoot().getChild( 0 );

const spy = sinon.spy( editor.editing, 'reconvertItem' );

model.change( writer => {
writer.setAttribute( 'alt', 'bar', image );
} );

expect( spy.called ).to.be.false;
} );
} );
} );
Loading

0 comments on commit be97052

Please sign in to comment.