Skip to content

Commit 0532a24

Browse files
authored
[LG-5497] feat(time-input): enhance context providers (#3178)
* feat(time-input): add Time Input component with styles, WIP * wip * feat(time-input): add constants and TimeInput component structure * remove code-editor * refactor(time-input): simplify TimeInput component * chore(time-input): add lodash as a dependency * chore(pnpm-lock): update deps * feat(time-input): integrate TimeInputProvider for controlled value management * feat(date-utils, time-input): add MIN_DATE and MAX_DATE constants; extend BaseTimeInputProps with locale, timeZone, min, max, and baseFontSize * feat(time-input): enhance TimeInputDisplayContext with state management and default values * refactor(time-input): move context to Context dir and update types * refactor(time-input): move context to Context dir and update types * fix(time-input): resolve merge conflict in TimeInputInputs types. forgot to save * docs(time-input): add JSDoc comments to TimeInputContext and TimeInputDisplayContext for better clarity * feat(time-input): enhance context providers with additional exports and comprehensive tests for state management * fix(time-input): adjust context provider types for better handling of undefined values * refactor(date-picker): migrate MIN_DATE and MAX_DATE constants to date-utils and update imports in context and stories * refactor(time-input): remove spec from excludes * fix(time-input): update context provider types to allow optional children prop * feat(time-input): integrate useControlled hook for value management in TimeInput component * chore(date-picker): add date-picker changeset * refactor(date-picker): consolidate date-utils imports in SharedDatePickerContext tests * lint * refactor(time-input): remove dark mode and theme from TimeInputDisplayContext
1 parent 7644125 commit 0532a24

30 files changed

+899
-190
lines changed

.changeset/public-parts-fix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@leafygreen-ui/date-picker': minor
3+
---
4+
5+
Move `MAX_DATE` and `MIN_DATE` from `date-picker` to `@leafygreen-ui/date-utils`.

.changeset/young-worms-stick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@leafygreen-ui/date-utils': minor
3+
---
4+
5+
Add `MAX_DATE` and `MIN_DATE` from `@leafygreen-ui/date-picker`.

packages/date-picker/src/DatePicker.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { StoryFn } from '@storybook/react';
55
import Button from '@leafygreen-ui/button';
66
import {
77
type DateType,
8+
MAX_DATE,
9+
MIN_DATE,
810
Month,
911
newUTC,
1012
SupportedLocales,
@@ -18,7 +20,6 @@ import LeafyGreenProvider from '@leafygreen-ui/leafygreen-provider';
1820
import Modal from '@leafygreen-ui/modal';
1921
import { Size } from '@leafygreen-ui/tokens';
2022

21-
import { MAX_DATE, MIN_DATE } from './shared/constants';
2223
import {
2324
SharedDatePickerContextProps,
2425
SharedDatePickerProvider,

packages/date-picker/src/shared/constants.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
1-
import { Month } from '@leafygreen-ui/date-utils';
1+
import { MAX_DATE, MIN_DATE } from '@leafygreen-ui/date-utils';
22
import { RenderMode } from '@leafygreen-ui/popover';
33
import { DropdownWidthBasis } from '@leafygreen-ui/select';
44

5-
/**
6-
* The default earliest selectable date
7-
* (Unix epoch start: https://en.wikipedia.org/wiki/Unix_time)
8-
* */
9-
export const MIN_DATE = new Date(Date.UTC(1970, Month.January, 1));
10-
11-
/**
12-
* The default latest selectable date
13-
* (Unix 32-bit rollover date: https://en.wikipedia.org/wiki/Year_2038_problem)
14-
*/
15-
export const MAX_DATE = new Date(Date.UTC(2038, Month.January, 19));
16-
175
// TODO: Update how defaultMin & defaultMax are defined,
186
// since day/month are constants,
197
// but year is consumer-defined

packages/date-picker/src/shared/context/SharedDatePickerContext.spec.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import React from 'react';
22
import { act, waitFor } from '@testing-library/react';
33

4-
import { Month, newUTC } from '@leafygreen-ui/date-utils';
4+
import { MAX_DATE, MIN_DATE, Month, newUTC } from '@leafygreen-ui/date-utils';
55
import { consoleOnce } from '@leafygreen-ui/lib';
66
import { renderHook } from '@leafygreen-ui/testing-lib';
77

8-
import { MAX_DATE, MIN_DATE } from '../constants';
9-
108
import {
119
SharedDatePickerContextProps,
1210
SharedDatePickerProvider,

packages/date-picker/src/shared/context/SharedDatePickerContext.utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import {
66
DateType,
77
getISODate,
88
isValidDate,
9+
MAX_DATE,
10+
MIN_DATE,
911
SupportedLocales,
1012
toDate,
1113
} from '@leafygreen-ui/date-utils';
1214
import { consoleOnce } from '@leafygreen-ui/lib';
1315
import { BaseFontSize, Size } from '@leafygreen-ui/tokens';
1416

15-
import { MAX_DATE, MIN_DATE } from '../constants';
1617
import { AutoComplete, BaseDatePickerProps, DatePickerState } from '../types';
1718
import { ModifiedPopoverProps } from '../types/BaseDatePickerProps.types';
1819
import { getFormatParts } from '../utils';

packages/date-utils/src/constants.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,15 @@ export enum Month {
1616
November,
1717
December,
1818
}
19+
20+
/**
21+
* The default earliest selectable date
22+
* (Unix epoch start: https://en.wikipedia.org/wiki/Unix_time)
23+
* */
24+
export const MIN_DATE = new Date(Date.UTC(1970, Month.January, 1));
25+
26+
/**
27+
* The default latest selectable date
28+
* (Unix 32-bit rollover date: https://en.wikipedia.org/wiki/Year_2038_problem)
29+
*/
30+
export const MAX_DATE = new Date(Date.UTC(2038, Month.January, 19));

packages/time-input/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"access": "public"
3030
},
3131
"dependencies": {
32+
"@leafygreen-ui/a11y": "workspace:^",
3233
"@leafygreen-ui/date-utils": "workspace:^",
3334
"@leafygreen-ui/emotion": "workspace:^",
3435
"@leafygreen-ui/form-field": "workspace:^",
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React from 'react';
2+
import { act } from '@testing-library/react';
3+
4+
import { renderHook } from '@leafygreen-ui/testing-lib';
5+
6+
import {
7+
TimeInputProvider,
8+
TimeInputProviderProps,
9+
useTimeInputContext,
10+
} from '.';
11+
12+
const renderTimeInputProvider = (props?: Partial<TimeInputProviderProps>) => {
13+
const defaultProps: TimeInputProviderProps = {
14+
value: undefined,
15+
setValue: jest.fn(),
16+
handleValidation: jest.fn(),
17+
};
18+
19+
const { result, rerender } = renderHook(() => useTimeInputContext(), {
20+
wrapper: ({ children }: { children?: React.ReactNode }) => (
21+
<TimeInputProvider {...defaultProps} {...props}>
22+
{children}
23+
</TimeInputProvider>
24+
),
25+
});
26+
27+
return { result, rerender };
28+
};
29+
30+
describe('packages/time-input-context', () => {
31+
describe('useTimeInputContext', () => {
32+
describe('value', () => {
33+
test('returns undefined values by default', () => {
34+
const { result } = renderTimeInputProvider({});
35+
36+
expect(result.current.value).toBeUndefined();
37+
});
38+
39+
test('provides the correct value when passed', () => {
40+
const testDate = new Date('2023-12-25T10:30:00');
41+
const mockSetValue = jest.fn();
42+
43+
const { result } = renderTimeInputProvider({
44+
value: testDate,
45+
setValue: mockSetValue,
46+
});
47+
48+
expect(result.current.value).toBe(testDate);
49+
});
50+
51+
test('provides null value correctly', () => {
52+
const mockSetValue = jest.fn();
53+
54+
const { result } = renderTimeInputProvider({
55+
value: null,
56+
setValue: mockSetValue,
57+
});
58+
59+
expect(result.current.value).toBeNull();
60+
});
61+
});
62+
63+
describe('setValue', () => {
64+
test('calls the provided setValue function with the correct value', () => {
65+
const mockSetValue = jest.fn();
66+
const testDate = new Date('2023-12-25T10:30:00');
67+
68+
const { result } = renderTimeInputProvider({
69+
setValue: mockSetValue,
70+
});
71+
72+
act(() => {
73+
result.current.setValue(testDate);
74+
});
75+
76+
expect(mockSetValue).toHaveBeenCalledWith(testDate);
77+
});
78+
79+
test('handles invalid date objects', () => {
80+
const mockSetValue = jest.fn();
81+
const invalidDate = new Date('invalid');
82+
83+
const { result } = renderTimeInputProvider({
84+
setValue: mockSetValue,
85+
});
86+
87+
act(() => {
88+
result.current.setValue(invalidDate);
89+
});
90+
91+
expect(mockSetValue).toHaveBeenCalledWith(invalidDate);
92+
});
93+
94+
test('calls setValue with null when undefined is passed', () => {
95+
const mockSetValue = jest.fn();
96+
97+
const { result } = renderTimeInputProvider({
98+
setValue: mockSetValue,
99+
});
100+
101+
act(() => {
102+
result.current.setValue(undefined);
103+
});
104+
105+
expect(mockSetValue).toHaveBeenCalledWith(null);
106+
});
107+
108+
test('handles null value correctly', () => {
109+
const mockSetValue = jest.fn();
110+
111+
const { result } = renderTimeInputProvider({
112+
setValue: mockSetValue,
113+
});
114+
115+
act(() => {
116+
result.current.setValue(null);
117+
});
118+
119+
expect(mockSetValue).toHaveBeenCalledWith(null);
120+
});
121+
});
122+
123+
describe('handleValidation', () => {
124+
test('calls the provided handleValidation function when provided', () => {
125+
const mockHandleValidation = jest.fn();
126+
const testDate = new Date('2023-12-25T10:30:00');
127+
128+
const { result } = renderTimeInputProvider({
129+
handleValidation: mockHandleValidation,
130+
});
131+
132+
act(() => {
133+
result.current.handleValidation(testDate);
134+
});
135+
136+
expect(mockHandleValidation).toHaveBeenCalledWith(testDate);
137+
});
138+
139+
test('handles undefined value correctly', () => {
140+
const mockHandleValidation = jest.fn();
141+
142+
const { result } = renderTimeInputProvider({
143+
handleValidation: mockHandleValidation,
144+
});
145+
146+
act(() => {
147+
result.current.handleValidation(undefined);
148+
});
149+
150+
expect(mockHandleValidation).toHaveBeenCalledWith(undefined);
151+
});
152+
});
153+
});
154+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React, { createContext, PropsWithChildren, useContext } from 'react';
2+
3+
import { DateType } from '@leafygreen-ui/date-utils';
4+
5+
import {
6+
TimeInputContextProps,
7+
TimeInputProviderProps,
8+
} from './TimeInputContext.types';
9+
10+
export const TimeInputContext = createContext<TimeInputContextProps>(
11+
{} as TimeInputContextProps,
12+
);
13+
14+
/**
15+
* This provider is used for the state context of the TimeInput component
16+
*/
17+
export const TimeInputProvider = ({
18+
children,
19+
value,
20+
setValue: _setValue,
21+
handleValidation: _handleValidation,
22+
}: PropsWithChildren<TimeInputProviderProps>) => {
23+
const setValue = (newVal?: DateType) => {
24+
_setValue(newVal ?? null);
25+
};
26+
27+
const handleValidation = (val?: DateType) => {
28+
_handleValidation?.(val);
29+
};
30+
31+
return (
32+
<TimeInputContext.Provider
33+
value={{
34+
value,
35+
setValue,
36+
handleValidation,
37+
}}
38+
>
39+
{children}
40+
</TimeInputContext.Provider>
41+
);
42+
};
43+
44+
export const useTimeInputContext = () => {
45+
return useContext(TimeInputContext);
46+
};

0 commit comments

Comments
 (0)