mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-13 12:17:34 +00:00
feat(ui): add Classic, Smart, and Compact desktop navigation styles (#1642)
* feat(ui): add Classic, Smart, and Compact desktop navigation styles Introduce a shared app-nav registry and reachable model so TopBar, the command palette, and mobile menus share one destination source. Smart bar is the default; Appearance gains a Navigation subsection with quick links. * fix(e2e): stop clearing top-nav prefs on every reload The desktop-navigation suite used addInitScript to wipe mode storage, which re-ran on reload and undid the classic/compact values under test. * fix(ui): harden nav PR docs and Compact quick-link coverage Drop the partial docs-refresh import that left missing image assets and a stale Display screenshot, keep navigation-scoped operator docs against main, and add an E2E path that proves Compact quick-link add, persist, and render after reload. * fix(ui): polish Compact quick links and menu mastheads Align Smart/Compact menus with Theme chrome, keep pin labels always visible, and drive add capacity from persisted pins (max five) with a trailing + picker and per-pin remove. * test(e2e): exact-match Compact Networking pin locator Avoid Playwright strict-mode clash with Actions for Networking. * fix(ui): simplify Compact quick link removal and fix + button trailing Remove the (...) dropdown per quick link in Compact mode. Right-click context menu remains as the sole on-bar removal affordance. Fix the + add-button to trail quick links rather than pinning to the far right by removing flex-1 from the quick-link rail.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Desktop navigation styles: Smart default, Compact quick-link picker,
|
||||
* labeled pins, and persistence.
|
||||
*/
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { loginAs, waitForStacksLoaded } from './helpers';
|
||||
|
||||
async function setTopNavMode(page: import('@playwright/test').Page, mode: 'classic' | 'smart' | 'compact' | null) {
|
||||
await page.evaluate((next) => {
|
||||
if (next === null) {
|
||||
window.localStorage.removeItem('sencho.appearance.topNavMode');
|
||||
window.localStorage.removeItem('sencho.appearance.topNavQuickLinks');
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem('sencho.appearance.topNavMode', next);
|
||||
}, mode);
|
||||
await page.reload();
|
||||
await loginAs(page);
|
||||
await waitForStacksLoaded(page);
|
||||
}
|
||||
|
||||
test.describe('Desktop navigation styles', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.evaluate(() => {
|
||||
window.localStorage.removeItem('sencho.appearance.topNavMode');
|
||||
window.localStorage.removeItem('sencho.appearance.topNavQuickLinks');
|
||||
});
|
||||
await loginAs(page);
|
||||
await waitForStacksLoaded(page);
|
||||
});
|
||||
|
||||
test('defaults to Smart bar with a More control', async ({ page }) => {
|
||||
const topbar = page.locator('[data-sn-chrome="topbar"]');
|
||||
await expect(topbar).toHaveAttribute('data-sn-nav-mode', 'smart');
|
||||
await expect(page.getByRole('button', { name: 'More navigation' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('persists mode across reload and navigates via Smart More', async ({ page }) => {
|
||||
await setTopNavMode(page, 'classic');
|
||||
await expect(page.locator('[data-sn-chrome="topbar"]')).toHaveAttribute('data-sn-nav-mode', 'classic');
|
||||
|
||||
await setTopNavMode(page, 'smart');
|
||||
await expect(page.locator('[data-sn-chrome="topbar"]')).toHaveAttribute('data-sn-nav-mode', 'smart');
|
||||
await page.getByRole('button', { name: 'More navigation' }).click();
|
||||
await expect(page.locator('.font-heading').filter({ hasText: 'More' })).toBeVisible();
|
||||
await page.getByRole('menuitem', { name: /Logs/i }).click();
|
||||
await expect(page.locator('body')).toContainText(/Logs|Central|Observability/i);
|
||||
});
|
||||
|
||||
test('Compact launcher opens Settings', async ({ page }) => {
|
||||
await setTopNavMode(page, 'compact');
|
||||
await expect(page.locator('[data-sn-chrome="topbar"]')).toHaveAttribute('data-sn-nav-mode', 'compact');
|
||||
await page.getByRole('button', { name: 'Open navigation launcher' }).click();
|
||||
await page.getByRole('menuitem', { name: /^Settings$/i }).click();
|
||||
await expect(page.getByText('Appearance', { exact: true }).first()).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('Compact trailing + adds a labeled pin that survives reload', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1100, height: 800 });
|
||||
await page.evaluate(() => {
|
||||
window.localStorage.setItem('sencho.appearance.topNavQuickLinks', '[]');
|
||||
});
|
||||
await setTopNavMode(page, 'compact');
|
||||
|
||||
const topbar = page.locator('[data-sn-chrome="topbar"]');
|
||||
await expect(topbar).toHaveAttribute('data-sn-nav-mode', 'compact');
|
||||
|
||||
await page.getByRole('button', { name: 'Add quick link' }).click();
|
||||
await page.getByRole('menuitem', { name: /Networking/i }).click();
|
||||
|
||||
const pin = topbar.getByRole('button', { name: 'Networking', exact: true });
|
||||
await expect(pin).toBeVisible();
|
||||
await expect(pin.locator('span.inline')).toBeVisible();
|
||||
|
||||
const stored = await page.evaluate(() => window.localStorage.getItem('sencho.appearance.topNavQuickLinks'));
|
||||
expect(stored).toContain('networking');
|
||||
|
||||
await page.reload();
|
||||
await loginAs(page);
|
||||
await waitForStacksLoaded(page);
|
||||
|
||||
await expect(page.locator('[data-sn-chrome="topbar"]').getByRole('button', { name: 'Networking', exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Add quick link' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user