Skip to content

Commit

Permalink
Merge pull request #14718 from ckeditor/ck/14312-ckbox-command-should…
Browse files Browse the repository at this point in the history
…-set-focus-in-the-opened-dialog

Fix (ckbox): CKBox dialog should be focused after being opened. Closes #14312.
  • Loading branch information
arkflpc authored Aug 29, 2023
2 parents c2d4af6 + 87305f3 commit f8552d5
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
36 changes: 36 additions & 0 deletions packages/ckeditor5-ckbox/src/ckboxcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ export default class CKBoxCommand extends Command {
document.body.appendChild( this._wrapper );

window.CKBox.mount( this._wrapper, this._prepareOptions() );

const MAX_NUMBER_OF_ATTEMPTS_TO_FOCUS = 50;

focusCKBoxItem( MAX_NUMBER_OF_ATTEMPTS_TO_FOCUS );
} );

// Handle closing of the CKBox dialog.
Expand Down Expand Up @@ -387,6 +391,38 @@ function getAssetUrl( asset: CKBoxRawAssetDefinition ) {
return url.toString();
}

/**
* Focuses the CKBox first item in gallery.
* This is a temporary fix. A permanent solution to this issue will be provided soon.
*
* @param limiter Max number of attempts to focus the ckbox item.
*/
function focusCKBoxItem( limiter: number ): void {
// Trying every 100 ms get access to the CKBox component until component will be loaded.
setTimeout( () => {
if ( limiter === 0 ) {
return;
}

const ckboxGalleryFirstItem = document.querySelector( '.ckbox-gallery .ckbox-gallery-item' );
// In case there is no items, "upload button" will be appeared in "div" with
// classname ".ckbox-empty-view".
const uploadButton = document.querySelector( '.ckbox-empty-view .ckbox-btn' );

// In case "upload button" is loaded in ".ckbox-empty-view" we focus actual button.
if ( uploadButton && uploadButton instanceof HTMLElement ) {
uploadButton.focus();
return;
}

if ( ckboxGalleryFirstItem && ckboxGalleryFirstItem instanceof HTMLElement ) {
ckboxGalleryFirstItem.focus();
} else {
focusCKBoxItem( limiter - 1 );
}
}, 100 );
}

/**
* Fired when the command is executed, the dialog is closed or the assets are chosen.
*
Expand Down
81 changes: 81 additions & 0 deletions packages/ckeditor5-ckbox/tests/ckboxcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ describe( 'CKBoxCommand', () => {

describe( 'events', () => {
describe( 'opening dialog ("ckbox:open")', () => {
beforeEach( () => {
sinon.useFakeTimers( { now: Date.now() } );
} );

afterEach( () => {
sinon.restore();
} );

it( 'should create a wrapper if it is not yet created and mount it in the document body', () => {
command.execute();

Expand All @@ -159,6 +167,79 @@ describe( 'CKBoxCommand', () => {
expect( document.body.appendChild.args[ 0 ][ 0 ] ).to.equal( wrapper );
} );

it( 'should focus ckbox gallery item after initialization', () => {
const ckboxGallery = document.createElement( 'div' );
const ckboxGalleryItem = document.createElement( 'div' );

ckboxGallery.setAttribute( 'class', 'ckbox-gallery' );
ckboxGalleryItem.setAttribute( 'class', 'ckbox-gallery-item' );

ckboxGallery.appendChild( ckboxGalleryItem );
document.body.append( ckboxGallery );

const spy = sinon.spy( ckboxGalleryItem, 'focus' );
command.execute();

sinon.clock.tick( 100 );
expect( spy.calledOnce ).to.be.true;
ckboxGallery.remove();
} );

it( 'should focus upload button item after initialization', () => {
const emptyView = document.createElement( 'div' );
const uploadButton = document.createElement( 'button' );

emptyView.setAttribute( 'class', 'ckbox-empty-view' );
uploadButton.setAttribute( 'class', 'ckbox-btn' );

emptyView.appendChild( uploadButton );
document.body.append( emptyView );

const spy = sinon.spy( uploadButton, 'focus' );
command.execute();

sinon.clock.tick( 100 );
expect( spy.calledOnce ).to.be.true;
emptyView.remove();
} );

it( 'should wait until ckbox will be loaded', () => {
const ckboxGallery = document.createElement( 'div' );
const ckboxGalleryItem = document.createElement( 'div' );

ckboxGallery.setAttribute( 'class', 'ckbox-gallery' );
ckboxGalleryItem.setAttribute( 'class', 'ckbox-gallery-item' );

const spy = sinon.spy( ckboxGalleryItem, 'focus' );
command.execute();

sinon.clock.tick( 100 );
expect( spy.notCalled ).to.be.true;

document.body.append( ckboxGallery );

sinon.clock.tick( 100 );
expect( spy.notCalled ).to.be.true;

ckboxGallery.appendChild( ckboxGalleryItem );
sinon.clock.tick( 100 );

expect( spy.calledOnce ).to.be.true;
ckboxGallery.remove();
} );

it( 'should giveup after 5 seconds', () => {
const ckboxGalleryItem = document.createElement( 'div' );

ckboxGalleryItem.setAttribute( 'class', 'ckbox-gallery-item' );

const spy = sinon.spy( ckboxGalleryItem, 'focus' );
command.execute();

sinon.clock.tick( 5100 );
expect( spy.notCalled ).to.be.true;
} );

it( 'should create and mount a wrapper only once', () => {
command.execute();

Expand Down

0 comments on commit f8552d5

Please sign in to comment.