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;