Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bf86e07
feat(time-input): add utils for toggling select and formatparts
shaneeza Nov 26, 2025
612b474
wip
shaneeza Nov 30, 2025
a578ecb
refactor(time-input): enhance time formatting utilities and improve d…
shaneeza Dec 1, 2025
804cd88
refactor(time-input): improve time input handling and formatting logic
shaneeza Dec 1, 2025
206e145
temp remove ts check
shaneeza Dec 1, 2025
9715407
refactor(time-input): simplify getFormatParts function by removing lo…
shaneeza Dec 2, 2025
dad7b62
test(time-input): add unit tests for getFormatParts utility to verify…
shaneeza Dec 2, 2025
f559593
refactor(time-input): remove locale dependency from formatParts in Ti…
shaneeza Dec 2, 2025
3e6f6be
feat(time-input): add default time parts and enhance time input conte…
shaneeza Dec 3, 2025
1afe57e
refactor(time-input): rename shouldShowSelect to is12hFormat for clar…
shaneeza Dec 12, 2025
cb84996
refactor(time-input): clean up unused state and comments in TimeInput…
shaneeza Dec 12, 2025
2547fdc
refactor(time-input): remove unused console logs and simplify time fo…
shaneeza Dec 12, 2025
94ed98f
refactor(time-input): remove outdated TODO comment in TimeInputContex…
shaneeza Dec 12, 2025
2d2fbd5
Merge branch 'shaneeza/segment-logic-integration' of github.com:mongo…
shaneeza Dec 12, 2025
aa4e7a2
feat(time-input): implement getNonLiteralTimeParts utility function w…
shaneeza Dec 16, 2025
d38ee94
refactor(time-input): rename defaultTimeParts to defaultDateTimeParts…
shaneeza Dec 16, 2025
d57c109
refactor(time-input): simplify import structure for getFormatPartsVal…
shaneeza Dec 16, 2025
aff3376
refactor(time-input): enhance getFormatter utility to conditionally i…
shaneeza Dec 16, 2025
9e343ff
refactor(time-input): consolidate imports for utility functions in Ti…
shaneeza Dec 16, 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 @@ -6,6 +6,8 @@ import React, {
} from 'react';
import defaults from 'lodash/defaults';

import { getFormatParts,hasDayPeriod } from '../../utils';

