Skip to content

Commit e423be1

Browse files
committed
Feat: timerpicker can't find second h2oai#2398
Added seconds select in time picker and updated the testing to be in format
1 parent fe7f7bd commit e423be1

File tree

9 files changed

+1792
-186
lines changed

9 files changed

+1792
-186
lines changed

py/h2o_lightwave/h2o_lightwave/types.py

+493-3
Large diffs are not rendered by default.

py/h2o_lightwave/h2o_lightwave/ui.py

+177-3
Large diffs are not rendered by default.

py/h2o_wave/h2o_wave/types.py

+493-3
Large diffs are not rendered by default.

py/h2o_wave/h2o_wave/ui.py

+177-3
Large diffs are not rendered by default.

r/R/ui.R

+261-35
Large diffs are not rendered by default.

tools/intellij-plugin/src/main/resources/templates/wave-components.xml

+102-51
Large diffs are not rendered by default.

tools/vscode-extension/component-snippets.json

+51-51
Large diffs are not rendered by default.

ui/src/time_picker.test.tsx

+28-28
Original file line numberDiff line numberDiff line change
@@ -48,95 +48,95 @@ describe('time_picker.tsx', () => {
4848
})
4949

5050
it('Sets args - init - value specified', async () => {
51-
render(<XTimePicker model={{ ...timepickerProps, value: '10:30' }} />)
51+
render(<XTimePicker model={{ ...timepickerProps, value: '10:30:00' }} />)
5252
await waitForIdleEventLoop()
53-
expect(wave.args[name]).toBe('10:30')
53+
expect(wave.args[name]).toBe('10:30:00')
5454
})
5555

5656
it('Set args when value is updated to different value', async () => {
57-
const { rerender } = render(<XTimePicker model={{ ...timepickerProps, value: '15:00' }} />)
57+
const { rerender } = render(<XTimePicker model={{ ...timepickerProps, value: '15:00:00' }} />)
5858
await waitForIdleEventLoop()
59-
expect(wave.args[name]).toBe('15:00')
60-
rerender(<XTimePicker model={{ ...timepickerProps, value: '15:30' }} />)
61-
expect(wave.args[name]).toBe('15:30')
59+
expect(wave.args[name]).toBe('15:00:00')
60+
rerender(<XTimePicker model={{ ...timepickerProps, value: '15:30:00' }} />)
61+
expect(wave.args[name]).toBe('15:30:00')
6262
})
6363

6464
it('Set args when value is updated to initial value', async () => {
65-
const { container, rerender } = render(<XTimePicker model={{ ...timepickerProps, value: '04:00' }} />)
65+
const { container, rerender } = render(<XTimePicker model={{ ...timepickerProps, value: '04:00:00' }} />)
6666
await waitForIdleEventLoop()
67-
expect(wave.args[name]).toBe('04:00')
67+
expect(wave.args[name]).toBe('04:00:00')
6868

6969
await waitFor(() => fireEvent.click(container.querySelector("input")!))
7070
fireEvent.keyDown(screen.getByRole('listbox'), { key: 'ArrowUp' })
7171

7272
await waitForIdleEventLoop()
73-
expect(wave.args[name]).toBe('05:00')
73+
expect(wave.args[name]).toBe('05:00:00')
7474

75-
rerender(<XTimePicker model={{ ...timepickerProps, value: '04:00' }} />)
76-
expect(wave.args[name]).toBe('04:00')
75+
rerender(<XTimePicker model={{ ...timepickerProps, value: '04:00:00' }} />)
76+
expect(wave.args[name]).toBe('04:00:00')
7777
})
7878

7979
it('Show correct input value in 12 hour time format', async () => {
80-
const { getByDisplayValue } = render(<XTimePicker model={{ ...timepickerProps, value: '14:30' }} />)
80+
const { getByDisplayValue } = render(<XTimePicker model={{ ...timepickerProps, value: '14:30:00' }} />)
8181
await waitForIdleEventLoop()
82-
expect(getByDisplayValue('02:30 PM')).toBeInTheDocument()
83-
expect(wave.args[name]).toBe('14:30')
82+
expect(getByDisplayValue('02:30:00 PM')).toBeInTheDocument()
83+
expect(wave.args[name]).toBe('14:30:00')
8484
})
8585

