Merge pull request #31276 from backstage/fix/buttonlink-router-integration

fix(bui): fix button link internal routing
This commit is contained in:
Charles de Dreuille
2025-10-03 16:45:49 +02:00
committed by GitHub
3 changed files with 60 additions and 12 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
Add react router for internal routing for ButtonLinks
@@ -14,15 +14,23 @@
* limitations under the License.
*/
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite';
import { ButtonLink } from './ButtonLink';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { Icon } from '../Icon';
import { MemoryRouter } from 'react-router-dom';
const meta = {
title: 'Backstage UI/ButtonLink',
component: ButtonLink,
decorators: [
(Story: StoryFn) => (
<MemoryRouter>
<Story />
</MemoryRouter>
),
],
argTypes: {
size: {
control: 'select',
@@ -16,13 +16,16 @@
import clsx from 'clsx';
import { forwardRef, Ref } from 'react';
import { Link as RALink } from 'react-aria-components';
import { Link as RALink, RouterProvider } from 'react-aria-components';
import { useNavigate, useHref } from 'react-router-dom';
import type { ButtonLinkProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { isExternalLink } from '../../utils/isExternalLink';
/** @public */
export const ButtonLink = forwardRef(
(props: ButtonLinkProps, ref: Ref<HTMLAnchorElement>) => {
const navigate = useNavigate();
const {
size = 'small',
variant = 'primary',
@@ -30,6 +33,7 @@ export const ButtonLink = forwardRef(
iconEnd,
children,
className,
href,
...rest
} = props;
@@ -40,17 +44,48 @@ export const ButtonLink = forwardRef(
const { classNames: classNamesButtonLink } = useStyles('ButtonLink');
const isExternal = isExternalLink(href);
// If it's an external link, render RALink without RouterProvider
if (isExternal) {
return (
<RALink
className={clsx(
classNames.root,
classNamesButtonLink.root,
className,
)}
ref={ref}
{...dataAttributes}
href={href}
{...rest}
>
{iconStart}
{children}
{iconEnd}
</RALink>
);
}
// For internal links, use RouterProvider
return (
<RALink
className={clsx(classNames.root, classNamesButtonLink.root, className)}
ref={ref}
{...dataAttributes}
{...rest}
>
{iconStart}
{children}
{iconEnd}
</RALink>
<RouterProvider navigate={navigate} useHref={useHref}>
<RALink
className={clsx(
classNames.root,
classNamesButtonLink.root,
className,
)}
ref={ref}
{...dataAttributes}
href={href}
{...rest}
>
{iconStart}
{children}
{iconEnd}
</RALink>
</RouterProvider>
);
},
);