Skip to content

Commit 41ef71d

Browse files
authored
chore: follow-up for rtl date/time format (#7685)
* add comments to explain unicode stuff * add blurb about the css changes in rac docs * add spacing to showFormatHelpText
1 parent 1ba8f01 commit 41ef71d

File tree

6 files changed

+26
-2
lines changed

6 files changed

+26
-2
lines changed

packages/@react-aria/datepicker/src/useDateSegment.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,14 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
388388
let dateSegments = ['day', 'month', 'year'];
389389
let segmentStyle : CSSProperties = {caretColor: 'transparent'};
390390
if (direction === 'rtl') {
391+
// While the bidirectional algorithm seems to work properly on inline elements with actual values, it returns different results for placeholder strings.
392+
// To ensure placeholder render in correct format, we apply the CSS equivalent of LRE (left-to-right embedding). See https://www.unicode.org/reports/tr9/#Explicit_Directional_Embeddings.
393+
// However, we apply this to both placeholders and date segments with an actual value because the date segments will shift around when deleting otherwise.
391394
if (dateSegments.includes(segment.type)) {
392395
segmentStyle = {caretColor: 'transparent', direction: 'ltr', unicodeBidi: 'embed'};
393396
} else if (segment.type === 'timeZoneName') {
397+
// This is needed so that the time zone renders on the left side of the time segments (hour:minute).
398+
// Otherwise, it will render on the right side which is incorrect.
394399
segmentStyle = {caretColor: 'transparent', unicodeBidi: 'embed'};
395400
}
396401
}

packages/@react-spectrum/datepicker/src/utils.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function useFormatHelpText(props: Pick<SpectrumDatePickerBase<any>, 'desc
3131
return (
3232
formatter.formatToParts(new Date()).map((s, i) => {
3333
if (s.type === 'literal') {
34-
return <span key={i}>{s.value}</span>;
34+
return <span key={i}>{` ${s.value} `}</span>;
3535
}
3636

3737
return <span key={i} style={{unicodeBidi: 'embed', direction: 'ltr'}}>{displayNames.of(s.type)}</span>;

packages/@react-stately/datepicker/src/useDateFieldState.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,11 @@ function processSegments(dateValue, validSegments, dateFormatter, resolvedOption
430430
isEditable
431431
} as DateSegment;
432432

433+
// There is an issue in RTL languages where time fields render (minute:hour) instead of (hour:minute).
434+
// To force an LTR direction on the time field since, we wrap the time segments in LRI (left-to-right) isolate unicode. See https://www.w3.org/International/questions/qa-bidi-unicode-controls.
435+
// These unicode characters will be added to the array of processed segments as literals and will mark the start and end of the embedded direction change.
433436
if (segment.type === 'hour') {
437+
// This marks the start of the embedded direction change.
434438
processedSegments.push({
435439
type: 'literal',
436440
text: '\u2066',
@@ -440,6 +444,7 @@ function processSegments(dateValue, validSegments, dateFormatter, resolvedOption
440444
isEditable: false
441445
});
442446
processedSegments.push(dateSegment);
447+
// This marks the end of the embedded direction change in the case that the granularity it set to "hour".
443448
if (segment.type === granularity) {
444449
processedSegments.push({
445450
type: 'literal',
@@ -450,8 +455,9 @@ function processSegments(dateValue, validSegments, dateFormatter, resolvedOption
450455
isEditable: false
451456
});
452457
}
453-
} else if (timeValue.includes(granularity) && segment.type === granularity) {
458+
} else if (timeValue.includes(segment.type) && segment.type === granularity) {
454459
processedSegments.push(dateSegment);
460+
// This marks the end of the embedded direction change.
455461
processedSegments.push({
456462
type: 'literal',
457463
text: '\u2069',
@@ -461,6 +467,7 @@ function processSegments(dateValue, validSegments, dateFormatter, resolvedOption
461467
isEditable: false
462468
});
463469
} else {
470+
// We only want to "wrap" the unicode around segments that are hour, minute, or second. If they aren't, just process as normal.
464471
processedSegments.push(dateSegment);
465472
}
466473
}

packages/react-aria-components/docs/DateField.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ If the date field does not have a visible label, an `aria-label` or `aria-labell
155155

156156
Note that most of this anatomy is shared with [TimeField](TimeField.html), so you can reuse many components between them if you have both.
157157

158+
### Internationalization
159+
160+
To ensure the proper date and time format in RTL locales, `DateInput` must have `display` set to either `inline`, `inline-block`, or `block`.
161+
158162
### Concepts
159163

160164
`DateField` makes use of the following concepts:

packages/react-aria-components/docs/DatePicker.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ If the date picker does not have a visible label, an `aria-label` or `aria-label
195195

196196
Note that most of this anatomy is shared with [DateRangePicker](DateRangePicker.html), so you can reuse many components between them if you have both.
197197

198+
### Internationalization
199+
200+
To ensure the proper date and time format in RTL locales, `DateInput` must have `display` set to either `inline`, `inline-block`, or `block`.
201+
198202
### Concepts
199203

200204
`DatePicker` makes use of the following concepts:

packages/react-aria-components/docs/DateRangePicker.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ If the date range picker does not have a visible label, an `aria-label` or `aria
232232

233233
Note that most of this anatomy is shared with [DatePicker](DatePicker.html), so you can reuse many components between them if you have both.
234234

235+
### Internationalization
236+
237+
To ensure the proper date and time format in RTL locales, `DateInput` must have `display` set to either `inline`, `inline-block`, or `block`.
238+
235239
### Concepts
236240

237241
`DateRangePicker` makes use of the following concepts:

0 commit comments

Comments
 (0)