Skip to content

Commit 8279f2f

Browse files
Merge pull request #6104 from Hacker0x01/fix/issue-5551-infinite-time-picker-height
fix: prevent infinite height growth in time picker
2 parents ff23ce8 + 8f630a7 commit 8279f2f

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

CLAUDE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,18 @@ The repo uses Husky with lint-staged. On commit:
155155
- **Formatting**: Prettier 3.4.2
156156
- **CSS**: Sass 1.93.2
157157

158+
## Bug Fix Workflow (TDD)
159+
160+
When fixing bugs, always follow Test-Driven Development:
161+
162+
1. **Write the test first** - Create a failing test that reproduces the bug
163+
2. **Confirm it fails** - Run the test to verify it captures the broken behavior
164+
3. **Implement the fix** - Make the minimal code change to fix the issue
165+
4. **Verify the test passes** - Run the test again to confirm the fix works
166+
5. **Run full test suite** - Ensure no regressions with `yarn test:ci`
167+
168+
This ensures every bug fix has regression coverage and documents the expected behavior.
169+
158170
## Code Conventions
159171

160172
- **Prettier handles all code formatting** - don't worry about tabs vs spaces

src/test/timepicker_test.test.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,44 @@ describe("TimePicker", () => {
8080
expect(resizeObserverCallback).toBe(null);
8181
});
8282
});
83+
84+
it("should not cause infinite loop when resize callback is called multiple times", async () => {
85+
const { container } = render(
86+
<DatePicker
87+
inline
88+
selected={new Date()}
89+
showTimeSelect
90+
timeIntervals={15}
91+
/>,
92+
);
93+
94+
await waitFor(() => {
95+
expect(mockObserve).toHaveBeenCalledTimes(1);
96+
});
97+
98+
const resizeObserverCallback = getResizeObserverCallback();
99+
const mockObserveElement = mockObserve.mock.calls[0][0];
100+
expect(typeof resizeObserverCallback).toBe("function");
101+
102+
const timeList = container.querySelector(
103+
".react-datepicker__time-list",
104+
) as HTMLElement;
105+
expect(timeList).not.toBeNull();
106+
107+
// Get initial height
108+
const initialHeight = timeList.style.height;
109+
110+
// Call resize callback multiple times (simulating what would happen in an infinite loop)
111+
if (resizeObserverCallback) {
112+
resizeObserverCallback([], mockObserveElement);
113+
resizeObserverCallback([], mockObserveElement);
114+
resizeObserverCallback([], mockObserveElement);
115+
}
116+
117+
// Height should remain stable (not grow infinitely)
118+
// The fix ensures setState is only called when height actually changes
119+
expect(timeList.style.height).toBe(initialHeight);
120+
});
83121
});
84122

85123
it("should update on input time change", () => {

src/time.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,14 @@ export default class Time extends Component<TimeProps, TimeState> {
9797

9898
private updateContainerHeight(): void {
9999
if (this.props.monthRef && this.header) {
100-
this.setState({
101-
height: this.props.monthRef.clientHeight - this.header.clientHeight,
102-
});
100+
const newHeight =
101+
this.props.monthRef.clientHeight - this.header.clientHeight;
102+
// Only update state if height actually changed to prevent infinite resize loops
103+
if (this.state.height !== newHeight) {
104+
this.setState({
105+
height: newHeight,
106+
});
107+
}
103108
}
104109
}
105110

0 commit comments

Comments
 (0)