<State>
{([time, setTime]) => (
<Form.TimePicker
label="Time"
description="08:00 АM to 05:00 PM"
min="08:00 АM"
max="05:00 PM"
onChange={setTime}
value={time}
/>
)}
</State>
Dropdown
If Time Picker's field is in focus, a dropdown to be shown with a list of availiable options to pick from.
Options are filtered by step
prop, but any intermediate value is still allowed to pick by typing.
Closest value, based on user's entry, to be highlighted in a dropdown. Dropdown behavior may be disabled with disableDropdown
prop.
<State>
{([time, setTime]) => (
<Stack spacing={4} className="m-b-1">
<Stack.Item fill>
<Form.TimePicker label="Time" onChange={setTime} value={time} />
</Stack.Item>
<Stack.Item fill>
<Form.TimePicker
label="Time (dropdown is disabled, type in a value)"
disableDropdown
onChange={setTime}
value={time}
/>
</Stack.Item>
</Stack>
)}
</State>
Time Range
Minimal and maximal time to be picked might be set with min
and max
props. Default time range is 12:00 АM
to 23:59 PM
.
<Stack spacing={4} className="m-b-1">
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Arrival Time"
description="09:00 АM to 11:00 PM, by hour step"
step={60}
min="09:00 АM"
max="11:00 PM"
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Lunch Time"
description="12:00 PM to 02:00 PM, by 30 min step"
min="12:00 PM"
max="02:00 PM"
step={30}
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
</Stack>
Step
Specified in minutes. Default value is 30
. Should not exceed 1440
(24 hours).
Step is used to generate dropdown options. User still can type in and choose any value in between min
and max
.
When autoRounding
is enabled, user is only allowd to choose values from options, generated by step
prop.
<Stack spacing={4} className="m-b-1">
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Minute after Minute"
description="08:00 AM to 08:15 AM"
min="08:00 AM"
max="08:15 AM"
step={1}
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Hour after Hour"
description="10:00 АM to 12:00 PM"
min="10:00 AM"
max="12:00 PM"
step={60}
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
</Stack>
Auto Rounding
Controls rounding behavior of a Time Picker:
- if false, ignores
step
prop, making it possible to type in any intermediate value, e.g. 02:53 PM
even if step=60
,
- if true, user are only allowed to choose option out of
step
-based row, e.g. value became ‘rounded’ to the closest value.
By default autoRounding
is disabled.
<Stack spacing={4} className="m-b-1">
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
autoRounding
label="Rounding to the closest (type in 10:55)"
description="10:00 AM to 12:00 PM"
min="10:00 AM"
max="12:00 PM"
step={60}
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="No rounding, any value acceptable (type in 10:55)"
description="10:00 AM to 12:00 PM"
min="10:00 AM"
max="12:00 PM"
step={60}
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
</Stack>
AM vs PM Default
When a user enters an ambiguous time (e.g. 12), the Time Picker can either read this as 12 AM or 12 PM.
By default, it resolves as AM. A PM default may also be used when most user contexts use PM values.
<Stack spacing={4} className="m-b-1">
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="AM preferable"
description="12:00 AM to 11:59 PM"
min="00:00 AM"
max="11:59 PM"
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
<Stack.Item fill>
<State>
{([time, setTime]) => (
<Form.TimePicker
defaultTimeSignifier="PM"
label="PM preferable"
description="00:00 AM to 11:59 PM (whole day)"
min="00:00 AM"
max="11:59 PM"
onChange={setTime}
value={time}
/>
)}
</State>
</Stack.Item>
</Stack>
By default, Time Picker uses 12-hour clock convention. It is common in US and few other countries.
In order to switch to 24-hour clock set format prop to hh:mm
.
<State initial={'17:00'}>
{([time, setTime]) => (
<Stack spacing={4} className="m-b-1">
<Stack.Item fill>
<Form.TimePicker
label="12-hour clock (default)"
onChange={setTime}
value={time}
/>
</Stack.Item>
<Stack.Item>
<Icon name="cached" size="30" className="m-t-4" />
</Stack.Item>
<Stack.Item fill>
<Form.TimePicker
label="24-hour clock"
format="hh:mm"
onChange={setTime}
value={time}
/>
</Stack.Item>
</Stack>
)}
</State>
Options and States
Time Picker can have a placeholder, a default value, a label (required and optional as well), a description, error and disabled states.
<Form>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Custom Placeholder"
placeholder="hh:mm am"
onChange={setTime}
value={time}
/>
)}
</State>
<State initial={'08:00 AM'}>
{([time, setTime]) => (
<Form.TimePicker
label="Default Value"
onChange={setTime}
value={time}
/>
)}
</State>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Required"
labelProps={{
required: true,
}}
onChange={setTime}
value={time}
/>
)}
</State>
<State>
{([time, setTime]) => (
<Form.TimePicker
label="Optional"
labelProps={{
optional: true,
}}
onChange={setTime}
value={time}
/>
)}
</State>
<State initial={'08:00 AM'}>
{([time, setTime]) => (
<Form.TimePicker
label="Disabled"
value={time}
onChange={setTime}
disabled
/>
)}
</State>
<State initial={'88:88'}>
{([time, setTime]) => (
<Form.TimePicker
label="Error"
value={time}
onChange={setTime}
error
/>
)}
</State>
</Form>
const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const zones = [
'Pacific/Honolulu',
'America/Los_Angeles',
'America/New_York',
'UTC',
'Asia/Yerevan',
'Asia/Yekaterinburg',
'Asia/Seoul',
'Pacific/Auckland',
];
const NowExample = () => {
const initialTime = new Date('2023-04-01T22:00:00Z');
const initialZone = zones[1];
const [time, setTime] = React.useState(initialTime);
const [zone, setZone] = React.useState(initialZone);
const onTimezoneChange = (tz) => {
setZone(tz);
};
const onTimeChange = (date) => {
setTime(date);
};
const now = () => new Date();
const timeTZ = (date) => {
const formatter = new Intl.DateTimeFormat([], {
hourCycle: 'h23',
hour: 'numeric',
minute: 'numeric',
timeZone: zone,
});
return formatter.format(date);
};
const dateTZ = (date) =>
date.toLocaleDateString([], {
year: 'numeric',
month: 'short',
day: 'numeric',
timeZone: zone,
});
const NowButton = () => (
<Button
xsmall
style={{ margin: '-5px 0' }}
onClick={(e) => {
e.preventDefault();
onTimeChange(now());
}}
aria-label="select current time"
children="Now"
/>
);
return (
<div style={{ maxWidth: '30rem' }}>
<Form.ButtonToggle
xsmall
label="Time Zone"
value={zone}
onChange={onTimezoneChange}
options={zones.map((tz) => ({
text: (tz.split('/')[1] || tz).replace('_', ' '),
value: tz,
}))}
/>
<TimezoneProvider timezone={zone}>
<Form.TimePicker
label="Start Time"
labelProps={{ action: <NowButton /> }}
onChange={onTimeChange}
value={time}
step={15}
/>
</TimezoneProvider>
<BodyText className="m-b-1">
Selected time and date:
<br />
{time ? `${timeTZ(time)} · ${dateTZ(time)}` : 'undefined'}
</BodyText>
</div>
);
};
render(NowExample);
Content Guidance
A label is a short, meaningful description that clearly indicates what the user is expected to enter. Labels should be 1 to 3 words and written in title case. Labels should primarily be nouns. Avoid using labels as CTAs. A label is not inline help and shouldn’t be used to provide instruction.
Follow capitalization rules
Input labels should be written in title case.
Avoid using placeholder text
Placeholder text has several accessibility issues:
- Text has low contrast which makes the text hard to read
- Text disappears as soon as the user starts typing
- Can’t accommodate additional context due to limited space
- Unreliable for screen readers
Use inline help to provide hints, formatting, and requirements.
Placeholders in Form Fields Are Harmful (Nielsen Norman Group)
Inline help should explain a feature or the outcome of the actions the user is about to take.
The description should adapt to the situation and context. The guidance could be focused on what is needed, or it could describe how to enter it.
- An overall description of the input field
- Hints for what kind of information needs to be input
- Specific formatting examples or requirements
Do not repeat the label in order to prompt someone to interact with it.
Don’t add inline help when unneeded or solely to make it match other inputs that have inline help text.
Use sentence case, and only include period if more than one sentence is used.
Follow the inline help content guidelines.
Accessibility
The user should be able to choose a time by typing into the input or by selecting an option from the dropdown.
The user should be able to use either method.
Keyboard Support
- When focus is on the input, press
down
to open and escape
to close the dropdown.
- When focus is on the input, use
tab
/ shift + tab
to perform autocomplete based on entry and move focus away from Time Picker.
- When focus is on the dropdown, use the arrow keys
up
/ down
or shift + tab
/ tab
to traverse through options and enter
to select a date.
Best Practices
Time Picker should:
- Use smart defaults for
step
and autoRounding
- Have clear description and label to be comfortable for end user
Related Content
Components
Patterns
- Form design pattern for how related controls are ordered.
Importing
import { TimePicker } from '@servicetitan/design-system';