Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1db42af
feat(time-input): add TimeFormField and TimeFormFieldInputContainer c…
shaneeza Dec 1, 2025
bb3916d
refactor(time-input): clean up unused select unit logic and improve s…
shaneeza Dec 1, 2025
df69d22
feat(time-input): enhance TimeInput component with new constants for …
shaneeza Dec 2, 2025
2f5707f
refactor(time-input): update time segment rules and default values to…
shaneeza Dec 2, 2025
1ef4c97
merge conflits
shaneeza Dec 3, 2025
87023fb
feat(time-input): enhance TimeInput stories with new argTypes for dar…
shaneeza Dec 3, 2025
580c90f
test(time-input): add comprehensive unit tests for TimeInputSegment c…
shaneeza Dec 3, 2025
2495aa1
test(time-input): add comprehensive unit tests for TimeInputSegment c…
shaneeza Dec 3, 2025
4cad730
refactor(time-input): update time segment handling to use explicit is…
shaneeza Dec 3, 2025
6e265a7
chore(pnpm-lock): add workspace links for @leafygreen-ui/input-box to…
shaneeza Dec 12, 2025
10d4039
merge conflict
shaneeza Dec 12, 2025
e46a73c
refactor(time-input): clean up comments and improve test descriptions…
shaneeza Dec 12, 2025
7e42f34
merge conflict
shaneeza Dec 12, 2025
d9b99ec
Merge branch 'LG-5538/segments-parse-time' of github.com:mongodb/leaf…
shaneeza Dec 12, 2025
f07a1b8
merge conflict
shaneeza Dec 12, 2025
ed87ee7
test(time-input): add unit tests for TimeInputBox component
shaneeza Dec 14, 2025
e18c444
feat(time-input): integrate LGIDs into TimeInput components for impro…
shaneeza Dec 14, 2025
a361ba7
test(time-input): add test to verify seconds input is not rendered wh…
shaneeza Dec 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@ export const useDateSegments = (
(isNull(date) || isUndefined(date)) && isValidDate(prevDate);

if (hasDateValueChanged || hasDateBeenCleared) {
// This returns a new state object with the updated segments from the new date
const newSegments = getFormattedSegmentsFromDate(date);
// Pass the new state and a copy of the previous state to the callback
onUpdate?.(newSegments, { ...segments });
// This updates all segments in the internal state of the hook
// This internally invokes `dateSegmentsReducer` and passes `updateObject` as the second argument. `segments` is the first argument. This updates the internal state of the hook.
dispatch(newSegments);
}
}, [date, onUpdate, prevDate, segments]);
Expand All @@ -69,8 +73,11 @@ export const useDateSegments = (
// finally, commit the new state

const updateObject = { [segment]: value };
// This returns a new state object with the updated segment
const nextState = dateSegmentsReducer(segments, updateObject);
// Pass the new state and a copy of the previous state to the callback
onUpdate?.(nextState, { ...segments }, segment);
// This internally invokes `dateSegmentsReducer` and passes `updateObject` as the second argument. `segments` is the first argument. This updates the internal state of the hook.
dispatch(updateObject);
};

