Skip to content

feat(configprovider): support configProvider #591

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/catalogue/components/catalogue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Dropdown, DropdownProps, Form, Input, Tabs } from 'antd';
import { BlockHeader, EllipsisText } from 'dt-react-component';
import { IBlockHeaderProps } from 'dt-react-component/blockHeader';

import useLocale from '../../locale/useLocale';
import { ITreeNode } from '../useTreeData';
import { CatalogIcon, CloseIcon, DragIcon, EllipsisIcon, SearchIcon } from './icon';
import CatalogueTree, { ICatalogueTree } from './tree';
Expand Down Expand Up @@ -49,12 +50,13 @@ function isTabMode<U extends Record<string, any> = {}, T extends readOnlyTab = a
const Catalogue = <U extends Record<string, any> = {}, T extends readOnlyTab = any>(
props: CatalogueProps<U, T>
) => {
const locale = useLocale('Catalogue');
const {
title,
addonBefore = <CatalogIcon style={{ fontSize: 20 }} />,
tooltip = false,
showSearch = false,
placeholder = '搜索目录名称',
placeholder = locale.searchPlaceholder,
addonAfter,
edit = true,
treeData,
Expand All @@ -78,7 +80,7 @@ const Catalogue = <U extends Record<string, any> = {}, T extends readOnlyTab = a
<Form.Item name="catalog_input" initialValue={item?.title as string}>
<Input
size="small"
placeholder={`请输入${title}名称`}
placeholder={locale.inputPlaceholder}
maxLength={100}
autoFocus
onFocus={() => form.setFields([{ name: 'catalog_input', errors: [] }])}
Expand Down
58 changes: 58 additions & 0 deletions src/configProvider/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import enUS from '../../locale/en-US';
import { LocaleContext } from '../../locale/useLocale';
import zhCN from '../../locale/zh-CN';
import ConfigProvider from '..';

describe('ConfigProvider', () => {
it('renders children correctly', () => {
const { getByText } = render(
<ConfigProvider locale={zhCN}>
<div>Test Content</div>
</ConfigProvider>
);
expect(getByText('Test Content')).toBeInTheDocument();
});

it('provides locale context correctly', () => {
const TestComponent = () => {
const { locale } = React.useContext(LocaleContext);
return <div data-testid="locale-value">{locale.locale}</div>;
};

const { getByTestId } = render(
<ConfigProvider locale={zhCN}>
<TestComponent />
</ConfigProvider>
);

expect(getByTestId('locale-value').textContent).toBe(zhCN.locale);
});

it('updates context when locale changes', () => {
const TestComponent = () => {
const { locale } = React.useContext(LocaleContext);
return <div data-testid="locale-value">{locale.locale}</div>;
};

const { getByTestId, rerender } = render(
<ConfigProvider locale={zhCN}>
<TestComponent />
</ConfigProvider>
);

expect(getByTestId('locale-value').textContent).toBe(zhCN.locale);

// 重新渲染,使用英文语言包
rerender(
<ConfigProvider locale={enUS}>
<TestComponent />
</ConfigProvider>
);

expect(getByTestId('locale-value').textContent).toBe(enUS.locale);
});
});
38 changes: 38 additions & 0 deletions src/configProvider/demos/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useState } from 'react';
import { Radio, Space } from 'antd';
import { BlockHeader, ConfigProvider, Copy, enUS, Input, zhCN } from 'dt-react-component';

export default function Basic() {
const [locale, setLocale] = useState(zhCN);

return (
<Space direction="vertical" size="large">
<Radio.Group
value={locale.locale}
onChange={(e) => setLocale(e.target.value === 'zh-CN' ? zhCN : enUS)}
>
<Radio.Button value="zh-CN">中文</Radio.Button>
<Radio.Button value="en-US">English</Radio.Button>
</Radio.Group>

<ConfigProvider locale={locale}>
<Space direction="vertical" size="middle">
<div>
<h3>Copy 组件</h3>
<Copy text="这是要复制的文本" />
</div>
<div>
<h3>BlockHeader 组件</h3>
<BlockHeader title="标题" defaultExpand>
<p>内容区域</p>
</BlockHeader>
</div>
<div>
<h3>Input.Match 组件</h3>
<Input.Match />
</div>
</Space>
</ConfigProvider>
</Space>
);
}
101 changes: 101 additions & 0 deletions src/configProvider/demos/custom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useState } from 'react';
import { Radio, Space } from 'antd';
import { BlockHeader, ConfigProvider, Copy, enUS, useLocale, zhCN } from 'dt-react-component';

// 自定义组件,使用useLocale获取当前语言环境
const LocaleConsumer = () => {
const copyLocale = useLocale('Copy');
const blockHeaderLocale = useLocale('BlockHeader');

return (
<div>
<h4>当前语言环境</h4>
<div style={{ background: '#f5f5f5', padding: '10px', borderRadius: '4px' }}>
<p>
<strong>Copy组件:</strong>
</p>
<ul>
<li>copy: {copyLocale.copy}</li>
<li>copied: {copyLocale.copied}</li>
</ul>

<p>
<strong>BlockHeader组件:</strong>
</p>
<ul>
<li>expand: {blockHeaderLocale.expand}</li>
<li>collapse: {blockHeaderLocale.collapse}</li>
</ul>
</div>
</div>
);
};

