@ceed/cds 1.24.1-next.3 → 1.26.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 (65) hide show
  1. package/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
  2. package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  3. package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
  4. package/dist/components/DataTable/components.d.ts +2 -1
  5. package/dist/components/DataTable/hooks.d.ts +1 -1
  6. package/dist/components/DataTable/styled.d.ts +3 -1
  7. package/dist/components/DataTable/types.d.ts +11 -0
  8. package/dist/components/DataTable/utils.d.ts +2 -2
  9. package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
  10. package/dist/components/RadioTileGroup/index.d.ts +3 -0
  11. package/dist/components/data-display/DataTable.md +177 -1
  12. package/dist/components/data-display/InfoSign.md +74 -91
  13. package/dist/components/data-display/Typography.md +411 -94
  14. package/dist/components/feedback/CircularProgress.md +257 -0
  15. package/dist/components/feedback/Dialog.md +76 -62
  16. package/dist/components/feedback/Modal.md +430 -138
  17. package/dist/components/feedback/Skeleton.md +280 -0
  18. package/dist/components/feedback/llms.txt +2 -0
  19. package/dist/components/index.d.ts +1 -0
  20. package/dist/components/inputs/Autocomplete.md +356 -107
  21. package/dist/components/inputs/ButtonGroup.md +115 -104
  22. package/dist/components/inputs/CurrencyInput.md +183 -5
  23. package/dist/components/inputs/DatePicker.md +108 -431
  24. package/dist/components/inputs/DateRangePicker.md +131 -492
  25. package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
  26. package/dist/components/inputs/FormControl.md +361 -0
  27. package/dist/components/inputs/IconButton.md +137 -88
  28. package/dist/components/inputs/Input.md +204 -73
  29. package/dist/components/inputs/MonthPicker.md +95 -422
  30. package/dist/components/inputs/MonthRangePicker.md +89 -466
  31. package/dist/components/inputs/PercentageInput.md +185 -16
  32. package/dist/components/inputs/RadioButton.md +163 -35
  33. package/dist/components/inputs/RadioList.md +241 -0
  34. package/dist/components/inputs/RadioTileGroup.md +507 -0
  35. package/dist/components/inputs/Select.md +222 -326
  36. package/dist/components/inputs/Slider.md +334 -0
  37. package/dist/components/inputs/Switch.md +143 -376
  38. package/dist/components/inputs/Textarea.md +213 -10
  39. package/dist/components/inputs/Uploader/Uploader.md +145 -66
  40. package/dist/components/inputs/llms.txt +4 -0
  41. package/dist/components/navigation/Breadcrumbs.md +57 -308
  42. package/dist/components/navigation/Drawer.md +180 -0
  43. package/dist/components/navigation/Dropdown.md +98 -215
  44. package/dist/components/navigation/IconMenuButton.md +40 -502
  45. package/dist/components/navigation/InsetDrawer.md +281 -650
  46. package/dist/components/navigation/Link.md +31 -348
  47. package/dist/components/navigation/Menu.md +92 -285
  48. package/dist/components/navigation/MenuButton.md +55 -448
  49. package/dist/components/navigation/Pagination.md +47 -338
  50. package/dist/components/navigation/Stepper.md +160 -28
  51. package/dist/components/navigation/Tabs.md +57 -316
  52. package/dist/components/surfaces/Accordions.md +49 -804
  53. package/dist/components/surfaces/Card.md +97 -157
  54. package/dist/components/surfaces/Divider.md +83 -234
  55. package/dist/components/surfaces/Sheet.md +153 -328
  56. package/dist/guides/ThemeProvider.md +89 -0
  57. package/dist/guides/llms.txt +9 -0
  58. package/dist/index.browser.js +224 -0
  59. package/dist/index.browser.js.map +7 -0
  60. package/dist/index.cjs +726 -425
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.js +641 -396
  63. package/dist/llms.txt +9 -0
  64. package/framer/index.js +1 -163
  65. package/package.json +22 -17
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- MonthPicker is a form input component that allows users to select a specific month and year from a calendar popup. Unlike DatePicker which allows day-level selection, MonthPicker focuses on month-level granularity. It displays months in a grid format for easy selection and is ideal for scenarios like billing periods, report months, subscription dates, or any use case where day-level precision is unnecessary.
5
+ MonthPicker is a form input component that allows users to select a specific month and year from a calendar popup. Unlike DatePicker which provides day-level selection, MonthPicker focuses on month-level granularity and displays months in a grid format for quick selection.
6
+
7
+ It is ideal for scenarios like billing periods, report months, subscription dates, or any use case where day-level precision is unnecessary. The component supports multiple value formats and display formats, controlled and uncontrolled modes, and date range constraints.
6
8
 
