Skip to content

refactor: improve utility functions#80

Merged
shubug1015 merged 8 commits intomainfrom
refactor/utility-functions
Jan 17, 2026
Merged

refactor: improve utility functions#80
shubug1015 merged 8 commits intomainfrom
refactor/utility-functions

Conversation

@shubug1015
Copy link
Copy Markdown
Owner

@shubug1015 shubug1015 commented Jan 15, 2026

Description

This PR refactors utility functions to improve readability, type safety, and maintainability.

Key changes include:

  • Simplifying recursive logic and reducing responsibility overload in utility functions
  • Introducing clearer function boundaries and more intention-revealing names

This refactor is focused on internal code quality and long-term maintainability.

Related Issue

Closes #79

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor

Checklist

  • My code follows the project style guidelines
  • I performed a self-review of my own code
  • I added tests that prove my fix is effective
  • I added necessary documentation

Summary by CodeRabbit

  • New Features

    • Added onAnimationEnd callback support for text animations.
    • New handling for newline characters so line breaks render correctly.
  • Refactor

    • Reworked text-splitting and animation internals for more consistent character/word animations.
    • Improved type safety and element detection to make animations more reliable.

✏️ Tip: You can customize this high-level summary in your review settings.

@shubug1015 shubug1015 requested a review from kjyong702 January 15, 2026 11:18
@shubug1015 shubug1015 self-assigned this Jan 15, 2026
@shubug1015 shubug1015 added enhancement New feature or request refactor Refactor code labels Jan 15, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Renames and migrations: splitNodeAndExtractText → splitReactNode, updates callers and tests to use { nodes, text }, renames hook prop splittedNodenodes and adds optional onAnimationEnd, refactors countNodes signature, renames type guard isNullishNodeisNonRenderableNode, and removes the old split utility files.

Changes

