@apollo-elements/mixins
Advanced tools
Comparing version 1.0.0 to 1.0.2
@@ -10,83 +10,89 @@ import getGraphQLScriptChildDocument from '@apollo-elements/lib/get-graphql-script-child-document'; | ||
* | ||
* @param {Class} superclass | ||
* @return {Class} | ||
* @param {*} superclass the class to mix into | ||
* @return {ApolloElement~mixin} The mixed class | ||
*/ | ||
export const ApolloElementMixin = superclass => class extends superclass { | ||
constructor() { | ||
super(); | ||
this.onElementMutation = this.onElementMutation.bind(this); | ||
export const ApolloElementMixin = superclass => | ||
/** | ||
* Class mixin for apollo elements | ||
* @mixin | ||
* @alias ApolloElementMixin~mixin | ||
*/ | ||
class extends superclass { | ||
constructor() { | ||
super(); | ||
this.onElementMutation = this.onElementMutation.bind(this); | ||
/** | ||
* Context to be passed to link execution chain. | ||
* @type {Object} | ||
*/ | ||
this.context; | ||
/** | ||
* Context to be passed to link execution chain. | ||
* @type {Object} | ||
*/ | ||
this.context; | ||
/** | ||
* Latest data. | ||
* @type {Object} | ||
*/ | ||
this.data; | ||
/** | ||
* Latest data. | ||
* @type {Object} | ||
*/ | ||
this.data; | ||
/** | ||
* Latest error. | ||
* @type {Object} | ||
*/ | ||
this.error; | ||
/** | ||
* Latest error. | ||
* @type {Object} | ||
*/ | ||
this.error; | ||
/** | ||
* Whether a request is in flight. | ||
* @type {Boolean} | ||
*/ | ||
this.loading; | ||
/** | ||
* Whether a request is in flight. | ||
* @type {Boolean} | ||
*/ | ||
this.loading; | ||
/** | ||
* The apollo client instance. | ||
* @type {ApolloClient} | ||
*/ | ||
this.client = window.__APOLLO_CLIENT__; | ||
} | ||
/** | ||
* The apollo client instance. | ||
* @type {ApolloClient} | ||
* GraphQL Document | ||
* | ||
* @return {DocumentNode} | ||
*/ | ||
this.client = window.__APOLLO_CLIENT__; | ||
} | ||
get document() { | ||
return this.__document || null; | ||
} | ||
/** | ||
* GraphQL Document | ||
* | ||
* @return {DocumentNode} | ||
*/ | ||
get document() { | ||
return this.__document || null; | ||
} | ||
set document(doc) { | ||
if (!doc) return; | ||
if (isValidGql(doc)) { | ||
this.__document = doc; | ||
} else { | ||
throw new TypeError('document must be a gql-parsed DocumentNode'); | ||
} | ||
} | ||
set document(doc) { | ||
if (!doc) return; | ||
if (isValidGql(doc)) { | ||
this.__document = doc; | ||
} else { | ||
throw new TypeError('document must be a gql-parsed DocumentNode'); | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback && super.connectedCallback(); | ||
this.elementMutationObserver = new MutationObserver(this.onElementMutation); | ||
this.elementMutationObserver.observe(this, { | ||
characterData: true, | ||
childList: true, | ||
subtree: true, | ||
}); | ||
this.document = getGraphQLScriptChildDocument(this) || null; | ||
} | ||
} | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback && super.connectedCallback(); | ||
this.elementMutationObserver = new MutationObserver(this.onElementMutation); | ||
this.elementMutationObserver.observe(this, { | ||
characterData: true, | ||
childList: true, | ||
subtree: true, | ||
}); | ||
this.document = getGraphQLScriptChildDocument(this) || null; | ||
} | ||
/** @protected */ | ||
disconnectedCallback() { | ||
super.disconnectedCallback && super.disconnectedCallback(); | ||
this.elementMutationObserver && this.elementMutationObserver.disconnect(); | ||
this.elementMutationObserver = null; | ||
} | ||
/** @protected */ | ||
disconnectedCallback() { | ||
super.disconnectedCallback && super.disconnectedCallback(); | ||
this.elementMutationObserver && this.elementMutationObserver.disconnect(); | ||
this.elementMutationObserver = null; | ||
} | ||
/** @protected */ | ||
onElementMutation() { | ||
const doc = getGraphQLScriptChildDocument(this); | ||
if (doc) this.document = doc; | ||
} | ||
}; | ||
/** @protected */ | ||
onElementMutation() { | ||
const doc = getGraphQLScriptChildDocument(this); | ||
if (doc) this.document = doc; | ||
} | ||
}; |
@@ -7,2 +7,8 @@ import { ApolloElementMixin } from './apollo-element-mixin'; | ||
/** @typedef {import('apollo-client').ErrorPolicy} ErrorPolicy */ | ||
/** @typedef {import('apollo-client').FetchPolicy} FetchPolicy */ | ||
/** @typedef {import('apollo-client').MutationUpdaterFn} MutationUpdaterFn */ | ||
/** @typedef {import('apollo-link').FetchResult} FetchResult */ | ||
/** @typedef {import('graphql').DocumentNode} DocumentNode */ | ||
const pickOptions = compose( | ||
@@ -30,195 +36,205 @@ stripUndefinedValues, | ||
* | ||
* @param {Class} superclass | ||
* @return {Class} | ||
* @param {*} superclass the class to mix into | ||
* @return {ApolloMutationMixin~mixin} the mixed class | ||
*/ | ||
export const ApolloMutationMixin = superclass => class extends ApolloElementMixin(superclass) { | ||
export const ApolloMutationMixin = superclass => | ||
/** | ||
* The mutation. | ||
* | ||
* @return {DocumentNode} | ||
* Class mixin for apollo-mutation elements | ||
* @mixin | ||
* @alias ApolloMutationMixin~mixin | ||
*/ | ||
get mutation() { | ||
return this.document; | ||
} | ||
class extends ApolloElementMixin(superclass) { | ||
/** | ||
* The mutation. | ||
* | ||
* @return {DocumentNode} | ||
*/ | ||
get mutation() { | ||
return this.document; | ||
} | ||
set mutation(mutation) { | ||
try { | ||
this.document = mutation; | ||
} catch (error) { | ||
throw new TypeError('Mutation must be a gql-parsed DocumentNode'); | ||
set mutation(mutation) { | ||
try { | ||
this.document = mutation; | ||
} catch (error) { | ||
throw new TypeError('Mutation must be a gql-parsed DocumentNode'); | ||
} | ||
} | ||
} | ||
constructor() { | ||
super(); | ||
constructor() { | ||
super(); | ||
/** | ||
* Whether the mutation has been called | ||
* @type {Boolean} | ||
*/ | ||
this.called = false; | ||
/** | ||
* Whether the mutation has been called | ||
* @type {Boolean} | ||
*/ | ||
this.called = false; | ||
/** | ||
* Whether to ignore the results of the mutation. | ||
* @type {Boolean} | ||
*/ | ||
this.ignoreResults = false; | ||
/** | ||
* Whether to ignore the results of the mutation. | ||
* @type {Boolean} | ||
*/ | ||
this.ignoreResults = false; | ||
/** | ||
* The ID number of the most recent mutation since the element was instantiated. | ||
* @type {Number} | ||
*/ | ||
this.mostRecentMutationId = 0; | ||
/** | ||
* The ID number of the most recent mutation since the element was instantiated. | ||
* @type {Number} | ||
*/ | ||
this.mostRecentMutationId = 0; | ||
/** | ||
* An object that represents the result of this mutation that | ||
* will be optimistically stored before the server has actually returned a | ||
* result. | ||
* | ||
* This is most often used for optimistic UI, where we want to be able to see | ||
* the result of a mutation immediately, and update the UI later if any errors | ||
* appear. | ||
* @type {Object} | ||
*/ | ||
this.optimisticResponse; | ||
/** | ||
* A function which updates the apollo cache when the query responds. | ||
* This function will be called twice over the lifecycle of a mutation. | ||
* Once at the very beginning if an optimisticResponse was provided. | ||
* The writes created from the optimistic data will be rolled back before | ||
* the second time this function is called which is when the mutation has | ||
* succesfully resolved. At that point update will be called with the actual | ||
* mutation result and those writes will not be rolled back. | ||
* | ||
* The reason a DataProxy is provided instead of the user calling the methods | ||
* directly on ApolloClient is that all of the writes are batched together at | ||
* the end of the update, and it allows for writes generated by optimistic | ||
* data to be rolled back. | ||
* @type {MutationUpdaterFn} | ||
*/ | ||
this.update; | ||
/** | ||
* An object that maps from the name of a variable as used in the mutation GraphQL document to that variable's value. | ||
* @type {Object} | ||
*/ | ||
this.variables; | ||
/** | ||
* @protected | ||
* @type {any} | ||
*/ | ||
this.__explicitlySetMutation; | ||
} | ||
/** | ||
* An object that represents the result of this mutation that | ||
* will be optimistically stored before the server has actually returned a | ||
* result. | ||
* Increments and returns the most recent mutation id. | ||
* | ||
* This is most often used for optimistic UI, where we want to be able to see | ||
* the result of a mutation immediately, and update the UI later if any errors | ||
* appear. | ||
* @type {Object} | ||
* @return {number} | ||
* @protected | ||
*/ | ||
this.optimisticResponse; | ||
generateMutationId() { | ||
this.mostRecentMutationId += 1; | ||
return this.mostRecentMutationId; | ||
} | ||
/** | ||
* A function which updates the apollo cache when the query responds. | ||
* This function will be called twice over the lifecycle of a mutation. | ||
* Once at the very beginning if an optimisticResponse was provided. | ||
* The writes created from the optimistic data will be rolled back before | ||
* the second time this function is called which is when the mutation has | ||
* succesfully resolved. At that point update will be called with the actual | ||
* mutation result and those writes will not be rolled back. | ||
* Returns true when an ID matches the most recent mutation id. | ||
* | ||
* The reason a DataProxy is provided instead of the user calling the methods | ||
* directly on ApolloClient is that all of the writes are batched together at | ||
* the end of the update, and it allows for writes generated by optimistic | ||
* data to be rolled back. | ||
* @type {MutationUpdaterFn} | ||
* @param {number} mutationId | ||
* @return {boolean} | ||
* @protected | ||
*/ | ||
this.update; | ||
isMostRecentMutation(mutationId) { | ||
return this.mostRecentMutationId === mutationId; | ||
} | ||
/** | ||
* An object that maps from the name of a variable as used in the mutation GraphQL document to that variable's value. | ||
* @type {Object} | ||
* This resolves a single mutation according to the options specified and returns a Promise which is either resolved with the resulting data or rejected with an error. | ||
* | ||
* @param {Object} options | ||
* @param {Object} options.context | ||
* @param {ErrorPolicy} options.errorPolicy | ||
* @param {FetchPolicy} options.fetchPolicy | ||
* @param {DocumentNode} options.mutation | ||
* @param {Object|Function} options.optimisticResponse | ||
* @param {Array<DocumentNode>} options.refetchQueries | ||
* @param {MutationUpdaterFn} options.update | ||
* @param {boolean} options.awaitRefetchQueries | ||
* @param {Object} options.variables | ||
* @return {Promise<FetchResult>} | ||
*/ | ||
this.variables; | ||
async mutate({ | ||
mutation = this.mutation, | ||
variables = this.variables, | ||
...options | ||
} = this) { | ||
const mutationId = this.generateMutationId(); | ||
this.__explicitlySetMutation; | ||
} | ||
this.loading = true; | ||
this.error = undefined; | ||
this.data = undefined; | ||
this.called = true; | ||
/** | ||
* Increments and returns the most recent mutation id. | ||
* | ||
* @return {number} | ||
* @protected | ||
*/ | ||
generateMutationId() { | ||
this.mostRecentMutationId += 1; | ||
return this.mostRecentMutationId; | ||
} | ||
/** | ||
* Returns true when an ID matches the most recent mutation id. | ||
* | ||
* @param {number} mutationId | ||
* @return {boolean} | ||
* @protected | ||
*/ | ||
isMostRecentMutation(mutationId) { | ||
return this.mostRecentMutationId === mutationId; | ||
} | ||
/** | ||
* This resolves a single mutation according to the options specified and returns a Promise which is either resolved with the resulting data or rejected with an error. | ||
* | ||
* @param {Object} options | ||
* @param {Object} options.context | ||
* @param {ErrorPolicy} options.errorPolicy | ||
* @param {FetchPolicy} options.fetchPolicy | ||
* @param {DocumentNode} options.mutation | ||
* @param {Object|Function} options.optimisticResponse | ||
* @param {Array<DocumentNode>} options.refetchQueries | ||
* @param {UpdateFunction} options.update | ||
* @param {boolean} options.awaitRefetchQueries | ||
* @param {Object} options.variables | ||
* @return {Promise<FetchResult>} | ||
*/ | ||
async mutate({ | ||
mutation = this.mutation, | ||
variables = this.variables, | ||
...options | ||
} = this) { | ||
const mutationId = this.generateMutationId(); | ||
this.loading = true; | ||
this.error = undefined; | ||
this.data = undefined; | ||
this.called = true; | ||
try { | ||
const response = | ||
await this.client.mutate(pickOptions({ | ||
mutation, | ||
variables, | ||
...options, | ||
})); | ||
this.onCompletedMutation(response, mutationId); | ||
return response; | ||
} catch (error) { | ||
this.onMutationError(error, mutationId); | ||
return error; | ||
try { | ||
const response = | ||
await this.client.mutate(pickOptions({ | ||
mutation, | ||
variables, | ||
...options, | ||
})); | ||
this.onCompletedMutation(response, mutationId); | ||
return response; | ||
} catch (error) { | ||
this.onMutationError(error, mutationId); | ||
return error; | ||
} | ||
} | ||
} | ||
/** | ||
* Callback for when a mutation is completed. | ||
* | ||
* @param {FetchResult} response | ||
* @param {number} mutationId | ||
* @return {any} | ||
* @protected | ||
*/ | ||
onCompletedMutation(response, mutationId) { | ||
const { data, errors = [] } = response; | ||
const error = errors.length ? new ApolloError({ graphQLErrors: errors }) : null; | ||
if (this.isMostRecentMutation(mutationId) && !this.ignoreResults) { | ||
this.loading = false; | ||
this.error = error; | ||
this.data = data; | ||
/** | ||
* Callback for when a mutation is completed. | ||
* | ||
* @param {FetchResult} response | ||
* @param {number} mutationId | ||
* @return {any} | ||
* @protected | ||
*/ | ||
onCompletedMutation(response, mutationId) { | ||
const { data, errors = [] } = response; | ||
const error = errors.length ? new ApolloError({ graphQLErrors: errors }) : null; | ||
if (this.isMostRecentMutation(mutationId) && !this.ignoreResults) { | ||
this.loading = false; | ||
this.error = error; | ||
this.data = data; | ||
} | ||
return this.onCompleted(data); | ||
} | ||
return this.onCompleted(data); | ||
} | ||
/** | ||
* Callback for when a mutation fails. | ||
* | ||
* @param {Error} error mutation error | ||
* @param {number} mutationId id of the mutation | ||
* @return {any} | ||
* @protected | ||
*/ | ||
onMutationError(error, mutationId) { | ||
if (this.isMostRecentMutation(mutationId)) { | ||
this.loading = false; | ||
this.data = null; | ||
this.error = error; | ||
/** | ||
* Callback for when a mutation fails. | ||
* | ||
* @param {ApolloError} error mutation error | ||
* @param {number} mutationId id of the mutation | ||
* @return {any} | ||
* @protected | ||
*/ | ||
onMutationError(error, mutationId) { | ||
if (this.isMostRecentMutation(mutationId)) { | ||
this.loading = false; | ||
this.data = null; | ||
this.error = error; | ||
} | ||
return this.onError(error); | ||
} | ||
return this.onError(error); | ||
} | ||
/** | ||
* Callback for when a mutation is completed. | ||
* | ||
* @abstract | ||
*/ | ||
onCompleted() { } | ||
/** | ||
* Callback for when a mutation is completed. | ||
* | ||
* @abstract | ||
*/ | ||
onCompleted() { } | ||
/** | ||
* Callback for when an error occurs in mutation. | ||
* | ||
* @abstract | ||
*/ | ||
onError() { } | ||
}; | ||
/** | ||
* Callback for when an error occurs in mutation. | ||
* | ||
* @abstract | ||
*/ | ||
onError() { } | ||
}; |
@@ -7,2 +7,16 @@ import { ApolloElementMixin } from './apollo-element-mixin.js'; | ||
/** @typedef {import('apollo-client').ApolloError} ApolloError */ | ||
/** @typedef {import('apollo-client').ApolloQueryResult} ApolloQueryResult */ | ||
/** @typedef {import('apollo-client').ErrorPolicy} ErrorPolicy */ | ||
/** @typedef {import('apollo-client').FetchMoreQueryOptions} FetchMoreQueryOptions */ | ||
/** @typedef {import('apollo-client').FetchPolicy} FetchPolicy */ | ||
/** @typedef {import('apollo-client').ObservableQuery} ObservableQuery */ | ||
/** @typedef {import('apollo-client').QueryOptions} QueryOptions */ | ||
/** @typedef {import('apollo-client').SubscribeToMoreOptions} SubscribeToMoreOptions */ | ||
/** @typedef {import('apollo-client').SubscriptionOptions} SubscriptionOptions */ | ||
/** @typedef {import('apollo-client').WatchQueryOptions} WatchQueryOptions */ | ||
/** @typedef {import('apollo-client/core/watchQueryOptions').ModifiableWatchQueryOptions} ModifiableWatchQueryOptions */ | ||
/** @typedef {import('graphql').DocumentNode} DocumentNode */ | ||
/** @typedef {import('zen-observable')} Observable */ | ||
const pickExecuteQueryOpts = compose( | ||
@@ -43,283 +57,277 @@ stripUndefinedValues, | ||
* | ||
* @param {Class} superclass | ||
* @return {ApolloQuery} | ||
* @param {*} superclass | ||
* @return {ApolloQueryMixin~mixin} | ||
*/ | ||
export const ApolloQueryMixin = superclass => class extends ApolloElementMixin(superclass) { | ||
export const ApolloQueryMixin = superclass => | ||
/** | ||
* A GraphQL document containing a single query. | ||
* | ||
* @return {DocumentNode} | ||
* Class mixin for apollo-query elements | ||
* @mixin | ||
* @template TData | ||
* @template TVariables | ||
* @alias ApolloQueryMixin~mixin | ||
*/ | ||
get query() { | ||
return this.document; | ||
} | ||
class extends ApolloElementMixin(superclass) { | ||
/** | ||
* A GraphQL document containing a single query. | ||
* | ||
* @return {DocumentNode} | ||
*/ | ||
get query() { | ||
return this.document; | ||
} | ||
set query(query) { | ||
try { | ||
this.document = query; | ||
} catch (error) { | ||
throw new TypeError('Query must be a gql-parsed DocumentNode'); | ||
set query(query) { | ||
try { | ||
this.document = query; | ||
} catch (error) { | ||
throw new TypeError('Query must be a gql-parsed DocumentNode'); | ||
} | ||
if (!this.noAutoSubscribe && query) { | ||
this.subscribe({ query, variables: this.variables }); | ||
} | ||
} | ||
if (!this.noAutoSubscribe && query) { | ||
this.subscribe({ query, variables: this.variables }); | ||
/** | ||
* An object map from variable name to variable value, where the variables are used within the GraphQL query. | ||
* | ||
* @return {Object<string, *>} | ||
*/ | ||
get variables() { | ||
return this.__variables; | ||
} | ||
} | ||
/** | ||
* An object map from variable name to variable value, where the variables are used within the GraphQL query. | ||
* | ||
* @return {Object<string, *>} | ||
*/ | ||
get variables() { | ||
return this.__variables; | ||
} | ||
set variables(variables) { | ||
this.__variables = variables; | ||
const query = this.__query; | ||
this.observableQuery | ||
? this.setVariables(variables) | ||
: this.subscribe({ query, variables }); | ||
} | ||
set variables(variables) { | ||
this.__variables = variables; | ||
const query = this.__query; | ||
this.observableQuery | ||
? this.setVariables(variables) | ||
: this.subscribe({ query, variables }); | ||
} | ||
/** | ||
* Exposes the [`ObservableQuery#setOptions`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setOptions) method. | ||
* | ||
* @return {ModifiableWatchQueryOptions} options [options](https://www.apollographql.com/docs/react/api/apollo-client.html#ModifiableWatchQueryOptions) object. | ||
*/ | ||
get options() { | ||
return this.__options; | ||
} | ||
/** | ||
* Exposes the [`ObservableQuery#setOptions`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setOptions) method. | ||
* | ||
* @return {ModifiableWatchQueryOptions} options [options](https://www.apollographql.com/docs/react/api/apollo-client.html#ModifiableWatchQueryOptions) object. | ||
*/ | ||
get options() { | ||
return this.__options; | ||
} | ||
set options(options) { | ||
this.__options = options; | ||
this.observableQuery && this.observableQuery.setOptions(options); | ||
} | ||
set options(options) { | ||
this.__options = options; | ||
this.observableQuery && this.observableQuery.setOptions(options); | ||
} | ||
constructor() { | ||
super(); | ||
this.nextData = this.nextData.bind(this); | ||
this.nextError = this.nextError.bind(this); | ||
constructor() { | ||
super(); | ||
this.nextData = this.nextData.bind(this); | ||
this.nextError = this.nextError.bind(this); | ||
/** | ||
* Specifies the ErrorPolicy to be used for this query. | ||
* @type {ErrorPolicy} | ||
*/ | ||
this.errorPolicy = 'none'; | ||
/** | ||
* Specifies the ErrorPolicy to be used for this query. | ||
* @type {ErrorPolicy} | ||
*/ | ||
this.errorPolicy = 'none'; | ||
/** | ||
* Specifies the FetchPolicy to be used for this query. | ||
* @type {FetchPolicy} | ||
*/ | ||
this.fetchPolicy = 'cache-first'; | ||
/** | ||
* Specifies the FetchPolicy to be used for this query. | ||
* @type {FetchPolicy} | ||
*/ | ||
this.fetchPolicy = 'cache-first'; | ||
/** | ||
* Whether or not to fetch results. | ||
* @type {Boolean} | ||
*/ | ||
this.fetchResults = undefined; | ||
/** | ||
* The time interval (in milliseconds) on which this query should be refetched from the server. | ||
* @type {Number} | ||
*/ | ||
this.pollInterval = undefined; | ||
/** | ||
* Whether or not updates to the network status should trigger next on the observer of this query. | ||
* @type {Boolean} | ||
*/ | ||
this.notifyOnNetworkStatusChange = undefined; | ||
/** | ||
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch). | ||
* @type {Boolean} | ||
*/ | ||
this.tryFetch = undefined; | ||
/** | ||
* The apollo ObservableQuery watching this element's query. | ||
* @type {ObservableQuery} | ||
*/ | ||
this.observableQuery; | ||
} | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback && super.connectedCallback(); | ||
if (this.query) this.subscribe(); | ||
} | ||
/** | ||
* Whether or not to fetch results. | ||
* @type {Boolean} | ||
* Exposes the [`ObservableQuery#refetch`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.refetch) method. | ||
* | ||
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used.. | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
this.fetchResults = undefined; | ||
refetch(...args) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.refetch(...args) | ||
); | ||
} | ||
/** | ||
* The time interval (in milliseconds) on which this query should be refetched from the server. | ||
* @type {Number} | ||
* Exposes the [`ObservableQuery#setVariables`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setVariables) method. | ||
* | ||
* @deprecated: This method on ObservableQuery is meant to be private. It will be removed. | ||
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used. | ||
* @param {boolean=} tryFetch Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch). | ||
* @param {boolean=} fetchResults Option to ignore fetching results when updating variables. | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
this.pollInterval = undefined; | ||
setVariables(variables, tryFetch = this.tryFetch, fetchResults = this.fetchResults) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.setVariables(variables, tryFetch, fetchResults) | ||
); | ||
} | ||
/** | ||
* Whether or not updates to the network status should trigger next on the observer of this query. | ||
* @type {Boolean} | ||
* Resets the observableQuery and subscribes. | ||
* | ||
* @param {SubscriptionOptions=} params | ||
* @return {Promise<ZenObservable.Observer<ApolloQueryResult>>} | ||
*/ | ||
this.notifyOnNetworkStatusChange = undefined; | ||
async subscribe({ query = this.query, variables = this.variables } = {}) { | ||
if (!hasAllVariables({ query, variables })) return; | ||
this.observableQuery = this.watchQuery({ query, variables }); | ||
return this.observableQuery.subscribe({ | ||
next: this.nextData, | ||
error: this.nextError, | ||
}); | ||
} | ||
/** | ||
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch). | ||
* @type {Boolean} | ||
* Lets you pass a GraphQL subscription and updateQuery function | ||
* to subscribe to more updates for your query. | ||
* | ||
* The `updateQuery` parameter is a function that takes the previous query data, | ||
* then a `{ subscriptionData: TSubscriptionResult }` object, | ||
* and returns an object with updated query data based on the new results. | ||
* | ||
* @param {SubscribeToMoreOptions=} options | ||
* @return {function(): void} | ||
*/ | ||
this.tryFetch = undefined; | ||
subscribeToMore(options) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.subscribeToMore(options) | ||
); | ||
} | ||
/** | ||
* The apollo ObservableQuery watching this element's query. | ||
* @type {ObservableQuery} | ||
* Executes a Query once and updates the component with the result | ||
* | ||
* @param {QueryOptions} options | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
this.observableQuery; | ||
} | ||
executeQuery({ | ||
query = this.query, | ||
variables = this.variables, | ||
...options | ||
} = this) { | ||
const opts = pickExecuteQueryOpts({ ...this, ...options, query, variables }); | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback && super.connectedCallback(); | ||
if (this.query) this.subscribe(); | ||
} | ||
const queryPromise = | ||
this.client | ||
.query(opts) | ||
.catch(this.nextError.bind(this)); | ||
/** | ||
* Exposes the [`ObservableQuery#refetch`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.refetch) method. | ||
* | ||
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used.. | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
refetch(...args) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.refetch(...args) | ||
); | ||
} | ||
queryPromise.then(this.nextData.bind(this)); | ||
/** | ||
* Exposes the [`ObservableQuery#setVariables`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setVariables) method. | ||
* | ||
* @deprecated: This method on ObservableQuery is meant to be private. It will be removed. | ||
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used. | ||
* @param {boolean=} tryFetch Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch). | ||
* @param {boolean=} fetchResults Option to ignore fetching results when updating variables. | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
setVariables(variables, tryFetch = this.tryFetch, fetchResults = this.fetchResults) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.setVariables(variables, tryFetch, fetchResults) | ||
); | ||
} | ||
return queryPromise; | ||
} | ||
/** | ||
* Resets the observableQuery and subscribes. | ||
* | ||
* @param {{query: DocumentNode, variables: Object}=} params | ||
* @return {Promise<ZenObservable.Observer<ApolloQueryResult<TData>>>} | ||
*/ | ||
async subscribe({ query = this.query, variables = this.variables } = {}) { | ||
if (!hasAllVariables({ query, variables })) return; | ||
this.observableQuery = this.watchQuery({ query, variables }); | ||
return this.observableQuery.subscribe({ | ||
next: this.nextData, | ||
error: this.nextError, | ||
}); | ||
} | ||
/** | ||
* Exposes the `ObservableQuery#fetchMore` method. | ||
* https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.fetchMore | ||
* | ||
* The optional `updateQuery` parameter is a function that takes the previous query data, | ||
* then a `{ subscriptionData: TSubscriptionResult }` object, | ||
* and returns an object with updated query data based on the new results. | ||
* | ||
* The optional `variables` parameter is an optional new variables object. | ||
* | ||
* @param {FetchMoreQueryOptions=} params | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
fetchMore({ query = this.query, updateQuery, variables } = {}) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.fetchMore({ query, updateQuery, variables }) | ||
); | ||
} | ||
/** | ||
* Lets you pass a GraphQL subscription and updateQuery function | ||
* to subscribe to more updates for your query. | ||
* | ||
* The `updateQuery` parameter is a function that takes the previous query data, | ||
* then a `{ subscriptionData: TSubscriptionResult }` object, | ||
* and returns an object with updated query data based on the new results. | ||
* | ||
* @param {{document: DocumentNode, updateQuery: Function, variables: Object, onError: Function}=} options | ||
* @return {function(): void} | ||
*/ | ||
subscribeToMore(options) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.subscribeToMore(options) | ||
); | ||
} | ||
/** | ||
* Creates an instance of ObservableQuery with the options provided by the element. | ||
* - `context` Context to be passed to link execution chain | ||
* - `errorPolicy` Specifies the ErrorPolicy to be used for this query | ||
* - `fetchPolicy` Specifies the FetchPolicy to be used for this query | ||
* - `fetchResults` Whether or not to fetch results | ||
* - `metadata` Arbitrary metadata stored in the store with this query. Designed for debugging, developer tools, etc. | ||
* - `notifyOnNetworkStatusChange` Whether or not updates to the network status should trigger next on the observer of this query | ||
* - `pollInterval` The time interval (in milliseconds) on which this query should be refetched from the server. | ||
* - `query` A GraphQL document that consists of a single query to be sent down to the server. | ||
* - `variables` A map going from variable name to variable value, where the variables are used within the GraphQL query. | ||
* | ||
* @param {WatchQueryOptions=} options | ||
* | ||
* @return {ObservableQuery} | ||
* @protected | ||
*/ | ||
watchQuery({ | ||
query = this.query, | ||
variables = this.variables, | ||
...options | ||
} = this) { | ||
const opts = pickOpts({ ...this, ...options, query, variables }); | ||
return this.client.watchQuery(opts); | ||
} | ||
/** | ||
* Executes a Query once and updates the component with the result | ||
* | ||
* @param {{ metadata: Object, context: Object, query: DocumentNode, variables: Object, fetchPolicy: FetchPolicy, errorPolicy: ErrorPolicy, fetchResults: boolean }=} options | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
executeQuery({ | ||
query = this.query, | ||
variables = this.variables, | ||
...options | ||
} = this) { | ||
const opts = pickExecuteQueryOpts({ ...this, ...options, query, variables }); | ||
/** | ||
* Updates the element with the result of a query. | ||
* | ||
* @param {ApolloQueryResult} result The result of the query. | ||
* @param {Object} result.data The data from the query. | ||
* @param {boolean} result.loading Whether the query is loading. | ||
* @param {number} result.networkStatus The networkStatus of the query. | ||
* @param {boolean} result.stale Whether the query is stale. | ||
* @protected | ||
*/ | ||
nextData({ data, loading, networkStatus, stale }) { | ||
this.data = data; | ||
this.loading = loading; | ||
this.networkStatus = networkStatus; | ||
this.stale = stale; | ||
} | ||
const queryPromise = | ||
this.client | ||
.query(opts) | ||
.catch(this.nextError.bind(this)); | ||
queryPromise.then(this.nextData.bind(this)); | ||
return queryPromise; | ||
} | ||
/** | ||
* Exposes the `ObservableQuery#fetchMore` method. | ||
* https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.fetchMore | ||
* | ||
* The optional `updateQuery` parameter is a function that takes the previous query data, | ||
* then a `{ subscriptionData: TSubscriptionResult }` object, | ||
* and returns an object with updated query data based on the new results. | ||
* | ||
* The optional `variables` parameter is an optional new variables object. | ||
* | ||
* @param {{ | ||
* query: DocumentNode, | ||
* updateQuery: function(prev: TData, { subscriptionData: TSubscriptionResult }): TData, | ||
* variables: Object | ||
* }=} params | ||
* @return {Promise<ApolloQueryResult>} | ||
*/ | ||
fetchMore({ query = this.query, updateQuery, variables } = {}) { | ||
return ( | ||
this.observableQuery && | ||
this.observableQuery.fetchMore({ query, updateQuery, variables }) | ||
); | ||
} | ||
/** | ||
* Creates an instance of ObservableQuery with the options provided by the element. | ||
* - `context` Context to be passed to link execution chain | ||
* - `errorPolicy` Specifies the ErrorPolicy to be used for this query | ||
* - `fetchPolicy` Specifies the FetchPolicy to be used for this query | ||
* - `fetchResults` Whether or not to fetch results | ||
* - `metadata` Arbitrary metadata stored in the store with this query. Designed for debugging, developer tools, etc. | ||
* - `notifyOnNetworkStatusChange` Whether or not updates to the network status should trigger next on the observer of this query | ||
* - `pollInterval` The time interval (in milliseconds) on which this query should be refetched from the server. | ||
* - `query` A GraphQL document that consists of a single query to be sent down to the server. | ||
* - `variables` A map going from variable name to variable value, where the variables are used within the GraphQL query. | ||
* | ||
* @param {{ | ||
* context: Object, | ||
* errorPolicy: ErrorPolicy, | ||
* fetchPolicy: FetchPolicy, | ||
* fetchResults: boolean, | ||
* metadata: Object, | ||
* notifyOnNetworkStatusChange: boolean, | ||
* pollInterval: number, | ||
* query: DocumentNode, | ||
* variables: Object, | ||
* }=} options | ||
* | ||
* @return {ObservableQuery} | ||
* @protected | ||
*/ | ||
watchQuery({ | ||
query = this.query, | ||
variables = this.variables, | ||
...options | ||
} = this) { | ||
const opts = pickOpts({ ...this, ...options, query, variables }); | ||
return this.client.watchQuery(opts); | ||
} | ||
/** | ||
* Updates the element with the result of a query. | ||
* | ||
* @param {ApolloQueryResult} result The result of the query. | ||
* @param {Object} result.data The data from the query. | ||
* @param {boolean} result.loading Whether the query is loading. | ||
* @param {number} result.networkStatus The networkStatus of the query. | ||
* @param {boolean} result.stale Whether the query is stale. | ||
* @protected | ||
*/ | ||
nextData({ data, loading, networkStatus, stale }) { | ||
this.data = data; | ||
this.loading = loading; | ||
this.networkStatus = networkStatus; | ||
this.stale = stale; | ||
} | ||
/** | ||
* Updates the element with the error when the query fails. | ||
* | ||
* @param {Error} error | ||
* @protected | ||
*/ | ||
nextError(error) { | ||
this.error = error; | ||
} | ||
}; | ||
/** | ||
* Updates the element with the error when the query fails. | ||
* | ||
* @param {Error} error | ||
* @protected | ||
*/ | ||
nextError(error) { | ||
this.error = error; | ||
} | ||
}; |
@@ -132,15 +132,33 @@ import { chai, expect, html } from '@open-wc/testing'; | ||
describe('setting variables', function describeSetVariables() { | ||
it('does nothing when there is no observableQuery', async function setVariablesNoQuery() { | ||
const el = await getElement({ client }); | ||
el.variables = { errorPolicy: 'foo' }; | ||
expect(el.variables).to.deep.equal({ errorPolicy: 'foo' }); | ||
describe('without observableQuery', function() { | ||
let el; | ||
beforeEach(async function() { | ||
el = await getElement({ client }); | ||
el.variables = { errorPolicy: 'foo' }; | ||
}); | ||
it('does nothing', async function setVariablesNoQuery() { | ||
expect(el.variables).to.deep.equal({ errorPolicy: 'foo' }); | ||
}); | ||
}); | ||
describe('with an observableQuery', function() { | ||
let el; | ||
let setVariablesSpy; | ||
const query = gql`query { foo }`; | ||
it('calls observableQuery.subscribe when there is a query', async function setVariablesCallsObservableQuerySetVariables() { | ||
const query = gql`query { foo }`; | ||
const el = await getElement({ client, query }); | ||
const setVariablesSpy = stub(el.observableQuery, 'setVariables'); | ||
// shouldn't this be an instance of ObservableQuery? | ||
el.variables = { errorPolicy: 'foo' }; | ||
expect(setVariablesSpy).to.have.been.calledWith({ errorPolicy: 'foo' }); | ||
beforeEach(async function() { | ||
el = await getElement({ client, query }); | ||
setVariablesSpy = spy(el.observableQuery, 'setVariables'); | ||
}); | ||
afterEach(function() { | ||
setVariablesSpy.restore(); | ||
}); | ||
it('calls observableQuery.subscribe', async function setVariablesCallsObservableQuerySetVariables() { | ||
// shouldn't this be an instance of ObservableQuery? | ||
el.variables = { errorPolicy: 'foo' }; | ||
expect(setVariablesSpy).to.have.been.calledWith(match({ errorPolicy: 'foo' })); | ||
}); | ||
}); | ||
@@ -340,4 +358,2 @@ }); | ||
}; | ||
// HACK: oof | ||
client.initQueryManager(); | ||
@@ -344,0 +360,0 @@ const watchQueryStub = stub(client.queryManager, 'watchQuery'); |
@@ -6,3 +6,17 @@ import isFunction from 'crocks/predicates/isFunction'; | ||
/** @typedef {import('apollo-client').FetchPolicy} FetchPolicy */ | ||
/** @typedef {import('apollo-client').ApolloError} ApolloError */ | ||
/** @typedef {import('apollo-client').SubscriptionOptions} SubscriptionOptions */ | ||
/** @typedef {import('graphql').DocumentNode} DocumentNode */ | ||
/** @typedef {import('zen-observable')} Observable */ | ||
/** | ||
* @typedef {Object} SubscriptionResult | ||
* @template {Object} TData | ||
* @property {Boolean} loading whether the subscription is loading | ||
* @property {TData} data subscription data | ||
* @property {ApolloError} error subscription error | ||
*/ | ||
/** | ||
* `ApolloSubscriptionMixin`: class mixin for apollo-subscription elements. | ||
@@ -14,133 +28,140 @@ * | ||
* | ||
* @param {class} superclass | ||
* @return {class} | ||
* @param {*} superclass the class to mix in to | ||
* @return {ApolloSubscriptionMixin~mixin} the mixed class | ||
*/ | ||
export const ApolloSubscriptionMixin = superclass => class extends ApolloElementMixin(superclass) { | ||
export const ApolloSubscriptionMixin = superclass => | ||
/** | ||
* A GraphQL document containing a single subscription. | ||
* | ||
* @return {DocumentNode} | ||
* Class mixin for apollo-subscription elements | ||
* @mixin | ||
* @alias ApolloSubscriptionMixin~mixin | ||
*/ | ||
get subscription() { | ||
return this.document; | ||
} | ||
class extends ApolloElementMixin(superclass) { | ||
/** | ||
* A GraphQL document containing a single subscription. | ||
* | ||
* @return {DocumentNode} | ||
*/ | ||
get subscription() { | ||
return this.document; | ||
} | ||
set subscription(subscription) { | ||
try { | ||
this.document = subscription; | ||
} catch (error) { | ||
throw new TypeError('Subscription must be a gql-parsed DocumentNode'); | ||
set subscription(subscription) { | ||
try { | ||
this.document = subscription; | ||
} catch (error) { | ||
throw new TypeError('Subscription must be a gql-parsed DocumentNode'); | ||
} | ||
if (subscription && !this.observable) this.subscribe(); | ||
} | ||
if (subscription && !this.observable) this.subscribe(); | ||
} | ||
/** | ||
* An object map from variable name to variable value, where the variables are used within the GraphQL subscription. | ||
* | ||
* @return {Object<string, *>} | ||
*/ | ||
get variables() { | ||
return this.__variables; | ||
} | ||
/** | ||
* An object map from variable name to variable value, where the variables are used within the GraphQL subscription. | ||
* | ||
* @return {Object<string, *>} | ||
*/ | ||
get variables() { | ||
return this.__variables; | ||
} | ||
set variables(variables) { | ||
this.__variables = variables; | ||
if (!this.observable) this.subscribe(); | ||
} | ||
set variables(variables) { | ||
this.__variables = variables; | ||
if (!this.observable) this.subscribe(); | ||
} | ||
constructor() { | ||
super(); | ||
this.nextData = this.nextData.bind(this); | ||
this.nextError = this.nextError.bind(this); | ||
constructor() { | ||
super(); | ||
this.nextData = this.nextData.bind(this); | ||
this.nextError = this.nextError.bind(this); | ||
/** | ||
* Specifies the FetchPolicy to be used for this subscription. | ||
* @type {FetchPolicy} | ||
*/ | ||
this.fetchPolicy = 'cache-first'; | ||
/** | ||
* Specifies the FetchPolicy to be used for this subscription. | ||
* @type {FetchPolicy} | ||
*/ | ||
this.fetchPolicy = 'cache-first'; | ||
/** | ||
* Whether or not to fetch results. | ||
* @type {Boolean} | ||
*/ | ||
this.fetchResults = undefined; | ||
/** | ||
* Whether or not to fetch results. | ||
* @type {Boolean} | ||
*/ | ||
this.fetchResults = undefined; | ||
/** | ||
* The time interval (in milliseconds) on which this subscription should be refetched from the server. | ||
* @type {Number} | ||
*/ | ||
this.pollInterval = undefined; | ||
/** | ||
* The time interval (in milliseconds) on which this subscription should be refetched from the server. | ||
* @type {Number} | ||
*/ | ||
this.pollInterval = undefined; | ||
/** | ||
* Whether or not updates to the network status should trigger next on the observer of this subscription. | ||
* @type {Boolean} | ||
*/ | ||
this.notifyOnNetworkStatusChange = undefined; | ||
/** | ||
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch). | ||
* @type {Boolean} | ||
*/ | ||
this.tryFetch = undefined; | ||
/** | ||
* Observable watching this element's subscription. | ||
* @type {Observable} | ||
*/ | ||
this.observable; | ||
} | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback && super.connectedCallback(); | ||
this.subscribe(); | ||
} | ||
/** | ||
* Whether or not updates to the network status should trigger next on the observer of this subscription. | ||
* @type {Boolean} | ||
* Resets the observable and subscribes. | ||
* | ||
* @template {Object} TData | ||
* @param {SubscriptionOptions} options | ||
* @return {Promise<Observer<SubscriptionResult<TData>>>} | ||
*/ | ||
this.notifyOnNetworkStatusChange = undefined; | ||
async subscribe({ | ||
fetchPolicy = this.fetchPolicy, | ||
query = this.subscription, | ||
variables = this.variables, | ||
} = {}) { | ||
if (!hasAllVariables({ query, variables })) return; | ||
this.observable = this.client.subscribe( | ||
stripUndefinedValues({ query, variables, fetchPolicy }) | ||
); | ||
return this.observable.subscribe({ | ||
next: this.nextData, | ||
error: this.nextError, | ||
}); | ||
} | ||
/** | ||
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch). | ||
* @type {Boolean} | ||
* Updates the element with the result of a subscription. | ||
* | ||
* @template TData | ||
* @param {SubscriptionResult<TData>} result The result of the subscription. | ||
* @protected | ||
*/ | ||
this.tryFetch = undefined; | ||
nextData({ data }) { | ||
const { client, onSubscriptionData } = this; | ||
const subscriptionData = { data }; | ||
if (isFunction(onSubscriptionData)) onSubscriptionData({ client, subscriptionData }); | ||
this.data = data; | ||
this.loading = false; | ||
this.error = undefined; | ||
} | ||
/** | ||
* Observable watching this element's subscription. | ||
* @type {Observable} | ||
* Updates the element with the error when the subscription fails. | ||
* | ||
* @param {ApolloError} error | ||
* @protected | ||
*/ | ||
this.observable; | ||
} | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback && super.connectedCallback(); | ||
this.subscribe(); | ||
} | ||
/** | ||
* Resets the observable and subscribes. | ||
* | ||
* @param {{fetchPolicy: FetchPolicy, query: DocumentNode, variables: Object}} options | ||
* @return {Promise<ZenObservable.Observer<SubscriptionResult<TData>>>} | ||
*/ | ||
async subscribe({ | ||
fetchPolicy = this.fetchPolicy, | ||
query = this.subscription, | ||
variables = this.variables, | ||
} = {}) { | ||
if (!hasAllVariables({ query, variables })) return; | ||
this.observable = this.client.subscribe( | ||
stripUndefinedValues({ query, variables, fetchPolicy }) | ||
); | ||
return this.observable.subscribe({ | ||
next: this.nextData, | ||
error: this.nextError, | ||
}); | ||
} | ||
/** | ||
* Updates the element with the result of a subscription. | ||
* | ||
* @param {ApolloQueryResult} result The result of the subscription. | ||
* @param {Object} result.data The data from the subscription. | ||
* @param {boolean} result.loading Whether the subscription is loading. | ||
* @protected | ||
*/ | ||
nextData({ data }) { | ||
const { client, onSubscriptionData } = this; | ||
if (isFunction(onSubscriptionData)) onSubscriptionData({ client, subscriptionData: { data } }); | ||
this.data = data; | ||
this.loading = false; | ||
this.error = undefined; | ||
} | ||
/** | ||
* Updates the element with the error when the subscription fails. | ||
* | ||
* @param {Error} error | ||
* @protected | ||
*/ | ||
nextError(error) { | ||
this.error = error; | ||
this.loading = false; | ||
} | ||
}; | ||
nextError(error) { | ||
this.error = error; | ||
this.loading = false; | ||
} | ||
}; |
@@ -6,2 +6,24 @@ # Change Log | ||
## [1.0.2](https://github.com/apollo-elements/apollo-elements/compare/@apollo-elements/mixins@1.0.1...@apollo-elements/mixins@1.0.2) (2019-06-06) | ||
### Bug Fixes | ||
* **mixins:** improved type docs ([0ac2a3a](https://github.com/apollo-elements/apollo-elements/commit/0ac2a3a)) | ||
## [1.0.1](https://github.com/apollo-elements/apollo-elements/compare/@apollo-elements/mixins@0.0.11...@apollo-elements/mixins@1.0.1) (2019-05-26) | ||
### Bug Fixes | ||
* **mixins:** allow default options in queries ([4a8895e](https://github.com/apollo-elements/apollo-elements/commit/4a8895e)) | ||
# [1.0.0](https://github.com/apollo-elements/apollo-elements/compare/@apollo-elements/mixins@0.0.11...@apollo-elements/mixins@1.0.0) (2019-04-03) | ||
@@ -8,0 +30,0 @@ |
{ | ||
"name": "@apollo-elements/mixins", | ||
"version": "1.0.0", | ||
"version": "1.0.2", | ||
"description": "👩🚀🌛 Custom Element class mixins for Apollo GraphQL 🚀👨🚀", | ||
@@ -31,12 +31,7 @@ "main": "index.js", | ||
"dependencies": { | ||
"@apollo-elements/lib": "^1.0.0", | ||
"apollo-client": "^2.4.12", | ||
"@apollo-elements/lib": "^1.0.1", | ||
"apollo-client": "^2.6.0", | ||
"crocks": "^0.11.1" | ||
}, | ||
"devDependencies": { | ||
"@apollo-elements/test-helpers": "^0.0.3", | ||
"@types/crocks": "^0.0.2", | ||
"graphql": "^14.1.1" | ||
}, | ||
"gitHead": "36a6a137c69795087edb47cb780008f4a43b06ee" | ||
"gitHead": "44b65b4a0aa6d5c5ed1d0c8ba403879ae9a4fba2" | ||
} |
@@ -43,2 +43,6 @@ # @apollo-elements/mixins | ||
## Aren't Mixins Considered Harmful? | ||
Different kind of mixin. These are [JS class mixins](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/). | ||
## 😎 Cool Tricks | ||
@@ -45,0 +49,0 @@ |
0
70
77327
19
1761
Updated@apollo-elements/lib@^1.0.1
Updatedapollo-client@^2.6.0