From 6848b23169b3e1b39038b13963479d9eccbe1b18 Mon Sep 17 00:00:00 2001 From: MadCcc <1075746765@qq.com> Date: Fri, 18 Nov 2022 09:55:42 +0800 Subject: [PATCH] fix: global icon style (#38617) * fix: global icon style * chore: code clean * feat: icon style in CP * chore: code clean * chore: code clean * chore: support ss * feat: wrapSSR only if iconPrefixCls does not equal parent * chore: code clean * chore: fix lint * chore: fix lint * chore: test * chore: wrap children * chore: code clean * chore: fix lint --- .../__tests__/cssinjs.test.tsx | 25 ++++++++++++++++--- components/config-provider/index.tsx | 17 ++++++++----- components/config-provider/style/index.tsx | 18 +++++++++++++ components/message/__tests__/config.test.tsx | 2 +- 4 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 components/config-provider/style/index.tsx diff --git a/components/config-provider/__tests__/cssinjs.test.tsx b/components/config-provider/__tests__/cssinjs.test.tsx index 3cb09be651..38c272f51f 100644 --- a/components/config-provider/__tests__/cssinjs.test.tsx +++ b/components/config-provider/__tests__/cssinjs.test.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { SmileOutlined } from '@ant-design/icons'; import ConfigProvider from '..'; import Button from '../../button'; import Divider from '../../divider'; @@ -6,7 +7,7 @@ import { render } from '../../../tests/utils'; describe('ConfigProvider.DynamicTheme', () => { beforeEach(() => { - Array.from(document.querySelectorAll('style')).forEach(style => { + Array.from(document.querySelectorAll('style')).forEach((style) => { style.parentNode?.removeChild(style); }); }); @@ -27,7 +28,7 @@ describe('ConfigProvider.DynamicTheme', () => { const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]')); expect( - dynamicStyles.some(style => { + dynamicStyles.some((style) => { const { innerHTML } = style; return ( innerHTML.includes('.ant-btn-primary') && innerHTML.includes('background-color:#f00000') @@ -64,7 +65,7 @@ describe('ConfigProvider.DynamicTheme', () => { const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]')); expect( - dynamicStyles.some(style => { + dynamicStyles.some((style) => { const { innerHTML } = style; return ( innerHTML.includes('.ant-divider') && innerHTML.includes('border-block-start:0 blue') @@ -72,4 +73,22 @@ describe('ConfigProvider.DynamicTheme', () => { }), ).toBeTruthy(); }); + + it('should support iconPrefixCls', () => { + const { container } = render( + + + , + ); + + expect(container.querySelector('.test-icon')).toBeTruthy(); + + const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]')); + expect( + dynamicStyles.some((style) => { + const { innerHTML } = style; + return innerHTML.includes('.test-icon'); + }), + ).toBeTruthy(); + }); }); diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx index 15deb64ef8..ca1dba948c 100644 --- a/components/config-provider/index.tsx +++ b/components/config-provider/index.tsx @@ -4,6 +4,7 @@ import { FormProvider as RcFormProvider } from 'rc-field-form'; import type { ValidateMessages } from 'rc-field-form/lib/interface'; import useMemo from 'rc-util/lib/hooks/useMemo'; import * as React from 'react'; +import type { ReactElement } from 'react'; import type { RequiredMark } from '../form/Form'; import type { Locale } from '../locale-provider'; import LocaleProvider, { ANT_MARK } from '../locale-provider'; @@ -19,6 +20,7 @@ import { DisabledContextProvider } from './DisabledContext'; import useTheme from './hooks/useTheme'; import type { SizeType } from './SizeContext'; import SizeContext, { SizeContextProvider } from './SizeContext'; +import useStyle from './style'; export { type RenderEmptyHandler, @@ -139,7 +141,7 @@ export const globalConfig = () => ({ }, }); -const ProviderChildren: React.FC = props => { +const ProviderChildren: React.FC = (props) => { const { children, csp: customCsp, @@ -172,8 +174,11 @@ const ProviderChildren: React.FC = props => { ); const iconPrefixCls = customIconPrefixCls || parentContext.iconPrefixCls || defaultIconPrefixCls; + const shouldWrapSSR = iconPrefixCls !== parentContext.iconPrefixCls; const csp = customCsp || parentContext.csp; + const wrapSSR = useStyle(iconPrefixCls); + const mergedTheme = useTheme(theme, parentContext.theme); const config = { @@ -192,7 +197,7 @@ const ProviderChildren: React.FC = props => { // Pass the props used by `useContext` directly with child component. // These props should merged into `config`. - PASSED_PROPS.forEach(propName => { + PASSED_PROPS.forEach((propName) => { const propValue = props[propName]; if (propValue) { (config as any)[propName] = propValue; @@ -208,7 +213,7 @@ const ProviderChildren: React.FC = props => { const currentKeys = Object.keys(currentConfig) as Array; return ( prevKeys.length !== currentKeys.length || - prevKeys.some(key => prevConfig[key] !== currentConfig[key]) + prevKeys.some((key) => prevConfig[key] !== currentConfig[key]) ); }, ); @@ -218,7 +223,7 @@ const ProviderChildren: React.FC = props => { [iconPrefixCls, csp], ); - let childNode = children; + let childNode = shouldWrapSSR ? wrapSSR(children as ReactElement) : children; // Additional Form provider let validateMessages: ValidateMessages = {}; @@ -292,11 +297,11 @@ const ConfigProvider: React.FC & { ConfigContext: typeof ConfigContext; SizeContext: typeof SizeContext; config: typeof setGlobalConfig; -} = props => ( +} = (props) => ( {(_, __, legacyLocale) => ( - {context => ( + {(context) => ( )} diff --git a/components/config-provider/style/index.tsx b/components/config-provider/style/index.tsx new file mode 100644 index 0000000000..687c4ab7f8 --- /dev/null +++ b/components/config-provider/style/index.tsx @@ -0,0 +1,18 @@ +import { useStyleRegister } from '@ant-design/cssinjs'; +import { resetIcon } from '../../style'; +import { useToken } from '../../theme'; + +const useStyle = (iconPrefixCls: string) => { + const [theme, token] = useToken(); + // Generate style for icons + return useStyleRegister( + { theme, token, hashId: '', path: ['ant-design-icons', iconPrefixCls] }, + () => [ + { + [`.${iconPrefixCls}`]: resetIcon(), + }, + ], + ); +}; + +export default useStyle; diff --git a/components/message/__tests__/config.test.tsx b/components/message/__tests__/config.test.tsx index 347f9d4142..9cc49bee87 100644 --- a/components/message/__tests__/config.test.tsx +++ b/components/message/__tests__/config.test.tsx @@ -80,7 +80,7 @@ describe('message.config', () => { const noticeWithoutLeaving = Array.from( document.querySelectorAll('.ant-message-notice'), - ).filter(ele => !ele.classList.contains('ant-message-move-up-leave')); + ).filter((ele) => !ele.classList.contains('ant-message-move-up-leave')); expect(noticeWithoutLeaving).toHaveLength(5); expect(noticeWithoutLeaving[4].textContent).toEqual('last');