diff --git a/src/components/ActionsPanel/ActionsPanel.tsx b/src/components/ActionsPanel/ActionsPanel.tsx index 6ab747e46d..134aa14e72 100644 --- a/src/components/ActionsPanel/ActionsPanel.tsx +++ b/src/components/ActionsPanel/ActionsPanel.tsx @@ -44,9 +44,7 @@ export const ActionsPanel = ({ size="m" onClick={onClose} className={b('button-close')} - extraProps={{ - 'aria-label': i18n('label_close'), - }} + aria-label={i18n('label_close')} > diff --git a/src/components/ActionsPanel/components/CollapseActions.tsx b/src/components/ActionsPanel/components/CollapseActions.tsx index 9faa79e1e6..65d565e5ba 100644 --- a/src/components/ActionsPanel/components/CollapseActions.tsx +++ b/src/components/ActionsPanel/components/CollapseActions.tsx @@ -69,9 +69,7 @@ export const CollapseActions = ({actions, maxRowActions}: Props) => { + ); } -}); -ButtonWithHandlers.displayName = 'Button'; + return ( + + ); +}) as >( + props: P extends {component: T} + ? ButtonComponentProps & {ref?: any} // TODO: Add ref inference + : P extends {href: string} + ? ButtonLinkProps & {ref?: React.Ref} + : ButtonButtonProps & {ref?: React.Ref}, +) => React.ReactElement; -export const Button = Object.assign(ButtonWithHandlers, {Icon: ButtonIcon}); +export const Button = Object.assign(_Button, {Icon: ButtonIcon}); const isButtonIconComponent = isOfType(ButtonIcon); const isSpan = isOfType<{className?: string}>('span'); diff --git a/src/components/Button/README.md b/src/components/Button/README.md index e633983fda..c423166009 100644 --- a/src/components/Button/README.md +++ b/src/components/Button/README.md @@ -448,33 +448,21 @@ LANDING_BLOCK--> ## Properties -| Name | Description | Type | Default | -| :----------- | :----------------------------------------------------------------- | :-----------------------------: | :-------------: | -| children | Button content. You can use both text and the `` component. | `ReactNode` | | -| className | `class` HTML attribute | `string` | | -| component | Overrides the root component | `ElementType` | `"button"` | -| disabled | Toggles the `disabled` state | `false` | `false` | -| extraProps | Additional properties | `Record` | | -| href | `href` HTML attribute | `string` | | -| id | `id` HTML attribute | `string` | | -| loading | Toggles the `loading` state | `false` | `false` | -| onBlur | `blur` event handler | `Function` | | -| onClick | `click` event handler | `Function` | | -| onFocus | `focus` event handler | `Function` | | -| onMouseEnter | `mouseenter` event handler | `Function` | | -| onMouseLeave | `mouseleave` event handler | `Function` | | -| pin | Sets the button edge style | `string` | `"round-round"` | -| qa | `data-qa` HTML attribute, used for testing | `string` | | -| rel | `rel` HTML attribute | `string` | | -| selected | Toggles the `selected` state | | | -| size | Sets the button size | `string` | `"m"` | -| style | `style` HTML attribute | `React.CSSProperties` | | -| tabIndex | `tabIndex` HTML attribute | `number` | | -| target | `target` HTML attribute | `string` | | -| title | `title` HTML attribute | `string` | | -| type | `type` HTML attribute | `"button"` `"submit"` `"reset"` | `"button"` | -| view | Sets the button appearance | `string` | `"normal"` | -| width | `"auto"` `"max"` | `"auto"` `"max"` | | +`Buttont` accepts any valid `button` or `a` element props in addition to these: + +| Name | Description | Type | Default | +| :-------- | :------------------------------------------------------------------- | :-----------------------------: | :-------------: | +| children | `Button` content. You can use both text and the `` component. | `React.ReactNode` | | +| component | Overrides the root component | `React.ElementType` | | +| disabled | Toggles the `disabled` state | `boolean` | `false` | +| href | Pass this to make the root component a link | `string` | | +| loading | Toggles the `loading` state | `boolean` | `false` | +| pin | Sets the `Button` edge style | `string` | `"round-round"` | +| qa | `data-qa` HTML attribute, used for testing | `string` | | +| selected | Toggles the `selected` state | `boolean` | | +| size | Sets the`Button` size | `"xs"` `"s"` `"m"` `"l"` `"xl"` | `"m"` | +| view | Sets the `Button` appearance | `ButtonView` | `"normal"` | +| width | Controls how `Button` uses parent's space | `"auto"` `"max"` | | ## CSS API diff --git a/src/components/Button/__stories__/Button.stories.tsx b/src/components/Button/__stories__/Button.stories.tsx index b485a93c86..156f55f80b 100644 --- a/src/components/Button/__stories__/Button.stories.tsx +++ b/src/components/Button/__stories__/Button.stories.tsx @@ -14,6 +14,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import {Showcase} from '../../../demo/Showcase'; import {Icon as IconComponent} from '../../Icon/Icon'; import {Button} from '../Button'; +import type {ButtonButtonProps, ButtonLinkProps} from '../Button'; import {ButtonViewShowcase} from './ButtonViewShowcase'; @@ -54,7 +55,7 @@ export const Default: Story = { }; export const View: Story = { - render: (args) => , + render: (args) => , }; export const Size: Story = { @@ -174,7 +175,7 @@ export const Link: Story = { children: ['Link Button', ], href: 'https://gravity-ui.com', target: '_blank', - }, + } as ButtonLinkProps, name: 'As Link', }; @@ -190,7 +191,7 @@ export const InsideText: Story = { {' '} amet diff --git a/src/components/Button/__tests__/Button.test.tsx b/src/components/Button/__tests__/Button.test.tsx index ec28285ad1..3476954662 100644 --- a/src/components/Button/__tests__/Button.test.tsx +++ b/src/components/Button/__tests__/Button.test.tsx @@ -5,7 +5,7 @@ import userEvent from '@testing-library/user-event'; import {render, screen} from '../../../../test-utils/utils'; import {Button} from '../Button'; -import type {ButtonPin, ButtonProps, ButtonSize, ButtonView} from '../Button'; +import type {ButtonPin, ButtonSize, ButtonView} from '../Button'; const qaId = 'button-component'; @@ -105,7 +105,7 @@ describe('Button', () => { test('should render custom component', () => { const text = 'Button with custom component'; - const ButtonComponent = (props: ButtonProps) => { + const ButtonComponent = (props: React.ButtonHTMLAttributes) => { return ( diff --git a/src/components/Dialog/DialogFooter/DialogFooter.tsx b/src/components/Dialog/DialogFooter/DialogFooter.tsx index d87e078fa4..f91f9ea23f 100644 --- a/src/components/Dialog/DialogFooter/DialogFooter.tsx +++ b/src/components/Dialog/DialogFooter/DialogFooter.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import {Button} from '../../Button'; -import type {ButtonProps, ButtonView} from '../../Button'; +import type {ButtonButtonProps, ButtonView} from '../../Button'; import {Popup} from '../../Popup'; import {block} from '../../utils/cn'; @@ -18,8 +18,8 @@ interface DialogFooterOwnProps { onClickButtonCancel?: (event: React.MouseEvent) => void; textButtonCancel?: string; textButtonApply?: string; - propsButtonCancel?: Partial; - propsButtonApply?: Partial; + propsButtonCancel?: ButtonButtonProps; + propsButtonApply?: ButtonButtonProps; loading?: boolean; children?: React.ReactNode; errorText?: string; diff --git a/src/components/Dialog/__stories__/DialogShowcase.tsx b/src/components/Dialog/__stories__/DialogShowcase.tsx index 382b3d1456..9113337c40 100644 --- a/src/components/Dialog/__stories__/DialogShowcase.tsx +++ b/src/components/Dialog/__stories__/DialogShowcase.tsx @@ -197,7 +197,7 @@ export function DialogShowcase() { textButtonApply="attack" propsButtonApply={{ className: 'my-custom-apply-btn-class', - extraProps: {autoFocus: true}, + autoFocus: true, }} textButtonCancel="fend off attack" propsButtonCancel={{className: 'my-custom-cancel-btn-class'}} diff --git a/src/components/FilePreview/FilePreviewAction.tsx b/src/components/FilePreview/FilePreviewAction.tsx index 7c0723455e..d41bc16429 100644 --- a/src/components/FilePreview/FilePreviewAction.tsx +++ b/src/components/FilePreview/FilePreviewAction.tsx @@ -3,6 +3,7 @@ import type * as React from 'react'; import {ActionTooltip} from '../ActionTooltip'; import type {ActionTooltipProps} from '../ActionTooltip'; import {Button} from '../Button'; +import type {ButtonButtonProps, ButtonLinkProps} from '../Button'; export interface FilePreviewActionProps { id?: string; @@ -11,9 +12,7 @@ export interface FilePreviewActionProps { href?: string; disabled?: boolean; onClick?: React.MouseEventHandler; - extraProps?: - | React.ButtonHTMLAttributes - | React.AnchorHTMLAttributes; + extraProps?: ButtonButtonProps | ButtonLinkProps; tooltipExtraProps?: Omit; } @@ -31,13 +30,14 @@ export function FilePreviewAction({ diff --git a/src/components/Palette/Palette.tsx b/src/components/Palette/Palette.tsx index c8a3469b28..e37e55175a 100644 --- a/src/components/Palette/Palette.tsx +++ b/src/components/Palette/Palette.tsx @@ -194,7 +194,7 @@ export const Palette = React.forwardRef(function P title={option.title} view={isSelected ? 'normal' : 'flat'} selected={isSelected} - extraProps={{value: option.value}} + value={option.value} size={size} onClick={() => handleSelection(option)} > diff --git a/src/components/PlaceholderContainer/PlaceholderContainer.tsx b/src/components/PlaceholderContainer/PlaceholderContainer.tsx index 36d920e69c..c9f30c23f1 100644 --- a/src/components/PlaceholderContainer/PlaceholderContainer.tsx +++ b/src/components/PlaceholderContainer/PlaceholderContainer.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import {Button} from '../Button'; +import type {ButtonProps} from '../Button'; import {block} from '../utils/cn'; import {componentClassName} from './constants'; @@ -10,19 +11,11 @@ import './PlaceholderContainer.scss'; const b = block(componentClassName); -const PlaceholderContainerAction = (props: PlaceholderContainerActionProps) => { +const PlaceholderContainerAction = ({text, ...buttonProps}: PlaceholderContainerActionProps) => { return (
-
); diff --git a/src/components/PlaceholderContainer/types.ts b/src/components/PlaceholderContainer/types.ts index c4fdad608b..db29d420d9 100644 --- a/src/components/PlaceholderContainer/types.ts +++ b/src/components/PlaceholderContainer/types.ts @@ -14,8 +14,9 @@ export type PlaceholderContainerImageProps = { export type PlaceholderContainerActionProps = Pick< ButtonProps, - 'disabled' | 'loading' | 'view' | 'size' | 'href' | 'onClick' + 'disabled' | 'loading' | 'view' | 'size' | 'onClick' > & { + href?: string; text: string; }; diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx index e6926af52d..cc7196f710 100644 --- a/src/components/Popover/Popover.tsx +++ b/src/components/Popover/Popover.tsx @@ -177,9 +177,7 @@ export const Popover = React.forwardRef diff --git a/src/components/Popover/__stories__/Popover.stories.tsx b/src/components/Popover/__stories__/Popover.stories.tsx index f95c641b34..d2ee4b3734 100644 --- a/src/components/Popover/__stories__/Popover.stories.tsx +++ b/src/components/Popover/__stories__/Popover.stories.tsx @@ -215,11 +215,9 @@ const AccessibleTemplate: StoryFn = () => { {({onClick, open}) => ( @@ -246,11 +244,9 @@ const AccessibleTemplate: StoryFn = () => { {({onClick}) => ( diff --git a/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx b/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx index 90c98fcbff..462f441042 100644 --- a/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx +++ b/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx @@ -4,7 +4,7 @@ import {ArrowRotateLeft} from '@gravity-ui/icons'; import _isEqual from 'lodash/isEqual'; import {Button} from '../../../Button'; -import type {ButtonProps} from '../../../Button'; +import type {ButtonButtonProps} from '../../../Button'; import {Icon} from '../../../Icon'; import {Flex} from '../../../layout'; import type {TableProps} from '../../Table'; @@ -72,11 +72,11 @@ export const WithTableSettingsCustomActionsShowcase = ({ ); }; -function SelectAllButton({onClick}: T) { +function SelectAllButton({onClick}: T) { return ; } -function ResetButton({onClick}: T) { +function ResetButton({onClick}: T) { return ( diff --git a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx index 90d14ae3b9..5701711c2d 100644 --- a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx +++ b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx @@ -384,7 +384,7 @@ export const TableColumnSetup = (props: TableColumnSetupProps) => { return ( renderSwitcher?.({onClick: toggleOpen, onKeyDown}) || ( - diff --git a/src/components/Table/hoc/withTableSettings/withTableSettings.tsx b/src/components/Table/hoc/withTableSettings/withTableSettings.tsx index 506be20b95..b839c68f74 100644 --- a/src/components/Table/hoc/withTableSettings/withTableSettings.tsx +++ b/src/components/Table/hoc/withTableSettings/withTableSettings.tsx @@ -221,7 +221,7 @@ export function withTableSettings( diff --git a/src/components/Tooltip/__stories__/Tooltip.stories.tsx b/src/components/Tooltip/__stories__/Tooltip.stories.tsx index 5233c03d41..1e3a9c4ebc 100644 --- a/src/components/Tooltip/__stories__/Tooltip.stories.tsx +++ b/src/components/Tooltip/__stories__/Tooltip.stories.tsx @@ -16,7 +16,7 @@ export const Default: Story = { render: (args) => { return ( - + ); }, diff --git a/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx b/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx index ccf3edcc9f..5280297ca5 100644 --- a/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx +++ b/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx @@ -91,11 +91,11 @@ export const WithGroupSelectionAndCustomIconStory = ({ [id]: !prevExpandedState[id], })); }} - extraProps={{ - 'aria-label': itemProps.content.expanded + aria-label={ + itemProps.content.expanded ? closeButtonLabel - : expandButtonLabel, - }} + : expandButtonLabel + } > ), @@ -90,11 +88,11 @@ export const WithItemLinksAndActionsStory = (props: WithItemLinksAndActionsStory [id]: !prevExpandedState[id], })); }} - extraProps={{ - 'aria-label': itemProps.content.expanded + aria-label={ + itemProps.content.expanded ? closeButtonLabel - : expandButtonLabel, - }} + : expandButtonLabel + } > { disabled={props.disabled} onClick={() => setRevealValue(!revealValue)} size={actionButtonSize} - extraProps={{ - 'aria-label': revealValue - ? i18n('label_hide-password') - : i18n('label_show-password'), - onMouseDown: (event: React.SyntheticEvent) => event.preventDefault(), - }} + onMouseDown={(event: React.SyntheticEvent) => event.preventDefault()} + aria-label={ + revealValue ? i18n('label_hide-password') : i18n('label_show-password') + } > diff --git a/src/components/controls/TextInput/__stories__/TextInputShowcase.tsx b/src/components/controls/TextInput/__stories__/TextInputShowcase.tsx index 49e6a137f3..cc8bad8fef 100644 --- a/src/components/controls/TextInput/__stories__/TextInputShowcase.tsx +++ b/src/components/controls/TextInput/__stories__/TextInputShowcase.tsx @@ -34,7 +34,7 @@ const EyeButton = (props: { view="flat" disabled={disabled} onClick={onClick} - extraProps={{'aria-label': opened ? showLabel : hideLabel}} + aria-label={opened ? showLabel : hideLabel} > diff --git a/src/components/controls/common/ClearButton/ClearButton.tsx b/src/components/controls/common/ClearButton/ClearButton.tsx index 7869dfc5b5..70f5344a83 100644 --- a/src/components/controls/common/ClearButton/ClearButton.tsx +++ b/src/components/controls/common/ClearButton/ClearButton.tsx @@ -58,10 +58,8 @@ export const ClearButton = (props: Props) => { size={size} className={b(null, className)} onClick={onClick} - extraProps={{ - onMouseDown: preventDefaultHandler, - 'aria-label': i18n('label_clear-button'), - }} + onMouseDown={preventDefaultHandler} + aria-label={i18n('label_clear-button')} > diff --git a/src/components/lab/NumberInput/NumericArrows/NumericArrows.tsx b/src/components/lab/NumberInput/NumericArrows/NumericArrows.tsx index 6423620840..0ebc64aba3 100644 --- a/src/components/lab/NumberInput/NumericArrows/NumericArrows.tsx +++ b/src/components/lab/NumberInput/NumericArrows/NumericArrows.tsx @@ -33,16 +33,14 @@ export function NumericArrows({ onDownClick, ...restProps }: NumericArrowsProps) { - const commonBtnProps: Partial = { + const commonBtnProps: ButtonProps = { size: 's', pin: 'brick-brick', view: 'flat-secondary', disabled, tabIndex: -1, width: 'max', - extraProps: { - 'aria-hidden': 'true', - }, + 'aria-hidden': 'true', }; return ( @@ -57,7 +55,7 @@ export function NumericArrows({ qa={INCREMENT_BUTTON_QA} {...commonBtnProps} onClick={onUpClick} - extraProps={{'aria-label': i18n('label_increment')}} + aria-label={i18n('label_increment')} > @@ -67,7 +65,7 @@ export function NumericArrows({ qa={DECREMENT_BUTTON_QA} {...commonBtnProps} onClick={onDownClick} - extraProps={{'aria-label': i18n('label_decrement')}} + aria-label={i18n('label_decrement')} > diff --git a/src/components/useList/components/ListItemExpandIcon/__stories__/ListItemExpandIcon.stories.tsx b/src/components/useList/components/ListItemExpandIcon/__stories__/ListItemExpandIcon.stories.tsx index 3ab02f8b70..2b8b0d84f9 100644 --- a/src/components/useList/components/ListItemExpandIcon/__stories__/ListItemExpandIcon.stories.tsx +++ b/src/components/useList/components/ListItemExpandIcon/__stories__/ListItemExpandIcon.stories.tsx @@ -27,7 +27,7 @@ const InsideButtonExample = (props: ListItemExpandIconProps) => { const [expanded, setExpanded] = React.useState(false); return ( - diff --git a/src/stories/Branding/BrandingConfugurator/BrandingConfigurator.tsx b/src/stories/Branding/BrandingConfugurator/BrandingConfigurator.tsx index 018c20c563..fb16b5330b 100644 --- a/src/stories/Branding/BrandingConfugurator/BrandingConfigurator.tsx +++ b/src/stories/Branding/BrandingConfugurator/BrandingConfigurator.tsx @@ -113,9 +113,7 @@ export function BrandingConfigurator({theme}: BrandingConfiguratorProps) { view="outlined" size="xl" onClick={handleRandomClick} - extraProps={{ - 'aria-label': 'Regenerate colors', - }} + aria-label="Regenerate colors" > diff --git a/src/stories/Branding/PaletteGenerator/PaletteGenerator.tsx b/src/stories/Branding/PaletteGenerator/PaletteGenerator.tsx index cc71894d69..fd2b54048d 100644 --- a/src/stories/Branding/PaletteGenerator/PaletteGenerator.tsx +++ b/src/stories/Branding/PaletteGenerator/PaletteGenerator.tsx @@ -179,9 +179,7 @@ export function PaletteGenerator({theme}: BrandingConfiguratorProps) { view="outlined" size="l" onClick={handleSwapContrastClick} - extraProps={{ - 'aria-label': 'Switch colors', - }} + aria-label="Switch colors" >