-
Notifications
You must be signed in to change notification settings - Fork 72
[LG-5538] feat(time-input): Parse time #3378
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?
Changes from 9 commits
bf86e07
612b474
a578ecb
804cd88
206e145
9715407
dad7b62
f559593
3e6f6be
1afe57e
cb84996
2547fdc
94ed98f
2d2fbd5
aa4e7a2
d38ee94
d57c109
aff3376
9e343ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,10 @@ import React, { | |
| useState, | ||
| } from 'react'; | ||
| import defaults from 'lodash/defaults'; | ||
| import defaultTo from 'lodash/defaultTo'; | ||
|
|
||
| import { hasDayPeriod } from '../../utils'; | ||
| import { getFormatParts } from '../../utils/getFormatParts/getFormatParts'; | ||
|
|
||
| import { | ||
| TimeInputDisplayContextProps, | ||
|
|
@@ -39,6 +43,19 @@ export const TimeInputDisplayProvider = ({ | |
|
|
||
| // TODO: min, max helpers | ||
|
|
||
| // Determines if the input should show a select for the day period (AM/PM) | ||
| const shouldShowSelect = !!hasDayPeriod(providerValue.locale); | ||
|
|
||
| // Only used to track the presentation format of the segments, not the value itself | ||
| const formatParts = getFormatParts({ | ||
| showSeconds: providerValue.showSeconds, | ||
| }); | ||
|
|
||
| const timeZone = defaultTo( | ||
| providerValue.timeZone, | ||
| Intl.DateTimeFormat().resolvedOptions().timeZone, | ||
| ); | ||
|
||
|
|
||
| return ( | ||
| <TimeInputDisplayContext.Provider | ||
| value={{ | ||
|
|
@@ -48,6 +65,9 @@ export const TimeInputDisplayProvider = ({ | |
| ariaLabelledbyProp, | ||
| isDirty, | ||
| setIsDirty, | ||
| shouldShowSelect, | ||
| formatParts, | ||
| timeZone, | ||
| }} | ||
| > | ||
| {children} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,57 @@ | ||
| 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', | ||
| // value: new Date('1990-02-20T12:30:00Z'), | ||
shaneeza marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }, | ||
| 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'), | ||
| ); | ||
| // const [value, setValue] = useState<DateType | undefined>(); | ||
| // const [value, setValue] = useState<DateType | undefined>( | ||
| // new Date('1990--20T14:30:50Z'), | ||
| // ); | ||
|
|
||
shaneeza marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return ( | ||
| <TimeInput {...props} value={value} onTimeChange={time => setValue(time)} /> | ||
| ); | ||
| }; | ||
|
|
||
| export const LiveExample = Template.bind({}); | ||
| 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 defaultTimeParts: TimeParts = { | ||||||
|
||||||
| export const defaultTimeParts: TimeParts = { | |
| export const defaultDateTimeParts: TimeParts = { |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||
| export const TimePartKeys = { | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| 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,17 @@ | ||
| import { getFilteredTimeParts } from './getFilteredTimeParts'; | ||
|
|
||
| describe('packages/time-input/utils/getFilteredTimeParts', () => { | ||
|
||
| test('returns the filtered time parts', () => { | ||
| const filteredTimeParts = getFilteredTimeParts({ | ||
| timeParts: [ | ||
| { type: 'hour', value: '12' }, | ||
| { type: 'literal', value: ':' }, | ||
| { type: 'minute', value: '30' }, | ||
| ], | ||
| }); | ||
| expect(filteredTimeParts).toEqual([ | ||
| { type: 'hour', value: '12' }, | ||
| { type: 'minute', value: '30' }, | ||
| ]); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /** | ||
| * Returns the time parts that are not literals (e.g. ':'). | ||
| * @param timeParts - The time parts to get the filtered time parts for | ||
| * @returns The filtered time parts | ||
| * | ||
| * @example | ||
| * ```js | ||
| * getFilteredTimeParts([ | ||
| * { type: 'hour', value: '12' }, | ||
| * { type: 'literal', value: ':' }, | ||
| * { type: 'minute', value: '30' }, | ||
| * ]); | ||
| * // returns: [{ type: 'hour', value: '12' }, { type: 'minute', value: '30' }] | ||
| * ``` | ||
| */ | ||
| export const getFilteredTimeParts = ({ | ||
|
||
| timeParts, | ||
| }: { | ||
| timeParts?: Array<Intl.DateTimeFormatPart>; | ||
| }) => { | ||
| const filteredTimeParts = | ||
| timeParts?.filter(part => part.type !== 'literal') ?? []; | ||
|
|
||
| return filteredTimeParts; | ||
| }; | ||
| 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: '' }, | ||
| ]); | ||
| }); | ||
| }); |
| 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; | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.