Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: correctly format date/time in RTL #7423

Merged
merged 53 commits into from
Jan 29, 2025
Merged

fix: correctly format date/time in RTL #7423

merged 53 commits into from
Jan 29, 2025

Conversation

yihuiliao
Copy link
Member

@yihuiliao yihuiliao commented Nov 21, 2024

Close #4711

Need to double check that nothing broke in #6562 with these changes.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

Arabic should render in the following order:

TimeZone DayPeriod Hour:Minute Year/Month/Day

Hebrew should render in the following order:

TimeZone DayPeriod Hour:Minute Day/Month/Year

Tab order should be:

Day -> Month -> Year -> Hour -> Minute -> DayPeriod -> TimeZone

Arabic Placeholders:

يوم / شهر / سنة

Hebrew Placeholders:

שנה.חודש.יום

Also be sure to test keyboard navigation, deleting/backspace, resizing

Note that with backspacing, the focus should move the previous node. So basically the tab order above but go from right to left

🧢 Your Project:

@rspbot
Copy link

rspbot commented Nov 21, 2024

@adobe adobe deleted a comment from rspbot Nov 21, 2024
@sadeghbarati
Copy link

sadeghbarati commented Dec 2, 2024

Hey, Thanks for working on this PR

Is this also going to fix keyboard focus management on RTL too?

For example DateField component in the storybook link, if you pick Farsi locale, in order to focus DateSegments with arrow key you need to do it opposite direction to focus next date segment


  • DateField with Farsi locale:

    • yyyy/mm/dd (روز/ماه/سال) format is right ✅
    • focus management with arrow keys is not right ❌
  • DatePicker and DateRangePicker with Farsi locale:

    • dd/mm/yyyy (سال/ماه/روز) format is not right ❌
    • focus management with arrow keys is right ✅

@yihuiliao
Copy link
Member Author

@sadeghbarati Yes, the plan is to also correct the keyboard navigation for RTL as well. The PR is still in the early stages so there's a lot that has yet to be implemented.

@rspbot
Copy link

rspbot commented Dec 4, 2024

@rspbot
Copy link

rspbot commented Dec 6, 2024

@rspbot
Copy link

rspbot commented Dec 20, 2024

@adobe adobe deleted a comment from rspbot Jan 7, 2025
@rspbot
Copy link

rspbot commented Jan 8, 2025

@adobe adobe deleted a comment from rspbot Jan 9, 2025
@rspbot
Copy link

rspbot commented Jan 17, 2025

@yihuiliao yihuiliao changed the title wip: fix incorrect date/time order in rtl fix incorrect date/time order in rtl Jan 17, 2025
@yihuiliao yihuiliao changed the title fix incorrect date/time order in rtl fix: ensure date/time segments are ordered correctly in RTL Jan 17, 2025
@yihuiliao yihuiliao marked this pull request as ready for review January 17, 2025 23:18
let el = this;

