From 83839f31223cfc0ac985895b243ed8693e9adc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Fri, 29 Dec 2017 13:41:05 +0100 Subject: [PATCH 01/12] [react-relay] Split tests into modern, compat, and classic files. --- types/react-relay/compat.d.ts | 8 +- .../test/react-relay-classic-tests.tsx | 10 +- .../test/react-relay-compat-tests.tsx | 53 ++++++ types/react-relay/test/react-relay-tests.tsx | 156 +----------------- types/react-relay/tsconfig.json | 1 + types/relay-runtime/relay-runtime-tests.tsx | 10 +- 6 files changed, 73 insertions(+), 165 deletions(-) create mode 100644 types/react-relay/test/react-relay-compat-tests.tsx diff --git a/types/react-relay/compat.d.ts b/types/react-relay/compat.d.ts index 22585d03d0..5f5785ab4b 100644 --- a/types/react-relay/compat.d.ts +++ b/types/react-relay/compat.d.ts @@ -1,12 +1,8 @@ -export { - QueryRenderer, - fetchQuery, - graphql, -} from "./index"; +export { QueryRenderer, fetchQuery, graphql } from "./index"; import { ConnectionConfig, RelayPaginationProp as RelayModernPaginationProp, - RelayRefetchProp as RelayModernRefetchProp + RelayRefetchProp as RelayModernRefetchProp, } from "./index"; import * as RelayRuntimeTypes from "relay-runtime"; import { RelayEnvironmentInterface } from "./classic"; diff --git a/types/react-relay/test/react-relay-classic-tests.tsx b/types/react-relay/test/react-relay-classic-tests.tsx index d8eaf005ca..a57cbc1b6f 100644 --- a/types/react-relay/test/react-relay-classic-tests.tsx +++ b/types/react-relay/test/react-relay-classic-tests.tsx @@ -1,15 +1,14 @@ import * as React from "react"; import * as Relay from "react-relay/classic"; +import { CompatContainer } from "./react-relay-compat-tests"; + interface Props { text: string; userId: string; } -// tslint:disable-next-line no-empty-interface -interface Response {} - -export default class AddTweetMutation extends Relay.Mutation { +export default class AddTweetMutation extends Relay.Mutation { getMutation() { return Relay.QL`mutation{addTweet}`; } @@ -64,6 +63,7 @@ const ArtworkContainer = Relay.createContainer(Artwork, { artwork: () => Relay.QL` fragment on Artwork { title + ${CompatContainer.getFragment("whatever")} } `, }, @@ -83,7 +83,7 @@ class StubbedArtwork extends React.Component { setVariables: () => {}, forceFetch: () => {}, hasOptimisticUpdate: () => false, - getPendingTransactions: (): Relay.RelayMutationTransaction[] => [], + getPendingTransactions: (): any => undefined, commitUpdate: () => {}, }, }; diff --git a/types/react-relay/test/react-relay-compat-tests.tsx b/types/react-relay/test/react-relay-compat-tests.tsx new file mode 100644 index 0000000000..e0ee5d3be2 --- /dev/null +++ b/types/react-relay/test/react-relay-compat-tests.tsx @@ -0,0 +1,53 @@ +import * as React from "react"; +import { + QueryRenderer as CompatQueryRenderer, + createFragmentContainer as createFragmentContainerCompat, + commitMutation as commitMutationCompat, + CompatEnvironment, + RelayPaginationProp as RelayPaginationPropCompat, +} from "react-relay/compat"; + +import { configs, mutation, optimisticResponse } from "./react-relay-tests"; + +// testting compat mutation with classic environment +function markNotificationAsReadCompat(environment: CompatEnvironment, source: string, storyID: string) { + const variables = { + input: { + source, + storyID, + }, + }; + + commitMutationCompat(environment, { + configs, + mutation, + optimisticResponse, + variables, + onCompleted: (response, errors) => { + console.log("Response received from server."); + }, + onError: err => console.error(err), + updater: (store, data) => { + const field = store.get(storyID); + if (field) { + field.setValue(data.story, "story"); + } + } + }); +} + +interface CompatProps { + relay: RelayPaginationPropCompat; +} + +class CompatComponent extends React.Component { + markNotificationAsRead(source: string, storyID: string) { + markNotificationAsReadCompat(this.props.relay.environment, source, storyID); + } + + render() { + return (
); + } +} + +export const CompatContainer = createFragmentContainerCompat(CompatComponent, {}); diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 6fbd2dea3f..54a81df1c9 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -1,9 +1,6 @@ import * as React from "react"; import { Environment, Network, RecordSource, Store, ConnectionHandler } from "relay-runtime"; -//////////////////////////// -// RELAY MODERN TESTS -/////////////////////////// import { graphql, commitMutation, @@ -215,7 +212,7 @@ const FeedPaginationContainer = createPaginationContainer( // ~~~~~~~~~~~~~~~~~~~~~ // Modern Mutations // ~~~~~~~~~~~~~~~~~~~~~ -const mutation = graphql` +export const mutation = graphql` mutation MarkReadNotificationMutation($input: MarkReadNotificationData!) { markReadNotification(data: $input) { notification { @@ -225,7 +222,7 @@ const mutation = graphql` } `; -const optimisticResponse = { +export const optimisticResponse = { markReadNotification: { notification: { seenState: "SEEN", @@ -233,7 +230,7 @@ const optimisticResponse = { }, }; -const configs = [ +export const configs = [ { type: "NODE_DELETE" as "NODE_DELETE", deletedIDFieldName: "destroyedShipId", @@ -325,150 +322,3 @@ requestSubscription( }, } ); - -//////////////////////////// -// RELAY COMPAT TESTS -/////////////////////////// -import { - QueryRenderer as CompatQueryRenderer, - createFragmentContainer as createFragmentContainerCompat, - commitMutation as commitMutationCompat, - CompatEnvironment, - RelayPaginationProp as RelayPaginationPropCompat, -} from "react-relay/compat"; - -// testting compat mutation with classic environment -function markNotificationAsReadCompat(environment: CompatEnvironment, source: string, storyID: string) { - const variables = { - input: { - source, - storyID, - }, - }; - - commitMutationCompat(environment, { - configs, - mutation, - optimisticResponse, - variables, - onCompleted: (response, errors) => { - console.log("Response received from server."); - }, - onError: err => console.error(err), - updater: (store, data) => { - const field = store.get(storyID); - if (field) { - field.setValue(data.story, "story"); - } - } - }); -} - -interface CompatProps { - relay: RelayPaginationPropCompat; -} - -export class CompatComponent extends React.Component { - markNotificationAsRead(source: string, storyID: string) { - markNotificationAsReadCompat(this.props.relay.environment, source, storyID); - } - - render() { - return (
); - } -} - -const CompatContainer = createFragmentContainerCompat(CompatComponent, {}); - -//////////////////////////// -// RELAY-CLASSIC TESTS -/////////////////////////// -import * as Relay from "react-relay/classic"; - -interface Props { - text: string; - userId: string; -} - -export default class AddTweetMutation extends Relay.Mutation { - getMutation() { - return Relay.QL`mutation{addTweet}`; - } - - getFatQuery() { - return Relay.QL` - fragment on AddTweetPayload { - tweetEdge - user - } - `; - } - - getConfigs() { - return [ - { - type: "RANGE_ADD", - parentName: "user", - parentID: this.props.userId, - connectionName: "tweets", - edgeName: "tweetEdge", - rangeBehaviors: { - "": "append", - }, - }, - ]; - } - - getVariables() { - return this.props; - } -} - -interface ArtwokRelayVariables { - artworkID: string; -} - -interface ArtworkProps extends Relay.RelayProps { - artwork: { - title: string; - }; -} - -class Artwork extends React.Component { - render() { - return {this.props.artwork.title}; - } -} - -const ArtworkContainer = Relay.createContainer(Artwork, { - fragments: { - artwork: () => Relay.QL` - fragment on Artwork { - title - ${ CompatContainer.getFragment('whatever') } - } - `, - }, -}); - -class StubbedArtwork extends React.Component { - render() { - const props = { - artwork: { title: "CHAMPAGNE FORMICA FLAG" }, - relay: { - route: { - name: "champagne", - }, - variables: { - artworkID: "champagne-formica-flag", - }, - setVariables: () => {}, - forceFetch: () => {}, - hasOptimisticUpdate: () => false, - getPendingTransactions: (): any => undefined, - commitUpdate: () => {}, - }, - }; - return ; - } -} diff --git a/types/react-relay/tsconfig.json b/types/react-relay/tsconfig.json index 937fa22803..bf3dc05339 100644 --- a/types/react-relay/tsconfig.json +++ b/types/react-relay/tsconfig.json @@ -24,6 +24,7 @@ "classic.d.ts", "compat.d.ts", "test/react-relay-tests.tsx", + "test/react-relay-compat-tests.tsx", "test/react-relay-classic-tests.tsx" ] } \ No newline at end of file diff --git a/types/relay-runtime/relay-runtime-tests.tsx b/types/relay-runtime/relay-runtime-tests.tsx index b39a117e61..4cbbacf680 100644 --- a/types/relay-runtime/relay-runtime-tests.tsx +++ b/types/relay-runtime/relay-runtime-tests.tsx @@ -1,4 +1,12 @@ -import { Environment, Network, RecordSource, Store, ConnectionHandler, ViewerHandler, RecordSourceInspector } from "relay-runtime"; +import { + Environment, + Network, + RecordSource, + Store, + ConnectionHandler, + ViewerHandler, + RecordSourceInspector, +} from "relay-runtime"; const source = new RecordSource(); const store = new Store(source); From be38f831c290b8892c239835d45967bad9c6fdb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Fri, 29 Dec 2017 14:04:57 +0100 Subject: [PATCH 02/12] [relay] Use React.ComponentType. --- types/react-relay/classic.d.ts | 2 +- types/react-relay/compat.d.ts | 7 +++---- types/react-relay/index.d.ts | 15 +++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/types/react-relay/classic.d.ts b/types/react-relay/classic.d.ts index c600992669..e0042c3c9b 100644 --- a/types/react-relay/classic.d.ts +++ b/types/react-relay/classic.d.ts @@ -117,7 +117,7 @@ export class DefaultNetworkLayer implements RelayNetworkLayer { } export function createContainer( - component: React.ComponentClass | React.StatelessComponent, + component: React.ComponentType, params?: CreateContainerOpts ): RelayContainerClass; export function injectNetworkLayer(networkLayer: RelayNetworkLayer): any; diff --git a/types/react-relay/compat.d.ts b/types/react-relay/compat.d.ts index 5f5785ab4b..6c659382af 100644 --- a/types/react-relay/compat.d.ts +++ b/types/react-relay/compat.d.ts @@ -26,7 +26,6 @@ export interface StatelessWithFragment extends React.StatelessComponent { getFragment: typeof getFragment; } export type ReactFragmentComponent = ComponentWithFragment | StatelessWithFragment; -export type ReactBaseComponent = React.ComponentClass | React.StatelessComponent; export type RelayClassicEnvironment = RelayEnvironmentInterface; // ~~~~~~~~~~~~~~~~~~~~~ @@ -64,18 +63,18 @@ export interface GeneratedNodeMap { } export function createFragmentContainer( - Component: ReactBaseComponent, + Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap ): ReactFragmentComponent; export function createRefetchContainer( - Component: ReactBaseComponent, + Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, taggedNode: RelayRuntimeTypes.GraphQLTaggedNode ): ReactFragmentComponent; export function createPaginationContainer( - Component: ReactBaseComponent, + Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, connectionConfig: ConnectionConfig ): ReactFragmentComponent; diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 0b7d87ce58..2a36ef4ec4 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -24,7 +24,6 @@ export type ConcreteFragment = any; export type ConcreteBatch = any; export type ConcreteFragmentDefinition = object; export type ConcreteOperationDefinition = object; -export type ReactBaseComponent = React.ComponentClass | React.StatelessComponent; // ~~~~~~~~~~~~~~~~~~~~~ // RelayProp @@ -87,9 +86,9 @@ export class QueryRenderer extends ReactRelayQueryRenderer {} // createFragmentContainer // ~~~~~~~~~~~~~~~~~~~~~ export function createFragmentContainer( - Component: ReactBaseComponent, + Component: React.ComponentType, fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap -): ReactBaseComponent; +): React.ComponentType; // ~~~~~~~~~~~~~~~~~~~~~ // createPaginationContainer @@ -134,13 +133,13 @@ export interface ConnectionConfig { query: GraphQLTaggedNode; } export function createPaginationContainer( - Component: ReactBaseComponent, + Component: React.ComponentType, fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, connectionConfig: ConnectionConfig -): ReactBaseComponent; +): React.ComponentType; // ~~~~~~~~~~~~~~~~~~~~~ -// createFragmentContainer +// createRefetchContainer // ~~~~~~~~~~~~~~~~~~~~~ export interface RefetchOptions { force?: boolean; @@ -157,7 +156,7 @@ export type RelayRefetchProp = RelayProp & { ): RelayRuntimeTypes.Disposable; }; export function createRefetchContainer( - Component: ReactBaseComponent, + Component: React.ComponentType, fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, taggedNode: GraphQLTaggedNode -): ReactBaseComponent; +): React.ComponentType; From c41956752fec523b4f47c5bc9bd60014a4da98bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Tue, 30 Jan 2018 15:29:12 +0100 Subject: [PATCH 03/12] [relay] Cleanup of where types are defined and scope tests. --- types/react-relay/compat.d.ts | 3 +- types/react-relay/index.d.ts | 30 +- types/react-relay/test/react-relay-tests.tsx | 284 ++++++++++--------- types/relay-runtime/index.d.ts | 23 +- 4 files changed, 185 insertions(+), 155 deletions(-) diff --git a/types/react-relay/compat.d.ts b/types/react-relay/compat.d.ts index 6c659382af..b74efc4ac2 100644 --- a/types/react-relay/compat.d.ts +++ b/types/react-relay/compat.d.ts @@ -4,14 +4,13 @@ import { RelayPaginationProp as RelayModernPaginationProp, RelayRefetchProp as RelayModernRefetchProp, } from "./index"; +export { ConcreteFragment, ConcreteRequest, ConcreteBatchRequest } from "relay-runtime"; import * as RelayRuntimeTypes from "relay-runtime"; import { RelayEnvironmentInterface } from "./classic"; // ~~~~~~~~~~~~~~~~~~~~~ // Maybe Fix // ~~~~~~~~~~~~~~~~~~~~~ -export type ConcreteFragment = any; -export type ConcreteBatch = any; export type ConcreteFragmentDefinition = object; export type ConcreteOperationDefinition = object; diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 2a36ef4ec4..91a1bbbe1d 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -11,6 +11,7 @@ export { commitLocalUpdate, commitRelayModernMutation as commitMutation, fetchRelayModernQuery as fetchQuery, + GraphQLTaggedNode, requestRelaySubscription as requestSubscription, } from "relay-runtime"; @@ -20,8 +21,6 @@ import * as RelayRuntimeTypes from "relay-runtime"; // ~~~~~~~~~~~~~~~~~~~~~ // Maybe Fix // ~~~~~~~~~~~~~~~~~~~~~ -export type ConcreteFragment = any; -export type ConcreteBatch = any; export type ConcreteFragmentDefinition = object; export type ConcreteOperationDefinition = object; @@ -39,24 +38,19 @@ export interface RelayProp { export function RelayQL(strings: string[], ...substitutions: any[]): RelayRuntimeTypes.RelayConcreteNode; // ~~~~~~~~~~~~~~~~~~~~~ -// RelayModernGraphQLTag +// ReactRelayTypes // ~~~~~~~~~~~~~~~~~~~~~ export interface GeneratedNodeMap { - [key: string]: GraphQLTaggedNode; + [key: string]: RelayRuntimeTypes.GraphQLTaggedNode; } -export type GraphQLTaggedNode = - | (() => ConcreteFragment | ConcreteBatch) - | { - modern(): ConcreteFragment | ConcreteBatch; - classic(relayQL: typeof RelayQL): ConcreteFragmentDefinition | ConcreteOperationDefinition; - }; + /** * Runtime function to correspond to the `graphql` tagged template function. * All calls to this function should be transformed by the plugin. */ export interface GraphqlInterface { - (strings: string[] | TemplateStringsArray): GraphQLTaggedNode; - experimental(strings: string[] | TemplateStringsArray): GraphQLTaggedNode; + (strings: string[] | TemplateStringsArray): RelayRuntimeTypes.GraphQLTaggedNode; + experimental(strings: string[] | TemplateStringsArray): RelayRuntimeTypes.GraphQLTaggedNode; } export const graphql: GraphqlInterface; @@ -66,7 +60,7 @@ export const graphql: GraphqlInterface; export interface QueryRendererProps { cacheConfig?: RelayRuntimeTypes.CacheConfig; environment: RelayRuntimeTypes.Environment; - query: GraphQLTaggedNode; + query: RelayRuntimeTypes.GraphQLTaggedNode; render(readyState: ReadyState): React.ReactElement | undefined | null; variables: RelayRuntimeTypes.Variables; rerunParamExperimental?: RelayRuntimeTypes.RerunParam; @@ -87,7 +81,7 @@ export class QueryRenderer extends ReactRelayQueryRenderer {} // ~~~~~~~~~~~~~~~~~~~~~ export function createFragmentContainer( Component: React.ComponentType, - fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap + fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap ): React.ComponentType; // ~~~~~~~~~~~~~~~~~~~~~ @@ -130,11 +124,11 @@ export interface ConnectionConfig { paginationInfo: { count: number; cursor?: string }, fragmentVariables: RelayRuntimeTypes.Variables ): RelayRuntimeTypes.Variables; - query: GraphQLTaggedNode; + query: RelayRuntimeTypes.GraphQLTaggedNode; } export function createPaginationContainer( Component: React.ComponentType, - fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, + fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, connectionConfig: ConnectionConfig ): React.ComponentType; @@ -157,6 +151,6 @@ export type RelayRefetchProp = RelayProp & { }; export function createRefetchContainer( Component: React.ComponentType, - fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, - taggedNode: GraphQLTaggedNode + fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, + taggedNode: RelayRuntimeTypes.GraphQLTaggedNode ): React.ComponentType; diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 54a81df1c9..9385922dcf 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -10,7 +10,8 @@ import { requestSubscription, QueryRenderer, RelayRefetchProp, - RelayPaginationProp + RelayPaginationProp, + RelayProp } from "react-relay"; // ~~~~~~~~~~~~~~~~~~~~~ @@ -54,160 +55,185 @@ const MyQueryRenderer = (props: { name: string }) => ( // ~~~~~~~~~~~~~~~~~~~~~ // Modern FragmentContainer // ~~~~~~~~~~~~~~~~~~~~~ -const MyFragmentContainer = createFragmentContainer( - class TodoListView extends React.Component { - render() { - return
; - } - }, - { - item: graphql` - fragment TodoItem_item on Todo { - text - isComplete - } - `, + +() => { + interface Props { + relay: RelayProp; + publicProp: string; } -); + + class TodoListView extends React.Component { + render() { + this.props.relay.environment; + return
{this.props.publicProp}
; + } + } + + const MyFragmentContainer = createFragmentContainer( + TodoListView, + { + item: graphql` + fragment TodoItem_item on Todo { + text + isComplete + } + `, + } + ); +}; // ~~~~~~~~~~~~~~~~~~~~~ // Modern RefetchContainer // ~~~~~~~~~~~~~~~~~~~~~ -interface StoryInterface { - id: string; -} -interface FeedStoriesProps { - relay: RelayRefetchProp; - feed: { - stories: { edges: Array<{ node: StoryInterface }> }; - }; -} -class Story extends React.Component<{ story: StoryInterface }> {} -class FeedStories extends React.Component { - render() { - return ( -
- {this.props.feed.stories.edges.map(edge => )} -
- ); + +() => { + interface StoryInterface { + id: string; + } + interface Props { + relay: RelayRefetchProp; + loadMoreTitle: string; + feed: { + stories: { edges: Array<{ node: StoryInterface }> }; + }; + } + class Story extends React.Component<{ story: StoryInterface }> {} + class FeedStories extends React.Component { + render() { + return ( +
+ {this.props.feed.stories.edges.map(edge => )} +
+ ); + } + + _loadMore() { + // Increments the number of stories being rendered by 10. + const refetchVariables = (fragmentVariables: { count: number }) => ({ + count: fragmentVariables.count + 10, + }); + this.props.relay.refetch(refetchVariables); + } } - _loadMore() { - // Increments the number of stories being rendered by 10. - const refetchVariables = (fragmentVariables: { count: number }) => ({ - count: fragmentVariables.count + 10, - }); - this.props.relay.refetch(refetchVariables); - } -} - -const FeedRefetchContainer = createRefetchContainer( - FeedStories, - { - feed: graphql.experimental` - fragment FeedStories_feed on Feed @argumentDefinitions(count: { type: "Int", defaultValue: 10 }) { - stories(first: $count) { - edges { - node { - id - ...Story_story + const FeedRefetchContainer = createRefetchContainer( + FeedStories, + { + feed: graphql.experimental` + fragment FeedStories_feed on Feed @argumentDefinitions(count: { type: "Int", defaultValue: 10 }) { + stories(first: $count) { + edges { + node { + id + ...Story_story + } } } } + `, + }, + graphql.experimental` + query FeedStoriesRefetchQuery($count: Int) { + feed { + ...FeedStories_feed @arguments(count: $count) + } } - `, - }, - graphql.experimental` - query FeedStoriesRefetchQuery($count: Int) { - feed { - ...FeedStories_feed @arguments(count: $count) - } - } - ` -); + ` + ); +}; // ~~~~~~~~~~~~~~~~~~~~~ // Modern PaginationContainer // ~~~~~~~~~~~~~~~~~~~~~ -interface FeedProps { - user: { feed: { edges: Array<{ node: StoryInterface }> } }; - relay: RelayPaginationProp; -} -class Feed extends React.Component { - render() { - return ( -
- {this.props.user.feed.edges.map(edge => )} -
- ); - } - _loadMore() { - if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { - return; +() => { + interface StoryInterface { + id: string; + } + class Story extends React.Component<{ story: StoryInterface }> {} + + interface Props { + relay: RelayPaginationProp; + loadMoreTitle: string; + user: { feed: { edges: Array<{ node: StoryInterface }> } }; + } + class Feed extends React.Component { + render() { + return ( +
+ {this.props.user.feed.edges.map(edge => )} +
+ ); } - this.props.relay.loadMore( - 10, // Fetch the next 10 feed items - e => { - console.log(e); + _loadMore() { + if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { + return; } - ); - } -} -const FeedPaginationContainer = createPaginationContainer( - Feed, - { - user: graphql` - fragment Feed_user on User { - feed( - first: $count - after: $cursor - orderby: $orderBy # other variables - ) @connection(key: "Feed_feed") { - edges { - node { - id - ...Story_story + this.props.relay.loadMore( + 10, // Fetch the next 10 feed items + e => { + console.log(e); + } + ); + } + } + + const FeedPaginationContainer = createPaginationContainer( + Feed, + { + user: graphql` + fragment Feed_user on User { + feed( + first: $count + after: $cursor + orderby: $orderBy # other variables + ) @connection(key: "Feed_feed") { + edges { + node { + id + ...Story_story + } } } } - } - `, - }, - { - direction: "forward", - getConnectionFromProps(props) { - return props.user && props.user.feed; + `, }, - getFragmentVariables(prevVars, totalCount) { - return { - ...prevVars, - count: totalCount, - }; - }, - getVariables(props, { count, cursor }, fragmentVariables) { - return { - count, - cursor, - // in most cases, for variables other than connection filters like - // `first`, `after`, etc. you may want to use the previous values. - orderBy: fragmentVariables.orderBy, - }; - }, - query: graphql` - query FeedPaginationQuery($count: Int!, $cursor: String, $orderby: String!) { - user { - # You could reference the fragment defined previously. - ...Feed_user + { + direction: "forward", + getConnectionFromProps(props) { + return props.user && props.user.feed; + }, + getFragmentVariables(prevVars, totalCount) { + return { + ...prevVars, + count: totalCount, + }; + }, + getVariables(props, { count, cursor }, fragmentVariables) { + return { + count, + cursor, + // in most cases, for variables other than connection filters like + // `first`, `after`, etc. you may want to use the previous values. + orderBy: fragmentVariables.orderBy, + }; + }, + query: graphql` + query FeedPaginationQuery($count: Int!, $cursor: String, $orderby: String!) { + user { + # You could reference the fragment defined previously. + ...Feed_user + } } - } - `, + `, + } + ); } -); +}; // ~~~~~~~~~~~~~~~~~~~~~ // Modern Mutations diff --git a/types/relay-runtime/index.d.ts b/types/relay-runtime/index.d.ts index 74b24ca1b8..a11a632956 100644 --- a/types/relay-runtime/index.d.ts +++ b/types/relay-runtime/index.d.ts @@ -17,8 +17,6 @@ export type RelayConcreteNode = any; export type RelayMutationTransaction = any; export type RelayMutationRequest = any; export type RelayQueryRequest = any; -export type ConcreteFragment = any; -export type ConcreteBatch = any; export type ConcreteFragmentDefinition = object; export type ConcreteOperationDefinition = object; @@ -32,6 +30,19 @@ export type ConcreteOperationDefinition = object; */ export type RelayContainer = any; +// ~~~~~~~~~~~~~~~~~~~~~ +// Used in artifacts +// emitted by +// relay-compiler +// ~~~~~~~~~~~~~~~~~~~~~ + +// File: https://github.com/facebook/relay/blob/fe0e70f10bbcba1fff89911313ea69f24569464b/packages/relay-runtime/util/RelayConcreteNode.js +export type ConcreteFragment = any; +export type ConcreteRequest = any; +export type ConcreteBatchRequest = any; + +export type RequestNode = ConcreteRequest | ConcreteBatchRequest; + // ~~~~~~~~~~~~~~~~~~~~~ // RelayQL // ~~~~~~~~~~~~~~~~~~~~~ @@ -44,9 +55,9 @@ export interface GeneratedNodeMap { [key: string]: GraphQLTaggedNode; } export type GraphQLTaggedNode = - | (() => ConcreteFragment | ConcreteBatch) + | (() => ConcreteFragment | RequestNode) | { - modern(): ConcreteFragment | ConcreteBatch; + modern(): ConcreteFragment | RequestNode; classic(relayQL: RelayQL): ConcreteFragmentDefinition | ConcreteOperationDefinition; }; // ~~~~~~~~~~~~~~~~~~~~~ @@ -85,7 +96,7 @@ export interface PayloadError { * May return an Observable or Promise of a raw server response. */ export function FetchFunction( - operation: ConcreteBatch, + operation: RequestNode, variables: Variables, cacheConfig: CacheConfig, uploadables?: UploadableMap @@ -99,7 +110,7 @@ export function FetchFunction( * fourth parameter. */ export type SubscribeFunction = ( - operation: ConcreteBatch, + operation: RequestNode, variables: Variables, cacheConfig: CacheConfig, observer: LegacyObserver From 2fcce313b69f86fce98b09d0baa862b140aad0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Tue, 30 Jan 2018 15:30:36 +0100 Subject: [PATCH 04/12] [react-relay] Remove `relay` prop from containers. --- types/react-relay/index.d.ts | 14 +++++++++++--- types/react-relay/test/react-relay-tests.tsx | 13 +++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 91a1bbbe1d..259896846f 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -18,6 +18,14 @@ export { import * as React from "react"; import * as RelayRuntimeTypes from "relay-runtime"; +// ~~~~~~~~~~~~~~~~~~~~~ +// Utility types +// ~~~~~~~~~~~~~~~~~~~~~ +type Diff = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]; +type Omit = { [P in Diff]: T[P] }; + +type RemoveRelayProp

= Omit

; + // ~~~~~~~~~~~~~~~~~~~~~ // Maybe Fix // ~~~~~~~~~~~~~~~~~~~~~ @@ -82,7 +90,7 @@ export class QueryRenderer extends ReactRelayQueryRenderer {} export function createFragmentContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap -): React.ComponentType; +): React.ComponentType>; // ~~~~~~~~~~~~~~~~~~~~~ // createPaginationContainer @@ -130,7 +138,7 @@ export function createPaginationContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, connectionConfig: ConnectionConfig -): React.ComponentType; +): React.ComponentType>; // ~~~~~~~~~~~~~~~~~~~~~ // createRefetchContainer @@ -153,4 +161,4 @@ export function createRefetchContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, taggedNode: RelayRuntimeTypes.GraphQLTaggedNode -): React.ComponentType; +): React.ComponentType>; diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 9385922dcf..6e4aaf3c8e 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -80,6 +80,10 @@ const MyQueryRenderer = (props: { name: string }) => ( `, } ); + + function doesNotRequireRelayPropToBeProvided() { + ; + } }; // ~~~~~~~~~~~~~~~~~~~~~ @@ -141,6 +145,11 @@ const MyQueryRenderer = (props: { name: string }) => ( } ` ); + + function doesNotRequireRelayPropToBeProvided() { + const feed = { stories: { edges: [] }}; // TODO + ; + } }; // ~~~~~~~~~~~~~~~~~~~~~ @@ -232,6 +241,10 @@ const MyQueryRenderer = (props: { name: string }) => ( `, } ); + + function doesNotRequireRelayPropToBeProvided() { + const user = { feed: { edges: [] }}; // TODO + ; } }; From 0351b87ecadbef2f7b04d61a7eac51b74e1796de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Sat, 3 Feb 2018 23:06:53 +0100 Subject: [PATCH 05/12] [relay] Add types used in compiler artifacts and improve tests. --- types/react-relay/test/react-relay-tests.tsx | 244 ++++++++++++------- types/relay-runtime/index.d.ts | 7 +- 2 files changed, 156 insertions(+), 95 deletions(-) diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 6e4aaf3c8e..fc150d7b3d 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Environment, Network, RecordSource, Store, ConnectionHandler } from "relay-runtime"; +import { Environment, Network, RecordSource, Store, ConnectionHandler, ConcreteFragment } from "relay-runtime"; import { graphql, @@ -52,126 +52,185 @@ const MyQueryRenderer = (props: { name: string }) => ( /> ); -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern FragmentContainer -// ~~~~~~~~~~~~~~~~~~~~~ - -() => { - interface Props { - relay: RelayProp; - publicProp: string; - } - - class TodoListView extends React.Component { - render() { - this.props.relay.environment; - return

{this.props.publicProp}
; - } - } - - const MyFragmentContainer = createFragmentContainer( - TodoListView, - { - item: graphql` - fragment TodoItem_item on Todo { - text - isComplete - } - `, - } - ); - - function doesNotRequireRelayPropToBeProvided() { - ; - } -}; - // ~~~~~~~~~~~~~~~~~~~~~ // Modern RefetchContainer // ~~~~~~~~~~~~~~~~~~~~~ -() => { - interface StoryInterface { - id: string; - } +type StoryLike = (storyID: string) => void; + +// Artifact produced by relay-compiler-language-typescript +enum _Story_story$ref {} +type Story_story$ref = _Story_story$ref & ConcreteFragment; +interface Story_story { + readonly id: string; + readonly text: string; + readonly isPublished: boolean; + readonly " $refType": Story_story$ref; +} + +const Story = (() => { interface Props { relay: RelayRefetchProp; - loadMoreTitle: string; - feed: { - stories: { edges: Array<{ node: StoryInterface }> }; - }; + story: Story_story; + onLike: StoryLike; } - class Story extends React.Component<{ story: StoryInterface }> {} - class FeedStories extends React.Component { + + interface State { + isLoading: boolean; + } + + class Story extends React.Component { + state = { + isLoading: false + }; + + componentDidMount() { + setInterval(this.handleRefresh.bind(this), 1000); + } + + handleRefresh() { + this.setState({ isLoading: true }); + this.props.relay.refetch({ id: this.props.story.id }, {}, error => { + this.setState({ isLoading: false }); + }, { force: true }); + } + render() { return (
- {this.props.feed.stories.edges.map(edge => )} -
); } - - _loadMore() { - // Increments the number of stories being rendered by 10. - const refetchVariables = (fragmentVariables: { count: number }) => ({ - count: fragmentVariables.count + 10, - }); - this.props.relay.refetch(refetchVariables); - } } - const FeedRefetchContainer = createRefetchContainer( - FeedStories, + const StoryRefetchContainer = createRefetchContainer( + Story, { - feed: graphql.experimental` - fragment FeedStories_feed on Feed @argumentDefinitions(count: { type: "Int", defaultValue: 10 }) { - stories(first: $count) { - edges { - node { - id - ...Story_story - } - } - } + story: graphql` + fragment Story_story on Todo { + id + text + isPublished } `, }, graphql.experimental` - query FeedStoriesRefetchQuery($count: Int) { - feed { - ...FeedStories_feed @arguments(count: $count) + query StoryRefetchQuery($id: ID!) { + story(id: $id) { + ...Story_story } } ` ); function doesNotRequireRelayPropToBeProvided() { - const feed = { stories: { edges: [] }}; // TODO - ; + const onLike = (id: string) => console.log(`Liked story #${id}`); + const story: { " $fragmentRefs": Story_story$ref } = {} as any; + // TODO: Fix requirement to cast fragment reference as `any`. + ; } -}; + + return StoryRefetchContainer; +})(); + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern FragmentContainer +// ~~~~~~~~~~~~~~~~~~~~~ + +// Artifact produced by relay-compiler-language-typescript +enum _FeedStories_feed$ref {} +type FeedStories_feed$ref = _FeedStories_feed$ref & ConcreteFragment; +interface FeedStories_feed { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly id: string; + readonly " $fragmentRefs": Story_story$ref; + }; + }>; + readonly " $refType": FeedStories_feed$ref; +} + +const Feed = (() => { + interface Props { + relay: RelayProp; + feed: FeedStories_feed; + onStoryLike: StoryLike; + } + + const FeedStories: React.SFC = ({ feed, onStoryLike, relay }) => { + // TODO: Getting env here for no good reason other than needing to test it works. + // If you have a good relavant example, please update! + relay.environment; + const stories = feed.edges.map(edge => { + // TODO: Fix requirement to cast fragment reference as `any`. + return ; + }); + return
{stories}
; + }; + + const FeedFragmentContainer = createFragmentContainer( + FeedStories, + { + feed: graphql` + fragment FeedStories_feed on Feed { + edges { + node { + id + ...Story_story + } + } + } + `, + } + ); + + function doesNotRequireRelayPropToBeProvided() { + const onStoryLike = (id: string) => console.log(`Liked story #${id}`); + const feed: { " $fragmentRefs": FeedStories_feed$ref } = {} as any; + // TODO: Fix requirement to cast fragment reference as `any`. + ; + } + + return FeedFragmentContainer; +})(); // ~~~~~~~~~~~~~~~~~~~~~ // Modern PaginationContainer // ~~~~~~~~~~~~~~~~~~~~~ -() => { - interface StoryInterface { - id: string; - } - class Story extends React.Component<{ story: StoryInterface }> {} +// Artifact produced by relay-compiler-language-typescript +enum _UserFeed_user$ref {} +type UserFeed_user$ref = _UserFeed_user$ref & ConcreteFragment; +interface UserFeed_user { + readonly feed: { + readonly pageInfo: { + readonly endCursor?: string | null; + readonly hasNextPage: boolean; + }; + readonly " $fragmentRefs": FeedStories_feed$ref; + }; + readonly " $refType": UserFeed_user$ref; +} +() => { interface Props { relay: RelayPaginationProp; loadMoreTitle: string; - user: { feed: { edges: Array<{ node: StoryInterface }> } }; + user: UserFeed_user; } - class Feed extends React.Component { + + class UserFeed extends React.Component { render() { + const onStoryLike = (id: string) => console.log(`Liked story #${id}`); + // TODO: Fix requirement to cast fragment reference as `any`. + const feed = this.props.user.feed as any; return (
- {this.props.user.feed.edges.map(edge => )} +
); @@ -191,21 +250,20 @@ const MyQueryRenderer = (props: { name: string }) => ( } } - const FeedPaginationContainer = createPaginationContainer( - Feed, + const UserFeedPaginationContainer = createPaginationContainer( + UserFeed, { user: graphql` - fragment Feed_user on User { + fragment UserFeed_user on User { feed( first: $count after: $cursor orderby: $orderBy # other variables ) @connection(key: "Feed_feed") { - edges { - node { - id - ...Story_story - } + ...FeedStories_feed + pageInfo { + endCursor + hasNextPage } } } @@ -214,7 +272,8 @@ const MyQueryRenderer = (props: { name: string }) => ( { direction: "forward", getConnectionFromProps(props) { - return props.user && props.user.feed; + // TODO: Fix requirement to have `edges` and both `pageInfo` details for forward and backward pagination + return props.user && props.user.feed as any; }, getFragmentVariables(prevVars, totalCount) { return { @@ -243,8 +302,9 @@ const MyQueryRenderer = (props: { name: string }) => ( ); function doesNotRequireRelayPropToBeProvided() { - const user = { feed: { edges: [] }}; // TODO - ; + const user: { " $fragmentRefs": UserFeed_user$ref } = {} as any; + // TODO: Fix requirement to cast fragment reference as `any`. + ; } }; diff --git a/types/relay-runtime/index.d.ts b/types/relay-runtime/index.d.ts index a11a632956..4c8c7193d0 100644 --- a/types/relay-runtime/index.d.ts +++ b/types/relay-runtime/index.d.ts @@ -37,9 +37,10 @@ export type RelayContainer = any; // ~~~~~~~~~~~~~~~~~~~~~ // File: https://github.com/facebook/relay/blob/fe0e70f10bbcba1fff89911313ea69f24569464b/packages/relay-runtime/util/RelayConcreteNode.js -export type ConcreteFragment = any; -export type ConcreteRequest = any; -export type ConcreteBatchRequest = any; +// Using enum here to create a distinct types. +export enum ConcreteFragment {} +export enum ConcreteRequest {} +export enum ConcreteBatchRequest {} export type RequestNode = ConcreteRequest | ConcreteBatchRequest; From 14f4b6d24e11c6eed8e7df4e6eac9b4c48be2450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Sun, 4 Feb 2018 01:04:33 +0100 Subject: [PATCH 06/12] [relay] Use `const enum` for unique fragment types. --- types/relay-runtime/index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/relay-runtime/index.d.ts b/types/relay-runtime/index.d.ts index 4c8c7193d0..da13713a8e 100644 --- a/types/relay-runtime/index.d.ts +++ b/types/relay-runtime/index.d.ts @@ -38,9 +38,9 @@ export type RelayContainer = any; // File: https://github.com/facebook/relay/blob/fe0e70f10bbcba1fff89911313ea69f24569464b/packages/relay-runtime/util/RelayConcreteNode.js // Using enum here to create a distinct types. -export enum ConcreteFragment {} -export enum ConcreteRequest {} -export enum ConcreteBatchRequest {} +export const enum ConcreteFragment {} +export const enum ConcreteRequest {} +export const enum ConcreteBatchRequest {} export type RequestNode = ConcreteRequest | ConcreteBatchRequest; From e1dd20d21c98674c24d4cd078d0af1704d090e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Sun, 4 Feb 2018 01:04:59 +0100 Subject: [PATCH 07/12] [relay] Update tests for emitted TS artifacts. --- types/react-relay/test/react-relay-tests.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index fc150d7b3d..782e4da522 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -1,3 +1,5 @@ +// tslint:disable:interface-over-type-literal + import * as React from "react"; import { Environment, Network, RecordSource, Store, ConnectionHandler, ConcreteFragment } from "relay-runtime"; @@ -59,14 +61,14 @@ const MyQueryRenderer = (props: { name: string }) => ( type StoryLike = (storyID: string) => void; // Artifact produced by relay-compiler-language-typescript -enum _Story_story$ref {} +const enum _Story_story$ref {} type Story_story$ref = _Story_story$ref & ConcreteFragment; -interface Story_story { +type Story_story = { readonly id: string; readonly text: string; readonly isPublished: boolean; readonly " $refType": Story_story$ref; -} +}; const Story = (() => { interface Props { @@ -142,9 +144,9 @@ const Story = (() => { // ~~~~~~~~~~~~~~~~~~~~~ // Artifact produced by relay-compiler-language-typescript -enum _FeedStories_feed$ref {} +const enum _FeedStories_feed$ref {} type FeedStories_feed$ref = _FeedStories_feed$ref & ConcreteFragment; -interface FeedStories_feed { +type FeedStories_feed = { readonly edges: ReadonlyArray<{ readonly node: { readonly id: string; @@ -152,7 +154,7 @@ interface FeedStories_feed { }; }>; readonly " $refType": FeedStories_feed$ref; -} +}; const Feed = (() => { interface Props { @@ -203,9 +205,9 @@ const Feed = (() => { // ~~~~~~~~~~~~~~~~~~~~~ // Artifact produced by relay-compiler-language-typescript -enum _UserFeed_user$ref {} +const enum _UserFeed_user$ref {} type UserFeed_user$ref = _UserFeed_user$ref & ConcreteFragment; -interface UserFeed_user { +type UserFeed_user = { readonly feed: { readonly pageInfo: { readonly endCursor?: string | null; @@ -214,7 +216,7 @@ interface UserFeed_user { readonly " $fragmentRefs": FeedStories_feed$ref; }; readonly " $refType": UserFeed_user$ref; -} +}; () => { interface Props { From 4a5279010a64e0978b1bfe8fae32a810dd34e31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Sun, 4 Feb 2018 01:25:50 +0100 Subject: [PATCH 08/12] [relay] ConcreteFragment should have been FragmentReference. --- types/react-relay/test/react-relay-tests.tsx | 8 ++++---- types/relay-runtime/index.d.ts | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 782e4da522..2cfa9bb893 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -1,7 +1,7 @@ // tslint:disable:interface-over-type-literal import * as React from "react"; -import { Environment, Network, RecordSource, Store, ConnectionHandler, ConcreteFragment } from "relay-runtime"; +import { Environment, Network, RecordSource, Store, ConnectionHandler, FragmentReference } from "relay-runtime"; import { graphql, @@ -62,7 +62,7 @@ type StoryLike = (storyID: string) => void; // Artifact produced by relay-compiler-language-typescript const enum _Story_story$ref {} -type Story_story$ref = _Story_story$ref & ConcreteFragment; +type Story_story$ref = _Story_story$ref & FragmentReference; type Story_story = { readonly id: string; readonly text: string; @@ -145,7 +145,7 @@ const Story = (() => { // Artifact produced by relay-compiler-language-typescript const enum _FeedStories_feed$ref {} -type FeedStories_feed$ref = _FeedStories_feed$ref & ConcreteFragment; +type FeedStories_feed$ref = _FeedStories_feed$ref & FragmentReference; type FeedStories_feed = { readonly edges: ReadonlyArray<{ readonly node: { @@ -206,7 +206,7 @@ const Feed = (() => { // Artifact produced by relay-compiler-language-typescript const enum _UserFeed_user$ref {} -type UserFeed_user$ref = _UserFeed_user$ref & ConcreteFragment; +type UserFeed_user$ref = _UserFeed_user$ref & FragmentReference; type UserFeed_user = { readonly feed: { readonly pageInfo: { diff --git a/types/relay-runtime/index.d.ts b/types/relay-runtime/index.d.ts index da13713a8e..60d5682309 100644 --- a/types/relay-runtime/index.d.ts +++ b/types/relay-runtime/index.d.ts @@ -38,12 +38,14 @@ export type RelayContainer = any; // File: https://github.com/facebook/relay/blob/fe0e70f10bbcba1fff89911313ea69f24569464b/packages/relay-runtime/util/RelayConcreteNode.js // Using enum here to create a distinct types. -export const enum ConcreteFragment {} -export const enum ConcreteRequest {} -export const enum ConcreteBatchRequest {} +export type ConcreteFragment = any; +export type ConcreteRequest = any; +export type ConcreteBatchRequest = any; export type RequestNode = ConcreteRequest | ConcreteBatchRequest; +export const enum FragmentReference {} + // ~~~~~~~~~~~~~~~~~~~~~ // RelayQL // ~~~~~~~~~~~~~~~~~~~~~ From 26b18f911e691cd6fcf8fc982ea20e60bddc28e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Tue, 6 Feb 2018 15:51:49 +0100 Subject: [PATCH 09/12] [relay] Make sure optional props remain optional. --- types/react-relay/index.d.ts | 4 +++- types/react-relay/test/react-relay-tests.tsx | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 259896846f..fb1cca3c29 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -21,8 +21,10 @@ import * as RelayRuntimeTypes from "relay-runtime"; // ~~~~~~~~~~~~~~~~~~~~~ // Utility types // ~~~~~~~~~~~~~~~~~~~~~ + +// Taken from https://github.com/pelotom/type-zoo type Diff = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]; -type Omit = { [P in Diff]: T[P] }; +type Omit = Pick>; type RemoveRelayProp

= Omit

; diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 2cfa9bb893..116a0395e1 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -75,6 +75,7 @@ const Story = (() => { relay: RelayRefetchProp; story: Story_story; onLike: StoryLike; + ignoreMe?: {}; } interface State { @@ -161,6 +162,7 @@ const Feed = (() => { relay: RelayProp; feed: FeedStories_feed; onStoryLike: StoryLike; + ignoreMe?: {}; } const FeedStories: React.SFC = ({ feed, onStoryLike, relay }) => { @@ -223,6 +225,7 @@ type UserFeed_user = { relay: RelayPaginationProp; loadMoreTitle: string; user: UserFeed_user; + ignoreMe?: {}; } class UserFeed extends React.Component { From cda26c85c2ebee75bdd2eae53cc391e1f5028d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Tue, 6 Feb 2018 15:52:51 +0100 Subject: [PATCH 10/12] [relay] Expose `componentRef` prop of containers. --- types/react-relay/index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index fb1cca3c29..34f3a500ae 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -92,7 +92,7 @@ export class QueryRenderer extends ReactRelayQueryRenderer {} export function createFragmentContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap -): React.ComponentType>; +): React.ComponentType & { componentRef?: any }>; // ~~~~~~~~~~~~~~~~~~~~~ // createPaginationContainer @@ -140,7 +140,7 @@ export function createPaginationContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, connectionConfig: ConnectionConfig -): React.ComponentType>; +): React.ComponentType & { componentRef?: any }>; // ~~~~~~~~~~~~~~~~~~~~~ // createRefetchContainer @@ -163,4 +163,4 @@ export function createRefetchContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, taggedNode: RelayRuntimeTypes.GraphQLTaggedNode -): React.ComponentType>; +): React.ComponentType & { componentRef?: any }>; From eee0c90fa61ff2b1c44df8e005306eaf843cbdf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Tue, 6 Feb 2018 17:02:15 +0100 Subject: [PATCH 11/12] [relay] Fix lint issues. --- types/react-relay/index.d.ts | 3 +++ types/react-relay/test/react-relay-tests.tsx | 8 ++++++-- types/relay-runtime/index.d.ts | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 34f3a500ae..3b2e00e147 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -23,9 +23,12 @@ import * as RelayRuntimeTypes from "relay-runtime"; // ~~~~~~~~~~~~~~~~~~~~~ // Taken from https://github.com/pelotom/type-zoo +// tslint:disable-next-line:strict-export-declare-modifiers type Diff = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]; +// tslint:disable-next-line:strict-export-declare-modifiers type Omit = Pick>; +// tslint:disable-next-line:strict-export-declare-modifiers type RemoveRelayProp

= Omit

; // ~~~~~~~~~~~~~~~~~~~~~ diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 116a0395e1..cc25f5f368 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -1,5 +1,3 @@ -// tslint:disable:interface-over-type-literal - import * as React from "react"; import { Environment, Network, RecordSource, Store, ConnectionHandler, FragmentReference } from "relay-runtime"; @@ -61,8 +59,10 @@ const MyQueryRenderer = (props: { name: string }) => ( type StoryLike = (storyID: string) => void; // Artifact produced by relay-compiler-language-typescript +// tslint:disable-next-line:no-const-enum const enum _Story_story$ref {} type Story_story$ref = _Story_story$ref & FragmentReference; +// tslint:disable-next-line:interface-over-type-literal type Story_story = { readonly id: string; readonly text: string; @@ -145,8 +145,10 @@ const Story = (() => { // ~~~~~~~~~~~~~~~~~~~~~ // Artifact produced by relay-compiler-language-typescript +// tslint:disable-next-line:no-const-enum const enum _FeedStories_feed$ref {} type FeedStories_feed$ref = _FeedStories_feed$ref & FragmentReference; +// tslint:disable-next-line:interface-over-type-literal type FeedStories_feed = { readonly edges: ReadonlyArray<{ readonly node: { @@ -207,8 +209,10 @@ const Feed = (() => { // ~~~~~~~~~~~~~~~~~~~~~ // Artifact produced by relay-compiler-language-typescript +// tslint:disable-next-line:no-const-enum const enum _UserFeed_user$ref {} type UserFeed_user$ref = _UserFeed_user$ref & FragmentReference; +// tslint:disable-next-line:interface-over-type-literal type UserFeed_user = { readonly feed: { readonly pageInfo: { diff --git a/types/relay-runtime/index.d.ts b/types/relay-runtime/index.d.ts index 60d5682309..2348a4d06b 100644 --- a/types/relay-runtime/index.d.ts +++ b/types/relay-runtime/index.d.ts @@ -37,13 +37,14 @@ export type RelayContainer = any; // ~~~~~~~~~~~~~~~~~~~~~ // File: https://github.com/facebook/relay/blob/fe0e70f10bbcba1fff89911313ea69f24569464b/packages/relay-runtime/util/RelayConcreteNode.js -// Using enum here to create a distinct types. export type ConcreteFragment = any; export type ConcreteRequest = any; export type ConcreteBatchRequest = any; export type RequestNode = ConcreteRequest | ConcreteBatchRequest; +// Using `enum` here to create a distinct type and `const` to ensure it doesn’t leave any generated code. +// tslint:disable-next-line:no-const-enum export const enum FragmentReference {} // ~~~~~~~~~~~~~~~~~~~~~ From 4baa6ab4c6110ca66ae30b65041460be3b8a3dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Tue, 6 Feb 2018 17:21:12 +0100 Subject: [PATCH 12/12] [relay] Improve typing of `componentRef` and refactor slightly. --- types/react-relay/index.d.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 3b2e00e147..55848cb47c 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -28,8 +28,13 @@ type Diff = ({ [P in T]: P } & { [P in U]: n // tslint:disable-next-line:strict-export-declare-modifiers type Omit = Pick>; -// tslint:disable-next-line:strict-export-declare-modifiers -type RemoveRelayProp

= Omit

; +export type RemoveRelayProp

= Omit

; + +export interface ComponentRef { + componentRef?: (ref: any) => void; +} + +export type RelayContainer = React.ComponentType & ComponentRef>; // ~~~~~~~~~~~~~~~~~~~~~ // Maybe Fix @@ -95,7 +100,7 @@ export class QueryRenderer extends ReactRelayQueryRenderer {} export function createFragmentContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap -): React.ComponentType & { componentRef?: any }>; +): RelayContainer; // ~~~~~~~~~~~~~~~~~~~~~ // createPaginationContainer @@ -143,7 +148,7 @@ export function createPaginationContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, connectionConfig: ConnectionConfig -): React.ComponentType & { componentRef?: any }>; +): RelayContainer; // ~~~~~~~~~~~~~~~~~~~~~ // createRefetchContainer @@ -166,4 +171,4 @@ export function createRefetchContainer( Component: React.ComponentType, fragmentSpec: RelayRuntimeTypes.GraphQLTaggedNode | GeneratedNodeMap, taggedNode: RelayRuntimeTypes.GraphQLTaggedNode -): React.ComponentType & { componentRef?: any }>; +): RelayContainer;