Finish porting to relay-1.7 including types

This commit is contained in:
Chi Vinh Le
2018-08-30 23:49:38 +02:00
parent 0dcff65344
commit 942ce366b8
23 changed files with 94 additions and 52 deletions
+3 -3
View File
@@ -24256,9 +24256,9 @@
"dev": true
},
"typescript": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz",
"integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.3.tgz",
"integrity": "sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==",
"dev": true
},
"ua-parser-js": {
+1 -1
View File
@@ -222,7 +222,7 @@
"typed-css-modules": "^0.3.4",
"typeface-manuale": "0.0.54",
"typeface-source-sans-pro": "0.0.54",
"typescript": "^3.0.0",
"typescript": "^3.0.3",
"uglifyjs-webpack-plugin": "^1.2.5",
"webpack": "4.12.0",
"webpack-cli": "^3.0.2",
@@ -14,7 +14,7 @@ const AppContainer: StatelessComponent<InnerProps> = ({ local: { view } }) => {
return <App view={view} />;
};
const enhanced = withLocalStateContainer<Local>(
const enhanced = withLocalStateContainer(
graphql`
fragment AppContainerLocal on Local {
view
@@ -0,0 +1,7 @@
import { _RefType } from "react-relay";
export type FragmentKeys<T> = {
[P in keyof T]: T[P] extends _RefType<any> | null ? P : never
}[keyof T];
export type FragmentKeysNoLocal<T> = Exclude<FragmentKeys<T>, "local">;
@@ -1,12 +1,22 @@
import { createFragmentContainer, GraphQLTaggedNode } from "react-relay";
import {
_RefType,
createFragmentContainer,
FragmentOrRegularProp,
GraphQLTaggedNode,
} from "react-relay";
import { InferableComponentEnhancerWithProps } from "recompose";
import { FragmentKeysNoLocal } from "./types";
/**
* withFragmentContainer is a curried version of `createFragmentContainers`
* from Relay.
*/
export default <T>(
fragmentSpec: { [P in keyof T]: GraphQLTaggedNode }
): InferableComponentEnhancerWithProps<T, { [P in keyof T]: any }> => (
component: React.ComponentType<any>
) => createFragmentContainer(component, fragmentSpec) as any;
fragmentSpec: { [P in FragmentKeysNoLocal<T>]: GraphQLTaggedNode } & {
_?: never;
}
): InferableComponentEnhancerWithProps<
{ [P in FragmentKeysNoLocal<T>]: T[P] },
{ [P in FragmentKeysNoLocal<T>]: FragmentOrRegularProp<T[P]> }
> => (component: React.ComponentType<any>) =>
createFragmentContainer(component, fragmentSpec) as any;
@@ -13,6 +13,7 @@ import {
GraphQLTaggedNode,
} from "relay-runtime";
import { _RefType } from "react-relay";
import { withContext } from "../bootstrap";
interface Props {
@@ -35,9 +36,9 @@ export const LOCAL_ID = "client:root.local";
* The `fragmentSpec` must be a `Fragment` on the `LOCAL_TYPE` which
* must have the `LOCAL_ID`.
*/
function withLocalStateContainer<T>(
function withLocalStateContainer(
fragmentSpec: GraphQLTaggedNode
): InferableComponentEnhancer<{ local: T }> {
): InferableComponentEnhancer<{ local: _RefType<any> }> {
return compose(
withContext(({ relayEnvironment }) => ({ relayEnvironment })),
hoistStatics((BaseComponent: React.ComponentType<any>) => {
@@ -1,24 +1,24 @@
import {
ConnectionConfig,
createPaginationContainer,
FragmentOrRegularProp,
GraphQLTaggedNode,
RelayPaginationProp,
} from "react-relay";
import { InferableComponentEnhancerWithProps } from "recompose";
import { FragmentKeysNoLocal } from "./types";
/**
* withPaginationContainer is a curried version of `createPaginationContainers`
* from Relay.
*/
export default <T, InnerProps, FragmentVariables, QueryVariables>(
fragmentSpec: { [P in keyof T]: GraphQLTaggedNode },
connectionConfig: ConnectionConfig<
InnerProps,
FragmentVariables,
QueryVariables
>
export default <T, FragmentVariables, QueryVariables>(
fragmentSpec: { [P in FragmentKeysNoLocal<T>]: GraphQLTaggedNode } & {
_?: never;
},
connectionConfig: ConnectionConfig<T, FragmentVariables, QueryVariables>
): InferableComponentEnhancerWithProps<
T & { relay: RelayPaginationProp },
{ [P in keyof T]: any }
{ [P in FragmentKeysNoLocal<T>]: T[P] } & { relay: RelayPaginationProp },
{ [P in FragmentKeysNoLocal<T>]: FragmentOrRegularProp<T[P]> }
> => (component: React.ComponentType<any>) =>
createPaginationContainer(component, fragmentSpec, connectionConfig) as any;
@@ -1,19 +1,23 @@
import {
createRefetchContainer,
FragmentOrRegularProp,
GraphQLTaggedNode,
RelayRefetchProp,
} from "react-relay";
import { InferableComponentEnhancerWithProps } from "recompose";
import { FragmentKeysNoLocal } from "./types";
/**
* withRefetchContainer is a curried version of `createRefetchContainers`
* from Relay.
*/
export default <T>(
fragmentSpec: { [P in keyof T]: GraphQLTaggedNode },
fragmentSpec: { [P in FragmentKeysNoLocal<T>]: GraphQLTaggedNode } & {
_?: never;
},
refetchQuery: GraphQLTaggedNode
): InferableComponentEnhancerWithProps<
T & { relay: RelayRefetchProp },
{ [P in keyof T]: any }
{ [P in FragmentKeysNoLocal<T>]: T[P] } & { relay: RelayRefetchProp },
{ [P in FragmentKeysNoLocal<T>]: FragmentOrRegularProp<T[P]> }
> => (component: React.ComponentType<any>) =>
createRefetchContainer(component, fragmentSpec, refetchQuery) as any;
@@ -1,13 +1,14 @@
import { Localized } from "fluent-react/compat";
import React, { MouseEvent, StatelessComponent } from "react";
import { PropTypesOf } from "talk-framework/types";
import { Button, Typography } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
import * as styles from "./PermalinkView.css";
export interface PermalinkViewProps {
comment: {} | null;
comment: PropTypesOf<typeof CommentContainer>["data"] | null;
showAllCommentsHref: string | null;
onShowAllComments: (e: MouseEvent<any>) => void;
}
@@ -3,24 +3,27 @@ import { noop } from "lodash";
import React from "react";
import sinon, { SinonSpy } from "sinon";
import { removeFragmentRefs } from "talk-framework/testHelpers";
import { PropTypesOf } from "talk-framework/types";
import ReplyList from "./ReplyList";
const ReplyListN = removeFragmentRefs(ReplyList);
it("renders correctly", () => {
const props: PropTypesOf<typeof ReplyList> = {
const props: PropTypesOf<typeof ReplyListN> = {
commentID: "comment-id",
comments: [{ id: "comment-1" }, { id: "comment-2" }],
onShowAll: noop,
hasMore: false,
disableShowAll: false,
};
const wrapper = shallow(<ReplyList {...props} />);
const wrapper = shallow(<ReplyListN {...props} />);
expect(wrapper).toMatchSnapshot();
});
describe("when there is more", () => {
const props: PropTypesOf<typeof ReplyList> = {
const props: PropTypesOf<typeof ReplyListN> = {
commentID: "comment-id",
comments: [{ id: "comment-1" }, { id: "comment-2" }],
onShowAll: sinon.spy(),
@@ -28,7 +31,7 @@ describe("when there is more", () => {
disableShowAll: false,
};
const wrapper = shallow(<ReplyList {...props} />);
const wrapper = shallow(<ReplyListN {...props} />);
it("renders a load more button", () => {
expect(wrapper).toMatchSnapshot();
});
@@ -41,7 +44,7 @@ describe("when there is more", () => {
});
const wrapperDisabledButton = shallow(
<ReplyList {...props} disableShowAll />
<ReplyListN {...props} disableShowAll />
);
it("disables load more button", () => {
expect(wrapperDisabledButton).toMatchSnapshot();
@@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import { PropTypesOf } from "talk-framework/types";
import { Button, HorizontalGutter } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
@@ -9,7 +10,9 @@ import Indent from "./Indent";
export interface ReplyListProps {
commentID: string;
comments: ReadonlyArray<{ id: string }>;
comments: ReadonlyArray<
{ id: string } & PropTypesOf<typeof CommentContainer>["data"]
>;
onShowAll: () => void;
hasMore: boolean;
disableShowAll: boolean;
@@ -3,12 +3,15 @@ import { noop } from "lodash";
import React from "react";
import sinon, { SinonSpy } from "sinon";
import { removeFragmentRefs } from "talk-framework/testHelpers";
import { PropTypesOf } from "talk-framework/types";
import Stream from "./Stream";
const StreamN = removeFragmentRefs(Stream);
it("renders correctly", () => {
const props: PropTypesOf<typeof Stream> = {
const props: PropTypesOf<typeof StreamN> = {
assetID: "asset-id",
isClosed: false,
comments: [{ id: "comment-1" }, { id: "comment-2" }],
@@ -17,13 +20,13 @@ it("renders correctly", () => {
hasMore: false,
user: null,
};
const wrapper = shallow(<Stream {...props} />);
const wrapper = shallow(<StreamN {...props} />);
expect(wrapper).toMatchSnapshot();
});
describe("when use is logged in", () => {
it("renders correctly", () => {
const props: PropTypesOf<typeof Stream> = {
const props: PropTypesOf<typeof StreamN> = {
assetID: "asset-id",
isClosed: false,
comments: [{ id: "comment-1" }, { id: "comment-2" }],
@@ -32,13 +35,13 @@ describe("when use is logged in", () => {
hasMore: false,
user: {},
};
const wrapper = shallow(<Stream {...props} />);
const wrapper = shallow(<StreamN {...props} />);
expect(wrapper).toMatchSnapshot();
});
});
describe("when there is more", () => {
const props: PropTypesOf<typeof Stream> = {
const props: PropTypesOf<typeof StreamN> = {
assetID: "asset-id",
isClosed: false,
comments: [{ id: "comment-1" }, { id: "comment-2" }],
@@ -48,7 +51,7 @@ describe("when there is more", () => {
user: null,
};
const wrapper = shallow(<Stream {...props} />);
const wrapper = shallow(<StreamN {...props} />);
it("renders a load more button", () => {
expect(wrapper).toMatchSnapshot();
});
@@ -58,7 +61,7 @@ describe("when there is more", () => {
expect((props.onLoadMore as SinonSpy).calledOnce).toBe(true);
});
const wrapperDisabledButton = shallow(<Stream {...props} disableLoadMore />);
const wrapperDisabledButton = shallow(<StreamN {...props} disableLoadMore />);
it("disables load more button", () => {
expect(wrapperDisabledButton).toMatchSnapshot();
});
+6 -2
View File
@@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import { PropTypesOf } from "talk-framework/types";
import { Button, HorizontalGutter } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
@@ -14,11 +15,14 @@ import * as styles from "./Stream.css";
export interface StreamProps {
assetID: string;
isClosed?: boolean;
comments: ReadonlyArray<{ id: string }>;
comments: ReadonlyArray<
{ id: string } & PropTypesOf<typeof CommentContainer>["data"] &
PropTypesOf<typeof ReplyListContainer>["comment"]
>;
onLoadMore?: () => void;
hasMore?: boolean;
disableLoadMore?: boolean;
user: {} | null;
user: PropTypesOf<typeof UserBoxContainer>["user"] | null;
}
const Stream: StatelessComponent<StreamProps> = props => {
@@ -16,7 +16,7 @@ const AppContainer: StatelessComponent<InnerProps> = ({
return <App showPermalinkView={!!commentID} />;
};
const enhanced = withLocalStateContainer<Local>(
const enhanced = withLocalStateContainer(
graphql`
fragment AppContainerLocal on Local {
commentID
@@ -28,7 +28,7 @@ export const CommentContainer: StatelessComponent<InnerProps> = props => {
return <Comment {...rest} {...props.data} />;
};
const enhanced = withFragmentContainer<{ data: Data }>({
const enhanced = withFragmentContainer<InnerProps>({
data: graphql`
fragment CommentContainer on Comment {
...CommentContainer_comment @relay(mask: false)
@@ -19,7 +19,7 @@ export const PermalinkContainer: StatelessComponent<InnerProps> = ({
) : null;
};
const enhanced = withLocalStateContainer<Local>(
const enhanced = withLocalStateContainer(
graphql`
fragment PermalinkButtonContainerLocal on Local {
assetURL
@@ -52,9 +52,7 @@ const enhanced = withContext(ctx => ({
pym: ctx.pym,
}))(
withSetCommentIDMutation(
withFragmentContainer<{
comment: CommentData | null;
}>({
withFragmentContainer<PermalinkViewContainerProps>({
comment: graphql`
fragment PermalinkViewContainer_comment on Comment {
...CommentContainer
@@ -67,7 +67,6 @@ interface FragmentVariables {
}
const enhanced = withPaginationContainer<
{ comment: Data },
InnerProps,
FragmentVariables,
ReplyListContainerPaginationQueryVariables
@@ -64,7 +64,6 @@ interface FragmentVariables {
}
const enhanced = withPaginationContainer<
{ asset: AssetData; user: UserData | null },
InnerProps,
FragmentVariables,
StreamContainerPaginationQueryVariables
@@ -78,7 +78,7 @@ export class UserBoxContainer extends Component<InnerProps> {
const enhanced = withSignOutMutation(
withSetAuthPopupStateMutation(
withShowAuthPopupMutation(
withLocalStateContainer<Local>(
withLocalStateContainer(
graphql`
fragment UserBoxContainerLocal on Local {
authPopup {
@@ -89,7 +89,7 @@ const enhanced = withSignOutMutation(
}
`
)(
withFragmentContainer<{ user: UserData | null }>({
withFragmentContainer<InnerProps>({
user: graphql`
fragment UserBoxContainer_user on User {
username
@@ -48,7 +48,7 @@ const PermalinkViewQuery: StatelessComponent<InnerProps> = ({
/>
);
const enhanced = withLocalStateContainer<Local>(
const enhanced = withLocalStateContainer(
graphql`
fragment PermalinkViewQueryLocal on Local {
commentID
@@ -1,3 +1,4 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { ReadyState } from "react-relay";
import {
@@ -23,6 +24,13 @@ export const render = ({
}
if (props) {
if (!props.asset) {
return (
<Localized id="comments-streamQuery-assetNotFound">
<div>Asset not found</div>
</Localized>
);
}
return <StreamContainer asset={props.asset} user={props.me} />;
}
@@ -54,7 +62,7 @@ const StreamQuery: StatelessComponent<InnerProps> = ({
/>
);
const enhanced = withLocalStateContainer<Local>(
const enhanced = withLocalStateContainer(
graphql`
fragment StreamQueryLocal on Local {
assetID
+2
View File
@@ -2,6 +2,8 @@
## Comments Tab
comments-streamQuery-assetNotFound = Asset not found
comments-postCommentForm-submit = Submit
comments-stream-loadMore = Load more
comments-replyList-showAll = Show all