Skip to content

Commit

Permalink
Merge pull request #4558 from VKCOM/imirdzhamolov/issue4304/fix/Adapt…
Browse files Browse the repository at this point in the history
…ivityProvider-double-render

fix(AdaptivityProvider): useMemo() instead useEffect() for define data
  • Loading branch information
inomdzhon authored Mar 23, 2023
2 parents 16ae1d3 + 8d29e56 commit 6fd6a4f
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { useAdaptivity } from '../../hooks/useAdaptivity';
import { useBridgeAdaptivity } from '../../hooks/useBridgeAdaptivity';
import { render, screen } from '@testing-library/react';
import { type BridgeAdaptivity, useBridgeAdaptivity } from '../../hooks/useBridgeAdaptivity';
import { SizeType, ViewHeight, ViewWidth } from '../../lib/adaptivity';
import { baselineComponent } from '../../testing/utils';
import { AdaptivityProvider } from './AdaptivityProvider';
import { AdaptivityContext, type AdaptivityProps } from './AdaptivityContext';
import { AdaptivityProvider, type AdaptivityProviderProps } from './AdaptivityProvider';

jest.mock('../../hooks/useBridgeAdaptivity', () => {
const bridgeMock = jest.fn(() => {
Expand All @@ -20,11 +20,12 @@ jest.mock('../../hooks/useBridgeAdaptivity', () => {
};
});

function Adaptive() {
const { sizeX, sizeY, viewWidth, viewHeight } = useAdaptivity();
function CatchAdaptivityProviderContext() {
const { sizeX, sizeY, viewWidth, viewHeight } = React.useContext(AdaptivityContext);

return (
<div
data-testid="adaptivity-provider-context"
data-size-x={sizeX}
data-size-y={sizeY}
data-view-width={viewWidth}
Expand All @@ -33,41 +34,42 @@ function Adaptive() {
);
}

function renderExtractAdaptive(hasPointer?: boolean): {
sizeX: string;
sizeY: string;
viewHeight: number;
viewWidth: number;
} {
const renderResult = render(
<AdaptivityProvider hasPointer={hasPointer}>
<Adaptive />
function renderAdaptiveProvider(props?: AdaptivityProviderProps): AdaptivityProps {
render(
<AdaptivityProvider {...props}>
<CatchAdaptivityProviderContext />
</AdaptivityProvider>,
);
const renderee = renderResult.container.firstElementChild as HTMLElement;
const elWithContextData = screen.getByTestId('adaptivity-provider-context');

const result = {
sizeX: String(renderee.dataset.sizeX),
sizeY: String(renderee.dataset.sizeY),
viewWidth: Number(renderee.dataset.viewWidth),
viewHeight: Number(renderee.dataset.viewHeight),
};

return result;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
sizeX: elWithContextData.dataset.sizeX,
sizeY: elWithContextData.dataset.sizeY,
viewWidth: elWithContextData.dataset.viewWidth
? Number(elWithContextData.dataset.viewWidth)
: undefined,
viewHeight: elWithContextData.dataset.viewHeight
? Number(elWithContextData.dataset.viewHeight)
: undefined,
} as AdaptivityProps;
}

function renderBridgeAdaptive(viewWidth = 0, viewHeight = 0, hasPointer?: boolean) {
function renderAdaptiveProviderWithBridge(
type: BridgeAdaptivity['type'],
viewportWidth = 0,
viewportHeight = 0,
hasPointer?: boolean,
) {
(useBridgeAdaptivity as jest.Mock).mockReturnValue({
type: 'adaptive',
viewportWidth: viewWidth,
viewportHeight: viewHeight,
type,
viewportWidth,
viewportHeight,
});

return renderExtractAdaptive(hasPointer);
return renderAdaptiveProvider({ hasPointer });
}

// TODO: More tests for these variations

describe('AdaptivityProvider', () => {
beforeEach(() => {
(useBridgeAdaptivity as jest.Mock).mockReturnValue({
Expand All @@ -76,126 +78,183 @@ describe('AdaptivityProvider', () => {
viewportHeight: 0,
});
});

baselineComponent(AdaptivityProvider, { forward: false });
describe('bridge adaptivity', () => {

describe('without bridge', () => {
it('should return undefined adaptivity props', () => {
const result = renderAdaptiveProvider();
expect(result).toEqual({
sizeX: undefined,
sizeY: undefined,
viewWidth: undefined,
viewHeight: undefined,
});
});

it('should define sizeX and sizeY adaptivity props', () => {
const result = renderAdaptiveProvider({
viewWidth: ViewWidth.DESKTOP,
viewHeight: ViewHeight.MEDIUM,
});
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.DESKTOP,
viewHeight: ViewHeight.MEDIUM,
});
});
});

describe('with bridge', () => {
it('should apply bridge adaptivity [viewWidth] SMALL_MOBILE', () => {
const result = renderBridgeAdaptive(300, 420);
const result = renderAdaptiveProviderWithBridge('adaptive', 300, 420);
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.REGULAR,
viewWidth: ViewWidth.SMALL_MOBILE,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [viewWidth] MOBILE', () => {
const result = renderBridgeAdaptive(320, 420);
const result = renderAdaptiveProviderWithBridge('adaptive', 320, 420);
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.REGULAR,
viewWidth: ViewWidth.MOBILE,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [viewWidth] SMALL_TABLET', () => {
const result = renderBridgeAdaptive(768, 420);
const result = renderAdaptiveProviderWithBridge('adaptive', 768, 420);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.SMALL_TABLET,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [viewWidth] TABLET', () => {
const result = renderBridgeAdaptive(1024, 420);
const result = renderAdaptiveProviderWithBridge('adaptive', 1024, 420);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.TABLET,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [viewWidth] DESKTOP', () => {
const result = renderBridgeAdaptive(1280, 420);
const result = renderAdaptiveProviderWithBridge('adaptive', 1280, 420);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.DESKTOP,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [viewHeight] EXTRA_SMALL', () => {
const result = renderBridgeAdaptive(320, 340);
const result = renderAdaptiveProviderWithBridge('adaptive', 320, 340);
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.MOBILE,
viewHeight: ViewHeight.EXTRA_SMALL,
});
});

it('should apply bridge adaptivity [viewHeight] SMALL', () => {
const result = renderBridgeAdaptive(320, 415);
const result = renderAdaptiveProviderWithBridge('adaptive', 320, 415);
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.REGULAR,
viewWidth: ViewWidth.MOBILE,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [viewHeight] MEDIUM', () => {
const result = renderBridgeAdaptive(320, 720);
const result = renderAdaptiveProviderWithBridge('adaptive', 320, 720);
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.REGULAR,
viewWidth: ViewWidth.MOBILE,
viewHeight: ViewHeight.MEDIUM,
});
});

it('should apply bridge adaptivity [both] SMALL_MOBILE / EXTRA_SMALL', () => {
const result = renderBridgeAdaptive(300, 340);
const result = renderAdaptiveProviderWithBridge('adaptive', 300, 340);
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.SMALL_MOBILE,
viewHeight: ViewHeight.EXTRA_SMALL,
});
});

it('should apply bridge adaptivity [both] SMALL_TABLET / SMALL', () => {
const result = renderBridgeAdaptive(768, 415);
const result = renderAdaptiveProviderWithBridge('adaptive', 768, 415);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.SMALL_TABLET,
viewHeight: ViewHeight.SMALL,
});
});

it('should apply bridge adaptivity [both] TABLET / MEDIUM', () => {
const result = renderBridgeAdaptive(1024, 720);
const result = renderAdaptiveProviderWithBridge('adaptive', 1024, 720);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.TABLET,
viewHeight: ViewHeight.MEDIUM,
});
});

it('should apply bridge adaptivity [both] DESKTOP / MEDIUM', () => {
const result = renderBridgeAdaptive(1280, 720);
const result = renderAdaptiveProviderWithBridge('adaptive', 1280, 720);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.DESKTOP,
viewHeight: ViewHeight.MEDIUM,
});
});
});
describe('custom props', () => {

it('should ignore custom mouse with bridge [viewWidth] SMALL_TABLET', () => {
const result = renderBridgeAdaptive(768, 420, false);
const result = renderAdaptiveProviderWithBridge('adaptive', 768, 420, false);
expect(result).toEqual({
sizeX: SizeType.REGULAR,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.SMALL_TABLET,
viewHeight: ViewHeight.SMALL,
});
});

it('should use bridge force_mobile preset', () => {
const result = renderAdaptiveProviderWithBridge('force_mobile');
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.REGULAR,
viewWidth: ViewWidth.MOBILE,
viewHeight: undefined,
});
});

