Skip to content

Commit 6f3fe1f

Browse files
Merge pull request #5539 from qburst/issue-5466/fix/update-time-select-height-on-calendar-resize
🎨🔨 Re-adjust the height of TimePicker on Calendar resize due to number of days
2 parents c1fd5c7 + c12270e commit 6f3fe1f

10 files changed

+154
-9
lines changed

src/test/calendar_test.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
SafeElementWrapper,
4646
safeQuerySelector,
4747
safeQuerySelectorAll,
48+
setupMockResizeObserver,
4849
} from "./test_utils";
4950

5051
import type { ReactDatePickerCustomHeaderProps } from "../calendar";
@@ -136,6 +137,10 @@ describe("Calendar", () => {
136137
};
137138
}
138139

140+
beforeAll(() => {
141+
setupMockResizeObserver();
142+
});
143+
139144
it("should start with the current date in view if no date range", () => {
140145
const now = newDate();
141146
const { instance } = getCalendar();

src/test/datepicker_test.test.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ import DatePicker, { registerLocale } from "../index";
2929
import CustomInput from "./helper_components/custom_input";
3030
import ShadowRoot from "./helper_components/shadow_root";
3131
import TestWrapper from "./helper_components/test_wrapper";
32-
import { getKey, safeQuerySelector } from "./test_utils";
32+
import {
33+
getKey,
34+
safeQuerySelector,
35+
setupMockResizeObserver,
36+
} from "./test_utils";
3337

3438
function getSelectedDayNode(container: HTMLElement) {
3539
return (
@@ -91,6 +95,10 @@ const showDocument = (calendarInput: HTMLElement) => {
9195
};
9296

9397
describe("DatePicker", () => {
98+
beforeEach(() => {
99+
setupMockResizeObserver();
100+
});
101+
94102
afterEach(() => {
95103
jest.resetAllMocks();
96104
});

src/test/exclude_time_period_test.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ import { render } from "@testing-library/react";
22
import React from "react";
33

44
import { newDate, setTime } from "../date_utils";
5+
import { setupMockResizeObserver } from "./test_utils";
56
import DatePicker from "../index";
67

78
describe("DatePicker", () => {
9+
beforeAll(() => {
10+
setupMockResizeObserver();
11+
});
12+
813
it("should only display times between minTime and maxTime", () => {
914
const now = newDate();
1015
const { container } = render(

src/test/exclude_times_test.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ import { render } from "@testing-library/react";
22
import React from "react";
33

44
import { setTime, newDate } from "../date_utils";
5+
import { setupMockResizeObserver } from "./test_utils";
56
import DatePicker from "../index";
67

78
describe("DatePicker", () => {
89
let now: Date, excludeTimes: Date[];
910

11+
beforeAll(() => {
12+
setupMockResizeObserver();
13+
});
14+
1015
beforeEach(() => {
1116
now = newDate();
1217
excludeTimes = [

src/test/min_time_test.test.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { useState } from "react";
33

44
import DatePicker from "../index";
55

6-
import { safeQuerySelector } from "./test_utils";
6+
import { safeQuerySelector, setupMockResizeObserver } from "./test_utils";
77

88
import type { DatePickerProps } from "../index";
99

@@ -44,6 +44,10 @@ const DatePickerWithState = (
4444
};
4545

4646
describe("Datepicker minTime", () => {
47+
beforeAll(() => {
48+
setupMockResizeObserver();
49+
});
50+
4751
it("should select time 12:00 AM when no minTime constraint is set.", () => {
4852
const { getByText, container } = render(<DatePickerWithState />);
4953

src/test/show_time_test.test.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import React from "react";
44
import DatePicker from "../index";
55
import TimeComponent from "../time";
66

7-
import { safeQuerySelector } from "./test_utils";
7+
import { safeQuerySelector, setupMockResizeObserver } from "./test_utils";
88

99
describe("DatePicker", () => {
10+
beforeAll(() => {
11+
setupMockResizeObserver();
12+
});
13+
1014
it("should show time component when showTimeSelect prop is present", () => {
1115
const { container } = render(<DatePicker showTimeSelect open />);
1216
const timeComponent = container.querySelector(

src/test/test_utils.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,37 @@ export const safeQuerySelectorAll = <T extends HTMLElement = HTMLElement>(
123123
return elements;
124124
};
125125

126+
let _resizeObserverCallbackFn: ResizeObserverCallback | null;
127+
128+
export const getResizeObserverCallback = () => _resizeObserverCallbackFn;
129+
130+
export const setupMockResizeObserver = () => {
131+
const mockObserve = jest.fn();
132+
const mockDisconnect = jest.fn();
133+
const mockUnobserve = jest.fn();
134+
135+
const ResizeObserverMock = jest.fn((fn) => {
136+
_resizeObserverCallbackFn = fn;
137+
138+
return {
139+
observe: mockObserve,
140+
disconnect: () => {
141+
_resizeObserverCallbackFn = null;
142+
mockDisconnect();
143+
},
144+
unobserve: jest.fn(),
145+
};
146+
});
147+
148+
global.ResizeObserver = ResizeObserverMock;
149+
150+
return {
151+
observe: mockObserve,
152+
disconnect: mockDisconnect,
153+
unobserve: mockUnobserve,
154+
};
155+
};
156+
126157
export class SafeElementWrapper<T extends HTMLElement> {
127158
constructor(private element: T) {}
128159

src/test/time_input_test.test.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ import DatePicker from "../index";
55
import InputTimeComponent from "../input_time";
66

77
import CustomTimeInput from "./helper_components/custom_time_input";
8-
import { safeQuerySelector } from "./test_utils";
8+
import { safeQuerySelector, setupMockResizeObserver } from "./test_utils";
99

1010
describe("timeInput", () => {
11+
beforeEach(() => {
12+
setupMockResizeObserver();
13+
});
14+
1115
afterEach(() => {
1216
jest.resetAllMocks();
1317
});

src/test/timepicker_test.test.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import DatePicker from "../index";
66

77
import {
88
getKey,
9+
getResizeObserverCallback,
910
SafeElementWrapper,
1011
safeQuerySelector,
1112
safeQuerySelectorAll,
13+
setupMockResizeObserver,
1214
} from "./test_utils";
1315

1416
const MIN_TIME_LI_LEN = 2;
@@ -19,10 +21,65 @@ describe("TimePicker", () => {
1921
let onChangeMoment: Date | undefined;
2022
let instance: DatePicker | null = null;
2123

24+
let mockObserve: jest.Mock, mockDisconnect: jest.Mock;
25+
26+
beforeAll(() => {
27+
const { observe, disconnect } = setupMockResizeObserver();
28+
mockObserve = observe;
29+
mockDisconnect = disconnect;
30+
});
31+
2232
beforeEach(() => {
2333
div = document.createElement("div");
2434
});
2535

36+
describe("Re-adjust height on Calendar Height Change", () => {
37+
beforeEach(() => {
38+
mockObserve.mockReset();
39+
mockDisconnect.mockReset();
40+
});
41+
42+
it("calls observe on mount", async () => {
43+
render(
44+
<DatePicker
45+
inline
46+
selected={new Date()}
47+
showTimeSelect
48+
timeIntervals={15}
49+
/>,
50+
);
51+
await waitFor(() => {
52+
expect(mockObserve).toHaveBeenCalledTimes(1);
53+
54+
const resizeObserverCallback = getResizeObserverCallback();
55+
const mockObserveElement = mockObserve.mock.calls[0][0];
56+
expect(typeof resizeObserverCallback).toBe("function");
57+
58+
if (resizeObserverCallback) {
59+
resizeObserverCallback([], mockObserveElement);
60+
}
61+
});
62+
});
63+
64+
it("calls disconnect on unmount", async () => {
65+
const component = render(
66+
<DatePicker
67+
inline
68+
selected={new Date()}
69+
showTimeSelect
70+
timeIntervals={15}
71+
/>,
72+
);
73+
74+
component.unmount();
75+
await waitFor(() => {
76+
expect(mockDisconnect).toHaveBeenCalledTimes(1);
77+
const resizeObserverCallback = getResizeObserverCallback();
78+
expect(resizeObserverCallback).toBe(null);
79+
});
80+
});
81+
});
82+
2683
it("should update on input time change", () => {
2784
renderDatePicker("February 28, 2018 4:43 PM");
2885
expect(getInputString()).toBe("February 28, 2018 4:43 PM");

src/time.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,19 @@ export default class Time extends Component<TimeProps, TimeState> {
6262
);
6363
};
6464

65+
private resizeObserver?: ResizeObserver;
6566
state: TimeState = {
6667
height: null,
6768
};
6869

6970
componentDidMount(): void {
7071
// code to ensure selected time will always be in focus within time window when it first appears
7172
this.scrollToTheSelectedTime();
72-
if (this.props.monthRef && this.header) {
73-
this.setState({
74-
height: this.props.monthRef.clientHeight - this.header.clientHeight,
75-
});
76-
}
73+
this.observeDatePickerHeightChanges();
74+
}
75+
76+
componentWillUnmount(): void {
77+
this.resizeObserver?.disconnect();
7778
}
7879

7980
private header?: HTMLDivElement;
@@ -82,6 +83,27 @@ export default class Time extends Component<TimeProps, TimeState> {
8283

8384
private centerLi?: HTMLLIElement;
8485

86+
private observeDatePickerHeightChanges(): void {
87+
const { monthRef } = this.props;
88+
this.updateContainerHeight();
89+
90+
if (monthRef) {
91+
this.resizeObserver = new ResizeObserver(() => {
92+
this.updateContainerHeight();
93+
});
94+
95+
this.resizeObserver.observe(monthRef);
96+
}
97+
}
98+
99+
private updateContainerHeight(): void {
100+
if (this.props.monthRef && this.header) {
101+
this.setState({
102+
height: this.props.monthRef.clientHeight - this.header.clientHeight,
103+
});
104+
}
105+
}
106+
85107
scrollToTheSelectedTime = (): void => {
86108
requestAnimationFrame((): void => {
87109
if (!this.list) return;

0 commit comments

Comments
 (0)