7
9
  ```tsx
8
10
  <MonthPicker />
@@ -25,15 +27,6 @@ MonthPicker is a form input component that allows users to select a specific mon
25
27
  | format | — | — |
26
28
  | onChange | — | — |
27
29
 
28
- > ⚠️ **Usage Warning** ⚠️
29
- >
30
- > MonthPicker has some unique formatting behaviors:
31
- >
32
- > - **Internal value format**: Values internally use full date format (`"YYYY/MM/DD"`) with day set to `01`
33
- > - **Display vs Value**: Input displays `YYYY/MM` but `onChange` receives `YYYY/MM/01`
34
- > - **Format consistency**: Ensure your `value` prop matches the `format` prop
35
- > - **displayFormat options**: Supports special token `MMMM` for full month names
36
-
37
30
  ## Usage
38
31
 
39
32
  ```tsx
@@ -52,19 +45,14 @@ function MonthForm() {
52
45
  }
53
46
  ```
54
47
 
55
- ## Examples
56
-
57
- ### Playground
58
-
59
- Interactive example with all controls.
60
-
61
- ```tsx
62
- <MonthPicker />
63
- ```
48
+ > 💡 **Use built-in form props**
49
+ >
50
+ > This component natively supports form elements such as `label` and `helperText` props.
51
+ > When building forms, use these built-in props instead of manually composing labels and helper text with Typography.
64
52
 
65
- ### Sizes
53
+ ## Sizes
66
54
 
67
- MonthPicker supports three sizes for different layouts.
55
+ MonthPicker supports three sizes (`sm`, `md`, `lg`) for different layouts and contexts.
68
56
 
69
57
  ```tsx
70
58
  <Stack gap={2}>
@@ -74,9 +62,11 @@ MonthPicker supports three sizes for different layouts.
74
62
  </Stack>
75
63
  ```
76
64
 
65
+ ## Form Field Features
66
+
77
67
  ### With Label
78
68
 
79
- Add a label above the month picker.
69
+ Add a label above the month picker to indicate the field purpose.
80
70
 
81
71
  ```tsx
82
72
  <MonthPicker label="Date" />
@@ -84,7 +74,7 @@ Add a label above the month picker.
84
74
 
85
75
  ### With Helper Text
86
76
 
87
- Provide additional guidance below the input.
77
+ Provide additional guidance below the input to help users understand the expected selection.
88
78
 
89
79
  ```tsx
90
80
  <MonthPicker
@@ -93,41 +83,43 @@ Provide additional guidance below the input.
93
83
  />
94
84
  ```
95
85
 
96
- ### Error State
86
+ ### Required Field
97
87
 
98
- Show validation errors with error styling.
88
+ Mark the field as required in forms. An asterisk indicator is displayed alongside the label.
99
89
 
100
90
  ```tsx
101
91
  <MonthPicker
102
- label="Date"
103
- helperText="Please select a date"
104
- error
92
+ label="Label"
93
+ helperText="I'm helper text"
94
+ required
105
95
  />
106
96
  ```
107
97
 