Cohort / File(s) Change Summary
Hook: useAnimatedChildren
src/hooks/useAnimatedChildren/useAnimatedChildren.tsx, src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx
Prop renamed from splittedNodenodes; hook signature and internal names updated; accepts optional onAnimationEnd; tests updated to pass { nodes, ... } and add onAnimationEnd behaviour tests.
Hook: useTextMotionAnimation
src/hooks/useTextMotionAnimation/useTextMotionAnimation.ts, src/hooks/useTextMotionAnimation/useTextMotionAnimation.spec.tsx
Replaced splitNodeAndExtractText usage with splitReactNode; adjusted mocked return shape and callers to use nodes.
Split utility (new)
src/utils/splitReactNode/splitReactNode.ts, src/utils/splitReactNode/splitReactNode.spec.tsx, src/utils/splitReactNode/index.ts
New splitReactNode(node, split) added and re-exported; returns { nodes, text }; comprehensive tests added for character/word modes and edge cases.
Split utility (removed)
src/utils/splitNodeAndExtractText/* (index.ts, splitNodeAndExtractText.ts, splitNodeAndExtractText.spec.tsx)
Removed re-export, deleted original implementation and its tests as part of migration.
countNodes
src/utils/countNodes/countNodes.ts, src/utils/countNodes/countNodes.spec.tsx
Signature changed from nodes: ReactNode[]node: ReactNode; traversal simplified to recursive single-node entry; tests rewritten/expanded across many node shapes.
Type guards
src/utils/typeGuards/typeGuards.ts, src/utils/typeGuards/typeGuards.spec.tsx
isNullishNode renamed to isNonRenderableNode (export name change); isElementWithChildren logic simplified and its behavior adjusted to treat elements without explicit children as having children.
splitText
src/utils/splitText/splitText.ts, src/utils/splitText/splitText.spec.ts
Explicit character case added; replaced default fallback with exhaustive never check; test adjusted to reflect invalid-split exhaustive behavior.
generateAnimation & misc docs
src/utils/generateAnimation/generateAnimation.ts, src/utils/sequenceHelpers/sequenceHelpers.ts, src/utils/accessibility/accessibility.ts
Minor refactors and JSDoc formatting tweaks: style constantization, early-return for custom animation path, and small doc edits.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • kjyong702
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'refactor: improve utility functions' is generic and vague, failing to convey the specific scope or nature of the refactoring work. Consider a more descriptive title that highlights key changes, such as 'refactor: rename splitNodeAndExtractText to splitReactNode and update type guards' to better communicate the primary objectives.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR successfully addresses all coding objectives from issue #79: simplified recursive logic in splitReactNode, split overloaded functions (isNullishNode renamed to isNonRenderableNode), improved naming clarity, aligned abstraction levels, and enhanced type safety through explicit type guards.
Out of Scope Changes check ✅ Passed All code changes directly support the refactoring objectives from issue #79. The addition of onAnimationEnd support and test expansions align with improving code quality and maintainability.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description follows the template structure with all required sections completed: Description, Related Issue, Type of change, and Checklist with appropriate items marked.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx`:
- Around line 195-225: The test named "triggers onAnimationEnd callback when
last node animation ends" currently only asserts that onAnimationEndMock is
defined; replace that check with a real invocation assertion (e.g.,
expect(onAnimationEndMock).toHaveBeenCalled() or
expect(onAnimationEndMock).toHaveBeenCalledTimes(1)) after firing the
animationEnd on lastSpan so the test verifies the onAnimationEnd callback was
actually invoked.
- Around line 138-153: The test only asserts the mock exists rather than that it
was invoked; update the test for useAnimatedChildren to render the generated
nodes into the DOM (from splitReactNode -> nodes), find the last animated
element (e.g., last span), dispatch/fire an 'animationend' event on that
element, and then assert onAnimationEndMock was called (e.g.,
toHaveBeenCalled()). Ensure you keep initialDelay: 0 and animationOrder:
'first-to-last' so the last node is the target, and use the same
onAnimationEndMock passed into useAnimatedChildren.

In `@src/hooks/useTextMotionAnimation/useTextMotionAnimation.spec.tsx`:
- Line 20: Rename the mock variable to match the imported function: change the
declaration to use mockSplitReactNode (e.g., const mockSplitReactNode =
splitReactNode as jest.Mock) and replace every usage of
mockSplitNodeAndExtractText in this test file with mockSplitReactNode so
references (mockReturnValue, mockImplementation, assertions) correctly reflect
the mocked splitReactNode function.

In `@src/utils/splitReactNode/splitReactNode.ts`:
- Around line 12-22: The JSDoc block references the wrong function name
(`splitNodeAndExtractText`) while the actual implementation is `splitReactNode`;
update the JSDoc to match the real function name and signatures (replace
occurrences of splitNodeAndExtractText with splitReactNode and ensure
param/return descriptions align with the function's parameters `node` and
`split` and return type `SplitResult`) so the doc comment accurately describes
`splitReactNode`.

In `@src/utils/splitText/splitText.spec.ts`:
- Around line 18-24: The test's name is inaccurate: splitText currently returns
the passed invalid value via the exhaustiveCheck path rather than defaulting to
a character split; update the test description to reflect that behavior (e.g.,
rename the it block to something like "should return the invalid split value
(exhaustiveCheck)" or similar) and keep the expectation that splitText(input,
invalidSplit) returns the invalid value; refer to the splitText function and the
exhaustiveCheck/default switch path when making the change.
🧹 Nitpick comments (8)
src/utils/generateAnimation/generateAnimation.ts (1)

32-48: Good refactor - Early return and explicit type guard improve readability.

The early return after handling custom animations (Line 35) eliminates implicit fall-through and makes the control flow clearer. The explicit type guard on Line 38 (typeof config === 'object' && config !== null && 'variant' in config) properly narrows the type before processing standard animations.

One minor observation: Object.assign(style, customProps) mutates style in-place, while the final return on Lines 51-56 creates a new object. This is fine since style is locally scoped, but the mixed mutation/immutable patterns could be slightly confusing. Consider using spread for consistency if preferred:

Object.entries(rest).forEach(([key, value]) => {
  if (value !== undefined && value !== null) {
    style[`--${name}-${key}`] = value as string | number;
  }
});

This is a minor style preference and not blocking.

src/utils/typeGuards/typeGuards.ts (1)

29-40: Consider renaming isElementWithChildren for predictability.

The simplified implementation (Line 39) now accepts any valid React element, regardless of whether it has children. However, the function name isElementWithChildren and the JSDoc (Lines 31-32: "checks if a React node is a valid React element with children") imply that children must be present.

The return type ReactElement<{ children?: ReactNode }> correctly indicates children are optional, but the name creates a misleading expectation.

Suggestion: Rename to isReactElement or isElementNode to match the actual behavior, or update the JSDoc to clarify the intent (e.g., "checks if a node is a React element that can contain children").

As per coding guidelines on Predictability: "Verify that function and variable names clearly imply their behavior."

Option 1: Rename for clarity
-export const isElementWithChildren = (node: ReactNode): node is ReactElement<{ children?: ReactNode }> => {
+export const isReactElement = (node: ReactNode): node is ReactElement<{ children?: ReactNode }> => {
   return isValidElement<{ children?: ReactNode }>(node);
 };
Option 2: Update JSDoc to clarify intent
 /**
  * `@description`
- * Type guard function that checks if a React node is a valid React element with children.
- * It returns `true` if the node is a valid React element with children, otherwise `false`.
+ * Type guard function that checks if a React node is a valid React element that may contain children.
+ * It returns `true` if the node is a valid React element, otherwise `false`.
  *
  * `@param` {ReactNode} node - The React node to check.
  *
- * `@returns` {boolean} `true` if the node is a valid React element with children, otherwise `false`.
+ * `@returns` {boolean} `true` if the node is a valid React element, otherwise `false`.
  */
