Skip to content

refactor(time): migrate ReadableTime from Flow to TypeScript - #4763

Merged
mergify[bot] merged 2 commits into
box:masterfrom
bonchevskyi:refactor/flow-to-ts-time
Aug 11, 2026
Merged

refactor(time): migrate ReadableTime from Flow to TypeScript#4763
mergify[bot] merged 2 commits into
box:masterfrom
bonchevskyi:refactor/flow-to-ts-time

Conversation

@bonchevskyi

@bonchevskyi bonchevskyi commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Convert ReadableTime component to TypeScript

This PR converts src/components/time from JavaScript with Flow to TypeScript.

Changes

  • Converted ReadableTime.js to ReadableTime.ts with exported ReadableTimeProps interface
  • Converted messages.js to messages.ts
  • Converted index.js to index.ts, re-exporting the component and its types
  • Converted ReadableTime.stories.js to ReadableTime.stories.tsx
  • Converted __tests__/ReadableTime.test.js to ReadableTime.test.tsx
  • Created .js.flow files for backward compatibility

Testing

  • Ran tests for src/components/time; all 12 pass with snapshots matching previous output
  • yarn lint:ts and flow check pass
  • Manually verified in Storybook (Components/ReadableTime) that behavior is unchanged

Summary by CodeRabbit

  • New Features

    • Added an internationalized timestamp display for relative times, dates, weekdays, and optional time details.
    • Supports configurable thresholds, future timestamp handling, uppercase formatting, and locale-aware messages.
    • Exported the timestamp component for use throughout the application.
  • Documentation

    • Added Storybook examples covering relative times, date formats, and future timestamps.

@bonchevskyi
bonchevskyi requested a review from a team as a code owner August 10, 2026 13:24
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Added the internationalized ReadableTime component with configurable relative and absolute timestamp formats, future-timestamp handling, locale-aware output, Flow compatibility, public exports, and Storybook examples.

Changes

ReadableTime formatting

Layer / File(s) Summary
Formatting contracts and messages
src/components/time/ReadableTime.ts, src/components/time/ReadableTime.js.flow, src/components/time/messages.*
Added typed props, timestamp classification, localized messages, relative thresholds, date and weekday formats, future-timestamp correction, and optional uppercase output.
Public exports and compatibility wiring
src/components/time/index.ts, src/components/time/index.js.flow
Exported ReadableTime and ReadableTimeProps from TypeScript and Flow entry points.
Usage examples and test setup
src/components/time/ReadableTime.stories.tsx, src/components/time/__tests__/ReadableTime.test.tsx
Added Storybook examples for relative, date, time, and future timestamps. Added Enzyme render utilities to the test imports.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested labels: ready-to-merge

Suggested reviewers: jpan-box, tjiang-box, vitali-usik

Poem

