• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prabhuignoto / react-chrono / #92

18 Jun 2025 10:08AM UTC coverage: 90.727% (+0.9%) from 89.791%
#92

push

web-flow
Minor cleanup and expanding test coverage (#548)

* refactor: rename project to React Chrono UI and update related files

* fix: update tsconfig to reference the correct entry file for React Chrono UI

* feat: enhance styles with vendor prefixes and improve cross-browser support

* Add tests for useNewScrollPosition hook and TimelineHorizontal component

- Implement comprehensive tests for the useNewScrollPosition hook covering horizontal, vertical, and edge cases.
- Create a new test file for the TimelineHorizontal component, ensuring it renders correctly and handles various props and states.
- Update snapshots for timeline control and vertical components to reflect recent changes in class names.
- Modify vitest configuration to include all test files in the src directory.

* refactor: simplify transform handling in timeline styles

* refactor: clean up test imports and remove unused styles in timeline components

1783 of 2112 branches covered (84.42%)

Branch coverage included in aggregate %.

670 of 674 new or added lines in 12 files covered. (99.41%)

400 existing lines in 29 files now uncovered.

10564 of 11497 relevant lines covered (91.88%)

10.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.76
/src/components/timeline-vertical/timeline-vertical.tsx
1
import { TimelineVerticalModel } from '@models/TimelineVerticalModel';
2
import {
1✔
3
  useCallback,
4
  FunctionComponent,
5
  ReactNode,
6
  memo,
7
  useMemo,
8
  ReactElement,
9
} from 'react';
10
import { useDynamicContext } from '../contexts';
1✔
11
import TimelineVerticalItem from './timeline-vertical-item';
1✔
12
import { TimelineVerticalWrapper } from './timeline-vertical.styles';
1✔
13

14
/**
15
 * Renders the main vertical timeline structure.
16
 * It maps over the provided `items` array and renders a `TimelineVerticalItem`
17
 * for each, handling layout variations (like alternating cards) and distributing
18
 * props and callbacks appropriately.
19
 *
20
 * @param {TimelineVerticalModel} props - The properties for the TimelineVertical component.
21
 * @returns {JSX.Element} The rendered TimelineVertical component.
22
 */
23
const TimelineVertical: FunctionComponent<TimelineVerticalModel> = memo(
1✔
24
  ({
1✔
25
    // Props with default values
26
    alternateCards = true, // Default to alternating card layout on desktop
6✔
27
    // Callbacks and data props
28
    autoScroll, // Function to handle automatic scrolling when an item becomes active
6✔
29
    contentDetailsChildren, // Optional array of custom nodes for each item's card details
6✔
30
    hasFocus, // Does the timeline component itself have focus? (Passed down)
6✔
31
    iconChildren, // Optional custom icon(s) for the timeline points
6✔
32
    items, // Array of timeline item data objects
6✔
33
    mode, // Timeline mode (e.g., VERTICAL, VERTICAL_ALTERNATING) - Used by children via context?
6✔
34
    onClick, // Global click handler for items (passed down)
6✔
35
    onElapsed, // Global handler for media elapsed events (passed down)
6✔
36
    onOutlineSelection, // Handler for outline selection (potentially unused here, passed down?)
6✔
37
    slideShowRunning, // Is a slideshow active? (Passed down)
6✔
38
    theme, // Theme object (Used by children via context or styled-components)
6✔
39
    cardLess, // Render without cards? (Passed down)
6✔
40
    nestedCardHeight, // Specific height for nested cards (Passed down)
6✔
41
  }: TimelineVerticalModel): ReactElement => {
6✔
42
    // Access dynamic context for mobile view detection
43
    const { isMobile } = useDynamicContext();
6✔
44

45
    /**
46
     * Callback handler passed to each TimelineVerticalItem's onActive.
47
     * When an item becomes active (e.g., scrolls into view), this triggers the autoScroll function
48
     * provided via props, allowing the parent component to scroll the timeline if needed.
49
     * @param {number} offset - Vertical offset within the active item's point element.
50
     * @param {number} wrapperOffset - The offsetTop of the active item's wrapper relative to the scroll parent.
51
     * @param {number} height - The clientHeight of the active item's wrapper.
52
     */
53
    const handleOnActive = useCallback(
6✔
54
      (offset: number, wrapperOffset: number, height: number) => {
6✔
55
        // Call the autoScroll prop function if it exists
UNCOV
56
        autoScroll?.({
×
57
          contentHeight: height,
×
58
          contentOffset: wrapperOffset,
×
59
          pointOffset: offset,
×
60
        });
×
61
      },
×
62
      [autoScroll], // Dependency: only recreate if autoScroll function changes
6✔
63
    );
6✔
64

65
    // Memoize the items rendering to avoid unnecessary recreations
66
    const renderItems = useMemo(() => {
6✔
67
      return items.map((item, index: number) => {
6✔
68
        let itemClassName = ''; // CSS class for layout ('left' or 'right')
24✔
69

70
        // Determine layout class based on mode and index
71
        // In alternating mode on non-mobile views, alternate 'left' and 'right'
72
        if (alternateCards && !isMobile) {
24✔
73
          itemClassName = index % 2 === 0 ? 'left' : 'right';
16✔
74
        }
16✔
75
        // Otherwise (non-alternating or mobile), default to 'right'
76
        else {
8✔
77
          itemClassName = 'right';
8✔
78
        }
8✔
79

80
        // Extract specific content details node for this item, if provided
81
        const contentDetails: ReactNode | null =
24✔
82
          (contentDetailsChildren &&
24!
UNCOV
83
            Array.isArray(contentDetailsChildren) && // Ensure it's an array
×
84
            contentDetailsChildren[index]) ?? // Get node at the current index
×
85
          null;
24✔
86

87
        // Determine the custom icon for this item
88
        let customIcon: ReactNode | null = null;
24✔
89
        if (Array.isArray(iconChildren)) {
24!
90
          // If iconChildren is an array, map icon to item by index
UNCOV
91
          customIcon = iconChildren[index] ?? null;
×
92
        } else if (iconChildren) {
24!
93
          // If iconChildren is a single node, apply it to all items
UNCOV
94
          customIcon = iconChildren;
×
95
        }
×
96

97
        // Render the individual timeline item component
98
        return (
24✔
99
          <TimelineVerticalItem
24✔
100
            {...item} // Spread all properties from the item data object
24✔
101
            // --- Pass down calculated or specific props ---
102
            alternateCards={alternateCards} // Pass down the alternating mode flag
24✔
103
            className={itemClassName} // Pass down the calculated 'left'/'right' class
24✔
104
            contentDetailsChildren={contentDetails} // Pass down the specific content details node
24✔
105
            iconChild={customIcon} // Pass down the specific icon node
24✔
106
            hasFocus={hasFocus} // Pass down the focus state
24✔
107
            index={index} // Pass down the item's index
24✔
108
            key={item.id ?? `timeline-item-${index}`} // Unique key for React rendering
24!
109
            onActive={handleOnActive} // Pass down the memoized active handler
24✔
110
            onClick={onClick} // Pass down the global click handler
24✔
111
            onElapsed={onElapsed} // Pass down the global elapsed handler
24✔
112
            // Removed onShowMore as the handler was empty
113
            slideShowRunning={slideShowRunning} // Pass down the slideshow state
24✔
114
            cardLess={cardLess} // Pass down the cardLess flag
24✔
115
            nestedCardHeight={nestedCardHeight} // Pass down the nested card height
24✔
116
          />
24✔
117
        );
118
      });
6✔
119
    }, [
6✔
120
      items,
6✔
121
      isMobile,
6✔
122
      alternateCards,
6✔
123
      contentDetailsChildren,
6✔
124
      iconChildren,
6✔
125
      hasFocus,
6✔
126
      handleOnActive,
6✔
127
      onClick,
6✔
128
      onElapsed,
6✔
129
      slideShowRunning,
6✔
130
      cardLess,
6✔
131
      nestedCardHeight,
6✔
132
    ]);
6✔
133

134
    // Render the main timeline wrapper
135
    return (
6✔
136
      <TimelineVerticalWrapper as="ul" data-testid="tree-main">
6✔
137
        {renderItems}
6✔
138
      </TimelineVerticalWrapper>
6✔
139
    );
140
  },
6✔
141
);
1✔
142

143
// Set display name for React DevTools
144
TimelineVertical.displayName = 'TimelineVertical';
1✔
145

146
export default TimelineVertical;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc