Skip to content
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

MenuLink with NextJS's next/link #951

Open
sgrund14 opened this issue Jul 31, 2022 · 7 comments
Open

MenuLink with NextJS's next/link #951

sgrund14 opened this issue Jul 31, 2022 · 7 comments

Comments

@sgrund14
Copy link

❓Question

Hello! I am having a bit of trouble getting the MenuLink component to work with the nextJS Link component, which does not currently support the forwardRef API so I've been messing around a little bit following this thread vercel/next.js#9784

const WrappedLinkWithForwardRef = React.forwardRef<
  HTMLAnchorElement,
  WrappedLinkProps
>(function WrappedLink(props, ref) {
  return (
    <Link href={props.href}>
      <a ref={ref}>{props.children}</a>
    </Link>
  )
})


// in the render method
<MenuLink as={WrappedLinkWithForwardRef} href="/help">
   Help
</MenuLink>

This code renders my menu fine, but it is no longer wrapped in the [data-reach-menu-item] div and doesn't receive focus when tabbing through the menu :(

Any ideas / experience using nextjs and reach ui together?

@indiesquidge
Copy link
Collaborator

Thanks for your question! Would you mind implementing your example in a workable CodeSandbox Template so we can take a look at it?

@sgrund14
Copy link
Author

sgrund14 commented Aug 10, 2022

@indiesquidge voila! apologies for the delay. have a little something working here. let me know if anything is broken. the relevant link logic is in components/Layout.jsx, as is the Reach UI template logic.

https://codesandbox.io/s/mystifying-mendel-457b7x

one thing i noticed was that the aria-activedescendant attribute is getting updated as I arrow around the menu, so it seems like it is sort of working 🤔 but the wrapping div for each MenuLink is absent, and so it does not get the data-selected attribute

Screen Shot 2022-08-09 at 11 14 55 PM

@sgrund14
Copy link
Author

@indiesquidge ever get the chance to take a look through this?

@indiesquidge
Copy link
Collaborator

@sgrund14 thanks for creating an example sandbox! I can look into this in the next few days

@sgrund14
Copy link
Author

awesome, thanks @indiesquidge !

@indiesquidge
Copy link
Collaborator

indiesquidge commented Aug 25, 2022

@sgrund14 I think this is a matter of your wrapping component swallowing the props that MenuLink provides. If you forward those props on the the underlying anchor element, I believe your component should work as you expect.

const WrappedLinkWithForwardRef = React.forwardRef<
  HTMLAnchorElement,
  WrappedLinkProps
>(function WrappedLink(props, ref) {
+ const { href, children, ...rest } = props;

  return (
-   <Link href={props.href}>
+   <Link href={href}>
-     <a ref={ref}>{props.children}</a>
+     <a {...rest} ref={ref}>{children}</a>
    </Link>
  );
});

@acdvorak
Copy link

acdvorak commented Oct 20, 2023

Building on @indiesquidge's solution, here's a complete example from @proper-px:

import Link from 'next/link';
import { forwardRef } from 'react';
import {
  Menu,
  MenuList,
  MenuButton,
  MenuItem,
  MenuLink,
} from '@reach/menu-button';

interface MenuLinkA11yProps {
  href: string;
  children: React.ReactNode;
}

const MenuLinkA11y = forwardRef<HTMLAnchorElement, MenuLinkA11yProps>(
  ({ href, children, ...rest }, ref) => {
    return (
      // The Reach UI <Link> component is required for client-side routing to
      // work with the Next.js 12 "Pages Router".
      <Link href={href}>
        <a ref={ref} {...rest}>
          {children}
        </a>
      </Link>
    );
  }
);

const signOut = () => {
  // ...
};

const renderMenu = () => (
  <Menu>
    <MenuButton>
      Open menu
    </MenuButton>
    <MenuList>
      <MenuLink as={MenuLinkA11y} href="/foo">
        Foo
      </MenuLink>
      <MenuLink as={MenuLinkA11y} href="/bar">
        Bar
      </MenuLink>
      <MenuItem onSelect={signOut}>Sign Out</MenuItem>
    </MenuList>
  </Menu>
);

For me, the above is fully keyboard accessible and works with client-side routing that uses the Next.js Pages Router.

I'm running Next.js version 12 and Reach UI Menu Button version 0.16.2.

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

No branches or pull requests

3 participants