Skip to content

Commit

Permalink
Refactor withViewportMatch to use useViewportMatch (#18950)
Browse files Browse the repository at this point in the history
* Refactor withViewportMatch to use useViewportMatch

* Update tests to mock useViewportMatch globally
  • Loading branch information
jorgefilipecosta committed Dec 7, 2019
1 parent 05de258 commit db52a91
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 23 deletions.
21 changes: 14 additions & 7 deletions packages/viewport/src/test/if-viewport-matches.js
Expand Up @@ -6,7 +6,7 @@ import TestRenderer, { act } from 'react-test-renderer';
/**
* WordPress dependencies
*/
import { dispatch } from '@wordpress/data';
import { useViewportMatch as useViewportMatchMock } from '@wordpress/compose';

/**
* Internal dependencies
Expand All @@ -17,27 +17,34 @@ import ifViewportMatches from '../if-viewport-matches';
describe( 'ifViewportMatches()', () => {
const Component = () => <div>Hello</div>;

afterEach( () => {
useViewportMatchMock.mockClear();
} );

it( 'should not render if query does not match', () => {
dispatch( 'core/viewport' ).setIsMatching( { '> wide': false } );
const EnhancedComponent = ifViewportMatches( '> wide' )( Component );
useViewportMatchMock.mockReturnValueOnce( false );
const EnhancedComponent = ifViewportMatches( '< wide' )( Component );

let testRenderer;
act( () => {
testRenderer = TestRenderer.create( <EnhancedComponent /> );
} );

expect( useViewportMatchMock.mock.calls ).toEqual( [ [ 'wide', '<' ] ] );

expect( testRenderer.root.findAllByType( Component ) ).toHaveLength( 0 );
} );

it( 'should render if query does match', () => {
act( () => {
dispatch( 'core/viewport' ).setIsMatching( { '> wide': true } );
} );
const EnhancedComponent = ifViewportMatches( '> wide' )( Component );
useViewportMatchMock.mockReturnValueOnce( true );
const EnhancedComponent = ifViewportMatches( '>= wide' )( Component );
let testRenderer;
act( () => {
testRenderer = TestRenderer.create( <EnhancedComponent /> );
} );

expect( useViewportMatchMock.mock.calls ).toEqual( [ [ 'wide', '>=' ] ] );

expect( testRenderer.root.findAllByType( Component ) ).toHaveLength( 1 );
} );
} );
35 changes: 29 additions & 6 deletions packages/viewport/src/test/with-viewport-match.js
Expand Up @@ -6,8 +6,8 @@ import renderer from 'react-test-renderer';
/**
* WordPress dependencies
*/
import { dispatch } from '@wordpress/data';
import { Component } from '@wordpress/element';
import { useViewportMatch as useViewportMatchMock } from '@wordpress/compose';

/**
* Internal dependencies
Expand All @@ -16,6 +16,10 @@ import '../store';
import withViewportMatch from '../with-viewport-match';

describe( 'withViewportMatch()', () => {
afterEach( () => {
useViewportMatchMock.mockClear();
} );

const ChildComponent = () => <div>Hello</div>;

// this is needed because TestUtils does not accept a stateless component.
Expand All @@ -30,14 +34,33 @@ describe( 'withViewportMatch()', () => {
};

it( 'should render with result of query as custom prop name', () => {
dispatch( 'core/viewport' ).setIsMatching( { '> wide': true } );
const EnhancedComponent = withViewportMatch(
{ isWide: '> wide' }
const EnhancedComponent = withViewportMatch( {
isWide: '>= wide',
isSmall: '>= small',
isLarge: 'large',
isLessThanSmall: '< small',
}
)( ChildComponent );

useViewportMatchMock.mockReturnValueOnce( false );
useViewportMatchMock.mockReturnValueOnce( true );
useViewportMatchMock.mockReturnValueOnce( true );
useViewportMatchMock.mockReturnValueOnce( false );

const wrapper = renderer.create( getTestComponent( EnhancedComponent ) );

expect( wrapper.root.findByType( ChildComponent ).props.isWide )
.toBe( true );
expect( useViewportMatchMock.mock.calls ).toEqual( [
[ 'wide', '>=' ],
[ 'small', '>=' ],
[ 'large', '>=' ],
[ 'small', '<' ],
] );

const { props } = wrapper.root.findByType( ChildComponent );
expect( props.isWide ).toBe( false );
expect( props.isSmall ).toBe( true );
expect( props.isLarge ).toBe( true );
expect( props.isLessThanSmall ).toBe( false );

wrapper.unmount();
} );
Expand Down
39 changes: 29 additions & 10 deletions packages/viewport/src/with-viewport-match.js
Expand Up @@ -6,8 +6,7 @@ import { mapValues } from 'lodash';
/**
* WordPress dependencies
*/
import { createHigherOrderComponent } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';
import { createHigherOrderComponent, pure, useViewportMatch } from '@wordpress/compose';

/**
* Higher-order component creator, creating a new component which renders with
Expand All @@ -32,13 +31,33 @@ import { withSelect } from '@wordpress/data';
*
* @return {Function} Higher-order component.
*/
const withViewportMatch = ( queries ) => createHigherOrderComponent(
withSelect( ( select ) => {
return mapValues( queries, ( query ) => {
return select( 'core/viewport' ).isViewportMatch( query );
} );
} ),
'withViewportMatch'
);
const withViewportMatch = ( queries ) => {
const useViewPortQueriesResult = () => mapValues( queries, ( query ) => {
let [ operator, breakpointName ] = query.split( ' ' );
if ( breakpointName === undefined ) {
breakpointName = operator;
operator = '>=';
}
// Hooks should unconditionally execute in the same order,
// we are respecting that as from the static query of the HOC we generate
// a hook that calls other hooks always in the same order (because the query never changes).
// eslint-disable-next-line react-hooks/rules-of-hooks
return useViewportMatch( breakpointName, operator );
} );
return createHigherOrderComponent(
( WrappedComponent ) => {
return pure( ( props ) => {
const queriesResult = useViewPortQueriesResult();
return (
<WrappedComponent
{ ...props }
{ ...queriesResult }
/>
);
} );
},
'withViewportMatch'
);
};

export default withViewportMatch;
6 changes: 6 additions & 0 deletions test/unit/config/global-mocks.js
@@ -0,0 +1,6 @@
jest.mock( '@wordpress/compose', () => {

This comment has been minimized.

Copy link
@gziolo

gziolo Dec 10, 2019

Member

It runs during the setup. Is it expected that it gets mocked for all tests and it won't be possible to use the original function? The recommended way of mocking is documented at https://jestjs.io/docs/en/manual-mocks/

return {
...jest.requireActual( '@wordpress/compose' ),
useViewportMatch: jest.fn(),
};
} );
1 change: 1 addition & 0 deletions test/unit/jest.config.js
Expand Up @@ -14,6 +14,7 @@ module.exports = {
},
preset: '@wordpress/jest-preset-default',
setupFiles: [
'<rootDir>/test/unit/config/global-mocks.js',
'<rootDir>/test/unit/config/gutenberg-phase.js',
'<rootDir>/test/unit/config/register-context.js',
],
Expand Down

0 comments on commit db52a91

Please sign in to comment.