# MonthPicker

## Introduction

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.

```tsx
<MonthPicker />
```

| Field         | Description | Default |
| ------------- | ----------- | ------- |
| value         | —           | —       |
| defaultValue  | —           | —       |
| name          | —           | —       |
| disabled      | —           | —       |
| required      | —           | —       |
| label         | —           | —       |
| error         | —           | —       |
| helperText    | —           | —       |
| minDate       | —           | —       |
| maxDate       | —           | —       |
| disableFuture | —           | —       |
| disablePast   | —           | —       |
| format        | —           | —       |
| onChange      | —           | —       |

> ⚠️ **Usage Warning** ⚠️
>
> MonthPicker has some unique formatting behaviors:
>
> - **Internal value format**: Values internally use full date format (`"YYYY/MM/DD"`) with day set to `01`
> - **Display vs Value**: Input displays `YYYY/MM` but `onChange` receives `YYYY/MM/01`
> - **Format consistency**: Ensure your `value` prop matches the `format` prop
> - **displayFormat options**: Supports special token `MMMM` for full month names

> 💡 **Use built-in form props**
>
> This component natively supports form elements such as `label` and `helperText` props.
> When building forms, use these built-in props instead of manually composing labels and helper text with Typography.

## Usage

```tsx
import { MonthPicker } from '@ceed/cds';

function MonthForm() {
  const [month, setMonth] = useState('');

  return (
    <MonthPicker
      label="Select Month"
      value={month}
      onChange={(e) => setMonth(e.target.value)}
    />
  );
}
```

## Examples

### Playground

Interactive example with all controls.

```tsx
<MonthPicker />
```

### Sizes

MonthPicker supports three sizes for different layouts.

```tsx
<Stack gap={2}>
  <MonthPicker size="sm" />
  <MonthPicker size="md" />
  <MonthPicker size="lg" />
</Stack>
```

### With Label

Add a label above the month picker.

```tsx
<MonthPicker label="Date" />
```

### With Helper Text

Provide additional guidance below the input.

```tsx
<MonthPicker
  label="Date"
  helperText="Please select a date"
/>
```

### Error State

Show validation errors with error styling.

```tsx
<MonthPicker
  label="Date"
  helperText="Please select a date"
  error
/>
```

### Required Field

Mark the field as required in forms.

```tsx
<MonthPicker
  label="Label"
  helperText="I'm helper text"
  required
/>
```

### Disabled

Prevent user interaction when disabled.

```tsx
<MonthPicker disabled />
```

### Minimum Date

Restrict selection to months on or after a minimum date.

```tsx
<MonthPicker minDate="2024-04-10" />
```

### Maximum Date

Restrict selection to months on or before a maximum date.

```tsx
<MonthPicker maxDate="2024-04-10" />
```

### Disable Future

Prevent selection of months in the future.

```tsx
<MonthPicker disableFuture />
```

### Disable Past

Prevent selection of months in the past.

```tsx
<MonthPicker disablePast />
```

### Controlled

Parent component manages the month state.

```tsx
<Stack gap={2}>
  <MonthPicker {...args} value={value} onChange={e => setValue(e.target.value)} />
  <Button onClick={() => {
    const currentValue = new Date(value);
    currentValue.setMonth(currentValue.getMonth() + 1);
    const year = currentValue.getFullYear();
    const month = String(currentValue.getMonth() + 1).padStart(2, '0');
    setValue(`${year}/${month}/01`);
  }}>
  Next Month
</Button>
</Stack>
```

### Uncontrolled

Component manages its own state internally.

```tsx
<MonthPicker
  label="Uncontrolled MonthPicker"
  helperText="Please select a date"
  defaultValue="2024/04/01"
/>
```

### With Formats

Different value formats for the `onChange` event.

```tsx
<Stack gap={2}>
  <MonthPicker {...args} value={value['YYYY.MM.DD']} label="YYYY.MM.DD" name="YYYY.MM.DD" format="YYYY.MM.DD" onChange={handleChange} />
  <MonthPicker {...args} value={value['YYYY/MM/DD']} label="YYYY/MM/DD" name="YYYY/MM/DD" format="YYYY/MM/DD" onChange={handleChange} />
  <MonthPicker {...args} value={value['MM/DD/YYYY']} label="MM/DD/YYYY" name="MM/DD/YYYY" format="MM/DD/YYYY" onChange={handleChange} />
  <MonthPicker {...args} value={value['YYYY-MM-DD']} label="YYYY-MM-DD" name="YYYY-MM-DD" format="YYYY-MM-DD" onChange={handleChange} />
  <MonthPicker {...args} value={value['DD/MM/YYYY']} label="DD/MM/YYYY" name="DD/MM/YYYY" format="DD/MM/YYYY" onChange={handleChange} />
  <MonthPicker {...args} value={value['DD.MM.YYYY']} label="DD.MM.YYYY" name="DD.MM.YYYY" format="DD.MM.YYYY" onChange={handleChange} />
</Stack>
```

