Merge pull request #29247 from backstage/canon-render-prop
Canon: Add render prop to Link
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
We added a render prop to the Link component to make sure it can work with React Router.
|
||||
@@ -15,6 +15,7 @@ import { RefAttributes } from 'react';
|
||||
import type { RemixiconComponentType } from '@remixicon/react';
|
||||
import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area';
|
||||
import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip';
|
||||
import type { useRender } from '@base-ui-components/react/use-render';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlignItems = 'stretch' | 'start' | 'center' | 'end';
|
||||
@@ -696,6 +697,32 @@ export type JustifyContent =
|
||||
| 'around'
|
||||
| 'between';
|
||||
|
||||
// @public (undocumented)
|
||||
export const Link: React_2.ForwardRefExoticComponent<
|
||||
Omit<LinkProps, 'ref'> & React_2.RefAttributes<HTMLElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface LinkProps extends useRender.ComponentProps<'a'> {
|
||||
// (undocumented)
|
||||
children: ReactNode;
|
||||
// (undocumented)
|
||||
className?: string;
|
||||
// (undocumented)
|
||||
style?: CSSProperties;
|
||||
// (undocumented)
|
||||
to?: string;
|
||||
// (undocumented)
|
||||
variant?:
|
||||
| 'subtitle'
|
||||
| 'body'
|
||||
| 'caption'
|
||||
| 'label'
|
||||
| Partial<Record<Breakpoint, 'subtitle' | 'body' | 'caption' | 'label'>>;
|
||||
// (undocumented)
|
||||
weight?: 'regular' | 'bold' | Partial<Record<Breakpoint, 'regular' | 'bold'>>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const marginPropDefs: (spacingValues: string[]) => {
|
||||
m: {
|
||||
|
||||
@@ -19,6 +19,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Link } from './Link';
|
||||
import { Flex } from '../Flex';
|
||||
import { Text } from '../Text';
|
||||
import { Link as RouterLink, MemoryRouter } from 'react-router-dom';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Link',
|
||||
@@ -33,7 +34,7 @@ type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
href: 'https://canon.backstage.io',
|
||||
to: 'https://canon.backstage.io',
|
||||
children: 'Sign up for Backstage',
|
||||
},
|
||||
};
|
||||
@@ -66,6 +67,7 @@ export const AllWeights: Story = {
|
||||
|
||||
export const Responsive: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
variant: {
|
||||
xs: 'label',
|
||||
md: 'body',
|
||||
@@ -73,6 +75,23 @@ export const Responsive: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomRender: Story = {
|
||||
render: () => {
|
||||
return (
|
||||
<Flex gap="4" direction="column" align="start">
|
||||
<Link render={<RouterLink to="/catalog" />}>Go to Catalog</Link>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
decorators: [
|
||||
Story => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export const Playground: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
|
||||
@@ -15,41 +15,43 @@
|
||||
*/
|
||||
|
||||
import React, { forwardRef } from 'react';
|
||||
import { useRender } from '@base-ui-components/react/use-render';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import type { LinkProps } from './types';
|
||||
|
||||
/** @public */
|
||||
export const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
|
||||
export const Link = forwardRef<HTMLElement, LinkProps>((props, ref) => {
|
||||
const {
|
||||
children,
|
||||
className,
|
||||
variant = 'body',
|
||||
weight = 'regular',
|
||||
style,
|
||||
className,
|
||||
render = <a />,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
// Get the responsive values for the variant and weight
|
||||
const responsiveVariant = useResponsiveValue(variant);
|
||||
const responsiveWeight = useResponsiveValue(weight);
|
||||
const internalRef = React.useRef<HTMLElement | null>(null);
|
||||
|
||||
return (
|
||||
<a
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
const { renderElement } = useRender({
|
||||
render,
|
||||
props: {
|
||||
className: clsx(
|
||||
'canon-Link',
|
||||
responsiveVariant && `canon-Link--variant-${responsiveVariant}`,
|
||||
responsiveWeight && `canon-Link--weight-${responsiveWeight}`,
|
||||
className,
|
||||
)}
|
||||
style={style}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
),
|
||||
responsiveVariant,
|
||||
responsiveWeight,
|
||||
...restProps,
|
||||
},
|
||||
refs: [ref, internalRef],
|
||||
});
|
||||
|
||||
return renderElement();
|
||||
});
|
||||
|
||||
Link.displayName = 'Link';
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import type { Breakpoint } from '../../types';
|
||||
import type { useRender } from '@base-ui-components/react/use-render';
|
||||
|
||||
/** @public */
|
||||
export interface LinkProps
|
||||
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
export interface LinkProps extends useRender.ComponentProps<'a'> {
|
||||
children: ReactNode;
|
||||
to?: string;
|
||||
variant?:
|
||||
| 'subtitle'
|
||||
| 'body'
|
||||
|
||||
@@ -41,6 +41,7 @@ export * from './components/TextField';
|
||||
export * from './components/Tooltip';
|
||||
export * from './components/Menu';
|
||||
export * from './components/ScrollArea';
|
||||
export * from './components/Link';
|
||||
export * from './components/Select';
|
||||
|
||||
// Types
|
||||
|
||||
Reference in New Issue
Block a user