diff --git a/.dumirc.ts b/.dumirc.ts
index 030f258d0..7615538a9 100644
--- a/.dumirc.ts
+++ b/.dumirc.ts
@@ -138,6 +138,7 @@ export default defineConfig({
{ title: 'Form 表单', link: '/components/form' },
{ title: 'Input 输入框', link: '/components/input' },
{ title: 'InputNumber 数字输入框', link: '/components/input-number' },
+ { title: 'Radio 单选框', link: '/components/radio' },
{ title: 'Select 选择器', link: '/components/select' },
{ title: 'TreeSelect 树选择', link: '/components/tree-select' },
],
diff --git a/docs/design/design-CHANGELOG.md b/docs/design/design-CHANGELOG.md
index d5108634f..a7a0a795e 100644
--- a/docs/design/design-CHANGELOG.md
+++ b/docs/design/design-CHANGELOG.md
@@ -8,6 +8,17 @@ group: 基础组件
---
+## 0.3.3
+
+`2024-04-25`
+
+- ConfigProvider
+ - 🐞 修复 ConfigProvider 开启 `theme.customFont` 并且多次嵌套后 `fontFamily` 不正确的问题。[#572](https://github.com/oceanbase/oceanbase-design/pull/572)
+ - 🐞 修复 ConfigProvider 自定义 `theme.token.fontFamily` 不生效的问题。[#573](https://github.com/oceanbase/oceanbase-design/pull/573)
+ - 🐞 修复 ConfigProvider 多次使用会默认多次注入 StaticFunction,导致 Modal、message 和 notification 静态方法不会正常展示的问题。[#574](https://github.com/oceanbase/oceanbase-design/pull/574)
+- 🐞 修复主题 Token `boxShadowSecondary` 通过静态 token 对象和 less 变量访问时值不正确的问题。[#569](https://github.com/oceanbase/oceanbase-design/pull/569)
+- 💄 优化 Radio.Button 选中置灰态的背景颜色,避免和字体颜色区分不清。[#570](https://github.com/oceanbase/oceanbase-design/pull/570)
+
## 0.3.2
`2024-04-12`
diff --git a/docs/ui/ui-CHANGELOG.md b/docs/ui/ui-CHANGELOG.md
index d1c495779..863d58c71 100644
--- a/docs/ui/ui-CHANGELOG.md
+++ b/docs/ui/ui-CHANGELOG.md
@@ -8,6 +8,12 @@ group: 业务组件
---
+## 0.3.3
+
+`2024-04-25`
+
+- ⭐️ Boundary 组件支持 `className` 属性,并且根据不同组件内置 ob-boundary-error、ob-boundary-403 和 ob-boundary-404 类名,便于上层判断异常类型。[#571](https://github.com/oceanbase/oceanbase-design/pull/571)
+
## 0.3.2
`2024-04-12`
diff --git a/packages/codemod/package.json b/packages/codemod/package.json
index 606928268..8cb29ed59 100644
--- a/packages/codemod/package.json
+++ b/packages/codemod/package.json
@@ -1,6 +1,6 @@
{
"name": "@oceanbase/codemod",
- "version": "0.3.1",
+ "version": "0.3.2",
"description": "Codemod for OceanBase Design upgrade",
"keywords": [
"oceanbase",
diff --git a/packages/design/package.json b/packages/design/package.json
index 07ae57aef..90172f002 100644
--- a/packages/design/package.json
+++ b/packages/design/package.json
@@ -1,6 +1,6 @@
{
"name": "@oceanbase/design",
- "version": "0.3.2",
+ "version": "0.3.3",
"description": "The Design System of OceanBase",
"keywords": [
"oceanbase",
diff --git a/packages/design/src/config-provider/__tests__/injectStaticFunction.test.tsx b/packages/design/src/config-provider/__tests__/injectStaticFunction.test.tsx
new file mode 100644
index 000000000..4fd54cde6
--- /dev/null
+++ b/packages/design/src/config-provider/__tests__/injectStaticFunction.test.tsx
@@ -0,0 +1,18 @@
+import React, { useContext } from 'react';
+import { render } from '@testing-library/react';
+import { ConfigProvider, useToken } from '@oceanbase/design';
+import { injectedStaticFunction } from '../../static-function';
+
+describe('ConfigProvider injectStaticFunction', () => {
+ it('injectStaticFunction', () => {
+ const Child = () => {
+ expect(injectedStaticFunction).toBe(true);
+ return
;
+ };
+ render(
+
+
+
+ );
+ });
+});
diff --git a/packages/design/src/config-provider/__tests__/static-function.test.tsx b/packages/design/src/config-provider/__tests__/static-function.test.tsx
index 27a9f1365..a3ca9c85f 100644
--- a/packages/design/src/config-provider/__tests__/static-function.test.tsx
+++ b/packages/design/src/config-provider/__tests__/static-function.test.tsx
@@ -4,7 +4,12 @@ import { ConfigProvider, token } from '@oceanbase/design';
import defaultTheme from '../../theme/default';
describe('ConfigProvider static function', () => {
- it('token', () => {
+ it('static token', () => {
+ expect(token.boxShadow).toBe(defaultTheme.token.boxShadow);
+ expect(token.boxShadowSecondary).toBe(defaultTheme.token.boxShadowSecondary);
+ });
+
+ it('static token in ConfigProvider', () => {
render(
diff --git a/packages/design/src/config-provider/__tests__/theme.test.tsx b/packages/design/src/config-provider/__tests__/theme.test.tsx
index 021cb6b1f..2e9068bfb 100644
--- a/packages/design/src/config-provider/__tests__/theme.test.tsx
+++ b/packages/design/src/config-provider/__tests__/theme.test.tsx
@@ -1,10 +1,12 @@
import React, { useContext } from 'react';
import { render } from '@testing-library/react';
-import { ConfigProvider, useToken } from '@oceanbase/design';
+import { ConfigProvider, useToken, theme } from '@oceanbase/design';
import defaultTheme from '../../theme/default';
+const antToken = theme.getDesignToken();
+
describe('ConfigProvider theme', () => {
- it('ConfigProvider theme token', () => {
+ it('token', () => {
const Child1 = () => {
const { token } = useToken();
expect(token.colorBgLayout).toBe(defaultTheme.token.colorBgLayout);
@@ -38,4 +40,73 @@ describe('ConfigProvider theme', () => {
);
});
+
+ // test order should before customFont to avoid be affected
+ it('token.fontFamily', () => {
+ const Child1 = () => {
+ const { token } = useToken();
+ expect(token.fontFamily).toBe(antToken.fontFamily);
+ return ;
+ };
+ const Child2 = () => {
+ const { token } = useToken();
+ expect(token.fontFamily).toBe(`'Custom Font'`);
+ return ;
+ };
+ const Child3 = () => {
+ const { token } = useToken();
+ expect(token.fontFamily).toBe(`'Custom Font'`);
+ return ;
+ };
+ render(
+
+
+
+
+
+
+
+
+
+ );
+ });
+
+ it('customFont', () => {
+ const Child1 = () => {
+ const { token } = useToken();
+ expect(token.fontFamily).toBe(antToken.fontFamily);
+ return ;
+ };
+ const Child2 = () => {
+ const { token } = useToken();
+ expect(token.fontFamily).toBe(`'Source Sans Pro', ${antToken.fontFamily}`);
+ return ;
+ };
+ const Child3 = () => {
+ const { token } = useToken();
+ expect(token.fontFamily).toBe(`'Source Sans Pro', ${antToken.fontFamily}`);
+ return ;
+ };
+ render(
+
+
+
+
+
+
+
+
+
+ );
+ });
});
diff --git a/packages/design/src/config-provider/index.tsx b/packages/design/src/config-provider/index.tsx
index ab9bcc567..a6efa8925 100644
--- a/packages/design/src/config-provider/index.tsx
+++ b/packages/design/src/config-provider/index.tsx
@@ -13,7 +13,7 @@ import type { StyleProviderProps } from '@ant-design/cssinjs';
import StyleContext from '@ant-design/cssinjs/es/StyleContext';
import type { StyleContextProps } from '@ant-design/cssinjs/es/StyleContext';
import { merge } from 'lodash';
-import StaticFunction from '../static-function';
+import StaticFunction, { injectedStaticFunction } from '../static-function';
import themeConfig from '../theme';
import defaultTheme from '../theme/default';
import darkTheme from '../theme/dark';
@@ -97,7 +97,7 @@ const ConfigProvider: ConfigProviderType = ({
spin,
table,
tabs,
- injectStaticFunction = true,
+ injectStaticFunction = !injectedStaticFunction,
styleProviderProps,
...restProps
}) => {
@@ -108,6 +108,8 @@ const ConfigProvider: ConfigProviderType = ({
const mergedTheme = merge(parentContext.theme, theme);
const currentTheme = mergedTheme?.isDark ? darkTheme : defaultTheme;
const { token } = themeConfig.useToken();
+ const fontFamily = mergedTheme.token?.fontFamily || token.fontFamily;
+ const customFont = mergedTheme.customFont;
// inherit from parent StyleProvider
const parentStyleContext = React.useContext(StyleContext);
@@ -134,9 +136,10 @@ const ConfigProvider: ConfigProviderType = ({
)}
theme={merge(currentTheme, mergedTheme, {
token: {
- fontFamily: mergedTheme.customFont
- ? `'Source Sans Pro', ${token.fontFamily}`
- : token.fontFamily,
+ fontFamily:
+ customFont && !fontFamily.startsWith(`'Source Sans Pro'`)
+ ? `'Source Sans Pro', ${fontFamily}`
+ : fontFamily,
},
})}
renderEmpty={
diff --git a/packages/design/src/radio/demo/radio-button.tsx b/packages/design/src/radio/demo/radio-button.tsx
new file mode 100644
index 000000000..e126e579e
--- /dev/null
+++ b/packages/design/src/radio/demo/radio-button.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { Radio, Space } from '@oceanbase/design';
+import type { RadioChangeEvent } from '@oceanbase/design';
+
+const App: React.FC = () => {
+ const onChange = (e: RadioChangeEvent) => {
+ console.log(`radio checked:${e.target.value}`);
+ };
+
+ return (
+
+
+ Hangzhou
+ Shanghai
+ Beijing
+ Chengdu
+
+
+ Hangzhou
+
+ Shanghai
+
+ Beijing
+ Chengdu
+
+
+ Hangzhou
+ Shanghai
+ Beijing
+ Chengdu
+
+
+ );
+};
+
+export default App;
diff --git a/packages/design/src/radio/demo/radio.tsx b/packages/design/src/radio/demo/radio.tsx
new file mode 100644
index 000000000..4b9ede0e4
--- /dev/null
+++ b/packages/design/src/radio/demo/radio.tsx
@@ -0,0 +1,31 @@
+import React, { useState } from 'react';
+import { Radio, Space } from '@oceanbase/design';
+import type { RadioChangeEvent } from '@oceanbase/design';
+
+const App: React.FC = () => {
+ const [value, setValue] = useState('A');
+
+ const onChange = (e: RadioChangeEvent) => {
+ console.log('radio checked', e.target.value);
+ setValue(e.target.value);
+ };
+
+ return (
+
+
+ A
+ B
+ C
+ D
+
+
+ A
+ B
+ C
+ D
+
+
+ );
+};
+
+export default App;
diff --git a/packages/design/src/radio/index.md b/packages/design/src/radio/index.md
new file mode 100644
index 000000000..25e435405
--- /dev/null
+++ b/packages/design/src/radio/index.md
@@ -0,0 +1,21 @@
+---
+title: Radio 单选框
+nav:
+ title: 基础组件
+ path: /components
+demo:
+ cols: 2
+---
+
+- 🔥 完全继承 antd [Radio](https://ant.design/components/radio-cn) 的能力和 API,可无缝切换。
+- 💄 定制主题和样式,符合 OceanBase Design 设计规范。
+
+## 代码演示
+
+
+
+
+
+## API
+
+- 详见 antd Radio 文档: https://ant.design/components/radio-cn
diff --git a/packages/design/src/static-function/index.tsx b/packages/design/src/static-function/index.tsx
index 056745c84..4ed880366 100644
--- a/packages/design/src/static-function/index.tsx
+++ b/packages/design/src/static-function/index.tsx
@@ -21,7 +21,7 @@ const mapToken = {
// 需要覆盖部分 Alias Token 的值
override: {
boxShadow: defaultTheme.token.boxShadow,
- boxShadowSecondary: defaultTheme.token.boxShadow,
+ boxShadowSecondary: defaultTheme.token.boxShadowSecondary,
},
};
let token = formatToken(mapToken);
@@ -36,6 +36,9 @@ let modal: Omit & {
useModal: typeof AntModal.useModal;
} = AntModal;
+// injected static function or not
+let injectedStaticFunction = false;
+
export default () => {
// 自动注入 useToken,避免每次使用都要声明一遍,比较繁琐
token = useToken().token;
@@ -54,7 +57,8 @@ export default () => {
...staticFunction.modal,
useModal: AntModal.useModal,
};
+ injectedStaticFunction = true;
return null;
};
-export { token, message, notification, modal };
+export { token, message, notification, modal, injectedStaticFunction };
diff --git a/packages/design/src/theme/default.ts b/packages/design/src/theme/default.ts
index 14a299798..297d4dfe1 100644
--- a/packages/design/src/theme/default.ts
+++ b/packages/design/src/theme/default.ts
@@ -84,6 +84,10 @@ const defaultTheme: ThemeConfig = {
InputNumber: {
handleVisible: true,
},
+ Radio: {
+ // temporarily fix style for checked disabled Radio.Button
+ controlItemBgActiveDisabled: '#e2e8f3',
+ },
Select: {
// work for all multiple select component, including Select, TreeSelect and Cascader and so on
multipleItemBg: '#F8FAFE',
diff --git a/packages/design/src/theme/style/compact.less b/packages/design/src/theme/style/compact.less
index b7e45a357..9c258129f 100644
--- a/packages/design/src/theme/style/compact.less
+++ b/packages/design/src/theme/style/compact.less
@@ -409,7 +409,7 @@
@borderRadiusSM: 4;
@borderRadiusLG: 8;
@borderRadiusOuter: 4;
-@boxShadowSecondary: 0 1px 2px 0 rgba(54, 69, 99, 0.03), 0 1px 6px -1px rgba(54, 69, 99, 0.02), 0 2px 4px 0 rgba(54, 69, 99, 0.02);
+@boxShadowSecondary: 0 6px 16px 0 rgba(54, 69, 99, 0.08), 0 3px 6px -4px rgba(54, 69, 99, 0.12), 0 9px 28px 8px rgba(54, 69, 99, 0.05);
@boxShadow: 0 1px 2px 0 rgba(54, 69, 99, 0.03), 0 1px 6px -1px rgba(54, 69, 99, 0.02), 0 2px 4px 0 rgba(54, 69, 99, 0.02);
@colorFillContent: #e2e8f3;
@colorFillContentHover: #cdd5e4;
diff --git a/packages/design/src/theme/style/default.less b/packages/design/src/theme/style/default.less
index dcf5a5d0e..25f2e7c06 100644
--- a/packages/design/src/theme/style/default.less
+++ b/packages/design/src/theme/style/default.less
@@ -409,7 +409,7 @@
@borderRadiusSM: 4;
@borderRadiusLG: 8;
@borderRadiusOuter: 4;
-@boxShadowSecondary: 0 1px 2px 0 rgba(54, 69, 99, 0.03), 0 1px 6px -1px rgba(54, 69, 99, 0.02), 0 2px 4px 0 rgba(54, 69, 99, 0.02);
+@boxShadowSecondary: 0 6px 16px 0 rgba(54, 69, 99, 0.08), 0 3px 6px -4px rgba(54, 69, 99, 0.12), 0 9px 28px 8px rgba(54, 69, 99, 0.05);
@boxShadow: 0 1px 2px 0 rgba(54, 69, 99, 0.03), 0 1px 6px -1px rgba(54, 69, 99, 0.02), 0 2px 4px 0 rgba(54, 69, 99, 0.02);
@colorFillContent: #e2e8f3;
@colorFillContentHover: #cdd5e4;
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 612ca85cb..40be205be 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@oceanbase/ui",
- "version": "0.3.2",
+ "version": "0.3.3",
"description": "The UI library based on OceanBase Design",
"keywords": [
"oceanbase",
diff --git a/packages/ui/src/Boundary/Components/Code.tsx b/packages/ui/src/Boundary/Components/Code.tsx
index 836bbf82b..77370fcc6 100644
--- a/packages/ui/src/Boundary/Components/Code.tsx
+++ b/packages/ui/src/Boundary/Components/Code.tsx
@@ -6,8 +6,9 @@ import type { CodeType } from '../constant';
import { CODE_PRESET } from '../constant';
import type { BoundaryLocale } from '../IBoundary';
import zhCN from '../locale/zh-CN';
+import classNames from 'classnames';
-export interface IBoundaryCode extends LocaleWrapperProps {
+export interface IBoundaryCode extends LocaleWrapperProps, React.HTMLProps {
code: CodeType;
onClick?: () => void;
children?: React.ReactNode;
@@ -18,7 +19,8 @@ export interface IBoundaryCode extends LocaleWrapperProps {
}
const BoundaryCode: React.FC = props => {
- const { children, onClick, code, imageUrl, title, buttonText, locale } = props;
+ const { children, onClick, code, imageUrl, title, buttonText, locale, className, ...restProps } =
+ props;
const info = useMemo(() => {
const data = CODE_PRESET(locale);
@@ -27,7 +29,15 @@ const BoundaryCode: React.FC = props => {
}, [code, locale]);
return (
-
+
{title || info.title}
diff --git a/packages/ui/src/Boundary/Components/Exception.tsx b/packages/ui/src/Boundary/Components/Exception.tsx
index cd42a7422..4ca210562 100644
--- a/packages/ui/src/Boundary/Components/Exception.tsx
+++ b/packages/ui/src/Boundary/Components/Exception.tsx
@@ -5,8 +5,9 @@ import LocaleWrapper from '../../locale/LocaleWrapper';
import { EXCEPTION_PRESET } from '../constant';
import type { BoundaryLocale } from '../IBoundary';
import zhCN from '../locale/zh-CN';
+import classNames from 'classnames';
-export interface ExceptionProps extends LocaleWrapperProps {
+export interface ExceptionProps extends LocaleWrapperProps, React.HTMLProps
{
children?: React.ReactNode;
style?: React.CSSProperties;
imageUrl?: string;
@@ -62,6 +63,8 @@ class BoundaryException extends React.PureComponent
+
{title || errorInfo.title}
@@ -105,7 +111,14 @@ class BoundaryException extends React.PureComponent
+
{title || notCompatibleInfo.title}
diff --git a/packages/ui/src/Boundary/Components/Function.tsx b/packages/ui/src/Boundary/Components/Function.tsx
index 4025a8930..881fbbdc1 100644
--- a/packages/ui/src/Boundary/Components/Function.tsx
+++ b/packages/ui/src/Boundary/Components/Function.tsx
@@ -1,8 +1,9 @@
import { Button } from '@oceanbase/design';
import React, { useMemo } from 'react';
import type { FunctionConfigType, FunctionStateType } from '../constant';
+import classNames from 'classnames';
-export interface IBoundaryFunction {
+export interface IBoundaryFunction extends React.HTMLProps
{
children?: React.ReactNode;
state: FunctionStateType;
config: FunctionConfigType;
@@ -10,13 +11,16 @@ export interface IBoundaryFunction {
}
export const BoundaryFunction: React.FC = props => {
- const { children, state, config, onClick } = props;
+ const { children, state, config, onClick, className, ...restProps } = props;
const info = useMemo(() => {
return state ? config[state] : config[Object.keys(config)[0]];
}, [config, state]);
return (
-
+
{info.title}
diff --git a/plugin-theme-less.ts b/plugin-theme-less.ts
index 9a173bc72..b3ae14431 100644
--- a/plugin-theme-less.ts
+++ b/plugin-theme-less.ts
@@ -59,7 +59,7 @@ export default (api: IApi) => {
: // 对于非暗色主题,需要覆盖部分 Alias Token 的值
{
boxShadow: item.token.boxShadow,
- boxShadowSecondary: item.token.boxShadow,
+ boxShadowSecondary: item.token.boxShadowSecondary,
},
};
const aliasToken = formatToken(mapToken);
diff --git a/tests/setupTests.ts b/tests/setupTests.ts
index f6eab975f..50fab256b 100644
--- a/tests/setupTests.ts
+++ b/tests/setupTests.ts
@@ -16,14 +16,6 @@ global.React = React;
ReactDOM.createPortal = vi.fn(modal => modal);
-vi.mock('react', async () => {
- const mockReact = await vi.importActual('react');
- return {
- ...mockReact,
- useLayoutEffect: mockReact.useEffect,
- };
-});
-
excludeAllWarning();
const fetchMocker = createFetchMock(vi);