Skip to content

SPIKE: Ws 2118 investigate sticky navugation on scroll up#13760

Draft
LilyL0u wants to merge 19 commits intolatestfrom
WS-2118-investigate-sticky-navugation-on-scroll-up
Draft

SPIKE: Ws 2118 investigate sticky navugation on scroll up#13760
LilyL0u wants to merge 19 commits intolatestfrom
WS-2118-investigate-sticky-navugation-on-scroll-up

Conversation

@LilyL0u
Copy link
Contributor

@LilyL0u LilyL0u commented Feb 25, 2026

Resolves JIRA: https://bbc.atlassian.net/browse/WS-2118

This pull request introduces a sticky navigation bar for the canonical navigation component, enhancing the user experience by making navigation more accessible when scrolling. It also adds keyboard navigation detection to hide the sticky nav to avoid accessibility problems, hides the sticky nav in lite mode, and updates related tests.

Need to decide what to do with event tracking/analytics in relation to the sticky nav.

Sticky Navigation Implementation and Accessibility Improvements:

  • Added a sticky navigation bar to CanonicalNavigationContainer that appears when the user scrolls up, disappears when scrolls down. It only shows when the main navigation is out of view. When the user scrolla back to near the top, the sticky nav will slide out of view to avoid overlaying them both. The threshold for where this happens can be changed. It is hidden when keyboard navigation (e.g., Tab key) is detected. This includes scroll direction detection, keyboard/pointer event handlers, and animation for smooth transitions. (src/app/components/Navigation/index.canonical.tsx [1] [2]
  • Ensured the sticky navigation is not rendered on "lite" sites and is hidden by default or during keyboard navigation for accessibility. (src/app/components/Navigation/index.canonical.tsx [1] [2]

Component and Test Enhancements:

  • Updated ScrollableNavigation and CanonicalDropdown components to support a new isSticky prop and set appropriate data-e2e attributes for sticky and non-sticky instances, aiding testability. (src/app/legacy/psammead/psammead-navigation/src/ScrollableNavigation/index.jsx [1] src/app/legacy/psammead/psammead-navigation/src/DropdownNavigation/index.jsx [2]
  • Added comprehensive tests for the sticky navigation behavior. (src/app/components/Navigation/index.canonical.test.tsx src/app/components/Navigation/index.canonical.test.tsxR1-R93)

Test Selector Robustness:

  • Updated Cypress analytics assertion scripts to use button:visible selectors, ensuring tests interact with the correct navigation button when multiple buttons may be present. (ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/navigation.ts [1] [2]

Summary

A very high-level summary of easily-reproducible changes that can be understood by non-devs, and why these changes where made.

Code changes

  • List key code changes that have been made.

Testing

  1. List the steps required to test this PR.

Useful Links

@LilyL0u LilyL0u changed the title Ws 2118 investigate sticky navugation on scroll up SPIKE: Ws 2118 investigate sticky navugation on scroll up Feb 26, 2026

// Sticky nav is rendered if it is not the lite site and if keyboard navigation has not been indicated with te tab key
const stickyNav =
!isLite && !isKeyboardNav ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

Worth considering for this spike but I think the sticky nav is still mounted even when hidden, so keyboard users may still tab into off-screen links and we also end up duplicating the navigation markup

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't seem possible to tab into off-screen links when I test it. aria-hidden="true" makes it not focusable. If duplication of markup is a problem, a different option can be looked into.

const { isLite } = use(RequestContext);
const { enabled: topBarOJsEnabled } = useToggle('topBarOJs');
const [isOpen, setIsOpen] = useState(false);
const [showSticky, setShowSticky] = useState(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

More for L39 but using one shared open/closed state for both navs might mean opening the sticky menu also opens the hidden original menu, which could lead to odd layout shifts or unexpected behaviour

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, this was actually causing a bug with the dropdown. I have added const [openDropdown, setOpenDropdown] = useState<null | 'main' | 'sticky'>( null, ); which should make it so only one dropdown menu can be open at a time.

return (
<Navigation dir={dir} isOpen={isOpen}>
// Sticky nav scroll logic
useEffect(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Possible performance risk here in that we update the state on scroll, so the nav could keep re-rendering while the user scrolls, which may be expensive w/ regards to mobile data considerations

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.

2 participants