if (el.getAttribute('role') === 'spinbutton') {
if (el.parentElement.getAttribute('data-testid') === 'end-date') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought the issue with some of these was that the dom order was not in alignment with the rendered order?
this test is a good base, but should we include some tests where the order differs? I think you could use el.getAttribute('data-type') to order based on the day/month/hour/etc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes, it's more obvious with when using examples with time segments so i should update the examples to include those

@@ -92,11 +92,11 @@ export const DateRangePickerExample = () => (
<Label style={{display: 'block'}}>Date</Label>
<Group style={{display: 'inline-flex'}}>
<div className={styles.field}>
<DateInput data-testid="date-range-picker-date-input" slot="start" style={{display: 'inline-flex'}}>
<DateInput data-testid="date-range-picker-date-input" slot="start" style={{display: 'inline'}}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sort of a breaking change
what will happen to people who have used this as an example to build their DatePicker's in the past?

Also, why is the style change different from DateField.mdx?

Copy link
Member Author

@yihuiliao yihuiliao Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so i experimented a bit and found that for the rtl segments to display in the correct order, DateInput needs to have display inline, inline-block, or block. hence why one is display inline and the other is display block. it would probably be a good idea to make it consistent and stick with display inline though.

and as for the sort of breaking change, i think folks can continue to use display: inline-flex. i don't believe the changes here will affect that. the only thing that they'll run into is that if they do choose to render their datepicker in a rtl locale, it will be in the incorrect order.

updating the docs and stories here is to reflect the changes needed for rtl locales to display in the right order. that way, those who do end up copying the docs + styles afterwards will have the proper styling for it to work correctly.

unfortunately, i don't think there's a way to have rtl locales render their date/time segments correctly using display flex, so it might require updates on the users side.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so long as inline-flex doesn't break anything, I'm fine with this
we should add a little blurb to the Accessibility section for the docs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that's a good idea to call it out in our docs somewhere. i think maybe the Internationalization section instead?

@rspbot
Copy link

rspbot commented Jan 22, 2025

@rspbot
Copy link

rspbot commented Jan 22, 2025

let update = () => {
if (ref.current) {
// TODO: For now, just querying this list of elements. However, it's possible that either through hooks or RAC that some users may include other focusable items that they would want to able to keyboard navigate to. In that case, we might want to utilize focusableElements in isFocusable.ts
let editableSegments: NodeListOf<Element> | undefined = ref.current?.querySelectorAll('span[role="spinbutton"], span[role="textbox"], button');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because this checks for span in all the selectors, will this break people who are using divs like we used to?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh good point. hmm in that case, we could update it so that it also checks for divs as well?

although, my guess is that people aren't using this in rtl locales given that the time/date formats were so off.


// Not needed in aria-described by because each segment has a label already, so this would be duplicative.
let group = getByRole('group');
expect(group).not.toHaveAttribute('aria-describedby');

expect(getByText('month / day / year')).toBeVisible();
Copy link
Member Author

@yihuiliao yihuiliao Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i removed this line since now each individual segment is in its own span and it would be hard to query those. we have a chromatic story to test this so it is covered. i kept the rest of the test since it would be good to continue checking the aria stuff.

(and the same goes for DatePicker.test and DateRangePicker.test)

@yihuiliao yihuiliao changed the title fix: ensure date/time segments are ordered correctly in RTL fix: correctly format date/time in RTL Jan 24, 2025
@rspbot
Copy link

rspbot commented Jan 24, 2025

@adobe adobe deleted a comment from rspbot Jan 24, 2025
Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed that "Delete" likely needs the same logical movement treatment that the arrow keys got. Otherwise, in Hebrew I start deleting from the year -> month -> day and then it stops, it never moves to the time field element

I'm ok for this to be in follow up, otherwise looks good

@yihuiliao
Copy link
Member Author

Noticed that "Delete" likely needs the same logical movement treatment that the arrow keys got. Otherwise, in Hebrew I start deleting from the year -> month -> day and then it stops, it never moves to the time field element

I think this is actually the correct behavior. "Delete" causes the focus to shift to the previous node. If you're deleting from year -> month -> day and then try deleting again, the focus never moves because there is no node before day.

It's the same in LTR. In en-US, if you start deleting year -> day -> month, you'll also never move to the timefield. It just feels like you should in RTL because we're reading the entire field as LTR because that's what we're used to.


update();

let observer = new MutationObserver(update);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the MutationObserver? When would the segments change order without the date picker itself re-rendering?

Copy link
Member Author

@yihuiliao yihuiliao Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm good point,,,i think it should be fine if we were to get rid of it

Copy link
Member

@snowystinger snowystinger Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was for if users added more buttons to control the field, Yihui had an example with a << and < button for jumping month and year I think

I don't know if those buttons would ever be removed or added dynamically

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, I think it was from the example in this issue #7645

isEditable
} as DateSegment;

if (segment.type === 'hour') {
Copy link
Member

@devongovett devongovett Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some comments to this function describing how it works and what the unicode characters do. Will be hard for future readers/contributors to understand what's going on if they aren't familiar with it. 😄

@yihuiliao yihuiliao added this pull request to the merge queue Jan 29, 2025
Merged via the queue into main with commit e228ed8 Jan 29, 2025
31 checks passed
@yihuiliao yihuiliao deleted the bidi-override branch January 29, 2025 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect date / time segment order in several components, for RTL languages
5 participants