8686
it('Shows midnight correctly in 12 hour time format', async () => {
8787
const { getByDisplayValue } = render(<XTimePicker model={{ ...timepickerProps, value: '00:00' }} />)
8888
await waitForIdleEventLoop()
89-
expect(getByDisplayValue('12:00 AM')).toBeInTheDocument()
90-
expect(wave.args[name]).toBe('00:00')
89+
expect(getByDisplayValue('12:00:00 AM')).toBeInTheDocument()
90+
expect(wave.args[name]).toBe('00:00:00')
9191
})
9292

9393
it('Shows noon correctly in 12 hour time format', async () => {
9494
const { getByDisplayValue } = render(<XTimePicker model={{ ...timepickerProps, value: '12:00' }} />)
9595
await waitForIdleEventLoop()
96-
expect(getByDisplayValue('12:00 PM')).toBeInTheDocument()
97-
expect(wave.args[name]).toBe('12:00')
96+
expect(getByDisplayValue('12:00:00 PM')).toBeInTheDocument()
97+
expect(wave.args[name]).toBe('12:00:00')
9898
})
9999

100100
it('Show correct input value in 24 hour time format', async () => {
101101
const { getByDisplayValue } = render(<XTimePicker model={{ ...timepickerProps, hour_format: '24', value: '23:30' }} />)
102102
await waitForIdleEventLoop()
103-
expect(getByDisplayValue('23:30')).toBeInTheDocument()
104-
expect(wave.args[name]).toBe('23:30')
103+
expect(getByDisplayValue('23:30:00')).toBeInTheDocument()
104+
expect(wave.args[name]).toBe('23:30:00')
105105
})
106106

107107
it('Value cannot be updated to be greater than max', async () => {
108108
const { rerender } = render(<XTimePicker model={{ ...timepickerProps, min: '00:00', max: '10:00', value: '04:00' }} />)
109109
await waitForIdleEventLoop()
110-
expect(wave.args[name]).toBe('04:00')
110+
expect(wave.args[name]).toBe('04:00:00')
111111
rerender(<XTimePicker model={{ ...timepickerProps, min: '00:00', max: '10:00', value: '14:00' }} />)
112112
await waitForIdleEventLoop()
113-
expect(wave.args[name]).toBe('10:00')
113+
expect(wave.args[name]).toBe('10:00:00')
114114
})
115115

116116
it('Value cannot be updated to be lower than min', async () => {
117117
const { rerender } = render(<XTimePicker model={{ ...timepickerProps, min: '02:00', max: '10:00', value: '04:00' }} />)
118118
await waitForIdleEventLoop()
119-
expect(wave.args[name]).toBe('04:00')
119+
expect(wave.args[name]).toBe('04:00:00')
120120
rerender(<XTimePicker model={{ ...timepickerProps, min: '02:00', max: '10:00', value: '01:00' }} />)
121121
await waitForIdleEventLoop()
122-
expect(wave.args[name]).toBe('02:00')
122+
expect(wave.args[name]).toBe('02:00:00')
123123
})
124124

125125
it('Changes out of bounds value when min is updated', async () => {
126126
const { rerender } = render(<XTimePicker model={{ ...timepickerProps, min: '00:00', max: '10:00', value: '04:00' }} />)
127127
await waitForIdleEventLoop()
128-
expect(wave.args[name]).toBe('04:00')
128+
expect(wave.args[name]).toBe('04:00:00')
129129
rerender(<XTimePicker model={{ ...timepickerProps, min: '05:00', max: '10:00', value: '04:00' }} />)
130130
await waitForIdleEventLoop()
131-
expect(wave.args[name]).toBe('05:00')
131+
expect(wave.args[name]).toBe('05:00:00')
132132
})
133133

134134
it('Changes out of bounds value when max is updated', async () => {
135135
const { rerender } = render(<XTimePicker model={{ ...timepickerProps, min: '00:00', max: '10:00', value: '04:00' }} />)
136136
await waitForIdleEventLoop()
137-
expect(wave.args[name]).toBe('04:00')
137+
expect(wave.args[name]).toBe('04:00:00')
138138
rerender(<XTimePicker model={{ ...timepickerProps, min: '00:00', max: '03:00', value: '04:00' }} />)
139139
await waitForIdleEventLoop()
140-
expect(wave.args[name]).toBe('03:00')
140+
expect(wave.args[name]).toBe('03:00:00')
141141
})
142142
})

ui/src/time_picker.tsx

