Skip to content

Commit fb2fbce

Browse files
authored
Merge branch 'development' into patch-1
2 parents 3c61fd0 + bdac5d0 commit fb2fbce

File tree

39 files changed

+659
-48
lines changed

39 files changed

+659
-48
lines changed

apps/ngx-bootstrap-docs-e2e/src/support/accordion.po.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ export class AccordionPo extends BasePo {
3838
}
3939

4040
async clickOnAccordionGroup(baseSelector: string, itemIndex: number) {
41-
await this.page
41+
const accordionButton = this.page
4242
.locator(baseSelector + ' accordion-group button')
43-
.nth(itemIndex)
44-
.click();
43+
.nth(itemIndex);
44+
45+
await accordionButton.waitFor({ state: 'visible', timeout: 10000 });
46+
await accordionButton.click();
4547
}
4648

4749
async expectItemContentVisible(baseSelector: string, itemIndex: number, visible: boolean) {

apps/ngx-bootstrap-docs-e2e/src/support/alerts.po.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ export class AlertsPo extends BasePo {
3131
local: '.alert-md-local',
3232
};
3333

34-
async expectAlertVisible(baseSelector: string, alertType: string, visible = true, timeout = 5000) {
35-
await expect(await this.page
36-
.locator(baseSelector + ` ${this.alertType[alertType]}`)
37-
).toBeVisible({ timeout: timeout, visible: visible });
34+
async expectAlertVisible(baseSelector: string, alertType: string, visible = true, timeout = 10000) {
35+
const alertElement = this.page.locator(baseSelector + ` ${this.alertType[alertType]}`);
36+
await alertElement.waitFor({ state: visible ? 'visible' : 'hidden', timeout: timeout });
37+
await expect(alertElement).toBeVisible({ visible: visible });
3838
}
3939

4040
async expectBtnNotExist(baseSelector: string, buttonName: string) {

apps/ngx-bootstrap-docs-e2e/src/support/base.po.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ export class BasePo {
1616
async navigateTo() {
1717
const bsVersionRoute = process.env['bsVersion'] ? `?_bsVersion=bs${process.env['bsVersion']}` : '';
1818
await this.page.goto(this.pageUrl + bsVersionRoute);
19+
await this.page.waitForLoadState('domcontentloaded');
20+
// Ensure Overview tab is active by clicking on it
21+
const overviewTab = this.page.locator('a[aria-selected="false"]').getByText('Overview');
22+
if (await overviewTab.isVisible()) {
23+
await overviewTab.click();
24+
await this.page.waitForTimeout(1000); // Wait for tab content to load
25+
}
1926
}
2027

2128
async scrollToMenu(menuTxt: string) {

apps/ngx-bootstrap-docs-e2e/src/support/buttons.po.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ export class ButtonsPo extends BasePo {
2929
};
3030

3131
async expectBtnVisible(baseSelector: string, btnSelector: string, btnName: string, btnNumber?: number) {
32-
await expect(await this.page
32+
const btnElement = this.page
3333
.locator(baseSelector + ` ${btnSelector}`)
3434
.getByText(btnName)
35-
.nth(btnNumber ? btnNumber : 0)
36-
).toBeVisible();
35+
.nth(btnNumber ? btnNumber : 0);
36+
37+
await btnElement.waitFor({ state: 'visible', timeout: 10000 });
38+
await expect(btnElement).toBeVisible();
3739
}
3840

3941
async expectBtnEnabled(baseSelector: string, btnName: string, enabled = true, btnNumber?: number) {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "19.0.3"
2+
"version": "19.0.4"
33
}

e2e/issues/issue-823.spec.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Issue #823: Tab ordering with ngIf/dynamic directives', () => {
4+
test.beforeEach(async ({ page }) => {
5+
await page.goto('/');
6+
await page.click('text=Tabs');
7+
});
8+
9+
test('should display tabs in correct order regardless of creation order', async ({ page }) => {
10+
// Wait for tabs to load
11+
await page.waitForSelector('.nav-tabs');
12+
13+
// Check that tabs exist and are in some order
14+
const tabs = page.locator('.nav-item');
15+
const tabCount = await tabs.count();
16+
17+
if (tabCount > 0) {
18+
// Tabs should be visible and clickable
19+
expect(await tabs.first().isVisible()).toBe(true);
20+
21+
// Should be able to click on tabs
22+
await tabs.first().click();
23+
24+
// Active tab should have active class
25+
const activeTab = page.locator('.nav-item.active, .nav-link.active');
26+
expect(await activeTab.count()).toBeGreaterThan(0);
27+
}
28+
});
29+
30+
test('should maintain tab functionality with ordered tabs', async ({ page }) => {
31+
// Test that tab ordering doesn't break basic functionality
32+
await page.waitForSelector('.nav-tabs');
33+
34+
const tabs = page.locator('.nav-link');
35+
const tabCount = await tabs.count();
36+
37+
if (tabCount >= 2) {
38+
// Click first tab
39+
await tabs.first().click();
40+
expect(await tabs.first().getAttribute('class')).toContain('active');
41+
42+
// Click second tab
43+
await tabs.nth(1).click();
44+
expect(await tabs.nth(1).getAttribute('class')).toContain('active');
45+
46+
// First tab should no longer be active
47+
expect(await tabs.first().getAttribute('class')).not.toContain('active');
48+
}
49+
});
50+
51+
test('should handle dynamic tab addition and removal', async ({ page }) => {
52+
// Test dynamic behavior that tabOrder should help with
53+
await page.waitForSelector('.nav-tabs');
54+
55+
const initialTabCount = await page.locator('.nav-item').count();
56+
57+
// Look for any buttons that might add/remove tabs dynamically
58+
const addButtons = page.locator('button:has-text("Add"), button:has-text("New Tab")');
59+
const removeButtons = page.locator('button:has-text("Remove"), .bs-remove-tab');
60+
61+
// If there are dynamic controls, test them
62+
if (await addButtons.count() > 0) {
63+
await addButtons.first().click();
64+
65+
// Tab count might change
66+
const newTabCount = await page.locator('.nav-item').count();
67+
expect(newTabCount).toBeGreaterThanOrEqual(initialTabCount);
68+
}
69+
70+
// Test removable tabs if they exist
71+
if (await removeButtons.count() > 0) {
72+
expect(await removeButtons.first().isVisible()).toBe(true);
73+
}
74+
});
75+
76+
test('should preserve tab content when ordering changes', async ({ page }) => {
77+
// Ensure tab content is preserved with ordering
78+
await page.waitForSelector('.nav-tabs');
79+
80+
const tabs = page.locator('.nav-link');
81+
const tabContent = page.locator('.tab-content, .tab-pane');
82+
83+
if (await tabs.count() > 0 && await tabContent.count() > 0) {
84+
// Click on different tabs and verify content shows
85+
for (let i = 0; i < Math.min(await tabs.count(), 3); i++) {
86+
await tabs.nth(i).click();
87+
88+
// Content should be visible
89+
expect(await tabContent.first().isVisible()).toBe(true);
90+
}
91+
}
92+
});
93+
94+
test('should handle conditional tabs with ngIf-like behavior', async ({ page }) => {
95+
// Simulate the ngIf scenario from the issue
96+
await page.waitForSelector('.nav-tabs');
97+
98+
// Check if there are any conditional/dynamic elements
99+
const conditionalElements = page.locator('[*ngIf], [ng-if], .conditional-tab');
100+
101+
// Basic functionality should work even with conditional rendering
102+
const tabs = page.locator('.nav-link');
103+
if (await tabs.count() > 0) {
104+
// Should be able to navigate between tabs
105+
await tabs.first().click();
106+
107+
// Tab should become active
108+
const activeClass = await tabs.first().getAttribute('class');
109+
expect(activeClass).toContain('active');
110+
}
111+
});
112+
113+
test('should maintain accessibility with ordered tabs', async ({ page }) => {
114+
// Test that tab ordering doesn't break accessibility
115+
await page.waitForSelector('.nav-tabs');
116+
117+
const tabs = page.locator('.nav-link');
118+
119+
if (await tabs.count() > 0) {
120+
// Check for proper ARIA attributes
121+
const firstTab = tabs.first();
122+
123+
// Should have proper role
124+
const role = await firstTab.getAttribute('role');
125+
expect(role).toBe('tab');
126+
127+
// Should have aria-selected
128+
const ariaSelected = await firstTab.getAttribute('aria-selected');
129+
expect(ariaSelected).toBeTruthy();
130+
131+
// Should be focusable
132+
await firstTab.focus();
133+
const focusedElement = page.locator(':focus');
134+
expect(await focusedElement.count()).toBe(1);
135+
}
136+
});
137+
138+
test('should handle keyboard navigation with ordered tabs', async ({ page }) => {
139+
// Test keyboard navigation works with tab ordering
140+
await page.waitForSelector('.nav-tabs');
141+
142+
const tabs = page.locator('.nav-link');
143+
144+
if (await tabs.count() >= 2) {
145+
// Focus first tab
146+
await tabs.first().focus();
147+
148+
// Use arrow keys to navigate
149+
await page.keyboard.press('ArrowRight');
150+
151+
// Should move focus (implementation may vary)
152+
const focusedElement = page.locator(':focus');
153+
expect(await focusedElement.count()).toBe(1);
154+
155+
// Test Tab key navigation
156+
await page.keyboard.press('Tab');
157+
158+
// Focus should move to next element
159+
expect(await focusedElement.count()).toBe(1);
160+
}
161+
});
162+
163+
test('should handle edge cases for tab ordering', async ({ page }) => {
164+
// Test edge cases that might occur with tab ordering
165+
await page.waitForSelector('.nav-tabs');
166+
167+
const tabs = page.locator('.nav-item');
168+
const initialCount = await tabs.count();
169+
170+
// Rapid clicking shouldn't break ordering
171+
if (initialCount >= 2) {
172+
await tabs.first().click();
173+
await tabs.nth(1).click();
174+
await tabs.first().click();
175+
176+
// Should still work correctly
177+
expect(await tabs.count()).toBe(initialCount);
178+
}
179+
180+
// Page refresh should maintain consistent ordering
181+
await page.reload();
182+
await page.waitForSelector('.nav-tabs');
183+
184+
const tabsAfterReload = page.locator('.nav-item');
185+
expect(await tabsAfterReload.count()).toBe(initialCount);
186+
});
187+
188+
test('should support tab ordering in different layouts', async ({ page }) => {
189+
// Test that tab ordering works in different tab layouts
190+
await page.waitForSelector('.nav-tabs');
191+
192+
// Look for different tab styles/layouts
193+
const verticalTabs = page.locator('.nav-tabs.flex-column, .nav-pills.flex-column');
194+
const justifiedTabs = page.locator('.nav-justified');
195+
const pillTabs = page.locator('.nav-pills');
196+
197+
// Test basic functionality regardless of layout
198+
const anyTabs = page.locator('.nav-link');
199+
200+
if (await anyTabs.count() > 0) {
201+
// Should work in any layout
202+
await anyTabs.first().click();
203+
204+
// Should have active state
205+
const activeTab = page.locator('.nav-link.active');
206+
expect(await activeTab.count()).toBe(1);
207+
}
208+
});
209+
});

libs/common-docs/src/lib/common/discover/discover.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class DiscoverComponent {
6565
link: 'https://trovimap.com/'
6666
},
6767
{
68-
logo: 'https://www.atmetis.nl/wp-content/uploads/2017/08/cropped-logo-atmetis-tagline-rgb1500px-1.png',
68+
logo: 'https://atmetis.nl/templates/assets/img/logo/atmetis-color.svg',
6969
name: 'AtMetis',
7070
description: 'Internal web-application for AtMetis - assessment company from Netherlands',
7171
link: 'https://www.atmetis.nl/'
@@ -78,7 +78,7 @@ export class DiscoverComponent {
7878
link: 'https://www.pramati.com/'
7979
},
8080
{
81-
logo: 'https://res.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1484678055/ectxiezxgzm3srv2jkvh.png',
81+
logo: 'https://employes.nl/static/employes-1504611522217dfcf2c76b401d63c44e.svg',
8282
name: 'Employes',
8383
description: 'Employes is a dutch based payroll-provider that offers a unique modern way to pay employees.\n',
8484
link: 'https://app.employes.nl/'

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-bootstrap-base",
3-
"version": "19.0.3",
3+
"version": "19.0.5",
44
"license": "MIT",
55
"author": "Dmitriy Shekhovtsov <[email protected]>",
66
"schematics": "./schematics/src/collection.json",

src/accordion/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-bootstrap/accordion",
3-
"version": "19.0.3",
3+
"version": "19.0.4",
44
"author": "Dmitriy Shekhovtsov <[email protected]>",
55
"license": "MIT"
66
}

0 commit comments

Comments
 (0)