Skip to content

Commit

Permalink
Data Module: Expose State using a GraphQL API
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Dec 19, 2017
1 parent 0257138 commit 60249eb
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 47 deletions.
68 changes: 67 additions & 1 deletion data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
* External dependencies
*/
import { createStore, combineReducers } from 'redux';
import { flowRight } from 'lodash';
import { flowRight, merge } from 'lodash';
import { ApolloLink, Observable, execute as executeLink } from 'apollo-link';
import { execute } from 'graphql';
import { makeExecutableSchema } from 'graphql-tools';

/**
* Internal dependencies
*/
import createQueryHigherOrderComponent from './query';

/**
* Module constants
Expand Down Expand Up @@ -32,3 +40,61 @@ export const subscribe = store.subscribe;
export const dispatch = store.dispatch;

export const getState = store.getState;

const schemas = [ `
type Query {
hello: String
}
` ];

const resolvers = [ {
Query: { hello: () => 'dolly' },
} ];

let schema = makeExecutableSchema( {
typeDefs: schemas[ 0 ],
resolvers: resolvers[ 0 ],
} );

/**
* Registers a sub GraphQL schema
*
* @param {String} registeredSchema The GraphQL schema to register
* @param {Object} registeredResolver The GraphQL resolver to register for this schema
*/
export function registerSchema( registeredSchema, registeredResolver ) {
schemas.push( registeredSchema );
resolvers.push( registeredResolver );
schema = makeExecutableSchema( {
typeDefs: schemas,
resolvers: merge( ...resolvers ),
} );
}

const graphLink = new ApolloLink( ( operation ) => {
return new Observable( observer => {
return store.subscribe( () => {
const result = execute(
schema,
operation.query,
null,
{ state: store.getState() },
operation.variables,
operation.operationName
);
if ( result.data || result.errors ) {
observer.next( result );
} else {
result.then( observer.next.bind( observer ) );
}
} );
} );
} );

const client = {
query: ( operation ) => {
return executeLink( graphLink, operation );
},
};

export const query = createQueryHigherOrderComponent( client );
66 changes: 66 additions & 0 deletions data/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* External Dependencies
*/
import { isFunction } from 'lodash';

/**
* WordPress Dependencies
*/
import { Component } from '@wordpress/element';

const createQueryHigherOrderComponent = ( client ) => ( mapPropsToQuery, mapPropsToVariables = () => ( {} ) ) => ( WrappedComponent ) => {
return class GraphQueryComponent extends Component {
constructor() {
super( ...arguments );
this.state = {
data: null, errors: null,
};
}

componentDidMount() {
this.buildQuery( this.props );
this.request();
}

componentWillUnmount() {
this.cancelRequest();
}

componentWillReceiveProps( newProps ) {
this.buildQuery( newProps );
this.request();
}

buildQuery( props ) {
if ( isFunction( mapPropsToQuery ) ) {
this.query = mapPropsToQuery( props );
} else {
this.query = mapPropsToQuery;
}
this.variables = mapPropsToVariables( props );
}

cancelRequest() {
if ( this.unsubscribe ) {
this.unsubscribe();
}
}

request() {
this.cancelRequest();
const query = client.query( { query: this.query, variables: this.variables } );
const observer = query.subscribe( ( results ) => {
this.setState( results );
} );
this.unsubscribe = observer.unsubscribe;
}

render() {
return (
<WrappedComponent { ...this.props } { ...this.state } />
);
}
};
};

export default createQueryHigherOrderComponent;
35 changes: 35 additions & 0 deletions editor/store/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Internal Dependencies
*/
import { getEditedPostTitle } from './selectors';

export const schema = `
type CoreEditedPost {
title: String
}
type CoreEditor {
post: CoreEditedPost
}
extend type Query {
editor: CoreEditor
}
`;

export const resolver = {
Query: {
editor: ( _, args, context ) => ( {
post: () => ( {
title() {
const state = context.state[ 'core/editor' ];
return getEditedPostTitle( state );
},
} ),
} ),
},

CoreEditor: editor => editor,

CoreEditedPost: post => post,
};
4 changes: 3 additions & 1 deletion editor/store/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* WordPress Dependencies
*/
import { registerReducer } from '@wordpress/data';
import { registerReducer, registerSchema } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -11,13 +11,15 @@ import reducer from './reducer';
import { withRehydratation, loadAndPersist } from './persist';
import enhanceWithBrowserSize from './browser';
import store from './store';
import { schema, resolver } from './data';

/**
* Module Constants
*/
const STORAGE_KEY = `GUTENBERG_PREFERENCES_${ window.userSettings.uid }`;

registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) );
registerSchema( schema, resolver );
loadAndPersist( store, 'preferences', STORAGE_KEY, PREFERENCES_DEFAULTS );
enhanceWithBrowserSize( store );

Expand Down
2 changes: 1 addition & 1 deletion lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function gutenberg_register_scripts_and_styles() {
wp_register_script(
'wp-data',
gutenberg_url( 'data/build/index.js' ),
array(),
array( 'wp-element' ),
filemtime( gutenberg_dir_path() . 'data/build/index.js' )
);
wp_register_script(
Expand Down
Loading

0 comments on commit 60249eb

Please sign in to comment.