+10-9
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface TimePicker {
3838
label?: S
3939
/** A string that provides a brief hint to the user as to what kind of information is expected in the field. */
4040
placeholder?: S
41-
/** The time value in hh:mm format. E.g. '10:30', '14:25', '23:59', '00:00' */
41+
/** The time value in hh:mm:ss format. E.g. '10:30:45', '14:25:30', '23:59:59', '00:00:00' */
4242
value?: S
4343
/** True if this field is disabled. */
4444
disabled?: B
@@ -52,9 +52,9 @@ export interface TimePicker {
5252
required?: B
5353
/** Specifies 12-hour or 24-hour time format. One of `12` or `24`. Defaults to `12`. */
5454
hour_format?: S
55-
/** The minimum allowed time value in hh:mm format. E.g.: '08:00', '13:30' */
55+
/** The minimum allowed time value in hh:mm:ss format. E.g.: '08:00:00', '13:30:00' */
5656
min?: S
57-
/** The maximum allowed time value in hh:mm format. E.g.: '15:30', '00:00' */
57+
/** The maximum allowed time value in hh:mm:ss format. E.g.: '15:30:00', '00:00:00' */
5858
max?: S
5959
/** Limits the available minutes to select from. One of `1`, `5`, `10`, `15`, `20`, `30` or `60`. Defaults to `1`. */
6060
minutes_step?: U
@@ -104,7 +104,7 @@ const
104104
LocalizationProvider = React.lazy(() => import('@mui/x-date-pickers/LocalizationProvider').then(({ LocalizationProvider }) => ({ default: LocalizationProvider }))),
105105
TimePicker = React.lazy(() => import('@mui/x-date-pickers/TimePicker').then(({ TimePicker }) => ({ default: TimePicker }))),
106106
allowedMinutesSteps: { [key: U]: U } = { 1: 1, 5: 5, 10: 10, 15: 15, 20: 20, 30: 30, 60: 60 },
107-
normalize = (time: S) => `2000-01-01T${time.slice(0, 5)}:00`,
107+
normalize = (time: S) => `2000-01-01T${time.slice(0, 8)}`,
108108
isBelowMin = (time: Date, minTime: Date) => time < minTime,
109109
isOverMax = (time: Date, maxTime: Date) => time > maxTime,
110110
isOutOfBounds = (time: Date, minTime: Date, maxTime: Date) => isBelowMin(time, minTime) || isOverMax(time, maxTime),
@@ -135,8 +135,9 @@ const
135135
{label && <Fluent.Label className={css.toolbarLabel}>{label}</Fluent.Label>}
136136
<Fluent.Text className={css.toolbarText}>
137137
<Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('hours')}>{time?.substring(0, 2) || '--'}</Fluent.Text>:
138-
<Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('minutes')}>{time?.substring(3, 5) || '--'}</Fluent.Text>{' '}
139-
<Fluent.Text className={css.toolbarAmPm}>{time?.substring(6, 8) || ''}</Fluent.Text>
138+
<Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('minutes')}>{time?.substring(3, 5) || '--'}</Fluent.Text>:
139+
<Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('seconds')}>{time?.substring(6, 8) || '--'}</Fluent.Text>{' '}
140+
<Fluent.Text className={css.toolbarAmPm}>{time?.substring(9, 11) || ''}</Fluent.Text>
140141
</Fluent.Text>
141142
</div>
142143

@@ -149,8 +150,8 @@ export const
149150
[isDialogOpen, setIsDialogOpen] = React.useState(false),
150151
textInputRef = React.useRef<HTMLDivElement | null>(null),
151152
popperRef = React.useRef<HTMLDivElement | null>(null),
152-
minTime = React.useMemo(() => parseTimeStringToDate(min || '00:00'), [min]),
153-
maxTime = React.useMemo(() => parseTimeStringToDate(max || '24:00'), [max]),
153+
minTime = React.useMemo(() => parseTimeStringToDate(min || '00:00:00'), [min]),
154+
maxTime = React.useMemo(() => parseTimeStringToDate(max || '24:00:00'), [max]),
154155
onChangeTime = (time: unknown) => {
155156
if (!(time instanceof Date)) return
156157
const newValue = formatDateToTimeString(time, '24')
@@ -192,7 +193,7 @@ export const
192193
},
193194
},
194195
{ format, AdapterDateFns, theme } = useTime(themeObj),
195-
formatDateToTimeString = React.useCallback((date: D, hour_format: S) => format ? format(date, hour_format === '12' ? 'hh:mm aa' : 'HH:mm') : '', [format])
196+
formatDateToTimeString = React.useCallback((date: D, hour_format: S) => format ? format(date, hour_format === '12' ? 'hh:mm:ss aa' : 'HH:mm:ss') : '', [format])
196197

197198
React.useEffect(() => {
198199
const time = m.value ? parseTimeStringToDate(m.value) : null

0 commit comments

Comments
 (0)