src/utils/countNodes/countNodes.spec.tsx (2)

24-32: Verify the whitespace text node count.

The test expects 3 nodes for {'Hello'} {'World'}, which implies the whitespace between the two JSX expressions is counted as a separate text node. This is correct JSX behavior—the space between } and { becomes its own text node.

Consider adding a brief comment to make this whitespace behavior explicit for future readers, as it's a subtle JSX detail that could confuse maintainers.


104-117: Clarify the custom component test behavior.

This test validates that countNodes counts children passed to a component, not the component's rendered output. The name "counts text nodes rendered by a functional component" could be misleading since countNodes operates on the React element tree, not the DOM.

Consider renaming to something like "counts children passed to a custom component" to better reflect the actual behavior being tested.

src/utils/splitReactNode/splitReactNode.spec.tsx (2)

62-74: Consider documenting the "unsupported type" fallback behavior.

Testing Symbol and function inputs exercises the final fallback branch that returns { nodes: [node], text: '' }. While this is valid defensive testing, consider whether this fallback behavior is intentional API design or just a safety net.

If intentional, a brief comment in the implementation explaining when this fallback applies would help future maintainers understand the design decision.


3-85: Consider grouping tests into describe blocks for consistency.

The countNodes.spec.tsx file uses nested describe blocks to organize tests by category (non-renderable nodes, text nodes, element nodes, etc.). For consistency across the codebase, consider applying the same structure here:

describe('splitReactNode utility', () => {
  describe('text nodes', () => { /* string/number tests */ });
  describe('element nodes', () => { /* React element tests */ });
  describe('arrays', () => { /* array tests */ });
  describe('non-renderable nodes', () => { /* null/boolean tests */ });
  describe('split modes', () => { /* character vs word tests */ });
});

This would improve cohesion with the related test file structure. As per coding guidelines, code that changes together should be located together—and consistent test organization aids maintainability.

src/utils/splitReactNode/splitReactNode.ts (1)

59-62: Consider moving mergeResults before its usage.

Per readability principles, code should read top-to-bottom. Currently, readers encounter mergeResults on line 37 before its definition on line 59, requiring them to jump around. Moving helper functions above their first usage improves flow.

src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx (1)

227-249: Test should verify the updated callback is wired correctly.

This test rerenders with a new callback but only checks it's defined. Consider firing animationEnd after rerender and verifying onAnimationEndMock2 is called (not onAnimationEndMock1).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4909267 and 1887db3.

📒 Files selected for processing (19)
  • src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx
  • src/hooks/useAnimatedChildren/useAnimatedChildren.tsx
  • src/hooks/useTextMotionAnimation/useTextMotionAnimation.spec.tsx
  • src/hooks/useTextMotionAnimation/useTextMotionAnimation.ts
  • src/utils/accessibility/accessibility.ts
  • src/utils/countNodes/countNodes.spec.tsx
  • src/utils/countNodes/countNodes.ts
  • src/utils/generateAnimation/generateAnimation.ts
  • src/utils/sequenceHelpers/sequenceHelpers.ts
  • src/utils/splitNodeAndExtractText/index.ts
  • src/utils/splitNodeAndExtractText/splitNodeAndExtractText.spec.tsx
  • src/utils/splitNodeAndExtractText/splitNodeAndExtractText.ts
  • src/utils/splitReactNode/index.ts
  • src/utils/splitReactNode/splitReactNode.spec.tsx
  • src/utils/splitReactNode/splitReactNode.ts
  • src/utils/splitText/splitText.spec.ts
  • src/utils/splitText/splitText.ts
  • src/utils/typeGuards/typeGuards.spec.ts
  • src/utils/typeGuards/typeGuards.ts
💤 Files with no reviewable changes (3)
  • src/utils/splitNodeAndExtractText/splitNodeAndExtractText.spec.tsx
  • src/utils/splitNodeAndExtractText/index.ts
  • src/utils/splitNodeAndExtractText/splitNodeAndExtractText.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx}: You are a senior frontend reviewer.
