@moneyforward/mfui-components 3.34.0 → 3.36.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 (108) hide show
  1. package/dist/src/DataTable/DataTable.d.ts +50 -0
  2. package/dist/src/DataTable/DataTable.js +44 -0
  3. package/dist/src/DataTable/DataTableFilterButton/DataTableFilterButton.d.ts +6 -0
  4. package/dist/src/DataTable/DataTableFilterButton/DataTableFilterButton.js +63 -0
  5. package/dist/src/DataTable/DataTableFilterButton/DataTableFilterButton.types.d.ts +49 -0
  6. package/dist/src/DataTable/DataTableFilterButton/DataTableFilterButton.types.js +1 -0
  7. package/dist/src/DataTable/DataTableFilterButton/index.d.ts +2 -0
  8. package/dist/src/DataTable/DataTableFilterButton/index.js +1 -0
  9. package/dist/src/DataTable/DataTableHeaderCell/DataTableHeaderCell.js +6 -2
  10. package/dist/src/DataTable/DataTableHeaderCell/DataTableHeaderCell.types.d.ts +23 -1
  11. package/dist/src/DataTable/DataTableSortButton/DataTableSortButton.d.ts +4 -0
  12. package/dist/src/DataTable/DataTableSortButton/DataTableSortButton.js +17 -0
  13. package/dist/src/DataTable/DataTableSortButton/DataTableSortButton.types.d.ts +25 -0
  14. package/dist/src/DataTable/DataTableSortButton/DataTableSortButton.types.js +1 -0
  15. package/dist/src/DataTable/DataTableSortButton/index.d.ts +2 -0
  16. package/dist/src/DataTable/DataTableSortButton/index.js +1 -0
  17. package/dist/src/DataTable/index.d.ts +2 -0
  18. package/dist/src/DateTimeSelection/DatePicker/DatePickerDateCell/DatePickerDateCell.js +3 -0
  19. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerPopover/DateRangePickerCalendar/DateRangePickerDateCell.d.ts +2 -2
  20. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerPopover/DateRangePickerCalendar/DateRangePickerDateCell.js +5 -2
  21. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/DateRangePickerTrigger.js +3 -2
  22. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/DateRangePickerTrigger.types.d.ts +17 -0
  23. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.js +3 -1
  24. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.d.ts +2 -1
  25. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.d.ts +2 -1
  26. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.js +2 -1
  27. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/MonthRangePickerMonthCell.d.ts +1 -0
  28. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/MonthRangePickerMonthCell.js +11 -8
  29. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/constants.d.ts +7 -15
  30. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/constants.js +26 -13
  31. package/dist/src/DateTimeSelection/TimePicker/TimePicker.d.ts +2 -0
  32. package/dist/src/DateTimeSelection/TimePicker/TimePicker.js +3 -2
  33. package/dist/src/DateTimeSelection/TimePicker/TimePicker.types.d.ts +27 -0
  34. package/dist/src/DateTimeSelection/shared/BasePicker/BasePicker.js +4 -1
  35. package/dist/src/DateTimeSelection/shared/BasePicker/constants.d.ts +1 -1
  36. package/dist/src/DateTimeSelection/shared/BasePicker/constants.js +2 -0
  37. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.d.ts +1 -1
  38. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.js +4 -4
  39. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.types.d.ts +33 -1
  40. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/BaseRangePickerDateCell.d.ts +2 -2
  41. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/BaseRangePickerDateCell.js +15 -36
  42. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/index.d.ts +0 -1
  43. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/index.js +0 -1
  44. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerPopover.js +2 -1
  45. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.js +3 -2
  46. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.types.d.ts +17 -0
  47. package/dist/src/DateTimeSelection/shared/accessibility/constants.d.ts +7 -16
  48. package/dist/src/DateTimeSelection/shared/accessibility/constants.js +30 -14
  49. package/dist/src/DateTimeSelection/shared/accessibility/generateDateLabel.d.ts +6 -1
  50. package/dist/src/DateTimeSelection/shared/accessibility/generateDateLabel.js +14 -8
  51. package/dist/src/DeterminateProgressIndicator/DeterminateProgressIndicator.d.ts +18 -0
  52. package/dist/src/DeterminateProgressIndicator/DeterminateProgressIndicator.js +20 -0
  53. package/dist/src/DeterminateProgressIndicator/DeterminateProgressIndicator.types.d.ts +41 -0
  54. package/dist/src/DeterminateProgressIndicator/DeterminateProgressIndicator.types.js +1 -0
  55. package/dist/src/DeterminateProgressIndicator/index.d.ts +2 -0
  56. package/dist/src/DeterminateProgressIndicator/index.js +1 -0
  57. package/dist/src/FilterSelectBox/FilterSelectBox.types.d.ts +1 -1
  58. package/dist/src/IconButton/IconButton.d.ts +2 -0
  59. package/dist/src/IconButton/IconButton.js +2 -2
  60. package/dist/src/IconButton/IconButton.types.d.ts +7 -0
  61. package/dist/src/MultipleFilterSelectBox/MultipleFilterSelectBox.types.d.ts +1 -1
  62. package/dist/src/MultipleSelectBox/MultipleSelectBox.d.ts +1 -1
  63. package/dist/src/MultipleSelectBox/MultipleSelectBox.js +2 -2
  64. package/dist/src/MultipleSelectBox/MultipleSelectBox.types.d.ts +5 -1
  65. package/dist/src/MultipleSelectBox/MultipleSelectBoxTrigger/MultipleSelectBoxTrigger.js +2 -2
  66. package/dist/src/MultipleSelectBox/MultipleSelectBoxTrigger/MultipleSelectBoxTrigger.types.d.ts +39 -1
  67. package/dist/src/MultipleSelectBox/hooks/useSearchBox.js +14 -3
  68. package/dist/src/Popover/Popover.js +1 -1
  69. package/dist/src/Popover/Popover.types.d.ts +1 -1
  70. package/dist/src/SelectBox/SelectBox.d.ts +1 -1
  71. package/dist/src/SelectBox/SelectBox.js +11 -4
  72. package/dist/src/SelectBox/SelectBox.types.d.ts +38 -1
  73. package/dist/src/SelectBox/hooks/useSearchBox.d.ts +6 -1
  74. package/dist/src/SelectBox/hooks/useSearchBox.js +23 -4
  75. package/dist/src/SplitView/SplitView.js +16 -2
  76. package/dist/src/TextBox/TextBox.d.ts +2 -1
  77. package/dist/src/TextBox/TextBox.js +4 -3
  78. package/dist/src/TextBox/TextBox.types.d.ts +26 -0
  79. package/dist/src/Textarea/Textarea.d.ts +2 -1
  80. package/dist/src/Textarea/Textarea.js +11 -3
  81. package/dist/src/Textarea/Textarea.types.d.ts +26 -0
  82. package/dist/src/index.d.ts +1 -0
  83. package/dist/src/index.js +1 -0
  84. package/dist/styled-system/recipes/base-range-picker-trigger-slot-recipe.d.ts +1 -0
  85. package/dist/styled-system/recipes/base-range-picker-trigger-slot-recipe.js +5 -0
  86. package/dist/styled-system/recipes/data-table-filter-button-slot-recipe.d.ts +33 -0
  87. package/dist/styled-system/recipes/data-table-filter-button-slot-recipe.js +43 -0
  88. package/dist/styled-system/recipes/data-table-header-cell-slot-recipe.d.ts +3 -2
  89. package/dist/styled-system/recipes/data-table-header-cell-slot-recipe.js +69 -1
  90. package/dist/styled-system/recipes/data-table-sort-button-slot-recipe.d.ts +33 -0
  91. package/dist/styled-system/recipes/data-table-sort-button-slot-recipe.js +35 -0
  92. package/dist/styled-system/recipes/determinate-progress-indicator-slot-recipe.d.ts +36 -0
  93. package/dist/styled-system/recipes/determinate-progress-indicator-slot-recipe.js +61 -0
  94. package/dist/styled-system/recipes/icon-button-slot-recipe.d.ts +2 -1
  95. package/dist/styled-system/recipes/icon-button-slot-recipe.js +33 -2
  96. package/dist/styled-system/recipes/index.d.ts +3 -0
  97. package/dist/styled-system/recipes/index.js +3 -0
  98. package/dist/styled-system/recipes/text-box-slot-recipe.d.ts +4 -0
  99. package/dist/styled-system/recipes/text-box-slot-recipe.js +8 -2
  100. package/dist/styled-system/recipes/textarea-slot-recipe.d.ts +4 -0
  101. package/dist/styled-system/recipes/textarea-slot-recipe.js +6 -0
  102. package/dist/styled-system/recipes/time-picker-slot-recipe.d.ts +4 -0
  103. package/dist/styled-system/recipes/time-picker-slot-recipe.js +8 -2
  104. package/dist/styles.css +293 -14
  105. package/dist/tsconfig.build.tsbuildinfo +1 -1
  106. package/package.json +10 -10
  107. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/constants.d.ts +0 -18
  108. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/constants.js +0 -18
@@ -281,4 +281,54 @@ export declare const DataTable: import("react").ForwardRefExoticComponent<Omit<D
281
281
  * ```
282
282
  */
283
283
  Row: import("react").ForwardRefExoticComponent<Omit<import("./DataTableRow").DataTableRowProps, "ref"> & import("react").RefAttributes<HTMLTableRowElement>>;
284
+ /**
285
+ * The `DataTableFilterButton` component.
286
+ * An icon-only filter button intended to be placed in the `actions` of `DataTable.HeaderCell`.
287
+ *
288
+ * It extends the props of `IconButton` except `children`. The filter icon
289
+ * (outline / filled) is switched internally based on the `filtered` prop, and the button is
290
+ * internally wired as the trigger of a `Popover` rendering `renderContent` — consumers only
291
+ * provide the filter state and the popover content.
292
+ *
293
+ * @example
294
+ *
295
+ * ```tsx
296
+ * <DataTable.HeaderCell
297
+ * actions={
298
+ * <DataTable.FilterButton filtered={isFiltered} renderContent={() => <FilterOptions />} />
299
+ * }
300
+ * >
301
+ * Name
302
+ * </DataTable.HeaderCell>
303
+ * ```
304
+ */
305
+ FilterButton: import("react").ForwardRefExoticComponent<Omit<import("..").IconButtonProps, "aria-label" | "children" | "customLinkComponent" | "href" | "target"> & {
306
+ 'aria-label'?: import("..").IconButtonProps['aria-label'];
307
+ popoverProps?: Pick<import("react").ComponentPropsWithoutRef<'div'>, 'aria-label' | 'aria-labelledby'>;
308
+ filtered?: boolean;
309
+ renderContent: import("..").PopoverProps['renderContent'];
310
+ } & import("react").RefAttributes<HTMLButtonElement>>;
311
+ /**
312
+ * The `DataTableSortButton` component.
313
+ * An icon-only sort button intended to be placed in the `actions` of `DataTable.HeaderCell`.
314
+ *
315
+ * It extends the props of `IconButton` except `children` — the sort icon
316
+ * (unsorted / ascending / descending) is switched internally based on the `sortOrder` prop,
317
+ * so consumers only provide the behavior (e.g. `onClick`).
318
+ *
319
+ * @example
320
+ *
321
+ * ```tsx
322
+ * <DataTable.HeaderCell
323
+ * sortOrder={sortOrder}
324
+ * actions={<DataTable.SortButton sortOrder={sortOrder} onClick={handleSort} />}
325
+ * >
326
+ * Age
327
+ * </DataTable.HeaderCell>
328
+ * ```
329
+ */
330
+ SortButton: import("react").ForwardRefExoticComponent<Omit<import("..").IconButtonProps, "aria-label" | "children"> & {
331
+ 'aria-label'?: import("..").IconButtonProps['aria-label'];
332
+ sortOrder?: 'ascending' | 'descending';
333
+ } & import("react").RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
284
334
  };
@@ -8,7 +8,9 @@ import { DataTableCell } from './DataTableCell';
8
8
  import { DataTableHeader } from './DataTableHeader';
9
9
  import { DataTableHeaderCell } from './DataTableHeaderCell';
10
10
  import { DataTableHeaderRow } from './DataTableHeaderRow';
11
+ import { DataTableFilterButton } from './DataTableFilterButton';
11
12
  import { DataTableRow } from './DataTableRow';
13
+ import { DataTableSortButton } from './DataTableSortButton';
12
14
  import { cx } from '../../styled-system/css';
13
15
  import { isComponentOrWrapped } from '../utilities/react/isComponentOrWrapped';
14
16
  const Base = forwardRef(({ className, size = 'medium', enableRowSelection = false, fixedHeader = false, showVerticalBorder = false, leftFixedColumnIndex, rightFixedColumnIndex, layout = 'default', wrapperProps, loading = false, emptyStateSlot, children, ...props }, ref) => {
@@ -324,4 +326,46 @@ export const DataTable = Object.assign(Base, {
324
326
  * ```
325
327
  */
326
328
  Row: DataTableRow,
329
+ /**
330
+ * The `DataTableFilterButton` component.
331
+ * An icon-only filter button intended to be placed in the `actions` of `DataTable.HeaderCell`.
332
+ *
333
+ * It extends the props of `IconButton` except `children`. The filter icon
334
+ * (outline / filled) is switched internally based on the `filtered` prop, and the button is
335
+ * internally wired as the trigger of a `Popover` rendering `renderContent` — consumers only
336
+ * provide the filter state and the popover content.
337
+ *
338
+ * @example
339
+ *
340
+ * ```tsx
341
+ * <DataTable.HeaderCell
342
+ * actions={
343
+ * <DataTable.FilterButton filtered={isFiltered} renderContent={() => <FilterOptions />} />
344
+ * }
345
+ * >
346
+ * Name
347
+ * </DataTable.HeaderCell>
348
+ * ```
349
+ */
350
+ FilterButton: DataTableFilterButton,
351
+ /**
352
+ * The `DataTableSortButton` component.
353
+ * An icon-only sort button intended to be placed in the `actions` of `DataTable.HeaderCell`.
354
+ *
355
+ * It extends the props of `IconButton` except `children` — the sort icon
356
+ * (unsorted / ascending / descending) is switched internally based on the `sortOrder` prop,
357
+ * so consumers only provide the behavior (e.g. `onClick`).
358
+ *
359
+ * @example
360
+ *
361
+ * ```tsx
362
+ * <DataTable.HeaderCell
363
+ * sortOrder={sortOrder}
364
+ * actions={<DataTable.SortButton sortOrder={sortOrder} onClick={handleSort} />}
365
+ * >
366
+ * Age
367
+ * </DataTable.HeaderCell>
368
+ * ```
369
+ */
370
+ SortButton: DataTableSortButton,
327
371
  });
@@ -0,0 +1,6 @@
1
+ export declare const DataTableFilterButton: import("react").ForwardRefExoticComponent<Omit<import("../..").IconButtonProps, "aria-label" | "children" | "customLinkComponent" | "href" | "target"> & {
2
+ 'aria-label'?: import("../..").IconButtonProps['aria-label'];
3
+ popoverProps?: Pick<import("react").ComponentPropsWithoutRef<'div'>, 'aria-label' | 'aria-labelledby'>;
4
+ filtered?: boolean;
5
+ renderContent: import("../..").PopoverProps['renderContent'];
6
+ } & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,63 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Filter, FilterOn } from '@moneyforward/mfui-icons-react';
3
+ import { forwardRef, useCallback, useId, useRef } from 'react';
4
+ import { dataTableFilterButtonSlotRecipe } from '../../../styled-system/recipes';
5
+ import { IconButton } from '../../IconButton';
6
+ import { Popover } from '../../Popover';
7
+ import { getFocusableNodes } from '../../utilities/dom/getFocusableNodes';
8
+ import { mergeRefs } from '../../utilities/dom/mergeRefs';
9
+ import { useFocusTrap } from '../../utilities/dom/useFocusTrap';
10
+ import { cx } from '../../../styled-system/css';
11
+ export const DataTableFilterButton = forwardRef(({ filtered = false, renderContent, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, popoverProps, onClick, onKeyDown, onBlur, className, ...props }, ref) => {
12
+ const filteredDescriptionId = useId();
13
+ const popoverContentId = useId();
14
+ const triggerRef = useRef(null);
15
+ const triggerLabel = ariaLabel ?? '絞り込み';
16
+ const dialogLabel = popoverProps?.['aria-label'] ?? '絞り込み条件';
17
+ const describedBy = [ariaDescribedBy, filtered ? filteredDescriptionId : undefined].filter(Boolean).join(' ');
18
+ const classes = dataTableFilterButtonSlotRecipe({ filtered });
19
+ const { handleOnKeyDown: handleFocusTrapKeyDown } = useFocusTrap({
20
+ onPressingTabOnNotFocusableElement: (event) => {
21
+ const nodes = getFocusableNodes(event.currentTarget);
22
+ const focusableNodeAfterFocusingNode = nodes.find((element) =>
23
+ // eslint-disable-next-line no-bitwise
24
+ element.compareDocumentPosition(event.target) & Node.DOCUMENT_POSITION_PRECEDING);
25
+ (focusableNodeAfterFocusingNode ?? nodes.at(0))?.focus();
26
+ event.preventDefault();
27
+ },
28
+ });
29
+ // Focus the dialog container itself (tabIndex={-1}) on open — not its first control —
30
+ // so opening with the mouse and with the keyboard behave the same: the first Tab always
31
+ // moves into the first control.
32
+ const focusInitialElement = useCallback((node) => {
33
+ node?.focus();
34
+ }, []);
35
+ return (_jsx(Popover
36
+ // handleTriggerKeyDown is intentionally not wired: arrow-key opening belongs to
37
+ // menu/listbox patterns, while this trigger opens a dialog.
38
+ , {
39
+ // handleTriggerKeyDown is intentionally not wired: arrow-key opening belongs to
40
+ // menu/listbox patterns, while this trigger opens a dialog.
41
+ renderTrigger: ({ setTriggerRef, togglePopover, closePopover, isPopoverOpen, handleTriggerBlur }) => (_jsxs(_Fragment, { children: [_jsx(IconButton, { ref: mergeRefs(ref, setTriggerRef, triggerRef), ...props, className: cx(classes.root, 'mfui-DataTableFilterButton__root', className), "aria-label": triggerLabel, "aria-describedby": describedBy || undefined, "aria-haspopup": "dialog", "aria-expanded": isPopoverOpen, "aria-controls": popoverContentId, onClick: (event) => {
42
+ togglePopover(event);
43
+ onClick?.(event);
44
+ }, onKeyDown: (event) => {
45
+ // The popover's own Escape handler lives on the portaled content, so it never
46
+ // sees a keydown fired on the trigger (e.g. after Shift+Tab back while open).
47
+ if (event.key === 'Escape' && isPopoverOpen) {
48
+ event.stopPropagation();
49
+ event.preventDefault();
50
+ closePopover(event);
51
+ }
52
+ onKeyDown?.(event);
53
+ }, onBlur: (event) => {
54
+ handleTriggerBlur(event);
55
+ onBlur?.(event);
56
+ }, children: filtered ? _jsx(FilterOn, {}) : _jsx(Filter, {}) }), filtered ? (_jsx("span", { id: filteredDescriptionId, className: cx(classes.filteredDescription, 'mfui-DataTableFilterButton__filteredDescription'), children: "\u9069\u7528\u4E2D" })) : null] })), renderContent: (options) => (_jsx("div", { ref: focusInitialElement, role: "dialog", "aria-label": dialogLabel, "aria-labelledby": popoverProps?.['aria-labelledby'], tabIndex: -1, className: cx(classes.dialog, 'mfui-DataTableFilterButton__dialog'), onKeyDown: handleFocusTrapKeyDown, children: renderContent(options) })), contentProps: { id: popoverContentId }, enableAutoFocusOnPopover: false, onClose: (event) => {
57
+ // Keep Escape focus restoration scoped to the filter button instead of changing the shared Popover.
58
+ if (event && 'key' in event && event.key === 'Escape') {
59
+ triggerRef.current?.focus();
60
+ }
61
+ } }));
62
+ });
63
+ DataTableFilterButton.displayName = 'DataTableFilterButton';
@@ -0,0 +1,49 @@
1
+ import { type ComponentPropsWithoutRef } from 'react';
2
+ import { type IconButtonProps } from '../../IconButton';
3
+ import { type PopoverProps } from '../../Popover';
4
+ /**
5
+ * The props for the `DataTableFilterButton` component.
6
+ *
7
+ * It extends the props of `IconButton` except `children` — the filter icon is
8
+ * rendered internally based on `filtered`, and the button is internally wired as
9
+ * the trigger of a `Popover` rendering `renderContent`. The link-related props
10
+ * (`href`, `customLinkComponent`, `target`) are also excluded: the trigger is
11
+ * always rendered as a `<button>` because a link cannot carry the disclosure
12
+ * semantics (`aria-haspopup` / `aria-expanded`).
13
+ */
14
+ export type DataTableFilterButtonProps = Omit<IconButtonProps, 'children' | 'aria-label' | 'href' | 'customLinkComponent' | 'target'> & {
15
+ /**
16
+ * The accessible name of the filter button.
17
+ * Override this value to provide a label specific to the column being filtered
18
+ * (e.g. `担当者で絞り込む`).
19
+ *
20
+ * @default '絞り込み'
21
+ */
22
+ 'aria-label'?: IconButtonProps['aria-label'];
23
+ /**
24
+ * Props applied to the filter popover that the button opens.
25
+ * `aria-label` and `aria-labelledby` name its dialog element (`aria-labelledby` can
26
+ * reference a visible title rendered inside `renderContent`). When neither is
27
+ * provided, the dialog is named `絞り込み条件`.
28
+ */
29
+ popoverProps?: Pick<ComponentPropsWithoutRef<'div'>, 'aria-label' | 'aria-labelledby'>;
30
+ /**
31
+ * Whether the column is currently filtered.
32
+ * The filter icon is switched internally according to this value:
33
+ *
34
+ * - `false`: outline filter icon
35
+ * - `true`: filled filter icon
36
+ *
37
+ * When `true`, the button is associated with an `適用中` description
38
+ * so the filtered state is exposed to assistive technologies without changing its accessible name.
39
+ *
40
+ * @default false
41
+ */
42
+ filtered?: boolean;
43
+ /**
44
+ * The content of the filter popover (e.g. a list of checkboxes for the column values).
45
+ * The popover itself — trigger wiring, positioning, and open/close behavior — is
46
+ * handled internally.
47
+ */
48
+ renderContent: PopoverProps['renderContent'];
49
+ };
@@ -0,0 +1,2 @@
1
+ export { DataTableFilterButton } from './DataTableFilterButton';
2
+ export type { DataTableFilterButtonProps } from './DataTableFilterButton.types';
@@ -0,0 +1 @@
1
+ export { DataTableFilterButton } from './DataTableFilterButton';
@@ -9,7 +9,7 @@ import { cx } from '../../../styled-system/css';
9
9
  import { useFixedColumns } from '../../utilities/dom/useFixedColumns';
10
10
  import { handleCheckboxCellClick } from '../utils/handleCheckbox';
11
11
  import { mergeRefs } from '../../utilities/dom/mergeRefs';
