-
Notifications
You must be signed in to change notification settings - Fork 1.1k
In-Page Navigation: Update page URL when scrolling to section #5068
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
Conversation
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 nice improvement. Small comment on separating the location check to improve readability.
Otherwise LGTM, thanks. 😄
786d92e
to
b820042
Compare
b820042
to
0b78a24
Compare
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.
@aduth sorry, I missed your response. Yes, that's what I was thinking. Separating the function to keep the code more readable.
The only quirk I see is that smooth scrolling isn't working. If I click on a branch locally it just jumps immediately to the section:
aduth-inpage_nav-2023-01-26.at.09.27.57.webm
Here's how it looks in develop:
develop-inpage_nav-2023-01-26.at.09.44.03.webm
Good catch @mejiaj . I think that's likely a result of how the My week is starting rather busy unfortunately, but I'll plan to take a closer look once time allows. Some potential avenues for fixes I might want to explore:
|
@aduth understandable and your help is much appreciated! Those ideas sound good. |
@aduth @mejiaj |
@amyleadem Yes, I agree, I wish the generated hashes were more accurate based on the title of the section. This behavior is inherited from code that already exists and ideally I'd hoped not to have to alter that. However, I think if my pull request is the first time those generated hashes are made user-facing in that it'd be expected a user might copy the URL from their address bar, it could be good a good idea to consider that in-scope of this work. I can take a closer look to see how feasible this could be. I think part of the challenge might be making sure that we don't generate hashes which would collide with existing IDs on the page, as well as content normalization like you had mentioned (converting spaces, invalid characters, etc.). |
@mejiaj @amyleadem Apologies for the delay in revisiting this; it's been a hectic few weeks for me 😅 I pushed up two additional commits which should hopefully address the feedback:
|
Actually, I might need to make a couple more revisions, since 66e0232 appears to have broken the behavior of the intersection observer (updating the hash as the user scrolls). There weren't existing tests for this, so I might look to add those as well to prevent future issues. |
Nevermind, I misunderstood what the intersection observer was for. I see that it's meant to be for setting the current section as "active", which appears to be working correctly still in my testing. |
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.
Thanks so much for your work on this @aduth! I am excited about this upgrade! I will continue digging in for a deeper review, but wanted to flag a couple quirks I found during functionality testing:
Current status is not always triggered when scrolling up
If you are in a low section on the page (for example, in the "vitae ligula" section) and click on an item in the in-page nav that is above where you are (say, the "Consectetuer adipiscing elit" section), the selected item does not receive the current status and does not show the vertical scroll bar. For the current status to activate, you need to scroll up a tiny bit. This issue does not exist on the current implementation of the component found in develop
.
This issue appears in Chrome, Safari, and Edge for Mac. It does NOT happen in Firefox.
One thing I noticed is that in this PR, the full headline must be visible before triggering the current state. In the current implementation, only the bottom part of the headline must be visible to trigger the current state. I am guessing this is contributing to the issue:
Links now turn purple after click
In-page nav links are now registering as visited when clicked. My instinct is to style these items so that the :visited
links look the same as the unvisited links, but I can see how there might be differing opinions on that.
I will return to review this more in-depth tomorrow. Let me know if you have any questions in the meantime.
Thanks again!
@amyleadem Those are both great points, let me see if I can fix 'em up! |
1. better interoperability with project's own handling of the heading tags 2. fix intersection observe re: uswds#5068 (review)
1. avoid eslint warning 2. leverage more purpose-suited do/while vs. while(true) break
Ok, pushed a couple more revisions!
|
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.
Everything seems to be working well, @aduth!
I'll hold off on approving until @mejiaj takes a deeper look at the JS, but it is looking good to me. Thank you!
I have tested the following items:
- Run
npm start
,npm test
,npm run lint
,npm run prettier:js
without error - Confirm that
usa-current
is appropriately applied when moving to a lower section (Both during scrolling and after selecting an in-page nav item) - Confirm that
usa-current
is appropriately applied when moving to a higher section (Both during scrolling and after selecting an in-page nav item) - Confirm smooth scroll when a new in-page nav item is selected
- Confirm that in-page navigation visually matches the existing component in
develop
- Confirm that a url hash is generated from the header text
- Note: Hash is generated when an item is selected via mouse or keyboard; it is not generated during scroll. This probably is expected behavior but wanted to flag it just in case.
- Confirm that the generated hash handles special characters
- Tested the following string in the header: "-#!A-b@--1---2.~!@#$%^&*()+`=[{]}|;:'<>?,./-*"; results came out as expected:
- Generated url hash is: #a-b-1-2
- Generated nav item text is: -#!A-b@--1---2.~!@#$%^&*()+`=[{]}|;:'<>?,./-*
- Tested the following string in the header: "-#!A-b@--1---2.~!@#$%^&*()+`=[{]}|;:'<>?,./-*"; results came out as expected:
- Confirm that a unique hash is generated when two headers have the same text
- Added a second "Vitae ligula" header in tests; results came out as expected:
- Generated url hash is: "#vitae-ligula-2"
- Added a second "Vitae ligula" header in tests; results came out as expected:
- When loading a url that includes a generated section hash (ex: localhost:6006/iframe.html?args=&id=components-in-page-navigation--default&viewMode=story#nullam-sit-amet-enim), confirm that
usa-current
is applied to the appropriate nav item and the appropriate section is shown in the viewport - Test functionality in Safari, Firefox, Chrome, Edge
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.
@aduth a few questions, but overall this looks pretty good.
I tested:
- Smooth scrolling in Chrome/Firefox
- Regex patterns match and work as expected
- Tested following
section_heading
values inusa-in-page-navigation.json
:- …All previous headers
--Aliquam erat volutpat: velit vitae ligula volutpat--
Duplicate Regex tester
Duplicate Regex tester
$$$Regex tester----hello&world! Lorem$Ipsum!
- Tested following
- Compared functionality and visuals with previous component
- Mobile behavior (
⚠️ Seems to scroll further than previous) - Push state forward/backward works as expected (
⚠️ Focus state disappears)
|
||
let index = 0; | ||
let sibling = heading; | ||
while (true) { |
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.
What are we checking for here?
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.
This tries to simulate a vertical offset in order to test that offsetTop
used for scrolling is behaving as expected, since JSDOM doesn't emulate that. The logic essentially considers computes the index of the current heading and multiplies that by a constant number. I'm open to other ideas for how to ensure the scrolling occurs as expected.
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 see, thanks. I couldn't find any alternative solutions to offsetTop
in their issues queue.
I guess what threw me off initially was the while (true)
inside of Object.defineProperty
.
}); | ||
|
||
it("assigns id to section headings", () => { | ||
const ok = [ |
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.
These are the test template headers we're testing, right?
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.
Yes, correct, this tests against the template.
"h2 ~ h3 ~ h3 ~ h3 > .usa-anchor#section-1-3", | ||
].every((selector) => document.querySelector(selector)); | ||
|
||
assert(ok); |
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.
And here we're asserting that the template headers exist in this structure?
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.
Yes, this is testing that the anchors are created correctly for each heading in the template.
do { | ||
id = baseId; | ||
|
||
// To avoid conflicts with existing IDs on the page, loop and append an | ||
// incremented suffix until a unique ID is found. | ||
suffix += 1; | ||
if (suffix > 1) { | ||
id += `-${suffix}`; | ||
} | ||
} while (document.getElementById(id)); |
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 really like this idea and could see it as its own utility in the future.
Only concern is it looks like we're looping over the headers twice? Since we're calling getHeadingId(el)
inside of the sectionHeadings.forEach((el)
.
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.
While this is technically a loop, I'd expect it would only run once in the vast majority of cases, since it would only ever run more than once if there was already an ID somewhere in the page which was the same as our computed ID using the heading's text.
if (window.location.hash.slice(1) !== el.id) { | ||
window.history.pushState(null, "", `#${el.id}`); | ||
} |
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.
Nice feature! I was able to go forwards/back on previously clicked in-page nav items.
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.
Yeah this was the trick to solve the issue you identified earlier, since assigning the hash fragment this way appears to work the same as window.location.hash = el.id
with the exception of scrolling, which is what we wanted to avoid.
Based on questions from review
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.
Thanks for all your work on this. I appreciate the code comments.
Thanks for all your work on this. I did another review and everything is working as expected, except I found two items related to keyboard navigation:
Outside of that, it is looking good to me! |
Great catch @amyleadem ! I'll take a look at the issue with changing hash on pressing Enter. For the second item, I suspect this might be due to a Safari default setting which doesn't navigate links using Tab by default, but you can change this in Preferences > Advanced. Can you confirm that it works as expected after clicking this checkbox? |
@aduth Thanks for the quick response! Updating that setting in Safari does fix the problem. I am able to tab into the component with this setting turned on. Since it was a pre-existing item, it should be fine to ignore it in this PR. I have opened issue #5169 so that we can evaluate separately if this meets our browser compatibility standards. @mejiaj, let me know if you have any objections to this. |
Apologies, I accidentally closed this due to some clumsy clicking. Not intentional! |
The changes in 3163fd1 should hopefully address the issues with the Enter keypress navigation, if you're able to take another look @amyleadem |
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.
This looks good to me! Thanks for your effort here!
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.
This is a nice new feature with a thoughtful implementation! Thanks, and thanks for the thorough reviews!
@thisisdano @amyleadem @mejiaj It looks like these changes didn't actually get included in v3.4.0 ? https://github.com/uswds/uswds/blob/v3.4.0/packages/usa-in-page-navigation/src/index.js |
Oh bother. This came in after I'd already created the release branch, then I guess I never updated the release branch. We'll get a 3.4.1 out asap to fix this! |
@aduth This should now be live in USWDS 3.4.1. Sorry for the mix-up! |
No worries, thanks for the quick turnaround on the patch release! |
Closes #5056
Description
Enhances the In-Page Navigation component to update the current URL when scrolling to a section.
Additional information
See #5056 for more details about rationale.