Review this code strictly based on the principles of "easy-to-change code"
from Frontend Fundamentals (Readability, Predictability, Cohesion, Low Coupling).

================================

  1. Readability
    ================================
  • Check if the code can be read top-to-bottom like a document.
  • Flag places where the reader must jump around to understand logic.
  • Suggest extracting logic when multiple contexts are mixed in one function or component.
  • Identify magic numbers or strings and suggest meaningful named constants.
  • Point out unclear or abbreviated variable/function names.
  • Prefer simple control flow over nested or overly clever logic.

================================
2. Predictability

  • Verify that function and variable names clearly imply their behavior.
  • Ensure functions with similar responsibilities return consistent data shapes.
  • Flag hidden side effects (global state mutation, implicit behavior).
  • Warn when the same name is used with different meanings across the codebase.
  • Suggest making implicit assumptions explicit.

================================
3. Cohesion

  • Check whether code that changes together is located together.
  • Flag cases where related logic is scattered across multiple files unnecessarily.
  • Suggest grouping constants, helpers, and hooks near the domain they belong to.
  • Identify components or functions that contain unrelated responsibilities.
  • Prefer feature/domain-based structure over technical grouping when applicable.

================================
4. Coupling

  • Identify code where a small change would cause wide ripple effects.
  • Warn when abstractions increase coupling instead of reducing it.
  • Prefer duplication over premature abstraction if it lowers change impact.
  • Flag excessive prop drilling and suggest alternative patterns if appropriate.
  • Ensure compo...

Files:

  • src/utils/splitText/splitText.spec.ts
  • src/utils/accessibility/accessibility.ts
  • src/utils/generateAnimation/generateAnimation.ts
  • src/utils/countNodes/countNodes.ts
  • src/utils/typeGuards/typeGuards.spec.ts
  • src/hooks/useTextMotionAnimation/useTextMotionAnimation.ts
  • src/utils/splitReactNode/index.ts
  • src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx
  • src/utils/typeGuards/typeGuards.ts
  • src/utils/splitReactNode/splitReactNode.spec.tsx
  • src/utils/sequenceHelpers/sequenceHelpers.ts
  • src/hooks/useTextMotionAnimation/useTextMotionAnimation.spec.tsx
  • src/utils/countNodes/countNodes.spec.tsx
  • src/utils/splitReactNode/splitReactNode.ts
  • src/hooks/useAnimatedChildren/useAnimatedChildren.tsx
  • src/utils/splitText/splitText.ts
🧬 Code graph analysis (11)
src/utils/splitText/splitText.spec.ts (1)
src/utils/splitText/splitText.ts (1)
  • splitText (12-28)
src/utils/generateAnimation/generateAnimation.ts (2)
src/constants/animationDefaults.ts (1)
  • ANIMATION_DEFAULTS (19-22)
src/types/animations.ts (2)
  • CustomAnimation (141-143)
  • StandardAnimation (123-130)
src/utils/countNodes/countNodes.ts (1)
src/utils/typeGuards/typeGuards.ts (3)
  • isNonRenderableNode (25-27)
  • isTextNode (12-14)
  • isElementWithChildren (38-40)