I’m a rabbit with timestamps bright,
Formatting each day and night.
Today, yesterday, dates in a row,
Localized words begin to glow.
Hop through Storybook—everything’s right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the migration of ReadableTime from Flow-typed JavaScript to TypeScript.
Description check ✅ Passed The description explains the migration scope, lists the converted files, documents compatibility files, and reports relevant testing results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (5)
src/components/time/index.js.flow (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: export the props type here for parity with index.ts.

index.ts line 2 exports ReadableTimeProps. This Flow entry point exports only the component. Flow consumers cannot reach the props shape from the index. ReadableTime.js.flow also keeps its Props type unexported, so exporting it there first is a prerequisite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/time/index.js.flow` around lines 1 - 3, Export the
ReadableTime props type through the Flow entry point for parity with index.ts.
First export the Props type from ReadableTime.js.flow, then re-export it
alongside ReadableTime from the Flow index.
src/components/time/ReadableTime.ts (2)

38-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive shouldShowYear after the future-timestamp correction, and avoid reassigning the parameter.

Line 39 computes shouldShowYear from the uncorrected timestamp. Lines 41-44 then replace timestamp. The derived value therefore describes a value that is no longer formatted. The corrected value is normally today, so isToday wins and shouldShowYear is ignored in practice, but the ordering is fragile for later edits.

Line 43 also reassigns a destructured parameter, which many ESLint configs reject under no-param-reassign.

♻️ Proposed refactor
     const shouldUppercase = uppercase && !nonUppercaseLocales.includes(intl.locale);
     const relativeIfNewerThanTs = Date.now() - relativeThreshold;
-    const shouldShowYear = !isCurrentYear(timestamp);
-
-    if (!allowFutureTimestamps && timestamp > Date.now()) {
-        // TODO: what is the reasoning behind this rule?
-        timestamp = relativeIfNewerThanTs; // Default to 'Today' for timestamps that would show a future date
-    }
+
+    // Default to 'Today' for timestamps that would show a future date
+    // TODO: what is the reasoning behind this rule?
+    const effectiveTimestamp =
+        !allowFutureTimestamps && timestamp > Date.now() ? relativeIfNewerThanTs : timestamp;
+
+    const shouldShowYear = !isCurrentYear(effectiveTimestamp);

Then use effectiveTimestamp in the branches below and in values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/time/ReadableTime.ts` around lines 38 - 44, In ReadableTime,
stop mutating the destructured timestamp parameter by introducing an
effectiveTimestamp value that applies the future-timestamp correction. Compute
shouldShowYear with effectiveTimestamp after that correction, then replace
subsequent timestamp usage in the formatting branches and values construction
with effectiveTimestamp.

9-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove intl from the exported ReadableTimeProps type.

ReadableTimeProps is a consumer-facing public type, but the exported default is injectIntl(ReadableTime), which supplies intl. This makes TypeScript consumers who type props with ReadableTimeProps add an injected prop instead of describing the props the wrapped component accepts. Re-export own props plus WrappedComponentProps from react-intl for typed consumers while keeping the implementation prop shape.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/time/ReadableTime.ts` around lines 9 - 24, Remove intl from
the exported ReadableTimeProps interface and preserve it only in the internal
ReadableTime implementation props. Update the public export typing for the
injectIntl-wrapped ReadableTime to combine the component’s own props with
react-intl’s WrappedComponentProps, so consumers type only supplied props while
the injected intl prop remains supported internally.
src/components/time/ReadableTime.js.flow (1)

11-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider reducing this shim to declarations only.

This file repeats the whole component body from ReadableTime.ts, including the branch chain and the threshold logic. Two copies of the same logic can drift as the TS file changes. A .js.flow sibling normally declares only the exported types and signatures, and the compiled TS output supplies the runtime behavior.

If the repository convention for this migration is to keep the full body, ignore this note.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/time/ReadableTime.js.flow` around lines 11 - 38, The
ReadableTime.js.flow shim duplicates the runtime implementation from
ReadableTime.ts; reduce it to declarations only by retaining the Props type and
exported component signature while removing the duplicated constants,
destructuring, and function body. Follow the repository’s existing migration
convention if sibling .js.flow files establish a different declaration pattern.
src/components/time/ReadableTime.stories.tsx (1)

50-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace the deprecated notes parameter with Docs metadata.

Storybook 10 moves story documentation into the docs parameters, while @storybook/addon-notes documentation remains available separately. Type the default export as Meta if you keep Storybook CSF typing here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/time/ReadableTime.stories.tsx` around lines 50 - 59, Update
the ReadableTime story’s default export to replace the deprecated
parameters.notes metadata with the Storybook docs metadata structure, while
preserving the existing notes content through the supported addon-notes
configuration. Type the default export as Meta if CSF typing is used, and leave
the existing chromatic setting unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/time/index.js.flow`:
- Around line 1-3: Export the ReadableTime props type through the Flow entry
point for parity with index.ts. First export the Props type from
ReadableTime.js.flow, then re-export it alongside ReadableTime from the Flow
index.

In `@src/components/time/ReadableTime.js.flow`:
- Around line 11-38: The ReadableTime.js.flow shim duplicates the runtime
implementation from ReadableTime.ts; reduce it to declarations only by retaining
the Props type and exported component signature while removing the duplicated
constants, destructuring, and function body. Follow the repository’s existing
migration convention if sibling .js.flow files establish a different declaration
pattern.

In `@src/components/time/ReadableTime.stories.tsx`:
- Around line 50-59: Update the ReadableTime story’s default export to replace
the deprecated parameters.notes metadata with the Storybook docs metadata
structure, while preserving the existing notes content through the supported
addon-notes configuration. Type the default export as Meta if CSF typing is
used, and leave the existing chromatic setting unchanged.

In `@src/components/time/ReadableTime.ts`:
- Around line 38-44: In ReadableTime, stop mutating the destructured timestamp
parameter by introducing an effectiveTimestamp value that applies the
future-timestamp correction. Compute shouldShowYear with effectiveTimestamp
after that correction, then replace subsequent timestamp usage in the formatting
branches and values construction with effectiveTimestamp.
- Around line 9-24: Remove intl from the exported ReadableTimeProps interface
and preserve it only in the internal ReadableTime implementation props. Update
the public export typing for the injectIntl-wrapped ReadableTime to combine the
component’s own props with react-intl’s WrappedComponentProps, so consumers type
only supplied props while the injected intl prop remains supported internally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c135d9e0-9db8-4599-9d0e-232dae622ef6

📥 Commits

Reviewing files that changed from the base of the PR and between 84b90a7 and b3409ac.

⛔ Files ignored due to path filters (1)
  • src/components/time/__tests__/__snapshots__/ReadableTime.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • src/components/time/ReadableTime.js.flow
  • src/components/time/ReadableTime.stories.tsx
  • src/components/time/ReadableTime.ts
  • src/components/time/__tests__/ReadableTime.test.tsx
  • src/components/time/index.js.flow
  • src/components/time/index.ts
  • src/components/time/messages.js.flow
  • src/components/time/messages.ts

Comment thread src/components/time/index.ts
@bonchevskyi
bonchevskyi force-pushed the refactor/flow-to-ts-time branch from b3409ac to 18c00ad Compare August 11, 2026 10:41
@mergify

mergify Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-08-11 15:14 UTC · Rule: Automatic strict merge · triggered by rule Automatic merge queue
  • Checks passed · in-place
  • Merged2026-08-11 15:38 UTC · at a3064803b586b9bc90d3e1290a6dad1ab8e48b3c · squash

This pull request spent 24 minutes 9 seconds in the queue, including 11 minutes 47 seconds running CI.

Required conditions to merge
  • github-review-approved [🛡 GitHub branch protection]
  • any of [🛡 GitHub branch protection]:
    • check-success = Summary
    • check-neutral = Summary
    • check-skipped = Summary
  • any of [🛡 GitHub branch protection]:
    • check-success = lint_test_build
    • check-neutral = lint_test_build
    • check-skipped = lint_test_build
  • any of [🛡 GitHub branch protection]:
    • check-success = license/cla
    • check-neutral = license/cla
    • check-skipped = license/cla
  • any of [🛡 GitHub branch protection]:
    • check-success = lint_pull_request
    • check-neutral = lint_pull_request
    • check-skipped = lint_pull_request

@mergify
mergify Bot merged commit 1604913 into box:master Aug 11, 2026
9 of 10 checks passed
@mergify mergify Bot removed the queued label Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants