-
Notifications
You must be signed in to change notification settings - Fork 6
Date Range Picker
- Overview
- User Stories
- Functionality
- Test Scenarios
- Accessibility
- Assumptions and Limitations
- References
Astrea + Design and Web Development
Developer Name Bozhidara Pachilova, Ivan Kitanov
Designer Name Yordanka Petkova-Radoeva, Silvia Ivanova
- Radoslav Mirchev | Date: 2025-04-16
- Radoslav Karaivanov | Date:
- Svilen Dimchevski | Date:
Version | Users | Date | Notes |
---|---|---|---|
1 | Bozhidara Pachilova | 2025-03-10 | Initial draft |
2 | Bozhidara Pachilova | 2025-04-14 | Spec ready for review |
3 | Ivan Kitanov | 2025-04-14 | Minor updates to customRanges property |
4 | Svilen Dimchevski | 2025-04-15 | Design Handoff in Figma added |
The igc-date-range-picker
lets users select start and end dates by either by selecting a date range through a dropdown/dialog popup calendar view or by typing in its input fields - one for start and one for end. It can be customized to match app requirements with features like range restrictions, formats, data range selection, grouping the start and end values together and many more.
The picker has two modes for displaying the date values - single and with two inputs. In single, the input is non-editable and the date range cannot be edited by typing. The two inputs mode, on the other hand, allows typing to edit the start and end dates in separate igc-date-time-input
s.
(Note: typing in single input mode might be implemented in the future after adding the ability to edit a date range for the igc-date-time-input
.
Provide a web component that uses the igc-calendar
to select a range value. The igc-calendar
is opened either in an igc-popover
for dropdown mode, or in an igc-dialog
for dialog mode. The date values are editable by typing when the component uses two inputs as editors. In the single input mode, the values can only be updated through calendar selection.
The value of the igc-date-range-picker
is of type DateRangeValue
- { start: Date | null, end: Date | null}
.
Reference specifications: Calendar, Date Time Input, Dialog
End-to-end user experience prototype
The igc-date-range-picker
must:
- let users specify a date range by either typing it in two inputs - for start and end value or picking a date range through its calendar component
- display the date-range picker as a single input field, which displays both start and end dates and is readonly
- have dropdown and dialog modes for opening the calendar
- open the calendar by clicking on a toggle icon in dropdown mode
- open the calendar by clicking on a toggle icon or on the input in dialog mode
- be form associated and when configured as part of a form, participate in form submission and validation
- expose CSS parts for styling as well as slots for projecting relevant elements such as prefix, suffix, helper text, separator etc.
- provide UI with buttons allowing to quickly select among a predefined set of ranges - last week, last month, etc.
- expose slots for defining additional custom actions
- expose configuration properties for modifying/localizing the inputs and display format of the date values in the inputs
- expose configuration properties for modifying/localizing the relevant parts of its calendar component
- have adequate keyboard support for navigation and selection
- support the themes available in the library
- be WAI-ARIA compliant.
Developer stories: As a developer I expect to be able to:
- set an initial value for the component and/or change the value of the component programmatically if need be
- set the interaction state of the component - make it disabled, readonly or nonEditable
- set additional properties such as label, placeholder and/or validators in order to enrich and guide the end user experience
- use the component in a standard form and have the component participate in form submission and validation
- choose whether the calendar picker would be a dropdown like experience or a dialog like experience
- control how the date value would be formatted in the inputs both during editing and display, by specifying a custom pattern/mask
- choose the component to render either single input displaying the date range values separated by a symbol ("-") or two inputs for start and end date
- define custom content for the separator between the two inputs
- hide/show days outside of the current month in the calendar picker
- set the day that weeks would start in the calendar picker
- set whether to show/hide week numbers in the calendar picker
- set special/disabled dates in the calendar picker
- show a single or two months in the calendar picker
- set the formatting and localization of dates and elements in the calendar picker
- listen and react to user interactions through events on the component
- opt in to show chips allowing to choose among a predefined set of ranges, such as last week, last month, etc.
- define additional custom actions
- be able to style parts of the component according to my prefereces.
End-user stories:
As an end-user I expect to be able to:
- type in and edit a date value inside the start and end inputs in dropdown mode
- select a date range from the calendar picker of the component and have it automatically filled in the input(s)
- identify and distinguish the current date, selected date range, special and disabled dates
- navigate and make edits in the input parts of the component using a keyboard
- navigate and make selection in the calendar picker part of the component using a keyboard
- navigate in and out of the component using only a keyboard
- be informed if the dates I've entered are valid and within range (if such is specified)
- select a range by clicking a button among a set of predefined date ranges.
3.1. End-User Experience
3.2. Developer Experience
interface DateRangeValue {
start: Date | null;
end: Date | null;
}
interface CustomDateRange {
label: string;
dateRange: DateRangeValue;
}
The predefined ranges chips are ported in a separate internal component named IgcPredefinedRangesAreaComponent
.
<igc-date-range-picker label-start="Start label" label-end="End label"></igc-date-range-picker>
<igc-date-range-picker value="{"start":"2025-04-13T21:00:00.000Z","end":"2025-04-14T21:00:00.000Z"}"></igc-date-range-picker>
<igc-date-range-picker display-format="yyyy-dd-mm" input-format="yyyy-dd-mm"></igc-date-range-picker>
Display and input format should be applied for both inputs.
For reference of the supported formats - the Date Time Input spec.
<igc-date-range-picker label="..." mode="dialog" display-format="yyyy/MM/dd">
<p slot="title">...</p>
<p slot="invalid">Please, select another range</p>
</igc-date-range-picker>
3.3. Behaviors
- on losing focus, igcChange is emitted; start and end are null (detail is { start: null, end: null }); (for both two and single inputs mode)
- if the control is readOnly, clicking the clear icon should not clear the value
- In dropdown mode:
- on selecting the first date, igcChange is emitted; start and end are the same date; (detail is { start: startDate, end: startDate});
- on selecting a second date, igcChange is emitted again; (detail is { start: startDate, end: endDate});
- In dialog mode:
- igcChange is emitted on pressing the "Done" button, or when closing the dialog by clicking outside of it. Doing so will confirm the selected dates.
- In dropdown mode, the input(s) start and end value are immediately reflected with selection
- In dialog mode, the input(s) start and end value are immediately reflected with selection. If the "Cancel" button is pressed, the values are reverted back.
4. Both inputs are initially empty. One of them - the start date, is filled by typing (dropdown mode).
- igcInput is emitted; the detail is { start: dateValue, end: null });
- On losing focus of the input igcChange is fired; detail is { start: dateValue, end: null });
- The typed date should be selected in the calendar.
- In scenarios when there are two inputs, both of them should reflect the form state, i.e. if the date-range-picker is invalid, both inputs should be in the invalid state.
- When checking for min/max value validation constraints (rangeUnderflow/rangeOverflow), both the start and end dates are checked to be less/greater than the applied min/max value. The control is invalid if any of the start/end values do not satisfy the condition.
- The calendar active date is set to
activeDate
property of thedate-range-picker
, if set. - If the above is not set and there is no value (both
start
andend
are null), the calendar's internal active date is used, which by default is the current date. - If a value is assigned to the
date-range-picker
, the first defined date among thestart
orend
is assigned as active date:
{ start: *start-date*, end: null } -> // active date is *start-date*
{ start: *start-date*, end: *end-date*} -> // active date is *start-date*
{ start: null, end: *end-date*} -> // active date is *end-date*
- On typing in the inputs, the active date gets assigned to the last modified/typed date.
- If both values are cleared by typing, the active date remains as the last that was assigned.
- In dropdown mode when selecting a chip value, the dropdown gets closed.
- In dialog mode when selecting a chip value, the dialog remains opened.
- Keyboard navigation is disabled.
- Toggling of the calendar picker is disabled.
- Clearing through the clear icon is disabled.
- The calendar and clear icons appear visually disabled.
3.3. Globalization/Localization The following strings should be exposed for localization:
- The "to" separator between two inputs
- The "Cancel" and "Done" buttons of the calendar dialog.
- The predefined ranges chips labels
- The resource strings of the calendar
i.e.
export interface IgcDateRangePickerResourceStrings
extends IgcCalendarResourceStrings {
separator: string;
done: string;
cancel: string;
last7Days: string;
last30Days: string;
//..
}
3.4. Keyboard Navigation
As long as focus is within parts of the date range picker component:
Key combination | Description |
---|---|
Escape | If the picker is shown closes the picker and returns focus to the input part. Otherwise it is a no-op |
When any of the input parts of the component is focused:
Key combination | Description |
---|---|
ArrowLeft / ArrowRight | Moves the caret one character in the desired direction |
Ctrl + ArrowLeft | Moves the caret to the beginning of the current input mask section or to the start of the previous one it is already at the beginning |
Ctrl + ArrowRight | Moves the caret to the end of the current input mask section or to the end of the next one it is already at the end |
ArrowUp | Increments by one step the currently "focused" part of the input mask |
ArrowDown | Decrements by one step the currently "focused" part of the input mask |
Home | Moves the caret at the beginning of the mask |
End | Moves the caret at the end of the mask |
Ctrl + ; | Sets the current date as the value of the component (start input - start value, end input - end value) |
Alt + ArrowDown | Opens the calendar dropdown |
Alt + ArrowUp | Closes the calendar dropdown |
When focus is within the calendar picker, the keyboard navigation follows the behaviors described in this section of the calendar specification.
3.5. API
Property | Attribute | Reflected | Property Type | Default | Description |
---|---|---|---|---|---|
open |
open |
Yes | boolean |
false |
Whether the calendar picker is open |
mode |
mode |
No | 'dropdown' | 'dialog' |
dropdown |
Whether to display the calendar picker in a dropdown or a modal dialog |
keepOpenOnSelect |
keep-open-on-select |
Yes | boolean |
false |
Whether the calendar picker should be kept open on selection |
keepOpenOnOutsideClick |
keep-open-on-outside-click |
Yes | boolean |
false |
Whether the calendar picker should be kept open when clicking outside of it |
value |
value |
No | DateRangeValue |
{ start: null; end: null } |
The value of the component |
min |
min |
No | Date |
- | The minimum start value required for the component to remain valid |
max |
max |
No | Date |
- | The maximum end value allowed for the component to remain valid |
required |
required |
Yes | boolean |
false |
Makes the component required in a form context |
disabled |
disabled |
Yes | boolean |
false |
Disables the component |
readOnly |
readonly |
Yes | boolean |
false |
Makes the component readonly |
nonEditable |
non-editable |
Yes | boolean |
false |
Whether to allow typing in the input(s) |
placeholder |
placeholder |
No | string |
- | The placeholder for the input (single input) |
placeholderStart |
placeholder-start |
No | string |
- | The placeholder for the start input (two inputs) |
placeholderEnd |
placeholder-end |
No | string |
- | The placeholder for the end input (two inputs) |
label |
label |
No | string |
- | The label for the component (single input) |
labelStart |
label-start |
No | string |
- | The label for the start input (two inputs) |
labelEnd |
label-end |
No | string |
- | The label for the end input (two inputs) |
outlined |
outlined |
Yes | boolean |
false |
Whether the input part will have outline appearance in the Material theme |
locale |
locale |
No | string |
en |
The locale used to display the value and used for formatting the display of the calendar dates |
inputFormat |
input-format |
No | string |
Default for locale
|
Date mask pattern when editing in the input part of the component Reference |
displayFormat |
display-format |
No | string |
inputFormat |
Date pattern to apply to the input value when the input(s) is not focused Reference |
prompt |
prompt |
No | string |
_ |
The prompt character used for unfilled parts of the input(s) mask |
weekStart |
week-start |
No | typed string |
sunday |
Sets the start day of the week |
showWeekNumbers |
show-week-numbers |
Yes | boolean |
false |
Whether to show the number of the week in the calendar days view |
hideOutsideDays |
hide-outside-days |
Yes | boolean |
false |
Whether to show dates that do not belong to the current month |
hideHeader |
hide-header |
Yes | boolean |
false |
Whether to show the calendar header. Applicable only in dialog mode |
headerOrientation |
header-orientation |
Yes | 'vertical' | 'horizontal' |
horizontal |
Whether to align the calendar header vertically or horizontally. Applicable only in dialog mode. |
orientation |
orientation |
No | 'vertical' | 'horizontal' |
horizontal |
Whether to align multiple months horizontally or vertically |
visibleMonths |
visible-months |
No | number |
1 | 2
|
The number of months to show in the calendar days view |
disabledDates |
- | No | DateRangeDescriptor[] |
- | The disabled dates for the calendar picker |
specialDates |
- | No | DateRangeDescriptor[] |
- | The special dates for the calendar picker |
activeDate |
active-date |
No | Date |
The current date if not set | Sets the date which is shown in view and is highlighted |
resourceStrings |
- | No | IgcDateRangePickerResourceStrings |
Resource strings for localization of the date-range picker and the calendar | |
useTwoInputs |
use-two-inputs |
Yes | boolean |
false |
Indicates whether the picker should use two inputs to display and edit the date range |
usePredefinedRanges |
use-predefined-ranges |
Yes | boolean |
false |
Indicates whether an area with chips of predefined date ranges to select from will be rendered |
customRanges |
- | No | CustomDateRange[] |
[] |
Renders chips with custom ranges based on the elements of the array. |
Name | Type signature | Description |
---|---|---|
show |
(): void |
Shows the component picker |
hide |
(): void |
Hides the component picker |
toggle |
(): void |
Toggles between the open state of the picker |
clear |
(): void |
Clears the input parts of the component of any user input |
select |
(value: DateRangeValue | null): void |
Selects a date range value in the picker |
setCustomValidity |
(message: string): void |
Sets a custom validation message. As long as message is not empty, the component is considered invalid |
Name | Cancellable | Description |
---|---|---|
igcOpening | Yes | Emitted when the calendar picker is opening |
igcOpened | No | Emitted after the picker is shown |
igcClosing | Yes | Emitted when the calendar picker is closing |
igcClosed | No | Emitted when the calendar picker is closed |
igcChange | No | Emitted when the value of the component is changed |
igcInput | No | Emitted when typing in the input part(s) of the component |
Name | Description |
---|---|
separator | Renders the separator element between the two inputs |
prefix | Renders content before the input (single input) |
prefix-start | Renders content before the start input (two inputs) |
prefix-end | Renders content before the end input (two inputs) |
clear-icon | Renders a clear icon template |
clear-icon-start | Renders a clear icon template for the start input (two inputs) |
clear-icon-end | Renders a clear icon template for the end input (two inputs) |
calendar-icon | Renders the icon/content for the calendar picker (applied to both inputs in two inputs mode) |
calendar-icon-start | Renders the icon/content for the calendar picker for the start input (two inputs) |
calendar-icon-end | Renders the icon/content for the calendar picker for the end input (two inputs) |
calendar-icon-open | Renders the icon/content for the picker in open state (applied to both inputs in two inputs mode) |
calendar-icon-open-start | Renders the icon/content for the picker in open state for the start input (two inputs) |
calendar-icon-open-end | Renders the icon/content for the picker in open state for the end input (two inputs) |
suffix | Renders content after the input (single input) |
suffix-start | Renders content after the start input (two inputs) |
suffix-end | Renders content after the end input (two inputs) |
helper-text | Renders content below the input(s) |
title | Renders content for the calendar title. Applicable only in dialog mode |
header-date | Renders content instead of the current date/range in the calendar header. Applicable only in dialog mode |
actions | Renders content in the action part of the picker in open state |
Part | Description |
---|---|
separator |
The separator element between the two inputs. |
ranges |
The wrapper that renders the custom and predefined ranges. |
label |
The label wrapper that renders content above the target input. |
calendar-icon |
The calendar icon wrapper for closed state. (single input) |
calendar-icon-start |
The calendar icon wrapper for closed state for the start input (two inputs). |
calendar-icon-end |
The calendar icon wrapper for closed state for the end input (two inputs). |
calendar-icon-open |
The calendar icon wrapper for opened state. |
calendar-icon-open-start |
The calendar icon wrapper for opened state for the start input (two inputs). |
calendar-icon-open-end |
The calendar icon wrapper for opened state for the end input (two inputs). |
actions |
The wrapper for the custom actions area. |
clear-icon |
The clear icon wrapper. (single input) |
clear-icon-start |
The clear icon wrapper for the start input (two inputs). |
clear-icon-end |
The clear icon wrapper for the end input (two inputs). |
input |
The native input element. |
prefix |
The prefix wrapper. |
suffix |
The suffix wrapper. |
helper-text |
The helper-text wrapper that renders content below the target input. |
Note
All CSS Parts of the Calendar component can also be used to style the igc-date-range-picker
calendar. Keep in mind that the content
and the label
parts of the calendar component are renamed respectively to calendar-content
and calendar-label
in the igc-date-range-picker
scope.
- should be successfully initialized in the DOM (defined and rendered).
- should not set an invalid date range as a value
- should be successfully initialized with an initial value
- should be successfully initialized in open state in dropdown mode
- should be successfully initialized in open state in dialog mode
- should pass automated WAI-ARIA tests in closed state
- should pass automated WAI-ARIA tests in open state in dropdown mode
- should pass automated WAI-ARIA tests in open state in dialog mode
- should set value through attribute correctly in case the date values are valid ISO 8601 strings
- should show/hide the picker based on the value of the open attribute
- should keep the calendar selection and input values on changing the mode
- should keep the calendar selection and input values on changing to two inputs and back to single
- should set the active date of the calendar to the start of the range when value is initially set
- should keep the picker open when keepOpenOnOutsideClick is enabled and a click if fired outside of the component
- should keep the picker open when keepOpenOnSelect is enabled and a selection is made in the calendar picker
- should not toggle the picker when readonly is set
- should not clear the value through the clear icon, if readonly is set
- should not allow to modify the value through selection or typing when
readOnly
is true - should modify value only through calendar selection and not input when
nonEditable
is true - should set properties of the calendar correctly
- should set properties of the input(s) correctly
- should set and render UI for a predefined set of ranges to select
- should render slotted elements
- Verify all API methods are invoked correctly and achieve their purpose.
- Verify all events are emitted correctly.
- Verify the above listed key combinations work as expected.
- should select a single date in dropdown mode and emit igcChange
- should select a range of dates in dropdown mode and emit igcChange
- should select a range of dates in dialog mode and emit igcChange when done is clicked
- should not emit igcChange when cancel is clicked and the value should be the initial value (dialog mode)
- should emit or not igcInput according to nonEditable property
- should show/hide the calendar on clicking the toggle icon in dropdown & dialog mode & emit the open/close events
- should show the calendar on clicking the input in dialog mode and emit the open/close events
- should swap the selected dates after input & on losing focus if the end is earlier than the start
- should not render any chips when usePredefinedRanges is false and there are no custom ranges added
- should render all predefined ranges and content in the actions slot
- should emit igcChange when the chips are clicked
- should render only custom chips, when usePredefinedRanges is false
- should be form associated
- should not participate in form submission if the value is empty/invalid
- should participate in form submission if there is a value and the value adheres to the validation constraints
- should reset to its default value state on form reset
- should reflect disabled ancestor state (fieldset/form)
- should enforce required constraint
- should enforce min value constraint
- should enforce max value constraint
- should invalidate the component if a disabled date is typed it in any of the inputs
- should enforce custom constraint
- should use the value of inputFormat for displayFormat, if it is not defined
- should default inputFormat to whatever Intl.DateTimeFormat returns for the current locale
- should properly set displayFormat to the set of predefined formats -
'short', 'medium', 'long', 'full'
- should expose the default strings for localization
- should update the masked value with the locale - single input
- should set the mask of the single input per the display format (like dd/MM/yyyy - dd/MM/yyyy)
- should render validation slots
Note
The rest of the properties/attributes should already be covered by the respective unit tests of the components
used in the igc-calendar
and igc-date-time-input
.
When the component is in dropdown mode, the underlying igc-calendar should have role="dialog"
The component should work in a Right-To-Left context without additional setup or configuration.
In the POC phase, the approach to slot the two inputs like:
<igc-date-range-picker>
<igc-date-range-input
label="Start date"
slot="start"
name="date-range-start-name"
>
</igc-date-range-input>
<igc-date-range-input
label="End date"
slot="end"
name="date-range-end-name"
></igc-date-range-input>
</igc-date-range-picker>
was tested, but discarded.
There are potentially two approaches. First is to create such a wrapper component for the igc-date-time-input
and pass properties to it. This would allow defining the needed icons, however, comes with the overhead of setting all properties through an additional layer.
Second might be to slot igc-date-time-input
s directly and possibly hook into a lifecycle method to "enforce" the toggle and clear icons, which might not be a best practice either.
The inputs will be rendered by the component and can be customized through properties - such as labelStart, labelEnd, etc.
Assumptions | Limitation Notes |
---|---|
Specify all referenced external sources