import { TimeInput } from '@uhg-abyss/web/ui/TimeInput';() => { const [value, setValue] = useState('');
return ( <TimeInput label="TimeInput Sandbox" value={value} onChange={setValue} /> );};Variants
Input with time picker
The default variant for TimeInput is an input field with a button to open a time picker.
Input only
Use the inputOnly prop when the time can be easily entered without a time picker.
useForm (Recommended)
Using useForm and FormProvider simplifies form management by providing out-of-the-box validation, form state handling, and performance optimizations, leveraging the power of react-hook-form.
useState
Using the useState hook gets values from the component state.
Format
Use the format prop to change the time format for the input. The possible values are '12' (the default) and '24'.
Display Properties
Label
Use the label prop to display a label above the input. To hide the input label, set hideLabel to true.
Use isRequired and isOptional for further customization.
Note: If using useForm, do not use isRequired. The same functionality can be achieved with required: true in validators.
Helper
Use the helper prop to display a help icon next to the label. Simply passing a string value will render the default helper, a Tooltip containing that string. The helper can be customized by passing in a node. It is recommended to use either a Tooltip or a Popover. See When should I use a Tooltip vs. a Popover? for more information on best practices regarding the two.
Subtext
Use the subText prop to display helpful information related to the input field. The prop accepts either a string or an object of the form:
{ text: string; position: 'above' | 'below';}The position property determines where the subtext will be displayed in relation to the input field. The default value is 'below'.
Width
Use the width prop to set the width of the input field.
Left Element
Use the inputLeftElement prop to add an element inside the text input field. The recommended usage is for inserting icons. The prop accepts an object with the following properties:
element: The element to be displayed inside the input field.description: An optional string that describes the purpose of the element for screen readers.
These are considered decorative and do not need to be exposed to screen readers. That said, please note that icons should not provide any information that is not also conveyed in a screen-readable way. For example, an exclamation mark (!) icon to indicate errors needs to be accompanied by aria-invalid.
If the icon is used to convey additional information, use the inputLeftElement.description prop to provide a description for screen readers.
Note: The recommended usage is when using the inputOnly prop.
Validation
Validators (useForm)
Use the validators prop to set validation rules for the field when using useForm. See the examples below for implementation on various types of validation.
Note: The default error message when required is true is minimally acceptable for accessibility. It is highly recommended to customize it to be more specific to the use of the field and form.
Error message (useState)
Use the errorMessage prop to display a custom error message below the input field when using useState.
Success message
Use the successMessage prop to display a custom success message below the input field. To provide a single success message across all form input components using useForm/FormProvider, you can provide successMessage to FormProvider as shown here.
Highlighted
Use the highlighted prop to enable a distinct background color when fields are required. To supply this across all form input components using useForm/FormProvider, you can provide highlighted to FormProvider as shown here.
Validation below menu
Set the validationBelowMenu prop to true to relocate the error and success message validation to below the menu, when open.
The default is false and the validation message will always remain displayed below the selection container, even when the calendar is open.
Interactivity
Clearable
Set the isClearable prop to true to display a clear button in the input field. The optional onClear callback prop can be used to trigger additional actions when the clear button is clicked.
Disabled
Set the isDisabled prop to true to disable the input field, preventing user interaction. The input will still display the current value, but users cannot change it.
Enable outside scroll
Set the enableOutsideScroll prop to true to allow the page to be scrolled while the calendar is open. The default value is false.
Responsiveness
On screens 360px wide or smaller, the picker will be placed in a full-screen takeover instead of a popup. Resize the window and open the picker to see the change!
TimeInput Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
className | string | - | - | CSS class name to apply to each element within the component |
css | Abyss.CSSProperties | Partial<Record<`abyss-${T}`, Abyss.CSSProperties>> | - | - | Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation |
data-testid | string | - | - | Suffix used to create a unique ID used for automated testing |
disableDefaultProviderProps | boolean | false | - | If set to true, the component will not use the DefaultPropsProvider values. If you aren’t using the DefaultPropsProvider, this prop can be ignored. |
enableOutsideScroll | boolean | false | - | If true, the page will remain scrollable when the popover is open |
errorMessage | string | never | - | - | Error message to display for the input. Only used when not in a FormProvider. errorMessage should not exist in useForm mode. |
helper | React.ReactNode | - | - | Helper element next to label |
hideLabel | boolean | false | - | If true, the label will be visually hidden |
highlighted | boolean | false | - | If true, the input field will be highlighted |
inputLeftElement | { element: React.ReactNode; description?: string | undefined; } | - | - | Content to be displayed as a left element within the input (typically a IconSymbol) |
inputOnly | boolean | false | - | If true, only the input field will be rendered without the picker button and popover time picker |
isClearable | boolean | - | - | If true, the input will display a clear button |
isDisabled | boolean | false | - | If true, the input will be disabled |
isOptional | boolean | false | - | If true, the label will be appended with "(Optional)" |
isRequired | boolean | never | - | - | Whether the input is required. Only used when not in a FormProvider. isRequired should not exist in useForm mode. |
label | string | - | The label for the input | |
model | never | string | - | - | model should not exist in useState mode. Model name for form validation. Only used when in a FormProvider. |
onApply | (currentTime: string) => void | - | - | Callback executed when the "Apply" button is clicked. Only shown when in mobile takeover mode. |
onBlur | React.FocusEventHandler<HTMLInputElement> | - | - | Callback function executed when the input is blurred |
onCancel | () => void | - | - | Callback executed when the "Cancel" button is clicked. Only shown when in mobile takeover mode. |
onClear | () => void | - | - | Callback function executed when the input is cleared |
onFocus | React.FocusEventHandler<HTMLInputElement> | - | - | Callback function executed when the input is focused |
onPickerButtonClick | (() => void) | undefined | - | - | Callback executed when the picker button is clicked |
openPosition | "default" | "top" | "bottom" | undefined | 'default' | - | Determines where the TimePicker will open relative to the input |
subText | string | { text: string; position: SubTextPosition; } | - | - | Additional descriptive text for the input |
successMessage | string | - | - | Success message to display for the input |
validationBelowMenu | boolean | false | - | If true, displays error and success validation messages below the menu |
validators | never | RegisterOptions | - | - | validators should not exist in useState mode. Validators for the input. Only used when in a FormProvider. |
value | ValueType | never | - | - | The value of the input. Only used when not in a FormProvider. value should not exist in useForm mode. |
TimeInput Classes
| Class Name | Description |
|---|---|
| .abyss-time-input-container | The time input container |
| .abyss-time-input-input-container | The container for the input fields |
| .abyss-time-input-hour-input | The hour input |
| .abyss-time-input-minute-input | The minute input |
| .abyss-time-input-am-pm-input | The AM/PM input |
| .abyss-time-picker-root | Root element of TimePicker |
| .abyss-time-picker-column-container | Container for input columns (hour, minute, AM/PM) |
| .abyss-time-picker-separator | Separator element between time inputs (colon) |
| .abyss-time-picker-hour-input | Hour input field in TimePicker |
| .abyss-time-picker-minute-input | Minute input field in TimePicker |
| .abyss-time-picker-am-pm-input | AM/PM input field in TimePicker |
| .abyss-time-picker-increase-hours-button | Arrow button to increase hours |
| .abyss-time-picker-decrease-hours-button | Arrow button to decrease hours |
| .abyss-time-picker-increase-minutes-button | Arrow button to increase minutes |
| .abyss-time-picker-decrease-minutes-button | Arrow button to decrease minutes |
| .abyss-time-picker-increase-ampm-button | Arrow button to increase AM/PM |
| .abyss-time-picker-decrease-ampm-button | Arrow button to decrease AM/PM |
| .abyss-time-picker-arrow-button | Arrow button root element |
| .abyss-time-picker-arrow-button-icon | Arrow button icon element |
| .abyss-time-input-root | Root element of PickerInput |
| .abyss-time-input-wrapper | Main wrapper for PickerInput content |
| .abyss-time-input-label-wrapper | Wrapper for label and subtext |
| .abyss-time-input-label | Label element for PickerInput |
| .abyss-time-input-sub-text | Subtext element (above or below input) |
| .abyss-time-input-format-text | Format text element (e.g. date format) |
| .abyss-time-input-field-wrapper | Wrapper for the input field and button |
| .abyss-time-input-focus-wrapper | Focus ring and input/clear button container |
| .abyss-time-input-input-wrapper | Wrapper for the input field and adjacent elements |
| .abyss-time-input-elements-container | Container for input elements |
| .abyss-time-input-left-element | Left element (e.g. icon) inside the input |
| .abyss-time-input-clear | Clear button for input |
| .abyss-time-input-picker-button | Popover open button |
| .abyss-time-input-icon | Icon inside the popover button |
| .abyss-time-input-descriptors | Validation or descriptor messages |
| .abyss-time-input-portal-container | Portal container for popover |
| .abyss-time-input-dismissable-layer | Dismissable layer for picker |
| .abyss-time-input-popover-wrapper | Popover wrapper for picker content |
| .abyss-time-input-takeover | Takeover container for mobile view |
| .abyss-time-input-takeover-selection-display-container | Container for the selection display in mobile takeover |
| .abyss-time-input-selection-display-text | Text element for the selection display |
| .abyss-time-input-takeover-button-container | Container for buttons in mobile takeover |
| .abyss-time-input-apply-button | Apply button |
| .abyss-time-input-cancel-button | Cancel button |
| .abyss-time-input-selection-display-wrapper | Wrapper for the selection display |
| .abyss-time-input-selection-display-root | Root element for the selection display |
| .abyss-time-input-selection-display-elements-container | Container for the elements in the selection display |
| .abyss-time-input-selection-display-clear | Clear button for the selection display |
TimeInput implements a simple group (<fieldset>) of three spin buttons for hours, minutes, and AM/PM. This makes the time picker dialog "redundant and unnecessary." To avoid the potential confusion and unnecessary redundant entry, the "Choose time" button does not receive keyboard focus.
Time Entry Keyboard Interactions Keyboard Interactions
| Key | Description |
|---|---|
| Tab | Moves focus to next TimeInput field, clear button or exits component |
| Shift + Tab | Moves focus to previous TimeInput field or exits component |
| Up Arrow | Increments current hour or minute field; toggles AM / PM |
| Down Arrow | Decrements current hour or minute field; toggles AM / PM |
Component Tokens
Note: Click on the token row to copy the token to your clipboard.
TimeInput Tokens
| Token Name | Value | |
|---|---|---|
| time-input.color.border.selection-indicator | #E5E5E6 | |
| time-input.color.icon.default | #323334 | |
| time-input.color.icon.disabled | #7D7F81 | |
| time-input.color.icon.utility.active | #000000 | |
| time-input.color.icon.utility.hover | #323334 | |
| time-input.color.icon.utility.rest | #4B4D4F | |
| time-input.color.surface.button.active | #E5E5E6 | |
| time-input.color.surface.button.hover | #F3F3F3 | |
| time-input.color.surface.button.rest | #FFFFFF | |
| time-input.color.surface.picker-mobile | #FFFFFF | |
| time-input.color.surface.selection-indicator.default | #F3F3F3 | |
| time-input.color.surface.selection-indicator.highlighted | #E5F8FB | |
| time-input.color.text.sub-label | #4B4D4F | |
| time-input.color.text.selection-indicator | #4B4D4F | |
| time-input.border-radius.all.selection-indicator | 4px | |
| time-input.border-width.all.selection-indicator | 1px | |
| time-input.border-width.bottom.separator | 1px | |
| time-input.sizing.all.icon | 20px | |
| time-input.spacing.gap.horizontal.takeover-button-container | 8px | |
| time-input.spacing.gap.horizontal.selection-indicator | 12px | |
| time-input.spacing.gap.vertical.selection-indicator | 8px | |
| time-input.spacing.padding.all.takeover-button-container | 16px | |
| time-input.spacing.padding.all.takeover-header | 16px | |
| time-input.spacing.padding.bottom.takeover-input-container | 24px | |
| time-input.spacing.padding.horizontal.takeover-input-container | 16px | |
| time-input.spacing.padding.horizontal.selection-indicator | 12px | |
| time-input.spacing.padding.top.takeover-input-container | 16px |
TimePicker Tokens
| Token Name | Value | |
|---|---|---|
| time-picker.color.border.container | #CBCCCD | |
| time-picker.color.icon.arrow.active | #00184D | |
| time-picker.color.icon.arrow.hover | #004BA0 | |
| time-picker.color.icon.arrow.rest | #002677 | |
| time-picker.color.surface.container | #FFFFFF | |
| time-picker.border-radius.all.container | 4px | |
| time-picker.border-width.all.container | 1px | |
| time-picker.sizing.all.icon.arrow | 24px | |
| time-picker.spacing.padding.all.container | 16px | |
| time-picker.spacing.padding.horizontal.input-column-container | 8px |
Input Tokens
| Token Name | Value | |
|---|---|---|
| input.color.surface.field.default | #FFFFFF | |
| input.color.surface.field.highlighted | #E5F8FB | |
| input.color.surface.field.disabled | #F3F3F3 | |
| input.color.border.field.rest | #4B4D4F | |
| input.color.border.field.hover.default | #196ECF | |
| input.color.border.field.hover.error | #990000 | |
| input.color.border.field.hover.success | #007000 | |
| input.color.border.field.active.default | #004BA0 | |
| input.color.border.field.active.error | #990000 | |
| input.color.border.field.active.success | #007000 | |
| input.color.text.input | #4B4D4F | |
| input.color.text.hint | #4B4D4F | |
| input.color.text.required | #990000 | |
| input.color.icon.utility.rest | #4B4D4F | |
| input.color.icon.utility.hover | #323334 | |
| input.color.icon.utility.active | #000000 | |
| input.color.icon.content | #323334 | |
| input.border-radius.all.field | 4px | |
| input.border-width.all.field.default | 1px | |
| input.border-width.all.field.active | 3px | |
| input.sizing.all.icon | 20px | |
| input.spacing.gap.vertical.container | 8px | |
| input.spacing.gap.horizontal.field | 12px | |
| input.spacing.gap.horizontal.input-indicator | 2px | |
| input.spacing.gap.horizontal.prefix-input | 2px | |
| input.spacing.gap.horizontal.suffix-clear | 2px | |
| input.spacing.padding.all.focus-container | 2px | |
| input.spacing.padding.horizontal.field | 12px | |
| input.spacing.padding.left.field | 12px | |
| input.spacing.padding.right.focus-field | 44px |
PickerInput Tokens
| Token Name | Value | |
|---|---|---|
| picker-input.color.border.popover | #CBCCCD | |
| picker-input.color.border.takeover-separator | #E5E5E6 | |
| picker-input.color.icon.default | #323334 | |
| picker-input.color.icon.disabled | #7D7F81 | |
| picker-input.color.surface.button.active | #E5E5E6 | |
| picker-input.color.surface.button.disabled | #F3F3F3 | |
| picker-input.color.surface.button.hover | #F3F3F3 | |
| picker-input.color.surface.button.rest | #FFFFFF | |
| picker-input.color.surface.popover | #FFFFFF | |
| picker-input.color.surface.takeover-selection-display.default | #F3F3F3 | |
| picker-input.color.surface.takeover-selection-display.highlighted | #E5F8FB | |
| picker-input.color.text.sub-label | #4B4D4F | |
| picker-input.color.text.takeover-selection-display | #4B4D4F | |
| picker-input.border-radius.all.popover | 4px | |
| picker-input.border-radius.all.takeover-selection-display | 4px | |
| picker-input.border-width.all.popover | 1px | |
| picker-input.border-width.all.takeover-selection-display | 1px | |
| picker-input.border-width.bottom.takeover-separator | 1px | |
| picker-input.sizing.all.icon | 20px | |
| picker-input.spacing.gap.horizontal.takeover-button-container | 8px | |
| picker-input.spacing.gap.horizontal.takeover-selection-display | 12px | |
| picker-input.spacing.gap.vertical.label | 4px | |
| picker-input.spacing.gap.vertical.popover | 8px | |
| picker-input.spacing.gap.vertical.takeover-selection-display | 8px | |
| picker-input.spacing.padding.all.takeover-header | 16px | |
| picker-input.spacing.padding.bottom.takeover-input-container | 24px | |
| picker-input.spacing.padding.horizontal.takeover-input-container | 16px | |
| picker-input.spacing.padding.horizontal.takeover-selection-display | 12px | |
| picker-input.spacing.padding.top.takeover-input-container | 16px | |
| picker-input.elevation.popover | 0px 2px 4px -2px rgba(0,0,0,0.16) |
Header Tokens
| Token Name | Value | |
|---|---|---|
| input-header.color.text.label | #4B4D4F | |
| input-header.color.text.hint | #4B4D4F | |
| input-header.color.icon.info.rest | #196ECF | |
| input-header.color.icon.info.hover | #004BA0 | |
| input-header.color.icon.info.active | #002677 | |
| input-header.sizing.all.icon | 24px | |
| input-header.spacing.gap.horizontal.container | 4px | |
| input-header.spacing.gap.horizontal.label | 4px | |
| input-header.spacing.gap.vertical.content | 4px | |
| input-header.spacing.padding.top.content | 2px |
Validation Tokens
| Token Name | Value | |
|---|---|---|
| input-validation.color.surface.container | #FFFFFF | |
| input-validation.color.text.error | #990000 | |
| input-validation.color.text.success | #007000 | |
| input-validation.color.icon.error | #990000 | |
| input-validation.color.icon.success | #007000 | |
| input-validation.sizing.all.icon | 20px | |
| input-validation.spacing.gap.horizontal.container | 4px |