-
Notifications
You must be signed in to change notification settings - Fork 842
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
[EuiSkipLink] Add overrideLinkBehavior
prop for SPA/hash routers
#5957
Conversation
const destinationEl = document.getElementById(destinationId); | ||
if (!destinationEl) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I debated adding some kind of fallback for checking for document.querySelector('main')
or document.querySelector('[role="main"]')
before returning, but decided against it for now. Not totally sure how opinionated we want to be on this or how much monkeypatching we want to do for Kibana.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If destinationEl
is false and we return early, that means users are clicking an "empty" link. On the other hand, there's a required destinationId
field that'd throw a TS error. I think this strikes the right balance.
|
||
destinationEl.scrollTo(); | ||
destinationEl.tabIndex = -1; // Ensure the destination content is focusable | ||
destinationEl.focus({ preventScroll: true }); // Scrolling is already handled above, and focus's autoscroll behaves oddly around fixed headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick note on this comment: this weird focus scroll jumping only seemed to occur on webkit browsers (Chrome, Safari, Edge), and not FF 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also a quick note on destinationEl.tabIndex = -1
- in Safari this causes clicking the entire main/page body to have a black outline. This outline does not occur on any other browser on click (normally should only show on keyboard tab). I think this is probably okay because anyone using the skip link is a keyboard user in any case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any concern with the { preventScroll: true }
interfering with scroll behavior if the target is below the fold?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any concern with the { preventScroll: true } interfering with scroll behavior if the target is below the fold?
preventScroll
is preventing scroll behavior, so no, there's no question about it interfering with scroll behavior. scrollTo()
above on line 94 takes care of scrolling to items that aren't in the browser viewport.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds good. Does scrollTo()
require coordinates or an options object as parameters? I'm testing the skip link on the mobile emulator in Chrome, and it doesn't seem to skip when I press Enter
. The MDN docs for scrollTo list two parameter options, but I can't tell if they're required or optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be just looking at this wrong. I was thinking about a situation where the skip link was at the top of the page and DOM source order, and the element being targeted was further down the source code and below the visual fold. In that case I wasn't sure scrollTo
would actually move without coordinates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case I wasn't sure scrollTo would actually move without coordinates.
Yes, it will :) All params are optional for scrollTo()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, shoot, I think you're totally right on this. I'm conflating window.scrollTo
and element.scrollIntoView
. I think scrollIntoView
is the one I want 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you Constance! I'll give this another quick look in 15 after I finish helping troubleshoot an e2e test.
@@ -27,8 +27,9 @@ export const euiSkipLinkStyles = ({ euiTheme }: UseEuiTheme) => { | |||
} | |||
`, | |||
fixed: css` | |||
position: fixed !important; // Needs to override euiScreenReaderOnly - prevents scroll jumping in Firefox |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In an uno reverse move to the comment I just left, FF was the only browser I tested that had scroll jumping issues due to shifting between EuiScreenReaderOnly's position: absolute
and this position: fixed
(as a top-level DOM node). TBH I figured it wouldn't hurt to just always make this position fixed, but maybe I'm off on that assumption?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's better to set the position immediately than on focus.
Also another note - I don't expect this PR to get us 100% of the way to usable with Kibana. We may need to do more things, such as:
I'd rather make the above changes incrementally and as we figure out that we need them however, rather than all 1 PR |
Preview documentation changes for this PR: https://eui.elastic.co/pr_5957/ |
…between position absolute and position fixed
Preview documentation changes for this PR: https://eui.elastic.co/pr_5957/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one question about the { preventScroll: true }
object in your focus method and a few comments. If the question is a non-issue, I'm happy to make my review Approved.
Thanks @constancecchen
@@ -27,8 +27,9 @@ export const euiSkipLinkStyles = ({ euiTheme }: UseEuiTheme) => { | |||
} | |||
`, | |||
fixed: css` | |||
position: fixed !important; // Needs to override euiScreenReaderOnly - prevents scroll jumping in Firefox |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's better to set the position immediately than on focus.
|
||
destinationEl.scrollTo(); | ||
destinationEl.tabIndex = -1; // Ensure the destination content is focusable | ||
destinationEl.focus({ preventScroll: true }); // Scrolling is already handled above, and focus's autoscroll behaves oddly around fixed headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any concern with the { preventScroll: true }
interfering with scroll behavior if the target is below the fold?
const destinationEl = document.getElementById(destinationId); | ||
if (!destinationEl) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If destinationEl
is false and we return early, that means users are clicking an "empty" link. On the other hand, there's a required destinationId
field that'd throw a TS error. I think this strikes the right balance.
overrideAnchorBehavior
prop for SPA/hash routersoverrideLinkBehavior
prop for SPA/hash routers
Preview documentation changes for this PR: https://eui.elastic.co/pr_5957/ |
Preview documentation changes for this PR: https://eui.elastic.co/pr_5957/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thank you for the quick turnaround and thoughtful discussion on scrolling behavior.
Thanks for the thorough code review @1Copenut!! |
Summary
See Ryan's comment here: elastic/kibana#38980 (comment)
Default skip anchor links mess up hash routers, which both EUI's docs and Kibana uses. We need the ability to set a prop to override manual anchor link behavior by replicating it manually (scrolling to and focusing the destination node).
As a proof of concept, this PR also sets up skip links for our own EUI docs.
Checklist
- [ ] Checked in both light and dark modes- [ ] Checked in mobile- [ ] Added documentation- [ ] Checked Code Sandbox works for any docs examples- [ ] Checked for breaking changes and labeled appropriately- [ ] Updated the Figma library counterpart