diff --git a/changelog.md b/changelog.md index 554b895..36afd46 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # 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 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) document new App UI APIs to control main/secondary menu and content tabs visibility diff --git a/test/test.contextmenu-nested-close.browser.ts b/test/test.contextmenu-nested-close.browser.ts index 2eb15a9..d93ab6c 100644 --- a/test/test.contextmenu-nested-close.browser.ts +++ b/test/test.contextmenu-nested-close.browser.ts @@ -76,8 +76,8 @@ tap.test('should close all parent menus when clicking action in nested submenu', expect(childItem).toBeTruthy(); childItem!.click(); - // Wait for menus to close - await new Promise(resolve => setTimeout(resolve, 200)); + // Wait for menus to close (windowLayer destruction takes 300ms + context menu 100ms) + await new Promise(resolve => setTimeout(resolve, 600)); // Verify action was called expect(actionCalled).toEqual(true); diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 648d485..9c90516 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { 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.' } diff --git a/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.demo.ts b/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.demo.ts index 43fa509..ad638e4 100644 --- a/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.demo.ts +++ b/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.demo.ts @@ -67,9 +67,8 @@ export const demoFunc = () => {
Horizontal Tabs with Animated Indicator
- - ${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.')}
@@ -82,9 +81,8 @@ export const demoFunc = () => {
Without Indicator
- - ${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.')}
`; diff --git a/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.ts b/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.ts index ae21719..f93d056 100644 --- a/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.ts +++ b/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.ts @@ -198,19 +198,12 @@ export class DeesAppuiTabs extends DeesElement { z-index: 1; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); } - - .content { - padding: 32px 24px; - } `, ]; public render(): TemplateResult { return html` ${this.renderTabsWrapper()} -
- -
`; } diff --git a/ts_web/elements/dees-contextmenu/dees-contextmenu.ts b/ts_web/elements/dees-contextmenu/dees-contextmenu.ts index 71f0dcd..e798870 100644 --- a/ts_web/elements/dees-contextmenu/dees-contextmenu.ts +++ b/ts_web/elements/dees-contextmenu/dees-contextmenu.ts @@ -134,6 +134,7 @@ export class DeesContextmenu extends DeesElement { private submenu: DeesContextmenu | null = null; private submenuTimeout: any = null; private parentMenu: DeesContextmenu | null = null; + private isDestroying: boolean = false; constructor() { super(); @@ -416,27 +417,33 @@ export class DeesContextmenu extends DeesElement { } public async destroy() { + // Guard against double-destruction + if (this.isDestroying) { + return; + } + this.isDestroying = true; + // Clear timeout if (this.submenuTimeout) { clearTimeout(this.submenuTimeout); this.submenuTimeout = null; } - + // Destroy submenu first if (this.submenu) { await this.submenu.destroy(); this.submenu = null; } - + // Only destroy window layer if this is not a submenu if (this.windowLayer && !this.parentMenu) { - this.windowLayer.destroy(); + await this.windowLayer.destroy(); } - + this.style.opacity = '0'; this.style.transform = 'scale(0.95) translateY(-10px)'; await domtools.plugins.smartdelay.delayFor(100); - + if (this.parentElement) { this.parentElement.removeChild(this); } @@ -446,13 +453,14 @@ export class DeesContextmenu extends DeesElement { * Destroys this menu and all parent menus in the chain */ public async destroyAll() { - // First destroy parent menus if they exist - if (this.parentMenu) { - await this.parentMenu.destroyAll(); - } else { - // If we're at the top level, just destroy this menu - await this.destroy(); + // Find the root menu (top-level parent) + let rootMenu: DeesContextmenu = this; + while (rootMenu.parentMenu) { + rootMenu = rootMenu.parentMenu; } + + // Destroy from the root - this will cascade through all submenus + await rootMenu.destroy(); } }