mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-30 20:38:45 +08:00
341 lines
10 KiB
TypeScript
341 lines
10 KiB
TypeScript
import * as React from 'react';
|
|
import { Theme } from '@ant-design/cssinjs';
|
|
|
|
import theme from '..';
|
|
import { render, renderHook } from '../../../tests/utils';
|
|
import ConfigProvider from '../../config-provider';
|
|
import type { ThemeConfig } from '../../config-provider/context';
|
|
import Input from '../../input';
|
|
import Row from '../../row';
|
|
import genRadius from '../themes/shared/genRadius';
|
|
|
|
const { useToken } = theme;
|
|
|
|
describe('Theme', () => {
|
|
const getHookToken = (config?: ThemeConfig) => {
|
|
let token: any;
|
|
const Demo = () => {
|
|
const { token: hookToken } = useToken();
|
|
token = hookToken;
|
|
return null;
|
|
};
|
|
render(
|
|
<ConfigProvider theme={config}>
|
|
<Demo />
|
|
</ConfigProvider>,
|
|
);
|
|
delete token._hashId;
|
|
delete token._tokenKey;
|
|
delete token._themeKey;
|
|
return token;
|
|
};
|
|
|
|
it('useTheme', () => {
|
|
const { result } = renderHook(() => useToken());
|
|
|
|
expect(result.current!.theme instanceof Theme).toBeTruthy();
|
|
expect(result.current!.hashId).toBeFalsy();
|
|
expect(result.current!.token).toEqual(
|
|
expect.objectContaining({
|
|
colorPrimary: '#1677ff',
|
|
'blue-6': '#1677ff',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('ConfigProvider with seed', () => {
|
|
const Demo = React.forwardRef((_, ref: any) => {
|
|
const themeObj = useToken();
|
|
ref.current = themeObj;
|
|
return null;
|
|
});
|
|
|
|
const themeRef = React.createRef<ReturnType<typeof useToken>>();
|
|
render(
|
|
<ConfigProvider
|
|
theme={{
|
|
token: {
|
|
colorPrimary: '#ff0000',
|
|
orange: '#ff8800',
|
|
},
|
|
}}
|
|
>
|
|
<Demo ref={themeRef} />
|
|
</ConfigProvider>,
|
|
);
|
|
|
|
expect(themeRef.current!.token).toEqual(
|
|
expect.objectContaining({
|
|
colorPrimary: '#ff0000',
|
|
colorPrimaryHover: '#ff3029', // It's safe to modify if theme logic changed
|
|
orange6: '#ff8800',
|
|
orange9: '#8c3d00', // It's safe to modify if theme logic changed
|
|
}),
|
|
);
|
|
});
|
|
|
|
describe('radius should be computed as expected', () => {
|
|
const radiusGroup = {
|
|
0: {
|
|
borderRadius: 0,
|
|
borderRadiusLG: 0,
|
|
borderRadiusSM: 0,
|
|
borderRadiusXS: 0,
|
|
borderRadiusOuter: 0,
|
|
},
|
|
2: {
|
|
borderRadius: 2,
|
|
borderRadiusLG: 2,
|
|
borderRadiusSM: 2,
|
|
borderRadiusXS: 1,
|
|
borderRadiusOuter: 2,
|
|
},
|
|
4: {
|
|
borderRadius: 4,
|
|
borderRadiusLG: 4,
|
|
borderRadiusSM: 4,
|
|
borderRadiusXS: 1,
|
|
borderRadiusOuter: 4,
|
|
},
|
|
5: {
|
|
borderRadius: 5,
|
|
borderRadiusLG: 6,
|
|
borderRadiusSM: 4,
|
|
borderRadiusXS: 1,
|
|
borderRadiusOuter: 4,
|
|
},
|
|
6: {
|
|
borderRadius: 6,
|
|
borderRadiusLG: 8,
|
|
borderRadiusSM: 4,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 4,
|
|
},
|
|
7: {
|
|
borderRadius: 7,
|
|
borderRadiusLG: 9,
|
|
borderRadiusSM: 5,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 4,
|
|
},
|
|
8: {
|
|
borderRadius: 8,
|
|
borderRadiusLG: 10,
|
|
borderRadiusSM: 6,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 6,
|
|
},
|
|
10: {
|
|
borderRadius: 10,
|
|
borderRadiusLG: 12,
|
|
borderRadiusSM: 6,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 6,
|
|
},
|
|
12: {
|
|
borderRadius: 12,
|
|
borderRadiusLG: 14,
|
|
borderRadiusSM: 6,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 6,
|
|
},
|
|
14: {
|
|
borderRadius: 14,
|
|
borderRadiusLG: 16,
|
|
borderRadiusSM: 7,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 6,
|
|
},
|
|
16: {
|
|
borderRadius: 16,
|
|
borderRadiusLG: 16,
|
|
borderRadiusSM: 8,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 6,
|
|
},
|
|
20: {
|
|
borderRadius: 20,
|
|
borderRadiusLG: 16,
|
|
borderRadiusSM: 8,
|
|
borderRadiusXS: 2,
|
|
borderRadiusOuter: 6,
|
|
},
|
|
};
|
|
|
|
Object.entries(radiusGroup).forEach(([base, result]) => {
|
|
it(`base ${base}`, () => {
|
|
expect(genRadius(Number(base))).toMatchObject(result);
|
|
});
|
|
});
|
|
});
|
|
|
|
const rowShouldThrow = (screens: any, error: string) => {
|
|
const demoRender = () =>
|
|
render(
|
|
<ConfigProvider theme={{ token: screens }}>
|
|
<Row />
|
|
</ConfigProvider>,
|
|
);
|
|
expect(demoRender).toThrow(error);
|
|
};
|
|
|
|
describe('invalid breakpoints order values should raise an error', () => {
|
|
const tests: Array<[string, number, string]> = [
|
|
['screenXS', 1000, 'screenXSMax<=screenSMMin fails : !(1010<=576)'],
|
|
['screenSM', 1000, 'screenSMMax<=screenMDMin fails : !(1010<=768)'],
|
|
['screenMD', 1000, 'screenMDMax<=screenLGMin fails : !(1010<=992)'],
|
|
['screenLG', 2000, 'screenLGMax<=screenXLMin fails : !(2010<=1200)'],
|
|
['screenXL', 2000, 'screenXLMax<=screenXXLMin fails : !(2010<=1600)'],
|
|
];
|
|
|
|
tests.forEach(([screen, value, error]: any) => {
|
|
it(`Screen ${screen} is too big`, () => {
|
|
rowShouldThrow(
|
|
{ [screen]: value, [`${screen}Min`]: value, [`${screen}Max`]: value + 10 },
|
|
error,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('invalid breakpoints MIN<=BP values should raise an error', () => {
|
|
const tests: Array<[string, number, string]> = [
|
|
['screenXSMin', 1000, 'screenXSMin<=screenXS fails : !(1000<=480)'],
|
|
['screenSMMin', 1000, 'screenSMMin<=screenSM fails : !(1000<=576)'],
|
|
['screenMDMin', 1000, 'screenMDMin<=screenMD fails : !(1000<=768)'],
|
|
['screenLGMin', 2000, 'screenLGMin<=screenLG fails : !(2000<=992)'],
|
|
['screenXLMin', 2000, 'screenXLMin<=screenXL fails : !(2000<=1200)'],
|
|
['screenXXLMin', 2000, 'screenXXLMin<=screenXXL fails : !(2000<=1600)'],
|
|
];
|
|
|
|
tests.forEach(([screen, value, error]: any) => {
|
|
it(`Screen ${screen}Min is too big regarding ${screen}`, () => {
|
|
rowShouldThrow({ [screen]: value }, error);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('invalid breakpoints BP<=MAX values should raise an error', () => {
|
|
const tests: Array<[string, number, string]> = [
|
|
['screenXS', 1000, 'screenXS<=screenXSMax fails : !(1000<=575)'],
|
|
['screenSM', 1000, 'screenSM<=screenSMMax fails : !(1000<=767)'],
|
|
['screenMD', 1000, 'screenMD<=screenMDMax fails : !(1000<=991)'],
|
|
['screenLG', 2000, 'screenLG<=screenLGMax fails : !(2000<=1199)'],
|
|
['screenXL', 2000, 'screenXL<=screenXLMax fails : !(2000<=1599)'],
|
|
];
|
|
|
|
tests.forEach(([screen, value, error]: any) => {
|
|
it(`Screen ${screen} is too big regarding ${screen}Max`, () => {
|
|
rowShouldThrow({ [screen]: value }, error);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('motion false token', () => {
|
|
const Shower = () => {
|
|
const { token } = useToken();
|
|
|
|
return <div className="duration">{token.motionDurationSlow}</div>;
|
|
};
|
|
|
|
const { container } = render(
|
|
<ConfigProvider theme={{ token: { motion: false } }}>
|
|
<Shower />
|
|
</ConfigProvider>,
|
|
);
|
|
|
|
expect(container.querySelector('.duration')?.textContent).toEqual('0s');
|
|
});
|
|
|
|
describe('getDesignToken', () => {
|
|
it('default', () => {
|
|
const token = theme.getDesignToken();
|
|
const hookToken = getHookToken();
|
|
expect(token).toEqual(hookToken);
|
|
});
|
|
|
|
it('with custom token', () => {
|
|
const config: ThemeConfig = {
|
|
token: {
|
|
colorPrimary: '#189cff',
|
|
borderRadius: 8,
|
|
fontSizeLG: 20,
|
|
},
|
|
};
|
|
const token = theme.getDesignToken(config);
|
|
const hookToken = getHookToken(config);
|
|
expect(token).toEqual(hookToken);
|
|
expect(token.colorPrimary).toEqual('#189cff');
|
|
});
|
|
|
|
it('with custom algorithm', () => {
|
|
const config: ThemeConfig = {
|
|
token: {
|
|
colorPrimary: '#1677ff',
|
|
borderRadius: 8,
|
|
fontSizeLG: 20,
|
|
},
|
|
algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],
|
|
};
|
|
const token = theme.getDesignToken(config);
|
|
const hookToken = getHookToken(config);
|
|
expect(token).toEqual(hookToken);
|
|
expect(token.colorPrimary).toEqual('#1668dc');
|
|
});
|
|
});
|
|
|
|
describe('colorLink', () => {
|
|
it('should follow colorPrimary by default', () => {
|
|
const token = getHookToken();
|
|
expect(token.colorLink).toEqual(token.colorInfo);
|
|
expect(token.colorLinkHover).toEqual(token.colorInfoHover);
|
|
expect(token.colorLinkActive).toEqual(token.colorInfoActive);
|
|
|
|
const token2 = getHookToken({ token: { colorPrimary: '#189cff' } });
|
|
expect(token2.colorLink).toEqual(token2.colorInfo);
|
|
expect(token2.colorLinkHover).toEqual(token2.colorInfoHover);
|
|
expect(token2.colorLinkActive).toEqual(token2.colorInfoActive);
|
|
// colorInfo should not follow colorPrimary
|
|
expect(token2.colorLink).not.toEqual('#189cff');
|
|
|
|
const token3 = getHookToken({ algorithm: [theme.darkAlgorithm] });
|
|
expect(token3.colorLink).toEqual(token3.colorInfo);
|
|
expect(token3.colorLinkHover).toEqual(token3.colorInfoHover);
|
|
expect(token3.colorLinkActive).toEqual(token3.colorInfoActive);
|
|
});
|
|
|
|
it('should be calculated correctly', () => {
|
|
const token = getHookToken({ token: { colorLink: '#189cff' } });
|
|
expect(token.colorLink).toEqual('#189cff');
|
|
expect(token.colorLinkHover).toEqual('#69c8ff');
|
|
expect(token.colorLinkActive).toEqual('#0978d9');
|
|
});
|
|
});
|
|
|
|
it('component token should support algorithm', () => {
|
|
const Demo = ({ algorithm }: { algorithm?: boolean | typeof theme.darkAlgorithm }) => (
|
|
<ConfigProvider
|
|
theme={{
|
|
components: {
|
|
Input: {
|
|
colorPrimary: '#00B96B',
|
|
algorithm,
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<Input />
|
|
</ConfigProvider>
|
|
);
|
|
|
|
const { container, rerender } = render(<Demo />);
|
|
expect(container.querySelector('input')).toHaveStyle({ 'border-color': '#4096ff' });
|
|
|
|
rerender(<Demo algorithm />);
|
|
expect(container.querySelector('input')).toHaveStyle({ 'border-color': '#20c77c' });
|
|
|
|
rerender(<Demo algorithm={theme.darkAlgorithm} />);
|
|
expect(container.querySelector('input')).toHaveStyle({ 'border-color': '#1fb572' });
|
|
});
|
|
});
|