You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.ts — imports: [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:
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)
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
BrowserAnimationsModule in the app module — notNoopAnimationsModule. tab-body.ts declares a real translateTab animation trigger; a browser test of visibility must exercise the real animation, not a stub
SamTabsNextModule (already exported from src/ui-kit/experimental/tabs/index.ts) imported by the gallery module
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:
Do not add styles or scripts entries to test-app/angular.json yet. Tabs needs neither. In particular the video-player harness requires accessible-html5-video-player's js/strings.js to be loaded beforejs/px-video.js (otherwise: ReferenceError: GLOBAL_STRINGS is not defined) — that belongs to whoever first needs it, with a comment explaining the ordering, rather than being guessed at here.
Do not add visual-regression/screenshot testing. Assert toBeVisible(), computed style, and bounding boxes — deterministic, and it states intent. Screenshot baselines bring font-rendering flake and baseline churn for no added signal on this class of bug.
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.
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-apptest-appas committed cannot render any library component. Verified on the currentmaster:test-app/tsconfig.json— nopathsmapping for@gsa-sam/sam-ui-elements, so no library import resolvestest-app/src/app/app.module.ts—imports: [BrowserModule]onlytest-app/src/app/app.component.html— the default Angular CLI placeholder pagetest-app/e2e/— a singlesmoke.spec.tsasserting the CLI placeholder headingSo "render
sam-tabs-nextand 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:
test-app/tsconfig.json— add apathsmapping resolving@gsa-sam/sam-ui-elements(and@gsa-sam/sam-ui-elements/*) to the rootsrc/, plusskipLibCheck(the root library is raw source built under a legacyes2015/strictNullChecks: falseconfig, which does not typecheck cleanly undertest-app's stricter settings)app.routes.ts(orRouterModule.forRoot) with one route:/tabs, renderingsam-tabs-nextwith at least two tabs of representative contentBrowserAnimationsModulein the app module — notNoopAnimationsModule.tab-body.tsdeclares a realtranslateTabanimation trigger; a browser test of visibility must exercise the real animation, not a stubSamTabsNextModule(already exported fromsrc/ui-kit/experimental/tabs/index.ts) imported by the gallery moduletest-app/e2e/tabs.spec.tsnavigating to/tabsThe convention that matters is one route per component at a stable URL. Get that right and #666 becomes "add a
/datepickerroute 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:
stylesorscriptsentries totest-app/angular.jsonyet. Tabs needs neither. In particular the video-player harness requiresaccessible-html5-video-player'sjs/strings.jsto be loaded beforejs/px-video.js(otherwise:ReferenceError: GLOBAL_STRINGS is not defined) — that belongs to whoever first needs it, with a comment explaining the ordering, rather than being guessed at here.toBeVisible(), computed style, and bounding boxes — deterministic, and it states intent. Screenshot baselines bring font-rendering flake and baseline churn for no added signal on this class of bug.Part 2 — The tab overlap bug
.mat-tab-body { display: block; }intab-group.scssis 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.htmlrelies on[hidden]="selectedIndex !== i"to hide inactive tab bodies, so the inactive tab's content never actually getsdisplay: none— it staysposition: absoluteat 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, sohiddenlooks correct there (computedDisplayreportsnone/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 unconditionaldisplay: 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) preventedsam-tabs-nextfrom rendering at all.Acceptance criteria
Harness
test-appcan import and render a component from the root library (pathsmapping +skipLibCheckintest-app/tsconfig.json)/tabsroute renderingsam-tabs-nextwith at least two tabsBrowserAnimationsModule(notNoopAnimationsModule)test-app/e2e/smoke.spec.tsstill passes (or is updated deliberately if the placeholder home page changes)styles/scriptsadditions totest-app/angular.json(out of scope, see above)Bug fix
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)tab-group.scss(confirms it reproduces the bug) before the fix, and passes aftertab-group.scss(or related tab styles) updated so only the active tab body is rendered visible after switching tabs, in a real browsertab-group.spec.ts,tab-header.spec.ts,tab-body.spec.ts) still passnpm --prefix test-app run test:e2epasses, and the E2E workflow passes in CINotes for whoever picks this up
dist/. Thepathsmapping points straight at rootsrc/.scripts/consumer-deep-imports.jsonbefore moving or renaming anything undersrc/ui-kit; it is a frozen list of deep import paths used by downstream consumers.test-appis a separate npm workspace:npm ci && npm ci --prefix test-app.Blocked by
None - can start immediately
Blocks
sam-datepicker-v2route)