Skip to content

Commit 41da37c

Browse files
feat: add internationalization support for openCalendarAriaLabel (#4082)
1 parent 5a7ae01 commit 41da37c

File tree

19 files changed

+244
-10
lines changed

19 files changed

+244
-10
lines changed

src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10320,7 +10320,7 @@ Defaults to \`day\`.",
1032010320
the component.",
1032110321
"i18nTag": true,
1032210322
"inlineType": {
10323-
"name": "CalendarProps.I18nStrings",
10323+
"name": "DatePickerProps.I18nStrings",
1032410324
"properties": [
1032510325
{
1032610326
"name": "currentMonthAriaLabel",
@@ -10337,6 +10337,22 @@ the component.",
1033710337
"optional": true,
1033810338
"type": "string",
1033910339
},
10340+
{
10341+
"inlineType": {
10342+
"name": "DatePickerProps.OpenCalendarAriaLabel",
10343+
"parameters": [
10344+
{
10345+
"name": "selectedDate",
10346+
"type": "string | null",
10347+
},
10348+
],
10349+
"returnType": "string",
10350+
"type": "function",
10351+
},
10352+
"name": "openCalendarAriaLabel",
10353+
"optional": true,
10354+
"type": "DatePickerProps.OpenCalendarAriaLabel",
10355+
},
1034010356
{
1034110357
"name": "previousMonthAriaLabel",
1034210358
"optional": true,
@@ -10357,7 +10373,7 @@ the component.",
1035710373
},
1035810374
"name": "i18nStrings",
1035910375
"optional": true,
10360-
"type": "CalendarProps.I18nStrings",
10376+
"type": "DatePickerProps.I18nStrings",
1036110377
},
1036210378
{
1036310379
"deprecatedTag": "The usage of the \`id\` attribute is reserved for internal use cases. For testing and other use cases,
@@ -10440,6 +10456,7 @@ Supported values and formats are listed in the
1044010456
"type": "string",
1044110457
},
1044210458
{
10459+
"deprecatedTag": "Use \`i18nStrings.openCalendarAriaLabel\` instead.",
1044310460
"description": "Specifies a function that generates the \`aria-label\` for the 'open calendar' button. The \`selectedDate\` parameter is
1044410461
a human-readable localised string representing the current value of the input.
1044510462
(for example, \`\`selectedDate => 'Choose Date' + (selectedDate ? \`, selected date is \${selectedDate}\` : '')\`\`)",

src/date-picker/__tests__/date-picker.test.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { render } from '@testing-library/react';
66

77
import DatePicker, { DatePickerProps } from '../../../lib/components/date-picker';
88
import FormField from '../../../lib/components/form-field';
9+
import TestI18nProvider from '../../../lib/components/i18n/testing';
910
import { NonCancelableEventHandler } from '../../../lib/components/internal/events';
1011
import createWrapper from '../../../lib/components/test-utils/dom';
1112
import DatePickerWrapper from '../../../lib/components/test-utils/dom/date-picker';
@@ -422,3 +423,68 @@ describe('generates aria-label for the "Open calendar" button', () => {
422423
expect(button).toHaveAttribute('aria-label', 'Choose Date');
423424
});
424425
});
426+
427+
describe('i18n', () => {
428+
test('uses openCalendarAriaLabel from i18n provider when no date is selected', () => {
429+
const { container } = render(
430+
<TestI18nProvider
431+
messages={{
432+
'date-picker': {
433+
'i18nStrings.openCalendarAriaLabel':
434+
'{selectedDate, select, none {Choose calendar date} other {Choose calendar date, selected {selectedDate}}}',
435+
},
436+
}}
437+
>
438+
<DatePicker value="" locale="en-US" openCalendarAriaLabel={undefined} />
439+
</TestI18nProvider>
440+
);
441+
const wrapper = createWrapper(container).findDatePicker()!;
442+
expect(wrapper.findOpenCalendarButton().getElement()).toHaveAttribute('aria-label', 'Choose calendar date');
443+
});
444+
445+
test('uses openCalendarAriaLabel from i18n provider when date is selected', () => {
446+
const { container } = render(
447+
<TestI18nProvider
448+
messages={{
449+
'date-picker': {
450+
'i18nStrings.openCalendarAriaLabel':
451+
'{selectedDate, select, none {Choose calendar date} other {Choose calendar date, selected {selectedDate}}}',
452+
},
453+
}}
454+
>
455+
<DatePicker value="2003-04-11" locale="en-US" openCalendarAriaLabel={undefined} />
456+
</TestI18nProvider>
457+
);
458+
const wrapper = createWrapper(container).findDatePicker()!;
459+
expect(wrapper.findOpenCalendarButton().getElement()).toHaveAttribute(
460+
'aria-label',
461+
'Choose calendar date, selected Friday, April 11, 2003'
462+
);
463+
});
464+
465+
test('uses openCalendarAriaLabel prop over i18n provider', () => {
466+
const { container } = render(
467+
<TestI18nProvider
468+
messages={{
469+
'date-picker': {
470+
'i18nStrings.openCalendarAriaLabel':
471+
'{selectedDate, select, none {Choose calendar date} other {Choose calendar date, selected {selectedDate}}}',
472+
},
473+
}}
474+
>
475+
<DatePicker
476+
value="2003-04-11"
477+
locale="en-US"
478+
openCalendarAriaLabel={selectedDate =>
479+
selectedDate ? `Custom label, selected ${selectedDate}` : 'Custom label'
480+
}
481+
/>
482+
</TestI18nProvider>
483+
);
484+
const wrapper = createWrapper(container).findDatePicker()!;
485+
expect(wrapper.findOpenCalendarButton().getElement()).toHaveAttribute(
486+
'aria-label',
487+
'Custom label, selected Friday, April 11, 2003'
488+
);
489+
});
490+
});

src/date-picker/index.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { InternalButton } from '../button/internal';
1111
import InternalCalendar from '../calendar/internal';
1212
import { useFormFieldContext } from '../contexts/form-field.js';
1313
import InternalDateInput from '../date-input/internal';
14+
import { useInternalI18n } from '../i18n/context.js';
1415
import { useLocale } from '../i18n/context.js';
1516
import { InputProps } from '../input/interfaces';
1617
import { getBaseProps } from '../internal/base-component';
@@ -73,6 +74,7 @@ const DatePicker = React.forwardRef(
7374
});
7475
checkControlled('DatePicker', 'value', value, 'onChange', onChange);
7576

77+
const i18n = useInternalI18n('date-picker');
7678
const contextLocale = useLocale();
7779
const normalizedLocale = normalizeLocale('DatePicker', locale || contextLocale);
7880

@@ -123,13 +125,17 @@ const DatePicker = React.forwardRef(
123125

124126
const hasFullValue = isValidFullDate({ date: value, granularity });
125127

126-
const buttonAriaLabel =
127-
openCalendarAriaLabel &&
128-
openCalendarAriaLabel(
129-
hasFullValue && parsedValue
130-
? getSelectedDateLabel({ date: parsedValue, granularity, locale: normalizedLocale })
131-
: null
132-
);
128+
const defaultOpenCalendarAriaLabel = i18n(
129+
'i18nStrings.openCalendarAriaLabel',
130+
i18nStrings?.openCalendarAriaLabel ?? openCalendarAriaLabel,
131+
format => (selectedDate: string | null) => format({ selectedDate: selectedDate ?? 'none' })
132+
);
133+
134+
const buttonAriaLabel = defaultOpenCalendarAriaLabel?.(
135+
hasFullValue && parsedValue
136+
? getSelectedDateLabel({ date: parsedValue, granularity, locale: normalizedLocale })
137+
: null
138+
);
133139

134140
const trigger = (
135141
<div className={styles['date-picker-trigger']}>

src/date-picker/interfaces.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface DatePickerProps
2222
* Specifies a function that generates the `aria-label` for the 'open calendar' button. The `selectedDate` parameter is
2323
* a human-readable localised string representing the current value of the input.
2424
* (for example, ``selectedDate => 'Choose Date' + (selectedDate ? `, selected date is ${selectedDate}` : '')``)
25+
* @deprecated Use `i18nStrings.openCalendarAriaLabel` instead.
2526
*/
2627
openCalendarAriaLabel?: DatePickerProps.OpenCalendarAriaLabel;
2728

@@ -130,7 +131,14 @@ export namespace DatePickerProps {
130131
focus(): void;
131132
}
132133

133-
export type I18nStrings = CalendarProps.I18nStrings;
134+
export interface I18nStrings extends CalendarProps.I18nStrings {
135+
/**
136+
* Specifies a function that generates the `aria-label` for the 'open calendar' button. The `selectedDate` parameter is
137+
* a human-readable localised string representing the current value of the input, or `null` when no date is selected.
138+
* @i18n
139+
*/
140+
openCalendarAriaLabel?: OpenCalendarAriaLabel;
141+
}
134142

135143
export type Granularity = DateGranularity;
136144

src/i18n/messages-types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ export interface I18nFormatArgTypes {
166166
"copy-to-clipboard": {
167167
"i18nStrings.copyButtonText": never;
168168
}
169+
"date-picker": {
170+
"i18nStrings.openCalendarAriaLabel": {
171+
"selectedDate": string;
172+
}
173+
}
169174
"date-range-picker": {
170175
"i18nStrings.relativeModeTitle": never;
171176
"i18nStrings.absoluteModeTitle": never;
@@ -217,6 +222,12 @@ export interface I18nFormatArgTypes {
217222
"drawer": {
218223
"i18nStrings.loadingText": never;
219224
}
225+
"error-boundary": {
226+
"i18nStrings.headerText": never;
227+
"i18nStrings.descriptionText": never;
228+
"i18nStrings.descriptionWithFeedbackText": never;
229+
"i18nStrings.refreshActionText": never;
230+
}
220231
"file-token-group": {
221232
"i18nStrings.limitShowFewer": never;
222233
"i18nStrings.limitShowMore": never;

src/i18n/messages/all.ar.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@
130130
"copy-to-clipboard": {
131131
"i18nStrings.copyButtonText": "نسخ"
132132
},
133+
"date-picker": {
134+
"i18nStrings.openCalendarAriaLabel": "{selectedDate, select, none {اختر التاريخ} other {اختر التاريخ، التاريخ المحدد هو {selectedDate}}}"
135+
},
133136
"date-range-picker": {
134137
"i18nStrings.relativeModeTitle": "الوضع النسبي",
135138
"i18nStrings.absoluteModeTitle": "الوضع المُطلق",
@@ -172,6 +175,12 @@
172175
"drawer": {
173176
"i18nStrings.loadingText": "جار تحميل المحتوى"
174177
},
178+
"error-boundary": {
179+
"i18nStrings.headerText": "خطأ غير متوقع، فشل عرض المحتوى",
180+
"i18nStrings.descriptionText": "حدِّث الصفحة للمحاولة مرة أخرى.",
181+
"i18nStrings.descriptionWithFeedbackText": "حدِّث الصفحة للمحاولة مرة أخرى. نحن نتتبع هذه المشكلة، ولكن يمكنك مشاركة <Feedback>المزيد من المعلومات هنا</Feedback>.",
182+
"i18nStrings.refreshActionText": "تحديث الصفحة"
183+
},
175184
"file-token-group": {
176185
"i18nStrings.limitShowFewer": "إظهار عدد أقل من عوامل التصفية",
177186
"i18nStrings.limitShowMore": "عرض المزيد من عوامل التصفية",

src/i18n/messages/all.de.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@
130130
"copy-to-clipboard": {
131131
"i18nStrings.copyButtonText": "Kopieren"
132132
},
133+
"date-picker": {
134+
"i18nStrings.openCalendarAriaLabel": "{selectedDate, select, none {Datum wählen} other {Datum wählen, ausgewähltes Datum ist {selectedDate}}}"
135+
},
133136
"date-range-picker": {
134137
"i18nStrings.relativeModeTitle": "Relativer Modus",
135138
"i18nStrings.absoluteModeTitle": "Absoluter Modus",
@@ -172,6 +175,12 @@
172175
"drawer": {
173176
"i18nStrings.loadingText": "Inhalte werden geladen"
174177
},
178+
"error-boundary": {
179+
"i18nStrings.headerText": "Unerwarteter Fehler, Inhalt konnte nicht angezeigt werden",
180+
"i18nStrings.descriptionText": "Aktualisieren Sie die Seite, um es erneut zu versuchen.",
181+
"i18nStrings.descriptionWithFeedbackText": "Aktualisieren Sie die Seite, um es erneut zu versuchen. Wir verfolgen dieses Problem nach, Sie können aber <Feedback>hier zusätzliche Informationen mitteilen</Feedback>.",
182+
"i18nStrings.refreshActionText": "Seite aktualisieren"
183+
},
175184
"file-token-group": {
176185
"i18nStrings.limitShowFewer": "Weniger anzeigen",
177186
"i18nStrings.limitShowMore": "Mehr anzeigen",

src/i18n/messages/all.en-GB.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@
130130
"copy-to-clipboard": {
131131
"i18nStrings.copyButtonText": "Copy"
132132
},
133+
"date-picker": {
134+
"i18nStrings.openCalendarAriaLabel": "{selectedDate, select, none {Choose a date} other {Choose a date, the selected date is {selectedDate}}}"
135+
},
133136
"date-range-picker": {
134137
"i18nStrings.relativeModeTitle": "Relative mode",
135138
"i18nStrings.absoluteModeTitle": "Absolute mode",
@@ -172,6 +175,12 @@
172175
"drawer": {
173176
"i18nStrings.loadingText": "Loading content"
174177
},
178+
"error-boundary": {
179+
"i18nStrings.headerText": "Unexpected error, content failed to show",
180+
"i18nStrings.descriptionText": "Refresh to try again.",
181+
"i18nStrings.descriptionWithFeedbackText": "Refresh to try again. We are tracking this issue, but you can share <Feedback>more information here</Feedback>.",
182+
"i18nStrings.refreshActionText": "Refresh page"
183+
},
175184
"file-token-group": {
176185
"i18nStrings.limitShowFewer": "Show fewer",
177186
"i18nStrings.limitShowMore": "Show more",

src/i18n/messages/all.en.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@
130130
"copy-to-clipboard": {
131131
"i18nStrings.copyButtonText": "Copy"
132132
},
133+
"date-picker": {
134+
"i18nStrings.openCalendarAriaLabel": "{selectedDate, select, none {Choose date} other {Choose date, selected date is {selectedDate}}}"
135+
},
133136
"date-range-picker": {
134137
"i18nStrings.relativeModeTitle": "Relative mode",
135138
"i18nStrings.absoluteModeTitle": "Absolute mode",
@@ -172,6 +175,12 @@
172175
"drawer": {
173176
"i18nStrings.loadingText": "Loading content"
174177
},
178+
"error-boundary": {
179+
"i18nStrings.headerText": "Unexpected error, content failed to show",
180+
"i18nStrings.descriptionText": "Refresh to try again.",
181+
"i18nStrings.descriptionWithFeedbackText": "Refresh to try again. We are tracking this issue, but you can share <Feedback>more information here</Feedback>.",
182+
"i18nStrings.refreshActionText": "Refresh page"
183+
},
175184
"file-token-group": {
176185
"i18nStrings.limitShowFewer": "Show fewer",
177186
"i18nStrings.limitShowMore": "Show more",

src/i18n/messages/all.es.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@
130130
"copy-to-clipboard": {
131131
"i18nStrings.copyButtonText": "Copiar"
132132
},
133+
"date-picker": {
134+
"i18nStrings.openCalendarAriaLabel": "{selectedDate, select, none {Elegir fecha} other {Elegir fecha; la fecha seleccionada es {selectedDate}}}"
135+
},
133136
"date-range-picker": {
134137
"i18nStrings.relativeModeTitle": "Modo relativo",
135138
"i18nStrings.absoluteModeTitle": "Modo absoluto",
@@ -172,6 +175,12 @@
172175
"drawer": {
173176
"i18nStrings.loadingText": "Cargando contenido"
174177
},
178+
"error-boundary": {
179+
"i18nStrings.headerText": "Error inesperado. No se pudo mostrar el contenido",
180+
"i18nStrings.descriptionText": "Actualice para intentarlo de nuevo.",
181+
"i18nStrings.descriptionWithFeedbackText": "Actualice para intentarlo de nuevo. Estamos siguiendo este problema, pero puede compartir <Feedback>más información aquí</Feedback>.",
182+
"i18nStrings.refreshActionText": "Actualizar página"
183+
},
175184
"file-token-group": {
176185
"i18nStrings.limitShowFewer": "Mostrar menos",
177186
"i18nStrings.limitShowMore": "Mostrar más",

0 commit comments

Comments
 (0)