feat: add tabindex option for paragraph button (#48567)

* feat: add tabIndex option for paragraph

* test: update snapshot

* test: update test case

* revert: revert the testcase

* test: update test case

* fix: fix the coverage test

* chore: code optimize

* Update index.tsx

Signed-off-by: lijianan <574980606@qq.com>

* Update CopyBtn.tsx

Signed-off-by: lijianan <574980606@qq.com>

* Update transButton.tsx

Signed-off-by: lijianan <574980606@qq.com>

* Update copy.test.tsx

Signed-off-by: lijianan <574980606@qq.com>

* Update CopyBtn.tsx

Signed-off-by: lijianan <574980606@qq.com>

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: lijianan <574980606@qq.com>
This commit is contained in:
希亚的西红柿 2024-04-24 18:06:38 +08:00 committed by GitHub
parent 5c2e6e3651
commit 9acde83991
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 68 additions and 11 deletions

View File

@ -7,10 +7,11 @@ import * as React from 'react';
import KeyCode from 'rc-util/lib/KeyCode';
interface TransButtonProps extends React.HTMLAttributes<HTMLDivElement> {
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
onClick?: (e?: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
noStyle?: boolean;
autoFocus?: boolean;
disabled?: boolean;
tabIndex?: number;
}
const inlineStyle: React.CSSProperties = {
@ -37,7 +38,7 @@ const TransButton = React.forwardRef<HTMLDivElement, TransButtonProps>((props, r
}
};
const { style, noStyle, disabled, ...restProps } = props;
const { style, noStyle, disabled, tabIndex = 0, ...restProps } = props;
let mergedStyle: React.CSSProperties = {};
@ -59,7 +60,7 @@ const TransButton = React.forwardRef<HTMLDivElement, TransButtonProps>((props, r
return (
<div
role="button"
tabIndex={0}
tabIndex={tabIndex}
ref={ref}
{...restProps}
onKeyDown={onKeyDown}

View File

@ -14,12 +14,12 @@ export interface CopyBtnProps extends Omit<CopyConfig, 'onCopy'> {
prefixCls: string;
copied: boolean;
locale: Locale['Text'];
onCopy: React.MouseEventHandler<HTMLDivElement>;
onCopy: (e?: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
iconOnly: boolean;
loading: boolean;
}
export default function CopyBtn(props: CopyBtnProps) {
const CopyBtn: React.FC<CopyBtnProps> = (props) => {
const { prefixCls, copied, locale = {}, onCopy, iconOnly, tooltips, icon, loading } = props;
const tooltipNodes = toList(tooltips);
@ -40,8 +40,9 @@ export default function CopyBtn(props: CopyBtnProps) {
[`${prefixCls}-copy-success`]: copied,
[`${prefixCls}-copy-icon-only`]: iconOnly,
})}
onClick={onCopy as any}
onClick={onCopy}
aria-label={ariaLabel}
tabIndex={props.tabIndex}
>
{copied
? getNode(iconNodes[1], <CheckOutlined />, true)
@ -49,4 +50,6 @@ export default function CopyBtn(props: CopyBtnProps) {
</TransButton>
</Tooltip>
);
}
};
export default CopyBtn;

View File

@ -33,6 +33,7 @@ export interface CopyConfig {
icon?: React.ReactNode;
tooltips?: React.ReactNode;
format?: 'text/plain' | 'text/html';
tabIndex?: number;
}
interface EditConfig {
@ -48,6 +49,7 @@ interface EditConfig {
autoSize?: boolean | AutoSizeType;
triggerType?: ('icon' | 'text')[];
enterIcon?: React.ReactNode;
tabIndex?: number;
}
export interface EllipsisConfig {
@ -372,8 +374,12 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
const renderExpand = () => {
const { expandable, symbol } = ellipsisConfig;
if (!expandable) return null;
if (expanded && expandable !== 'collapsible') return null;
if (!expandable) {
return null;
}
if (expanded && expandable !== 'collapsible') {
return null;
}
return (
<a
@ -389,9 +395,11 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
// Edit
const renderEdit = () => {
if (!enableEdit) return;
if (!enableEdit) {
return;
}
const { icon, tooltip } = editConfig;
const { icon, tooltip, tabIndex } = editConfig;
const editTitle = toArray(tooltip)[0] || textLocale?.edit;
const ariaLabel = typeof editTitle === 'string' ? editTitle : '';
@ -403,6 +411,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
className={`${prefixCls}-edit`}
onClick={onEditClick}
aria-label={ariaLabel}
tabIndex={tabIndex}
>
{icon || <EditOutlined role="button" />}
</TransButton>

View File

@ -328,4 +328,28 @@ describe('Typography copy', () => {
);
expect(container.querySelector('.ant-typography-copy')).toBeTruthy();
});
it('tabIndex of copy button', () => {
const { container } = render(
<Base component="p" copyable={{ tabIndex: -1 }}>
test
</Base>,
);
expect(container.querySelector('.ant-typography-copy')?.getAttribute('tabIndex')).toBe('-1');
});
it('locale text for button tooltip', async () => {
const { container } = render(
<Base component="p" copyable>
test
</Base>,
);
fireEvent.mouseEnter(container.querySelectorAll('.ant-typography-copy')[0]);
await waitFakeTimer();
await waitFor(() => {
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe('Copy');
});
fireEvent.click(container.querySelectorAll('.ant-typography-copy')[0]);
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe('Copied');
});
});

View File

@ -90,4 +90,16 @@ describe('Typography.Editable', () => {
);
expect(container.querySelector('.ant-typography-edit')).toBeTruthy();
});
it('tabIndex of edit button', () => {
const { container, rerender } = render(<Base component="p">test</Base>);
expect(container.querySelector('.ant-typography-edit')).toBeFalsy();
rerender(
<Base component="p" editable={{ tabIndex: -1 }}>
test
</Base>,
);
expect(container.querySelector('.ant-typography-edit')?.getAttribute('tabIndex')).toBe('-1');
});
});

View File

@ -92,6 +92,7 @@ Common props ref[Common props](/docs/react/common-props)
icon: ReactNode,
tooltips: false | [ReactNode, ReactNode],
format: 'text/plain' | 'text/html',
tabIndex: number,
}
| Property | Description | Type | Default | Version |
@ -101,6 +102,7 @@ Common props ref[Common props](/docs/react/common-props)
| text | The text to copy | string | - | |
| tooltips | Custom tooltip text, hide when it is false | \[ReactNode, ReactNode] | \[`Copy`, `Copied`] | 4.4.0 |
| onCopy | Called when copied text | function | - | |
| tabIndex | Set tabIndex of the copy button | number | 0 | 5.17.0 |
### editable
@ -117,6 +119,7 @@ Common props ref[Common props](/docs/react/common-props)
onEnd: function,
triggerType: ('icon' | 'text')[],
enterIcon: ReactNode,
tabIndex: number,
}
| Property | Description | Type | Default | Version |
@ -133,6 +136,7 @@ Common props ref[Common props](/docs/react/common-props)
| onEnd | Called when type ENTER to exit editable state | function | - | 4.14.0 |
| triggerType | Edit mode trigger - icon, text or both (not specifying icon as trigger hides it) | Array&lt;`icon`\|`text`> | \[`icon`] | |
| enterIcon | Custom "enter" icon in the edit field (passing `null` removes the icon) | ReactNode | `<EnterOutlined />` | 4.17.0 |
| tabIndex | Set tabIndex of the edit button | number | 0 | 5.17.0 |
### ellipsis

View File

@ -93,6 +93,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LT2jR41Uj2EAAA
icon: ReactNode,
tooltips: false | [ReactNode, ReactNode],
format: 'text/plain' | 'text/html',
tabIndex: number,
}
| 参数 | 说明 | 类型 | 默认值 | 版本 |
@ -102,6 +103,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LT2jR41Uj2EAAA
| text | 拷贝到剪切板里的文本 | string | - | |
| tooltips | 自定义提示文案,为 false 时隐藏文案 | \[ReactNode, ReactNode] | \[`复制`, `复制成功`] | 4.4.0 |
| onCopy | 拷贝成功的回调函数 | function | - | |
| tabIndex | 自定义复制按钮的 tabIndex | number | 0 | 5.17.0 |
### editable
@ -118,6 +120,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LT2jR41Uj2EAAA
onEnd: function,
triggerType: ('icon' | 'text')[],
enterIcon: ReactNode,
tabIndex: number,
}
| 参数 | 说明 | 类型 | 默认值 | 版本 |
@ -134,6 +137,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LT2jR41Uj2EAAA
| onEnd | 按 ENTER 结束编辑状态时触发 | function | - | 4.14.0 |
| triggerType | 编辑模式触发器类型,图标、文本或者两者都设置(不设置图标作为触发器时它会隐藏) | Array&lt;`icon`\|`text`> | \[`icon`] | |
| enterIcon | 在编辑段中自定义“enter”图标传递“null”将删除图标 | ReactNode | `<EnterOutlined />` | 4.17.0 |
| tabIndex | 自定义编辑按钮的 tabIndex | number | 0 | 5.17.0 |
### ellipsis