108
- ### Required Field
98
+ ### Error State
109
99
 
110
- Mark the field as required in forms.
100
+ Display validation errors with error styling applied to the input, label, and helper text.
111
101
 
112
102
  ```tsx
113
103
  <MonthPicker
114
- label="Label"
115
- helperText="I'm helper text"
116
- required
104
+ label="Date"
105
+ helperText="Please select a date"
106
+ error
117
107
  />
118
108
  ```
119
109
 
120
110
  ### Disabled
121
111
 
122
- Prevent user interaction when disabled.
112
+ Prevent user interaction when the picker is disabled.
123
113
 
124
114
  ```tsx
125
115
  <MonthPicker disabled />
126
116
  ```
127
117
 
118
+ ## Date Constraints
119
+
128
120
  ### Minimum Date
129
121
 
130
- Restrict selection to months on or after a minimum date.
122
+ Restrict selection to months on or after a minimum date. Months before the limit are visually disabled.
131
123
 
132
124
  ```tsx
133
125
  <MonthPicker minDate="2024-04-10" />
@@ -135,7 +127,7 @@ Restrict selection to months on or after a minimum date.
135
127
 
136
128
  ### Maximum Date
137
129
 
138
- Restrict selection to months on or before a maximum date.
130
+ Restrict selection to months on or before a maximum date. Months after the limit are visually disabled.
139
131
 
140
132
  ```tsx
141
133
  <MonthPicker maxDate="2024-04-10" />
@@ -143,7 +135,7 @@ Restrict selection to months on or before a maximum date.
143
135
 
144
136
  ### Disable Future
145
137
 
146
- Prevent selection of months in the future.
138
+ Prevent selection of any month in the future, relative to the current date.
147
139
 
148
140
  ```tsx
149
141
  <MonthPicker disableFuture />
@@ -151,15 +143,17 @@ Prevent selection of months in the future.
151
143
 
152
144
  ### Disable Past
153
145
 
154
- Prevent selection of months in the past.
146
+ Prevent selection of any month in the past, relative to the current date.
155
147
 
156
148
  ```tsx
157
149
  <MonthPicker disablePast />
158
150
  ```
159
151
 
152
+ ## Controlled vs Uncontrolled
153
+
160
154
  ### Controlled
161
155
 
162
- Parent component manages the month state.
156
+ The parent component manages the month state via the `value` and `onChange` props. External buttons or logic can programmatically update the selected value.
163
157
 
164
158
  ```tsx
165
159
  <Stack gap={2}>
@@ -178,7 +172,7 @@ Parent component manages the month state.
178
172
 
179
173
  ### Uncontrolled
180
174
 
181
- Component manages its own state internally.
175
+ The component manages its own state internally using `defaultValue`. The parent can still listen for changes via `onChange`.
182
176
 
183
177
  ```tsx
184
178
  <MonthPicker
@@ -188,9 +182,11 @@ Component manages its own state internally.
188
182
  />
189
183
  ```
190
184
 
191
- ### With Formats
185
+ ## Formats
192
186
 
193
- Different value formats for the `onChange` event.
187
+ ### Value Formats
188
+
189
+ The `format` prop determines the shape of the value emitted by `onChange`. Each format includes a day component (set to `01`). The display always defaults to `YYYY/MM` unless `displayFormat` is also set.
194
190
 
195
191
  ```tsx
196
192
  <Stack gap={2}>
@@ -203,9 +199,9 @@ Different value formats for the `onChange` event.
203
199
  </Stack>
204
200
  ```
205
201
 
206
- ### With Display Formats
202
+ ### Display Formats
207
203
 
208
- Different display formats including full month names.
204
+ The `displayFormat` prop controls what users see in the input field, independent of the underlying value format. Supports the special token `MMMM` for full month names.
209
205
 
210
206
  ```tsx
211
207
  <Stack gap={2}>
@@ -236,24 +232,21 @@ Different display formats including full month names.
236
232
  </Stack>
237
233
  ```
