From ed208ad39c3e9b4fdc28e629ca37faaf8ba1b5ae Mon Sep 17 00:00:00 2001 From: Umanskiy Aleksey Date: Thu, 14 Aug 2025 18:08:00 +0300 Subject: [PATCH 1/4] fix(tabset): fixed tabset active input --- .../docs-section/docs-section.component.ts | 21 ++++++++- .../tabs/src/lib/demos/dynamic/dynamic.ts | 2 +- src/tabs/tabset.component.ts | 44 +++++++++++++++---- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/libs/common-docs/src/lib/docs-section/docs-section.component.ts b/libs/common-docs/src/lib/docs-section/docs-section.component.ts index ca4b636b15..255a5613cf 100644 --- a/libs/common-docs/src/lib/docs-section/docs-section.component.ts +++ b/libs/common-docs/src/lib/docs-section/docs-section.component.ts @@ -1,6 +1,6 @@ import { ContentSection } from '../models/content-section.model'; import { ChangeDetectorRef, Component, Injector, Input } from '@angular/core'; -import { Router, NavigationExtras } from '@angular/router'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; const availableTabsPaths = ['overview', 'api', 'examples'] as const; type AvailableTabsPathsType = typeof availableTabsPaths[number]; @@ -36,9 +36,28 @@ export class DocsSectionComponent { constructor( private injector: Injector, + private activatedRoute: ActivatedRoute, private router: Router, private changeDetection: ChangeDetectorRef ) { + this.activatedRoute.queryParamMap.subscribe((params) => { + this.initActiveTab(params.get('tab')?.toString()); + }); + } + + initActiveTab(activeTab?: string) { + this.resetTabs(); + if (!activeTab || !this.checkActiveTab(activeTab)) { + this.overview = true; + this.onSelect('overview'); + return; + } + + this[activeTab as AvailableTabsPathsType] = true; + } + + checkActiveTab(activeTab: string): boolean { + return activeTab === 'overview' || activeTab === 'api' || activeTab === 'examples'; } onSelect(tabName: string) { diff --git a/libs/doc-pages/tabs/src/lib/demos/dynamic/dynamic.ts b/libs/doc-pages/tabs/src/lib/demos/dynamic/dynamic.ts index 5b5b623166..045d6e3b8b 100644 --- a/libs/doc-pages/tabs/src/lib/demos/dynamic/dynamic.ts +++ b/libs/doc-pages/tabs/src/lib/demos/dynamic/dynamic.ts @@ -19,7 +19,7 @@ interface ITab { export class DemoTabsDynamicComponent { tabs: ITab[] = [ { title: 'Dynamic Title 1', content: 'Dynamic content 1', removable: false, disabled: false}, - { title: 'Dynamic Title 2', content: 'Dynamic content 2', removable: false, disabled: false}, + { title: 'Dynamic Title 2', content: 'Dynamic content 2', removable: false, disabled: false, active: true}, { title: 'Dynamic Title 3', content: 'Dynamic content 3', removable: true, disabled: false} ]; diff --git a/src/tabs/tabset.component.ts b/src/tabs/tabset.component.ts index e37400092a..85c50f1b6f 100644 --- a/src/tabs/tabset.component.ts +++ b/src/tabs/tabset.component.ts @@ -65,6 +65,7 @@ export class TabsetComponent implements OnDestroy { protected _justified = false; protected _type = 'tabs'; protected _isKeysAllowed = true; + private defaultActivationScheduled = false; constructor( config: TabsetConfig, @@ -86,26 +87,51 @@ export class TabsetComponent implements OnDestroy { // Default behavior - add to end this.tabs.push(tab); } - - // Set active if it's the first tab and not already active - tab.active = this.tabs.length === 1 && !tab.active; + + // Activation logic + // - If the newly added tab is already active, set it active again to leverage + // TabDirective's setter which will deactivate others. + // - Otherwise, schedule a single deferred default-first activation if none active. + if (tab.active) { + tab.active = true; + return; + } + + if (!this.defaultActivationScheduled) { + this.defaultActivationScheduled = true; + // Defer default activation to avoid racing template-driven [active] inputs + Promise.resolve().then(() => { + this.defaultActivationScheduled = false; + // Guard in case the tabset changed meanwhile + if (!this.tabs.length) { + return; + } + if (this.tabs.some((t: TabDirective) => !!t.active)) { + return; + } + const firstEnabled = this.tabs.find((t: TabDirective) => !t.disabled); + if (firstEnabled) { + firstEnabled.active = true; + } + }); + } } private insertTabByOrder(tab: TabDirective): void { let insertIndex = this.tabs.length; // Default to end - + // Find the correct position to insert the ordered tab for (let i = 0; i < this.tabs.length; i++) { const existingTab = this.tabs[i]; - + // If the existing tab has an order and the new tab's order is less than it - if (existingTab.tabOrder !== undefined && - tab.tabOrder !== undefined && + if (existingTab.tabOrder !== undefined && + tab.tabOrder !== undefined && tab.tabOrder < existingTab.tabOrder) { insertIndex = i; break; } - + // If we reach an unordered tab, we want to insert before it // (ordered tabs should come before unordered tabs) if (existingTab.tabOrder === undefined) { @@ -113,7 +139,7 @@ export class TabsetComponent implements OnDestroy { break; } } - + // Insert at the found position this.tabs.splice(insertIndex, 0, tab); } From fc26f2159a3f6ff29ee381d0cd1466f575fcd836 Mon Sep 17 00:00:00 2001 From: Umanskiy Aleksey Date: Thu, 14 Aug 2025 18:27:24 +0300 Subject: [PATCH 2/4] fix(tabset): updated tests --- src/tabs/testing/tabset.component.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tabs/testing/tabset.component.spec.ts b/src/tabs/testing/tabset.component.spec.ts index 4c6297ef56..b0e54b7081 100644 --- a/src/tabs/testing/tabset.component.spec.ts +++ b/src/tabs/testing/tabset.component.spec.ts @@ -118,7 +118,10 @@ describe('Component: Tabs', () => { fixture.detectChanges(); }); - it('should select first tab as active by default', () => { + it('should select first tab as active by default', async () => { + // Default activation is deferred to a microtask; wait for it before asserting + await fixture.whenStable(); + fixture.detectChanges(); expectActiveTabs(element, [true, false, false, false]); }); From 19aa793d1ce79a77f1857ffa6951937b116b1da3 Mon Sep 17 00:00:00 2001 From: Umanskiy Aleksey Date: Tue, 19 Aug 2025 16:45:31 +0300 Subject: [PATCH 3/4] 20.0.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09c084a4f9..f888826062 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ngx-bootstrap-base", - "version": "20.0.0", + "version": "20.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ngx-bootstrap-base", - "version": "20.0.0", + "version": "20.0.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index be1e5ced24..a0a8392020 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap-base", - "version": "20.0.0", + "version": "20.0.1", "license": "MIT", "author": "Dmitriy Shekhovtsov ", "schematics": "./schematics/src/collection.json", From 21e69f6b4bac0e364565390dce435fe9b0bc4b61 Mon Sep 17 00:00:00 2001 From: Umanskiy Aleksey Date: Tue, 19 Aug 2025 16:47:38 +0300 Subject: [PATCH 4/4] chore(version-bump): updated to v20.0.1 --- apps/ngx-bootstrap-docs/src/assets/json/current-version.json | 2 +- src/accordion/package.json | 2 +- src/alert/package.json | 2 +- src/buttons/package.json | 2 +- src/carousel/package.json | 2 +- src/chronos/package.json | 2 +- src/collapse/package.json | 2 +- src/component-loader/package.json | 2 +- src/datepicker/package.json | 2 +- src/dropdown/package.json | 2 +- src/focus-trap/package.json | 2 +- src/locale/package.json | 2 +- src/mini-ngrx/package.json | 2 +- src/modal/package.json | 2 +- src/pagination/package.json | 2 +- src/popover/package.json | 2 +- src/positioning/package.json | 2 +- src/progressbar/package.json | 2 +- src/rating/package.json | 2 +- src/root/package.json | 2 +- src/schematics/package.json | 2 +- src/schematics/src/utils/current_dependency_versions.json | 2 +- src/sortable/package.json | 2 +- src/tabs/package.json | 2 +- src/timepicker/package.json | 2 +- src/tooltip/package.json | 2 +- src/typeahead/package.json | 2 +- src/utils/package.json | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/ngx-bootstrap-docs/src/assets/json/current-version.json b/apps/ngx-bootstrap-docs/src/assets/json/current-version.json index 424a7f9977..d92fd98417 100644 --- a/apps/ngx-bootstrap-docs/src/assets/json/current-version.json +++ b/apps/ngx-bootstrap-docs/src/assets/json/current-version.json @@ -1,3 +1,3 @@ { - "version": "20.0.0" + "version": "20.0.1" } diff --git a/src/accordion/package.json b/src/accordion/package.json index 0cc7d6c945..46c1fef03f 100644 --- a/src/accordion/package.json +++ b/src/accordion/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/accordion", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/alert/package.json b/src/alert/package.json index 8eed6c2ffc..35a5e51016 100644 --- a/src/alert/package.json +++ b/src/alert/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/alert", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/buttons/package.json b/src/buttons/package.json index faffd196a1..16b8c0bc73 100644 --- a/src/buttons/package.json +++ b/src/buttons/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/buttons", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/carousel/package.json b/src/carousel/package.json index e9fd879243..a7d46bd78d 100644 --- a/src/carousel/package.json +++ b/src/carousel/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/carousel", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/chronos/package.json b/src/chronos/package.json index d064c62e81..62c77dbfe9 100644 --- a/src/chronos/package.json +++ b/src/chronos/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/chronos", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/collapse/package.json b/src/collapse/package.json index 9e047d5d52..8d5b4b7cb2 100644 --- a/src/collapse/package.json +++ b/src/collapse/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/collapse", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/component-loader/package.json b/src/component-loader/package.json index 7fd1834905..965292b1f0 100644 --- a/src/component-loader/package.json +++ b/src/component-loader/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/component-loader", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/datepicker/package.json b/src/datepicker/package.json index 002e6cfd96..a4e46e44eb 100644 --- a/src/datepicker/package.json +++ b/src/datepicker/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/datepicker", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/dropdown/package.json b/src/dropdown/package.json index df4261245c..de5be9d21f 100644 --- a/src/dropdown/package.json +++ b/src/dropdown/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/dropdown", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/focus-trap/package.json b/src/focus-trap/package.json index 23ac66b309..bd2071ed77 100644 --- a/src/focus-trap/package.json +++ b/src/focus-trap/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/focus-trap", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/locale/package.json b/src/locale/package.json index a148ce4573..718961f8ef 100644 --- a/src/locale/package.json +++ b/src/locale/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/locale", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/mini-ngrx/package.json b/src/mini-ngrx/package.json index ce0714fdae..7bb82b7374 100644 --- a/src/mini-ngrx/package.json +++ b/src/mini-ngrx/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/mini-ngrx", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/modal/package.json b/src/modal/package.json index 7b39513465..58f144ded7 100644 --- a/src/modal/package.json +++ b/src/modal/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/modal", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/pagination/package.json b/src/pagination/package.json index 442557ab4e..38ef4b8593 100644 --- a/src/pagination/package.json +++ b/src/pagination/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/pagination", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/popover/package.json b/src/popover/package.json index 5b75bc6f74..a095506456 100644 --- a/src/popover/package.json +++ b/src/popover/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/popover", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/positioning/package.json b/src/positioning/package.json index 9076094832..3356ce023d 100644 --- a/src/positioning/package.json +++ b/src/positioning/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/positioning", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/progressbar/package.json b/src/progressbar/package.json index caca30f332..9473378d15 100644 --- a/src/progressbar/package.json +++ b/src/progressbar/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/progressbar", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/rating/package.json b/src/rating/package.json index 503a9d5679..8994464617 100644 --- a/src/rating/package.json +++ b/src/rating/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/rating", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/root/package.json b/src/root/package.json index efbee16fa9..4e3fa55c6d 100644 --- a/src/root/package.json +++ b/src/root/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap", - "version": "20.0.0", + "version": "20.0.1", "description": "Angular Bootstrap", "author": "Dmitriy Shekhovtsov ", "license": "MIT", diff --git a/src/schematics/package.json b/src/schematics/package.json index 5bb02816c3..c541a350cd 100644 --- a/src/schematics/package.json +++ b/src/schematics/package.json @@ -1,6 +1,6 @@ { "name": "schematics", - "version": "20.0.0", + "version": "20.0.1", "schematics": "./collection.json", "author": "Dmitriy Shekhovtsov ", "license": "MIT" diff --git a/src/schematics/src/utils/current_dependency_versions.json b/src/schematics/src/utils/current_dependency_versions.json index 8f81bbf619..844cd863d1 100644 --- a/src/schematics/src/utils/current_dependency_versions.json +++ b/src/schematics/src/utils/current_dependency_versions.json @@ -1,4 +1,4 @@ { - "NGX_BOOTSTRAP_VERSION": "20.0.0", + "NGX_BOOTSTRAP_VERSION": "20.0.1", "BOOTSTRAP_VERSION": "^5.2.3" } diff --git a/src/sortable/package.json b/src/sortable/package.json index 496a0c0558..4bb8d7713f 100644 --- a/src/sortable/package.json +++ b/src/sortable/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/sortable", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/tabs/package.json b/src/tabs/package.json index 4eb945136d..0789bbad7e 100644 --- a/src/tabs/package.json +++ b/src/tabs/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/tabs", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/timepicker/package.json b/src/timepicker/package.json index 10961bbcbf..770da7d8b9 100644 --- a/src/timepicker/package.json +++ b/src/timepicker/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/timepicker", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/tooltip/package.json b/src/tooltip/package.json index e735764012..f3748d3e9d 100644 --- a/src/tooltip/package.json +++ b/src/tooltip/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/tooltip", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/typeahead/package.json b/src/typeahead/package.json index 86bbb902c7..0442fbd06b 100644 --- a/src/typeahead/package.json +++ b/src/typeahead/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/typeahead", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" } diff --git a/src/utils/package.json b/src/utils/package.json index 063711d436..aacecfcfb7 100644 --- a/src/utils/package.json +++ b/src/utils/package.json @@ -1,6 +1,6 @@ { "name": "ngx-bootstrap/utils", - "version": "20.0.0", + "version": "20.0.1", "author": "Dmitriy Shekhovtsov ", "license": "MIT" }