Skip to content

Commit

Permalink
Add mechanism to set a width on withViewportMatch (#17085)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta committed Dec 11, 2019
1 parent 620d929 commit 255da17
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 20 deletions.
2 changes: 1 addition & 1 deletion packages/compose/README.md
Expand Up @@ -125,7 +125,7 @@ Runs a media query and returns its value when it changes.

_Parameters_

- _query_ `string`: Media Query.
- _query_ `[string]`: Media Query.

_Returns_

Expand Down
7 changes: 5 additions & 2 deletions packages/compose/src/hooks/use-media-query/index.js
Expand Up @@ -6,12 +6,15 @@ import { useState, useEffect } from '@wordpress/element';
/**
* Runs a media query and returns its value when it changes.
*
* @param {string} query Media Query.
* @param {string} [query] Media Query.
* @return {boolean} return value of the media query.
*/
export default function useMediaQuery( query ) {
const [ match, setMatch ] = useState( false );
useEffect( () => {
if ( ! query ) {
return;
}
const updateMatch = () => setMatch( window.matchMedia( query ).matches );
updateMatch();
const list = window.matchMedia( query );
Expand All @@ -21,5 +24,5 @@ export default function useMediaQuery( query ) {
};
}, [ query ] );

return match;
return query && match;
}
21 changes: 21 additions & 0 deletions packages/compose/src/hooks/use-media-query/test/index.js
Expand Up @@ -88,4 +88,25 @@ describe( 'useMediaQuery', () => {
} );
expect( removeListener ).toHaveBeenCalled();
} );

it( 'should not call matchMedia if a query is not passed', async () => {
global.matchMedia.mockReturnValue( { addListener, removeListener, matches: false } );
let root;
await act( async () => {
root = create( <TestComponent /> );
} );
expect( root.toJSON() ).toBe( 'useMediaQuery: undefined' );

await act( async () => {
root.update( <TestComponent query={ false } /> );
} );
expect( root.toJSON() ).toBe( 'useMediaQuery: false' );

await act( async () => {
root.unmount();
} );
expect( global.matchMedia ).not.toHaveBeenCalled();
expect( addListener ).not.toHaveBeenCalled();
expect( removeListener ).not.toHaveBeenCalled();
} );
} );
28 changes: 26 additions & 2 deletions packages/compose/src/hooks/use-viewport-match/index.js
@@ -1,3 +1,8 @@
/**
* WordPress dependencies
*/
import { createContext, useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
Expand Down Expand Up @@ -37,6 +42,18 @@ const CONDITIONS = {
'<': 'max-width',
};

/**
* Object mapping media query operators to a function that given a breakpointValue and a width evaluates if the operator matches the values.
*
* @type {Object<WPViewportOperator,Function>}
*/
const OPERATOR_EVALUATORS = {
'>=': ( breakpointValue, width ) => ( width >= breakpointValue ),
'<': ( breakpointValue, width ) => ( width < breakpointValue ),
};

const ViewportMatchWidthContext = createContext( null );

/**
* Returns true if the viewport matches the given query, or false otherwise.
*
Expand All @@ -53,8 +70,15 @@ const CONDITIONS = {
* @return {boolean} Whether viewport matches query.
*/
const useViewportMatch = ( breakpoint, operator = '>=' ) => {
const mediaQuery = `(${ CONDITIONS[ operator ] }: ${ BREAKPOINTS[ breakpoint ] }px)`;
return useMediaQuery( mediaQuery );
const simulatedWidth = useContext( ViewportMatchWidthContext );
const mediaQuery = ! simulatedWidth && `(${ CONDITIONS[ operator ] }: ${ BREAKPOINTS[ breakpoint ] }px)`;
const mediaQueryResult = useMediaQuery( mediaQuery );
if ( simulatedWidth ) {
return OPERATOR_EVALUATORS[ operator ]( BREAKPOINTS[ breakpoint ], simulatedWidth );
}
return mediaQueryResult;
};

useViewportMatch.__experimentalWidthProvider = ViewportMatchWidthContext.Provider;

export default useViewportMatch;
39 changes: 39 additions & 0 deletions packages/compose/src/hooks/use-viewport-match/test/index.js
Expand Up @@ -79,4 +79,43 @@ describe( 'useViewportMatch', () => {

root.unmount();
} );

it( 'should correctly simulate a value', async () => {
let root;
useMediaQueryMock.mockReturnValue( true );

const innerElement = <TestComponent breakpoint="wide" operator=">=" />;
const WidthProvider = useViewportMatch.__experimentalWidthProvider;

await act( async () => {
root = create( <WidthProvider value={ 300 }>{ innerElement }</WidthProvider> );
} );
expect( root.toJSON() ).toBe( 'useViewportMatch: false' );

await act( async () => {
root.update( <WidthProvider value={ 1200 }>{ innerElement }</WidthProvider> );
} );
expect( root.toJSON() ).toBe( 'useViewportMatch: false' );

await act( async () => {
root.update( <WidthProvider value={ 1300 }>{ innerElement }</WidthProvider> );
} );
expect( root.toJSON() ).toBe( 'useViewportMatch: true' );

await act( async () => {
root.update( <WidthProvider value={ 1300 }>
<TestComponent breakpoint="wide" operator="<" />
</WidthProvider> );
} );
expect( root.toJSON() ).toBe( 'useViewportMatch: false' );

expect( useMediaQueryMock.mock.calls ).toEqual( [
[ false ],
[ false ],
[ false ],
[ false ],
] );

root.unmount();
} );
} );
32 changes: 17 additions & 15 deletions packages/edit-post/src/editor.js
Expand Up @@ -15,7 +15,7 @@ import {
SlotFillProvider,
DropZoneProvider,
} from '@wordpress/components';
import { compose } from '@wordpress/compose';
import { compose, useViewportMatch } from '@wordpress/compose';

/**
* Internal dependencies
Expand Down Expand Up @@ -116,20 +116,22 @@ class Editor extends Component {
<EditPostSettings.Provider value={ settings }>
<SlotFillProvider>
<DropZoneProvider>
<EditorProvider
settings={ editorSettings }
post={ post }
initialEdits={ initialEdits }
useSubRegistry={ false }
{ ...props }
>
<ErrorBoundary onError={ onError }>
<EditorInitialization postId={ postId } />
<Layout />
<KeyboardShortcuts shortcuts={ preventEventDiscovery } />
</ErrorBoundary>
<PostLockedModal />
</EditorProvider>
<useViewportMatch.__experimentalWidthProvider value={ undefined }>
<EditorProvider
settings={ editorSettings }
post={ post }
initialEdits={ initialEdits }
useSubRegistry={ false }
{ ...props }
>
<ErrorBoundary onError={ onError }>
<EditorInitialization postId={ postId } />
<Layout />
<KeyboardShortcuts shortcuts={ preventEventDiscovery } />
</ErrorBoundary>
<PostLockedModal />
</EditorProvider>
</useViewportMatch.__experimentalWidthProvider>
</DropZoneProvider>
</SlotFillProvider>
</EditPostSettings.Provider>
Expand Down

0 comments on commit 255da17

Please sign in to comment.