Skip to content

Commit ea35e87

Browse files
authored
Merge pull request #963 from umbraco/v1/bugfix/do-not-select-when-opening
Fix: do not select when opening href on selectable elements
2 parents 15b38ff + 02a3a01 commit ea35e87

File tree

2 files changed

+55
-22
lines changed

2 files changed

+55
-22
lines changed

packages/uui-base/lib/mixins/SelectableMixin.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,45 @@ export const SelectableMixin = <T extends Constructor<LitElement>>(
7575

7676
constructor(...args: any[]) {
7777
super(...args);
78-
this.addEventListener('click', this._handleClick);
79-
this.addEventListener('keydown', this.handleSelectKeydown);
78+
this.addEventListener('click', this.#onClick);
79+
this.addEventListener('keydown', this.#onKeydown);
8080
}
8181

82-
private handleSelectKeydown = (e: KeyboardEvent) => {
82+
readonly #onKeydown = (e: KeyboardEvent) => {
8383
const composePath = e.composedPath();
8484
if (
8585
(this._selectable || (this.deselectable && this.selected)) &&
8686
composePath.indexOf(this.selectableTarget) === 0
8787
) {
8888
if (this.selectableTarget === this) {
8989
if (e.code !== 'Space' && e.code !== 'Enter') return;
90-
this._toggleSelect();
90+
this.#toggleSelect();
9191
e.preventDefault();
9292
}
9393
}
9494
};
9595

96-
private _select() {
96+
readonly #onClick = (e: Event) => {
97+
const composePath = e.composedPath();
98+
if (
99+
(this._selectable || (this.deselectable && this.selected)) &&
100+
composePath.indexOf(this.selectableTarget) === 0
101+
) {
102+
this.#toggleSelect();
103+
}
104+
};
105+
106+
#toggleSelect() {
107+
// 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..
108+
if (!this.selectable) return;
109+
if (this.deselectable === false) {
110+
this.#select();
111+
} else {
112+
this.selected ? this.#deselect() : this.#select();
113+
}
114+
}
115+
116+
#select() {
97117
if (!this.selectable) return;
98118
const selectEvent = new UUISelectableEvent(UUISelectableEvent.SELECTED);
99119
this.dispatchEvent(selectEvent);
@@ -102,30 +122,14 @@ export const SelectableMixin = <T extends Constructor<LitElement>>(
102122
this.selected = true;
103123
}
104124

105-
private _deselect() {
125+
#deselect() {
106126
if (!this.deselectable) return;
107127
const selectEvent = new UUISelectableEvent(UUISelectableEvent.DESELECTED);
108128
this.dispatchEvent(selectEvent);
109129
if (selectEvent.defaultPrevented) return;
110130

111131
this.selected = false;
112132
}
113-
114-
private _handleClick(e: Event) {
115-
if (e.composedPath().indexOf(this.selectableTarget) !== -1) {
116-
this._toggleSelect();
117-
}
118-
}
119-
120-
private _toggleSelect() {
121-
// 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..
122-
if (!this.selectable) return;
123-
if (this.deselectable === false) {
124-
this._select();
125-
} else {
126-
this.selected ? this._deselect() : this._select();
127-
}
128-
}
129133
}
130134
// prettier-ignore
131135
return (SelectableMixinClass as unknown) as Constructor<SelectableMixinInterface> & T;

packages/uui-card-media/lib/uui-card-media.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,35 @@ describe('UUICardMediaElement', () => {
9999
expect(event.type).to.equal(UUISelectableEvent.SELECTED);
100100
expect(element.selected).to.be.true;
101101
});
102+
it('do react to a click event on performed in this way', async () => {
103+
element.selectable = true;
104+
await elementUpdated(element);
105+
element.click();
106+
await Promise.resolve();
107+
expect(element.selected).to.be.true;
108+
});
109+
it('do not react to a click event on other parts', async () => {
110+
element.selectable = true;
111+
await elementUpdated(element);
112+
const listener = oneEvent(element, UUICardEvent.OPEN);
113+
element
114+
.shadowRoot!.querySelector<HTMLAnchorElement>('#open-part')!
115+
.click();
116+
const event = await listener;
117+
expect(event).to.exist;
118+
expect(event.type).to.equal(UUICardEvent.OPEN);
119+
expect(element.selected).to.be.false;
120+
});
121+
it('do not react to a click event on other parts as href', async () => {
122+
element.selectable = true;
123+
element.href = '#hello';
124+
await elementUpdated(element);
125+
element
126+
.shadowRoot!.querySelector<HTMLAnchorElement>('#open-part')!
127+
.click();
128+
await Promise.resolve();
129+
expect(element.selected).to.be.false;
130+
});
102131
it('can be selected with keyboard', async () => {
103132
element.selectable = true;
104133
await elementUpdated(element);

0 commit comments

Comments
 (0)