diff --git a/.changeset/public-nails-melt.md b/.changeset/public-nails-melt.md new file mode 100644 index 0000000000..a8d916950a --- /dev/null +++ b/.changeset/public-nails-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +**Breaking** We are removing the render prop on the Button component. If you want to use Button as a link, simply add the href prop and we will automaticlly use it as an anchor tag underneath. diff --git a/canon-docs/src/content/components/button.mdx b/canon-docs/src/content/components/button.mdx index aff74fe60b..dceabde154 100644 --- a/canon-docs/src/content/components/button.mdx +++ b/canon-docs/src/content/components/button.mdx @@ -11,6 +11,7 @@ import { buttonFullWidthSnippet, buttonDisabledSnippet, buttonResponsiveSnippet, + buttonAsLinkSnippet, } from './button.props'; import { ComponentInfos } from '@/components/ComponentInfos'; @@ -102,3 +103,15 @@ Here's a view when buttons are disabled. Here's a view when buttons are responsive. + +### As Link + +Use the `href` prop to make a button act as a link. + +} + code={buttonAsLinkSnippet} +/> diff --git a/canon-docs/src/content/components/button.props.ts b/canon-docs/src/content/components/button.props.ts index fc833a5f73..9cbfc4a80c 100644 --- a/canon-docs/src/content/components/button.props.ts +++ b/canon-docs/src/content/components/button.props.ts @@ -61,3 +61,7 @@ export const buttonDisabledSnippet = ` export const buttonResponsiveSnippet = ``; + +export const buttonAsLinkSnippet = ``; diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 4db3a81bbc..df15e5c20d 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -3,8 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { AnchorHTMLAttributes } from 'react'; import { Avatar as Avatar_2 } from '@base-ui-components/react/avatar'; import { Breakpoint as Breakpoint_2 } from '@backstage/canon'; +import { ButtonHTMLAttributes } from 'react'; import { ChangeEvent } from 'react'; import { Collapsible as Collapsible_2 } from '@base-ui-components/react/collapsible'; import { ComponentProps } from 'react'; @@ -152,37 +154,36 @@ export const breakpoints: Breakpoint[]; // @public (undocumented) export const Button: ForwardRefExoticComponent< - Omit & RefAttributes + ButtonProps & RefAttributes >; // @public (undocumented) -export type ButtonOwnProps = GetPropDefTypes; +export type ButtonAnchorProps = ButtonCommonProps & + Omit, 'type' | 'onClick'> & { + href: string; + onClick?: React.MouseEventHandler; + }; // @public (undocumented) -export const buttonPropDefs: { - variant: { - type: 'enum'; - values: ('primary' | 'secondary')[]; - className: string; - default: 'primary'; - responsive: true; - }; - size: { - type: 'enum'; - values: ('small' | 'medium')[]; - className: string; - default: 'medium'; - responsive: true; - }; +export type ButtonCommonProps = { + size?: 'small' | 'medium'; + variant?: 'primary' | 'secondary'; + iconStart?: ReactElement; + iconEnd?: ReactElement; + className?: string; + style?: React.CSSProperties; + children?: React.ReactNode; }; +// @public (undocumented) +export type ButtonNativeProps = ButtonCommonProps & + Omit, 'href'> & { + href?: undefined; + onClick?: React.MouseEventHandler; + }; + // @public -export interface ButtonProps extends useRender.ComponentProps<'button'> { - iconEnd?: ReactElement; - iconStart?: ReactElement; - size?: ButtonOwnProps['size']; - variant?: ButtonOwnProps['variant']; -} +export type ButtonProps = ButtonAnchorProps | ButtonNativeProps; // @public (undocumented) export const Checkbox: ForwardRefExoticComponent< diff --git a/packages/canon/src/components/Button/Button.props.ts b/packages/canon/src/components/Button/Button.props.ts deleted file mode 100644 index e363360afa..0000000000 --- a/packages/canon/src/components/Button/Button.props.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { PropDef, GetPropDefTypes } from '../../props/prop-def'; - -/** @public */ -export const buttonPropDefs = { - variant: { - type: 'enum', - values: ['primary', 'secondary'], - className: 'canon-Button--variant', - default: 'primary', - responsive: true, - }, - size: { - type: 'enum', - values: ['small', 'medium'], - className: 'canon-Button--size', - default: 'medium', - responsive: true, - }, -} satisfies { - variant: PropDef<'primary' | 'secondary'>; - size: PropDef<'small' | 'medium'>; -}; - -/** @public */ -export type ButtonOwnProps = GetPropDefTypes; diff --git a/packages/canon/src/components/Button/Button.stories.tsx b/packages/canon/src/components/Button/Button.stories.tsx index 2d79da7e29..8c9cc521f0 100644 --- a/packages/canon/src/components/Button/Button.stories.tsx +++ b/packages/canon/src/components/Button/Button.stories.tsx @@ -134,13 +134,9 @@ export const Disabled: Story = { export const AsLink: Story = { args: { children: 'I am a link', + href: 'https://canon.backstage.io', + target: '_blank', }, - render: args => ( - + ); + } +}); export default Button; diff --git a/packages/canon/src/components/Button/index.tsx b/packages/canon/src/components/Button/index.ts similarity index 76% rename from packages/canon/src/components/Button/index.tsx rename to packages/canon/src/components/Button/index.ts index 9051f1145e..b9c497ae1f 100644 --- a/packages/canon/src/components/Button/index.tsx +++ b/packages/canon/src/components/Button/index.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { Button } from './Button'; -export type { ButtonProps } from './types'; -export { buttonPropDefs } from './Button.props'; -export type { ButtonOwnProps } from './Button.props'; + +export * from './Button'; +export * from './types'; diff --git a/packages/canon/src/components/Button/types.ts b/packages/canon/src/components/Button/types.ts index e91538299e..c7ac2b88b7 100644 --- a/packages/canon/src/components/Button/types.ts +++ b/packages/canon/src/components/Button/types.ts @@ -14,35 +14,40 @@ * limitations under the License. */ -import type { ButtonOwnProps } from './Button.props'; -import { ReactElement } from 'react'; -import type { useRender } from '@base-ui-components/react/use-render'; +import { + ReactElement, + AnchorHTMLAttributes, + ButtonHTMLAttributes, +} from 'react'; + +/** @public */ +export type ButtonCommonProps = { + size?: 'small' | 'medium'; + variant?: 'primary' | 'secondary'; + iconStart?: ReactElement; + iconEnd?: ReactElement; + className?: string; + style?: React.CSSProperties; + children?: React.ReactNode; +}; + +/** @public */ +export type ButtonAnchorProps = ButtonCommonProps & + Omit, 'type' | 'onClick'> & { + href: string; + onClick?: React.MouseEventHandler; + }; + +/** @public */ +export type ButtonNativeProps = ButtonCommonProps & + Omit, 'href'> & { + href?: undefined; + onClick?: React.MouseEventHandler; + }; /** * Properties for {@link Button} * * @public */ -export interface ButtonProps extends useRender.ComponentProps<'button'> { - /** - * The size of the button - * @defaultValue 'medium' - */ - size?: ButtonOwnProps['size']; - - /** - * The visual variant of the button - * @defaultValue 'primary' - */ - variant?: ButtonOwnProps['variant']; - - /** - * Optional icon to display at the start of the button - */ - iconStart?: ReactElement; - - /** - * Optional icon to display at the end of the button - */ - iconEnd?: ReactElement; -} +export type ButtonProps = ButtonAnchorProps | ButtonNativeProps;