src/utils/typeGuards/typeGuards.spec.ts (1)
src/utils/typeGuards/typeGuards.ts (2)
  • isNonRenderableNode (25-27)
  • isElementWithChildren (38-40)
src/hooks/useTextMotionAnimation/useTextMotionAnimation.ts (1)
src/hooks/useAnimatedChildren/useAnimatedChildren.tsx (1)
  • useAnimatedChildren (37-67)
src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx (2)
src/hooks/useAnimatedChildren/useAnimatedChildren.tsx (1)
  • useAnimatedChildren (37-67)
src/hooks/useAnimatedChildren/index.ts (1)
  • useAnimatedChildren (2-2)
src/utils/splitReactNode/splitReactNode.spec.tsx (1)
src/utils/splitReactNode/splitReactNode.ts (1)
  • splitReactNode (23-57)
src/hooks/useTextMotionAnimation/useTextMotionAnimation.spec.tsx (1)
src/hooks/useAnimatedChildren/useAnimatedChildren.tsx (1)
  • useAnimatedChildren (37-67)
src/utils/countNodes/countNodes.spec.tsx (1)
src/utils/countNodes/countNodes.ts (1)
  • countNodes (14-34)
src/utils/splitReactNode/splitReactNode.ts (3)
src/utils/splitReactNode/index.ts (1)
  • splitReactNode (1-1)
src/types/common.ts (1)
  • Split (18-18)
src/utils/typeGuards/typeGuards.ts (3)
  • isNonRenderableNode (25-27)
  • isTextNode (12-14)
  • isElementWithChildren (38-40)
src/hooks/useAnimatedChildren/useAnimatedChildren.tsx (2)
src/utils/countNodes/countNodes.ts (1)
  • countNodes (14-34)
src/types/common.ts (2)
  • AnimationOrder (34-34)
  • Motion (50-59)
🔇 Additional comments (24)
src/utils/accessibility/accessibility.ts (1)

1-10: LGTM!

The JSDoc formatting improvement adds visual separation between sections, enhancing documentation readability. The function itself is well-designed: single responsibility, clear naming, and type-safe return signature.

src/utils/sequenceHelpers/sequenceHelpers.ts (2)

3-20: LGTM!

The JSDoc formatting improvement (blank line before @returns) follows standard documentation conventions. The function itself is pure, well-named, and has a single clear responsibility.


22-34: LGTM!

Consistent JSDoc formatting with calculateSequenceIndex. The function is simple, pure, and the name clearly communicates its intent.

src/utils/generateAnimation/generateAnimation.ts (2)

27-27: LGTM - Clean initialization.

Initializing style as an empty object is cleaner and avoids unnecessary type assertions.


51-56: LGTM - Clear return structure.

The return object explicitly shows the final shape with animation at the top and custom properties spread in. This is readable and predictable.

src/utils/splitText/splitText.ts (2)

14-15: Good choice using spread for character splitting.

Using [...text] instead of text.split('') correctly handles multi-byte Unicode characters (e.g., emojis, accented characters). This is a subtle but important improvement for text processing utilities.


23-26: LGTM - Exhaustive switch enforcement.

The never type check in the default case ensures compile-time errors if new Split variants are added without updating this function. This is an excellent pattern for type-safe exhaustive handling.

src/utils/typeGuards/typeGuards.spec.ts (3)

3-3: LGTM - Import updated for renamed function.

The import correctly reflects the rename from isNullishNode to isNonRenderableNode.


38-67: LGTM - Tests properly updated for renamed type guard.

