Skip to content

Commit

Permalink
Merge pull request #12675 from ckeditor/ck/11936-type-around-feature-…
Browse files Browse the repository at this point in the history
…is-missing-aria-tags

Fix (widget): Screen readers should now read the keyboard shortcuts to type around a widget. Closes #11936.
  • Loading branch information
niegowski committed Nov 10, 2022
2 parents 5faf012 + 070bcd6 commit ccc1611
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 29 deletions.
8 changes: 6 additions & 2 deletions packages/ckeditor5-image/tests/image.js
Expand Up @@ -74,7 +74,9 @@ describe( 'Image', () => {
);

expect( viewDocument.selection.isFake ).to.be.true;
expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'alt text image widget' );
expect( viewDocument.selection.fakeSelectionLabel ).to.equal(
'alt text image widget. Press Enter to type after or press Shift + Enter to type before the widget'
);
} );

it( 'should create proper fake selection label when alt attribute is empty', () => {
Expand All @@ -91,7 +93,9 @@ describe( 'Image', () => {
);

expect( viewDocument.selection.isFake ).to.be.true;
expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'image widget' );
expect( viewDocument.selection.fakeSelectionLabel ).to.equal(
'image widget. Press Enter to type after or press Shift + Enter to type before the widget'
);
} );