### With Display Formats

Different display formats including full month names.

```tsx
<Stack gap={2}>
  <MonthPicker {...args} value={value1} label="YYYY.MM" name="YYYY.MM" displayFormat="YYYY.MM" onChange={e => {
    setValue1(e.target.value);
    args.onChange?.(e);
  }} />
  <MonthPicker {...args} value={value2} label="YYYY/MM" name="YYYY/MM" displayFormat="YYYY/MM" onChange={e => {
    setValue2(e.target.value);
    args.onChange?.(e);
  }} />
  <MonthPicker {...args} value={value3} label="MM/YYYY" name="MM/YYYY" displayFormat="MM/YYYY" onChange={e => {
    setValue3(e.target.value);
    args.onChange?.(e);
  }} />
  <MonthPicker {...args} value={value4} label="YYYY-MM" name="YYYY-MM" displayFormat="YYYY-MM" onChange={e => {
    setValue4(e.target.value);
    args.onChange?.(e);
  }} />
  <MonthPicker {...args} value={value5} label="YYYY-MM-DD" name="YYYY-MM-DD" displayFormat="YYYY-MM-DD" onChange={e => {
    setValue5(e.target.value);
    args.onChange?.(e);
  }} />
  <MonthPicker {...args} value={value6} label="MMMM YYYY" name="MMMM YYYY" displayFormat="MMMM YYYY" onChange={e => {
    setValue6(e.target.value);
    args.onChange?.(e);
  }} />
</Stack>
```

## When to Use

### ✅ Good Use Cases

- **Billing periods**: Monthly invoices, subscription billing cycles
- **Report filtering**: Monthly reports, quarterly summaries
- **Fiscal periods**: Fiscal months, budget allocation periods
- **Expiration dates**: Credit card expiry (month/year)
- **Historical data**: Selecting past months for data analysis
- **Scheduling**: Monthly recurring events

### ❌ When Not to Use

- **Specific dates**: Use DatePicker when day selection matters
- **Date ranges**: Use DateRangePicker or MonthRangePicker
- **Week selection**: Consider a custom week picker
- **Quarter selection**: Consider a dropdown with Q1-Q4 options
- **Year only**: Use a year picker or simple dropdown

## Common Use Cases

### Billing Period Selection

```tsx
function BillingPeriodSelector() {
  const [billingMonth, setBillingMonth] = useState('');

  const handleGenerate = () => {
    const [year, month] = billingMonth.split('/');
    generateInvoice({ year: parseInt(year), month: parseInt(month) });
  };

  return (
    <Stack gap={2}>
      <MonthPicker
        label="Billing Period"
        value={billingMonth}
        onChange={(e) => setBillingMonth(e.target.value)}
        disableFuture
        helperText="Select the month for invoice generation"
      />
      <Button onClick={handleGenerate} disabled={!billingMonth}>
        Generate Invoice
      </Button>
    </Stack>
  );
}
```

### Report Month Filter

```tsx
function ReportFilters({ onFilter }) {
  const [reportMonth, setReportMonth] = useState('');

  // Get current month as default
  useEffect(() => {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    setReportMonth(`${year}/${month}/01`);
  }, []);

  return (
    <Stack direction="row" gap={2} alignItems="flex-end">
      <MonthPicker
        label="Report Month"
        value={reportMonth}
        onChange={(e) => setReportMonth(e.target.value)}
        disableFuture
        displayFormat="MMMM YYYY"  // Shows "January 2024"
      />
      <Button onClick={() => onFilter(reportMonth)}>
        Generate Report
      </Button>
    </Stack>
  );
}
```

### Credit Card Expiry

```tsx
function CreditCardExpiry() {
  const [expiry, setExpiry] = useState('');

  // Get current date for minimum
  const today = new Date();
  const minDate = `${today.getFullYear()}/${String(today.getMonth() + 1).padStart(2, '0')}/01`;

  return (
    <MonthPicker
      label="Card Expiry"
      value={expiry}
      onChange={(e) => setExpiry(e.target.value)}
      minDate={minDate}
      displayFormat="MM/YYYY"  // Common credit card format
      helperText="Enter card expiration date"
      required
    />
  );
}
```

### Fiscal Period Selector

