Skip to content

Feat merge 4.x #593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 7, 2025
2 changes: 1 addition & 1 deletion src/blockHeader/__tests__/blockHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
});
test('should render BlockHeader default BlockHeader render', () => {
const { getByText, rerender } = render(<BlockHeader {...props} />);
expect(getByText(props.title)).toBeTruthy();
expect(getByText(props.title as any)).toBeTruthy();
rerender(<BlockHeader title="标题2" />);
expect(getByText('标题2')).toBeTruthy();
});
test('should render BlockHeader props default in BlockHeader', () => {
const { container } = render(<BlockHeader title="测试" background />);
const wrap = container.firstChild;
expect(wrap!.firstChild!.firstChild!.firstChild).toHaveClass('title__addon-before');

Check warning on line 47 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion

Check warning on line 47 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion

Check warning on line 47 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
fireEvent.click(document.getElementsByClassName(`${prefixCls}__title`)[0]);
});
test('should render BlockHeader test click event', () => {
Expand Down Expand Up @@ -97,11 +97,11 @@
const props = { title: '测试1', background: false };
const { container } = render(<BlockHeader {...props} />);
const wrap = container.firstChild;
expect(wrap!.firstChild).not.toHaveClass(`dtc-block-header__title--background`);

Check warning on line 100 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
});
test('should render BlockHeader className when size is small', () => {
const { container, getByText } = render(<BlockHeader {...props2} />);
const wrap = container.firstChild!;

Check warning on line 104 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
expect(wrap).toHaveClass(`${prefixCls} test__className`);
expect(wrap.firstChild).toHaveClass(
`dtc-block-header__title dtc-block-header__title--small dtc-block-header__title--background`
Expand All @@ -113,14 +113,14 @@

test('should render BlockHeader tooltip success', () => {
const { container } = render(<BlockHeader {...props3} />);
const wrap = container.firstChild!;

Check warning on line 116 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
const tooltipWrap = wrap.firstChild!.firstChild!.lastChild;

Check warning on line 117 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion

Check warning on line 117 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
expect(tooltipWrap!.firstChild).toHaveAttribute('data-mock-icon', 'QuestionOutlined');

Check warning on line 118 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
});

test('should render BlockHeader description success', () => {
const { container } = render(<BlockHeader {...props4} />);
const wrap = container.firstChild!;

Check warning on line 123 in src/blockHeader/__tests__/blockHeader.test.tsx

View workflow job for this annotation

GitHub Actions / setup

Forbidden non-null assertion
const description = wrap.firstChild!.firstChild!.lastChild;
expect(description).toHaveTextContent('说明文字');
});
Expand Down
15 changes: 15 additions & 0 deletions src/blockHeader/demos/title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { BlockHeader, EllipsisText } from 'dt-react-component';

export default () => {
return (
<BlockHeader
title={
<EllipsisText
maxWidth={200}
value="标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长"
/>
}
/>
);
};
5 changes: 3 additions & 2 deletions src/blockHeader/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ demo:
<code src="./demos/addonBefore.tsx" description="通过 `addonBefore` 可以设置标题前的图标,不设置时默认是一个色块,设置为假值(`undefined` 除外)不展示图标">自定义 icon</code>
<code src="./demos/addonAfter.tsx" description="通过 `addonAfter` 可以设置后缀自定义内容块">带提示信息的标题</code>
<code src="./demos/expand.tsx" description="通过配置 expand/defaultExpand 控制展开/收起">支持 `children` 做为内容展示</code>
<code src="./demos/title.tsx" description="title 支持 ReactNode">标题超长</code>

## API

Expand All @@ -38,8 +39,8 @@ demo:
| contentClassName | 展示内容的样式类名 | `string` | - |
| contentStyle | 展示内容的样式 | `React.CSSProperties` | - |
| background | 是否显示背景 | `boolean` | `true` |
| defaultExpand | 是否默认展开内容 | `boolean` | `-` |
| defaultExpand | 是否默认展开内容 | `boolean` | `-` |
| expand | 当前展开状态 | `boolean` | |
| spaceBottom | 自定义下边距,优先级高于 hasBottom | `number` | `16` |
| spaceBottom | 自定义下边距,优先级高于 hasBottom | `number` | `16` |
| children | 展开/收起的内容 | `React.ReactNode` | - |
| onExpand | 展开/收起时的回调 | `(expand: boolean) => void` | - |
8 changes: 6 additions & 2 deletions src/blockHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { QuestionOutlined, UpOutlined } from '@dtinsight/react-icons';
import { Tooltip } from 'antd';
import classNames from 'classnames';

import useLocale from '../locale/useLocale';
import { LabelTooltipType, toTooltipProps } from '../utils';
import './style.scss';

Expand All @@ -14,7 +15,7 @@ function isControlled(props: IBlockHeaderProps) {

export interface IBlockHeaderProps {
/** 标题 */
title: string;
title: ReactNode;
/** 标题前的图标,默认是一个色块 */
addonBefore?: ReactNode;
/** 标题后的提示说明文字 */
Expand Down Expand Up @@ -76,6 +77,7 @@ const BlockHeader: React.FC<IBlockHeaderProps> = function (props) {
} = props;

const [internalExpand, setInternalExpand] = useState(defaultExpand);
const locale = useLocale('BlockHeader');

const currentExpand = isControlled(props) ? expand : internalExpand;

Expand Down Expand Up @@ -124,7 +126,9 @@ const BlockHeader: React.FC<IBlockHeaderProps> = function (props) {
{addonAfter && <div className={`title__addon-after`}>{addonAfter}</div>}
{showCollapse && (
<div className={`title__collapse`}>
<div className="collapse__text">{currentExpand ? '收起' : '展开'}</div>
<div className="collapse__text">
{currentExpand ? locale.collapse : locale.expand}
</div>
<UpOutlined
className={classNames('collapse__icon', {
'collapse__icon--up': currentExpand,
Expand Down
9 changes: 9 additions & 0 deletions src/configProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

import { Locale, LocaleContext } from '../locale/useLocale';

const ConfigProvider = ({ locale, children }: { locale: Locale; children: React.ReactNode }) => {
return <LocaleContext.Provider value={{ locale }}>{children}</LocaleContext.Provider>;
};

export default ConfigProvider;
6 changes: 4 additions & 2 deletions src/copy/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CopyOutlined } from '@dtinsight/react-icons';
import { message, Tooltip } from 'antd';
import classNames from 'classnames';

import useLocale from '../locale/useLocale';
import { LabelTooltipType, toTooltipProps } from '../utils';
import './style.scss';

Expand All @@ -18,14 +19,15 @@ export interface ICopyProps {
}

const Copy: React.FC<ICopyProps> = (props) => {
const locale = useLocale('Copy');
const {
button = <CopyOutlined className="dtc-copy__default-icon" />,
text,
tooltip = '复制',
tooltip = locale.copy,
style,
className,
disabled = false,
onCopy = () => message.success('复制成功'),
onCopy = () => message.success(locale.copied),
} = props;

const handleCopy = () => {
Expand Down
9 changes: 6 additions & 3 deletions src/dropdown/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import classNames from 'classnames';
import { isEqual } from 'lodash-es';
import List from 'rc-virtual-list';

import useLocale from '../locale/useLocale';
import './style.scss';

interface IDropdownSelectProps
Expand All @@ -35,6 +36,8 @@ export default function Select({
const [visible, setVisible] = useState(false);
const [selected, setSelected] = useState<CheckboxValueType[]>(value || defaultValue || []);

const locale = useLocale('Dropdown');

const handleCheckedAll = (e: CheckboxChangeEvent) => {
if (e.target.checked) {
setSelected(options?.map((i) => i.value) || []);
Expand Down Expand Up @@ -130,7 +133,7 @@ export default function Select({
checked={checkAll}
indeterminate={indeterminate}
>
全选
{locale.selectAll}
</Checkbox>
</Col>
<Col span={24} className={`${prefix}__menu`}>
Expand Down Expand Up @@ -169,10 +172,10 @@ export default function Select({
</Row>
<Space size={8} className={`${prefix}__btns`}>
<Button size="small" disabled={resetDisabled} onClick={handleReset}>
重置
{locale.resetText}
</Button>
<Button size="small" type="primary" onClick={handleSubmit}>
确定
{locale.okText}
</Button>
</Space>
</>
Expand Down
9 changes: 8 additions & 1 deletion src/ellipsisText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ const EllipsisText = (props: IEllipsisTextProps) => {
* @return {*}
*/
const getStyle = (dom: NewHTMLElement, attr: string) => {
if (!dom) {
return null;
}
// Compatible width IE8
// @ts-ignore
return window.getComputedStyle(dom)[attr] || dom.currentStyle[attr];
Expand Down Expand Up @@ -203,7 +206,11 @@ const EllipsisText = (props: IEllipsisTextProps) => {
* @return {*}
*/
const onResize = () => {
const ellipsisNode = ellipsisRef.current!;
if (!ellipsisRef.current) {
return;
}

const ellipsisNode = ellipsisRef.current;
const parentElement = ellipsisNode.parentElement!;
const rangeWidth = getRangeWidth(ellipsisNode);
const containerWidth = getContainerWidth(parentElement);
Expand Down
12 changes: 7 additions & 5 deletions src/errorBoundary/loadError.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import React from 'react';

import useLocale from '../locale/useLocale';

const LoadError: React.FC = function () {
const locale = useLocale('LoadError');
return (
<div className="dtc-error" data-testid="test-error">
<div>

<h2 style={{ textAlign: 'center' }} data-testid="test-error">
发现新版本,请
{locale.please}
<a
onClick={() => {
location.reload();
}}
>
刷新
{locale.refresh}
</a>
获取新版本。
{locale.get}
</h2>
<h4 style={{ textAlign: 'center' }}>若该提示长时间存在,请联系管理员。</h4>
<h4 style={{ textAlign: 'center' }}>{locale.title}</h4>
</div>
</div>
);
Expand Down
7 changes: 6 additions & 1 deletion src/fullscreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { CSSProperties, HTMLAttributes, ReactNode, useEffect, useState }
import { Button } from 'antd';

import KeyEventListener from '../keyEventListener';
import useLocale from '../locale/useLocale';
import MyIcon from './icon';

const { KeyCombiner } = KeyEventListener;
Expand Down Expand Up @@ -44,7 +45,11 @@ export default function Fullscreen({
...other
}: IFullscreenProps) {
const [isFullScreen, setIsFullScreen] = useState(false);

const locale = useLocale('Fullscreen');

const customIcon = isFullScreen ? exitFullIcon : fullIcon;

useEffect(() => {
const propsDom = document.getElementById(target);
const domEle = propsDom || document.body;
Expand Down Expand Up @@ -188,7 +193,7 @@ export default function Fullscreen({
) : (
<Button onClick={handleFullScreen}>
<MyIcon style={iconStyle} type={isFullScreen} themeDark={themeDark} />
{isFullScreen ? '退出全屏' : '全屏'}
{isFullScreen ? locale.exitFull : locale.full}
</Button>
)}
</KeyCombiner>
Expand Down
5 changes: 4 additions & 1 deletion src/globalLoading/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import classNames from 'classnames';

import useLocale from '../locale/useLocale';
import './style.scss';

export interface IGlobalLoadingProps {
Expand All @@ -13,8 +14,10 @@ export interface IGlobalLoadingProps {
}

const GlobalLoading: React.FC<IGlobalLoadingProps> = function (props) {
const locale = useLocale('GlobalLoading');

const {
loadingTitle = '应用加载中,请等候~',
loadingTitle = locale.loading,
mainBackground = '#F2F7FA',
circleBackground = '#1D78FF',
titleColor = '#3D446E',
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { default as Button } from './button';
export { default as Catalogue } from './catalogue';
export { default as Chat } from './chat';
export { default as CollapsibleActionItems } from './collapsibleActionItems';
export { default as ConfigProvider } from './configProvider';
export { default as ContentLayout } from './contentLayout';
export { default as ContextMenu } from './contextMenu';
export { default as Copy } from './copy';
Expand All @@ -24,6 +25,8 @@ export { default as GlobalLoading } from './globalLoading';
export { default as Image } from './image';
export { default as Input } from './input';
export { default as KeyEventListener } from './keyEventListener';
export { default as enUS } from './locale/en-US';
export { default as zhCN } from './locale/zh-CN';
export { default as MarkdownRender } from './markdownRender';
export { default as Modal } from './modal/modal';
export { default as NotFound } from './notFound';
Expand Down
45 changes: 25 additions & 20 deletions src/input/match.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Input, type InputProps, Tooltip } from 'antd';
import classNames from 'classnames';

import useLocale from '../locale/useLocale';
import { CaseSensitiveIcon, FrontIcon, PreciseIcon, TailIcon } from './icons';
import './match.scss';

Expand Down Expand Up @@ -29,30 +30,11 @@ interface IMatchProps extends Omit<InputProps, 'suffix'> {
onSearch?: (value: string, searchType: SearchType) => void;
}

const searchTypeList = [
{
key: 'caseSensitive',
tip: '区分大小写匹配',
},
{
key: 'precise',
tip: '精确匹配',
},
{
key: 'front',
tip: '头部匹配',
},
{
key: 'tail',
tip: '尾部匹配',
},
] as const;

export default function Match({
className,
value,
searchType,
filterOptions = searchTypeList.map((i) => i.key),
filterOptions: propFilterOptions,
onTypeChange,
onSearch,
onChange,
Expand All @@ -62,12 +44,35 @@ export default function Match({
const [internalValue, setValue] = useState<string>('');
const [internalSearchType, setSearchType] = useState<SearchType>('fuzzy');

const locale = useLocale('Input');

const handleTypeChange = (key: SearchType) => {
const next = realSearchType === key ? 'fuzzy' : key;
onTypeChange?.(next);
setSearchType(next);
};

const searchTypeList = [
{
key: 'caseSensitive',
tip: locale.case,
},
{
key: 'precise',
tip: locale.precise,
},
{
key: 'front',
tip: locale.front,
},
{
key: 'tail',
tip: locale.tail,
},
] as const;

const filterOptions = propFilterOptions || searchTypeList.map((i) => i.key);

const options = searchTypeList.filter((i) => filterOptions.includes(i.key));

const realSearchType = searchType || internalSearchType;
Expand Down
Loading