Skip to content

Commit

Permalink
Framework: Add viewport module (data module experiments, proof-of-con…
Browse files Browse the repository at this point in the history
…cept) (#5206)

* Testing: Fix typo in post editor source lint rule

* Components: Add ifCondition higher-order component

* Data: Add registerStore convenience registration

* Framework: Add viewport module

* Post Editor: Port editor to use viewport module

* Viewport: Split withViewportMatch from ifViewportMatches

* Edit Post: Remove unused viewportType preference
  • Loading branch information
aduth authored Feb 27, 2018
1 parent 9162e4c commit 09f0f92
Show file tree
Hide file tree
Showing 37 changed files with 612 additions and 264 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,13 @@
"message": "Use @wordpress/utils as import path instead."
},
{
"selector": "ImportDeclaration[source.value=/^edit-poost$/]",
"selector": "ImportDeclaration[source.value=/^edit-post$/]",
"message": "Use @wordpress/edit-post as import path instead."
},
{
"selector": "ImportDeclaration[source.value=/^viewport$/]",
"message": "Use @wordpress/viewport as import path instead."
},
{
"selector": "CallExpression[callee.name=/^__|_n|_x$/]:not([arguments.0.type=/^Literal|BinaryExpression$/])",
"message": "Translate function arguments must be string literals."
Expand Down
2 changes: 1 addition & 1 deletion bin/build-plugin-zip.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ zip -r gutenberg.zip \
blocks/library/*/*.php \
post-content.js \
$vendor_scripts \
{blocks,components,date,editor,element,hooks,i18n,data,utils,edit-post}/build/*.{js,map} \
{blocks,components,date,editor,element,hooks,i18n,data,utils,edit-post,viewport}/build/*.{js,map} \
{blocks,components,editor,edit-post}/build/*.css \
languages/gutenberg.pot \
README.md
Expand Down
20 changes: 20 additions & 0 deletions components/higher-order/if-condition/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
If Condition
============

`ifCondition` is a higher-order component creator, used for creating a new component which renders if the given condition is satisfied.

## Usage

`ifCondition`, passed with a predicate function, will render the underlying component only if the predicate returns a truthy value. The predicate is passed the component's own original props as an argument.

```jsx
function MyEvenNumber( { number } ) {
// This is only reached if the `number` prop is even. Otherwise, nothing
// will be rendered.
return <strong>{ number }</strong>;
}

MyEvenNumber = ifCondition(
( { number } ) => number % 2 === 0
)( MyEvenNumber );
```
28 changes: 28 additions & 0 deletions components/higher-order/if-condition/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* WordPress dependencies
*/
import { getWrapperDisplayName } from '@wordpress/element';

/**
* Higher-order component creator, creating a new component which renders if
* the given condition is satisfied or with the given optional prop name.
*
* @param {Function} predicate Function to test condition.
*
* @return {Function} Higher-order component.
*/
const ifCondition = ( predicate ) => ( WrappedComponent ) => {
const EnhancedComponent = ( props ) => {
if ( ! predicate( props ) ) {
return null;
}

return <WrappedComponent { ...props } />;
};

EnhancedComponent.displayName = getWrapperDisplayName( WrappedComponent, 'ifCondition' );

return EnhancedComponent;
};

export default ifCondition;
1 change: 1 addition & 0 deletions components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export { default as TreeSelect } from './tree-select';
export { Slot, Fill, Provider as SlotFillProvider } from './slot-fill';

// Higher-Order Components
export { default as ifCondition } from './higher-order/if-condition';
export { default as navigateRegions } from './higher-order/navigate-regions';
export { default as withAPIData } from './higher-order/with-api-data';
export { default as withContext } from './higher-order/with-context';
Expand Down
26 changes: 26 additions & 0 deletions data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ export function globalListener() {
listeners.forEach( listener => listener() );
}

/**
* Convenience for registering reducer with actions and selectors.
*
* @param {string} reducerKey Reducer key.
* @param {Object} options Store description (reducer, actions, selectors).
*
* @return {Object} Registered store object.
*/
export function registerStore( reducerKey, options ) {
if ( ! options.reducer ) {
throw new TypeError( 'Must specify store reducer' );
}

const store = registerReducer( reducerKey, options.reducer );

if ( options.actions ) {
registerActions( reducerKey, options.actions );
}

if ( options.selectors ) {
registerSelectors( reducerKey, options.selectors );
}

return store;
}

/**
* Registers a new sub-reducer to the global state and returns a Redux-like store object.
*
Expand Down
36 changes: 35 additions & 1 deletion data/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { compose } from '@wordpress/element';
* Internal dependencies
*/
import {
registerStore,
registerReducer,
registerSelectors,
registerActions,
Expand All @@ -22,7 +23,40 @@ import {
subscribe,
} from '../';

describe( 'store', () => {
describe( 'registerStore', () => {
it( 'should be shorthand for reducer, actions, selectors registration', () => {
const store = registerStore( 'butcher', {
reducer( state = { ribs: 6, chicken: 4 }, action ) {
switch ( action.type ) {
case 'sale':
return {
...state,
[ action.meat ]: state[ action.meat ] / 2,
};
}

return state;
},
selectors: {
getPrice: ( state, meat ) => state[ meat ],
},
actions: {
startSale: ( meat ) => ( { type: 'sale', meat } ),
},
} );

expect( store.getState() ).toEqual( { ribs: 6, chicken: 4 } );
expect( dispatch( 'butcher' ) ).toHaveProperty( 'startSale' );
expect( select( 'butcher' ) ).toHaveProperty( 'getPrice' );
expect( select( 'butcher' ).getPrice( 'chicken' ) ).toBe( 4 );
expect( select( 'butcher' ).getPrice( 'ribs' ) ).toBe( 6 );
dispatch( 'butcher' ).startSale( 'chicken' );
expect( select( 'butcher' ).getPrice( 'chicken' ) ).toBe( 2 );
expect( select( 'butcher' ).getPrice( 'ribs' ) ).toBe( 6 );
} );
} );

describe( 'registerReducer', () => {
it( 'Should append reducers to the state', () => {
const reducer1 = () => 'chicken';
const reducer2 = () => 'ribs';
Expand Down
40 changes: 16 additions & 24 deletions edit-post/components/header/fixed-toolbar-toggle/index.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,41 @@
/**
* External Dependencies
* WordPress Dependencies
*/
import { connect } from 'react-redux';
import { withSelect, withDispatch } from '@wordpress/data';

/**
* WordPress Dependencies
*/
import { __ } from '@wordpress/i18n';
import { compose } from '@wordpress/element';
import { MenuItemsGroup, MenuItemsToggle, withInstanceId } from '@wordpress/components';
import { ifViewportMatches } from '@wordpress/viewport';

/**
* Internal Dependencies
*/
import { hasFixedToolbar, isMobile } from '../../../store/selectors';
import { toggleFeature } from '../../../store/actions';

function FeatureToggle( { onToggle, active, onMobile } ) {
if ( onMobile ) {
return null;
}
function FeatureToggle( { onToggle, isActive } ) {
return (
<MenuItemsGroup
label={ __( 'Settings' ) }
filterName="editPost.MoreMenu.settings"
>
<MenuItemsToggle
label={ __( 'Fix Toolbar to Top' ) }
isSelected={ active }
isSelected={ isActive }
onClick={ onToggle }
/>
</MenuItemsGroup>
);
}

export default connect(
( state ) => ( {
active: hasFixedToolbar( state ),
onMobile: isMobile( state ),
} ),
( dispatch, ownProps ) => ( {
export default compose( [
withSelect( ( select ) => ( {
isActive: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
} ) ),
withDispatch( ( dispatch, ownProps ) => ( {
onToggle() {
dispatch( toggleFeature( 'fixedToolbar' ) );
dispatch( 'core/edit-post' ).toggleFeature( 'fixedToolbar' );
ownProps.onToggle();
},
} ),
undefined,
{ storeKey: 'edit-post' }
)( withInstanceId( FeatureToggle ) );
} ) ),
ifViewportMatches( 'medium' ),
withInstanceId,
] )( FeatureToggle );
25 changes: 12 additions & 13 deletions edit-post/components/header/header-toolbar/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/**
* External dependencies
* WordPress dependencies
*/
import { connect } from 'react-redux';
import { compose } from '@wordpress/element';
import { withSelect } from '@wordpress/data';
import { withViewportMatch } from '@wordpress/viewport';

/**
* WordPress dependencies
Expand All @@ -21,9 +23,8 @@ import {
* Internal dependencies
*/
import './style.scss';
import { hasFixedToolbar } from '../../../store/selectors';

function HeaderToolbar( { fixedToolbarActive } ) {
function HeaderToolbar( { hasFixedToolbar, isLargeViewport } ) {
return (
<NavigableToolbar
className="edit-post-header-toolbar"
Expand All @@ -34,7 +35,7 @@ function HeaderToolbar( { fixedToolbarActive } ) {
<EditorHistoryRedo />
<TableOfContents />
<MultiBlocksSwitcher />
{ fixedToolbarActive && (
{ hasFixedToolbar && isLargeViewport && (
<div className="edit-post-header-toolbar__block-toolbar">
<BlockToolbar />
</div>
Expand All @@ -43,11 +44,9 @@ function HeaderToolbar( { fixedToolbarActive } ) {
);
}

export default connect(
( state ) => ( {
fixedToolbarActive: hasFixedToolbar( state ),
} ),
undefined,
undefined,
{ storeKey: 'edit-post' }
)( HeaderToolbar );
export default compose( [
withSelect( ( select ) => ( {
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
} ) ),
withViewportMatch( { isLargeViewport: 'medium' } ),
] )( HeaderToolbar );
30 changes: 11 additions & 19 deletions edit-post/components/visual-editor/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';

/**
* WordPress dependencies
*/
Expand All @@ -15,16 +10,17 @@ import {
BlockSelectionClearer,
MultiSelectScrollIntoView,
} from '@wordpress/editor';
import { Fragment } from '@wordpress/element';
import { Fragment, compose } from '@wordpress/element';
import { withSelect } from '@wordpress/data';
import { withViewportMatch } from '@wordpress/viewport';

/**
* Internal dependencies
*/
import './style.scss';
import BlockInspectorButton from './block-inspector-button';
import { hasFixedToolbar } from '../../store/selectors';

function VisualEditor( props ) {
function VisualEditor( { hasFixedToolbar, isLargeViewport } ) {
return (
<BlockSelectionClearer className="edit-post-visual-editor">
<EditorGlobalKeyboardShortcuts />
Expand All @@ -33,7 +29,7 @@ function VisualEditor( props ) {
<WritingFlow>
<PostTitle />
<BlockList
showContextualToolbar={ ! props.hasFixedToolbar }
showContextualToolbar={ isLargeViewport && ! hasFixedToolbar }
renderBlockMenu={ ( { children, onClose } ) => (
<Fragment>
<BlockInspectorButton onClick={ onClose } />
Expand All @@ -46,13 +42,9 @@ function VisualEditor( props ) {
);
}

export default connect(
( state ) => {
return {
hasFixedToolbar: hasFixedToolbar( state ),
};
},
undefined,
undefined,
{ storeKey: 'edit-post' }
)( VisualEditor );
export default compose( [
withSelect( ( select ) => ( {
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
} ) ),
withViewportMatch( { isLargeViewport: 'medium' } ),
] )( VisualEditor );
13 changes: 0 additions & 13 deletions edit-post/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,6 @@ export function toggleGeneralSidebarEditorPanel( panel ) {
};
}

/**
* Returns an action object used in signalling that the viewport type preference should be set.
*
* @param {string} viewportType The viewport type (desktop or mobile).
* @return {Object} Action object.
*/
export function setViewportType( viewportType ) {
return {
type: 'SET_VIEWPORT_TYPE',
viewportType,
};
}

/**
* Returns an action object used to toggle a feature flag.
*
Expand Down
11 changes: 0 additions & 11 deletions edit-post/store/constants.js

This file was deleted.

1 change: 0 additions & 1 deletion edit-post/store/defaults.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export const PREFERENCES_DEFAULTS = {
editorMode: 'visual',
viewportType: 'desktop', // 'desktop' | 'mobile'
activeGeneralSidebar: 'editor', // null | 'editor' | 'plugin'
activeSidebarPanel: { // The keys in this object should match activeSidebarPanel values
editor: null, // 'document' | 'block'
Expand Down
Loading

0 comments on commit 09f0f92

Please sign in to comment.