mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-30 16:16:19 +00:00
fix: keep the message list within the screen on phones
This commit is contained in:
@@ -4655,7 +4655,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
{/* Email List - full width on mobile, fixed width/height on tablet/desktop */}
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex flex-col bg-background",
|
||||
"relative flex flex-col bg-background min-w-0",
|
||||
isHorizontalMailLayout
|
||||
? (shouldHideHorizontalViewerPane ? "md:w-full md:min-h-0" : "md:w-full md:h-auto")
|
||||
: "h-full border-e border-border",
|
||||
|
||||
@@ -158,6 +158,7 @@ because the UI behaviour is currently incomplete. Worth a look:
|
||||
| `IT_WEBMAIL_URL`| `http://localhost:3000` | Webmail origin |
|
||||
| `IT_JMAP_URL` | `http://localhost:8025` | Stalwart JMAP/admin base URL |
|
||||
| `IT_SMTP_PORT` | `1025` | Stalwart submission port |
|
||||
| `IT_ACCOUNT_USERS` | `alice,bob,carol` | Local parts of the three test accounts, for a stack with other users |
|
||||
| `IT_VIDEO` | `retain-on-failure`| Video capture: `on` records a `.webm` for **every** test; also `off` / `on-first-retry`. Videos land at `integration/test-results/<test>/video.webm` |
|
||||
|
||||
Record videos for a whole run (passing tests included):
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { test, expect, devices } from '@playwright/test';
|
||||
import { ACCOUNTS } from './helpers/config';
|
||||
import { sendMail } from './helpers/smtp';
|
||||
import { JmapClient } from './helpers/jmap';
|
||||
import { login, emailItem, expectEmailVisible } from './helpers/app';
|
||||
|
||||
/**
|
||||
* On a phone the message list is the whole screen. The selection toolbar
|
||||
* above it stays in the layout while collapsed, and its row of nowrap
|
||||
* buttons is wider than a phone. The list pane used to grow to that width
|
||||
* (a flex item with min-width:auto), which pushed the header's buttons,
|
||||
* every row's date and the subject's ellipsis off the right edge of the
|
||||
* screen, with nothing selected. The toolbar then measured itself as wide
|
||||
* and kept its labels too.
|
||||
*/
|
||||
const alice = ACCOUNTS.alice;
|
||||
const subject = `IT phone-layout ${Date.now()}`;
|
||||
|
||||
test.use({ ...devices['Pixel 7'] });
|
||||
|
||||
/** Fails when `locator` has no box or any part of it is off the screen's sides. */
|
||||
async function expectOnScreen(locator: import('@playwright/test').Locator, width: number): Promise<void> {
|
||||
const box = await locator.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.x).toBeGreaterThanOrEqual(0);
|
||||
expect(box!.x + box!.width).toBeLessThanOrEqual(width + 0.5);
|
||||
}
|
||||
|
||||
test.describe('phone layout', () => {
|
||||
test.beforeEach(async () => {
|
||||
const jmap = await JmapClient.connect(alice.email, alice.password);
|
||||
await jmap.reset();
|
||||
await sendMail({ from: alice.email, authPass: alice.password, to: alice.email, subject, body: 'x' });
|
||||
await jmap.waitForEmail(subject);
|
||||
});
|
||||
|
||||
test('the message list is never wider than the screen', async ({ page }) => {
|
||||
await login(page, alice);
|
||||
await expectEmailVisible(page, subject);
|
||||
const { width } = page.viewportSize()!;
|
||||
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(width);
|
||||
await expectOnScreen(page.locator('[data-mobile-mail-header]'), width);
|
||||
// The account button is the header's last control.
|
||||
await expectOnScreen(page.locator('[data-testid="account-switcher"]').first(), width);
|
||||
await expectOnScreen(emailItem(page, subject).first(), width);
|
||||
});
|
||||
|
||||
test('the selection toolbar compacts so every button is on screen', async ({ page }) => {
|
||||
await login(page, alice);
|
||||
await expectEmailVisible(page, subject);
|
||||
const { width } = page.viewportSize()!;
|
||||
|
||||
await emailItem(page, subject).first().getByRole('checkbox').click();
|
||||
// The toolbar only drops its labels when it measures narrower than a desktop list.
|
||||
const toolbar = page.locator('[data-batch-narrow]');
|
||||
await expect(toolbar).toBeVisible();
|
||||
await expectOnScreen(toolbar, width);
|
||||
for (const button of await toolbar.getByRole('button').all()) {
|
||||
await expectOnScreen(button, width);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -35,11 +35,16 @@ function acct(user: string): TestAccount {
|
||||
return { user, email: `${user}@${DOMAIN}`, password: ACCOUNT_PASSWORD };
|
||||
}
|
||||
|
||||
/** The mailboxes provisioned by the Stalwart bootstrap plan. */
|
||||
/**
|
||||
* The mailboxes provisioned by the Stalwart bootstrap plan. IT_ACCOUNT_USERS
|
||||
* renames the three local parts (in this order) for a stack with other users,
|
||||
* such as the shared test servers.
|
||||
*/
|
||||
const USERS = (process.env.IT_ACCOUNT_USERS ?? 'alice,bob,carol').split(',');
|
||||
export const ACCOUNTS = {
|
||||
alice: acct('alice'),
|
||||
bob: acct('bob'),
|
||||
carol: acct('carol'),
|
||||
alice: acct(USERS[0]),
|
||||
bob: acct(USERS[1]),
|
||||
carol: acct(USERS[2]),
|
||||
} as const;
|
||||
|
||||
export type AccountKey = keyof typeof ACCOUNTS;
|
||||
|
||||
Reference in New Issue
Block a user