Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: solution to double selection #958

Merged
merged 8 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions packages/uui-base/lib/mixins/SelectableMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

type Constructor<T = {}> = new (...args: any[]) => T;

export declare class SelectableMixinInterface extends LitElement {

Check warning on line 8 in packages/uui-base/lib/mixins/SelectableMixin.ts

View workflow job for this annotation

GitHub Actions / test (20)

Class declaration should be prefixed with "UUI"
/**
* Enable the ability to select this element.
* @attr
Expand Down Expand Up @@ -39,7 +39,7 @@
* @fires {UUISelectableEvent} selected - fires when the media card is selected
* @fires {UUISelectableEvent} deselected - fires when the media card is deselected
*/
class SelectableMixinClass extends superClass {

Check warning on line 42 in packages/uui-base/lib/mixins/SelectableMixin.ts

View workflow job for this annotation

GitHub Actions / test (20)

Class declaration should be prefixed with "UUI"
private _selectable = false;
/**
* Enable the ability to select this element.
Expand All @@ -54,8 +54,8 @@
const oldVal = this._selectable;
this._selectable = newVal;
// Potentially problematic as a component might need focus for another feature when not selectable:
if (!this.selectableTarget) {
// If not selectable target, then make it self selectable. (A selectable target should be made focusable by the component itself)
if (this.selectableTarget === this) {
// If the selectable target, then make it self selectable. (A different selectable target should be made focusable by the component itself)
this.setAttribute('tabindex', `${newVal ? '0' : '-1'}`);
}
this.requestUpdate('selectable', oldVal);
Expand All @@ -80,10 +80,16 @@
}

private handleSelectKeydown = (e: KeyboardEvent) => {
//if (e.composedPath().indexOf(this.selectableTarget) !== -1) {
if (this.selectableTarget === this) {
if (e.key !== ' ' && e.key !== 'Enter') return;
this._toggleSelect();
const composePath = e.composedPath();
if (
(this._selectable || (this.deselectable && this.selected)) &&
composePath.indexOf(this.selectableTarget) === 0
) {
if (this.selectableTarget === this) {
if (e.code !== 'Space' && e.code !== 'Enter') return;
this._toggleSelect();
e.preventDefault();
}
}
};

Expand Down Expand Up @@ -112,7 +118,7 @@
}

private _toggleSelect() {
// Only allow for select-interaction if selectable is true. Deselectable is ignorered in this case, we do not want a DX where only deselection is a possibility..
// Only allow for select-interaction if selectable is true. Deselectable is ignored in this case, we do not want a DX where only deselection is a possibility..
if (!this.selectable) return;
if (this.deselectable === false) {
this._select();
Expand Down
31 changes: 24 additions & 7 deletions packages/uui-card-block-type/lib/uui-card-block-type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe('UUICardBlockTypeElement', () => {
describe('events', () => {
describe('open', () => {
it('emits a open event when open-part is clicked', async () => {
const listener = oneEvent(element, UUICardEvent.OPEN, false);
const listener = oneEvent(element, UUICardEvent.OPEN);
const infoElement: HTMLElement | null =
element.shadowRoot!.querySelector('#open-part');
infoElement?.click();
Expand All @@ -93,25 +93,42 @@ describe('UUICardBlockTypeElement', () => {
it('emits a selected event when selectable', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED, false);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;
});

it('can be selected with keyboard', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' }));
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;

const unselectedListener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Space' }));
const event2 = await unselectedListener;
expect(event2).to.exist;
expect(event2.type).to.equal(UUISelectableEvent.DESELECTED);
expect(element.selected).to.be.false;
});
});

describe('deselect', () => {
it('emits a deselected event when preselected', async () => {
element.selectable = true;
element.selected = true;
await elementUpdated(element);
const listener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
false,
);
const listener = oneEvent(element, UUISelectableEvent.DESELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
Expand Down
33 changes: 25 additions & 8 deletions packages/uui-card-content-node/lib/uui-card-content-node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('UUICardContentNodeElement', () => {
describe('events', () => {
describe('open', () => {
it('emits a open event when info is clicked', async () => {
const listener = oneEvent(element, UUICardEvent.OPEN, false);
const listener = oneEvent(element, UUICardEvent.OPEN);
const infoElement: HTMLElement | null =
element.shadowRoot!.querySelector('#open-part');
infoElement?.click();
Expand All @@ -85,7 +85,7 @@ describe('UUICardContentNodeElement', () => {
});

it('emits a open event when icon is clicked', async () => {
const listener = oneEvent(element, UUICardEvent.OPEN, false);
const listener = oneEvent(element, UUICardEvent.OPEN);
const iconElement: HTMLElement | null =
element.shadowRoot!.querySelector('#icon');
iconElement?.click();
Expand All @@ -99,25 +99,42 @@ describe('UUICardContentNodeElement', () => {
it('emits a selected event when selectable', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED, false);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;
});

it('can be selected with keyboard', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' }));
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;

const unselectedListener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Space' }));
const event2 = await unselectedListener;
expect(event2).to.exist;
expect(event2.type).to.equal(UUISelectableEvent.DESELECTED);
expect(element.selected).to.be.false;
});
});

describe('deselect', () => {
it('emits a deselected event when preselected', async () => {
element.selectable = true;
element.selected = true;
await elementUpdated(element);
const listener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
false,
);
const listener = oneEvent(element, UUISelectableEvent.DESELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
Expand Down
30 changes: 23 additions & 7 deletions packages/uui-card-media/lib/uui-card-media.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('UUICardMediaElement', () => {
describe('events', () => {
describe('open', () => {
it('emits a open event when open-part is clicked', async () => {
const listener = oneEvent(element, UUICardEvent.OPEN, false);
const listener = oneEvent(element, UUICardEvent.OPEN);
const infoElement: HTMLElement | null =
element.shadowRoot!.querySelector('#open-part');
infoElement?.click();
Expand All @@ -92,25 +92,41 @@ describe('UUICardMediaElement', () => {
it('emits a selected event when selectable', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED, false);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;
});
it('can be selected with keyboard', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' }));
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;

const unselectedListener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Space' }));
const event2 = await unselectedListener;
expect(event2).to.exist;
expect(event2.type).to.equal(UUISelectableEvent.DESELECTED);
expect(element.selected).to.be.false;
});
});

describe('deselect', () => {
it('emits a deselected event when preselected', async () => {
element.selectable = true;
element.selected = true;
await elementUpdated(element);
const listener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
false,
);
const listener = oneEvent(element, UUISelectableEvent.DESELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
Expand Down
32 changes: 25 additions & 7 deletions packages/uui-card-user/lib/uui-card-user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('UUICardUserElement', () => {
describe('events', () => {
describe('open', () => {
it('emits a open event when open-part is clicked', async () => {
const listener = oneEvent(element, UUICardEvent.OPEN, false);
const listener = oneEvent(element, UUICardEvent.OPEN);
const infoElement: HTMLElement | null =
element.shadowRoot!.querySelector('#open-part');
infoElement?.click();
Expand All @@ -91,25 +91,43 @@ describe('UUICardUserElement', () => {
it('emits a selected event when selectable', async () => {
element.selectable = true;
await elementUpdated(element);
const listener = oneEvent(element, UUISelectableEvent.SELECTED, false);
const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;
});

it('can be selected with keyboard', async () => {
element.selectable = true;
await elementUpdated(element);

const listener = oneEvent(element, UUISelectableEvent.SELECTED);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' }));
const event = await listener;
expect(event).to.exist;
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
expect(element.selected).to.be.true;

const unselectedListener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
);
element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Space' }));
const event2 = await unselectedListener;
expect(event2).to.exist;
expect(event2.type).to.equal(UUISelectableEvent.DESELECTED);
expect(element.selected).to.be.false;
});
});

describe('deselect', () => {
it('emits a deselected event when preselected', async () => {
element.selectable = true;
element.selected = true;
await elementUpdated(element);
const listener = oneEvent(
element,
UUISelectableEvent.DESELECTED,
false,
);
const listener = oneEvent(element, UUISelectableEvent.DESELECTED);
element.click();
const event = await listener;
expect(event).to.exist;
Expand Down
Loading