Skip to content

Commit

Permalink
fix: fire click on Enter and Space, but not when disabled (#2438)
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen authored Sep 3, 2021
1 parent 389d1ec commit 28aa14c
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 14 deletions.
25 changes: 25 additions & 0 deletions packages/button/src/vaadin-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,31 @@ class Button extends ActiveMixin(TabindexMixin(FocusMixin(ElementMixin(ThemableM
this.setAttribute('role', 'button');
}
}

/**
* Since the button component is designed on the base of the `[role=button]` attribute,
* and doesn't have a native <button> inside, in order to be fully accessible from the keyboard,
* it should manually fire the `click` event once an activation key is pressed,
* as it follows from the WAI-ARIA specifications:
* https://www.w3.org/TR/wai-aria-practices-1.1/#button
*
* According to the UI Events specifications,
* the `click` event should be fired exactly on `keydown`:
* https://www.w3.org/TR/uievents/#event-type-keydown
*
* @param {KeyboardEvent} event
* @protected
* @override
*/
_onKeyDown(event) {
super._onKeyDown(event);

if (this._activeKeys.includes(event.key)) {
// `DisabledMixin` overrides the standard `click()` method
// so that it doesn't fire the `click` event when the element is disabled.
this.click();
}
}
}

customElements.define(Button.is, Button);
Expand Down
29 changes: 29 additions & 0 deletions packages/button/test/button.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sinon from 'sinon';
import { expect } from '@esm-bundle/chai';
import { sendKeys } from '@web/test-runner-commands';
import {
Expand Down Expand Up @@ -72,6 +73,34 @@ describe('vaadin-button', () => {
});
});

describe('keyboard', () => {
beforeEach(() => {
element = fixtureSync('<vaadin-button>Press me</vaadin-button>');
element.focus();
});

['Enter', 'Space'].forEach((key) => {
it(`should fire click event on ${key}`, async () => {
const spy = sinon.spy();
element.addEventListener('click', spy);

await sendKeys({ down: key });

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

it(`should not fire click event on ${key} when disabled`, async () => {
const spy = sinon.spy();
element.addEventListener('click', spy);
element.disabled = true;

await sendKeys({ down: key });

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

describe('mixins', () => {
beforeEach(() => {
element = fixtureSync('<vaadin-button>Press me</vaadin-button>');
Expand Down
12 changes: 12 additions & 0 deletions packages/field-base/src/disabled-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ const DisabledMixinImplementation = (superclass) =>
this.removeAttribute('aria-disabled');
}
}

/**
* Overrides the default element `click` method in order to prevent
* firing the `click` event when the element is disabled.
*
* @override
*/
click() {
if (!this.disabled) {
super.click();
}
}
};

/**
Expand Down
9 changes: 9 additions & 0 deletions packages/field-base/test/disabled-mixin.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sinon from 'sinon';
import { expect } from '@esm-bundle/chai';
import { fixtureSync } from '@vaadin/testing-helpers';
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
Expand Down Expand Up @@ -38,4 +39,12 @@ describe('disabled-mixin', () => {
element.disabled = false;
expect(element.hasAttribute('aria-disabled')).to.be.false;
});

it('should prevent firing click event when disabled', () => {
const spy = sinon.spy();
element.addEventListener('click', spy);
element.disabled = true;
element.click();
expect(spy.called).to.be.false;
});
});
15 changes: 1 addition & 14 deletions packages/vaadin-app-layout/src/vaadin-drawer-toggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,9 @@ class DrawerToggleElement extends Button {
super();

this.addEventListener('click', () => {
if (!this.disabled) {
this.__fireClick();
}
});

this.addEventListener('keyup', (event) => {
if (/^( |SpaceBar|Enter)$/.test(event.key) && !this.disabled) {
this.__fireClick();
}
this.dispatchEvent(new CustomEvent('drawer-toggle-click', { bubbles: true, composed: true }));
});
}

/** @private */
__fireClick() {
this.dispatchEvent(new CustomEvent('drawer-toggle-click', { bubbles: true, composed: true }));
}
}

customElements.define(DrawerToggleElement.is, DrawerToggleElement);
Expand Down

0 comments on commit 28aa14c

Please sign in to comment.