Skip to content

Commit

Permalink
test: add test cases for breakpoints customize (#39697)
Browse files Browse the repository at this point in the history
Rename useResponsiveObserve to useResponsiveObserver. Add validation of breakpoints values and test
  • Loading branch information
azro352 committed Dec 22, 2022
1 parent f1c6350 commit 328a800
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 29 deletions.
6 changes: 3 additions & 3 deletions components/_util/__tests__/responsiveObserve.test.tsx
Original file line number Diff line number Diff line change
@@ -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 />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
2 changes: 1 addition & 1 deletion components/avatar/SizeContext.tsx
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
4 changes: 2 additions & 2 deletions components/avatar/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
10 changes: 5 additions & 5 deletions components/descriptions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -135,19 +135,19 @@ 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;
}
setScreens(newScreens);
});

return () => {
responsiveObserve.unsubscribe(token);
responsiveObserver.unsubscribe(token);
};
}, []);

Expand Down
6 changes: 3 additions & 3 deletions components/grid/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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[]) => {
Expand Down Expand Up @@ -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;

Expand Down
10 changes: 5 additions & 5 deletions components/grid/hooks/useBreakpoint.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
10 changes: 5 additions & 5 deletions components/grid/row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 (
Expand All @@ -121,7 +121,7 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
setScreens(screen);
}
});
return () => responsiveObserve.unsubscribe(token);
return () => responsiveObserver.unsubscribe(token);
}, []);

// ================================== Render ==================================
Expand Down
4 changes: 2 additions & 2 deletions components/list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion components/table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion components/table/interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
63 changes: 63 additions & 0 deletions components/theme/__tests__/token.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
});
});
});
});

0 comments on commit 328a800

Please sign in to comment.