Skip to content

feat(time): add Time atom component#259

Merged
egdev6 merged 8 commits into
mainfrom
feature/time
Jul 13, 2026
Merged

feat(time): add Time atom component#259
egdev6 merged 8 commits into
mainfrom
feature/time

Conversation

@FranRubioDev

Copy link
Copy Markdown
Collaborator

Summary

Adds the Time atom component — a segmented time input supporting 12/24h, minute/second granularity, steppers, clock icon, and floating label.

Closes #37

Review scope

  • Primary files/areas: src/components/atoms/time/
  • Out of scope: i18n locale formatting
  • Review workload: large (6 new files, ~1 200 lines)

Type of change

  • New component

Workflow gates

  • Linked issue is present with Closes #NNN or maintainer approved an exception.
  • Linked issue has label status:approved before implementation starts.
  • Work was started through the Project flow: assignee set, Project status In progress, branch/worktree recorded.
  • PR title follows Conventional Commit format: <type>(<optional scope>): <description>.
  • Commits follow the same commitlint-enforced format.
  • Diff is focused; unrelated work is called out in Review scope.
  • MCP/runtime artifacts are absent: .playwright-mcp, page-*.png, page-*.jpeg, *.md.playwright-output.

Component evidence (required for component changes)

  • Validated component spec is linked or quoted in the issue.
  • Accessibility contract from the spec was implemented or deviations are explained below.
  • Follows the 6-file pattern: types.ts, use*.ts, Component.tsx, Component.test.tsx, Component.stories.tsx, index.ts.
  • CVA variants live in types.ts; JSX component has no state, CVA calls, or business logic.
  • Public props with runtime defaults have matching @default JSDoc in types.ts (node scripts/verify-prop-default-docs.mjs).
  • Uses design tokens from theme.css; no hardcoded colors or arbitrary color utilities.
  • Storybook covers default, variants, documented states, edge cases, and dark mode when visually different.
  • Component audit result is PASS or accepted PASS WITH WARNINGS.
  • Visual review result is included when visuals changed.

Accessibility evidence

  • Role/name semantics verified with Testing Library queries or equivalent.
  • Keyboard-only behavior verified: Tab / Shift+Tab, Arrow keys, Backspace.
  • Focus lifecycle verified: segment focus/blur cycle, container click redirects to hour.
  • Disabled, required, invalid/error states expose the expected semantics where applicable.
  • Reduced motion behavior is respected where motion changed.
  • Screen reader or axe/manual evidence is included below when applicable.

Changes

File Change
src/components/atoms/time/types.ts CVA variants, prop types, JSDoc @default tags
src/components/atoms/time/useTime.ts All logic — segments, keyboard, focus, steppers
src/components/atoms/time/Time.tsx Presentational component, no state
src/components/atoms/time/Time.stories.tsx Storybook stories for all variants and states
src/components/atoms/time/Time.test.tsx Unit tests with Vitest + Testing Library
src/components/atoms/time/index.ts Public exports

Validation evidence

  • TypeScript: pnpm exec tsc --noEmit — passes
  • Unit tests: 590 passed (32 test files) including the Time suite
  • Build/package: not run (per project convention)
  • Storybook or visual check: stories created, pending Storybook visual review
  • Accessibility check: keyboard and focus behavior covered in tests
  • Security/dependency check: no new dependencies added

@FranRubioDev FranRubioDev requested a review from egdev6 as a code owner June 5, 2026 00:38
@FranRubioDev FranRubioDev added the type:feature Feature changes label Jun 5, 2026

@egdev6 egdev6 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the Time atom work. I’d request changes before merge because a few issues currently break the component contract and user-facing behavior.

Blocking items to fix:

  • Time is not exported from src/index.ts, so package consumers cannot import it from @stack-and-flow/design-system.
  • handleContainerClick refocuses Hours for any click inside the wrapper, so clicking Minutes/Seconds/AM-PM does not keep focus on the clicked segment.
  • 12h mode is not safe yet: the AM/PM segment is treated as free text / numeric stepping and can become invalid values like 1 instead of PM.
  • onChange can emit stale or unclamped values because the callback uses the raw value/old segment snapshot instead of the normalized next state.
  • Storybook docs need to follow the repo convention: JSDoc above const meta and each story, not parameters.docs.description.component.

Please add focused regression tests for segment click focus, AM/PM keyboard/input behavior, and normalized onChange payloads. Also update the PR evidence once the component audit and visual review are addressed.

@egdev6

egdev6 commented Jun 12, 2026

Copy link
Copy Markdown
Member

@FranRubioDev le puedes pedir a la IA que revise los comentario sobre tu rama

Segment clicks no longer refocus Hours, AM/PM now toggles consistently
via keyboard/steppers instead of relying on broken numeric parsing, and
onChange emits the same clamped state that gets applied instead of a
stale/unclamped snapshot.
Añade la story WithoutLabel para mostrar el componente Time sin
etiqueta visible, útil cuando el contexto ya indica el propósito del campo.
egdev6
egdev6 previously requested changes Jul 13, 2026

