@utilitywarehouse/hearth-react-native 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.storybook/preview.tsx +3 -0
  2. package/.turbo/turbo-build.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/CHANGELOG.md +10 -0
  5. package/build/components/CurrencyInput/CurrencyInput.js +1 -1
  6. package/build/components/DatePicker/DatePicker.context.d.ts +19 -0
  7. package/build/components/DatePicker/DatePicker.context.js +3 -0
  8. package/build/components/DatePicker/DatePicker.d.ts +19 -0
  9. package/build/components/DatePicker/DatePicker.js +479 -0
  10. package/build/components/DatePicker/DatePicker.props.d.ts +125 -0
  11. package/build/components/DatePicker/DatePicker.props.js +1 -0
  12. package/build/components/DatePicker/DatePickerCalendar.d.ts +2 -0
  13. package/build/components/DatePicker/DatePickerCalendar.js +28 -0
  14. package/build/components/DatePicker/DatePickerDay.d.ts +11 -0
  15. package/build/components/DatePicker/DatePickerDay.js +242 -0
  16. package/build/components/DatePicker/DatePickerDays.d.ts +2 -0
  17. package/build/components/DatePicker/DatePickerDays.js +157 -0
  18. package/build/components/DatePicker/DatePickerFooter.d.ts +2 -0
  19. package/build/components/DatePicker/DatePickerFooter.js +23 -0
  20. package/build/components/DatePicker/DatePickerHeader/DatePickerHeader.props.d.ts +8 -0
  21. package/build/components/DatePicker/DatePickerHeader/DatePickerHeader.props.js +1 -0
  22. package/build/components/DatePicker/DatePickerHeader/DatePickerMonthButton.d.ts +2 -0
  23. package/build/components/DatePicker/DatePickerHeader/DatePickerMonthButton.js +14 -0
  24. package/build/components/DatePicker/DatePickerHeader/DatePickerNextButton.d.ts +2 -0
  25. package/build/components/DatePicker/DatePickerHeader/DatePickerNextButton.js +32 -0
  26. package/build/components/DatePicker/DatePickerHeader/DatePickerPrevButton.d.ts +2 -0
  27. package/build/components/DatePicker/DatePickerHeader/DatePickerPrevButton.js +32 -0
  28. package/build/components/DatePicker/DatePickerHeader/DatePickerSelectors.d.ts +6 -0
  29. package/build/components/DatePicker/DatePickerHeader/DatePickerSelectors.js +64 -0
  30. package/build/components/DatePicker/DatePickerHeader/DatePickerTimeButton.d.ts +1 -0
  31. package/build/components/DatePicker/DatePickerHeader/DatePickerTimeButton.js +22 -0
  32. package/build/components/DatePicker/DatePickerHeader/DatePickerYearButton.d.ts +2 -0
  33. package/build/components/DatePicker/DatePickerHeader/DatePickerYearButton.js +25 -0
  34. package/build/components/DatePicker/DatePickerHeader/index.d.ts +3 -0
  35. package/build/components/DatePicker/DatePickerHeader/index.js +30 -0
  36. package/build/components/DatePicker/DatePickerMonths.d.ts +2 -0
  37. package/build/components/DatePicker/DatePickerMonths.js +69 -0
  38. package/build/components/DatePicker/DatePickerWeekdays.d.ts +8 -0
  39. package/build/components/DatePicker/DatePickerWeekdays.js +26 -0
  40. package/build/components/DatePicker/DatePickerYears.d.ts +2 -0
  41. package/build/components/DatePicker/DatePickerYears.js +83 -0
  42. package/build/components/DatePicker/TimePicker.d.ts +3 -0
  43. package/build/components/DatePicker/TimePicker.js +84 -0
  44. package/build/components/DatePicker/enums.d.ts +12 -0
  45. package/build/components/DatePicker/enums.js +12 -0
  46. package/build/components/DatePicker/index.d.ts +4 -0
  47. package/build/components/DatePicker/index.js +3 -0
  48. package/build/components/DatePicker/polyfill.d.ts +0 -0
  49. package/build/components/DatePicker/polyfill.js +22 -0
  50. package/build/components/DatePicker/time-picker/animated-math.d.ts +4 -0
  51. package/build/components/DatePicker/time-picker/animated-math.js +19 -0
  52. package/build/components/DatePicker/time-picker/period-native.d.ts +6 -0
  53. package/build/components/DatePicker/time-picker/period-native.js +17 -0
  54. package/build/components/DatePicker/time-picker/period-picker.d.ts +6 -0
  55. package/build/components/DatePicker/time-picker/period-picker.js +10 -0
  56. package/build/components/DatePicker/time-picker/period-web.d.ts +6 -0
  57. package/build/components/DatePicker/time-picker/period-web.js +21 -0
  58. package/build/components/DatePicker/time-picker/wheel-native.d.ts +8 -0
  59. package/build/components/DatePicker/time-picker/wheel-native.js +19 -0
  60. package/build/components/DatePicker/time-picker/wheel-picker/index.d.ts +2 -0
  61. package/build/components/DatePicker/time-picker/wheel-picker/index.js +2 -0
  62. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.d.ts +16 -0
  63. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.js +97 -0
  64. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.d.ts +21 -0
  65. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.js +88 -0
  66. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.d.ts +23 -0
  67. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.js +21 -0
  68. package/build/components/DatePicker/time-picker/wheel-web.d.ts +8 -0
  69. package/build/components/DatePicker/time-picker/wheel-web.js +148 -0
  70. package/build/components/DatePicker/time-picker/wheel.d.ts +8 -0
  71. package/build/components/DatePicker/time-picker/wheel.js +10 -0
  72. package/build/components/DatePicker/utils.d.ts +212 -0
  73. package/build/components/DatePicker/utils.js +391 -0
  74. package/build/components/DatePickerInput/DatePickerInput.d.ts +6 -0
  75. package/build/components/DatePickerInput/DatePickerInput.js +126 -0
  76. package/build/components/DatePickerInput/DatePickerInput.props.d.ts +47 -0
  77. package/build/components/DatePickerInput/DatePickerInput.props.js +1 -0
  78. package/build/components/DatePickerInput/DatePickerInputDoneButton.d.ts +8 -0
  79. package/build/components/DatePickerInput/DatePickerInputDoneButton.js +19 -0
  80. package/build/components/DatePickerInput/DatePickerInputDoneButton.web.d.ts +5 -0
  81. package/build/components/DatePickerInput/DatePickerInputDoneButton.web.js +5 -0
  82. package/build/components/DatePickerInput/index.d.ts +2 -0
  83. package/build/components/DatePickerInput/index.js +1 -0
  84. package/build/components/Input/InputField.d.ts +1 -1
  85. package/build/components/Input/InputField.js +1 -1
  86. package/build/components/Input/InputSlot.d.ts +1 -1
  87. package/build/components/Input/InputSlot.js +3 -3
  88. package/build/components/UnstyledIconButton/UnstyledIconButton.js +2 -2
  89. package/build/components/UnstyledIconButton/UnstyledIconButtonRoot.js +1 -1
  90. package/build/components/index.d.ts +2 -0
  91. package/build/components/index.js +2 -0
  92. package/build/hooks/index.d.ts +4 -3
  93. package/build/hooks/index.js +4 -3
  94. package/build/hooks/usePrevious.d.ts +1 -0
  95. package/build/hooks/usePrevious.js +8 -0
  96. package/build/hooks/useTheme.d.ts +2 -1
  97. package/build/hooks/useTheme.js +2 -2
  98. package/build/tokens/components/dark/date-picker.d.ts +1 -0
  99. package/build/tokens/components/dark/date-picker.js +1 -0
  100. package/build/tokens/components/dark/illustrations.d.ts +7 -0
  101. package/build/tokens/components/dark/illustrations.js +6 -0
  102. package/build/tokens/components/dark/index.d.ts +1 -0
  103. package/build/tokens/components/dark/index.js +1 -0
  104. package/build/tokens/components/dark/segmented-control.d.ts +2 -2
  105. package/build/tokens/components/dark/segmented-control.js +2 -2
  106. package/build/tokens/components/dark/table.d.ts +3 -0
  107. package/build/tokens/components/dark/table.js +3 -0
  108. package/build/tokens/components/light/date-picker.d.ts +1 -0
  109. package/build/tokens/components/light/date-picker.js +1 -0
  110. package/build/tokens/components/light/illustrations.d.ts +7 -0
  111. package/build/tokens/components/light/illustrations.js +6 -0
  112. package/build/tokens/components/light/index.d.ts +1 -0
  113. package/build/tokens/components/light/index.js +1 -0
  114. package/build/tokens/components/light/segmented-control.d.ts +2 -2
  115. package/build/tokens/components/light/segmented-control.js +2 -2
  116. package/build/tokens/components/light/table.d.ts +3 -0
  117. package/build/tokens/components/light/table.js +3 -0
  118. package/build/utils/index.d.ts +1 -0
  119. package/build/utils/index.js +1 -0
  120. package/build/utils/isEqual.d.ts +2 -0
  121. package/build/utils/isEqual.js +29 -0
  122. package/chromatic.config.json +6 -0
  123. package/docs/components/AllComponents.web.tsx +43 -1
  124. package/docs/components/ViewWrap.tsx +32 -0
  125. package/docs/components/index.ts +1 -0
  126. package/docs/getting-started.mdx +6 -6
  127. package/package.json +10 -7
  128. package/src/components/CurrencyInput/CurrencyInput.tsx +2 -1
  129. package/src/components/DatePicker/DatePicker.context.ts +23 -0
  130. package/src/components/DatePicker/DatePicker.docs.mdx +239 -0
  131. package/src/components/DatePicker/DatePicker.props.ts +139 -0
  132. package/src/components/DatePicker/DatePicker.stories.tsx +98 -0
  133. package/src/components/DatePicker/DatePicker.tsx +669 -0
  134. package/src/components/DatePicker/DatePickerCalendar.tsx +41 -0
  135. package/src/components/DatePicker/DatePickerDay.tsx +302 -0
  136. package/src/components/DatePicker/DatePickerDays.tsx +241 -0
  137. package/src/components/DatePicker/DatePickerFooter.tsx +35 -0
  138. package/src/components/DatePicker/DatePickerHeader/DatePickerHeader.props.ts +10 -0
  139. package/src/components/DatePicker/DatePickerHeader/DatePickerMonthButton.tsx +29 -0
  140. package/src/components/DatePicker/DatePickerHeader/DatePickerNextButton.tsx +46 -0
  141. package/src/components/DatePicker/DatePickerHeader/DatePickerPrevButton.tsx +46 -0
  142. package/src/components/DatePicker/DatePickerHeader/DatePickerSelectors.tsx +96 -0
  143. package/src/components/DatePicker/DatePickerHeader/DatePickerTimeButton.tsx +48 -0
  144. package/src/components/DatePicker/DatePickerHeader/DatePickerYearButton.tsx +50 -0
  145. package/src/components/DatePicker/DatePickerHeader/index.tsx +64 -0
  146. package/src/components/DatePicker/DatePickerMonths.tsx +101 -0
  147. package/src/components/DatePicker/DatePickerWeekdays.tsx +49 -0
  148. package/src/components/DatePicker/DatePickerYears.tsx +119 -0
  149. package/src/components/DatePicker/TimePicker.tsx +141 -0
  150. package/src/components/DatePicker/enums.ts +14 -0
  151. package/src/components/DatePicker/index.ts +13 -0
  152. package/src/components/DatePicker/polyfill.ts +21 -0
  153. package/src/components/DatePicker/time-picker/animated-math.ts +33 -0
  154. package/src/components/DatePicker/time-picker/period-native.tsx +34 -0
  155. package/src/components/DatePicker/time-picker/period-picker.tsx +16 -0
  156. package/src/components/DatePicker/time-picker/period-web.tsx +36 -0
  157. package/src/components/DatePicker/time-picker/wheel-native.tsx +37 -0
  158. package/src/components/DatePicker/time-picker/wheel-picker/index.ts +3 -0
  159. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.tsx +132 -0
  160. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.ts +22 -0
  161. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker.tsx +200 -0
  162. package/src/components/DatePicker/time-picker/wheel-web.tsx +181 -0
  163. package/src/components/DatePicker/time-picker/wheel.tsx +18 -0
  164. package/src/components/DatePicker/utils.ts +549 -0
  165. package/src/components/DatePickerInput/DatePickerInput.docs.mdx +237 -0
  166. package/src/components/DatePickerInput/DatePickerInput.props.ts +50 -0
  167. package/src/components/DatePickerInput/DatePickerInput.stories.tsx +178 -0
  168. package/src/components/DatePickerInput/DatePickerInput.tsx +265 -0
  169. package/src/components/DatePickerInput/DatePickerInputDoneButton.tsx +42 -0
  170. package/src/components/DatePickerInput/DatePickerInputDoneButton.web.tsx +7 -0
  171. package/src/components/DatePickerInput/index.ts +2 -0
  172. package/src/components/Icon/Icon.stories.tsx +4 -3
  173. package/src/components/Input/InputField.tsx +0 -2
  174. package/src/components/Input/InputSlot.tsx +14 -3
  175. package/src/components/Modal/Modal.stories.tsx +2 -30
  176. package/src/components/UnstyledIconButton/UnstyledIconButton.tsx +14 -2
  177. package/src/components/UnstyledIconButton/UnstyledIconButtonRoot.tsx +7 -3
  178. package/src/components/index.ts +2 -0
  179. package/src/hooks/index.ts +4 -3
  180. package/src/hooks/usePrevious.ts +9 -0
  181. package/src/hooks/useTheme.ts +4 -3
  182. package/src/tokens/components/dark/date-picker.ts +1 -0
  183. package/src/tokens/components/dark/illustrations.ts +7 -0
  184. package/src/tokens/components/dark/index.ts +1 -0
  185. package/src/tokens/components/dark/segmented-control.ts +2 -2
  186. package/src/tokens/components/dark/table.ts +3 -0
  187. package/src/tokens/components/light/date-picker.ts +1 -0
  188. package/src/tokens/components/light/illustrations.ts +7 -0
  189. package/src/tokens/components/light/index.ts +1 -0
  190. package/src/tokens/components/light/segmented-control.ts +2 -2
  191. package/src/tokens/components/light/table.ts +3 -0
  192. package/src/utils/index.ts +1 -0
  193. package/src/utils/isEqual.ts +30 -0
