From 950f35468f914db11747dd4f5563487cab45264d Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Tue, 18 Oct 2022 19:30:21 +0200 Subject: [PATCH 1/7] Add label to be read by screen readers about possibility to type around a widget. --- packages/ckeditor5-image/tests/image.js | 8 ++++++-- packages/ckeditor5-widget/src/utils.js | 20 ++++++++++--------- .../src/widgettypearound/widgettypearound.js | 4 ++++ packages/ckeditor5-widget/tests/utils.js | 20 ++++++++++++++++--- packages/ckeditor5-widget/tests/widget.js | 4 +++- 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/ckeditor5-image/tests/image.js b/packages/ckeditor5-image/tests/image.js index 4493b47e44a..0b8b34388f6 100644 --- a/packages/ckeditor5-image/tests/image.js +++ b/packages/ckeditor5-image/tests/image.js @@ -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', () => { @@ -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', () => { diff --git a/packages/ckeditor5-widget/src/utils.js b/packages/ckeditor5-widget/src/utils.js index 06a07547273..4609c7885bd 100644 --- a/packages/ckeditor5-widget/src/utils.js +++ b/packages/ckeditor5-widget/src/utils.js @@ -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 ); } @@ -199,8 +201,8 @@ export function setHighlightHandling( element, writer, add = addHighlight, remov * @param {String|Function} labelOrCreator * @param {module:engine/view/downcastwriter~DowncastWriter} writer */ -export function setLabel( element, labelOrCreator, writer ) { - writer.setCustomProperty( 'widgetLabel', labelOrCreator, element ); +export function setLabel( element, labelOrCreator ) { + element.getCustomProperty( 'widgetLabel' ).push( labelOrCreator ); } /** @@ -210,13 +212,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; + } + }, '' ); } /** diff --git a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js index 7792dc7431a..4ad7317b832 100644 --- a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js +++ b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js @@ -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' } ); } diff --git a/packages/ckeditor5-widget/tests/utils.js b/packages/ckeditor5-widget/tests/utils.js index fe846543195..9ac27c3d485 100644 --- a/packages/ckeditor5-widget/tests/utils.js +++ b/packages/ckeditor5-widget/tests/utils.js @@ -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 ); @@ -176,20 +182,20 @@ 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 ); @@ -197,6 +203,14 @@ describe( 'widget utils', () => { 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()', () => { diff --git a/packages/ckeditor5-widget/tests/widget.js b/packages/ckeditor5-widget/tests/widget.js index 3eedfedf46d..c025a1d77b3 100644 --- a/packages/ckeditor5-widget/tests/widget.js +++ b/packages/ckeditor5-widget/tests/widget.js @@ -360,7 +360,9 @@ describe( 'Widget', () => { it( 'should use element\'s label to set fake selection if one is provided', () => { setModelData( model, '[foo bar]' ); - 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', () => { From 9c77360816015c853be7e510f9bada2f2fa168a8 Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Wed, 2 Nov 2022 13:19:36 +0100 Subject: [PATCH 2/7] Merge the fix to TS files. --- packages/ckeditor5-widget/_src/utils.js | 3 +-- packages/ckeditor5-widget/src/utils.ts | 27 +++++++++++-------- .../src/widgettypearound/widgettypearound.ts | 10 +++++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/ckeditor5-widget/_src/utils.js b/packages/ckeditor5-widget/_src/utils.js index 4609c7885bd..f2f900272a9 100644 --- a/packages/ckeditor5-widget/_src/utils.js +++ b/packages/ckeditor5-widget/_src/utils.js @@ -198,8 +198,7 @@ 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 ) { element.getCustomProperty( 'widgetLabel' ).push( labelOrCreator ); diff --git a/packages/ckeditor5-widget/src/utils.ts b/packages/ckeditor5-widget/src/utils.ts index eef48112f27..7c80c1a3ff3 100644 --- a/packages/ckeditor5-widget/src/utils.ts +++ b/packages/ckeditor5-widget/src/utils.ts @@ -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 ) { @@ -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; + + widgetLabel.push( labelOrCreator ); } /** @@ -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; - if ( !labelCreator ) { - return ''; - } - - return typeof labelCreator == 'function' ? labelCreator() : labelCreator; + return widgetLabel.reduce( ( prev: string, current: string | Function ) => { + if ( typeof current === 'function' ) { + return prev ? prev + '. ' + current() : current(); + } else { + return prev ? prev + '. ' + current : current; + } + }, '' ); } /** diff --git a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts index cbdbc61b881..38d4dd0c2fe 100644 --- a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts +++ b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts @@ -256,9 +256,19 @@ export default class WidgetTypeAround extends Plugin { editor.editing.downcastDispatcher.on>( '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; + + widgetLabel.push( () => { + return this.isEnabled ? t( 'Press Enter to type after or press Shift + Enter to type before the widget' ) : ''; + } ); } }, { priority: 'low' } ); } From 328bfc4c2c233ad718fd537da4ff3e2f1090f8af Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Wed, 9 Nov 2022 11:11:04 +0100 Subject: [PATCH 3/7] Change TS Function type to be more precise Co-authored-by: Kuba Niegowski <1232187+niegowski@users.noreply.github.com> --- packages/ckeditor5-widget/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-widget/src/utils.ts b/packages/ckeditor5-widget/src/utils.ts index 7c80c1a3ff3..66c61921782 100644 --- a/packages/ckeditor5-widget/src/utils.ts +++ b/packages/ckeditor5-widget/src/utils.ts @@ -230,7 +230,7 @@ export function setHighlightHandling( * @param {string|Function} labelOrCreator */ export function setLabel( element: Element, labelOrCreator: string | ( () => string ) ): void { - const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array; + const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array string )>; widgetLabel.push( labelOrCreator ); } From e480305451cb480fce58bb31548527a19a33f592 Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Wed, 9 Nov 2022 11:13:28 +0100 Subject: [PATCH 4/7] Change TS Function type to be more precise Co-authored-by: Kuba Niegowski <1232187+niegowski@users.noreply.github.com> --- packages/ckeditor5-widget/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-widget/src/utils.ts b/packages/ckeditor5-widget/src/utils.ts index 66c61921782..ff2cf6702c3 100644 --- a/packages/ckeditor5-widget/src/utils.ts +++ b/packages/ckeditor5-widget/src/utils.ts @@ -242,7 +242,7 @@ export function setLabel( element: Element, labelOrCreator: string | ( () => str * @returns {String} */ export function getLabel( element: Element ): string { - const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array; + const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array string )>; return widgetLabel.reduce( ( prev: string, current: string | Function ) => { if ( typeof current === 'function' ) { From cbe03363fd051837f7d07f49430c0921f1c6a02b Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Wed, 9 Nov 2022 11:13:59 +0100 Subject: [PATCH 5/7] Change TS Function type to be more precise Co-authored-by: Kuba Niegowski <1232187+niegowski@users.noreply.github.com> --- packages/ckeditor5-widget/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-widget/src/utils.ts b/packages/ckeditor5-widget/src/utils.ts index ff2cf6702c3..a6c358e4a35 100644 --- a/packages/ckeditor5-widget/src/utils.ts +++ b/packages/ckeditor5-widget/src/utils.ts @@ -244,7 +244,7 @@ export function setLabel( element: Element, labelOrCreator: string | ( () => str export function getLabel( element: Element ): string { const widgetLabel = element.getCustomProperty( 'widgetLabel' ) as Array string )>; - return widgetLabel.reduce( ( prev: string, current: string | Function ) => { + return widgetLabel.reduce( ( prev: string, current: string | ( () => string ) ) => { if ( typeof current === 'function' ) { return prev ? prev + '. ' + current() : current(); } else { From 0159a5c2570d97430e5450340b146789236749b7 Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Wed, 9 Nov 2022 11:14:20 +0100 Subject: [PATCH 6/7] Change TS Function type to be more precise Co-authored-by: Kuba Niegowski <1232187+niegowski@users.noreply.github.com> --- .../ckeditor5-widget/src/widgettypearound/widgettypearound.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts index 38d4dd0c2fe..f640f18dc4a 100644 --- a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts +++ b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts @@ -264,7 +264,7 @@ export default class WidgetTypeAround extends Plugin { if ( isTypeAroundWidget( viewElement, data.item, schema ) ) { injectUIIntoWidget( conversionApi.writer, buttonTitles, viewElement! ); - const widgetLabel = viewElement.getCustomProperty( 'widgetLabel' ) as Array; + const widgetLabel = viewElement.getCustomProperty( 'widgetLabel' ) as Array string )>; widgetLabel.push( () => { return this.isEnabled ? t( 'Press Enter to type after or press Shift + Enter to type before the widget' ) : ''; From abca6fb264bccd71aa417e6fd958074aa5f18c40 Mon Sep 17 00:00:00 2001 From: Marta Motyczynska Date: Wed, 9 Nov 2022 11:28:31 +0100 Subject: [PATCH 7/7] Add aria-hidden to type-around buttons. --- .../ckeditor5-widget/src/widgettypearound/widgettypearound.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts index f640f18dc4a..e8a2d4929f6 100644 --- a/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts +++ b/packages/ckeditor5-widget/src/widgettypearound/widgettypearound.ts @@ -932,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 )