```tsx
function FiscalPeriodSelector({ fiscalYearStart = 4 }) { // April
  const [selectedMonth, setSelectedMonth] = useState('');
  const [fiscalPeriod, setFiscalPeriod] = useState('');

  // Calculate fiscal period from selected month
  useEffect(() => {
    if (!selectedMonth) return;

    const [year, month] = selectedMonth.split('/').map(Number);
    const fiscalMonth = month >= fiscalYearStart
      ? month - fiscalYearStart + 1
      : month + (12 - fiscalYearStart + 1);
    const fiscalYear = month >= fiscalYearStart ? year : year - 1;

    setFiscalPeriod(`FY${fiscalYear} P${fiscalMonth}`);
  }, [selectedMonth, fiscalYearStart]);

  return (
    <Stack gap={2}>
      <MonthPicker
        label="Select Period"
        value={selectedMonth}
        onChange={(e) => setSelectedMonth(e.target.value)}
        displayFormat="MMMM YYYY"
      />
      {fiscalPeriod && (
        <Typography level="body-sm">Fiscal Period: {fiscalPeriod}</Typography>
      )}
    </Stack>
  );
}
```

### Monthly Budget Allocation

```tsx
function BudgetAllocation({ departments }) {
  const [selectedMonth, setSelectedMonth] = useState('');
  const [budgets, setBudgets] = useState({});

  // Restrict to future months only
  const today = new Date();
  const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
  const minDate = `${nextMonth.getFullYear()}/${String(nextMonth.getMonth() + 1).padStart(2, '0')}/01`;

  return (
    <form>
      <MonthPicker
        label="Budget Month"
        value={selectedMonth}
        onChange={(e) => setSelectedMonth(e.target.value)}
        minDate={minDate}
        displayFormat="MMMM YYYY"
        helperText="Select month for budget allocation"
        required
      />

      {selectedMonth && departments.map((dept) => (
        <CurrencyInput
          key={dept.id}
          label={`${dept.name} Budget`}
          value={budgets[dept.id] || ''}
          onChange={(value) => setBudgets(prev => ({
            ...prev,
            [dept.id]: value
          }))}
        />
      ))}

      <Button type="submit">Allocate Budget</Button>
    </form>
  );
}
```

### Year-Month Comparison

```tsx
function MonthComparison() {
  const [month1, setMonth1] = useState('');
  const [month2, setMonth2] = useState('');

  return (
    <Stack gap={2}>
      <Stack direction="row" gap={2}>
        <MonthPicker
          label="Compare From"
          value={month1}
          onChange={(e) => setMonth1(e.target.value)}
          disableFuture
        />
        <MonthPicker
          label="Compare To"
          value={month2}
          onChange={(e) => setMonth2(e.target.value)}
          disableFuture
          minDate={month1}  // Must be after first month
        />
      </Stack>
      <Button disabled={!month1 || !month2}>
        Compare Months
      </Button>
    </Stack>
  );
}
```

## Props and Customization

### Key Props

| Prop            | Type                                                        | Default        | Description                             |
| --------------- | ----------------------------------------------------------- | -------------- | --------------------------------------- |
| `value`         | `string`                                                    | -              | Controlled value (format: `YYYY/MM/DD`) |
| `defaultValue`  | `string`                                                    | -              | Default value for uncontrolled mode     |
| `onChange`      | `(e: { target: { name?: string; value: string } }) => void` | -              | Change handler                          |
| `format`        | `string`                                                    | `'YYYY/MM/DD'` | Format for `value` and `onChange`       |
| `displayFormat` | `string`                                                    | `'YYYY/MM'`    | Format displayed in the input           |
| `label`         | `string`                                                    | -              | Label text                              |
| `helperText`    | `string`                                                    | -              | Helper text below input                 |
| `error`         | `boolean`                                                   | `false`        | Error state                             |
| `size`          | `'sm' \| 'md' \| 'lg'`                                      | `'md'`         | Component size                          |
| `disabled`      | `boolean`                                                   | `false`        | Disabled state                          |
| `required`      | `boolean`                                                   | `false`        | Required field indicator                |
| `minDate`       | `string`                                                    | -              | Minimum selectable month                |
| `maxDate`       | `string`                                                    | -              | Maximum selectable month                |
| `disableFuture` | `boolean`                                                   | `false`        | Disable all future months               |
| `disablePast`   | `boolean`                                                   | `false`        | Disable all past months                 |

### Display Format Tokens

| Token  | Description     | Example |
| ------ | --------------- | ------- |
| `YYYY` | 4-digit year    | 2024    |
| `MM`   | 2-digit month   | 04      |
| `DD`   | 2-digit day     | 01      |
| `MMMM` | Full month name | January |

Common display format patterns:

- `YYYY/MM` - Default (2024/04)
- `YYYY-MM` - ISO-like (2024-04)
- `MM/YYYY` - European (04/2024)
- `MMMM YYYY` - Full name (April 2024)
- `YYYY.MM` - Period separator (2024.04)

### Value Format Understanding

