Skip to content

refactor(dashboard): Header with ProductSwitcher, CommandPalette on Dialog, Footer (KEA-7886, trilha G) - #17313

Merged
diegosouzapw merged 11 commits into
feat/new-uikitfrom
feat/new-uikit-G
Oct 20, 2026
Merged

diegosouzapw merged 11 commits into
feat/new-uikitfrom
feat/new-uikit-G

Conversation

@diegosouzapw

@diegosouzapw diegosouzapw commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Trilha G: Header, CommandPalette and Footer on the Keak CI UI Kit (KEA-7886)

Spec §3.7 (shell: header, product switcher, command palette, footer) and §3.9 (i18n); plan Tasks 28–29.

⚠️ base-red inherited: #17219

Merge after A, C, D, E and F. This branch contains E's and C's commits (base feat/new-uikit-E, with origin/feat/new-uikit-C merged for the forwardRef Input adapter the palette focuses).

What changes

  • Header now renders on the kit shell header (<header class="ci-sidebar-shell-header">, without an explicit banner role, because F mounts it inside the kit inset's <main>). Its fixed kit height grows by the safe-area inset (height: calc(4rem + var(--ci-border-default) + safeTop), with paddingTop: safeTop), so the macOS Electron / iOS standalone offsets of §3.7 still apply without squeezing the 36 px controls. Kit imports use subpaths (@omniroute/ui-kit/sidebar, /keak-brand, /dialog, /separator, /dropdown-menu), never the barrel:
    • kit SidebarTrigger, labelled by state: "Collapse sidebar" / "Expand sidebar" on desktop, "Open menu" / "Close" on mobile;
    • page icon and title, <h1 class="ci-heading">;
    • kit ProductSwitcher with KEAK_PRODUCT_LINKS / KEAK_ACTIVE_PRODUCT, labelled header.products;
    • quick-navigation buttons, LanguageSelector, ThemeToggle and logout, all on the Button adapter (ghost/outline) with <Icon>. No Material spans are left.
    • onMenuClick and showMenuButton were removed in fix round 2, together with F's ShellHeader that passed them. Header calls useSidebar(), so it must render inside the kit SidebarProvider. F's kit shell provides that, and the legacy shell uses F's own HeaderLegacy copy.
  • ThemeToggle: a Button with variant="ghost" ("outline" for the card variant), size="icon", <Icon light_mode|dark_mode>, and a translated aria-label that flips.
  • LanguageSelector: the kit DropdownMenu with one DropdownMenuRadioItem per locale inside a DropdownMenuRadioGroup. Choosing a locale still calls persistLocale + router.refresh(), and choosing the current one does nothing. The hand-rolled outside-click panel is gone.
  • CommandPalette: the kit Dialog / DialogContent with showCloseButton={false} and an sr-only DialogTitle ("Command palette"). Search uses the Input adapter (type="search", icon="search", ref-focused). The listbox / group / option[aria-selected] structure, search, grouping, handleNavigate and arrow/Enter keys are unchanged.
    • Escape now goes through the Dialog's onOpenChange, so it is no longer handled twice by the document listener.
    • The dialog opens 10vh from the top, like the old palette: the kit centring transform is overridden, and it fades in only.
  • Footer: column titles use ci-eyebrow, a kit Separator sits above the bottom row, and links use hover:text-heading. All links and the brand/social block are kept.
    • Fixed: internal links rendered link.label, which does not exist, so Features, Pricing, Documentation, API Reference, Terms and Privacy showed empty text. They now render t(link.key).
  • i18n: header.openMenu ("Open menu" / "Abrir menu") and header.products ("Keak products" / "Produtos Keak") added to en.json and pt-BR.json only. The other 65 locales get them in the i18n wave (check-key-completeness --warn lists them as missing).

Files

  • src/shared/components/{Header,ThemeToggle,LanguageSelector,CommandPalette,Footer}.tsx
  • src/i18n/messages/{en,pt-BR}.json
  • tests (new): tests/unit/ui/{header-kit,language-selector-kit,command-palette-kit,footer-kit}.test.tsx
  • fix round 2: src/shared/components/layouts/DashboardShell.tsx, src/shared/components/Sidebar.tsx (the trigger is mobile-only), and config/quality/dashboard-typecheck-baseline.json (only the Header.tsx entry is removed)
  • tests (new, round 2): tests/unit/ui/shell-single-trigger.test.tsx
  • tests (realigned): tests/unit/ui/rtl-logical-classes.test.tsx, tests/e2e/sidebar-navigation.spec.ts, and, in round 2, tests/unit/ui/sidebar-kit-render.test.tsx and tests/unit/sidebar-hierarchy-a11y-12869.test.tsx (shell-trigger stand-in in the harness)

Tests realigned

  • tests/unit/ui/rtl-logical-classes.test.tsx: the "uses end-0 for the dropdown panel" check is now "anchors the kit DropdownMenuContent to the inline end (align="end")". The absolute end-0 panel no longer exists; floating-ui resolves align="end" against the document direction, so the menu still mirrors under dir=rtl. The "no right-0", "text-start" and "no text-left" checks are kept as they were. lint-staged also re-ran Prettier over the whole file on commit, which accounts for the rest of the diff (quote style only).
  • tests/e2e/sidebar-navigation.spec.ts (shared with trilha F, one locator only): the mobile opener moves from getByRole("button", { name: "menu", exact: true }) to getByRole("button", { name: "Open menu" }), because the trigger now carries the translated label (controller ruling).

Conflicts with F (resolved in fix round 2)

F also edits tests/e2e/sidebar-navigation.spec.ts and tests/unit/ui/rtl-logical-classes.test.tsx. Resolution (verified with git merge-tree against origin/feat/new-uikit-F 1e8125f, and re-verified by the independent review against F's head 881b8cd: same two hunks):

  • take F's version of both files;
  • in the spec, replace F's "Pre-G Header" comment and its name: "menu" click with getByRole("button", { name: "Open menu" });
  • in the RTL test, swap F's end-0 check for the align="end" check above.

The e2e run below used exactly this resolved tree.

Fix round 2 (after F #17320 merged)

  • 10a04838: back-merge of origin/feat/new-uikit (now containing F). The two expected conflicts were resolved exactly as described above, and no other file conflicted.
  • e5728a03: one sidebar trigger in the kit shell.
    • The Header's SidebarTrigger is the only collapse/expand control. F's in-sidebar trigger now renders only inside the mobile Sheet, as its Close control; the modal Sheet hides the header while it is open.
    • ShellHeader, and the onMenuClick/showMenuButton props it passed (which the Header ignored), are gone.
    • HEADER_DESCRIPTIONS gets the same key-pair fix F applied to HeaderLegacy: the key type names proxy, and the dead settings key is dropped. This lets the Header.tsx entry (TS2353 ×2) leave dashboard-typecheck-baseline.json: the baseline only shrinks.
    • mobileBreakpoint=1024 stays. It is the legacy lg drawer breakpoint, and it matches the header title's lg:flex.
    • New tests/unit/ui/shell-single-trigger.test.tsx (real shell, Header and Sidebar): desktop has exactly one collapse/expand trigger, in the header, before and after collapsing; mobile has one "Open menu", and the open drawer has one "Close". It was RED with 2 desktop triggers.
    • F's tests realigned to the header-owned trigger:
      • sidebar-kit-render and sidebar-hierarchy-a11y-12869 harnesses get a stand-in shell trigger;
      • the aside is asserted to have no desktop trigger;
      • the Tab order is now brand → search → first item;
      • the desktop collapse/expand clicks in sidebar-navigation.spec.ts target header.ci-sidebar-shell-header.
  • Build on .113: rc 0, "Turbopack build encountered 232 warnings".
  • e2e on .113 (Playwright start mode, G with F merged): 14/14 passed. That covers sidebar-navigation.spec.ts (desktop light/dark, and mobile 320/390 light/dark), navigation.spec.ts (4) and smoke.spec.ts (4). This closes Task 29 Step 4, and the desktop sidebar overflow failures of the earlier run are gone.
  • Screenshots re-captured from that build: _artifacts/G-header-light-1440.png (one trigger, in the header; brand fits) and _artifacts/G-palette-light-1440.png (borderless search row with a single ×), plus the dark and 390 px variants.

Fix round 1 (review batch-G-review.md)

  • 3f24b951: every trilha G file and the header test import kit subpaths instead of the barrel.
  • d7d50b5a: the header height absorbs the safe-area inset; there is no explicit role="banner"; the palette flattens .ci-input-shell (where the kit puts the border and focus ring) instead of the already borderless inner input; the language trigger's accessible name is Language: EN — English, which follows WCAG 2.5.3 label-in-name (the visible EN is in the name, plus the purpose) and overrides the brief's aria-label={currentLang.name}.
  • Focused evidence: 5 vitest files with 26/26 passing, node guards 21/21, eslint clean, typecheck:core 0. Round 2 then re-captured the palette screenshots and ran smoke.spec.ts; see Fix round 2.

Validation

Each check is listed with where it ran and its result. ".113" means the 32-core box via gate113.sh. "Local" means this devbox, where load averages of 25–58 were coming from parallel tracks.

Check Where Result
npm run lint .113 rc 0 (0 errors). The local run hit its 40-minute cap on a cold cache (exit 124), so it was re-run on .113.
npm run typecheck:core local, then .113 after the back-merge exit 0 / exit 0
npm run check:dashboard-typecheck local, then .113 after the back-merge exit 0 / exit 0
npm run check:ui-icu local exit 0
npm run test:vitest:ui .113 (run twice) 3413 passed, 19 failed in 10 files, both runs. None are trilha G files (see below). modelsCombosCatalog appears only in the first run's partial list.
Base comparison: the 10 failing files run alone .113, on 00233e15 (E + C, before G) and on G Base: 13 failed in 5 files. G: the same 13 in the same 5 files. These are inherited reds (sidebar-pinned-items, sidebar-accordion-header-click, radar-admin-sidebar, rtk-context-page-load-failure, api-key-redis-auth-cache). The other 5 (manifestAdapter, proxySaveRefresh, api-key-routing-editor, burn-rate-chart, compressionPanel) only fail under full-suite load and pass alone.
Trilha G unit tests: header-kit, language-selector-kit, command-palette-kit, footer-kit, rtl-logical-classes, adapters, theme-store-bridge .113, after the back-merge 52/52 passed
node --test: ui-kit-shell-flag, design-kit-bridge (accent-ink guard), sidebar-essentials-static .113, after the back-merge 21/21 passed
npm run check:tracked-artifacts local exit 0
npm run check:cycles (advisory) local cycles=14, equal to the baseline; no SCC involves a trilha G file
node scripts/i18n/check-key-completeness.mjs --warn local exit 0; 65 locales are missing header.openMenu / header.products (expected, handled by the i18n wave)
pre-commit (lint-staged, any-budget, tracked-artifacts, check-ui-kit-snapshot, ai-attribution) local passed on every commit
npm run build .113 rc 0; finished through colocate-standalone (only the tail was captured, so no warning count). The local build hit its 40-minute cap under load 58.
e2e on the G+F tree: sidebar-navigation.spec.ts + navigation.spec.ts + smoke.spec.ts .113, Playwright start mode, port 21377 (fix round 2, after F merged) 14/14 passed: desktop light/dark, mobile 320/390 light/dark, navigation 4, smoke 4. In the first pre-round-2 run (12 passed, 2 failed), the two desktop cases failed on F's sidebar brand overflow; round 2 resolved that.

The first e2e run used a git merge-tree G+F tree, which was never committed. The fix-round-2 run used the real branch after F was back-merged (10a04838) and e5728a03.

After the full battery, one follow-up commit was validated with focused tests only: 0c3d2595 (palette input hides the native ::-webkit-search-cancel-button, which the first screenshots showed doubled next to the clear button). The back-merge of origin/feat/new-uikit (A's merged PR #17279) was then re-checked on .113 with typecheck:core, dashboard-typecheck and the focused suites above.

Screenshots

Re-captured in fix round 2 from the G+F production build on .113 (/dashboard/providers):

  • _artifacts/G-header-light-1440.png, _artifacts/G-header-dark-1440.png: the single sidebar trigger in the header; icon, title and description; "Keak OmniRoute" product switcher; Quick nav + Ctrl+K; language menu; theme toggle; logout. The sidebar brand fits its column.
  • _artifacts/G-header-light-390.png, _artifacts/G-header-dark-390.png: mobile header with "Open menu", search, language, theme and logout.
  • _artifacts/G-palette-light-1440.png, _artifacts/G-palette-dark-1440.png, _artifacts/G-palette-light-390.png, _artifacts/G-palette-dark-390.png: palette open with "prov". The search row is borderless with a single clear ×; section eyebrows, the surface-2 selection and the hints footer are visible.

…eld styles the input shell; language selector name follows label-in-name; no banner inside main (KEA-7886, trilha G)
…kit-G

# Conflicts:
#	tests/e2e/sidebar-navigation.spec.ts
#	tests/unit/ui/rtl-logical-classes.test.tsx
…iption keys match the live items (KEA-7886, trilha G)
@diegosouzapw
diegosouzapw merged commit 3f569b5 into feat/new-uikit Oct 20, 2026
3 checks passed
@diegosouzapw
diegosouzapw deleted the feat/new-uikit-G branch October 22, 2026 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant