Skip to content

Commit

Permalink
fix(core/dropdown): prevent closing (#1334)
Browse files Browse the repository at this point in the history
  • Loading branch information
nuke-ellington authored Jun 20, 2024
1 parent 9e75d7b commit 5149f1a
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-planets-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@siemens/ix": patch
---

fix(core/dropdown): prevent closing
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export class ApplicationHeader {
typeof ApplicationLayoutContext
>;

get contentBackground() {
return this.hostElement.shadowRoot.querySelector('.dropdown-content');
}

componentWillLoad() {
useContextConsumer(
this.hostElement,
Expand Down Expand Up @@ -165,6 +169,12 @@ export class ApplicationHeader {
this.hasSlottedElements = hasSlottedElements(slotElement);
}

private onContentBgClick(e: MouseEvent) {
if (e.target === this.contentBackground) {
e.preventDefault();
}
}

render() {
return (
<Host
Expand Down Expand Up @@ -209,7 +219,10 @@ export class ApplicationHeader {
discoverAllSubmenus
trigger={this.resolveContextMenuButton()}
>
<div class="dropdown-content">
<div
class="dropdown-content"
onClick={(e) => this.onContentBgClick(e)}
>
<slot
onSlotchange={() => this.updateIsSlottedContent()}
></slot>
Expand Down
13 changes: 12 additions & 1 deletion packages/core/src/components/avatar/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class Avatar {
@State() hasSlottedElements = false;

private slotElement: HTMLSlotElement;
private dropdownElement: HTMLIxDropdownElement;

componentWillLoad() {
const closest = closestElement('ix-application-header', this.hostElement);
Expand All @@ -149,6 +150,12 @@ export class Avatar {
});
}

private onDropdownClick(event: MouseEvent) {
if (event.target === this.dropdownElement) {
event.preventDefault();
}
}

render() {
if (this.isClosestApplicationHeader) {
return (
Expand All @@ -168,8 +175,10 @@ export class Avatar {
<AvatarImage image={this.image} initials={this.initials} />
</BaseButton>
<ix-dropdown
ref={(ref: HTMLIxDropdownElement) => (this.dropdownElement = ref)}
trigger={this.resolveAvatarTrigger()}
class="avatar-dropdown"
onClick={(e) => this.onDropdownClick(e)}
>
{this.username && (
<Fragment>
Expand All @@ -179,7 +188,9 @@ export class Avatar {
initials={this.initials}
userName={this.username}
/>
{this.hasSlottedElements && <ix-divider></ix-divider>}
{this.hasSlottedElements && (
<ix-divider onClick={(e) => e.preventDefault()}></ix-divider>
)}
</Fragment>
)}
<slot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,10 @@ export class CategoryFilter {
data-id={id}
title={id}
key={id}
onClick={() => this.addToken(id, this.category)}
onClick={(e) => {
e.preventDefault();
this.addToken(id, this.category);
}}
>
{`${this.getFilterOperatorString()} ${id}`}
</button>
Expand Down
25 changes: 25 additions & 0 deletions packages/core/src/components/dropdown-button/dropdown-button.ct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { expect } from '@playwright/test';
import { test } from '@utils/test';

test('renders', async ({ mount, page }) => {
await mount(`
<ix-dropdown-button label="Open">
<ix-dropdown-item label="Test"></ix-dropdown-item>
</ix-dropdown-button>
`);

await page.locator('ix-dropdown-button').click();
const item = page.locator('ix-dropdown-item');
await expect(item).toBeVisible();

await item.click();
await expect(item).not.toBeVisible();
});
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class DropdownController {

dismissOthers(uid: string) {
let path = this.buildComposedPath(uid, new Set<string>());
path.add(uid);

this.dropdowns.forEach((dropdown) => {
if (
Expand Down
10 changes: 7 additions & 3 deletions packages/core/src/components/dropdown/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,14 +506,19 @@ export class Dropdown implements ComponentInterface, DropdownInterface {
private onDropdownClick(event: PointerEvent) {
const target = dropdownController.pathIncludesTrigger(event.composedPath());
if (target) {
event.preventDefault();
if (target !== this.triggerElement) {
event.preventDefault();
}

if (this.isTriggerElement(target)) {
return;
}
}

if (this.closeBehavior === 'inside' || this.closeBehavior === 'both') {
if (
!event.defaultPrevented &&
(this.closeBehavior === 'inside' || this.closeBehavior === 'both')
) {
dropdownController.dismissAll([this.getId()], this.ignoreRelatedSubmenu);
return;
}
Expand Down Expand Up @@ -549,7 +554,6 @@ export class Dropdown implements ComponentInterface, DropdownInterface {
>
<div style={{ display: 'contents' }}>
{this.header && <div class="dropdown-header">{this.header}</div>}

<slot></slot>
</div>
</Host>
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/components/dropdown/test/dropdown.ct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,28 @@ test.describe('Close behavior', () => {
});
});

test('Prevent closing', async ({ page, mount }) => {
await mount(`
<ix-button id="trigger">Open</ix-button>
<ix-dropdown trigger="trigger">
<ix-dropdown-header id="header">Header</ix-dropdown-header>
<ix-dropdown-item id="item-1">Item 1</ix-dropdown-item>
</ix-dropdown>`);

const header = await page.locator('ix-dropdown-header');
header.evaluate((h) =>
h.addEventListener('click', (event) => {
event.preventDefault();
})
);
await page.locator('#trigger').click();
await expect(header).toBeVisible();
await header.click();
await expect(header).toBeVisible();
await page.locator('#item-1').click();
await expect(header).not.toBeVisible();
});

test.describe('Nested dropdowns 1/3', () => {
function mountDropdown(
mount: (selector: string) => Promise<ElementHandle<HTMLElement>>,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/components/menu-category/menu-category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ export class MenuCategory {
if (e.target instanceof HTMLElement) {
if (e.target.tagName === 'IX-MENU-ITEM') {
this.showDropdown = false;
} else {
e.preventDefault();
}
}
}}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ export class Select {
hidden: this.hideListHeader || this.isDropdownEmpty,
}}
title={this.i18nSelectListHeader}
onClick={(e) => e.preventDefault()}
>
{this.i18nSelectListHeader}
</div>
Expand Down

0 comments on commit 5149f1a

Please sign in to comment.