diff --git a/.changeset/heavy-cups-peel.md b/.changeset/heavy-cups-peel.md new file mode 100644 index 00000000000..7cd119236e2 --- /dev/null +++ b/.changeset/heavy-cups-peel.md @@ -0,0 +1,5 @@ +--- +"@siemens/ix": patch +--- + +Update `@floating-ui/dom` dependency to fix a wrong placement of the `ix-dropdown` if the dropdown is placed inside a `dialog`-element with animations in certain environments. diff --git a/packages/angular-test-app/src/select/select.spec.ts b/packages/angular-test-app/src/test/select/select-form.spec.ts similarity index 68% rename from packages/angular-test-app/src/select/select.spec.ts rename to packages/angular-test-app/src/test/select/select-form.spec.ts index f79c77f8761..27786eba28a 100644 --- a/packages/angular-test-app/src/select/select.spec.ts +++ b/packages/angular-test-app/src/test/select/select-form.spec.ts @@ -13,9 +13,11 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing'; import { IxModule } from '@siemens/ix-angular'; import { By } from '@angular/platform-browser'; +import { waitForHydration } from '../utils/wait-for-hydration'; +import { checkForError } from '../utils/check-for-error'; @Component({ - selector: 'ix-example-form-select', + selector: 'ix-example-select-form', template: `
@@ -35,7 +37,7 @@ import { By } from '@angular/platform-browser'; `, }) -class SelectComponent { +class SelectFormComponent { public form = new FormGroup({ select: new FormControl('1') }); value = '3'; selection = ['3', '4', '5']; @@ -46,45 +48,19 @@ class SelectComponent { } } -function waitForHydration(element: HTMLElement, timeout = 5000): Promise { - return new Promise((resolve, reject) => { - const interval = 50; - let elapsedTime = 0; - - const checkClass = () => { - if (element.classList.contains('hydrated')) { - resolve(); - } else if (elapsedTime >= timeout) { - reject(new Error(`Timeout waiting for hydration`)); - } else { - elapsedTime += interval; - setTimeout(checkClass, interval); - } - }; - - checkClass(); - }); -} - -function checkForError(consoleSpy: jasmine.Spy, errorName: string): boolean { - return consoleSpy.calls - .allArgs() - .some((arg) => arg[0].toString().includes(errorName)); -} - describe('SelectFormComponent', () => { - let component: SelectComponent; - let fixture: ComponentFixture; + let component: SelectFormComponent; + let fixture: ComponentFixture; let consoleSpy: jasmine.Spy; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [SelectComponent], + declarations: [SelectFormComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [FormsModule, ReactiveFormsModule, IxModule.forRoot()], }).compileComponents(); - fixture = TestBed.createComponent(SelectComponent); + fixture = TestBed.createComponent(SelectFormComponent); component = fixture.componentInstance; consoleSpy = spyOn(console, 'error').and.callThrough(); @@ -98,7 +74,9 @@ describe('SelectFormComponent', () => { }); it('should change the form control value', async () => { - const select = fixture.debugElement.query(By.css('ix-select[formControlName="select"]')); + const select = fixture.debugElement.query( + By.css('ix-select[formControlName="select"]') + ); await waitForHydration(select.nativeElement); @@ -110,7 +88,9 @@ describe('SelectFormComponent', () => { }); it('should change the input value and check for errors', async () => { - const select = fixture.debugElement.query(By.css('ix-select:not([formControlName="select"])')); + const select = fixture.debugElement.query( + By.css('ix-select:not([formControlName="select"])') + ); await waitForHydration(select.nativeElement); diff --git a/packages/angular-test-app/src/test/utils/check-for-error.ts b/packages/angular-test-app/src/test/utils/check-for-error.ts new file mode 100644 index 00000000000..39064b4cd3e --- /dev/null +++ b/packages/angular-test-app/src/test/utils/check-for-error.ts @@ -0,0 +1,8 @@ +export function checkForError( + consoleSpy: jasmine.Spy, + errorName: string +): boolean { + return consoleSpy.calls + .allArgs() + .some((arg) => arg[0].toString().includes(errorName)); +} diff --git a/packages/angular-test-app/src/test/utils/wait-for-hydration.ts b/packages/angular-test-app/src/test/utils/wait-for-hydration.ts new file mode 100644 index 00000000000..92fa7c480fd --- /dev/null +++ b/packages/angular-test-app/src/test/utils/wait-for-hydration.ts @@ -0,0 +1,22 @@ +export function waitForHydration( + element: HTMLElement, + timeout = 5000 +): Promise { + return new Promise((resolve, reject) => { + const interval = 50; + let elapsedTime = 0; + + const checkClass = () => { + if (element.classList.contains('hydrated')) { + resolve(); + } else if (elapsedTime >= timeout) { + reject(new Error(`Timeout waiting for hydration`)); + } else { + elapsedTime += interval; + setTimeout(checkClass, interval); + } + }; + + checkClass(); + }); +} diff --git a/packages/core/package.json b/packages/core/package.json index fe8a61d2d50..7963f333518 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -43,7 +43,7 @@ "pkg": "pnpm pack" }, "dependencies": { - "@floating-ui/dom": "^1.6.10", + "@floating-ui/dom": "^1.6.13", "@stencil/core": "~4.17.0", "@types/luxon": "^3.3.7", "animejs": "~3.2.1", diff --git a/packages/core/src/components/dropdown/test/dropdown.ct.ts b/packages/core/src/components/dropdown/test/dropdown.ct.ts index 2bf06045bde..3289e7c3df5 100644 --- a/packages/core/src/components/dropdown/test/dropdown.ct.ts +++ b/packages/core/src/components/dropdown/test/dropdown.ct.ts @@ -694,3 +694,51 @@ test.describe('A11y', () => { }); }); }); + +test('Dropdown works in floating-ui', async ({ mount, page }) => { + await mount(` + + + + Open + + + + + + `); + + await page.evaluate(() => { + const dialog = document.getElementById('dialog') as HTMLDialogElement; + dialog.showModal(); + }); + + const trigger = page.locator('#trigger'); + await trigger.click(); + + const dropdown = page.locator('#dropdown'); + + const dropdownRect = (await dropdown.boundingBox())!; + const triggerRect = (await trigger.boundingBox())!; + + expect(Math.round(dropdownRect.x)).toBe(Math.round(triggerRect.x)); + expect(Math.round(dropdownRect.y)).toBe( + Math.round(triggerRect.y + triggerRect.height) + ); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f08ae0c327f..276d5132b2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -280,8 +280,8 @@ importers: packages/core: dependencies: '@floating-ui/dom': - specifier: ^1.6.10 - version: 1.6.10 + specifier: ^1.6.13 + version: 1.6.13 '@popperjs/core': specifier: ^2.11.0 version: 2.11.8 @@ -4239,11 +4239,11 @@ packages: '@floating-ui/core@1.6.7': resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==} - '@floating-ui/dom@1.6.10': - resolution: {integrity: sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==} + '@floating-ui/dom@1.6.13': + resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} - '@floating-ui/utils@0.2.7': - resolution: {integrity: sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==} + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} @@ -20960,14 +20960,14 @@ snapshots: '@floating-ui/core@1.6.7': dependencies: - '@floating-ui/utils': 0.2.7 + '@floating-ui/utils': 0.2.9 - '@floating-ui/dom@1.6.10': + '@floating-ui/dom@1.6.13': dependencies: '@floating-ui/core': 1.6.7 - '@floating-ui/utils': 0.2.7 + '@floating-ui/utils': 0.2.9 - '@floating-ui/utils@0.2.7': {} + '@floating-ui/utils@0.2.9': {} '@gar/promisify@1.1.3': {}