Skip to content

Fix inactive tab content rendering on top of active tab in sam-tabs-next #665

Description

@fpigeonjr

What to build

Two things: a shared Playwright rendering harness in test-app (this issue is the first consumer, so it lands here), and the CSS fix for the tab overlap bug it makes testable.

Part 1 — Shared component-render harness in test-app

test-app as committed cannot render any library component. Verified on the current master:

  • test-app/tsconfig.json — no paths mapping for @gsa-sam/sam-ui-elements, so no library import resolves
  • test-app/src/app/app.module.tsimports: [BrowserModule] only
  • test-app/src/app/app.component.html — the default Angular CLI placeholder page
  • test-app/e2e/ — a single smoke.spec.ts asserting the CLI placeholder heading

So "render sam-tabs-next and click a tab" is not a one-liner today; it requires scaffolding that was built by hand (and thrown away) during the manual testing of #660. Rather than have each of #665, #666, and #583 rebuild it divergently, the first one to land establishes it.

Scope it deliberately small — a foundation plus exactly one route, not a full gallery:

  1. test-app/tsconfig.json — add a paths mapping resolving @gsa-sam/sam-ui-elements (and @gsa-sam/sam-ui-elements/*) to the root src/, plus skipLibCheck (the root library is raw source built under a legacy es2015/strictNullChecks: false config, which does not typecheck cleanly under test-app's stricter settings)
  2. A routed gallery shell — app.routes.ts (or RouterModule.forRoot) with one route: /tabs, rendering sam-tabs-next with at least two tabs of representative content
  3. BrowserAnimationsModule in the app module — not NoopAnimationsModule. tab-body.ts declares a real translateTab animation trigger; a browser test of visibility must exercise the real animation, not a stub
  4. SamTabsNextModule (already exported from src/ui-kit/experimental/tabs/index.ts) imported by the gallery module
  5. test-app/e2e/tabs.spec.ts navigating to /tabs

The convention that matters is one route per component at a stable URL. Get that right and #666 becomes "add a /datepicker route plus a spec", and #583's runtime-AA option becomes "scan each gallery route with axe" — without anyone re-litigating the foundation.

Explicitly out of scope:

Part 2 — The tab overlap bug

.mat-tab-body { display: block; } in tab-group.scss is an author-stylesheet rule, which always wins the CSS cascade over the browser's built-in [hidden] { display: none; } user-agent rule, regardless of selector specificity. tab-group.html relies on [hidden]="selectedIndex !== i" to hide inactive tab bodies, so the inactive tab's content never actually gets display: none — it stays position: absolute at the same coordinates as the active tab, rendering both tab bodies stacked on top of each other.

Confirmed in a real browser via manual testing on #660: after clicking a second tab, the previous tab's content is still visibly present, overlapping the new tab's content.

Not reproducible in the existing Angular TestBed/jsdom unit specs — jsdom does not apply the component's compiled .scss, so hidden looks correct there (computedDisplay reports none/inline) while the real-browser render is broken. This is precisely why the harness above is a prerequisite rather than a nice-to-have: there is no other way to get a failing signal for this fix.

Fix the CSS so the inactive tab body is actually hidden (e.g. give the author rule a [hidden] selector so it can win the cascade, or drop the unconditional display: block).

Note this bug is pre-existing and was not introduced by #660 — it was simply unobservable before, because the RxJS 7 "unbound operator" crash in tab-header.ts (fixed in #660) prevented sam-tabs-next from rendering at all.

Acceptance criteria

Harness

  • test-app can import and render a component from the root library (paths mapping + skipLibCheck in test-app/tsconfig.json)
  • A routed gallery shell exists with a /tabs route rendering sam-tabs-next with at least two tabs
  • The app module uses BrowserAnimationsModule (not NoopAnimationsModule)
  • The existing test-app/e2e/smoke.spec.ts still passes (or is updated deliberately if the placeholder home page changes)
  • No styles/scripts additions to test-app/angular.json (out of scope, see above)

Bug fix

  • New Playwright e2e spec (test-app/e2e/tabs.spec.ts) navigates to /tabs, clicks to switch tabs, and asserts the previously-active tab's content is not visible (toBeVisible() false, and/or non-overlapping bounding boxes)
  • Spec fails against the current tab-group.scss (confirms it reproduces the bug) before the fix, and passes after
  • tab-group.scss (or related tab styles) updated so only the active tab body is rendered visible after switching tabs, in a real browser
  • Existing tab unit specs (tab-group.spec.ts, tab-header.spec.ts, tab-body.spec.ts) still pass
  • npm --prefix test-app run test:e2e passes, and the E2E workflow passes in CI
  • No regression to ink-bar position, keyboard navigation, or the centering animation

Notes for whoever picks this up

  • The library is published as raw source — there is no build step and no dist/. The paths mapping points straight at root src/.
  • Check scripts/consumer-deep-imports.json before moving or renaming anything under src/ui-kit; it is a frozen list of deep import paths used by downstream consumers.
  • test-app is a separate npm workspace: npm ci && npm ci --prefix test-app.

Blocked by

None - can start immediately

Blocks

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions