Skip to content

Commit fcdf86f

Browse files
rhamiltoclaude
andauthored
feat(BulkSelect): add internationalization (i18n) support (#896)
Adds optional props for customizing all user-facing strings in BulkSelect to support internationalization and custom terminology. All changes are backward compatible with sensible English defaults. New props: - selectNoneLabel: Custom label for "Select none" option - selectPageLabel: Function receiving pageCount to format "Select page" label - selectAllLabel: Function receiving totalCount to format "Select all" label - selectedLabel: Function receiving selectedCount to format the displayed count Implementation details: - Default label functions are hoisted to module level for stable references - Label functions are called inside useMemo to prevent unnecessary re-renders - Optimized for performance with expensive operations like pseudolocalization - Parameter names avoid i18n reserved keywords (e.g., 'count' for pluralization) Fixes #895 Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 9032e97 commit fcdf86f

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

packages/module/src/BulkSelect/BulkSelect.test.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,35 @@ describe('BulkSelect component', () => {
6969
expect(toggleWrapper).toBeInTheDocument();
7070
expect(toggleWrapper).toHaveClass('custom-menu-toggle');
7171
});
72+
73+
test('should render with custom i18n labels', async () => {
74+
const user = userEvent.setup();
75+
render(
76+
<BulkSelect
77+
canSelectAll
78+
pageCount={5}
79+
totalCount={10}
80+
selectedCount={2}
81+
pageSelected={false}
82+
pagePartiallySelected={true}
83+
onSelect={() => null}
84+
selectNoneLabel="Aucune sélection (0)"
85+
selectPageLabel={(pageCount) => `Sélectionner la page${pageCount ? ` (${pageCount})` : ''}`}
86+
selectAllLabel={(totalCount) => `Tout sélectionner${totalCount ? ` (${totalCount})` : ''}`}
87+
selectedLabel={(selectedCount) => `${selectedCount} sélectionné${selectedCount > 1 ? 's' : ''}`}
88+
/>
89+
);
90+
91+
// Check custom selected label
92+
expect(screen.getByText('2 sélectionnés')).toBeInTheDocument();
93+
94+
// Open the dropdown to check option labels
95+
const toggleButton = screen.getByLabelText('Bulk select toggle');
96+
await user.click(toggleButton);
97+
98+
// Check custom dropdown labels
99+
expect(screen.getByText('Aucune sélection (0)')).toBeInTheDocument();
100+
expect(screen.getByText('Sélectionner la page (5)')).toBeInTheDocument();
101+
expect(screen.getByText('Tout sélectionner (10)')).toBeInTheDocument();
102+
});
72103
});

packages/module/src/BulkSelect/BulkSelect.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export const BulkSelectValue = {
2222

2323
export type BulkSelectValue = (typeof BulkSelectValue)[keyof typeof BulkSelectValue];
2424

25+
const defaultSelectPageLabel = (pageCount?: number) => `Select page${pageCount ? ` (${pageCount})` : ''}`;
26+
const defaultSelectAllLabel = (totalCount?: number) => `Select all${totalCount ? ` (${totalCount})` : ''}`;
27+
const defaultSelectedLabel = (selectedCount: number) => `${selectedCount} selected`;
28+
2529
/** extends DropdownProps */
2630
export interface BulkSelectProps extends Omit<DropdownProps, 'toggle' | 'onSelect'> {
2731
/** BulkSelect className */
@@ -50,6 +54,14 @@ export interface BulkSelectProps extends Omit<DropdownProps, 'toggle' | 'onSelec
5054
dropdownListProps?: Omit<DropdownListProps, 'children'>;
5155
/** Additional props for MenuToggleProps */
5256
menuToggleProps?: Omit<MenuToggleProps, 'children' | 'splitButtonItems' | 'ref' | 'isExpanded' | 'onClick'>;
57+
/** Custom label for "Select none" option. Defaults to "Select none (0)" */
58+
selectNoneLabel?: string;
59+
/** Custom label for "Select page" option. Receives pageCount as parameter. Defaults to "Select page (N)" */
60+
selectPageLabel?: (pageCount?: number) => string;
61+
/** Custom label for "Select all" option. Receives totalCount as parameter. Defaults to "Select all (N)" */
62+
selectAllLabel?: (totalCount?: number) => string;
63+
/** Custom label formatter for selected count. Receives selectedCount as parameter. Defaults to "N selected" */
64+
selectedLabel?: (selectedCount: number) => string;
5365
}
5466

5567
export const BulkSelect: FC<BulkSelectProps> = ({
@@ -65,6 +77,10 @@ export const BulkSelect: FC<BulkSelectProps> = ({
6577
menuToggleCheckboxProps,
6678
dropdownListProps,
6779
menuToggleProps,
80+
selectNoneLabel = 'Select none (0)',
81+
selectPageLabel = defaultSelectPageLabel,
82+
selectAllLabel = defaultSelectAllLabel,
83+
selectedLabel = defaultSelectedLabel,
6884
...props
6985
}: BulkSelectProps) => {
7086
const [ isOpen, setOpen ] = useState(false);
@@ -73,23 +89,25 @@ export const BulkSelect: FC<BulkSelectProps> = ({
7389
() => (
7490
<>
7591
<DropdownItem ouiaId={`${ouiaId}-select-none`} value={BulkSelectValue.none} key={BulkSelectValue.none}>
76-
Select none (0)
92+
{selectNoneLabel}
7793
</DropdownItem>
7894
{isDataPaginated && (
7995
<DropdownItem ouiaId={`${ouiaId}-select-page`} value={BulkSelectValue.page} key={BulkSelectValue.page}>
80-
{`Select page${pageCount ? ` (${pageCount})` : ''}`}
96+
{selectPageLabel(pageCount)}
8197
</DropdownItem>
8298
)}
8399
{canSelectAll && (
84100
<DropdownItem ouiaId={`${ouiaId}-select-all`} value={BulkSelectValue.all} key={BulkSelectValue.all}>
85-
{`Select all${totalCount ? ` (${totalCount})` : ''}`}
101+
{selectAllLabel(totalCount)}
86102
</DropdownItem>
87103
)}
88104
</>
89105
),
90-
[ isDataPaginated, canSelectAll, ouiaId, pageCount, totalCount ]
106+
[ isDataPaginated, canSelectAll, ouiaId, selectNoneLabel, selectPageLabel, selectAllLabel, pageCount, totalCount ]
91107
);
92108

109+
const selectedLabelText = selectedLabel(selectedCount);
110+
93111
const allOption = isDataPaginated ? BulkSelectValue.page : BulkSelectValue.all;
94112
const noneOption = isDataPaginated ? BulkSelectValue.nonePage : BulkSelectValue.none;
95113

@@ -129,7 +147,7 @@ export const BulkSelect: FC<BulkSelectProps> = ({
129147
>
130148
{selectedCount > 0 ? (
131149
<span data-ouia-component-id={`${ouiaId}-text`}>
132-
{`${selectedCount} selected`}
150+
{selectedLabelText}
133151
</span>
134152
) : null}
135153
</MenuToggleCheckbox>

0 commit comments

Comments
 (0)