@@ -0,0 +1,237 @@
1
+ import { Canvas, Controls, Meta } from '@storybook/addon-docs/blocks';
2
+ import { BottomSheetModalProvider, Center, DatePickerInput, FormField } from '../../';
3
+ import { BackToTopButton, UsageWrap, ViewFigmaButton } from '../../../docs/components';
4
+ import * as Stories from './DatePickerInput.stories';
5
+
6
+ <ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=3725-357" />
7
+
8
+ <Meta title="Forms / Date Picker Input" />
9
+
10
+ <BackToTopButton />
11
+
12
+ # Date Picker Input
13
+
14
+ `DatePickerInput` extends the base input to present a calendar trigger for choosing dates while still allowing direct text entry. It keeps the field inline while delegating selection to the `DatePicker` bottom sheet and formats values with Day.js when needed. The default `DD/MM/YYYY` mask auto-inserts separators and surfaces a numeric keypad for faster entry.
15
+
16
+ - [Playground](#playground)
17
+ - [Usage](#usage)
18
+ - [Props](#props)
19
+ - [Formatting and validation](#formatting-and-validation)
20
+ - [Examples](#examples)
21
+ - [Accessibility](#accessibility)
22
+ - [Screen reader support](#screen-reader-support)
23
+ - [Keyboard navigation](#keyboard-navigation)
24
+ - [Best practices](#best-practices)
25
+
26
+ ## Playground
27
+
28
+ <Canvas of={Stories.Playground} />
29
+ <Controls of={Stories.Playground} />
30
+
31
+ ## Usage
32
+
33
+ Wrap the component with `BottomSheetModalProvider` so the underlying picker can render its modal. Provide a controlled `value` if you want to react to changes immediately or let the component manage its own display string.
34
+
35
+ <UsageWrap>
36
+ <BottomSheetModalProvider>
37
+ <Center>
38
+ <DatePickerInput placeholder="DD/MM/YYYY" />
39
+ </Center>
40
+ </BottomSheetModalProvider>
41
+ </UsageWrap>
42
+
43
+ ```tsx
44
+ import { useState } from 'react';
45
+ import {
46
+ BottomSheetModalProvider,
47
+ DatePickerInput,
48
+ type DateType,
49
+ } from '@utilitywarehouse/hearth-react-native';
50
+
51
+ type Props = {};
52
+
53
+ const BookingDateField = ({}: Props) => {
54
+ const [date, setDate] = useState<DateType>();
55
+
56
+ return (
57
+ <BottomSheetModalProvider>
58
+ <DatePickerInput
59
+ value={date}
60
+ onChange={({ date }) => setDate(date ?? undefined)}
61
+ onClear={() => setDate(undefined)}
62
+ placeholder="DD/MM/YYYY"
63
+ />
64
+ </BottomSheetModalProvider>
65
+ );
66
+ };
67
+ ```
68
+
69
+ When used inside `FormField`, validation, disabled, and required states are inherited from the context.
70
+
71
+ ## Props
72
+
73
+ `DatePickerInput` inherits all React Native `TextInput` props (except `children`) and adds the following:
74
+
75
+ | Prop | Type | Default | Description |
76
+ | ------------------- | ---------------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
+ | `validationStatus` | `'initial' \| 'valid' \| 'invalid'` | `'initial'` | Renders the corresponding validation style. Inherited from `FormField` when nested. |
78
+ | `disabled` | `boolean` | `false` | Disables both typing and the calendar trigger button. |
79
+ | `readonly` | `boolean` | `false` | Prevents manual typing while keeping the picker available. |
80
+ | `focused` | `boolean` | `false` | Forces the focused state styling. |
81
+ | `inBottomSheet` | `boolean` | `false` | Uses `BottomSheetTextInput` when rendering inside a bottom sheet. |
82
+ | `format` | `string` | `'DD/MM/YYYY'` | Day.js format string used to render selected dates and parse manual input. Leaving it as `'DD/MM/YYYY'` automatically inserts `/` separators and requests a numeric keypad. |
83
+ | `openButtonLabel` | `string` | `'Open date picker'` | Accessible label read by screen readers for the calendar trigger button. |
84
+ | `autoCloseOnSelect` | `boolean` | `true` | Closes the picker automatically after a date is chosen. |
85
+ | `datePickerProps` | `Omit<DatePickerSingleProps, 'mode' \| 'date' \| 'onChange' \| 'ref'>` | `-` | Forwards props to the underlying `DatePicker` (e.g. `minDate`, `maxDate`, `timeZone`). |
86
+ | `onChange` | `(payload: { date: DateType }) => void` | `-` | Fired whenever a valid date is parsed from typing or picked via the calendar. |
87
+ | `onClear` | `() => void` | `-` | Called after the clear action resets the input. Also displays a trailing clear button when provided. |
88
+
89
+ ## Formatting and validation
90
+
91
+ ### Automatic formatting
92
+
93
+ When `format` is left as `'DD/MM/YYYY'`, the input automatically inserts `/` separators as people type and requests a numeric keypad on supported platforms. This keeps manual entry quick while matching the preferred UK date layout.
94
+
95
+ On iOS, a contextual **Done** button appears above the keyboard so people can dismiss it as soon as they're finished typing.
96
+
97
+ ### Handling validation yourself
98
+
99
+ `DatePickerInput` doesn't display validation styles by itself. Pair it with `FormField` (or your own state) to surface errors whenever the typed value doesn't match your expected format.
100
+
101
+ ```tsx
102
+ import { useState } from 'react';
103
+ import dayjs from 'dayjs';
104
+ import { DatePickerInput, FormField, type DateType } from '@utilitywarehouse/hearth-react-native';
105
+
106
+ const TravelDateField = () => {
107
+ const [value, setValue] = useState<DateType>();
108
+ const [rawInput, setRawInput] = useState('');
109
+ const [status, setStatus] = useState<'initial' | 'invalid'>('initial');
110
+
111
+ const handleBlur = () => {
112
+ if (!rawInput) {
113
+ setStatus('initial');
114
+ return;
115
+ }
116
+
117
+ const isValid = dayjs(rawInput, 'DD/MM/YYYY', true).isValid();
118
+ setStatus(isValid ? 'initial' : 'invalid');
119
+ };
120
+
121
+ return (
122
+ <FormField
123
+ label="Travel date"
124
+ helperText="Use DD/MM/YYYY"
125
+ validationStatus={status}
126
+ invalidText="Enter a valid date"
127
+ >
128
+ <DatePickerInput
129
+ value={value}
130
+ onChange={({ date }) => {
131
+ setValue(date ?? undefined);
132
+ if (date) {
133
+ setStatus('initial');
134
+ }
135
+ }}
136
+ onChangeText={setRawInput}
137
+ onBlur={handleBlur}
138
+ onClear={() => {
139
+ setValue(undefined);
140
+ setRawInput('');
141
+ setStatus('initial');
142
+ }}
143
+ />
144
+ </FormField>
145
+ );
146
+ };
147
+ ```
148
+
149
+ Swap `'DD/MM/YYYY'` in the example for the `format` you're using, and reuse the same rule inside `dayjs(..., format, true)` to keep the behaviour aligned.
150
+
151
+ Selection through the calendar always returns a JavaScript `Date` that is reformatted according to `format` when displayed in the field.
152
+
153
+ ## Examples
154
+
155
+ ### With `FormField`
156
+
157
+ <UsageWrap>
158
+ <BottomSheetModalProvider>
159
+ <Center>
160
+ <FormField label="Travel date" helperText="Choose a departure day">
161
+ <DatePickerInput onClear={() => {}} />
162
+ </FormField>
163
+ </Center>
164
+ </BottomSheetModalProvider>
165
+ </UsageWrap>
166
+
167
+ ```tsx
168
+ <FormField label="Travel date" helperText="Choose a departure day">
169
+ <DatePickerInput onClear={() => {}} />
170
+ </FormField>
171
+ ```
172
+
173
+ ### Custom formatting
174
+
175
+ <UsageWrap>
176
+ <BottomSheetModalProvider>
177
+ <Center>
178
+ <DatePickerInput format="MMM D, YYYY" placeholder="MMM D, YYYY" />
179
+ </Center>
180
+ </BottomSheetModalProvider>
181
+ </UsageWrap>
182
+
183
+ ```tsx
184
+ <DatePickerInput format="MMM D, YYYY" placeholder="MMM D, YYYY" />
185
+ ```
186
+
187
+ ## Accessibility
188
+
189
+ `DatePickerInput` is designed to work seamlessly with VoiceOver (iOS), TalkBack (Android), and other assistive technologies:
190
+
191
+ ### Screen reader support
192
+
193
+ - **Text input field**:
194
+
195
+ - Announces as "Date input" by default (customizable via `accessibilityLabel`).
196
+ - Provides helpful context: "Enter the date in DD/MM/YYYY format" (or your custom format) via `accessibilityHint`.
197
+ - Announces disabled and readonly states appropriately.
198
+ - When focused, screen readers identify it as an editable text field.
199
+
200
+ - **Calendar trigger button**:
201
+
202
+ - Announces as "Open date picker" by default (customizable via `openButtonLabel`).
203
+ - Includes helpful hint: "Opens the date picker calendar".
204
+ - Clearly identified as a button with `accessibilityRole="button"`.
205
+ - Respects disabled/readonly state from the input or surrounding `FormField`.
206
+
207
+ - **Clear button** (when `onClear` is provided):
208
+ - Announces as "Clear date".
209
+ - Includes hint: "Removes the current date".
210
+ - Only appears when there's a value to clear and the input isn't readonly/disabled.
211
+
212
+ ### Keyboard and input management
213
+
214
+ - Both the text input and action buttons are individually focusable and navigable.
215
+ - The `DD/MM/YYYY` format triggers a numeric keypad on iOS/Android for faster entry.
216
+ - On iOS, a contextual **Done** button appears above the keyboard to dismiss it when finished typing.
217
+ - Custom formats can be specified to match your application's needs while maintaining accessibility.
218
+
219
+ ### Integration with DatePicker
220
+
221
+ - When the calendar opens, screen readers announce "Date picker opened" and automatically focus on the calendar content.
222
+ - All calendar navigation (days, months, years) is fully accessible with descriptive labels.
223
+ - Selected dates are announced with full context: "Monday, October 8, selected".
224
+ - The underlying `DatePicker` follows platform-specific accessibility patterns for optimal screen reader experience.
225
+
226
+ ### Validation and state
227
+
228
+ - Validation state is inherited from surrounding `FormField` or controlled via `validationStatus` prop.
229
+ - Invalid states are properly communicated through the `FormField`'s error messaging system.
230
+ - The `importantForAccessibility` prop is set to `'yes'` by default to ensure the input is prioritized by screen readers.
231
+
232
+ ### Best practices
233
+
234
+ - Always pair with `FormField` for proper label, helper text, and error message announcements.
235
+ - Use `openButtonLabel` to customize the calendar button announcement if the default doesn't fit your use case.
236
+ - Provide clear validation feedback through `FormField` when manual date entry doesn't match the expected format.
237
+ - Test with VoiceOver and TalkBack to ensure the date entry flow works smoothly in your specific context.
@@ -0,0 +1,50 @@
1
+ import type { TextInputProps, ViewProps } from 'react-native';
2
+ import type { DatePickerSingleProps } from '../DatePicker/DatePicker';
3
+ import type { DateType } from '../DatePicker/DatePicker.props';
4
+
5
+ export interface DatePickerInputBaseProps {
6
+ disabled?: boolean;
7
+ validationStatus?: 'initial' | 'valid' | 'invalid';
8
+ readonly?: boolean;
9
+ focused?: boolean;
10
+ placeholder?: string;
11
+ inBottomSheet?: boolean;
12
+ required?: boolean;
13
+ /**
14
+ * Controls how the selected date is formatted when displayed inside the input.
15
+ * Accepts any Day.js format string. When left as the default `DD/MM/YYYY`,
16
+ * the input automatically inserts separators as people type.
17
+ */
18
+ format?: string;
19
+ /**
20
+ * Accessible label announced when activating the calendar trigger button.
21
+ */
22
+ openButtonLabel?: string;
23
+ /**
24
+ * When true (default), the calendar sheet is dismissed as soon as a date is picked.
25
+ */
26
+ autoCloseOnSelect?: boolean;
27
+ /**
28
+ * Additional props forwarded to the underlying DatePicker instance.
29
+ */
30
+ datePickerProps?: Omit<DatePickerSingleProps, 'mode' | 'date' | 'onChange' | 'ref'>;
31
+ /**
32
+ * Handles cleared input values.
33
+ */
34
+ onClear?: () => void;
35
+ }
36
+
37
+ export type DatePickerInputProps = DatePickerInputBaseProps &
38
+ Omit<TextInputProps, 'value' | 'onChange' | 'children'> &
39
+ ViewProps & {
40
+ /**
41
+ * Controlled date value. Accepts Date, string, number or Day.js instances.
42
+ */
43
+ value?: DateType;
44
+ /**
45
+ * Fired after a valid date is parsed either from typing or the picker selection.
46
+ */
47
+ onChange?: (params: { date: DateType }) => void;
48
+ };
49
+
50
+ export default DatePickerInputProps;
@@ -0,0 +1,178 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { useState } from 'react';
3
+ import { Platform } from 'react-native';
4
+ import { DatePickerInput, View } from '..';
5
+ import { VariantTitle, ViewWrap } from '../../../docs/components';
6
+ import type { DateType } from '../DatePicker';
7
+ import { Flex } from '../Flex';
8
+ import { FormField } from '../FormField';
9
+
10
+ const meta = {
11
+ title: 'Stories / DatePickerInput',
12
+ component: DatePickerInput,
13
+ parameters: {
14
+ layout: 'centered',
15
+ },
16
+ argTypes: {
17
+ format: {
18
+ control: 'text',
19
+ description: 'Day.js format string used to render and parse the value',
20
+ defaultValue: 'DD/MM/YYYY',
21
+ },
22
+ validationStatus: {
23
+ control: 'select',
24
+ options: ['initial', 'valid', 'invalid'],
25
+ description: 'Manually set the validation status',
26
+ defaultValue: 'initial',
27
+ },
28
+ disabled: {
29
+ control: 'boolean',
30
+ description: 'Disable the input and trigger button',
31
+ defaultValue: false,
32
+ },
33
+ readonly: {
34
+ control: 'boolean',
35
+ description: 'Make the input read-only (typing disabled, picker still accessible)',
36
+ defaultValue: false,
37
+ },
38
+ focused: {
39
+ control: 'boolean',
40
+ description: 'Force the focused visual state',
41
+ defaultValue: false,
42
+ },
43
+ openButtonLabel: {
44
+ control: 'text',
45
+ description: 'Accessible label for the calendar trigger button',
46
+ defaultValue: 'Open date picker',
47
+ },
48
+ autoCloseOnSelect: {
49
+ control: 'boolean',
50
+ description: 'Automatically close the picker after selecting a date',
51
+ defaultValue: false,
52
+ },
53
+ },
54
+ args: {
55
+ format: 'DD/MM/YYYY',
56
+ validationStatus: 'initial',
57
+ disabled: false,
58
+ readonly: false,
59
+ focused: false,
60
+ openButtonLabel: 'Open date picker',
61
+ autoCloseOnSelect: false,
62
+ placeholder: 'DD/MM/YYYY',
63
+ },
64
+ } satisfies Meta<typeof DatePickerInput>;
65
+
66
+ export default meta;
67
+
68
+ type Story = StoryObj<typeof meta>;
69
+
70
+ export const Playground: Story = {
71
+ render: args => {
72
+ const [selected, setSelected] = useState<DateType>();
73
+
74
+ const picker = (
75
+ <DatePickerInput
76
+ {...args}
77
+ value={selected}
78
+ onChange={({ date }) => setSelected(date ?? undefined)}
79
+ onClear={() => setSelected(undefined)}
80
+ />
81
+ );
82
+
83
+ if (Platform.OS !== 'web') {
84
+ return picker;
85
+ }
86
+
87
+ return (
88
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : {}}>
89
+ <ViewWrap>{picker}</ViewWrap>
90
+ </View>
91
+ );
92
+ },
93
+ };
94
+
95
+ export const CustomFormat: Story = {
96
+ parameters: {
97
+ controls: { include: ['format'] },
98
+ },
99
+ args: {
100
+ format: 'YYYY-MM-DD',
101
+ placeholder: 'YYYY-MM-DD',
102
+ },
103
+ render: args => {
104
+ const [selected, setSelected] = useState<DateType>(new Date());
105
+
106
+ const picker = (
107
+ <DatePickerInput
108
+ {...args}
109
+ value={selected}
110
+ onChange={({ date }) => setSelected(date ?? undefined)}
111
+ onClear={() => setSelected(undefined)}
112
+ />
113
+ );
114
+
115
+ if (Platform.OS !== 'web') {
116
+ return picker;
117
+ }
118
+
119
+ return <ViewWrap>{picker}</ViewWrap>;
120
+ },
121
+ };
122
+
123
+ export const States: Story = {
124
+ parameters: {
125
+ controls: { include: [] },
126
+ },
127
+ render: () => {
128
+ const [withValue, setWithValue] = useState<DateType>(new Date());
129
+ const [clearableDate, setClearableDate] = useState<DateType>(new Date());
130
+ const [formFieldDate, setFormFieldDate] = useState<DateType>();
131
+
132
+ return (
133
+ <Flex direction="column" space="lg">
134
+ <VariantTitle title="Default">
135
+ <DatePickerInput placeholder="DD/MM/YYYY" />
136
+ </VariantTitle>
137
+ <VariantTitle title="With value">
138
+ <DatePickerInput
139
+ value={withValue}
140
+ onChange={({ date }) => setWithValue(date ?? undefined)}
141
+ onClear={() => setWithValue(undefined)}
142
+ />
143
+ </VariantTitle>
144
+ <VariantTitle title="Disabled">
145
+ <DatePickerInput disabled placeholder="DD/MM/YYYY" />
146
+ </VariantTitle>
147
+ <VariantTitle title="Readonly">
148
+ <DatePickerInput readonly value={withValue} />
149
+ </VariantTitle>
150
+ <VariantTitle title="Invalid">
151
+ <DatePickerInput validationStatus="invalid" placeholder="DD/MM/YYYY" />
152
+ </VariantTitle>
153
+ <VariantTitle title="Valid">
154
+ <DatePickerInput validationStatus="valid" placeholder="DD/MM/YYYY" />
155
+ </VariantTitle>
156
+ <VariantTitle title="Custom format">
157
+ <DatePickerInput format="MMM D, YYYY" placeholder="MMM D, YYYY" />
158
+ </VariantTitle>
159
+ <VariantTitle title="With clear action">
160
+ <DatePickerInput
161
+ value={clearableDate}
162
+ onChange={({ date }) => setClearableDate(date ?? undefined)}
163
+ onClear={() => setClearableDate(undefined)}
164
+ />
165
+ </VariantTitle>
166
+ <VariantTitle title="Inside FormField">
167
+ <FormField label="Booking date" helperText="Pick a date">
168
+ <DatePickerInput
169
+ value={formFieldDate}
170
+ onChange={({ date }) => setFormFieldDate(date ?? undefined)}
171
+ onClear={() => setFormFieldDate(undefined)}
172
+ />
173
+ </FormField>
174
+ </VariantTitle>
175
+ </Flex>
176
+ );
177
+ },
178
+ };