mirror of
https://github.com/wassname/talk.git
synced 2026-08-15 12:55:10 +08:00
Implement Replies
This commit is contained in:
+6
-1
@@ -15,7 +15,12 @@ const config: Config = {
|
||||
"core/client/stream/**/*.graphql",
|
||||
"core/client/server/**/*.graphql",
|
||||
],
|
||||
ignore: ["core/**/*.d.ts", "core/**/*.graphql.ts"],
|
||||
ignore: [
|
||||
"core/**/*.d.ts",
|
||||
"core/**/*.graphql.ts",
|
||||
"**/test/**/*",
|
||||
"core/**/*.spec.*",
|
||||
],
|
||||
executor: new CommandExecutor("npm run compile:relay-stream", {
|
||||
runOnInit: true,
|
||||
}),
|
||||
|
||||
@@ -224,6 +224,11 @@ module.exports = {
|
||||
jsx: "preserve",
|
||||
noEmit: false,
|
||||
},
|
||||
|
||||
// Overwrites the behavior of `include` and `exclude` to only
|
||||
// include files that are actually being imported and which
|
||||
// are necessary to compile the bundle.
|
||||
onlyCompileBundledFiles: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import React, { StatelessComponent } from "react";
|
||||
|
||||
export interface IndentProps {
|
||||
level?: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Indent: StatelessComponent<IndentProps> = props => {
|
||||
return (
|
||||
<div style={{ marginLeft: "16px", marginTop: "8px", marginBottom: "8px" }}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Indent;
|
||||
@@ -0,0 +1,36 @@
|
||||
import { shallow } from "enzyme";
|
||||
import { noop } from "lodash";
|
||||
import React from "react";
|
||||
import sinon from "sinon";
|
||||
|
||||
import ReplyList from "./ReplyList";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props = {
|
||||
commentID: "comment-id",
|
||||
comments: [{ id: "comment-1" }, { id: "comment-2" }],
|
||||
onLoadMore: noop,
|
||||
hasMore: false,
|
||||
};
|
||||
const wrapper = shallow(<ReplyList {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("when there is more", () => {
|
||||
const props = {
|
||||
commentID: "comment-id",
|
||||
comments: [{ id: "comment-1" }, { id: "comment-2" }],
|
||||
onLoadMore: sinon.spy(),
|
||||
hasMore: true,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<ReplyList {...props} />);
|
||||
it("renders a load more button", () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("calls onLoadMore", () => {
|
||||
wrapper.find("#talk-reply-list--show-all--comment-id").simulate("click");
|
||||
expect(props.onLoadMore.calledOnce).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import * as React from "react";
|
||||
import { StatelessComponent } from "react";
|
||||
|
||||
import { Button } from "talk-ui/components";
|
||||
|
||||
import CommentContainer from "../containers/CommentContainer";
|
||||
import Indent from "./Indent";
|
||||
|
||||
export interface ReplyListProps {
|
||||
commentID: string;
|
||||
comments: ReadonlyArray<{ id: string }>;
|
||||
onLoadMore: () => void;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
const ReplyList: StatelessComponent<ReplyListProps> = props => {
|
||||
return (
|
||||
<Indent>
|
||||
{props.comments.map(comment => (
|
||||
<CommentContainer key={comment.id} data={comment} gutterBottom />
|
||||
))}
|
||||
{props.hasMore && (
|
||||
<Button
|
||||
id={`talk-reply-list--show-all--${props.commentID}`}
|
||||
onClick={props.onLoadMore}
|
||||
secondary
|
||||
invert
|
||||
fullWidth
|
||||
>
|
||||
Show All Replies
|
||||
</Button>
|
||||
)}
|
||||
</Indent>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReplyList;
|
||||
@@ -7,7 +7,7 @@ import Stream from "./Stream";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props = {
|
||||
id: "asset-id",
|
||||
assetID: "asset-id",
|
||||
isClosed: false,
|
||||
comments: [{ id: "comment-1" }, { id: "comment-2" }],
|
||||
onLoadMore: noop,
|
||||
@@ -19,7 +19,7 @@ it("renders correctly", () => {
|
||||
|
||||
it("renders when comments is null", () => {
|
||||
const props = {
|
||||
id: "asset-id",
|
||||
assetID: "asset-id",
|
||||
isClosed: false,
|
||||
comments: null,
|
||||
onLoadMore: noop,
|
||||
@@ -31,7 +31,7 @@ it("renders when comments is null", () => {
|
||||
|
||||
describe("when there is more", () => {
|
||||
const props = {
|
||||
id: "asset-id",
|
||||
assetID: "asset-id",
|
||||
isClosed: false,
|
||||
comments: [{ id: "comment-1" }, { id: "comment-2" }],
|
||||
onLoadMore: sinon.spy(),
|
||||
@@ -44,7 +44,7 @@ describe("when there is more", () => {
|
||||
});
|
||||
|
||||
it("calls onLoadMore", () => {
|
||||
wrapper.find("#talk-stream--loadmore").simulate("click");
|
||||
wrapper.find("#talk-stream--load-more").simulate("click");
|
||||
expect(props.onLoadMore.calledOnce).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import * as React from "react";
|
||||
import { StatelessComponent } from "react";
|
||||
|
||||
import Logo from "talk-stream/components/Logo";
|
||||
import CommentContainer from "talk-stream/containers/CommentContainer";
|
||||
import PostCommentFormContainer from "talk-stream/containers/PostCommentFormContainer";
|
||||
import { Button } from "talk-ui/components";
|
||||
|
||||
import CommentContainer from "../containers/CommentContainer";
|
||||
import PostCommentFormContainer from "../containers/PostCommentFormContainer";
|
||||
import ReplyListContainer from "../containers/ReplyListContainer";
|
||||
import Logo from "./Logo";
|
||||
|
||||
export interface StreamProps {
|
||||
id: string;
|
||||
assetID: string;
|
||||
isClosed: boolean;
|
||||
comments: ReadonlyArray<{ id: string }> | null;
|
||||
onLoadMore: () => void;
|
||||
@@ -22,13 +24,16 @@ const Stream: StatelessComponent<StreamProps> = props => {
|
||||
return (
|
||||
<div>
|
||||
<Logo gutterBottom />
|
||||
<PostCommentFormContainer assetID={props.id} />
|
||||
<PostCommentFormContainer assetID={props.assetID} />
|
||||
{props.comments.map(comment => (
|
||||
<CommentContainer key={comment.id} data={comment} gutterBottom />
|
||||
<div key={comment.id}>
|
||||
<CommentContainer data={comment} gutterBottom />
|
||||
<ReplyListContainer comment={comment} />
|
||||
</div>
|
||||
))}
|
||||
{props.hasMore && (
|
||||
<Button
|
||||
id={"talk-stream--loadmore"}
|
||||
id={"talk-stream--load-more"}
|
||||
onClick={props.onLoadMore}
|
||||
secondary
|
||||
invert
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<Indent>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
key="comment-1"
|
||||
/>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
key="comment-2"
|
||||
/>
|
||||
</Indent>
|
||||
`;
|
||||
|
||||
exports[`when there is more renders a load more button 1`] = `
|
||||
<Indent>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
key="comment-1"
|
||||
/>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
key="comment-2"
|
||||
/>
|
||||
<withPropsOnChange(Button)
|
||||
fullWidth={true}
|
||||
id="talk-reply-list--show-all--comment-id"
|
||||
invert={true}
|
||||
onClick={[Function]}
|
||||
secondary={true}
|
||||
>
|
||||
Show All Replies
|
||||
</withPropsOnChange(Button)>
|
||||
</Indent>
|
||||
`;
|
||||
@@ -8,24 +8,44 @@ exports[`renders correctly 1`] = `
|
||||
<withContext(createMutationContainer(PostCommentFormContainer))
|
||||
assetID="asset-id"
|
||||
/>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
<div
|
||||
key="comment-1"
|
||||
/>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
gutterBottom={true}
|
||||
/>
|
||||
<Relay(ReplyListContainer)
|
||||
comment={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
key="comment-2"
|
||||
/>
|
||||
>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
/>
|
||||
<Relay(ReplyListContainer)
|
||||
comment={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -43,27 +63,47 @@ exports[`when there is more renders a load more button 1`] = `
|
||||
<withContext(createMutationContainer(PostCommentFormContainer))
|
||||
assetID="asset-id"
|
||||
/>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
<div
|
||||
key="comment-1"
|
||||
/>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
gutterBottom={true}
|
||||
/>
|
||||
<Relay(ReplyListContainer)
|
||||
comment={
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
key="comment-2"
|
||||
/>
|
||||
>
|
||||
<Relay(CommentContainer)
|
||||
data={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
}
|
||||
}
|
||||
gutterBottom={true}
|
||||
/>
|
||||
<Relay(ReplyListContainer)
|
||||
comment={
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<withPropsOnChange(Button)
|
||||
fullWidth={true}
|
||||
id="talk-stream--loadmore"
|
||||
id="talk-stream--load-more"
|
||||
invert={true}
|
||||
onClick={[Function]}
|
||||
secondary={true}
|
||||
|
||||
@@ -9,6 +9,16 @@ import Comment, { CommentProps } from "../components/Comment";
|
||||
|
||||
type InnerProps = { data: Data } & Omit<CommentProps, keyof Data>;
|
||||
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
graphql`
|
||||
fragment CommentContainer_comment on Comment {
|
||||
author {
|
||||
username
|
||||
}
|
||||
body
|
||||
}
|
||||
`;
|
||||
|
||||
export const CommentContainer: StatelessComponent<InnerProps> = props => {
|
||||
const { data, ...rest } = props;
|
||||
return <Comment {...rest} {...props.data} />;
|
||||
@@ -17,10 +27,7 @@ export const CommentContainer: StatelessComponent<InnerProps> = props => {
|
||||
const enhanced = withFragmentContainer<{ data: Data }>({
|
||||
data: graphql`
|
||||
fragment CommentContainer on Comment {
|
||||
author {
|
||||
username
|
||||
}
|
||||
body
|
||||
...CommentContainer_comment @relay(mask: false)
|
||||
}
|
||||
`,
|
||||
})(CommentContainer);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { shallow } from "enzyme";
|
||||
import { noop } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
import { ReplyListContainer } from "./ReplyListContainer";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props: any = {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
replies: {
|
||||
edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }],
|
||||
},
|
||||
},
|
||||
relay: {
|
||||
hasMore: noop,
|
||||
isLoading: noop,
|
||||
},
|
||||
};
|
||||
const wrapper = shallow(<ReplyListContainer {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders correctly when replies are null", () => {
|
||||
const props: any = {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
replies: null,
|
||||
},
|
||||
relay: {
|
||||
hasMore: noop,
|
||||
isLoading: noop,
|
||||
},
|
||||
};
|
||||
const wrapper = shallow(<ReplyListContainer {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,125 @@
|
||||
import React from "react";
|
||||
import { graphql, RelayPaginationProp } from "react-relay";
|
||||
|
||||
import { withPaginationContainer } from "talk-framework/lib/relay";
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
import { ReplyListContainer_comment as Data } from "talk-stream/__generated__/ReplyListContainer_comment.graphql";
|
||||
import {
|
||||
COMMENT_SORT,
|
||||
ReplyListContainerPaginationQueryVariables,
|
||||
} from "talk-stream/__generated__/ReplyListContainerPaginationQuery.graphql";
|
||||
|
||||
import ReplyList from "../components/ReplyList";
|
||||
|
||||
export interface InnerProps {
|
||||
comment: Data;
|
||||
relay: RelayPaginationProp;
|
||||
}
|
||||
|
||||
export class ReplyListContainer extends React.Component<InnerProps> {
|
||||
public render() {
|
||||
if (this.props.comment.replies === null) {
|
||||
return null;
|
||||
}
|
||||
const comments = this.props.comment.replies.edges.map(edge => edge.node);
|
||||
return (
|
||||
<ReplyList
|
||||
commentID={this.props.comment.id}
|
||||
comments={comments}
|
||||
onLoadMore={this.loadMore}
|
||||
hasMore={this.props.relay.hasMore()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private loadMore = () => {
|
||||
if (!this.props.relay.hasMore() || this.props.relay.isLoading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.relay.loadMore(
|
||||
999999999, // Fetch All Replies
|
||||
error => {
|
||||
if (error) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: (cvle) This should be autogenerated.
|
||||
interface FragmentVariables {
|
||||
count: number;
|
||||
cursor?: string;
|
||||
orderBy: COMMENT_SORT;
|
||||
}
|
||||
|
||||
const enhanced = withPaginationContainer<
|
||||
{ comment: Data },
|
||||
InnerProps,
|
||||
FragmentVariables,
|
||||
ReplyListContainerPaginationQueryVariables
|
||||
>(
|
||||
{
|
||||
comment: graphql`
|
||||
fragment ReplyListContainer_comment on Comment
|
||||
@argumentDefinitions(
|
||||
count: { type: "Int!", defaultValue: 5 }
|
||||
cursor: { type: "Cursor" }
|
||||
orderBy: { type: "COMMENT_SORT!", defaultValue: CREATED_AT_ASC }
|
||||
) {
|
||||
id
|
||||
replies(first: $count, after: $cursor, orderBy: $orderBy)
|
||||
@connection(key: "ReplyList_replies") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...CommentContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
direction: "forward",
|
||||
getConnectionFromProps(props) {
|
||||
return props.comment && props.comment.replies;
|
||||
},
|
||||
// This is also the default implementation of `getFragmentVariables` if it isn't provided.
|
||||
getFragmentVariables(prevVars, totalCount) {
|
||||
return {
|
||||
...prevVars,
|
||||
count: totalCount,
|
||||
};
|
||||
},
|
||||
getVariables(props, { count, cursor }, fragmentVariables) {
|
||||
return {
|
||||
count,
|
||||
cursor,
|
||||
orderBy: fragmentVariables.orderBy,
|
||||
commentID: props.comment.id,
|
||||
};
|
||||
},
|
||||
query: graphql`
|
||||
# Pagination query to be fetched upon calling 'loadMore'.
|
||||
# Notice that we re-use our fragment, and the shape of this query matches our fragment spec.
|
||||
query ReplyListContainerPaginationQuery(
|
||||
$count: Int!
|
||||
$cursor: Cursor
|
||||
$orderBy: COMMENT_SORT!
|
||||
$commentID: ID!
|
||||
) {
|
||||
comment(id: $commentID) {
|
||||
...ReplyListContainer_comment
|
||||
@arguments(count: $count, cursor: $cursor, orderBy: $orderBy)
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
)(ReplyListContainer);
|
||||
|
||||
export type ReplyListContainerProps = PropTypesOf<typeof enhanced>;
|
||||
export default enhanced;
|
||||
@@ -4,7 +4,7 @@ import React from "react";
|
||||
|
||||
import { StreamContainer } from "./StreamContainer";
|
||||
|
||||
it("renders username and body", () => {
|
||||
it("renders correctly", () => {
|
||||
const props: any = {
|
||||
asset: {
|
||||
id: "asset-id",
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
|
||||
import Stream from "../components/Stream";
|
||||
|
||||
export interface InnerProps {
|
||||
interface InnerProps {
|
||||
asset: Data;
|
||||
relay: RelayPaginationProp;
|
||||
}
|
||||
@@ -23,7 +23,8 @@ export class StreamContainer extends React.Component<InnerProps> {
|
||||
: null;
|
||||
return (
|
||||
<Stream
|
||||
{...this.props.asset}
|
||||
assetID={this.props.asset.id}
|
||||
isClosed={this.props.asset.isClosed}
|
||||
comments={comments}
|
||||
onLoadMore={this.loadMore}
|
||||
hasMore={this.props.relay.hasMore()}
|
||||
@@ -77,6 +78,7 @@ const enhanced = withPaginationContainer<
|
||||
node {
|
||||
id
|
||||
...CommentContainer
|
||||
...ReplyListContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<ReplyList
|
||||
commentID="comment-id"
|
||||
comments={
|
||||
Array [
|
||||
Object {
|
||||
"id": "comment-1",
|
||||
},
|
||||
Object {
|
||||
"id": "comment-2",
|
||||
},
|
||||
]
|
||||
}
|
||||
onLoadMore={[Function]}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`renders correctly when replies are null 1`] = `""`;
|
||||
@@ -1,7 +1,8 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders username and body 1`] = `
|
||||
exports[`renders correctly 1`] = `
|
||||
<Stream
|
||||
assetID="asset-id"
|
||||
comments={
|
||||
Array [
|
||||
Object {
|
||||
@@ -12,7 +13,6 @@ exports[`renders username and body 1`] = `
|
||||
},
|
||||
]
|
||||
}
|
||||
id="asset-id"
|
||||
isClosed={false}
|
||||
onLoadMore={[Function]}
|
||||
/>
|
||||
|
||||
@@ -36,47 +36,53 @@ exports[`loads more comments 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Isabelle
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Hey!
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Isabelle
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Hey!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,37 +124,41 @@ exports[`renders comment stream 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="root root invert fullWidth secondary"
|
||||
id="talk-stream--loadmore"
|
||||
id="talk-stream--load-more"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders comment stream 1`] = `
|
||||
<div
|
||||
className="root"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="root heading1 gutterBottom"
|
||||
>
|
||||
Talk NEO
|
||||
</h1>
|
||||
<form
|
||||
autoComplete="off"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div>
|
||||
<textarea
|
||||
className="textarea"
|
||||
name="body"
|
||||
onChange={[Function]}
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="postButtonContainer"
|
||||
>
|
||||
<button
|
||||
className="root root primary"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
>
|
||||
Post
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
I like yoghurt
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "8px",
|
||||
"marginLeft": "16px",
|
||||
"marginTop": "8px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -36,33 +36,37 @@ exports[`renders comment stream 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders comment stream 1`] = `
|
||||
<div
|
||||
className="root"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="root heading1 gutterBottom"
|
||||
>
|
||||
Talk NEO
|
||||
</h1>
|
||||
<form
|
||||
autoComplete="off"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div>
|
||||
<textarea
|
||||
className="textarea"
|
||||
name="body"
|
||||
onChange={[Function]}
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="postButtonContainer"
|
||||
>
|
||||
<button
|
||||
className="root root primary"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
>
|
||||
Post
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "8px",
|
||||
"marginLeft": "16px",
|
||||
"marginTop": "8px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="root root invert fullWidth secondary"
|
||||
id="talk-reply-list--show-all--comment-0"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
>
|
||||
Show All Replies
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`show all replies 1`] = `
|
||||
<div
|
||||
className="root"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="root heading1 gutterBottom"
|
||||
>
|
||||
Talk NEO
|
||||
</h1>
|
||||
<form
|
||||
autoComplete="off"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div>
|
||||
<textarea
|
||||
className="textarea"
|
||||
name="body"
|
||||
onChange={[Function]}
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="postButtonContainer"
|
||||
>
|
||||
<button
|
||||
className="root root primary"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
>
|
||||
Post
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Markus
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
Joining Too
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "8px",
|
||||
"marginLeft": "16px",
|
||||
"marginTop": "8px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="root gutterBottom"
|
||||
>
|
||||
<p
|
||||
className="root body1 gutterBottom author"
|
||||
>
|
||||
Lukas
|
||||
</p>
|
||||
<p
|
||||
className="root body1"
|
||||
>
|
||||
What's up?
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="root root invert fullWidth secondary"
|
||||
id="talk-reply-list--show-all--comment-0"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
>
|
||||
Show All Replies
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -3,7 +3,6 @@ import { createFetch } from "relay-local-schema";
|
||||
import {
|
||||
commitLocalUpdate,
|
||||
Environment,
|
||||
FetchFunction,
|
||||
Network,
|
||||
RecordProxy,
|
||||
RecordSource,
|
||||
|
||||
@@ -49,3 +49,33 @@ export const assets = [
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const commentWithReplies = {
|
||||
id: "comment-with-replies",
|
||||
author: users[0],
|
||||
body: "I like yoghurt",
|
||||
createdAt: "2018-07-06T18:24:00",
|
||||
replies: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: comments[1], cursor: comments[1].createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const assetWithReplies = {
|
||||
id: "asset-with-replies",
|
||||
isClosed: false,
|
||||
comments: {
|
||||
edges: [
|
||||
{ node: comments[0], cursor: comments[0].createdAt },
|
||||
{ node: commentWithReplies, cursor: commentWithReplies.createdAt },
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -82,7 +82,7 @@ it("renders comment stream", async () => {
|
||||
|
||||
it("loads more comments", async () => {
|
||||
testRenderer.root
|
||||
.findByProps({ id: "talk-stream--loadmore" })
|
||||
.findByProps({ id: "talk-stream--load-more" })
|
||||
.props.onClick();
|
||||
|
||||
// Wait for loading.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from "react";
|
||||
import TestRenderer from "react-test-renderer";
|
||||
import { RecordProxy } from "relay-runtime";
|
||||
|
||||
import { timeout } from "talk-common/utils";
|
||||
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
|
||||
import AppQuery from "talk-stream/queries/AppQuery";
|
||||
|
||||
import createEnvironment from "./createEnvironment";
|
||||
import { assetWithReplies } from "./fixtures";
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
asset: () => assetWithReplies,
|
||||
},
|
||||
};
|
||||
|
||||
const environment = createEnvironment({
|
||||
// Set this to true, to see graphql responses.
|
||||
logNetwork: false,
|
||||
resolvers,
|
||||
initLocalState: (localRecord: RecordProxy) => {
|
||||
localRecord.setValue(assetWithReplies.id, "assetID");
|
||||
},
|
||||
});
|
||||
|
||||
const context: TalkContext = {
|
||||
relayEnvironment: environment,
|
||||
localeMessages: [],
|
||||
};
|
||||
|
||||
const testRenderer = TestRenderer.create(
|
||||
<TalkContextProvider value={context}>
|
||||
<AppQuery />
|
||||
</TalkContextProvider>
|
||||
);
|
||||
|
||||
it("renders comment stream", async () => {
|
||||
// Wait for loading.
|
||||
await timeout();
|
||||
expect(testRenderer.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import TestRenderer from "react-test-renderer";
|
||||
import { RecordProxy } from "relay-runtime";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { timeout } from "talk-common/utils";
|
||||
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
|
||||
import AppQuery from "talk-stream/queries/AppQuery";
|
||||
|
||||
import createEnvironment from "./createEnvironment";
|
||||
import { assets, comments } from "./fixtures";
|
||||
|
||||
const assetStub = {
|
||||
...assets[0],
|
||||
comments: {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
...comments[0],
|
||||
replies: {
|
||||
edges: sinon
|
||||
.stub()
|
||||
.onFirstCall()
|
||||
.returns([
|
||||
{
|
||||
node: comments[1],
|
||||
cursor: comments[1].createdAt,
|
||||
},
|
||||
])
|
||||
.onSecondCall()
|
||||
.returns([
|
||||
{
|
||||
node: comments[2],
|
||||
cursor: comments[2].createdAt,
|
||||
},
|
||||
]),
|
||||
pageInfo: sinon
|
||||
.stub()
|
||||
.onFirstCall()
|
||||
.returns({
|
||||
endCursor: comments[1].createdAt,
|
||||
hasNextPage: true,
|
||||
})
|
||||
.onSecondCall()
|
||||
.returns({
|
||||
endCursor: comments[2].createdAt,
|
||||
hasNextPage: false,
|
||||
}),
|
||||
},
|
||||
},
|
||||
cursor: comments[0].createdAt,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
asset: () => assetStub,
|
||||
},
|
||||
};
|
||||
|
||||
const environment = createEnvironment({
|
||||
// Set this to true, to see graphql responses.
|
||||
logNetwork: false,
|
||||
resolvers,
|
||||
initLocalState: (localRecord: RecordProxy) => {
|
||||
localRecord.setValue(assetStub.id, "assetID");
|
||||
},
|
||||
});
|
||||
|
||||
const context: TalkContext = {
|
||||
relayEnvironment: environment,
|
||||
localeMessages: [],
|
||||
};
|
||||
|
||||
const testRenderer = TestRenderer.create(
|
||||
<TalkContextProvider value={context}>
|
||||
<AppQuery />
|
||||
</TalkContextProvider>
|
||||
);
|
||||
|
||||
it("renders comment stream", async () => {
|
||||
// Wait for loading.
|
||||
await timeout();
|
||||
expect(testRenderer.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("show all replies", async () => {
|
||||
testRenderer.root
|
||||
.findByProps({ id: `talk-reply-list--show-all--${comments[0].id}` })
|
||||
.props.onClick();
|
||||
|
||||
// Wait for loading.
|
||||
await timeout();
|
||||
expect(testRenderer.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
Reference in New Issue
Block a user