238
234
 
239
- ## When to Use
240
-
241
- ### ✅ Good Use Cases
242
-
243
- - **Billing periods**: Monthly invoices, subscription billing cycles
244
- - **Report filtering**: Monthly reports, quarterly summaries
245
- - **Fiscal periods**: Fiscal months, budget allocation periods
246
- - **Expiration dates**: Credit card expiry (month/year)
247
- - **Historical data**: Selecting past months for data analysis
248
- - **Scheduling**: Monthly recurring events
235
+ ### With Reset Button
249
236
 
250
- ### When Not to Use
237
+ A controlled example with an external reset button to clear the selected value.
251
238
 
252
- - **Specific dates**: Use DatePicker when day selection matters
253
- - **Date ranges**: Use DateRangePicker or MonthRangePicker
254
- - **Week selection**: Consider a custom week picker
255
- - **Quarter selection**: Consider a dropdown with Q1-Q4 options
256
- - **Year only**: Use a year picker or simple dropdown
239
+ ```tsx
240
+ <div style={{
241
+ display: 'flex',
242
+ gap: '10px'
243
+ }}>
244
+ <MonthPicker {...props} value={value} onChange={event => {
245
+ setValue(event.target.value);
246
+ }} />
247
+ <Button onClick={() => setValue('')}>Reset</Button>
248
+ </div>
249
+ ```
257
250
 
258
251
  ## Common Use Cases
259
252
 
@@ -263,55 +256,32 @@ Different display formats including full month names.
263
256
  function BillingPeriodSelector() {
264
257
  const [billingMonth, setBillingMonth] = useState('');
265
258
 
266
- const handleGenerate = () => {
267
- const [year, month] = billingMonth.split('/');
268
- generateInvoice({ year: parseInt(year), month: parseInt(month) });
269
- };
270
-
271
259
  return (
272
- <Stack gap={2}>
273
- <MonthPicker
274
- label="Billing Period"
275
- value={billingMonth}
276
- onChange={(e) => setBillingMonth(e.target.value)}
277
- disableFuture
278
- helperText="Select the month for invoice generation"
279
- />
280
- <Button onClick={handleGenerate} disabled={!billingMonth}>
281
- Generate Invoice
282
- </Button>
283
- </Stack>
260
+ <MonthPicker
261
+ label="Billing Period"
262
+ value={billingMonth}
263
+ onChange={(e) => setBillingMonth(e.target.value)}
264
+ disableFuture
265
+ helperText="Select the month for invoice generation"
266
+ />
284
267
  );
285
268
  }
286
269
  ```
287
270
 
288
- ### Report Month Filter
271
+ ### Report Month Filter with Display Format
289
272
 
290
273
  ```tsx