The test suite correctly covers the isNonRenderableNode behavior. The new name is more semantically accurate since booleans are not "nullish" (they're truthy/falsy values) but they are non-renderable in React's rendering model.


81-83: Verify: Function name suggests "with children" but implementation accepts elements without children.

The test now expects isElementWithChildren to return true for elements created without a children prop. Per the implementation in src/utils/typeGuards/typeGuards.ts (lines 37-39), this function now simply checks isValidElement() without verifying children exist.

This creates a naming/behavior mismatch:

  • Name implies: Element must have children
  • Behavior: Any valid React element passes

If this is intentional (e.g., checking for elements that can have children rather than elements that do have children), consider renaming to isReactElement or isElementNode for predictability. Otherwise, the current name may confuse future maintainers. As per coding guidelines, function names should clearly imply their behavior.

src/utils/typeGuards/typeGuards.ts (1)

16-27: LGTM - Rename improves semantic clarity.

Renaming isNullishNode to isNonRenderableNode is a good choice. The term "nullish" typically refers to null | undefined, but this guard also includes boolean. "Non-renderable" accurately describes all three types that React skips during rendering.

src/utils/countNodes/countNodes.ts (2)

3-3: LGTM - Import updated for renamed type guard.

Correctly imports the renamed isNonRenderableNode function.


14-34: LGTM - Clean refactor with improved readability.

The refactored implementation is well-structured:

  1. Clear sequential checks - Early returns for non-renderable (→ 0) and text nodes (→ 1) eliminate nesting
  2. Consistent type guard usage - All three guards from typeGuards.ts are used appropriately
  3. Simplified API - Single ReactNode parameter is more intuitive than requiring an array wrapper

The edge case where isElementWithChildren returns true but node.props.children is undefined is safely handled: countNodes(undefined) will hit the isNonRenderableNode check and return 0.

This aligns well with the PR objective of reducing complexity and improving maintainability.

src/utils/countNodes/countNodes.spec.tsx (1)

6-16: Well-structured test organization for non-renderable nodes.

The grouping of null, undefined, boolean, and empty fragment tests clearly documents the expected behavior for edge cases. This aligns well with the isNonRenderableNode guard in the implementation.

src/utils/splitReactNode/index.ts (1)

1-1: Clean public API export.

The barrel export correctly exposes the renamed splitReactNode utility, aligning with the PR objective to improve naming clarity.

src/hooks/useTextMotionAnimation/useTextMotionAnimation.ts (1)

33-43: Clean integration with renamed utility and improved naming.

The refactor from splittedNode to nodes improves readability (fixing the grammatical issue). The conditional nodes: shouldAnimate ? nodes : [children] correctly maintains a consistent array shape for useAnimatedChildren regardless of animation state.

src/utils/splitReactNode/splitReactNode.spec.tsx (1)

3-85: Comprehensive test coverage for the new utility.

The tests thoroughly cover the splitReactNode behavior across various input types. The coverage includes primitive types, React elements, arrays, and edge cases.

src/hooks/useTextMotionAnimation/useTextMotionAnimation.spec.tsx (2)

37-40: LGTM!

Test correctly verifies that splitReactNode is called with the expected arguments and aligns with the renamed function.


72-83: LGTM!

The test properly validates that useAnimatedChildren receives the nodes property (previously splittedNode), maintaining consistency with the API change.

src/utils/splitReactNode/splitReactNode.ts (1)

23-57: Well-structured recursive implementation.

The function handles all ReactNode variants with clear type guards, maintains consistent return shapes across branches, and properly preserves element structure through cloneElement. The single-responsibility branches align well with the PR's goal of reducing complexity.

src/hooks/useAnimatedChildren/useAnimatedChildren.spec.tsx (1)

288-307: LGTM!

Good addition testing newline character handling. The test verifies that <br> elements are rendered for newlines, which is important edge case coverage.

src/hooks/useAnimatedChildren/useAnimatedChildren.tsx (3)

11-17: Good API improvement.

Renaming splittedNode to nodes better reflects the parameter's purpose (an array of React nodes) and aligns with standard React terminology. The addition of the optional onAnimationEnd callback provides useful extensibility.


44-48: LGTM - correct stable callback pattern.

Using a ref to hold onAnimationEnd and updating it via effect ensures the callback remains stable for memoization while always invoking the latest version. This prevents unnecessary re-renders when the callback identity changes.


69-115: LGTM!

The internal wrapWithAnimatedSpan function is updated consistently with the public API changes. The renaming from splittedNode to nodes and the return object structure { nodes: animatedNodes, nextSequenceIndex } maintain clarity and predictability.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@shubug1015 shubug1015 marked this pull request as ready for review January 17, 2026 08:39
@shubug1015 shubug1015 merged commit f645188 into main Jan 17, 2026
10 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 18, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request refactor Refactor code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] Improve utility functions for readability and maintainability

1 participant