Repository navigation
fix(rtl): respect dir of ancestor elements throughout components - #31459
OS-jacobbell wants to merge 21 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
dir setting of ancestor elements throughout components
dir setting of ancestor elements throughout componentsdir of ancestor elements throughout components
ShaneK
left a comment
There was a problem hiding this comment.
Nice sweep of this! Just a few comments inline, though I'd want the test coverage added before it goes in.
Co-authored-by: Shane <shane.king@outsystems.com>
ShaneK
left a comment
There was a problem hiding this comment.
Thanks for adding the tests and taking the suggestions! Most of them pass on main too though, so they aren't covering the change yet, and CI is red on the new popover one. I've left comments inline.
ShaneK
left a comment
There was a problem hiding this comment.
Really close! Just a few more comments inline.
ShaneK
left a comment
There was a problem hiding this comment.
Looks great, thanks for sticking with this one! Just need the datetime test fixed so lint passes. 🚀
Co-authored-by: Shane <shane.king@outsystems.com>
brandyscarney
left a comment
There was a problem hiding this comment.
Looking good! Requested changes for the popover direction regression on popover.tsx. The other comments are non-blocking.
| * elements can match. | ||
| */ | ||
| if (!el.hasAttribute('dir')) { | ||
| el.setAttribute('dir', isRTL(el) ? 'rtl' : 'ltr'); |
There was a problem hiding this comment.
Blocking: this sets the host's dir from isRTL(el), which is wherever the popover currently sits, but the enter animations (ios.enter.ts:35, md.enter.ts:23) take their direction from the trigger or event target. Controller popovers (including ion-select's) and React inline popovers are already under ion-app before present() runs. So for a popover opened from inside <div dir="rtl"> in an LTR app, it gets placed for RTL but its content renders LTR. On main both followed the document, so they always agreed. This is a regression introduced by this PR.
Could present() pick the source element once (trigger, then event target, then el), set the host dir from that, and have both enter animations just call isRTL(baseEl)? That keeps placement and content in sync, respects a dir set directly on ion-popover, and means the direction lookup isn't duplicated across both enter animations.
To reproduce, add this to an index.html file in core (expand):
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Popover - RTL Repro</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-content class="ion-padding">
<div dir="rtl">
<!-- This is added to move the button away from the right edge -->
<div style="display: inline-block; width: 200px; height: 20px"></div>
<button id="open">Open</button>
</div>
</ion-content>
</ion-app>
<script type="module">
import { popoverController } from '../../../../../dist/ionic/index.esm.js';
document.querySelector('#open').addEventListener('click', async (ev) => {
const content = document.createElement('div');
content.className = 'ion-padding';
content.textContent = 'Hello world!';
const popover = await popoverController.create({
event: ev,
component: content,
side: 'start',
alignment: 'start',
});
await popover.present();
});
</script>
</body>
</html>The popover opens to the right of the button with transform-origin: left top (RTL), but ion-popover gets dir="ltr" and its content renders LTR.
Not blocking and could go in FW-7850 instead: once present() resolves from the trigger or event target, ion-select with the default label placement still won't pick up an ancestor dir. It builds its popover event as { ...ev, detail: { ionShadowTarget } } (select.tsx:784), and spreading drops target since it's a prototype getter, so its options keep rendering LTR inside an RTL ancestor. Resolving direction from the same element getPopoverPosition positions against (utils.ts:637, using its host since isRTL stops at shadow roots) would cover it.
There was a problem hiding this comment.
Good catch, Shane and I decided on using the trigger element back when popover wasn't saving its own dir. Now that it is, the animations can use just baseEl (the popover itself).
There was a problem hiding this comment.
Maybe PopoverOptions should have a dir property?
Issue number: internal
What is the current behavior?
Some components check
document.dir === 'rtl'to determine whether to be left-to-right or right-to-left.What is the new behavior?
isRTL(this.el). This way, any element in the parent chain can define RTL, not just the root document element.isRTL.Does this introduce a breaking change?