@egdev6 egdev6 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up fixes. This is closer, but I still need to request changes because there are a few accessibility and behavior issues that block the Time atom contract.

Blocking items:

  • src/components/atoms/time/types.ts:8, types.ts:51, types.ts:79, Time.tsx:89-96: focus visibility still uses outline-none plus decorative shadow-glow-*. Please use the shared focus-visible:focus-ring / wrapper focus-ring pattern instead. Keyboard focus cannot depend on glow styling.
  • src/components/atoms/time/useTime.ts:148-163, Time.tsx:76-82: 12h mode defines the hour minimum as 1, but typed input can still accept 00, and ARIA always exposes aria-valuemin={0}. Please derive min/max from the same segment rules, clamp lower bounds, and add boundary tests for 00, 0, 12, 23, 59, and 60.
  • src/components/atoms/time/Time.tsx:76-87: AM/PM is rendered as role="spinbutton" without a valid numeric value model or aria-valuetext. Please either model it with an appropriate non-spinbutton pattern, or provide valid spinbutton semantics plus aria-valuetext="AM|PM".

Also worth fixing in the same pass:

  • Time.tsx:118: the label points to ${id}-hour, but the hour input does not render that id, so clicking the label will not focus the field.
  • Time.tsx:182 and index.ts:1: this should follow the repo named export pattern (export const Time, export { Time } from './Time').
  • Time.stories.tsx:5-28: Storybook docs should use the required ## Description JSDoc structure and explain why each story exists.
  • Time.test.tsx:189-191: avoid asserting internal Tailwind classes like w-full; assert observable behavior or public API effects instead.

CI is green, but these are contract/accessibility issues rather than pipeline failures. Once the focus ring, 12h boundary handling, and AM/PM semantics are corrected, this should be much easier to approve.

@egdev6

egdev6 commented Jul 13, 2026

Copy link
Copy Markdown
Member

Thanks for the latest fixes. This is much closer, but I still would not approve it yet.

Remaining blockers I found on the current head (be95b2f):

  • src/components/atoms/time/useTime.ts:160-168: 12h lower-bound clamping only handles value === '00'. Typing a single 0 can still emit { hour: '0' } through onChange (useTime.ts:190-193). The test says “00 or 0”, but only types 00 (Time.test.tsx:291-295). Please clamp any parsed value below the 12h minimum and add an explicit 0 regression test.
  • src/components/atoms/time/Time.tsx:81-87: aria-valuenow uses parseInt(...) || undefined, so valid zero values like 00 are exposed as undefined. For numeric spinbuttons, 0 is a valid current value and should stay 0.
  • pnpm-lock.yaml: the PR still includes large lockfile churn without a matching package.json change. For a component-only PR, please revert it or explain exactly why the dependency graph needed to change.

Also, the prior note about avoiding internal Tailwind assertions is still open: Time.test.tsx:94-97 asserts w-full. That is lower severity than the two behavior/accessibility issues above, but it should be cleaned up before approval.

CI being green is good, but these are component contract and accessibility boundary issues, so they still need a code fix.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Time atom to the Stack-and-Flow Design System: a segmented time input (HH:MM[:SS] + optional AM/PM) with optional steppers, optional clock icon, label + hint support, and an exported public API surface.

Changes:

  • Introduces the Time presentational component and useTime hook to handle segment state, focus, and keyboard interactions.
  • Adds CVA variants + prop/types definitions for sizing, variants, and hint/status styling.
  • Adds a full Storybook story set and a comprehensive Vitest + Testing Library test suite; exports Time from the package entrypoint.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/index.ts Re-exports Time from the root package API.
src/components/atoms/time/types.ts Defines CVA variants, prop/types, and hint/segment types for Time.
src/components/atoms/time/useTime.ts Implements all time-segment logic: parsing/clamping, focus management, keyboard controls, and stepper behavior.
src/components/atoms/time/Time.tsx Renders the segmented inputs, label/hint UI, optional clock adornment, and optional steppers.
src/components/atoms/time/Time.stories.tsx Adds Storybook coverage for variants, states, and configurations.
src/components/atoms/time/Time.test.tsx Adds hook + component test coverage for behavior, ARIA attributes, and keyboard interactions.
src/components/atoms/time/index.ts Public exports for the component and its types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/atoms/time/Time.tsx
Comment thread src/components/atoms/time/Time.tsx
Comment thread src/components/atoms/time/types.ts
@egdev6 egdev6 dismissed their stale review July 13, 2026 14:22

done

@egdev6 egdev6 merged commit 31d3fc6 into main Jul 13, 2026
10 checks passed
@egdev6 egdev6 deleted the feature/time branch July 13, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature Feature changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ATOMS] Time

3 participants