GSA / GSA/sam-ui-elements

sam-datepicker-v2 renders unstyled: .datepicker__* CSS was lost in the 2019 fork

Open
#681 0 comments 0 reactions 0 assignees View on GitHub
bugfix
Dominant language
TypeScript
Stars
8
Forks
33
Avg merge
2d 51m
Merged PRs (30d)
65

Description

`sam-datepicker-v2` renders with no styling at all. Its template asks for a full set of bespoke `.datepicker__*` classes that **are not defined in any stylesheet** — not in this repo, not in `sam-styles/`, not in `@gsa-sam/sam-styles`, and not in any `node_modules` of either workspace.

Measured on the `/datepicker` gallery route added in #666 (Chromium, real browser):

```
popup: 684 x 2081 px
.datepicker__calendar position: static (needs absolute)
.datepicker__calendar__month__day display: block, width: 684px
```

Day cells stack vertically down the page instead of forming a 7-column grid, and the popup is not positioned over the input.

## Root cause

`sam-datepicker-v2` is a fork of [`angular2-material-datepicker`](https://www.npmjs.com/package/angular2-material-datepicker) (MIT). The animation trigger is byte-identical, keyframe offsets and all:

```ts
// upstream and SAM, identical
trigger('calendarAnimation', [
transition('* => left', [ animate(180, keyframes([
style({ transform: 'translateX(105%)', offset: 0.5 }),
style({ transform: 'translateX(-130%)', offset: 0.51 }),
style({ transform: 'translateX(0)', offset: 1 }) ]))]),
```

Upstream carried its CSS **inline** in `styles: [...]` — 25 `.datepicker__*` rules, including the structural ones:

```css
.datepicker { position: relative; display: inline-block; }
.datepicker__calendar { position: absolute; overflow: hidden; z-index: 1000;
top: 1.9em; left: 0; height: 24.25em; width: 20.5em;
background-color: #fff; box-shadow: 0 3px 6px rgba(0,0,0,.16), 0 3px 6px rgba(0,0,0,.23); }
```

SAM's fork (`3ba95ec0`, Feb 2019, "PR for adding date range component related to IAE-14621" #263) moved the inline template out to `picker.template.html` and **dropped the `styles:` block entirely**, without re-adding it to either the component or `sam-styles/`. The class names came along, so the markup still requests CSS that has never existed in this repo. `date-range-v2.component.scss` has been **0 bytes since that same commit** and `picker.component.ts` has no `styleUrls`.

## Why this went unnoticed for ~7 years

Nothing rendered it. `test-app` could not render any library component until #665, and the sole tracked consumer does not use this component:

- `scripts/consumer-deep-imports.json` tracks 3 consumers, all the same app (`iae-sam-front-end`, `-front-end3`, `-front-end-uat`).
- Zero occurrences of `sam-datepicker-v2` or `sam-date-range-v2` in that app's source. It uses the **stable** control instead: 32x ``, 8x ``.
- `SamUIKitModule` -> `SamExperimentalModule` does register `SamDateRangeV2Module`, so it is compiled into the consumer bundle and available in templates — just never used.
- Five other SAM frontends checked (`-search`, `-admin`, `-integrity`, `-systemaccounts`, `-role-management-service`) do not depend on `sam-ui-elements` at all.

Contrast with the stable `sam-date`, which also has no `styleUrls` but whose markup uses **USWDS** classes (`usa-form-group`, `usa-form-control`) that every consumer already loads globally. That is why `sam-date` renders fine with no CSS from this library and `sam-datepicker-v2` does not.

## Decision needed first

Given zero known consumers, "write calendar CSS" may not be the right answer. Pick one:

1. **Restore the lost upstream CSS** as component `styleUrls` (matches `experimental/tabs`, which does self-style; Angular's `_ngcontent` scoping avoids any global-namespace collision). Upstream is MIT — attribute it.
2. **Add it to `sam-styles/themes/sam-gov`**, matching the library's dominant global-CSS model (that is how `iae-sam-front-end` styles the kit: `@import '@gsa-sam/sam-ui-elements/sam-styles/themes/sam-gov'`). Riskier — global namespace.
3. **Deprecate `date-range-v2`.** It is `experimental/`, forked, unstyled, and unused. Removal is a breaking change for the deep-import contract, so check `scripts/consumer-deep-imports.json` first (it currently lists no `date-range-v2` paths).

## Two smaller, separable gaps

These are legitimately the consumer's responsibility, but the harness needs them for manual testing either way:

- **`.fa fa-calendar`** — a SAM addition (upstream had no icon; it clicked the input). `@fortawesome/fontawesome-free` is a root **peerDependency**, so consumers load the webfont CSS. `test-app` has only `fontawesome-svg-core` (JS/SVG API), so no glyph renders.
- **`.sr-only`** — Bootstrap-era class. `@gsa-sam/sam-styles` ships only USWDS `.usa-sr-only`, so nothing defines it and the screen-reader label renders as the visible text "Calendar" next to the input.

## Acceptance criteria

- [ ] A decision is recorded on options 1/2/3 above
- [ ] If styling: `.datepicker`, `.datepicker__calendar`, nav/arrow/chevron, `__labels`/`__label`, `__month`/`__month__day`, and `__cancel` all render as a positioned 7-column calendar overlay
- [ ] If styling: upstream MIT attribution is included alongside the restored rules
- [ ] `test-app` loads FontAwesome webfont CSS and an `.sr-only` rule so `/datepicker` is manually testable without DevTools injection
- [ ] Playwright assertions cover the cascade-level properties jsdom cannot see (`position: absolute` on the popup, popup width, day-cell width ~14.28%) — per AGENTS.md's Vitest/Playwright boundary
- [ ] **The existing open-click guard in `test-app/e2e/datepicker.spec.ts` still fails when the `contains()` fix in `picker.component.ts` is reverted.** It clicks the `.sr-only` child explicitly for exactly this reason; adding an `.sr-only` rule collapses that span to 1x1, and a centre click on `.fa-calendar` would pass even with the defect reintroduced. Verify by reverting the fix locally.

## Context

Found while manually testing #666, which added the `/datepicker` gallery route — the first time this component had ever been rendered in a browser in CI or locally. Deliberately kept out of #666 to hold that PR to its two event-handling fixes.

Contributor guide

Open the contributing guide

Research direction

First compare picker.component.ts, picker.template.html, and date-range-v2.component.scss with the upstream component, then review scripts/consumer-deep-imports.json and the three proposed paths. Run the /datepicker gallery and test-app/e2e/datepicker.spec.ts; done means the chosen decision is recorded, the calendar cascade and accessibility setup are testable, and the Playwright assertions pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, playwright, sass, typescript
Domain
accessibility, frontend, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.