Expand Down
1 change: 1 addition & 0 deletions packages/time-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@leafygreen-ui/emotion": "workspace:^",
"@leafygreen-ui/form-field": "workspace:^",
"@leafygreen-ui/hooks": "workspace:^",
"@leafygreen-ui/input-box": "workspace:^",
"@leafygreen-ui/lib": "workspace:^",
"@leafygreen-ui/palette": "workspace:^",
"@leafygreen-ui/select": "workspace:^",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,28 @@ export const TimeInputDisplayProvider = ({
...defaults(rest, defaultTimeInputDisplayContext),
};

// TODO: min, max helpers

// Determines if the input should show a select for the day period (AM/PM)
const is12hFormat = !!hasDayPeriod(providerValue.locale);
/**
* Determines if the input should show a select for the day period (AM/PM)
*/
const is12HourFormat = !!hasDayPeriod(providerValue.locale);

// Only used to track the presentation format of the segments, not the value itself
/**
* Only used to track the presentation format of the segments, not the value itself
*/
const formatParts = getFormatParts({
showSeconds: providerValue.showSeconds,
});

/**
* Gets the time zone from the provider value or the browser's default
*/
const timeZone = defaultTo(
providerValue.timeZone,
Intl.DateTimeFormat().resolvedOptions().timeZone,
);

// TODO: min, max helpers will be in a future PR
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Would be helpful to create a backlog ticket for this and reference that in this comment


return (
<TimeInputDisplayContext.Provider
value={{
Expand All @@ -65,7 +72,7 @@ export const TimeInputDisplayProvider = ({
ariaLabelledbyProp,
isDirty,
setIsDirty,
is12hFormat,
is12HourFormat,
formatParts,
timeZone,
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ export type TimeInputDisplayContextProps = Omit<
setIsDirty: React.Dispatch<React.SetStateAction<boolean>>;

/**
* Whether the AM/PM select should be shown
* Whether the time input is in 12-hour format. Helps determine if the AM/PM select should be shown.
*
* @default false
*/
is12hFormat: boolean;
is12HourFormat: boolean;

/**
* An array of {@link Intl.DateTimeFormatPart},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ export const defaultTimeInputDisplayContext: TimeInputDisplayContextProps = {
errorMessage: '',
isDirty: false,
setIsDirty: () => {},
is12hFormat: false,
is12HourFormat: false,
showSeconds: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ export {
type TimeInputDisplayContextProps,
type TimeInputDisplayProviderProps,
} from './TimeInputDisplayContext.types';
export { defaultTimeInputDisplayContext } from './TimePickerDisplayContext.utils';
export { defaultTimeInputDisplayContext } from './TimePickerDisplayContext.utils'; //
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';

import { FormField } from '@leafygreen-ui/form-field';

import { useTimeInputDisplayContext } from '../../Context/TimeInputDisplayContext';

import { TimeFormFieldProps } from './TimeFormField.types';

/**
* A wrapper around `FormField` that sets the relevant
* attributes, and styling
*/
export const TimeFormField = React.forwardRef<
HTMLDivElement,
TimeFormFieldProps
>(({ children, ...rest }: TimeFormFieldProps, fwdRef) => {
const {
label,
description,
// stateNotification: { state, message: errorMessage },
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need to remove this?

disabled,
size,
} = useTimeInputDisplayContext();

return (
<FormField
label={label}
description={description}
disabled={disabled}
// state={state}
// errorMessage={errorMessage}
size={size}
ref={fwdRef}
{...rest}
>
{children}
</FormField>
);
});

TimeFormField.displayName = 'TimeFormField';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { FormFieldProps } from '@leafygreen-ui/form-field';

export type TimeFormFieldProps = React.ComponentPropsWithoutRef<'div'> & {
children: FormFieldProps['children'];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { css, cx } from '@leafygreen-ui/emotion';

const selectStyles = css`
border-top-right-radius: 0;
border-bottom-right-radius: 0;
`;

const baseStyles = css`
&:hover,
&:focus-within {
z-index: 1;
}
`;

export const getContainerStyles = ({
is12HourFormat,
}: {
is12HourFormat: boolean;
}) =>
cx(baseStyles, {
[selectStyles]: is12HourFormat,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';

import { FormFieldInputContainer } from '@leafygreen-ui/form-field';

import { useTimeInputDisplayContext } from '../../Context/TimeInputDisplayContext';

import { getContainerStyles } from './TimeFormFieldInputContainer.styles';
import { TimeFormFieldInputContainerProps } from './TimeFormFieldInputContainer.types';

/**
* A wrapper around `FormField` that sets the relevant
* attributes, and styling
*/
export const TimeFormFieldInputContainer = React.forwardRef<
HTMLDivElement,
TimeFormFieldInputContainerProps
>(({ children, onInputClick }: TimeFormFieldInputContainerProps, fwdRef) => {
const { label, ariaLabelProp, ariaLabelledbyProp, is12HourFormat } =
useTimeInputDisplayContext();

return (
<FormFieldInputContainer
ref={fwdRef}
role="combobox"
tabIndex={-1}
aria-label={!label && ariaLabelProp ? ariaLabelProp : undefined}
aria-labelledby={
!label && !ariaLabelProp && ariaLabelledbyProp
? ariaLabelledbyProp
: undefined
}
onClick={onInputClick}
className={getContainerStyles({ is12HourFormat })}
>
{children}
</FormFieldInputContainer>
);
});

TimeFormFieldInputContainer.displayName = 'TimeFormFieldInputContainer';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FormFieldProps } from '@leafygreen-ui/form-field';

export type TimeFormFieldInputContainerProps =
React.ComponentPropsWithoutRef<'div'> & {
children: FormFieldProps['children'];
onInputClick?: React.MouseEventHandler<HTMLDivElement>;
};
2 changes: 2 additions & 0 deletions packages/time-input/src/TimeFormField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { TimeFormField } from './TimeFormField/TimeFormField';
export { TimeFormFieldInputContainer } from './TimeFormFieldInputContainer/TimeFormFieldInputContainer';
13 changes: 12 additions & 1 deletion packages/time-input/src/TimeInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React, { useState } from 'react';
import { type StoryMetaType } from '@lg-tools/storybook-utils';
import {
storybookArgTypes,
type StoryMetaType,
} from '@lg-tools/storybook-utils';
import { StoryFn } from '@storybook/react';

import { DateType, SupportedLocales } from '@leafygreen-ui/date-utils';

import { Size } from './TimeInput/TimeInput.types';
import { TimeInput } from '.';

const meta: StoryMetaType<typeof TimeInput> = {
Expand All @@ -20,20 +24,27 @@ const meta: StoryMetaType<typeof TimeInput> = {
'onSegmentChange',
'value',
'onTimeChange',
'data-lgid',
'data-testid',
],
},
},
args: {
showSeconds: true,
locale: SupportedLocales.ISO_8601,
timeZone: 'UTC',
label: 'Time Input',
darkMode: false,
size: Size.Default,
},
argTypes: {
locale: { control: 'select', options: Object.values(SupportedLocales) },
timeZone: {
control: 'select',
options: [undefined, 'UTC', 'America/New_York', 'Europe/London'],
},
darkMode: storybookArgTypes.darkMode,
size: { control: 'select', options: Object.values(Size) },
},
};

Expand Down
Empty file.
Empty file.
31 changes: 31 additions & 0 deletions packages/time-input/src/TimeInputBox/TimeInputBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';

import { InputBox } from '@leafygreen-ui/input-box';

import { getTimeSegmentRules } from '../constants';
import { useTimeInputDisplayContext } from '../Context/TimeInputDisplayContext/TimeInputDisplayContext';
import { TimeSegment } from '../shared.types';
import { TimeInputSegment } from '../TimeInputSegment/TimeInputSegment';

import { TimeInputBoxProps } from './TimeInputBox.types';

export const TimeInputBox = React.forwardRef<HTMLDivElement, TimeInputBoxProps>(
({ children, ...rest }: TimeInputBoxProps, fwdRef) => {
const { disabled, formatParts, size, is12HourFormat } =
useTimeInputDisplayContext();
return (
<InputBox
ref={fwdRef}
segmentEnum={TimeSegment}
segmentRules={getTimeSegmentRules({ is12HourFormat })}
disabled={disabled}
segmentComponent={TimeInputSegment}
size={size}
formatParts={formatParts}
{...rest}
/>
);
},
);

TimeInputBox.displayName = 'TimeInputBox';
7 changes: 7 additions & 0 deletions packages/time-input/src/TimeInputBox/TimeInputBox.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TimeSegment, TimeSegmentsState } from '../shared.types';

export interface TimeInputBoxProps
extends React.ComponentPropsWithoutRef<'div'> {
segments: TimeSegmentsState;
setSegment: (segment: TimeSegment, value: string) => void;
}
Empty file.
Loading
Loading