export default function Custom() {
// 创建自定义语言包
const [customLocale, setCustomLocale] = useState({
...zhCN,
locale: 'custom-zh',
Copy: {
copy: '点击复制',
copied: '已复制到剪贴板',
},
BlockHeader: {
expand: '展开全部',
collapse: '收起全部',
},
});

const [localeType, setLocaleType] = useState('custom');

const handleLocaleChange = (type: string) => {
setLocaleType(type);
if (type === 'zh-CN') {
setCustomLocale(zhCN);
} else if (type === 'en-US') {
setCustomLocale(enUS);
} else {
// 恢复自定义语言包
setCustomLocale({
...zhCN,
locale: 'custom-zh',
Copy: {
copy: '点击复制',
copied: '已复制到剪贴板',
},
BlockHeader: {
expand: '展开全部',
collapse: '收起全部',
},
});
}
};

return (
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Radio.Group value={localeType} onChange={(e) => handleLocaleChange(e.target.value)}>
<Radio.Button value="custom">自定义语言</Radio.Button>
<Radio.Button value="zh-CN">默认中文</Radio.Button>
<Radio.Button value="en-US">默认英文</Radio.Button>
</Radio.Group>

<ConfigProvider locale={customLocale}>
<Space direction="vertical" style={{ width: '100%' }}>
<div>
<h3>使用自定义语言包</h3>
<Copy text="这是要复制的文本" />
</div>

<div>
<h3>BlockHeader 示例</h3>
<BlockHeader title="标题" defaultExpand>
<p>内容区域</p>
</BlockHeader>
</div>

<LocaleConsumer />
</Space>
</ConfigProvider>
</Space>
);
}
86 changes: 86 additions & 0 deletions src/configProvider/demos/nested.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useState } from 'react';
import { Divider, Radio, Space } from 'antd';
import { ConfigProvider, Copy, enUS, useLocale, zhCN } from 'dt-react-component';

// 自定义组件,使用useLocale获取当前语言环境
const LocaleDisplay = () => {
const copyLocale = useLocale('Copy');
return (
<div>
<p>当前Copy组件的文案:</p>
<ul>
<li>copy: {copyLocale.copy}</li>
<li>copied: {copyLocale.copied}</li>
</ul>
</div>
);
};

export default function Nested() {
const [outerLocale, setOuterLocale] = useState(zhCN);
const [innerLocale, setInnerLocale] = useState(enUS);

return (
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<div>
<h3>外层ConfigProvider</h3>
<Radio.Group
value={outerLocale.locale}
onChange={(e) => setOuterLocale(e.target.value === 'zh-CN' ? zhCN : enUS)}
>
<Radio.Button value="zh-CN">中文</Radio.Button>
<Radio.Button value="en-US">English</Radio.Button>
</Radio.Group>
</div>

<ConfigProvider locale={outerLocale}>
<Space
direction="vertical"
style={{ width: '100%', border: '1px solid #eee', padding: '16px' }}
>
<div>
<h4>外层ConfigProvider的组件</h4>
<Copy text="这是要复制的文本" />
<LocaleDisplay />
</div>

<Divider>嵌套的ConfigProvider</Divider>

<div>
<h4>内层ConfigProvider</h4>
<Radio.Group
value={innerLocale.locale}
onChange={(e) =>
setInnerLocale(e.target.value === 'zh-CN' ? zhCN : enUS)
}
>
<Radio.Button value="zh-CN">中文</Radio.Button>
<Radio.Button value="en-US">English</Radio.Button>
</Radio.Group>

<ConfigProvider locale={innerLocale}>
<div
style={{
marginTop: '16px',
border: '1px dashed #ccc',
padding: '16px',
}}
>
<h4>内层ConfigProvider的组件</h4>
<Copy text="这是要复制的文本" />
<LocaleDisplay />
</div>
</ConfigProvider>
</div>
</Space>
</ConfigProvider>

<div>
<h3>无ConfigProvider的组件</h3>
<p>当组件不在ConfigProvider内时,将使用默认的中文语言包</p>
<Copy text="这是要复制的文本" />
<LocaleDisplay />
</div>
</Space>
);
}
61 changes: 61 additions & 0 deletions src/configProvider/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: ConfigProvider 全局化配置
group: 组件
toc: content
demo:
cols: 1
---

# ConfigProvider 全局化配置

## 何时使用

ConfigProvider 用于全局配置组件库的默认属性,目前主要用于配置国际化文案。

## 代码演示

<code src="./demos/basic.tsx" title="基本使用" description="ConfigProvider 的基本用法,通过切换语言环境,可以看到组件的文案会随之变化。"></code>

<code src="./demos/nested.tsx" title="嵌套使用" description="ConfigProvider 支持嵌套使用,内层的 ConfigProvider 会覆盖外层的配置。"></code>

<code src="./demos/custom.tsx" title="自定义语言包" description="可以通过扩展默认语言包来创建自定义的语言配置。"></code>

## API

### ConfigProvider

| 参数 | 说明 | 类型 | 默认值 |
| -------- | ---------- | ----------------- | ------ |
| locale | 语言包配置 | [Locale](#locale) | - |
| children | 子组件 | ReactNode | - |

### Locale

```typescript
interface Locale {
locale: string;
BlockHeader: { expand: string; collapse: string };
Catalogue: { searchPlaceholder: string; inputPlaceholder: string };
Chat: {
stopped: string;
stop: string;
regenerate: string;
};
Copy: { copied: string; copy: string };
Dropdown: { selectAll: string; resetText: string; okText: string };
ErrorBoundary: {
please: string;
get: string;
refresh: string;
title: string;
};
// ... 其他组件的文案配置
}
```

## 注意事项

- 组件库默认使用中文(zh-CN)语言包。
- 当组件不在 ConfigProvider 内时,将使用默认的中文语言包。
- 可以通过 `useLocale` hook 在组件内部获取当前的语言环境。
- 自定义语言包时,可以只覆盖需要修改的部分,其他部分会使用默认语言包。
2 changes: 1 addition & 1 deletion src/errorBoundary/loadError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import useLocale from '../locale/useLocale';

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