it( 'should remove selected class from previously selected element', () => {
Expand Down
23 changes: 12 additions & 11 deletions packages/ckeditor5-widget/_src/utils.js
Expand Up @@ -115,6 +115,8 @@ export function toWidget( element, writer, options = {} ) {
writer.setCustomProperty( 'widget', true, element );
element.getFillerOffset = getFillerOffset;

writer.setCustomProperty( 'widgetLabel', [], element );

if ( options.label ) {
setLabel( element, options.label, writer );
}
Expand Down Expand Up @@ -196,11 +198,10 @@ export function setHighlightHandling( element, writer, add = addHighlight, remov
* {@link ~getLabel `getLabel()`}.
*
* @param {module:engine/view/element~Element} element
* @param {String|Function} labelOrCreator
* @param {module:engine/view/downcastwriter~DowncastWriter} writer
* @param {string|Function} labelOrCreator
*/
export function setLabel( element, labelOrCreator, writer ) {
writer.setCustomProperty( 'widgetLabel', labelOrCreator, element );
export function setLabel( element, labelOrCreator ) {
element.getCustomProperty( 'widgetLabel' ).push( labelOrCreator );
}

/**
Expand All @@ -210,13 +211,13 @@ export function setLabel( element, labelOrCreator, writer ) {
* @returns {String}
*/
export function getLabel( element ) {
const labelCreator = element.getCustomProperty( 'widgetLabel' );

if ( !labelCreator ) {
return '';
}

return typeof labelCreator == 'function' ? labelCreator() : labelCreator;
return element.getCustomProperty( 'widgetLabel' ).reduce( ( prev, current ) => {
if ( typeof current === 'function' ) {
return prev ? prev + '. ' + current() : current();
} else {
return prev ? prev + '. ' + current : current;
}
}, '' );
}

/**
Expand Down
Expand Up @@ -232,6 +232,10 @@ export default class WidgetTypeAround extends Plugin {
// Filter out non-widgets and inline widgets.
if ( isTypeAroundWidget( viewElement, data.item, schema ) ) {
injectUIIntoWidget( conversionApi.writer, buttonTitles, viewElement );

viewElement.getCustomProperty( 'widgetLabel' ).push( () => {
return this.isEnabled ? t( 'Press Enter to type after or press Shift + Enter to type before the widget' ) : '';
} );
}
}, { priority: 'low' } );
}
Expand Down
27 changes: 16 additions & 11 deletions packages/ckeditor5-widget/src/utils.ts
Expand Up @@ -136,8 +136,10 @@ export function toWidget(
writer.setCustomProperty( 'widget', true, element );
element.getFillerOffset = getFillerOffset;

writer.setCustomProperty( 'widgetLabel', [], element );

if ( options.label ) {
setLabel( element, options.label, writer );
setLabel( element, options.label );
}

if ( options.hasSelectionHandle ) {
Expand Down Expand Up @@ -225,11 +227,12 @@ export function setHighlightHandling(
* {@link ~getLabel `getLabel()`}.
*
* @param {module:engine/view/element~Element} element
* @param {String|Function} labelOrCreator
* @param {module:engine/view/downcastwriter~DowncastWriter} writer
* @param {string|Function} labelOrCreator
*/
export function setLabel( element: Element, labelOrCreator: string | ( () => string ), writer: DowncastWriter ): void {
writer.setCustomProperty( 'widgetLabel', labelOrCreator, element );
export function setLabel( element: Element, labelOrCreator: string | ( () => string ) ): void {
const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array<string | ( () => string )>;

widgetLabel.push( labelOrCreator );
}

/**
Expand All @@ -239,13 +242,15 @@ export function setLabel( element: Element, labelOrCreator: string | ( () => str
* @returns {String}
*/
export function getLabel( element: Element ): string {
const labelCreator = element.getCustomProperty( 'widgetLabel' ) as string | ( () => string ) | undefined;
const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array<string | ( () => string )>;

if ( !labelCreator ) {
return '';
}

return typeof labelCreator == 'function' ? labelCreator() : labelCreator;
return widgetLabel.reduce( ( prev: string, current: string | ( () => string ) ) => {
if ( typeof current === 'function' ) {
return prev ? prev + '. ' + current() : current();
} else {
return prev ? prev + '. ' + current : current;
}
}, '' );
}

/**
Expand Down
Expand Up @@ -256,9 +256,19 @@ export default class WidgetTypeAround extends Plugin {
editor.editing.downcastDispatcher.on<DowncastInsertEvent<Element>>( 'insert', ( evt, data, conversionApi ) => {
const viewElement = conversionApi.mapper.toViewElement( data.item );

if ( !viewElement ) {
return;
}

// Filter out non-widgets and inline widgets.
if ( isTypeAroundWidget( viewElement, data.item, schema ) ) {
injectUIIntoWidget( conversionApi.writer, buttonTitles, viewElement! );

const widgetLabel = viewElement.getCustomProperty( 'widgetLabel' ) as Array<string | ( () => string )>;

widgetLabel.push( () => {
return this.isEnabled ? t( 'Press Enter to type after or press Shift + Enter to type before the widget' ) : '';
} );
}
}, { priority: 'low' } );
}
Expand Down Expand Up @@ -922,7 +932,8 @@ function injectButtons( wrapperDomElement: HTMLElement, buttonTitles: { before:
'ck-widget__type-around__button',
`ck-widget__type-around__button_${ position }`
],
title: buttonTitles[ position ]
title: buttonTitles[ position ],
'aria-hidden': 'true'
},
children: [
wrapperDomElement.ownerDocument.importNode( RETURN_ARROW_ICON_ELEMENT, true )
Expand Down
20 changes: 17 additions & 3 deletions packages/ckeditor5-widget/tests/utils.js
Expand Up @@ -69,6 +69,12 @@ describe( 'widget utils', () => {
expect( getLabel( element ) ).to.equal( 'foo bar baz label' );
} );

it( 'should set element\'s custom property \'widgetLabel\' as an array', () => {
toWidget( element, writer );

expect( element.getCustomProperty( 'widgetLabel' ) ).to.be.an( 'array' );
} );

it( 'should set default highlight handling methods - CSS class', () => {
toWidget( element, writer );

Expand Down Expand Up @@ -176,27 +182,35 @@ describe( 'widget utils', () => {

describe( 'label utils', () => {
it( 'should allow to set label for element', () => {
const element = new ViewElement( viewDocument, 'p' );
toWidget( element, writer );
setLabel( element, 'foo bar baz', writer );

expect( getLabel( element ) ).to.equal( 'foo bar baz' );
} );

it( 'should return empty string for elements without label', () => {
const element = new ViewElement( viewDocument, 'div' );
toWidget( element, writer );

expect( getLabel( element ) ).to.equal( '' );
} );

it( 'should allow to use a function as label creator', () => {
const element = new ViewElement( viewDocument, 'p' );
toWidget( element, writer );
let caption = 'foo';
setLabel( element, () => caption, writer );

expect( getLabel( element ) ).to.equal( 'foo' );
caption = 'bar';
expect( getLabel( element ) ).to.equal( 'bar' );
} );

it( 'should concatenate a label from a function creator and a string', () => {
toWidget( element, writer );
setLabel( element, () => 'foo', writer );
element.getCustomProperty( 'widgetLabel' ).push( 'bar' );

expect( getLabel( element ) ).to.equal( 'foo. bar' );
} );
} );

describe( 'toWidgetEditable()', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/ckeditor5-widget/tests/widget.js
Expand Up @@ -360,7 +360,9 @@ describe( 'Widget', () => {
it( 'should use element\'s label to set fake selection if one is provided', () => {
setModelData( model, '[<widget>foo bar</widget>]' );

expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'element label' );
expect( viewDocument.selection.fakeSelectionLabel ).to.equal(
'element label. Press Enter to type after or press Shift + Enter to type before the widget'
);
} );

it( 'should add selected class when other content is selected with widget', () => {
Expand Down

0 comments on commit ccc1611

Please sign in to comment.