it('should use bridge force_mobile_compact preset', () => {
const result = renderAdaptiveProviderWithBridge('force_mobile_compact');
expect(result).toEqual({
sizeX: SizeType.COMPACT,
sizeY: SizeType.COMPACT,
viewWidth: ViewWidth.MOBILE,
viewHeight: undefined,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,8 @@ export const AdaptivityProvider = ({
children,
}: React.PropsWithChildren<AdaptivityProps>) => {
const bridge = useBridgeAdaptivity();
const [adaptivity, setAdaptivity] = React.useState({
viewWidth,
viewHeight,
sizeX,
sizeY,
hasPointer,
hasHover,
});

React.useEffect(() => {
setAdaptivity(
const adaptivity = React.useMemo(
() =>
calculateAdaptivity(
{
viewWidth,
Expand All @@ -43,8 +34,8 @@ export const AdaptivityProvider = ({
},
bridge,
),
);
}, [viewWidth, viewHeight, sizeX, sizeY, hasPointer, hasHover, bridge]);
[viewWidth, viewHeight, sizeX, sizeY, hasPointer, hasHover, bridge],
);

return <AdaptivityContext.Provider value={adaptivity}>{children}</AdaptivityContext.Provider>;
};
Expand Down

0 comments on commit 6fd6a4f

Please sign in to comment.