Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

aduth
Copy link
Contributor

@aduth aduth commented Dec 13, 2022

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.

Copy link
Contributor

@mejiaj mejiaj left a 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. 😄

@aduth aduth force-pushed the aduth-5056-in-page-nav-hash branch from 786d92e to b820042 Compare January 11, 2023 19:46
@aduth aduth force-pushed the aduth-5056-in-page-nav-hash branch from b820042 to 0b78a24 Compare January 11, 2023 19:47
Copy link
Contributor

@mejiaj mejiaj left a 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

@aduth
Copy link
Contributor Author

aduth commented Jan 30, 2023

Good catch @mejiaj . I think that's likely a result of how the handleScrollToSection function is now assigning the URL hash, effectively bypassing the smooth scrolling.

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:

  • Invert hash assignment to happen by default link click behavior, rather than window.location.hash assignment
  • Consider scroll-behavior CSS, which may interoperate better with window.location.hash assignment

@mejiaj
Copy link
Contributor

mejiaj commented Jan 31, 2023

@aduth understandable and your help is much appreciated!

Those ideas sound good. scroll-behavior CSS looks to have much better support now, interesting.

@amyleadem
Copy link
Contributor

amyleadem commented Feb 1, 2023

@aduth @mejiaj
I like this added functionality!
This might be out of scope for this PR, but I see an opportunity to make the url more readable by changing the generated id (and resulting hash) from section_X to the header title string (possibly truncated, with dashes in place of spaces). Would there be any risk to this? Curious to hear what you think.

@aduth
Copy link
Contributor Author

aduth commented Feb 27, 2023

@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.).

@aduth
Copy link
Contributor Author

aduth commented Feb 28, 2023

@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:

  • d82568b ought to address the issue where smooth scroll was not happening as expected
  • 66e0232 introduces a revised way for generating IDs based on heading content (rather than a generic "section_#"). I've handled a few edge cases already in the logic, but it's probably worth taking a detailed look at how this might handle more extreme cases.

@aduth
Copy link
Contributor Author

aduth commented Feb 28, 2023

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.

@aduth aduth marked this pull request as draft February 28, 2023 17:32
@aduth
Copy link
Contributor Author

aduth commented Feb 28, 2023

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.

@aduth aduth marked this pull request as ready for review February 28, 2023 19:30
@amyleadem amyleadem self-requested a review February 28, 2023 20:39
@amyleadem amyleadem self-assigned this Feb 28, 2023
Copy link
Contributor

@amyleadem amyleadem left a 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.

image

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:

image

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.

image

I will return to review this more in-depth tomorrow. Let me know if you have any questions in the meantime.

Thanks again!

@amyleadem amyleadem removed their assignment Feb 28, 2023
@aduth
Copy link
Contributor Author

aduth commented Mar 1, 2023

@amyleadem Those are both great points, let me see if I can fix 'em up!

aduth added 4 commits March 1, 2023 08:44
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
@aduth
Copy link
Contributor Author

aduth commented Mar 1, 2023

Ok, pushed a couple more revisions!

  • e0d704e should fix the issue with "active" links not being highlighted as expected, by restoring the original behavior of using a newly-created anchor link rather than setting the ID on the heading directly. While I kinda hoped to be able to avoid the new element, this simplifies the diff, avoids the issues with the "active" states, and may avoid some compatibility issues if the consuming project is doing its own things with the heading attributes.
  • 74f658c avoids the visited coloring of the navigation items. The code for this is a bit more complex than I might have anticipated 😄 , but there didn't seem to be a way with the current set-link-from-bg mixin to force a specific visited color.

@amyleadem amyleadem self-assigned this Mar 1, 2023
Copy link
Contributor

@amyleadem amyleadem left a 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.~!@#$%^&*()+`=[{]}|;:'<>?,./-*
  • 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"
  • 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

@amyleadem amyleadem removed their assignment Mar 1, 2023
Copy link
Contributor

@mejiaj mejiaj left a 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 in usa-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!
  • 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) {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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 = [
Copy link
Contributor

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?

Copy link
Contributor Author

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Comment on lines +87 to +96
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));
Copy link
Contributor

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).

Copy link
Contributor Author

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.

Comment on lines +135 to +137
if (window.location.hash.slice(1) !== el.id) {
window.history.pushState(null, "", `#${el.id}`);
}
Copy link
Contributor

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.

Copy link
Contributor Author

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
@aduth
Copy link
Contributor Author

aduth commented Mar 6, 2023

Thanks @mejiaj for the review! I pushed a few clarifying code comments in b051d44 based on some of the questions you raised.

Copy link
Contributor

@mejiaj mejiaj left a 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.

@mejiaj mejiaj requested a review from amyleadem March 6, 2023 15:54
@amyleadem
Copy link
Contributor

@aduth
cc: @mejiaj

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:

  • Selecting an item with enter does not generate a hash (but it successfully generates a hash on click)
  • Tab navigation and Focus states do not work in Safari (this appears to be an existing bug that also happens on develop)

Outside of that, it is looking good to me!

@aduth
Copy link
Contributor Author

aduth commented Mar 7, 2023

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?

Screen Shot 2023-03-07 at 12 03 06 PM

@amyleadem
Copy link
Contributor

amyleadem commented Mar 7, 2023

@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.

@amyleadem amyleadem closed this Mar 7, 2023
@amyleadem amyleadem reopened this Mar 7, 2023
@amyleadem
Copy link
Contributor

amyleadem commented Mar 7, 2023

Apologies, I accidentally closed this due to some clumsy clicking. Not intentional!

@aduth
Copy link
Contributor Author

aduth commented Mar 7, 2023

The changes in 3163fd1 should hopefully address the issues with the Enter keypress navigation, if you're able to take another look @amyleadem

Copy link
Contributor

@amyleadem amyleadem left a 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!

@amyleadem amyleadem requested a review from thisisdano March 7, 2023 20:43
Copy link
Contributor

@thisisdano thisisdano left a 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 thisisdano merged commit 57db4f9 into uswds:develop Mar 7, 2023
@thisisdano thisisdano mentioned this pull request Mar 9, 2023
@aduth aduth deleted the aduth-5056-in-page-nav-hash branch March 13, 2023 15:17
@aduth
Copy link
Contributor Author

aduth commented Mar 13, 2023

@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

@thisisdano
Copy link
Contributor

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!

This was referenced Mar 13, 2023
@thisisdano
Copy link
Contributor

@aduth This should now be live in USWDS 3.4.1. Sorry for the mix-up!

@aduth
Copy link
Contributor Author

aduth commented Mar 13, 2023

No worries, thanks for the quick turnaround on the patch release!

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.

USWDS - Feature: In-page navigation: Update URL fragment when navigating

4 participants