feat(dees-contextmenu,dees-appui-tabs,test): Prevent double-destruction of context menus, await window layer teardown, update destroyAll behavior, remove tabs content slot, and adjust tests
This commit is contained in:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-12-29 - 3.7.0 - feat(dees-contextmenu,dees-appui-tabs,test)
|
||||||
|
Prevent double-destruction of context menus, await window layer teardown, update destroyAll behavior, remove tabs content slot, and adjust tests
|
||||||
|
|
||||||
|
- Add isDestroying guard in DeesContextmenu.destroy to avoid double-destruction.
|
||||||
|
- Await windowLayer.destroy() to ensure the window layer is fully torn down before continuing.
|
||||||
|
- Ensure submenu timeouts are cleared and submenu.destroy() is awaited during teardown.
|
||||||
|
- Change destroyAll to locate the root/top-level menu and destroy from the root to cascade teardown reliably.
|
||||||
|
- Remove the .content wrapper and the <slot> output from dees-appui-tabs (demo updated to render content outside the component) — this is a breaking change to the tabs API (slotted content no longer rendered).
|
||||||
|
- Increase test timeout in test.contextmenu-nested-close.browser.ts to 600ms to account for ~300ms windowLayer destruction + ~100ms context menu delay.
|
||||||
|
|
||||||
## 2025-12-29 - 3.6.1 - fix(readme)
|
## 2025-12-29 - 3.6.1 - fix(readme)
|
||||||
document new App UI APIs to control main/secondary menu and content tabs visibility
|
document new App UI APIs to control main/secondary menu and content tabs visibility
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ tap.test('should close all parent menus when clicking action in nested submenu',
|
|||||||
expect(childItem).toBeTruthy();
|
expect(childItem).toBeTruthy();
|
||||||
childItem!.click();
|
childItem!.click();
|
||||||
|
|
||||||
// Wait for menus to close
|
// Wait for menus to close (windowLayer destruction takes 300ms + context menu 100ms)
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise(resolve => setTimeout(resolve, 600));
|
||||||
|
|
||||||
// Verify action was called
|
// Verify action was called
|
||||||
expect(actionCalled).toEqual(true);
|
expect(actionCalled).toEqual(true);
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@design.estate/dees-catalog',
|
name: '@design.estate/dees-catalog',
|
||||||
version: '3.6.1',
|
version: '3.7.0',
|
||||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,9 +67,8 @@ export const demoFunc = () => {
|
|||||||
<div class="demo-container">
|
<div class="demo-container">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">Horizontal Tabs with Animated Indicator</div>
|
<div class="section-title">Horizontal Tabs with Animated Indicator</div>
|
||||||
<dees-appui-tabs .tabs=${horizontalTabs}>
|
<dees-appui-tabs .tabs=${horizontalTabs}></dees-appui-tabs>
|
||||||
${demoContent('Select a tab to see the smooth sliding animation of the indicator. The indicator automatically adjusts its width to match the tab content with minimal padding.')}
|
${demoContent('Select a tab to see the smooth sliding animation of the indicator. The indicator automatically adjusts its width to match the tab content with minimal padding.')}
|
||||||
</dees-appui-tabs>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@@ -82,9 +81,8 @@ export const demoFunc = () => {
|
|||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">Without Indicator</div>
|
<div class="section-title">Without Indicator</div>
|
||||||
<dees-appui-tabs .showTabIndicator=${false} .tabs=${noIndicatorTabs}>
|
<dees-appui-tabs .showTabIndicator=${false} .tabs=${noIndicatorTabs}></dees-appui-tabs>
|
||||||
${demoContent('Tabs can also be used without the animated indicator by setting showTabIndicator to false.')}
|
${demoContent('Tabs can also be used without the animated indicator by setting showTabIndicator to false.')}
|
||||||
</dees-appui-tabs>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -198,19 +198,12 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 32px 24px;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
|
||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.renderTabsWrapper()}
|
${this.renderTabsWrapper()}
|
||||||
<div class="content">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export class DeesContextmenu extends DeesElement {
|
|||||||
private submenu: DeesContextmenu | null = null;
|
private submenu: DeesContextmenu | null = null;
|
||||||
private submenuTimeout: any = null;
|
private submenuTimeout: any = null;
|
||||||
private parentMenu: DeesContextmenu | null = null;
|
private parentMenu: DeesContextmenu | null = null;
|
||||||
|
private isDestroying: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -416,27 +417,33 @@ export class DeesContextmenu extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async destroy() {
|
public async destroy() {
|
||||||
|
// Guard against double-destruction
|
||||||
|
if (this.isDestroying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isDestroying = true;
|
||||||
|
|
||||||
// Clear timeout
|
// Clear timeout
|
||||||
if (this.submenuTimeout) {
|
if (this.submenuTimeout) {
|
||||||
clearTimeout(this.submenuTimeout);
|
clearTimeout(this.submenuTimeout);
|
||||||
this.submenuTimeout = null;
|
this.submenuTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy submenu first
|
// Destroy submenu first
|
||||||
if (this.submenu) {
|
if (this.submenu) {
|
||||||
await this.submenu.destroy();
|
await this.submenu.destroy();
|
||||||
this.submenu = null;
|
this.submenu = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only destroy window layer if this is not a submenu
|
// Only destroy window layer if this is not a submenu
|
||||||
if (this.windowLayer && !this.parentMenu) {
|
if (this.windowLayer && !this.parentMenu) {
|
||||||
this.windowLayer.destroy();
|
await this.windowLayer.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.style.opacity = '0';
|
this.style.opacity = '0';
|
||||||
this.style.transform = 'scale(0.95) translateY(-10px)';
|
this.style.transform = 'scale(0.95) translateY(-10px)';
|
||||||
await domtools.plugins.smartdelay.delayFor(100);
|
await domtools.plugins.smartdelay.delayFor(100);
|
||||||
|
|
||||||
if (this.parentElement) {
|
if (this.parentElement) {
|
||||||
this.parentElement.removeChild(this);
|
this.parentElement.removeChild(this);
|
||||||
}
|
}
|
||||||
@@ -446,13 +453,14 @@ export class DeesContextmenu extends DeesElement {
|
|||||||
* Destroys this menu and all parent menus in the chain
|
* Destroys this menu and all parent menus in the chain
|
||||||
*/
|
*/
|
||||||
public async destroyAll() {
|
public async destroyAll() {
|
||||||
// First destroy parent menus if they exist
|
// Find the root menu (top-level parent)
|
||||||
if (this.parentMenu) {
|
let rootMenu: DeesContextmenu = this;
|
||||||
await this.parentMenu.destroyAll();
|
while (rootMenu.parentMenu) {
|
||||||
} else {
|
rootMenu = rootMenu.parentMenu;
|
||||||
// If we're at the top level, just destroy this menu
|
|
||||||
await this.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy from the root - this will cascade through all submenus
|
||||||
|
await rootMenu.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user