12
- export const DataTableHeaderCell = forwardRef(({ children, type = 'text', sortButtonProps, sortOrder, ascendingIconProps, descendingIconProps, unsortedIconProps, className, columnIndex, disabled = false, ...props }, ref) => {
12
+ export const DataTableHeaderCell = forwardRef(({ children, type = 'text', sortButtonProps, sortOrder, actions, ascendingIconProps, descendingIconProps, unsortedIconProps, className, columnIndex, disabled = false, ...props }, ref) => {
13
13
  const { size, leftFixedColumnIndex, rightFixedColumnIndex, enableRowSelection, showVerticalBorder } = useDataTableContext();
14
14
  const { cellRef, isFixedColumn, isEdgeFixedColumn, fixedPositionStyles } = useFixedColumns({
15
15
  enableRowSelection,
@@ -20,6 +20,7 @@ export const DataTableHeaderCell = forwardRef(({ children, type = 'text', sortBu
20
20
  const classes = dataTableHeaderCellSlotRecipe({
21
21
  size,
22
22
  type,
23
+ hasActions: Boolean(actions),
23
24
  fixedColumn: isFixedColumn,
24
25
  edgeFixedColumn: isEdgeFixedColumn,
25
26
  isCheckboxCell: enableRowSelection && columnIndex === 0,
@@ -53,7 +54,10 @@ export const DataTableHeaderCell = forwardRef(({ children, type = 'text', sortBu
53
54
  return (_jsx("div", { className: cx(classes.headerCell, 'mfui-DataTableHeaderCell__headerCell',
54
55
  /* TODO: Remove in v5.0.0 */ 'mfui-DataGridHeaderCell__headerCell'), children: isStringChild ? _jsx(Typography, { variant: "strongBody", children: children }) : children }));
55
56
  })();
57
+ // Actions are rendered as siblings of the sort button (never inside it)
58
+ // to avoid nesting interactive elements.
59
+ const cellContentWithActions = actions ? (_jsxs("div", { className: cx(classes.container, 'mfui-DataTableHeaderCell__container'), children: [cellContent, _jsx("div", { className: cx(classes.actionsContainer, 'mfui-DataTableHeaderCell__actionsContainer'), children: actions })] })) : (cellContent);
56
60
  return (_jsx("th", { ref: mergeRefs(ref, cellRef), className: cx(classes.root, 'mfui-DataTableHeaderCell__root',
57
- /* TODO: Remove in v5.0.0 */ 'mfui-DataGridHeaderCell__root', className), "aria-sort": sortOrder, style: fixedPositionStyles, ...props, children: cellContent }));
61
+ /* TODO: Remove in v5.0.0 */ 'mfui-DataGridHeaderCell__root', className), "aria-sort": sortOrder, style: fixedPositionStyles, ...props, children: cellContentWithActions }));
58
62
  });
59
63
  DataTableHeaderCell.displayName = 'DataTableHeaderCell';
@@ -1,4 +1,4 @@
1
- import { type ComponentPropsWithRef, type ComponentPropsWithoutRef } from 'react';
1
+ import { type ComponentPropsWithRef, type ComponentPropsWithoutRef, type ReactNode } from 'react';
2
2
  import { type CellDataType } from '../DataTable.types';
3
3
  /**
4
4
  * The props for the `DataTableHeaderCell` component.
@@ -61,6 +61,28 @@ export type DataTableHeaderCellProps = {
61
61
  * @default undefined
62
62
  */
63
63
  sortOrder?: 'ascending' | 'descending';
64
+ /**
65
+ * Action elements (e.g. `IconButton`) displayed at the right side of the header cell.
66
+ *
67
+ * The actions are rendered as siblings of the sort button, not inside it,
68
+ * so interactive elements are never nested even when used together with sorting.
69
+ *
70
+ * @default undefined
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * <DataTable.HeaderCell
75
+ * actions={
76
+ * <IconButton aria-label="絞り込み">
77
+ * <Filter />
78
+ * </IconButton>
79
+ * }
80
+ * >
81
+ * Name
82
+ * </DataTable.HeaderCell>
83
+ * ```
84
+ */
85
+ actions?: ReactNode;
64
86
  /**
65
87
  * **Do not pass this prop manually.**
66
88
  *
@@ -0,0 +1,4 @@
1
+ export declare const DataTableSortButton: import("react").ForwardRefExoticComponent<Omit<import("../..").IconButtonProps, "aria-label" | "children"> & {
2
+ 'aria-label'?: import("../..").IconButtonProps['aria-label'];
3
+ sortOrder?: 'ascending' | 'descending';
4
+ } & import("react").RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { SortAscending, SortDescending, SortUnsorted } from '@moneyforward/mfui-icons-react';
3
+ import { forwardRef } from 'react';
4
+ import { IconButton } from '../../IconButton';
5
+ import { dataTableSortButtonSlotRecipe } from '../../../styled-system/recipes';
6
+ import { cx } from '../../../styled-system/css';
7
+ export const DataTableSortButton = forwardRef(({ sortOrder, 'aria-label': ariaLabel = '並び替え', className, ...props }, ref) => {
8
+ const classes = dataTableSortButtonSlotRecipe({ sorted: Boolean(sortOrder) });
9
+ const sortIcon = (() => {
10
+ if (!sortOrder) {
11
+ return _jsx(SortUnsorted, {});
12
+ }
13
+ return sortOrder === 'ascending' ? _jsx(SortAscending, {}) : _jsx(SortDescending, {});
14
+ })();
15
+ return (_jsx(IconButton, { ref: ref, "aria-label": ariaLabel, className: cx(classes.root, 'mfui-DataTableSortButton__root', className), ...props, children: sortIcon }));
16
+ });
17
+ DataTableSortButton.displayName = 'DataTableSortButton';
@@ -0,0 +1,25 @@
1
+ import { type IconButtonProps } from '../../IconButton';
2
+ /**
3
+ * The props for the `DataTableSortButton` component.
4
+ *
5
+ * It extends the props of `IconButton` except `children` — the sort icon is
6
+ * rendered internally based on `sortOrder`.
7
+ */
8
+ export type DataTableSortButtonProps = Omit<IconButtonProps, 'children' | 'aria-label'> & {
9
+ /**
10
+ * The accessible name of the sort button.
11
+ * Override this value to provide a label specific to the column being sorted.
12
+ *
13
+ * @default '並び替え'
14
+ */
15
+ 'aria-label'?: IconButtonProps['aria-label'];
16
+ /**
17
+ * The current sort order of the column.
18
+ * The sort icon is switched internally according to this value:
19
+ *
20
+ * - `undefined`: unsorted icon
21
+ * - `'ascending'`: ascending icon
22
+ * - `'descending'`: descending icon
23
+ */
24
+ sortOrder?: 'ascending' | 'descending';
25
+ };
@@ -0,0 +1,2 @@
1
+ export { DataTableSortButton } from './DataTableSortButton';
2
+ export type { DataTableSortButtonProps } from './DataTableSortButton.types';
@@ -0,0 +1 @@
1
+ export { DataTableSortButton } from './DataTableSortButton';
@@ -6,4 +6,6 @@ export type { DataTableCellProps } from './DataTableCell';
6
6
  export type { DataTableHeaderProps } from './DataTableHeader';
7
7
  export type { DataTableHeaderCellProps } from './DataTableHeaderCell';
8
8
  export type { DataTableHeaderRowProps } from './DataTableHeaderRow';
9
+ export type { DataTableFilterButtonProps } from './DataTableFilterButton';
9
10
  export type { DataTableRowProps } from './DataTableRow';
11
+ export type { DataTableSortButtonProps } from './DataTableSortButton';
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback } from 'react';
3
3
  import dayjs from 'dayjs';
4
4
  import { useBasePickerContext } from '../../shared/BasePicker/BasePickerProvider';
5
+ import { useCalendarLocale } from '../../shared/CalendarLocale/CalendarLocaleContext';
5
6
  import { DateCell } from '../../shared/DateCell';
6
7
  import { getDateAriaAttributes } from '../../shared/accessibility';
7
8
  /**
@@ -14,10 +15,12 @@ import { getDateAriaAttributes } from '../../shared/accessibility';
14
15
  */
15
16
  export function DatePickerDateCell({ date, selected, disabled, onKeyDown, ...otherProps }) {
16
17
  const { viewingValue, setViewingValue, setPendingFocusDate } = useBasePickerContext();
18
+ const calendarLocale = useCalendarLocale();
17
19
  const ariaAttributes = getDateAriaAttributes({
18
20
  date,
19
21
  selected,
20
22
  disabled,
23
+ locale: calendarLocale,
21
24
  });
22
25
  const handleKeyDown = useCallback((event) => {
23
26
  const { key, currentTarget } = event;
@@ -6,7 +6,7 @@ export type DateRangePickerDateCellProps = DateCellProps & {
6
6
  isRangeEnd?: boolean;
7
7
  };
8
8
  /**
9
- * DateRangePicker-specific DateCell component with enhanced accessibility
10
- * for Japanese users. Provides configurable labels and range-specific context.
9
+ * DateRangePicker-specific DateCell component with enhanced accessibility.
10
+ * Provides locale-aware labels and range-specific context.
11
11
  */
12
12
  export declare function DateRangePickerDateCell({ date, selected, inRange, disabled, isRangeStart, isRangeEnd, ...rest }: DateRangePickerDateCellProps): import("react/jsx-runtime").JSX.Element;
@@ -1,11 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { DateCell } from '../../../shared/DateCell';
3
+ import { useCalendarLocale } from '../../../shared/CalendarLocale/CalendarLocaleContext';
3
4
  import { getDateAriaAttributes } from '../../../shared/accessibility';
4
5
  /**
5
- * DateRangePicker-specific DateCell component with enhanced accessibility
6
- * for Japanese users. Provides configurable labels and range-specific context.
6
+ * DateRangePicker-specific DateCell component with enhanced accessibility.
7
+ * Provides locale-aware labels and range-specific context.
7
8
  */
8
9
  export function DateRangePickerDateCell({ date, selected, inRange, disabled, isRangeStart, isRangeEnd, ...rest }) {
10
+ const calendarLocale = useCalendarLocale();
9
11
  const ariaAttributes = getDateAriaAttributes({
10
12
  date,
11
13
  selected,
@@ -13,6 +15,7 @@ export function DateRangePickerDateCell({ date, selected, inRange, disabled, isR
13
15
  disabled,
14
16
  isRangeStart,
15
17
  isRangeEnd,
18
+ locale: calendarLocale,
16
19
  });
17
20
  return (_jsx(DateCell, { ...rest, date: date, selected: selected, inRange: inRange, disabled: disabled, ...ariaAttributes }));
18
21
  }
@@ -16,7 +16,7 @@ import { useDateRangeTriggerInputClick } from './hooks/useDateRangeTriggerInputC
16
16
  * Trigger component for DateRangePicker
17
17
  * Handles the display and interaction of the date range input field
18
18
  */
19
- export const DateRangePickerTrigger = forwardRef(({ enableClearValue = false, invalid = false, size = 'medium', disabled = false, wrapperProps = {}, clearButtonProps = {}, startInputProps = {}, endInputProps = {}, calendarButtonProps = {}, startInputRef, endInputRef, popoverOpenFunction, format = 'YYYY-MM-DD', ...props }, ref) => {
19
+ export const DateRangePickerTrigger = forwardRef(({ enableClearValue = false, invalid = false, caution = false, size = 'medium', disabled = false, wrapperProps = {}, clearButtonProps = {}, startInputProps = {}, endInputProps = {}, calendarButtonProps = {}, startInputRef, endInputRef, popoverOpenFunction, format = 'YYYY-MM-DD', ...props }, ref) => {
20
20
  const { toggle, disableAutoOpen } = useDateRangePickerContext();
21
21
  const { handleInputClick } = useDateRangeTriggerInputClick(disableAutoOpen ?? false, popoverOpenFunction);
22
22
  const { onClick: onClickClearButton, 'aria-label': clearAriaLabel = LOCALE.CLEAR_BUTTON_LABEL } = clearButtonProps;
@@ -35,7 +35,8 @@ export const DateRangePickerTrigger = forwardRef(({ enableClearValue = false, in
35
35
  const { placeholder: startPlaceholder = LOCALE.START_DATE_PLACEHOLDER } = startInputProps;
36
36
  const { placeholder: endPlaceholder = LOCALE.END_DATE_PLACEHOLDER } = endInputProps;
37
37
  const shouldShowClearButton = enableClearValue && (enhancedStartInputProps.value || enhancedEndInputProps.value);
38
- const classes = baseRangePickerTriggerSlotRecipe({ invalid, disabled, size });
38
+ // The error state takes precedence over the caution state
39
+ const classes = baseRangePickerTriggerSlotRecipe({ invalid, caution: caution && !invalid, disabled, size });
39
40
  const handleClickClearButton = useCallback((event) => {
40
41
  handleClear();
41
42
  onClickClearButton?.(event);
@@ -11,6 +11,23 @@ export type DateRangePickerTriggerProps = {
11
11
  * @default false
12
12
  */
13
13
  invalid?: boolean;
14
+ /**
15
+ * Whether the inputs need the user's attention while still being submittable.
16
+ *
17
+ * This changes the border color only, so you must also render an adjacent
18
+ * `<HelpMessage messageType="caution">` describing the concern — color alone does not
19
+ * convey the state (WCAG 2.2 1.4.1).
20
+ *
21
+ * Associate that `HelpMessage` with both inputs via `startInputProps['aria-describedby']`
22
+ * and `endInputProps['aria-describedby']` so screen readers announce it on focus, and give
23
+ * it `role="status"` so it is also announced the moment it appears while focus is already
24
+ * inside one of the inputs.
25
+ *
26
+ * Ignored while `invalid` is `true`, because the error state takes precedence.
27
+ *
28
+ * @default false
29
+ */
30
+ caution?: boolean;
14
31
  /**
15
32
  * Whether to enable the clear functionality
16
33
  *
@@ -10,6 +10,7 @@ import { cx } from '../../../../styled-system/css';
10
10
  import { dayjs } from '../../../utilities/date/dayjs';
11
11
  import { DateRangePickerPopover } from '../../DateRangePicker/DateRangePickerPopover';
12
12
  import { CalendarLocaleProvider } from '../../shared/CalendarLocale/CalendarLocaleContext';
13
+ import { getBaseRangePickerTriggerLabel } from '../../shared/BaseRangePicker/BaseRangePickerTrigger/constants';
13
14
  import { useBaseRangePickerContext } from '../../shared/BaseRangePicker/BaseRangePickerProvider';
14
15
  /**
15
16
  * Internal component that accesses BaseRangePickerContext for display and clear handling.
@@ -29,5 +30,6 @@ export function InternalDateRangePickerContent({ classes, triggerRef, dialogId,
29
30
  const dateToFocus = startDate ?? endDate ?? new Date();
30
31
  setPendingFocusDate(dayjs(dateToFocus).format('YYYY-MM-DD'));
31
32
  }, [startDate, endDate, setPendingFocusDate]);
32
- return (_jsx(Popover, { open: isCalendarOpen, targetDOMNode: targetDOMNode, minWidth: "min-content", allowedPlacements: ['bottom-start', 'top-start'], renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterDateRangePicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterDateRangePicker__trigger', className), "data-selected": hasValue, "data-mfui-has-clear-button": enableClearButton ? hasValue : null, "aria-controls": dialogId, "aria-expanded": isCalendarOpen, "aria-haspopup": "dialog", onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur, children: [_jsx("span", { className: cx(classes.content, 'mfui-FilterDateRangePicker__content'), "data-mfui-content": "filter-date-range-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-date-range-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-date-range-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-date-range-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterDateRangePicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), enableClearButton && hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterDateRangePicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "filter-date-range-picker-clear-button", disabled: disabled, onClick: handleTriggerClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: cx(classes.popoverWrapper, 'mfui-FilterDateRangePicker__popoverWrapper'), children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(DateRangePickerPopover, {}) }) })), onOpenStateChanged: toggleCalendar, onOpen: handlePopoverOpen }));
33
+ return (_jsx(Popover, { open: isCalendarOpen, targetDOMNode: targetDOMNode, minWidth: "min-content", allowedPlacements: ['bottom-start', 'top-start'], renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterDateRangePicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterDateRangePicker__trigger', className), "data-selected": hasValue, "data-mfui-has-clear-button": enableClearButton ? hasValue : null, "aria-controls": dialogId, "aria-expanded": isCalendarOpen, "aria-haspopup": "dialog", onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur, children: [_jsx("span", { className: cx(classes.content, 'mfui-FilterDateRangePicker__content'), "data-mfui-content": "filter-date-range-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-date-range-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-date-range-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-date-range-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterDateRangePicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), enableClearButton && hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterDateRangePicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ??
34
+ getBaseRangePickerTriggerLabel('CLEAR_BUTTON_LABEL', calendarLocale), "data-mfui-content": "filter-date-range-picker-clear-button", disabled: disabled, onClick: handleTriggerClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: cx(classes.popoverWrapper, 'mfui-FilterDateRangePicker__popoverWrapper'), children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(DateRangePickerPopover, {}) }) })), onOpenStateChanged: toggleCalendar, onOpen: handlePopoverOpen }));
33
35
  }
@@ -48,7 +48,8 @@ export type FilterDateRangePickerProps = {
48
48
  /**
49
49
  * Props for the clear button.
50
50
  *
51
- * @property aria-label - The alternative text for the clear button. Default is '値をクリアする'.
51
+ * @property aria-label - The alternative text for the clear button.
52
+ * Default follows `calendarLocale` ('値をクリアする' for 'ja', 'Clear value' for 'en').
52
53
  */
53
54
  clearButtonProps?: {
54
55
  'aria-label'?: string;
@@ -45,7 +45,8 @@ export type FilterMonthPickerProps = {
45
45
  /**
46
46
  * Props for the clear button.
47
47
  *
48
- * @property aria-label - The alternative text for the clear button. Default is '値をクリアする'.
48
+ * @property aria-label - The alternative text for the clear button.
49
+ * Default follows `calendarLocale` ('値をクリアする' for 'ja', 'Clear value' for 'en').
49
50
  */
50
51
  clearButtonProps?: {
51
52
  'aria-label'?: string;
@@ -10,6 +10,7 @@ import { cx } from '../../../../styled-system/css';
10
10
  import { dayjs } from '../../../utilities/date/dayjs';
11
11
  import { MonthPickerPanel } from '../../MonthPicker/MonthPickerPanel/MonthPickerPanel';
12
12
  import { CalendarLocaleProvider } from '../../shared/CalendarLocale/CalendarLocaleContext';
13
+ import { getBasePickerLabel } from '../../shared/BasePicker/constants';
13
14
  import { useBasePickerContext } from '../../shared/BasePicker/BasePickerProvider';
14
15
  import { useFocusTrap } from '../../../utilities/dom/useFocusTrap';
15
16
  import { getFocusableNodes } from '../../../utilities/dom/getFocusableNodes';
@@ -39,7 +40,7 @@ export function InternalMonthPickerPopover({ currentValue, defaultValue, isPicke
39
40
  setViewingValue(monthToFocus);
40
41
  setPendingFocusDate(dayjs(monthToFocus).format(MONTH_BASE_FORMAT));
41
42
  }, [currentValue, defaultValue, setViewingValue, setPendingFocusDate]);
42
- return (_jsx(Popover, { open: isPickerOpen, targetDOMNode: targetDOMNode, renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterMonthPicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterMonthPicker__trigger', className), "data-selected": hasValue, "data-mfui-has-clear-button": enableClearButton ? hasValue : null, "aria-controls": dialogId, "aria-expanded": isPickerOpen, "aria-haspopup": "dialog", onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur, children: [_jsx("span", { className: cx(classes.content, 'mfui-FilterMonthPicker__content'), "data-mfui-content": "filter-month-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-month-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-month-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-month-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterMonthPicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), enableClearButton && hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterMonthPicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "filter-month-picker-clear-button", disabled: disabled, onClick: handleClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: "mfui-FilterMonthPicker__popoverWrapper", onKeyDown: handleOnKeyDown, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(MonthPickerPanel, { viewingValue: viewingValue, setViewingValue: setViewingValue, value: currentValue, minMonth: minMonth, maxMonth: maxMonth, checkDisabledMonth: checkDisabledMonth, onChange: (date) => {
43
+ return (_jsx(Popover, { open: isPickerOpen, targetDOMNode: targetDOMNode, renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterMonthPicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterMonthPicker__trigger', className), "data-selected": hasValue, "data-mfui-has-clear-button": enableClearButton ? hasValue : null, "aria-controls": dialogId, "aria-expanded": isPickerOpen, "aria-haspopup": "dialog", onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur, children: [_jsx("span", { className: cx(classes.content, 'mfui-FilterMonthPicker__content'), "data-mfui-content": "filter-month-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-month-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-month-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-month-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterMonthPicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), enableClearButton && hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterMonthPicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ?? getBasePickerLabel('CLEAR_VALUE', calendarLocale), "data-mfui-content": "filter-month-picker-clear-button", disabled: disabled, onClick: handleClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: "mfui-FilterMonthPicker__popoverWrapper", onKeyDown: handleOnKeyDown, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(MonthPickerPanel, { viewingValue: viewingValue, setViewingValue: setViewingValue, value: currentValue, minMonth: minMonth, maxMonth: maxMonth, checkDisabledMonth: checkDisabledMonth, onChange: (date) => {
43
44
  handleValueChange(date);
44
45
  } }) }) })), minWidth: "min-content", allowedPlacements: ['bottom-start', 'top-start'], onOpenStateChanged: togglePicker, onOpen: handlePopoverOpen }));
45
46
  }
@@ -1,5 +1,6 @@
1
1
  import { type MonthRangePickerMonthCellProps } from './MonthRangePickerMonthCell.types';
2
2
  import 'dayjs/locale/en';
3
+ import 'dayjs/locale/ja';
3
4
  /**
4
5
  * MonthRangePickerMonthCell component that wraps MonthCell with range selection visual states.
5
6
  *
@@ -6,6 +6,7 @@ import { getMonthCellMonthFormat } from '../../shared/utilities/monthCellMonthFo
6
6
  import { dayjs } from '../../../utilities/date/dayjs';
7
7
  import { MONTH_RANGE_PICKER_A11Y_LABELS } from './constants';
8
8
  import 'dayjs/locale/en';
9
+ import 'dayjs/locale/ja';
9
10
  /**
10
11
  * MonthRangePickerMonthCell component that wraps MonthCell with range selection visual states.
11
12
  *
@@ -26,26 +27,28 @@ export function MonthRangePickerMonthCell({ isInRange, isRangeStart, isRangeEnd,
26
27
  const monthFormat = useMemo(() => getMonthCellMonthFormat(calendarLocale, monthCellProps.date), [calendarLocale, monthCellProps.date]);
27
28
  // Create accessible label for range selection
28
29
  const accessibleLabel = useMemo(() => {
29
- const monthText = calendarLocale === 'en'
30
- ? dayjs(monthCellProps.date).locale('en').format('MMMM YYYY')
31
- : dayjs(monthCellProps.date).format('MMMM YYYY');
30
+ // Fall back to 'ja' so an unknown locale from untyped consumers cannot crash the calendar
31
+ const safeLocale = calendarLocale in MONTH_RANGE_PICKER_A11Y_LABELS ? calendarLocale : 'ja';
32
+ const labels = MONTH_RANGE_PICKER_A11Y_LABELS[safeLocale];
33
+ // Pin the dayjs locale per call so the output does not depend on the global dayjs locale
34
+ const monthText = dayjs(monthCellProps.date).locale(safeLocale).format('MMMM YYYY');
32
35
  const statusParts = [];
33
36
  if (monthCellProps.disabled) {
34
- statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.UNAVAILABLE);
37
+ statusParts.push(labels.UNAVAILABLE);
35
38
  }
36
39
  else if (selected) {
37
40
  if (isRangeStart) {
38
- statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.RANGE_START);
41
+ statusParts.push(labels.RANGE_START);
39
42
  }
40
43
  else if (isRangeEnd) {
41
- statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.RANGE_END);
44
+ statusParts.push(labels.RANGE_END);
42
45
  }
43
46
  else {
44
- statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.SELECTED);
47
+ statusParts.push(labels.SELECTED);
45
48
  }
46
49
  }
47
50
  else if (isInRange) {
48
- statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.IN_RANGE);
51
+ statusParts.push(labels.IN_RANGE);
49
52
  }
50
53
  return statusParts.length > 0 ? `${monthText}, ${statusParts.join(', ')}` : monthText;
51
54
  }, [calendarLocale, monthCellProps.date, monthCellProps.disabled, selected, isInRange, isRangeStart, isRangeEnd]);