Browse Source

test: add test cases for breakpoints customize (#39697)

Rename useResponsiveObserve to useResponsiveObserver. Add validation of breakpoints values and test
pull/39723/head
azro352 2 years ago
committed by GitHub
parent
commit
328a800132
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      components/_util/__tests__/responsiveObserve.test.tsx
  2. 43
      components/_util/responsiveObserver.ts
  3. 2
      components/avatar/SizeContext.tsx
  4. 4
      components/avatar/avatar.tsx
  5. 10
      components/descriptions/index.tsx
  6. 6
      components/grid/__tests__/index.test.tsx
  7. 10
      components/grid/hooks/useBreakpoint.tsx
  8. 10
      components/grid/row.tsx
  9. 4
      components/list/index.tsx
  10. 2
      components/table/Table.tsx
  11. 2
      components/table/interface.tsx
  12. 63
      components/theme/__tests__/token.test.tsx

6
components/_util/__tests__/responsiveObserve.test.tsx

@ -1,13 +1,13 @@
import React from 'react';
import { render } from '../../../tests/utils';
import useResponsiveObserve from '../responsiveObserve';
import useResponsiveObserver from '../responsiveObserver';
describe('Test ResponsiveObserve', () => {
it('test ResponsiveObserve subscribe and unsubscribe', () => {
let responsiveObserveRef: any;
const Demo = () => {
const responsiveObserve = useResponsiveObserve();
responsiveObserveRef = responsiveObserve;
const responsiveObserver = useResponsiveObserver();
responsiveObserveRef = responsiveObserver;
return null;
};
render(<Demo />);

43
components/_util/responsiveObserve.ts → components/_util/responsiveObserver.ts

@ -19,9 +19,50 @@ const getResponsiveMap = (token: GlobalToken): BreakpointMap => ({
xxl: `(min-width: ${token.screenXXL}px)`,
});
/**
* Ensures that the breakpoints token are valid, in good order
* For each breakpoint : screenMin <= screen <= screenMax and screenMax <= nextScreenMin
*/
const validateBreakpoints = (token: GlobalToken) => {
const indexableToken: any = token;
const revBreakpoints = [...responsiveArray].reverse();
revBreakpoints.forEach((breakpoint: Breakpoint, i: number) => {
const breakpointUpper = breakpoint.toUpperCase();
const screenMin = `screen${breakpointUpper}Min`;
const screen = `screen${breakpointUpper}`;
if (!(indexableToken[screenMin] <= indexableToken[screen])) {
throw new Error(
`${screenMin}<=${screen} fails : !(${indexableToken[screenMin]}<=${indexableToken[screen]})`,
);
}
if (i < revBreakpoints.length - 1) {
const screenMax = `screen${breakpointUpper}Max`;
if (!(indexableToken[screen] <= indexableToken[screenMax])) {
throw new Error(
`${screen}<=${screenMax} fails : !(${indexableToken[screen]}<=${indexableToken[screenMax]})`,
);
}
const nextBreakpointUpperMin: string = revBreakpoints[i + 1].toUpperCase();
const nextScreenMin = `screen${nextBreakpointUpperMin}Min`;
if (!(indexableToken[screenMax] <= indexableToken[nextScreenMin])) {
throw new Error(
`${screenMax}<=${nextScreenMin} fails : !(${indexableToken[screenMax]}<=${indexableToken[nextScreenMin]})`,
);
}
}
});
return token;
};
export default function useResponsiveObserver() {
const [, token] = useToken();
const responsiveMap: BreakpointMap = getResponsiveMap(token);
const responsiveMap: BreakpointMap = getResponsiveMap(validateBreakpoints(token));
// To avoid repeat create instance, we add `useMemo` here.
return React.useMemo(() => {

2
components/avatar/SizeContext.tsx

@ -1,5 +1,5 @@
import * as React from 'react';
import type { ScreenSizeMap } from '../_util/responsiveObserve';
import type { ScreenSizeMap } from '../_util/responsiveObserver';
export type AvatarSize = 'large' | 'small' | 'default' | number | ScreenSizeMap;

4
components/avatar/avatar.tsx

@ -4,8 +4,8 @@ import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import useBreakpoint from '../grid/hooks/useBreakpoint';
import type { Breakpoint } from '../_util/responsiveObserve';
import { responsiveArray } from '../_util/responsiveObserve';
import type { Breakpoint } from '../_util/responsiveObserver';
import { responsiveArray } from '../_util/responsiveObserver';
import warning from '../_util/warning';
import type { AvatarSize } from './SizeContext';
import SizeContext from './SizeContext';

10
components/descriptions/index.tsx

@ -4,8 +4,8 @@ import toArray from 'rc-util/lib/Children/toArray';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
import type { Breakpoint, ScreenMap } from '../_util/responsiveObserve';
import useResponsiveObserve, { responsiveArray } from '../_util/responsiveObserve';
import type { Breakpoint, ScreenMap } from '../_util/responsiveObserver';
import useResponsiveObserver, { responsiveArray } from '../_util/responsiveObserver';
import warning from '../_util/warning';
import DescriptionsItem from './Item';
import Row from './Row';
@ -135,11 +135,11 @@ function Descriptions({
const mergedColumn = getColumn(column, screens);
const [wrapSSR, hashId] = useStyle(prefixCls);
const responsiveObserve = useResponsiveObserve();
const responsiveObserver = useResponsiveObserver();
// Responsive
React.useEffect(() => {
const token = responsiveObserve.subscribe((newScreens) => {
const token = responsiveObserver.subscribe((newScreens) => {
if (typeof column !== 'object') {
return;
}
@ -147,7 +147,7 @@ function Descriptions({
});
return () => {
responsiveObserve.unsubscribe(token);
responsiveObserver.unsubscribe(token);
};
}, []);

6
components/grid/__tests__/index.test.tsx

@ -7,8 +7,8 @@ import useBreakpoint from '../hooks/useBreakpoint';
import { render, fireEvent } from '../../../tests/utils';
// Mock for `responsiveObserve` to test `unsubscribe` call
jest.mock('../../_util/responsiveObserve', () => {
const modules = jest.requireActual('../../_util/responsiveObserve');
jest.mock('../../_util/responsiveObserver', () => {
const modules = jest.requireActual('../../_util/responsiveObserver');
const originHook = modules.default;
const useMockResponsiveObserver = (...args: any[]) => {
@ -116,7 +116,7 @@ describe('Grid', () => {
expect(asFragment().firstChild).toMatchSnapshot();
});
it('useResponsiveObserve.unsubscribe should be called when unmounted', () => {
it('useResponsiveObserver.unsubscribe should be called when unmounted', () => {
const { unmount } = render(<Row gutter={{ xs: 20 }} />);
const called: number = (global as any).unsubscribeCnt;

10
components/grid/hooks/useBreakpoint.tsx

@ -1,22 +1,22 @@
import { useEffect, useRef } from 'react';
import useForceUpdate from '../../_util/hooks/useForceUpdate';
import type { ScreenMap } from '../../_util/responsiveObserve';
import useResponsiveObserve from '../../_util/responsiveObserve';
import type { ScreenMap } from '../../_util/responsiveObserver';
import useResponsiveObserver from '../../_util/responsiveObserver';
function useBreakpoint(refreshOnChange: boolean = true): ScreenMap {
const screensRef = useRef<ScreenMap>({});
const forceUpdate = useForceUpdate();
const responsiveObserve = useResponsiveObserve();
const responsiveObserver = useResponsiveObserver();
useEffect(() => {
const token = responsiveObserve.subscribe((supportScreens) => {
const token = responsiveObserver.subscribe((supportScreens) => {
screensRef.current = supportScreens;
if (refreshOnChange) {
forceUpdate();
}
});
return () => responsiveObserve.unsubscribe(token);
return () => responsiveObserver.unsubscribe(token);
}, []);
return screensRef.current;

10
components/grid/row.tsx

@ -2,8 +2,8 @@ import classNames from 'classnames';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import useFlexGapSupport from '../_util/hooks/useFlexGapSupport';
import type { Breakpoint, ScreenMap } from '../_util/responsiveObserve';
import useResponsiveObserve, { responsiveArray } from '../_util/responsiveObserve';
import type { Breakpoint, ScreenMap } from '../_util/responsiveObserver';
import useResponsiveObserver, { responsiveArray } from '../_util/responsiveObserver';
import RowContext from './RowContext';
import { useRowStyle } from './style';
@ -106,11 +106,11 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
const gutterRef = React.useRef<Gutter | [Gutter, Gutter]>(gutter);
const responsiveObserve = useResponsiveObserve();
const responsiveObserver = useResponsiveObserver();
// ================================== Effect ==================================
React.useEffect(() => {
const token = responsiveObserve.subscribe((screen) => {
const token = responsiveObserver.subscribe((screen) => {
setCurScreens(screen);
const currentGutter = gutterRef.current || 0;
if (
@ -121,7 +121,7 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
setScreens(screen);
}
});
return () => responsiveObserve.unsubscribe(token);
return () => responsiveObserver.unsubscribe(token);
}, []);
// ================================== Render ==================================

4
components/list/index.tsx

@ -10,8 +10,8 @@ import type { PaginationConfig } from '../pagination';
import Pagination from '../pagination';
import type { SpinProps } from '../spin';
import Spin from '../spin';
import type { Breakpoint } from '../_util/responsiveObserve';
import { responsiveArray } from '../_util/responsiveObserve';
import type { Breakpoint } from '../_util/responsiveObserver';
import { responsiveArray } from '../_util/responsiveObserver';
import Item from './Item';
// CSSINJS

2
components/table/Table.tsx

@ -15,7 +15,7 @@ import Pagination from '../pagination';
import type { SpinProps } from '../spin';
import Spin from '../spin';
import type { TooltipProps } from '../tooltip';
import type { Breakpoint } from '../_util/responsiveObserve';
import type { Breakpoint } from '../_util/responsiveObserver';
import scrollTo from '../_util/scrollTo';
import warning from '../_util/warning';
import Column from './Column';

2
components/table/interface.tsx

@ -8,7 +8,7 @@ import type * as React from 'react';
import type { CheckboxProps } from '../checkbox';
import type { PaginationProps } from '../pagination';
import type { TooltipProps } from '../tooltip';
import type { Breakpoint } from '../_util/responsiveObserve';
import type { Breakpoint } from '../_util/responsiveObserver';
import type { INTERNAL_SELECTION_ITEM } from './hooks/useSelection';
export { GetRowKey, ExpandableConfig };

63
components/theme/__tests__/token.test.tsx

@ -3,6 +3,7 @@ import * as React from 'react';
import genRadius from '../themes/shared/genRadius';
import { render, renderHook } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import Row from '../../row';
import theme from '..';
const { useToken } = theme;
@ -142,4 +143,66 @@ describe('Theme', () => {
});
});
});
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);
});
});
});
});

Loading…
Cancel
Save