import React from 'react'; import kebabCase from 'lodash/kebabCase'; import canUseDom from 'rc-util/lib/Dom/canUseDom'; import ConfigProvider from '..'; import { Button, InputNumber, Select } from '../..'; import { resetWarned } from '../../_util/warning'; import { render } from '../../../tests/utils'; import theme from '../../theme'; import type { GlobalToken } from '../../theme'; import { useToken } from '../../theme/internal'; const { defaultAlgorithm, darkAlgorithm, compactAlgorithm } = theme; /* biome-ignore lint/style/noVar: has to be a global variable */ /* eslint-disable-next-line no-var */ var mockCanUseDom = true; jest.mock('rc-util/lib/Dom/canUseDom', () => () => mockCanUseDom); describe('ConfigProvider.Theme', () => { beforeEach(() => { mockCanUseDom = true; }); const colorList = ['primaryColor', 'successColor', 'warningColor', 'errorColor', 'infoColor']; colorList.forEach((colorName) => { it(colorName, () => { ConfigProvider.config({ prefixCls: 'bamboo', theme: { [colorName]: '#0000FF', }, }); const styles = Array.from(document.querySelectorAll('style')); const themeStyle = styles.find((style) => style.getAttribute('rc-util-key')?.includes('-dynamic-theme'), ); expect(themeStyle).toBeTruthy(); expect(themeStyle?.innerHTML).toContain(`--bamboo-${kebabCase(colorName)}: rgb(0, 0, 255)`); }); }); it('warning for SSR', () => { resetWarned(); const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); mockCanUseDom = false; expect(canUseDom()).toBeFalsy(); ConfigProvider.config({ theme: { infoColor: 'red', }, }); expect(errorSpy).toHaveBeenCalledWith( 'Warning: [antd: ConfigProvider] SSR do not support dynamic theme with css variables.', ); errorSpy.mockRestore(); }); it('algorithm should work', () => { let tokenRef: Partial = {}; const Demo = () => { const [, token] = useToken(); tokenRef = token; return null; }; render( , ); expect(tokenRef?.colorPrimaryText).toBe('#1668dc'); }); it('compactAlgorithm should work', () => { let tokenRef: Partial = {}; const Demo = () => { const [, token] = useToken(); tokenRef = token; return null; }; render( , ); expect(tokenRef).toEqual( expect.objectContaining({ sizeXXL: 48, sizeXL: 32, sizeLG: 16, sizeMD: 16, sizeMS: 12, size: 8, sizeSM: 8, sizeXS: 4, sizeXXS: 4, }), ); }); it('should support algorithm array', () => { let tokenRef: Partial = {}; const Demo = () => { const [, token] = useToken(); tokenRef = token; return null; }; render( , ); expect(tokenRef?.colorPrimaryText).toBe('#1668dc'); }); it('overriding component token should work', () => { render( , ); const dynamicStyles = Array.from( document.querySelectorAll('style[data-css-hash]'), ).map((item) => item?.innerHTML ?? ''); expect( dynamicStyles.some( (style) => style.includes('.ant-input-number') && style.includes('width:50.1234px'), ), ).toBeTruthy(); }); it('hashed should be true if not changed', () => { let hashId = 'hashId'; theme.defaultConfig.hashed = true; const Demo = () => { const [, , hash] = useToken(); hashId = hash; return null; }; render( , ); expect(hashId).not.toBe(''); theme.defaultConfig.hashed = false; }); it('The order does not affect the result', () => { const tokens: Record<'a' | 'b', Partial> = { a: {}, b: {}, }; const Token: React.FC<{ type: 'a' | 'b' }> = ({ type }) => { const [, token] = useToken(); tokens[type] = token; return null; }; render( <> , ); expect(tokens.a).toMatchObject(tokens.b); }); it('theme separated should work', () => { let tokenRef: Partial = {}; const Demo = () => { const [, token] = useToken(); tokenRef = token; return null; }; render( , ); expect(tokenRef?.colorPrimaryText).toBe('#1677ff'); }); it('theme inherit should not affect hashed and cssVar', () => { let hashId = 'hashId'; let cssVar: any; const Demo = () => { const [, , hash, , cssVarConfig] = useToken(); hashId = hash; cssVar = cssVarConfig; return null; }; render( , ); expect(hashId).toBeTruthy(); expect(cssVar).toBeTruthy(); }); describe('cssVar', () => { it('should work', () => { const { container } = render( , ); const button = container.querySelector('button')!; expect(button).toHaveClass('foo'); expect(button).toHaveStyle({ '--ant-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--ant-button-default-shadow)', 'border-radius': 'var(--ant-border-radius)', }); }); it('prefix', () => { const { container } = render( <> , ); const fooBtn = container.querySelector('.button-foo')!; const barBtn = container.querySelector('.button-bar')!; expect(fooBtn).toHaveClass('foo'); expect(fooBtn).toHaveStyle({ '--ant-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--ant-button-default-shadow)', 'border-radius': 'var(--ant-border-radius)', }); expect(barBtn).toHaveClass('bar'); expect(barBtn).toHaveStyle({ '--bar-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--bar-button-default-shadow)', 'border-radius': 'var(--bar-border-radius)', }); }); it('prefix follow prefixCls by default', () => { const { container } = render( <> , ); const fooBtn = container.querySelector('.button-foo')!; expect(fooBtn).toHaveClass('foo'); expect(fooBtn).toHaveStyle({ '--foo-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--foo-button-default-shadow)', 'border-radius': 'var(--foo-border-radius)', }); const barBtn = container.querySelector('.button-bar')!; expect(barBtn).toHaveClass('bar'); expect(barBtn).toHaveStyle({ '--bar-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--bar-button-default-shadow)', 'border-radius': 'var(--bar-border-radius)', }); const bananaBtn = container.querySelector('.button-banana')!; expect(bananaBtn).toHaveClass('banana'); expect(bananaBtn).toHaveStyle({ '--banana-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--banana-button-default-shadow)', 'border-radius': 'var(--banana-border-radius)', }); const catBtn = container.querySelector('.button-cat')!; expect(catBtn).toHaveClass('apple'); expect(catBtn).toHaveStyle({ '--cat-color-text': 'rgba(0, 0, 0, 0.88)', boxShadow: 'var(--cat-button-default-shadow)', 'border-radius': 'var(--cat-border-radius)', }); }); it('component token should work', () => { const { container } = render(