TimePicker
Time Picker is an input field for entering or selecting a specific time.
Available from eds-core/1.1.0
Quick Start
- Installation
npm install @adaptavant/eds-core
- Import
import { TimePicker } from '@adaptavant/eds-core';
Format
Define the time format for the TimePicker using the format
prop. 12
is the default value.
const handleSelect = ({ newOption }) => {
console.log(newOption?.time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker
onSelect={handleSelect}
label="Select time"
labelVisibility="visible"
titleForMobile="Select time"
closeButtonPropsForMobile={{ label: "Close" }}
/>
<TimePicker
format="24"
onSelect={handleSelect}
label="Select time"
labelVisibility="visible"
titleForMobile="Select time"
closeButtonPropsForMobile={{ label: "Close" }}
/>
</div>
);
Interval
Specify time interval in minutes using interval
prop. 60
minutes is the default interval.
const handleSelect = ({ newOption }) => {
console.log(newOption?.time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker onSelect={handleSelect} />
<TimePicker interval={45} onSelect={handleSelect} />
<TimePicker interval={30} onSelect={handleSelect} />
<TimePicker interval={15} onSelect={handleSelect} />
</div>
);
Variant
The Timepicker is available in 2 variants, standard
is the default.
const handleSelect = ({ newOption }) => {
console.log(newOption?.time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker onSelect={handleSelect} />
<TimePicker variant="subtle" onSelect={handleSelect} />
</div>
);
DefaultValue
Use defaultValue
prop to set a default time . It accepts a Date object or a string.
The <TimePicker/>
will use the last two parameters (hour and minute) from the Date object and ignore the first three (year, month, date). It will then set the defaultOption
in the provided format.
In the example below, the <TimePicker/>
will set the defaultValue
to 3:00 AM because the default format is 12
.
Note: The component rerenders with every update of defaultValue
. Please be mindful when using this prop to avoid performance issues.
const [defaultValue,setDefaultValue] = React.useState(new Date(2021,9,20,3,0));
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker defaultValue={defaultValue} onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
MinValue
Use the minValue
prop to set the minimum time for the <TimePicker/>
. It accepts a Date
object, but only the hours and minutes are considered; the date part is ignored. Time options earlier than this value will not be rendered.
const [startTime, setStartTime] = React.useState("");
const [endTime, setEndTime] = React.useState("");
const [endMinValue, setEndMinValue] = React.useState("");
const handleStartSelect = ({ newOption, inputState }) => {
const time = newOption ? newOption.time : "";
setStartTime(time);
if (time) {
setEndMinValue(newOption.dateObject);
}
};
const handleEndSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setEndTime(time);
};
return (
<Box className="flex items-center gap-3">
<Box className="flex items-start gap-4">
<TimePicker interval={30} onSelect={handleStartSelect} />
<Track as="span" className="text-lg text-gray-500 self-center h-8">
-
</Track>
<TimePicker
interval={30}
minValue={endMinValue}
onSelect={handleEndSelect}
/>
</Box>
{startTime && endTime && (
<Text className="text-body-12 text-secondary">
Selected Range: {startTime} - {endTime}
</Text>
)}
</Box>
);
MaxValue
Use the maxValue
prop to set the maximum time for the <TimePicker/>
. It accepts a Date
object, but only the hours and minutes are considered; the date part is ignored. Time options later than this value will not be rendered.
const [startTime, setStartTime] = React.useState("");
const [endTime, setEndTime] = React.useState("");
const [startMaxValue, setStartMaxValue] = React.useState("");
const handleStartSelect = ({ newOption, inputState }) => {
const time = newOption ? newOption.time : "";
setStartTime(time);
};
const handleEndSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setEndTime(time);
//Set MaxValue for startTime to limit the selection
if (time) {
setStartMaxValue(newOption.dateObject);
}
};
return (
<Box className="flex items-center gap-3">
<Box className="flex items-start gap-4">
<TimePicker
interval={30}
maxValue={startMaxValue}
onSelect={handleStartSelect}
/>
<Track as="span" className="text-lg text-gray-500 self-center h-8">
-
</Track>
<TimePicker interval={30} onSelect={handleEndSelect} />
</Box>
{startTime && endTime && (
<Text className="text-body-12 text-secondary">
Selected Range: {startTime} - {endTime}
</Text>
)}
</Box>
);
CustomValues
Use the customValues
prop to add specific time options to the list in addition to the default options. This prop accepts an array of Date
objects. By default, time options are generated at fixed intervals (e.g., 15, 30 minutes), so if you need a specific time not covered by these intervals, such as 11:59 PM, you can use customValues
.
The <TimePicker />
will use only the hour and minute from the Date
object, ignoring the year, month, and date, and will include this time in the list of available options.
However, note that certain edge cases will prevent a customValue
from being added to the list:
- If the time is less than or equal to
minValue
or greater than or equal tomaxValue
. - If the time is already present in the generated options list.
const [customValues,setCustomValues] = React.useState([new Date(0,0,0,23,59)]);
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker format="24" customValues={customValues} onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
DurationLabel
Use the showDurationLabel
prop in combination with the minValue
prop to display a duration label for each time option. The showDurationLabel
prop enables the label visibility, while the minValue
prop sets the minimum time threshold for the options to include duration labels.
const now = new Date(); // Get the current date and time
const todayAt534AM = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(),
5, // Hours
34, // Minutes
0, // Seconds
0 // Milliseconds
);
const [minValue,setMinValue] = React.useState(todayAt534AM);
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker minValue={minValue} showDurationLabel onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
Label
Use label
to provide a concise description of the purpose for <TimePicker/>
.
By default label
is hidden , Use labelVisibility
to display the label.
Notes: In Version 1.1.0, the label
prop is optional. It becomes mandatory in the subsequent versions.Additionally, the labelVisibility
prop is introduced in the following version.
const handleSelect = ({ newOption }) => {
console.log(newOption?.time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker label="Start time" onSelect={handleSelect} />
<TimePicker
label="Start time"
labelVisibility="visible"
format="24"
onSelect={handleSelect}
closeButtonPropsForMobile={{ label: "Select time" }}
/>
</div>
);
Disabled
Use the isDisabled
prop to show that a <Timepicker/>
isn't usable.
const [defaultValue,setDefaultValue] = React.useState(new Date(2021,9,20,3,0));
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker isDisabled defaultValue={defaultValue} onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
Clear
When set to true
, the TimePicker will clear its selected value and input. This can be used to programmatically reset the TimePicker.
const [value, setValue] = React.useState("");
const [clear, setClear] = React.useState(false);
const handleSelect = ({ newOption, inputState }) => {
// Reset the clear state if it's true
clear && setClear(false);
const time = newOption?.time ?? "";
// If the input state is invalid, set the clear state to true, otherwise set the value
inputState === "INVALID_FORMAT" ? setClear(true) : setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker clear={clear} onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
Events
Use onSelect
prop to trigger a callback when a time option is selected. It expects an object with the following keys
newOption
- Represents each option in the TimePicker and the data sent to the callback.time
- Represents the selected time as a string.dateObject
- Represents System Timezone usingDate
object.
inputState
- Represents the state of the TimePicker input value.Below are the possible states.
State | Description |
---|---|
| Indicates that the TimePicker input is empty. This state occurs when the user has not selected or entered any time |
| Represents a valid time input. The user has selected or entered a time that meets all the validation criteria, including being within any specified constraints such as |
| Indicates that the user has entered a time in an invalid format. This state is used to notify the user that the time format does not match the expected format (e.g., "HH AM/PM" for 12-hour format) |
| Indicates that the selected time is less than or equal to the specified Note: This can happen if the user enters a time manually. |
| Indicates that the selected time is greater than or equal to the specified Note: This can happen if the user enters a time manually. |
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption, inputState }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
Error Message
Use the errorMessage
prop to set the invalid state for the TimePicker. Check the input value state using the inputState
prop from the onSelect
callback.
The Timepicker's error state relies on the errorMessage
prop. If it isn't provided, no error message will be shown even if the input is invalid.
const [selectedTime, setSelectedTime] = React.useState("");
const [errorMsg, setErrorMsg] = React.useState("");
const handleSelect = ({ newOption, inputState }) => {
const time = newOption ? newOption.time : "";
setSelectedTime(time);
if (inputState === 'INVALID_FORMAT') {
setErrorMsg('Invalid');
}
if (inputState === 'VALID' || inputState === 'EMPTY') {
setErrorMsg('');
}
};
return (
<div className="flex gap-3 items-center">
<TimePicker errorMessage={errorMsg} onSelect={handleSelect} />
{selectedTime && (
<Text className="text-body-12 text-secondary">
Selected time: {selectedTime}
</Text>
)}
</div>
);
CustomDays
Use customDays
prop to define custom options like "FullDay"
or "24 Hours"
in the <TimePicker/>
. These options will be non-scrollable and remain fixed at the top, while the rest of the options will be scrollable.
Note: We do not recommend using more than two customDays
options for optimal usability.
const [customDays,setCustomdays] = React.useState(["Full Day"]);
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker format="24" customDays={customDays} onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
To have a custom day selected by default on load, you can pass it using the defaultValue
prop along with the customDays
prop.
const [customDays,setCustomdays] = React.useState(["Full Day"]);
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker format="24" customDays={customDays} defaultValue="Full Day" onSelect={handleSelect} />
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
Style API
Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.
Please refer to the Style API documentation for more insights.
TimePicker parts
const [selectedTime, setSelectedTime] = React.useState("");
const [customDays,setCustomDays] = React.useState(["Full Day"]);
const [errorMsg, setErrorMsg] = React.useState("");
const handleSelect = ({ newOption, inputState }) => {
const time = newOption ? newOption.time : "";
setSelectedTime(time);
if (inputState === "INVALID_FORMAT") {
setErrorMsg("Invalid");
}
if (inputState === "VALID" || inputState === "EMPTY") {
setErrorMsg("");
}
};
return (
<div className="flex gap-3">
<TimePicker
customDays={customDays}
onSelect={handleSelect}
label="Select"
labelVisibility="visible"
errorMessage={errorMsg}
classNames={{
label: "text-secondary",
field: "sm:w-[78px]",
input: "text-positive",
errorMessage: "text-caution",
errorIcon: "text-caution",
errorTrack: "bg-canvas-tertiary",
listboxItem: "text-left",
separator: "border-accent",
popover: "max-w-[80px]",
}}
/>
{selectedTime && (
<Text className="text-body-12 text-secondary mt-1">
Selected Slot: {selectedTime}
</Text>
)}
</div>
);
//Custom styles for Duration Label
const now = new Date(); // Get the current date and time
const todayAt534AM = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(),
5, // Hours
34, // Minutes
0, // Seconds
0 // Milliseconds
);
const [minValue, setMinValue] = React.useState(todayAt534AM);
const [value, setValue] = React.useState("");
const handleSelect = ({ newOption }) => {
const time = newOption ? newOption.time : "";
setValue(time);
};
return (
<div className="flex gap-3 items-center">
<TimePicker
minValue={minValue}
showDurationLabel
onSelect={handleSelect}
classNames={{
durationLabel: "text-positive",
timeSlot: "text-critical",
timeSlotWrapper: "gap-3",
}}
/>
{value && (
<Text className="text-body-12 text-secondary">
Selected time: {value}
</Text>
)}
</div>
);
Stylable Parts | Description |
---|---|
durationLabel | The text containing the secondary label with duration in hours and minutes. |
errorMessage | The text content displaying an error message. |
errorIcon | The icon shown in the error state. |
errorTrack | The track that highlights the error visually. |
field | The container for the time input field. |
input | The input element for entering or selecting the time. |
label | The label for the timepicker. |
listbox | The dropdown displaying available time options. |
listboxItem | Individual time option within the listbox. |
popover | The container that wraps the dropdown and its items. |
separator | The space between the scrollable and non-scrollable options in the popover. |
timeSlot | Represents each time option string displayed in the listbox (i.e) 8:00AM 9:00AM and so on. |
timeSlotWrapper | The container that wraps the the timeSlot and optional durationLabel. |
Note: The Timepicker doesn't have a root stylable part, so applying classNames directly may not be effective.
Usage guidelines
Do
- Use clear labels: Clearly label the TimePicker to indicate its purpose, such as "Select time" or "Choose appointment time."
- Consistent styling: Ensure that the TimePicker follows the same design and styling as other elements in your interface for a cohesive user experience.
- Accessibility: Implement proper HTML attributes to make the TimePicker accessible to users of assistive technologies, such as screen readers.
Don’t
- Avoid ambiguity: Avoid using vague labels or icons that may confuse users about the function of the TimePicker.
- Overcomplicate: Refrain from adding too many features or functionalities to the TimePicker. Keep it simple and focused on selecting a time.
- Inconsistent placement: Do not place the TimePicker in unpredictable locations across your interface. Follow established design patterns for its placement.
- Ignoring mobile responsiveness: Ensure that the TimePicker is responsive and functions well on various screen sizes, including mobile devices. Test its usability across different devices and resolutions to ensure a seamless experience for all users.
Best practices
Do
Ensure that the label within the TimePicker display complete and descriptive text. If the text can't fit horizontally, stack them vertically to maintain readability.
Don’t
Steer clear of using label text that is excessively long and requires wrapping onto multiple lines, as it can negatively impact readability and user experience.
Do
Use label text that is clear and prompts the user to take action, such as "Select time" or "Start time"
Don’t
Refrain from adding icons to emphasize label text. Keep the label text simple and focused on the action it performs.
Do
Use sentence-case capitalization for label text to improve readability and consistency.
Don’t
Avoid using title case or all uppercase (all caps) for label text, as this can make the text harder to read and may disrupt the visual harmony of the interface.