-
Notifications
You must be signed in to change notification settings - Fork 72
[LG-5532] feat(time-input) display segment values #3379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: shaneeza/segment-logic-integration
Are you sure you want to change the base?
[LG-5532] feat(time-input) display segment values #3379
Conversation
…omponents with context integration
…tate handling in TimeInputInputs component
…segment rules and default values
… support 12/24 hour formats
…cale dependency and enhancing formatting logic
… format output with and without seconds
…meInputDisplayContext
…kMode and size, and add console log for debugging in TimeInputInputs
…omponent, covering rendering, value updates, and keyboard interactions
…omponent, covering rendering, value updates, and keyboard interactions
…12HourFormat prop, replacing TimeSegments with TimeSegment for improved clarity and consistency
…ity in time input context
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements display functionality for time input segments in the TimeInput component, enabling users to see and interact with individual time parts (hour, minute, second) in a segmented input format.
Key Changes
- Introduced
TimeInputSegmentcomponent with comprehensive test coverage for keyboard navigation and value formatting - Added
InputBoxintegration to render segmented time inputs with proper validation and formatting rules - Refactored time format detection from
is12hFormattois12HourFormatfor clarity and consistency
Reviewed changes
Copilot reviewed 27 out of 32 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
packages/time-input/src/TimeInputSegment/TimeInputSegment.tsx |
New component rendering individual time segments with validation |
packages/time-input/src/TimeInputSegment/TimeInputSegment.spec.tsx |
Comprehensive test suite for segment keyboard interactions |
packages/time-input/src/TimeInputBox/TimeInputBox.tsx |
Container component integrating InputBox with time-specific configuration |
packages/time-input/src/TimeInputInputs/TimeInputInputs.tsx |
Updated to use TimeInputBox and manage segment state |
packages/time-input/src/constants.ts |
Added segment rules, min/max values, and placeholder definitions |
packages/time-input/src/shared.types.ts |
Defined TimeSegment enum and TimeSegmentsState type |
packages/time-input/src/hooks/useSelectUnit/useSelectUnit.ts |
Hook managing AM/PM select unit synchronization with date value |
packages/time-input/src/TimeFormField/* |
New wrapper components for FormField with time-specific styling |
packages/time-input/src/Context/TimeInputDisplayContext/* |
Renamed is12hFormat to is12HourFormat across context |
packages/date-picker/src/shared/hooks/useDateSegments/useDateSegments.ts |
Added clarifying comments for segment update flow |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
packages/time-input/src/TimeInputSegment/TimeInputSegment.spec.tsx
Outdated
Show resolved
Hide resolved
| const onChangeHandler = | ||
| jest.fn<TimeInputSegmentChangeEventHandler>(); | ||
| const { input } = renderSegment({ | ||
| segment: 'minute', |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded segment value 'minute' should use the dynamic segment variable from the test parameter (line 368) to ensure test correctness for both 'minute' and 'second' segments.
| segment: 'minute', | |
| segment, |
packages/time-input/src/Context/TimeInputDisplayContext/index.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is being replaced in the next PR so it doesn't need to be reviewed
… in TimeInputSegment tests
|
Size Change: +2.16 kB (+0.12%) Total Size: 1.83 MB
ℹ️ View Unchanged
|
… and update related logic for clarity
…nclude time parts and improve tests for locale handling
…meInputDisplayContext
…r formatting date and time parts
…lated logic for consistency
…ts to reflect date time terminology
…FormField component
| is12HourFormat: boolean; | ||
| }) => | ||
| cx(baseStyles, { | ||
| [selectStyles]: is12HourFormat, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be worth adding a comment on why we're removing border radius for 12hr format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is a pattern that exists in NumberInput as well, what do you think about building this into FormFieldInputContainer itself?
| return ( | ||
| <FormFieldInputContainer | ||
| ref={fwdRef} | ||
| role="combobox" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Time input isn't a combobox. I's just a text input
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoopsie
| describe('12 hour format', () => { | ||
| it('should have a min of 1 for the hour segment', () => { | ||
| const { hourInput } = renderTimeInputBox({ | ||
| displayProps: { locale: SupportedLocales.en_US }, | ||
| }); | ||
| expect(hourInput).toHaveAttribute('min', '1'); | ||
| }); | ||
| it('should have a max of 12 for the hour segment', () => { | ||
| const { hourInput } = renderTimeInputBox({ | ||
| displayProps: { locale: SupportedLocales.en_US }, | ||
| }); | ||
| expect(hourInput).toHaveAttribute('max', '12'); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are brittle since we're checking the DOM attributes, not the actual behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can remove these since i'm testing the behavior in TimeInputSegment
packages/time-input/src/constants.ts
Outdated
| return { | ||
| [TimeSegment.Hour]: { | ||
| maxChars: 2, | ||
| minExplicitValue: is12HourFormat ? 1 : 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be:
| minExplicitValue: is12HourFormat ? 1 : 2, | |
| minExplicitValue: is12HourFormat ? 2 : 3, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoops, should be. I thought I changed this in this PR, but I did it in the next PR, which adds tests for it.
| test('has 2 options', () => { | ||
| const { getInput, getOptions } = renderTimeInputSelect({ | ||
| unit: 'AM', | ||
| onChange: () => {}, | ||
| }); | ||
|
|
||
| userEvent.click(getInput()); | ||
| expect(getOptions()).toHaveLength(2); | ||
| }); | ||
|
|
||
| test('has AM and PM options', () => { | ||
| const { getInput, getOptionByValue } = renderTimeInputSelect({ | ||
| unit: 'AM', | ||
| onChange: () => {}, | ||
| }); | ||
|
|
||
| userEvent.click(getInput()); | ||
| expect(getOptionByValue('AM')).toBeInTheDocument(); | ||
| expect(getOptionByValue('PM')).toBeInTheDocument(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these tests can be combined
…ean up tests by removing unused code
…db/leafygreen-ui into LG-5538/segments-parse-time
…ygreen-ui into LG-5532/segments-display-values
…orrect time input handling
| /** | ||
| * Determines if the input should show a select for the day period (AM/PM) | ||
| */ | ||
| const is12HourFormat = !!hasDayPeriod(providerValue.locale); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const is12HourFormat = !!hasDayPeriod(providerValue.locale); | |
| const is12HourFormat = hasDayPeriod(providerValue.locale); |
| formatParts?: Array<Intl.DateTimeFormatPart>; | ||
|
|
||
| /** | ||
| * LGIDs for the code snippet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to update comment?
| 'aria-labelledby'?: string; | ||
|
|
||
| /** | ||
| * LGIDs for the code snippet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to update comment?
| /** | ||
| * LGIDs for the code snippet. | ||
| */ | ||
| lgIds: GetLgIdsReturnType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you remind me what this pattern is for? It seems odd to pass these down through props and can't remember why it's necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is so that nested child components have access to the dynamic LGIDs.
| @@ -0,0 +1,5 @@ | |||
| import { FormFieldProps } from '@leafygreen-ui/form-field'; | |||
|
|
|||
| export type TimeFormFieldProps = React.ComponentPropsWithoutRef<'div'> & { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be ComponentPropsWithRef?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a component is wrapped in a forwardRef, you should use ComponentPropsWithoutRef because the ref shouldn't be included with the props. React gives you the ref as the second parameter in the forwardRef callback function.
| meta?: { | ||
| key?: (typeof keyMap)[keyof typeof keyMap]; | ||
| [key: string]: any; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this / what is it used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm moving this to shared.types since will be used in TimeInputBox for the onSegmentChange callback.
| import { TimeSegment, TimeSegmentsState } from '../shared.types'; | ||
|
|
||
| export interface TimeInputBoxProps | ||
| extends React.ComponentPropsWithoutRef<'div'> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ComponentPropsWithRef?
| test.todo( | ||
| 'should call onSegmentChange with the segment name and the value', | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason we can't do this now? imo we should avoid test.todo() because they tend to be left as todos or leave a ticket so it's returned to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is one large PR that I've broken into 5 separate PRs, this has been added in the 5th PR.
|
|
||
| import { TimeInputBoxProps } from './TimeInputBox.types'; | ||
|
|
||
| export const TimeInputBox = React.forwardRef<HTMLDivElement, TimeInputBoxProps>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we axe the empty *.styles.ts file?
|
|
||
| import { TimeInputSegmentProps } from './TimeInputSegment.types'; | ||
|
|
||
| export const TimeInputSegment = React.forwardRef< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we axe the empty *.styles.ts file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I plan on adding some styles to this file in a future PR
…efault context values
|
Coverage after merging LG-5532/segments-display-values into shaneeza/segment-logic-integration will be
Coverage Report for Changed Files
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
✍️ Proposed changes
🎟 Jira ticket: LG-5532
This PR is the second PR in a chain of PRs
This PR implements display functionality for time input segments, enabling users to view individual time parts (hour, minute, second) in a segmented input format. The ability to update segments is not included in this PR.
✅ Checklist
🧪 How to test changes