From ab29bcc750e212a48ce9adf0d290c74e2b5b3d72 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Fri, 8 Dec 2023 11:49:53 +0800 Subject: [PATCH 1/3] :sparkles: feat: support iconPrefixCls and prefixCls on createStyles --- docs/api/create-styles.en-US.md | 24 +-- docs/api/create-styles.zh-CN.md | 30 ++-- docs/demos/api/createStyles/with-antd-cp.tsx | 32 ++++ package.json | 4 +- src/factories/createStyles/index.ts | 7 +- src/factories/createStyles/types.ts | 2 + .../createThemeProvider/TokenContainer.tsx | 2 +- src/factories/createThemeProvider/index.tsx | 1 + src/factories/createUseTheme.ts | 19 ++- src/functions/createInstance.ts | 2 + src/types/styled.ts | 1 + src/types/theme.ts | 3 +- .../__snapshots__/createStyles.test.tsx.snap | 152 +++++------------- tests/functions/createStyles.test.tsx | 42 ++++- 14 files changed, 171 insertions(+), 150 deletions(-) create mode 100644 docs/demos/api/createStyles/with-antd-cp.tsx diff --git a/docs/api/create-styles.en-US.md b/docs/api/create-styles.en-US.md index 69e3d2c9..978770bf 100644 --- a/docs/api/create-styles.en-US.md +++ b/docs/api/create-styles.en-US.md @@ -192,19 +192,25 @@ Type: `string` The prefix marked on the ThemeProvider. By using this parameter, you can flexibly override the antd prefix. ```ts -const useStyles = createStyles(({ css, prefixCls }) => { - return { - primary: css` - .${prefixCls}-btn { - border: 12px; - } - `, - }; -}); +const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => ({ + button: css` + &.${prefixCls}-btn { + background: lightsteelblue; + border: none; + color: royalblue; + } + + .${iconPrefixCls} { + color: darkblue; + } + `, +})); ``` The above style code can accurately override regardless of the value of the prefixCls wrapped by the outer ThemeProvider. + + ## ClassNameGeneratorOption The second parameter of `createStyles` can be used to control the generated className. diff --git a/docs/api/create-styles.zh-CN.md b/docs/api/create-styles.zh-CN.md index 758b7253..af91fad3 100644 --- a/docs/api/create-styles.zh-CN.md +++ b/docs/api/create-styles.zh-CN.md @@ -185,25 +185,31 @@ declare module 'antd-style' { 判断亮暗色主题的语法糖,实现上等价于 `appearance === 'dark'`,直接使用 isDarkMode 可以降低外观的判断成本。 -### prefixCls +### prefixCls 与 iconPrefixCls 类型:`string` -在 ThemeProvider 上标记的 prefixCls,利用该参数,可以实现灵活的 antd 前缀覆盖。 +在 ThemeProvider 上标记的 prefixCls/iconPrefixCls,利用该参数,可以实现灵活的 antd 前缀覆盖。 ```ts -const useStyles = createStyles(({ css, prefixCls }) => { - return { - primary: css` - .${prefixCls}-btn { - border: 12px; - } - `, - }; -}); +const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => ({ + button: css` + &.${prefixCls}-btn { + background: lightsteelblue; + border: none; + color: royalblue; + } + + .${iconPrefixCls} { + color: darkblue; + } + `, +})); ``` -上述样式代码,无论外层包裹的 ThemeProvider prefixCls 改成什么值,均可准确覆盖到。 +上述样式代码,无论是包裹的 `ThemeProvider` / `ConfigProvider` prefixCls 改成什么值,均可准确覆盖到。 + + ## ClassNameGeneratorOption diff --git a/docs/demos/api/createStyles/with-antd-cp.tsx b/docs/demos/api/createStyles/with-antd-cp.tsx new file mode 100644 index 00000000..5c79d55a --- /dev/null +++ b/docs/demos/api/createStyles/with-antd-cp.tsx @@ -0,0 +1,32 @@ +import { SmileOutlined } from '@ant-design/icons'; +import { Button, ConfigProvider } from 'antd'; +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => ({ + button: css` + &.${prefixCls}-btn { + background: lightsteelblue; + border: none; + color: royalblue; + } + + .${iconPrefixCls} { + color: darkblue; + } + `, +})); + +const App = () => { + const { styles } = useStyles(); + + return ( + + ); +}; +export default () => ( + + + +); diff --git a/package.json b/package.json index 0ebcb9f7..a48159e3 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@types/react-dom": "^18", "@types/testing-library__jest-dom": "^5", "@umijs/lint": "^4", - "@vitest/coverage-v8": "latest", + "@vitest/coverage-v8": "0.34.6", "antd": "^5", "babel-plugin-antd-style": "^1", "chalk": "^4", @@ -133,7 +133,7 @@ "ts-node": "^10", "typescript": "^5", "vite": "^4.5.0", - "vitest": "latest", + "vitest": "0.34.6", "zustand": "^4" }, "peerDependencies": { diff --git a/src/factories/createStyles/index.ts b/src/factories/createStyles/index.ts index baba29ad..bc178f47 100644 --- a/src/factories/createStyles/index.ts +++ b/src/factories/createStyles/index.ts @@ -57,7 +57,7 @@ export const createStylesFactory = // 函数场景 if (styleOrGetStyle instanceof Function) { - const { stylish, appearance, isDarkMode, prefixCls, ...token } = theme; + const { stylish, appearance, isDarkMode, prefixCls, iconPrefixCls, ...token } = theme; // 创建响应式断点选择器的工具函数 // @ts-ignore @@ -73,6 +73,7 @@ export const createStylesFactory = appearance, isDarkMode, prefixCls, + iconPrefixCls, // 工具函数们 cx, css: serializeCSS, @@ -121,8 +122,8 @@ export const createStylesFactory = }, [props, theme]); return useMemo(() => { - const { prefixCls, ...res } = theme; - return { styles, cx, theme: res, prefixCls }; + const { prefixCls, iconPrefixCls, ...res } = theme; + return { styles, cx, theme: res, prefixCls, iconPrefixCls }; }, [styles, theme]); }; }; diff --git a/src/factories/createStyles/types.ts b/src/factories/createStyles/types.ts index 097031ff..1e3d380f 100644 --- a/src/factories/createStyles/types.ts +++ b/src/factories/createStyles/types.ts @@ -31,6 +31,7 @@ export interface CreateStylesUtils extends CommonStyleUtils { * @default ant */ prefixCls: string; + iconPrefixCls: string; } /** @@ -39,6 +40,7 @@ export interface CreateStylesUtils extends CommonStyleUtils { export interface ReturnStyles extends Pick { styles: ReturnStyleToUse; theme: Omit; + iconPrefixCls: string; prefixCls: string; } diff --git a/src/factories/createThemeProvider/TokenContainer.tsx b/src/factories/createThemeProvider/TokenContainer.tsx index 5fec2e92..4f8d329a 100644 --- a/src/factories/createThemeProvider/TokenContainer.tsx +++ b/src/factories/createThemeProvider/TokenContainer.tsx @@ -20,7 +20,7 @@ const TokenContainer: (props: TokenContainerProps) => ReactElement | customToken: customTokenOrFn, defaultCustomToken: defaultCustomTokenFn, customStylish: stylishOrGetStylish, - prefixCls = 'ant', + prefixCls, StyledThemeProvider, }) => { const themeState = useThemeMode(); diff --git a/src/factories/createThemeProvider/index.tsx b/src/factories/createThemeProvider/index.tsx index c5890f3f..fbfced13 100644 --- a/src/factories/createThemeProvider/index.tsx +++ b/src/factories/createThemeProvider/index.tsx @@ -63,6 +63,7 @@ export const createThemeProvider = ( onThemeModeChange, styled, }) => { + // 从上一层的 Context 中获取上下文信息,以实现嵌套继承的效果 const { prefixCls: defaultPrefixCls, StyledThemeContext, diff --git a/src/factories/createUseTheme.ts b/src/factories/createUseTheme.ts index 674ad215..e276bb54 100644 --- a/src/factories/createUseTheme.ts +++ b/src/factories/createUseTheme.ts @@ -4,6 +4,7 @@ import { Context, useContext, useMemo } from 'react'; import { DEFAULT_THEME_CONTEXT } from '@/functions/setupStyled'; import { useAntdTheme } from '@/hooks/useAntdTheme'; import { useThemeMode } from '@/hooks/useThemeMode'; +import { ConfigProvider } from 'antd'; interface CreateUseThemeOptions { StyleEngineContext: Context; @@ -11,7 +12,11 @@ interface CreateUseThemeOptions { export const createUseTheme = (options: CreateUseThemeOptions) => (): Theme => { const { StyleEngineContext } = options; - const { StyledThemeContext, CustomThemeContext, prefixCls } = useContext(StyleEngineContext); + const { + StyledThemeContext, + CustomThemeContext, + prefixCls: outPrefixCls, + } = useContext(StyleEngineContext); const antdTheme = useAntdTheme(); const themeState = useThemeMode(); @@ -19,14 +24,20 @@ export const createUseTheme = (options: CreateUseThemeOptions) => (): Theme => { const defaultCustomTheme = useContext(CustomThemeContext); const styledTheme = useContext(StyledThemeContext ?? DEFAULT_THEME_CONTEXT) || {}; + const { iconPrefixCls, getPrefixCls } = useContext(ConfigProvider.ConfigContext); + + const antdPrefixCls = getPrefixCls(); + const prefixCls = outPrefixCls ?? antdPrefixCls; + const initTheme = useMemo( () => ({ ...antdTheme, ...themeState, ...defaultCustomTheme, - prefixCls: prefixCls || 'ant', + prefixCls, + iconPrefixCls, }), - [antdTheme, themeState, prefixCls, defaultCustomTheme], + [antdTheme, themeState, defaultCustomTheme, prefixCls, iconPrefixCls], ); // 如果是个空值,说明没有套 Provider,返回 antdTheme 的默认值 @@ -34,5 +45,5 @@ export const createUseTheme = (options: CreateUseThemeOptions) => (): Theme => { return initTheme; } - return styledTheme as Theme; + return { ...styledTheme, prefixCls, iconPrefixCls } as Theme; }; diff --git a/src/functions/createInstance.ts b/src/functions/createInstance.ts index 6e991ab3..721ee7f9 100644 --- a/src/functions/createInstance.ts +++ b/src/functions/createInstance.ts @@ -34,6 +34,7 @@ export interface CreateOptions { * 默认的组件 prefixCls */ prefixCls?: string; + iconPrefixCls?: string; /** * 是否开启急速模式 * @@ -87,6 +88,7 @@ export const createInstance = (options: CreateOptions) => { CustomThemeContext, StyledThemeContext: styledThemeContext, prefixCls: internalOptions?.prefixCls, + iconPrefixCls: internalOptions?.iconPrefixCls, }); const useTheme = createUseTheme({ StyleEngineContext }); diff --git a/src/types/styled.ts b/src/types/styled.ts index 54f95021..8081f77b 100644 --- a/src/types/styled.ts +++ b/src/types/styled.ts @@ -35,4 +35,5 @@ export interface StyleEngine { * @description 当前组件的 CSS 类名前缀 */ prefixCls?: string; + iconPrefixCls?: string; } diff --git a/src/types/theme.ts b/src/types/theme.ts index 8b1005bd..79d8b4de 100644 --- a/src/types/theme.ts +++ b/src/types/theme.ts @@ -1,4 +1,4 @@ -import { ThemeConfig, MappingAlgorithm } from 'antd'; +import { MappingAlgorithm, ThemeConfig } from 'antd'; import { AliasToken } from 'antd/es/theme/interface'; import { BrowserPrefers, ThemeAppearance, ThemeMode } from './appearance'; @@ -76,4 +76,5 @@ export interface Theme extends FullToken, ThemeContextState { * antd 组件的 prefixCls */ prefixCls: string; + iconPrefixCls: string; } diff --git a/tests/functions/__snapshots__/createStyles.test.tsx.snap b/tests/functions/__snapshots__/createStyles.test.tsx.snap index 1950ae86..ab536631 100644 --- a/tests/functions/__snapshots__/createStyles.test.tsx.snap +++ b/tests/functions/__snapshots__/createStyles.test.tsx.snap @@ -1,37 +1,47 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 只返回一个 css 字符串,使用正常,类型定义也正常 1`] = ` -.emotion-0 { - background-color: #ffffff; - padding: 24px; +exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 可以获取 prefixCls 与 iconPrefixCls 1`] = ` +.emotion-0.cp-btn { + background: lightsteelblue; + border: none; + color: royalblue; } -
- card -
-`; - -exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 字符串模板的对象模式用法 1`] = ` -.emotion-0 { - background-color: #f5f5f5; - padding: 24px; +.emotion-0 .cpicon { + color: darkblue; } -.emotion-1 { - margin-top: 24px; -} - -
-
- card -
-
+ + + + + + CP Button + + `; exports[`createStyles > styles 对象的使用 > cx 的处理方式 1`] = ` @@ -54,93 +64,3 @@ exports[`createStyles > styles 对象的使用 > cx 的处理方式 1`] = ` `; - -exports[`createStyles > styles 对象的使用 > styleObject 方法 > css模式的写法 1`] = ` -.emotion-0 { - background-color: #f5f5f5; - padding: 24px; -} - -.emotion-1 { - margin-top: 16px; -} - -
-
- card -
-
-`; - -exports[`createStyles > styles 对象的使用 > styleObject 方法 > 只返回一个 css 字符串,使用正常,类型定义也正常 1`] = ` -.emotion-0 { - background-color: #f5f5f5; - padding: 24px; -} - -
- card -
-`; - -exports[`createStyles > styles 对象的使用 > styleObject 方法 > 对象模式的用法 1`] = ` -.emotion-0 { - background-color: #f5f5f5; - padding: 24px; -} - -.emotion-1 { - margin-top: 16px; -} - -
-
- card -
-
-`; - -exports[`createStyles > theme 对象使用 > 默认用法 1`] = ` -.emotion-0 { - color: #1677ff; -} - -
- 123 -
-`; - -exports[`createStyles > 响应主题 > 嵌套主题 1`] = ` -.emotion-0 { - background-color: #1677ff; - color: red; - padding: 24px; -} - -.emotion-1 { - margin-top: 24px; -} - -
-
- card -
-
-`; diff --git a/tests/functions/createStyles.test.tsx b/tests/functions/createStyles.test.tsx index 507058ae..75bd5d79 100644 --- a/tests/functions/createStyles.test.tsx +++ b/tests/functions/createStyles.test.tsx @@ -1,5 +1,7 @@ +import { SmileOutlined } from '@ant-design/icons'; import { render } from '@testing-library/react'; -import { createStyles, css, GetCustomToken, ThemeProvider } from 'antd-style'; +import { Button, ConfigProvider } from 'antd'; +import { GetCustomToken, ThemeProvider, createStyles, css } from 'antd-style'; import { FC, PropsWithChildren } from 'react'; describe('createStyles', () => { @@ -49,7 +51,43 @@ describe('createStyles', () => { const { container } = render(); expect(container.firstChild).toMatchSnapshot(); - expect(container.firstChild).toHaveStyle({ backgroundColor: '#fff' }); + }); + + it('可以获取 prefixCls 与 iconPrefixCls', () => { + const useStyles = createStyles(({ css, prefixCls, iconPrefixCls }) => { + return { + button: css` + &.${prefixCls}-btn { + background: lightsteelblue; + border: none; + color: royalblue; + } + + .${iconPrefixCls} { + color: darkblue; + } + `, + }; + }); + + const App = () => { + const { styles } = useStyles(); + + return ( + + ); + }; + const wrapper = ({ children }: PropsWithChildren) => ( + + {children} + + ); + + const { container } = render(, { wrapper }); + + expect(container.firstChild).toMatchSnapshot(); }); }); From f7c45e2dbc3b5f127839a78862c08e8c8e16a71d Mon Sep 17 00:00:00 2001 From: arvinxx Date: Fri, 8 Dec 2023 12:24:35 +0800 Subject: [PATCH 2/3] :camera_flash: test: update snapshot --- .../__snapshots__/createStyles.test.tsx.snap | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/tests/functions/__snapshots__/createStyles.test.tsx.snap b/tests/functions/__snapshots__/createStyles.test.tsx.snap index ab536631..f633955c 100644 --- a/tests/functions/__snapshots__/createStyles.test.tsx.snap +++ b/tests/functions/__snapshots__/createStyles.test.tsx.snap @@ -1,5 +1,18 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 只返回一个 css 字符串,使用正常,类型定义也正常 1`] = ` +.emotion-0 { + background-color: #ffffff; + padding: 24px; +} + +
+ card +
+`; + exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 可以获取 prefixCls 与 iconPrefixCls 1`] = ` .emotion-0.cp-btn { background: lightsteelblue; @@ -44,6 +57,27 @@ exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方 `; +exports[`createStyles > styles 对象的使用 > createStyleFn 通过函数方式可以拿到 token 等信息 > 字符串模板的对象模式用法 1`] = ` +.emotion-0 { + background-color: #f5f5f5; + padding: 24px; +} + +.emotion-1 { + margin-top: 24px; +} + +
+
+ card +
+
+`; + exports[`createStyles > styles 对象的使用 > cx 的处理方式 1`] = ` .emotion-0 { background-color: #f5f5f5; @@ -64,3 +98,93 @@ exports[`createStyles > styles 对象的使用 > cx 的处理方式 1`] = ` `; + +exports[`createStyles > styles 对象的使用 > styleObject 方法 > css模式的写法 1`] = ` +.emotion-0 { + background-color: #f5f5f5; + padding: 24px; +} + +.emotion-1 { + margin-top: 16px; +} + +
+
+ card +
+
+`; + +exports[`createStyles > styles 对象的使用 > styleObject 方法 > 只返回一个 css 字符串,使用正常,类型定义也正常 1`] = ` +.emotion-0 { + background-color: #f5f5f5; + padding: 24px; +} + +
+ card +
+`; + +exports[`createStyles > styles 对象的使用 > styleObject 方法 > 对象模式的用法 1`] = ` +.emotion-0 { + background-color: #f5f5f5; + padding: 24px; +} + +.emotion-1 { + margin-top: 16px; +} + +
+
+ card +
+
+`; + +exports[`createStyles > theme 对象使用 > 默认用法 1`] = ` +.emotion-0 { + color: #1677ff; +} + +
+ 123 +
+`; + +exports[`createStyles > 响应主题 > 嵌套主题 1`] = ` +.emotion-0 { + background-color: #1677ff; + color: red; + padding: 24px; +} + +.emotion-1 { + margin-top: 24px; +} + +
+
+ card +
+
+`; From a6bbd39fc4b4940c153d3be4ff019d81ee9252dd Mon Sep 17 00:00:00 2001 From: arvinxx Date: Fri, 8 Dec 2023 12:32:43 +0800 Subject: [PATCH 3/3] :white_check_mark: test: fix test --- tests/functions/createStyles.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functions/createStyles.test.tsx b/tests/functions/createStyles.test.tsx index 75bd5d79..9cfcf264 100644 --- a/tests/functions/createStyles.test.tsx +++ b/tests/functions/createStyles.test.tsx @@ -51,6 +51,7 @@ describe('createStyles', () => { const { container } = render(); expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toHaveStyle({ backgroundColor: '#fff' }); }); it('可以获取 prefixCls 与 iconPrefixCls', () => {