diff --git a/frontend/e2e/app.spec.ts b/frontend/e2e/app.spec.ts index 7be1284..9e6e09d 100644 --- a/frontend/e2e/app.spec.ts +++ b/frontend/e2e/app.spec.ts @@ -1,4 +1,4 @@ -import { expect, Page, test } from '@playwright/test'; +import { expect, Locator, Page, test } from '@playwright/test'; async function mockUser(page: Page, authenticated: boolean, completeProfile = true): Promise { await page.unroute('**/api/user'); @@ -43,6 +43,27 @@ async function expectNoHorizontalOverflow(page: Page): Promise { expect(hasHorizontalOverflow).toBe(false); } +async function expectNoScrollbars(locator: Locator): Promise { + const scrollableElements = await locator.evaluate((element) => { + return [element, ...element.querySelectorAll('*')] + .filter((candidate) => { + const { overflowX, overflowY } = getComputedStyle(candidate); + const hasHorizontalScrollbar = + (overflowX === 'auto' || overflowX === 'scroll') && + candidate.scrollWidth > candidate.clientWidth; + const hasVerticalScrollbar = + (overflowY === 'auto' || overflowY === 'scroll') && + candidate.scrollHeight > candidate.clientHeight; + return hasHorizontalScrollbar || hasVerticalScrollbar; + }) + .map( + (candidate) => + `${candidate.tagName.toLowerCase()}.${candidate.getAttribute('class') ?? ''}`, + ); + }); + expect(scrollableElements).toEqual([]); +} + test.beforeEach(async ({ page }) => { await mockUser(page, false); }); @@ -74,13 +95,34 @@ test('requires a newly connected landlord to complete their profile', async ({ p const dialog = page.getByRole('dialog'); await expect(dialog.getByRole('heading', { name: 'Complétez votre profil' })).toBeVisible(); + await expectNoHorizontalOverflow(page); + await expectNoScrollbars(dialog); await page.keyboard.press('Escape'); await expect(dialog).toBeVisible(); - await expect(dialog.getByRole('button', { name: 'Déconnexion' })).toBeVisible(); + const logoutButton = dialog.getByRole('button', { name: 'Déconnexion' }); + const saveButton = dialog.getByRole('button', { name: 'Enregistrer le profil' }); + await expect(logoutButton).toBeVisible(); + await expect(saveButton).toBeVisible(); + + const [logoutBox, saveBox, actionBox, surfaceBox] = await Promise.all([ + logoutButton.boundingBox(), + saveButton.boundingBox(), + dialog.locator('.profile-actions').boundingBox(), + dialog.locator('.mat-mdc-dialog-surface').boundingBox(), + ]); + if (!logoutBox || !saveBox || !actionBox || !surfaceBox) { + throw new Error('Profile dialog actions must be measurable.'); + } + expect(logoutBox.x).toBeCloseTo(actionBox.x, 1); + expect(saveBox.x + saveBox.width).toBeCloseTo(actionBox.x + actionBox.width, 1); + expect(Math.abs(logoutBox.y - saveBox.y)).toBeLessThan(8); + expect(surfaceBox.y + surfaceBox.height - (actionBox.y + actionBox.height)).toBeGreaterThanOrEqual( + 16, + ); await dialog.getByLabel('Téléphone').fill('+33612345678'); await dialog.getByLabel('Adresse postale').fill('1 rue des Lilas, Lyon'); - await dialog.getByRole('button', { name: 'Enregistrer le profil' }).click(); + await saveButton.click(); await expect(dialog).toBeHidden(); await expect(page.getByTestId('primary-navigation')).toBeVisible(); diff --git a/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.css b/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.css index 0b5db33..0481703 100644 --- a/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.css +++ b/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.css @@ -3,9 +3,8 @@ width: min(36rem, calc(100vw - 3rem)); max-width: 100%; gap: var(--space-4, 1rem); - max-height: 75vh; - overscroll-behavior-y: contain; - container-type: inline-size; + padding-inline: var(--space-6, 1.5rem); + padding-block-end: var(--space-6, 1.5rem); } .profile-dialog-content > p, diff --git a/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.html b/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.html index d7c65a8..2fc05e4 100644 --- a/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.html +++ b/frontend/src/app/components/landlord-profile-completion-dialog/landlord-profile-completion-dialog.html @@ -1,5 +1,5 @@

Complétez votre profil

- +

Vos coordonnées sont nécessaires pour créer vos baux et vos quittances.

@if (profileStatus() === 'loading') { @@ -13,13 +13,26 @@
} @else { + > + + } -
- - - + +@if (profileStatus() !== 'ready') { + + + +} diff --git a/frontend/src/app/components/landlord-profile-form/landlord-profile-form.css b/frontend/src/app/components/landlord-profile-form/landlord-profile-form.css index fe367b6..ee8145c 100644 --- a/frontend/src/app/components/landlord-profile-form/landlord-profile-form.css +++ b/frontend/src/app/components/landlord-profile-form/landlord-profile-form.css @@ -3,6 +3,11 @@ container-type: inline-size; } +:host(.profile-completion-form) { + --mat-button-text-horizontal-padding: var(--space-1, 0.25rem); + --mat-button-filled-horizontal-padding: var(--space-1, 0.25rem); +} + .profile-form { display: grid; gap: var(--space-4, 1rem); @@ -32,6 +37,12 @@ justify-content: flex-end; } +:host(.profile-completion-form) .profile-actions { + flex-wrap: nowrap; + justify-content: space-between; + gap: var(--space-1, 0.25rem); +} + @container (min-width: 32rem) { .profile-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); diff --git a/frontend/src/app/components/landlord-profile-form/landlord-profile-form.html b/frontend/src/app/components/landlord-profile-form/landlord-profile-form.html index 5d405c9..2d2c873 100644 --- a/frontend/src/app/components/landlord-profile-form/landlord-profile-form.html +++ b/frontend/src/app/components/landlord-profile-form/landlord-profile-form.html @@ -52,6 +52,7 @@ />
+