import {
TimeInputDisplayContextProps,
TimeInputDisplayProviderProps,
Expand Down Expand Up @@ -39,6 +41,14 @@ export const TimeInputDisplayProvider = ({

// TODO: min, max helpers

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

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

return (
<TimeInputDisplayContext.Provider
value={{
Expand All @@ -48,6 +58,8 @@ export const TimeInputDisplayProvider = ({
ariaLabelledbyProp,
isDirty,
setIsDirty,
is12hFormat,
formatParts,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ export type TimeInputDisplayContextProps = Omit<
* Setter for whether the input has been interacted with
*/
setIsDirty: React.Dispatch<React.SetStateAction<boolean>>;

/**
* Whether the AM/PM select should be shown
*/
is12hFormat: boolean;

/**
* An array of {@link Intl.DateTimeFormatPart},
* used to determine the order of segments in the input
*/
formatParts?: Array<Intl.DateTimeFormatPart>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const displayContextPropNames: Array<DisplayContextPropKeys> = [
'size',
'errorMessage',
'state',
'showSeconds',
];

/**
Expand All @@ -51,4 +52,6 @@ export const defaultTimeInputDisplayContext: TimeInputDisplayContextProps = {
errorMessage: '',
isDirty: false,
setIsDirty: () => {},
is12hFormat: false,
showSeconds: true,
};
37 changes: 35 additions & 2 deletions packages/time-input/src/TimeInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
import React from 'react';
import React, { useState } from 'react';
import { type StoryMetaType } from '@lg-tools/storybook-utils';
import { StoryFn } from '@storybook/react';

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

import { TimeInput } from '.';

const meta: StoryMetaType<typeof TimeInput> = {
title: 'Components/Inputs/TimeInput',
component: TimeInput,
parameters: {
default: 'LiveExample',
controls: {
exclude: [
'handleValidation',
'initialValue',
'onChange',
'onDateChange',
'onSegmentChange',
'value',
'onTimeChange',
],
},
},
args: {
showSeconds: true,
locale: SupportedLocales.ISO_8601,
timeZone: 'UTC',
},
argTypes: {
locale: { control: 'select', options: Object.values(SupportedLocales) },
timeZone: {
control: 'select',
options: [undefined, 'UTC', 'America/New_York', 'Europe/London'],
},
},
};

export default meta;

const Template: StoryFn<typeof TimeInput> = props => <TimeInput {...props} />;
const Template: StoryFn<typeof TimeInput> = props => {
const [value, setValue] = useState<DateType | undefined>(
new Date('1990-02-20T14:30:50Z'),
);

return (
<TimeInput {...props} value={value} onTimeChange={time => setValue(time)} />
);
};

export const LiveExample = Template.bind({});
7 changes: 7 additions & 0 deletions packages/time-input/src/TimeInput/TimeInput.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export type DisplayTimeInputProps = {
* A message to show in red underneath the input when state is `Error`
*/
errorMessage?: string;

/**
* Whether to show seconds in the input.
*
* @default true
*/
showSeconds?: boolean;
} & DarkModeProps &
AriaLabelPropsWithLabel;

Expand Down
26 changes: 20 additions & 6 deletions packages/time-input/src/TimeInputInputs/TimeInputInputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import { FormField, FormFieldInputContainer } from '@leafygreen-ui/form-field';

import { unitOptions } from '../constants';
import { useTimeInputContext } from '../Context/TimeInputContext/TimeInputContext';
import { useTimeInputDisplayContext } from '../Context/TimeInputDisplayContext/TimeInputDisplayContext';
import { TimeInputSelect } from '../TimeInputSelect/TimeInputSelect';
import { UnitOption } from '../TimeInputSelect/TimeInputSelect.types';
import { getFormatPartsValues } from '../utils';

import { wrapperBaseStyles } from './TimeInputInputs.styles';
import { TimeInputInputsProps } from './TimeInputInputs.types';
Expand All @@ -15,25 +18,36 @@
*/
export const TimeInputInputs = forwardRef<HTMLDivElement, TimeInputInputsProps>(
(_props: TimeInputInputsProps, forwardedRef) => {
const { is12hFormat, timeZone, locale } = useTimeInputDisplayContext();
const [selectUnit, setSelectUnit] = useState<UnitOption>(unitOptions[0]);

const { value } = useTimeInputContext();

const handleSelectChange = (unit: UnitOption) => {
setSelectUnit(unit);
};

const timeParts = getFormatPartsValues({

Check warning on line 30 in packages/time-input/src/TimeInputInputs/TimeInputInputs.tsx

View workflow job for this annotation

GitHub Actions / Check lints

'timeParts' is assigned a value but never used. Allowed unused vars must match /^_/u
locale: locale,
timeZone: timeZone,
value: value,
});

// TODO: break this out more
return (
<FormField aria-labelledby="temp" label="Time Input" ref={forwardedRef}>
<div className={cx(wrapperBaseStyles)}>
<FormFieldInputContainer>
<div>TODO: Input segments go here</div>
</FormFieldInputContainer>
<TimeInputSelect
unit={selectUnit.displayName}
onChange={unit => {
handleSelectChange(unit);
}}
/>
{is12hFormat && (
<TimeInputSelect
unit={selectUnit.displayName}
onChange={unit => {
handleSelectChange(unit);
}}
/>
)}
</div>
</FormField>
);
Expand Down
12 changes: 12 additions & 0 deletions packages/time-input/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { TimeParts } from './shared.types';

export const unitOptions = [
{ displayName: 'AM', value: 'AM' },
{ displayName: 'PM', value: 'PM' },
];

export const defaultDateTimeParts: TimeParts = {
hour: '',
minute: '',
second: '',
month: '',
day: '',
year: '',
dayPeriod: 'AM',
};
13 changes: 13 additions & 0 deletions packages/time-input/src/shared.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const TimePartKeys = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
export const TimePartKeys = {
export const DateTimePartKeys = {

hour: 'hour',
minute: 'minute',
second: 'second',
month: 'month',
day: 'day',
year: 'year',
dayPeriod: 'dayPeriod',
} as const;

export type TimePartKeys = (typeof TimePartKeys)[keyof typeof TimePartKeys];

export type TimeParts = Record<TimePartKeys, string>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getFormatParts } from './getFormatParts';

describe('packages/time-input/utils/getFormatParts', () => {
test('returns the correct format parts without seconds', () => {
const formatParts = getFormatParts({});
expect(formatParts).toEqual([
{ type: 'hour', value: '' },
{ type: 'literal', value: ':' },
{ type: 'minute', value: '' },
]);
});

test('returns the correct format parts with seconds', () => {
const formatParts = getFormatParts({ showSeconds: true });
expect(formatParts).toEqual([
{ type: 'hour', value: '' },
{ type: 'literal', value: ':' },
{ type: 'minute', value: '' },
{ type: 'literal', value: ':' },
{ type: 'second', value: '' },
]);
});
});
43 changes: 43 additions & 0 deletions packages/time-input/src/utils/getFormatParts/getFormatParts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Returns an array of {@link Intl.DateTimeFormatPart} for the provided locale.
*
* Filters out the dayPeriod and the empty literal before it
* since they are not part of the time format parts.
*
* This will return `:` for every literal part regardless of the locale.
*
* @param showSeconds - Whether to show seconds
* @returns The format parts
*
* @example
*
* ```js
* getFormatParts({ showSeconds: true });
*
* // [
* // { type: 'hour', value: '' },
* // { type: 'literal', value: ':' },
* // { type: 'minute', value: '' },
* // { type: 'literal', value: ':' },
* // { type: 'second', value: '' },
* // ]
*/
export const getFormatParts = ({
showSeconds = false,
}: {
showSeconds?: boolean;
}): Array<Intl.DateTimeFormatPart> | undefined => {
const formatParts: Array<Intl.DateTimeFormatPart> = [
{ type: 'hour', value: '' },
{ type: 'literal', value: ':' },
{ type: 'minute', value: '' },
...(showSeconds
? ([
{ type: 'literal', value: ':' },
{ type: 'second', value: '' },
] as Array<Intl.DateTimeFormatPart>)
: []),
];

return formatParts;
};
Loading
Loading