```tsx
// MonthPicker always uses day=01 in values
// Even though display shows "2024/04", the value is "2024/04/01"

<MonthPicker
  value="2024/04/01"  // Must include day
  displayFormat="YYYY/MM"  // Shows "2024/04"
  onChange={(e) => {
    console.log(e.target.value); // "2024/04/01"
    // Extract just year and month
    const [year, month] = e.target.value.split('/');
    console.log(`${year}-${month}`); // "2024-04"
  }}
/>
```

### Format vs DisplayFormat

```tsx
// format: Affects the value in onChange
// displayFormat: Affects what users see in the input

<MonthPicker
  format="YYYY-MM-DD"      // onChange returns "2024-04-01"
  displayFormat="MMMM YYYY" // Input shows "April 2024"
  onChange={(e) => {
    console.log(e.target.value); // "2024-04-01"
  }}
/>
```

### Controlled vs Uncontrolled

```tsx
// Uncontrolled - component manages state
<MonthPicker
  defaultValue="2024/04/01"
  onChange={(e) => console.log(e.target.value)}
/>

// Controlled - you manage state
const [month, setMonth] = useState('2024/04/01');
<MonthPicker
  value={month}
  onChange={(e) => setMonth(e.target.value)}
/>
```

## Accessibility

MonthPicker includes built-in accessibility features:

### ARIA Attributes

- Input has proper `role="textbox"`
- Calendar button has `aria-label="Toggle Calendar"`
- Month grid uses proper navigation roles
- Selected month marked with `aria-selected`

### Keyboard Navigation

- **Tab**: Move focus between input and calendar button
- **Enter/Space**: Open calendar when focused on button
- **Arrow Keys**: Navigate between months in calendar
- **Escape**: Close calendar popup
- **Enter**: Select focused month

### Screen Reader Support

```tsx
// Months are announced with full context
<button aria-label="April 2024">Apr</button>

// Navigation buttons are descriptive
<button aria-label="Previous Year">←</button>
<button aria-label="Next Year">→</button>
```

### Focus Management

- Focus moves to current year's month grid when opened
- Focus returns to input when calendar closes
- Clear visual focus indicators on all interactive elements

## Best Practices

### ✅ Do

1. **Use appropriate display formats**: Match the display format to user expectations

```tsx
// ✅ Good: User-friendly display format
<MonthPicker
  displayFormat="MMMM YYYY"  // "January 2024" is clearer
  label="Billing Month"
/>
```

2. **Set reasonable date limits**: Restrict months based on context

```tsx
// ✅ Good: Logical constraints for billing
<MonthPicker
  disableFuture  // Can't bill for future months
  minDate="2020/01/01"  // Company started in 2020
/>
```

3. **Provide context in helper text**: Guide users on selection purpose

```tsx
// ✅ Good: Clear guidance
<MonthPicker
  label="Statement Month"
  helperText="Select the month for your account statement"
/>
```

4. **Handle the day value**: Remember values include day set to 01

```tsx
// ✅ Good: Extract month/year when needed
const handleChange = (e) => {
  const [year, month] = e.target.value.split('/');
  setSelectedPeriod({ year: parseInt(year), month: parseInt(month) });
};
```

### ❌ Don't

1. **Don't expect day-only display in values**: Values always include day

```tsx
// ❌ Bad: Expecting value without day
<MonthPicker
  value="2024/04"  // Wrong! Needs "2024/04/01"
/>

// ✅ Good: Full value format
<MonthPicker
  value="2024/04/01"
/>
```

2. **Don't use inconsistent formats**: Match value format to format prop

```tsx
// ❌ Bad: Mismatched formats
<MonthPicker
  format="YYYY/MM/DD"
  value="04/2024"  // Wrong format
/>
```

3. **Don't forget to handle empty states**: Validate before processing

4. **Don't use for day-level selection**: Use DatePicker when days matter

## Performance Considerations

### Memoize Handlers

When using MonthPicker in complex forms:

```tsx
const handleChange = useCallback((e) => {
  setMonth(e.target.value);
}, []);

<MonthPicker value={month} onChange={handleChange} />
```

### Extract Month/Year Efficiently

When you need just the month and year:

```tsx
const { year, month } = useMemo(() => {
  if (!selectedMonth) return { year: null, month: null };
  const [year, month] = selectedMonth.split('/').map(Number);
  return { year, month };
}, [selectedMonth]);
```

### Date Calculations

For fiscal year or period calculations:

```tsx
const fiscalData = useMemo(() => {
  if (!selectedMonth) return null;

  const [year, month] = selectedMonth.split('/').map(Number);
  const fiscalYear = month >= 4 ? year : year - 1;
  const fiscalQuarter = Math.ceil((month >= 4 ? month - 3 : month + 9) / 3);

  return { fiscalYear, fiscalQuarter };
}, [selectedMonth]);
```

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.