291
- function ReportFilters({ onFilter }) {
274
+ function ReportFilter() {
292
275
  const [reportMonth, setReportMonth] = useState('');
293
276
 
294
- // Get current month as default
295
- useEffect(() => {
296
- const now = new Date();
297
- const year = now.getFullYear();
298
- const month = String(now.getMonth() + 1).padStart(2, '0');
299
- setReportMonth(`${year}/${month}/01`);
300
- }, []);
301
-
302
277
  return (
303
- <Stack direction="row" gap={2} alignItems="flex-end">
304
- <MonthPicker
305
- label="Report Month"
306
- value={reportMonth}
307
- onChange={(e) => setReportMonth(e.target.value)}
308
- disableFuture
309
- displayFormat="MMMM YYYY" // Shows "January 2024"
310
- />
311
- <Button onClick={() => onFilter(reportMonth)}>
312
- Generate Report
313
- </Button>
314
- </Stack>
278
+ <MonthPicker
279
+ label="Report Month"
280
+ value={reportMonth}
281
+ onChange={(e) => setReportMonth(e.target.value)}
282
+ disableFuture
283
+ displayFormat="MMMM YYYY"
284
+ />
315
285
  );
316
286
  }
317
287
  ```
@@ -322,7 +292,6 @@ function ReportFilters({ onFilter }) {
322
292
  function CreditCardExpiry() {
323
293
  const [expiry, setExpiry] = useState('');
324
294
 
325
- // Get current date for minimum
326
295
  const today = new Date();
327
296
  const minDate = `${today.getFullYear()}/${String(today.getMonth() + 1).padStart(2, '0')}/01`;
328
297
 
@@ -332,7 +301,7 @@ function CreditCardExpiry() {
332
301
  value={expiry}
333
302
  onChange={(e) => setExpiry(e.target.value)}
334
303
  minDate={minDate}
335
- displayFormat="MM/YYYY" // Common credit card format
304
+ displayFormat="MM/YYYY"
336
305
  helperText="Enter card expiration date"
337
306
  required
338
307
  />
@@ -340,355 +309,59 @@ function CreditCardExpiry() {
340
309
  }
341
310
  ```
342
311
 
343
- ### Fiscal Period Selector
344
-
345
- ```tsx
346
- function FiscalPeriodSelector({ fiscalYearStart = 4 }) { // April
347
- const [selectedMonth, setSelectedMonth] = useState('');
348
- const [fiscalPeriod, setFiscalPeriod] = useState('');
349
-
350
- // Calculate fiscal period from selected month
351
- useEffect(() => {
352
- if (!selectedMonth) return;
353
-
354
- const [year, month] = selectedMonth.split('/').map(Number);
355
- const fiscalMonth = month >= fiscalYearStart
356
- ? month - fiscalYearStart + 1
357
- : month + (12 - fiscalYearStart + 1);
358
- const fiscalYear = month >= fiscalYearStart ? year : year - 1;
359
-
360
- setFiscalPeriod(`FY${fiscalYear} P${fiscalMonth}`);
361
- }, [selectedMonth, fiscalYearStart]);
362
-
363
- return (
364
- <Stack gap={2}>
365
- <MonthPicker
366
- label="Select Period"
367
- value={selectedMonth}
368
- onChange={(e) => setSelectedMonth(e.target.value)}
369
- displayFormat="MMMM YYYY"
370
- />
371
- {fiscalPeriod && (
372
- <Typography level="body-sm">Fiscal Period: {fiscalPeriod}</Typography>
373
- )}
374
- </Stack>
375
- );
376
- }
377
- ```
378
-
379
- ### Monthly Budget Allocation
380
-
381
- ```tsx
382
- function BudgetAllocation({ departments }) {
383
- const [selectedMonth, setSelectedMonth] = useState('');
384
- const [budgets, setBudgets] = useState({});
385
-
386
- // Restrict to future months only
387
- const today = new Date();
388
- const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
389
- const minDate = `${nextMonth.getFullYear()}/${String(nextMonth.getMonth() + 1).padStart(2, '0')}/01`;
390
-
391
- return (
392
- <form>
393
- <MonthPicker
394
- label="Budget Month"
395
- value={selectedMonth}
396
- onChange={(e) => setSelectedMonth(e.target.value)}
397
- minDate={minDate}
398
- displayFormat="MMMM YYYY"
399
- helperText="Select month for budget allocation"
400
- required
401
- />
402
-
403
- {selectedMonth && departments.map((dept) => (
404
- <CurrencyInput
405
- key={dept.id}
406
- label={`${dept.name} Budget`}
407
- value={budgets[dept.id] || ''}
408
- onChange={(value) => setBudgets(prev => ({
409
- ...prev,
410
- [dept.id]: value
411
- }))}
412
- />
413
- ))}
414
-
415
- <Button type="submit">Allocate Budget</Button>
416
- </form>
417
- );
418
- }
419
- ```
420
-
421
- ### Year-Month Comparison
422
-
423
- ```tsx
424
- function MonthComparison() {
425
- const [month1, setMonth1] = useState('');
426
- const [month2, setMonth2] = useState('');
427
-
428
- return (
429
- <Stack gap={2}>
430
- <Stack direction="row" gap={2}>
431
- <MonthPicker
432
- label="Compare From"
433
- value={month1}
434
- onChange={(e) => setMonth1(e.target.value)}
435
- disableFuture
436
- />
437
- <MonthPicker
438
- label="Compare To"
439
- value={month2}
440
- onChange={(e) => setMonth2(e.target.value)}
441
- disableFuture
442
- minDate={month1} // Must be after first month
443
- />
444
- </Stack>
445
- <Button disabled={!month1 || !month2}>
446
- Compare Months
447
- </Button>
448
- </Stack>
449
- );
450
- }
451
- ```
452
-
453
- ## Props and Customization
454
-
455
- ### Key Props
456
-
457
- | Prop | Type | Default | Description |
458
- | --------------- | ----------------------------------------------------------- | -------------- | --------------------------------------- |
459
- | `value` | `string` | - | Controlled value (format: `YYYY/MM/DD`) |
460
- | `defaultValue` | `string` | - | Default value for uncontrolled mode |
461
- | `onChange` | `(e: { target: { name?: string; value: string } }) => void` | - | Change handler |
462
- | `format` | `string` | `'YYYY/MM/DD'` | Format for `value` and `onChange` |
463
- | `displayFormat` | `string` | `'YYYY/MM'` | Format displayed in the input |
464
- | `label` | `string` | - | Label text |
465
- | `helperText` | `string` | - | Helper text below input |
466
- | `error` | `boolean` | `false` | Error state |
467
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Component size |
468
- | `disabled` | `boolean` | `false` | Disabled state |
469
- | `required` | `boolean` | `false` | Required field indicator |
470
- | `minDate` | `string` | - | Minimum selectable month |
471
- | `maxDate` | `string` | - | Maximum selectable month |
472
- | `disableFuture` | `boolean` | `false` | Disable all future months |
473
- | `disablePast` | `boolean` | `false` | Disable all past months |
474
-
475
- ### Display Format Tokens
476
-
477
- | Token | Description | Example |
478
- | ------ | --------------- | ------- |
479
- | `YYYY` | 4-digit year | 2024 |
480
- | `MM` | 2-digit month | 04 |
481
- | `DD` | 2-digit day | 01 |
482
- | `MMMM` | Full month name | January |
483
-
484
- Common display format patterns:
485
-
486
- - `YYYY/MM` - Default (2024/04)
487
- - `YYYY-MM` - ISO-like (2024-04)
488
- - `MM/YYYY` - European (04/2024)
489
- - `MMMM YYYY` - Full name (April 2024)
490
- - `YYYY.MM` - Period separator (2024.04)
491
-
492
- ### Value Format Understanding
493
-
494
- ```tsx
495
- // MonthPicker always uses day=01 in values
496
- // Even though display shows "2024/04", the value is "2024/04/01"
497
-
498
- <MonthPicker
499
- value="2024/04/01" // Must include day
500
- displayFormat="YYYY/MM" // Shows "2024/04"
501
- onChange={(e) => {
502
- console.log(e.target.value); // "2024/04/01"
503
- // Extract just year and month
504
- const [year, month] = e.target.value.split('/');
505
- console.log(`${year}-${month}`); // "2024-04"
506
- }}
507
- />
508
- ```
509
-
510
- ### Format vs DisplayFormat
511
-
512
- ```tsx
513
- // format: Affects the value in onChange
514
- // displayFormat: Affects what users see in the input
515
-
516
- <MonthPicker
517
- format="YYYY-MM-DD" // onChange returns "2024-04-01"
518
- displayFormat="MMMM YYYY" // Input shows "April 2024"
519
- onChange={(e) => {
520
- console.log(e.target.value); // "2024-04-01"
521
- }}
522
- />
523
- ```
524
-
525
- ### Controlled vs Uncontrolled
526
-
527
- ```tsx
528
- // Uncontrolled - component manages state
529
- <MonthPicker
530
- defaultValue="2024/04/01"
531
- onChange={(e) => console.log(e.target.value)}
532
- />
533
-
534
- // Controlled - you manage state
535
- const [month, setMonth] = useState('2024/04/01');
536
- <MonthPicker
537
- value={month}
538
- onChange={(e) => setMonth(e.target.value)}
539
- />
540
- ```
541
-
542
- ## Accessibility
543
-
544
- MonthPicker includes built-in accessibility features:
545
-
546
- ### ARIA Attributes
547
-
548
- - Input has proper `role="textbox"`
549
- - Calendar button has `aria-label="Toggle Calendar"`
550
- - Month grid uses proper navigation roles
551
- - Selected month marked with `aria-selected`
552
-
553
- ### Keyboard Navigation
554
-
555
- - **Tab**: Move focus between input and calendar button
556
- - **Enter/Space**: Open calendar when focused on button
557
- - **Arrow Keys**: Navigate between months in calendar
558
- - **Escape**: Close calendar popup
559
- - **Enter**: Select focused month
312
+ ## Best Practices
560
313
 
561
- ### Screen Reader Support
314
+ 1. **Match value format to `format` prop**: The `value` prop must follow the same pattern as `format`. Mismatches cause unexpected behavior.
562
315
 
563
316
  ```tsx
564
- // Months are announced with full context
565
- <button aria-label="April 2024">Apr</button>
317
+ // Good: Value matches default format
318
+ <MonthPicker value="2024/04/01" />
566
319
 
567
- // Navigation buttons are descriptive
568
- <button aria-label="Previous Year">←</button>
569
- <button aria-label="Next Year">→</button>
320
+ // Bad: Value missing the day component
321
+ <MonthPicker value="2024/04" />
570
322
  ```
571
323
 
572
- ### Focus Management
573
-
574
- - Focus moves to current year's month grid when opened
575
- - Focus returns to input when calendar closes
576
- - Clear visual focus indicators on all interactive elements
577
-
578
- ## Best Practices
579
-
580
- ### ✅ Do
581
-
582
- 1. **Use appropriate display formats**: Match the display format to user expectations
324
+ 2. **Use `displayFormat` for user-friendly presentation**: Keep the value format stable for programmatic use, and customize only the visible representation.
583
325
 
584
326
  ```tsx
585
- // ✅ Good: User-friendly display format
327
+ // ✅ Good: Separate concerns
586
328
  <MonthPicker
587
- displayFormat="MMMM YYYY" // "January 2024" is clearer
588
- label="Billing Month"
329
+ format="YYYY/MM/DD"
330
+ displayFormat="MMMM YYYY"
589
331
  />
590
332
  ```
591
333
 
592
- 2. **Set reasonable date limits**: Restrict months based on context
334
+ 3. **Provide context with `helperText`**: Guide users on what they are selecting, especially when constraints are applied.
593
335
 
594
336
  ```tsx
595
- // ✅ Good: Logical constraints for billing
337
+ // ✅ Good: Clear guidance
596
338
  <MonthPicker
597
- disableFuture // Can't bill for future months
598
- minDate="2020/01/01" // Company started in 2020
339
+ label="Statement Month"
340
+ helperText="Select the month for your account statement"
341
+ disableFuture
599
342
  />
600
343
  ```
601
344
 
602
- 3. **Provide context in helper text**: Guide users on selection purpose
345
+ 4. **Set reasonable date constraints**: Use `minDate`, `maxDate`, `disableFuture`, or `disablePast` to prevent invalid selections at the UI level.
603
346
 
604
347
  ```tsx
605
- // ✅ Good: Clear guidance
606
- <MonthPicker
607
- label="Statement Month"
608
- helperText="Select the month for your account statement"
609
- />
348
+ // ✅ Good: Logical constraints
349
+ <MonthPicker disableFuture minDate="2020/01/01" />
610
350
  ```
611
351
 
612
- 4. **Handle the day value**: Remember values include day set to 01
352
+ 5. **Handle the day component in values**: Remember that `onChange` values always include a day set to `01`. Extract year and month when only those parts are needed.
613
353
 
614
354
  ```tsx
615
- // ✅ Good: Extract month/year when needed
355
+ // ✅ Good: Extract month/year
616
356
  const handleChange = (e) => {
617
357
  const [year, month] = e.target.value.split('/');
618
358
  setSelectedPeriod({ year: parseInt(year), month: parseInt(month) });
619
359
  };
620
360
  ```
621
361
 
622
- ### ❌ Don't
623
-
624
- 1. **Don't expect day-only display in values**: Values always include day
625
-
626
- ```tsx
627
- // ❌ Bad: Expecting value without day
628
- <MonthPicker
629
- value="2024/04" // Wrong! Needs "2024/04/01"
630
- />
631
-
632
- // ✅ Good: Full value format
633
- <MonthPicker
634
- value="2024/04/01"
635
- />
636
- ```
637
-
638
- 2. **Don't use inconsistent formats**: Match value format to format prop
639
-
640
- ```tsx
641
- // ❌ Bad: Mismatched formats
642
- <MonthPicker
643
- format="YYYY/MM/DD"
644
- value="04/2024" // Wrong format
645
- />
646
- ```
647
-
648
- 3. **Don't forget to handle empty states**: Validate before processing
649
-
650
- 4. **Don't use for day-level selection**: Use DatePicker when days matter
651
-
652
- ## Performance Considerations
653
-
654
- ### Memoize Handlers
655
-
656
- When using MonthPicker in complex forms:
657
-
658
- ```tsx
659
- const handleChange = useCallback((e) => {
660
- setMonth(e.target.value);
661
- }, []);
662
-
663
- <MonthPicker value={month} onChange={handleChange} />
664
- ```
665
-
666
- ### Extract Month/Year Efficiently
667
-
668
- When you need just the month and year:
669
-
670
- ```tsx
671
- const { year, month } = useMemo(() => {
672
- if (!selectedMonth) return { year: null, month: null };
673
- const [year, month] = selectedMonth.split('/').map(Number);
674
- return { year, month };
675
- }, [selectedMonth]);
676
- ```
677
-
678
- ### Date Calculations
679
-
680
- For fiscal year or period calculations:
681
-
682
- ```tsx
683
- const fiscalData = useMemo(() => {
684
- if (!selectedMonth) return null;
685
-
686
- const [year, month] = selectedMonth.split('/').map(Number);
687
- const fiscalYear = month >= 4 ? year : year - 1;
688
- const fiscalQuarter = Math.ceil((month >= 4 ? month - 3 : month + 9) / 3);
689
-
690
- return { fiscalYear, fiscalQuarter };
691
- }, [selectedMonth]);
692
- ```
362
+ ## Accessibility
693
363
 
694
- MonthPicker simplifies month-level date selection for billing, reporting, and scheduling scenarios. Remember that values always include day (set to 01), and use displayFormat to show user-friendly month representations like full month names.
364
+ - The input has `role="textbox"` and the calendar toggle button has `aria-label="Toggle Calendar"` for screen reader identification.
365
+ - **Keyboard navigation**: Use **Tab** to move focus between the input and calendar button, **Enter/Space** to open the calendar, **Arrow Keys** to navigate months, and **Escape** to close the popup.
366
+ - Each month button in the calendar grid has a descriptive `aria-label` (e.g., "April 2024") so screen readers announce the full context.
367
+ - Focus management moves focus into the calendar grid when opened and returns focus to the input when the calendar is closed.