Update IconButton
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -697,37 +697,38 @@ export const Icon: (props: IconProps) => JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const IconButton: ForwardRefExoticComponent<
|
||||
IconButtonProps & RefAttributes<HTMLButtonElement>
|
||||
IconButtonProps & RefAttributes<HTMLElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type IconButtonOwnProps = GetPropDefTypes<typeof iconButtonPropDefs>;
|
||||
export type IconButtonAnchorProps = IconButtonCommonProps &
|
||||
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'type' | 'onClick'> & {
|
||||
href: string;
|
||||
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const iconButtonPropDefs: {
|
||||
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 IconButtonCommonProps = {
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint_2, 'small' | 'medium'>>;
|
||||
variant?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| Partial<Record<Breakpoint_2, 'primary' | 'secondary'>>;
|
||||
icon?: ReactElement;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type IconButtonNativeProps = IconButtonCommonProps &
|
||||
Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'href'> & {
|
||||
href?: undefined;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface IconButtonProps
|
||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
||||
icon: ReactElement;
|
||||
size?: IconButtonOwnProps['size'];
|
||||
variant?: IconButtonOwnProps['variant'];
|
||||
}
|
||||
export type IconButtonProps = IconButtonAnchorProps | IconButtonNativeProps;
|
||||
|
||||
// @public (undocumented)
|
||||
export const IconContext: Context<IconContextProps>;
|
||||
|
||||
@@ -33,9 +33,7 @@ export const Button = forwardRef<HTMLElement, ButtonProps>((props, ref) => {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
function isAnchor(props: { href?: unknown }): props is { href: string } {
|
||||
return typeof props.href === 'string';
|
||||
}
|
||||
const isAnchor = typeof props.href === 'string';
|
||||
|
||||
const responsiveSize = useResponsiveValue(size);
|
||||
const responsiveVariant = useResponsiveValue(variant);
|
||||
@@ -64,7 +62,7 @@ export const Button = forwardRef<HTMLElement, ButtonProps>((props, ref) => {
|
||||
</>
|
||||
);
|
||||
|
||||
if (isAnchor(props)) {
|
||||
if (isAnchor) {
|
||||
const { onClick, ...anchorRest } =
|
||||
rest as AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
return (
|
||||
|
||||
@@ -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 iconButtonPropDefs = {
|
||||
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 IconButtonOwnProps = GetPropDefTypes<typeof iconButtonPropDefs>;
|
||||
@@ -83,6 +83,15 @@ export const Disabled: Story = {
|
||||
),
|
||||
};
|
||||
|
||||
export const AsLink: Story = {
|
||||
args: {
|
||||
icon: <Icon name="cloud" />,
|
||||
href: 'https://canon.backstage.io',
|
||||
target: '_blank',
|
||||
'aria-label': 'Cloud icon button',
|
||||
},
|
||||
};
|
||||
|
||||
export const Responsive: Story = {
|
||||
args: {
|
||||
icon: <Icon name="cloud" />,
|
||||
|
||||
@@ -14,46 +14,74 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import { AnchorHTMLAttributes, ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
|
||||
import type { IconButtonProps } from './types';
|
||||
|
||||
/** @public */
|
||||
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||
(props: IconButtonProps, ref) => {
|
||||
export const IconButton = forwardRef<HTMLElement, IconButtonProps>(
|
||||
(props, ref) => {
|
||||
const {
|
||||
size = 'small',
|
||||
variant = 'primary',
|
||||
icon,
|
||||
className,
|
||||
href,
|
||||
style,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const isAnchor = typeof props.href === 'string';
|
||||
const responsiveSize = useResponsiveValue(size);
|
||||
const responsiveVariant = useResponsiveValue(variant);
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
className={clsx('canon-IconButton', className)}
|
||||
const content = (
|
||||
<span
|
||||
className="canon-IconButtonIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
data-variant={responsiveVariant}
|
||||
style={style}
|
||||
{...rest}
|
||||
>
|
||||
<span
|
||||
className="canon-IconButtonIcon"
|
||||
aria-hidden="true"
|
||||
data-size={responsiveSize}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
</button>
|
||||
{icon}
|
||||
</span>
|
||||
);
|
||||
|
||||
if (isAnchor) {
|
||||
const { onClick, ...anchorRest } =
|
||||
rest as AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
className={clsx('canon-IconButton', className)}
|
||||
data-variant={responsiveVariant}
|
||||
data-size={responsiveSize}
|
||||
style={style}
|
||||
ref={ref as React.Ref<HTMLAnchorElement>}
|
||||
onClick={onClick}
|
||||
{...anchorRest}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
const { onClick, ...buttonRest } =
|
||||
rest as ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={clsx('canon-Button', className)}
|
||||
data-variant={responsiveVariant}
|
||||
data-size={responsiveSize}
|
||||
style={style}
|
||||
ref={ref as React.Ref<HTMLButtonElement>}
|
||||
onClick={onClick}
|
||||
{...buttonRest}
|
||||
>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export default IconButton;
|
||||
IconButton.displayName = 'IconButton';
|
||||
|
||||
@@ -14,7 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { IconButton } from './IconButton';
|
||||
export type { IconButtonProps } from './types';
|
||||
export { iconButtonPropDefs } from './IconButton.props';
|
||||
export type { IconButtonOwnProps } from './IconButton.props';
|
||||
export * from './IconButton';
|
||||
export * from './types';
|
||||
|
||||
@@ -14,30 +14,43 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IconButtonOwnProps } from './IconButton.props';
|
||||
import { ReactElement } from 'react';
|
||||
import {
|
||||
AnchorHTMLAttributes,
|
||||
ButtonHTMLAttributes,
|
||||
ReactElement,
|
||||
} from 'react';
|
||||
import { Breakpoint } from '@backstage/canon';
|
||||
|
||||
/** @public */
|
||||
export type IconButtonCommonProps = {
|
||||
size?: 'small' | 'medium' | Partial<Record<Breakpoint, 'small' | 'medium'>>;
|
||||
variant?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| Partial<Record<Breakpoint, 'primary' | 'secondary'>>;
|
||||
icon?: ReactElement;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type IconButtonAnchorProps = IconButtonCommonProps &
|
||||
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'type' | 'onClick'> & {
|
||||
href: string;
|
||||
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type IconButtonNativeProps = IconButtonCommonProps &
|
||||
Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'href'> & {
|
||||
href?: undefined;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Properties for {@link IconButton}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IconButtonProps
|
||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
||||
/**
|
||||
* The size of the button
|
||||
* @defaultValue 'medium'
|
||||
*/
|
||||
size?: IconButtonOwnProps['size'];
|
||||
|
||||
/**
|
||||
* The visual variant of the button
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
variant?: IconButtonOwnProps['variant'];
|
||||
|
||||
/**
|
||||
* Icon to display in the button
|
||||
*/
|
||||
icon: ReactElement;
|
||||
}
|
||||
export type IconButtonProps = IconButtonAnchorProps | IconButtonNativeProps;
|
||||
|
||||
Reference in New Issue
Block a user