[CORL-260] Bring back sorting (#2186)

* feat: sort stream

* feat: add FieldSet component to ui

* feat: make accessible and add feature test

* test: fix snapshots
This commit is contained in:
Kiwi
2019-02-13 16:11:13 +00:00
committed by Wyatt Johnson
parent 68839c721c
commit f4037ce6fb
43 changed files with 1047 additions and 110 deletions
@@ -19,6 +19,7 @@ exports[`init local state 1`] = `
\\"network\\": {
\\"__ref\\": \\"client:root.local.network\\"
},
\\"defaultStreamOrderBy\\": \\"CREATED_AT_DESC\\",
\\"authPopup\\": {
\\"__ref\\": \\"client:root.local.authPopup\\"
},
@@ -39,6 +39,8 @@ export default async function initLocalState(
if (query.commentID) {
localRecord.setValue(query.commentID, "commentID");
}
// Set sort
localRecord.setValue("CREATED_AT_DESC", "defaultStreamOrderBy");
// Create authPopup Record
const authPopupRecord = createAndRetain(
@@ -54,5 +56,8 @@ export default async function initLocalState(
// Set active tab
localRecord.setValue("COMMENTS", "activeTab");
// Set sort
localRecord.setValue("CREATED_AT_DESC", "defaultStreamOrderBy");
});
}
@@ -38,6 +38,7 @@ type Local {
storyID: String
storyURL: String
commentID: String
defaultStreamOrderBy: COMMENT_SORT!
}
extend type Query {
@@ -0,0 +1,21 @@
import { Environment, RecordSource } from "relay-runtime";
import { LOCAL_ID } from "talk-framework/lib/relay";
import { createRelayEnvironment } from "talk-framework/testHelpers";
import { commit } from "./SetStreamOrderByMutation";
let environment: Environment;
const source: RecordSource = new RecordSource();
beforeAll(() => {
environment = createRelayEnvironment({
source,
});
});
it("Sets streamOrderBy", () => {
const orderBy = "CREATED_AT_ASC";
commit(environment, { orderBy });
expect(source.get(LOCAL_ID)!.streamOrderBy).toEqual(orderBy);
});
@@ -0,0 +1,31 @@
import { commitLocalUpdate, Environment } from "relay-runtime";
import { createMutationContainer, LOCAL_ID } from "talk-framework/lib/relay";
export interface SetStreamOrderByInput {
orderBy:
| "CREATED_AT_ASC"
| "CREATED_AT_DESC"
| "REPLIES_DESC"
| "RESPECT_DESC"
| "%future added value";
}
export type SetStreamOrderByMutation = (
input: SetStreamOrderByInput
) => Promise<void>;
export async function commit(
environment: Environment,
input: SetStreamOrderByInput
) {
return commitLocalUpdate(environment, store => {
const record = store.get(LOCAL_ID)!;
record.setValue(input.orderBy, "streamOrderBy");
});
}
export const withSetStreamOrderByMutation = createMutationContainer(
"setStreamOrderBy",
commit
);
@@ -0,0 +1,5 @@
.root {
border: 0;
border-top: 1px solid var(--palette-divider);
margin: var(--spacing-unit) 0;
}
@@ -0,0 +1,6 @@
import React, { StatelessComponent } from "react";
import * as styles from "./Divider.css";
const Divider: StatelessComponent = () => <hr className={styles.root} />;
export default Divider;
@@ -0,0 +1,8 @@
.mobileSelect {
font-size: 0;
padding: 11px;
width: 0px;
}
.mobileAfterWrapper {
right: 5px;
}
@@ -0,0 +1,53 @@
import { noop } from "lodash";
import React from "react";
import TestRenderer from "react-test-renderer";
import { LocalizationProvider } from "fluent-react/compat";
import { PropTypesOf } from "talk-framework/types";
import { UIContext, UIContextProps } from "talk-ui/components";
import SortMenu from "./SortMenu";
it("renders correctly on small screens", () => {
const props: PropTypesOf<typeof SortMenu> = {
orderBy: "CREATED_AT_ASC",
onChange: noop,
};
const context: UIContextProps = {
mediaQueryValues: {
width: 320,
},
};
const testRenderer = TestRenderer.create(
<LocalizationProvider bundles={[]}>
<UIContext.Provider value={context}>
<SortMenu {...props} />
</UIContext.Provider>
</LocalizationProvider>
);
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("renders correctly on big screens", () => {
const props: PropTypesOf<typeof SortMenu> = {
orderBy: "CREATED_AT_ASC",
onChange: noop,
};
const context: UIContextProps = {
mediaQueryValues: {
width: 1600,
},
};
const testRenderer = TestRenderer.create(
<LocalizationProvider bundles={[]}>
<UIContext.Provider value={context}>
<SortMenu {...props} />
</UIContext.Provider>
</LocalizationProvider>
);
expect(testRenderer.toJSON()).toMatchSnapshot();
});
@@ -0,0 +1,70 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import {
Flex,
Icon,
MatchMedia,
Option,
SelectField,
Typography,
} from "talk-ui/components";
import Divider from "./Divider";
import * as styles from "./SortMenu.css";
interface Props {
orderBy:
| "CREATED_AT_ASC"
| "CREATED_AT_DESC"
| "REPLIES_DESC"
| "RESPECT_DESC"
| "%future added value";
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
}
const SortMenu: StatelessComponent<Props> = props => (
<MatchMedia ltWidth="sm">
{matches => (
<div>
<Flex justifyContent="flex-end" alignItems="center" itemGutter>
{!matches && (
<Localized id="comments-sortMenu-sortBy">
<Typography
variant="bodyCopyBold"
container={<label htmlFor="talk-comments-sortMenu" />}
>
Sort By
</Typography>
</Localized>
)}
<SelectField
id="talk-comments-sortMenu"
value={props.orderBy}
onChange={props.onChange}
afterWrapper={(matches && <Icon>sort</Icon>) || undefined}
classes={{
select: (matches && styles.mobileSelect) || undefined,
afterWrapper: (matches && styles.mobileAfterWrapper) || undefined,
}}
>
<Localized id="comments-sortMenu-newest">
<Option value="CREATED_AT_DESC">Newest</Option>
</Localized>
<Localized id="comments-sortMenu-oldest">
<Option value="CREATED_AT_ASC">Oldest</Option>
</Localized>
<Localized id="comments-sortMenu-mostReplies">
<Option value="REPLIES_DESC">Most Replies</Option>
</Localized>
<Localized id="comments-sortMenu-mostReactions">
<Option value="RESPECT_DESC">Most Reactions</Option>
</Localized>
</SelectField>
</Flex>
<Divider />
</div>
)}
</MatchMedia>
);
export default SortMenu;
@@ -27,6 +27,8 @@ it("renders correctly", () => {
disableLoadMore: false,
hasMore: false,
me: null,
orderBy: "CREATED_AT_ASC",
onChangeOrderBy: noop,
};
const wrapper = shallow(<StreamN {...props} />);
expect(wrapper).toMatchSnapshot();
@@ -50,6 +52,8 @@ describe("when use is logged in", () => {
label: "Respect",
},
},
orderBy: "CREATED_AT_ASC",
onChangeOrderBy: noop,
};
const wrapper = shallow(<StreamN {...props} />);
expect(wrapper).toMatchSnapshot();
@@ -73,6 +77,8 @@ describe("when there is more", () => {
disableLoadMore: false,
hasMore: true,
me: null,
orderBy: "CREATED_AT_ASC",
onChangeOrderBy: noop,
};
const wrapper = shallow(<StreamN {...props} />);
@@ -4,12 +4,13 @@ import { StatelessComponent } from "react";
import { PropTypesOf } from "talk-framework/types";
import UserBoxContainer from "talk-stream/containers/UserBoxContainer";
import { Button, HorizontalGutter } from "talk-ui/components";
import { Button, HorizontalGutter, Spinner } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
import PostCommentFormContainer from "../containers/PostCommentFormContainer";
import ReplyListContainer from "../containers/ReplyListContainer";
import PostCommentFormFake from "./PostCommentFormFake";
import SortMenu from "./SortMenu";
import styles from "./Stream.css";
@@ -34,6 +35,9 @@ export interface StreamProps {
PropTypesOf<typeof CommentContainer>["me"] &
PropTypesOf<typeof ReplyListContainer>["me"]
| null;
orderBy: PropTypesOf<typeof SortMenu>["orderBy"];
onChangeOrderBy: (e: React.ChangeEvent<HTMLSelectElement>) => void;
refetching?: boolean;
}
const Stream: StatelessComponent<StreamProps> = props => {
@@ -47,43 +51,49 @@ const Stream: StatelessComponent<StreamProps> = props => {
<PostCommentFormFake />
)}
</HorizontalGutter>
<HorizontalGutter
id="talk-comments-stream-log"
data-testid="comments-stream-log"
role="log"
aria-live="polite"
>
{props.comments.map(comment => (
<HorizontalGutter key={comment.id}>
<CommentContainer
me={props.me}
settings={props.settings}
comment={comment}
story={props.story}
/>
<ReplyListContainer
settings={props.settings}
me={props.me}
comment={comment}
story={props.story}
/>
</HorizontalGutter>
))}
{props.hasMore && (
<Localized id="comments-stream-loadMore">
<Button
id={"talk-comments-stream-loadMore"}
onClick={props.onLoadMore}
variant="outlined"
fullWidth
disabled={props.disableLoadMore}
aria-controls="talk-comments-stream-log"
>
Load More
</Button>
</Localized>
)}
</HorizontalGutter>
{props.comments.length > 0 && (
<SortMenu orderBy={props.orderBy} onChange={props.onChangeOrderBy} />
)}
{props.refetching && <Spinner />}
{!props.refetching && (
<HorizontalGutter
id="talk-comments-stream-log"
data-testid="comments-stream-log"
role="log"
aria-live="polite"
>
{props.comments.map(comment => (
<HorizontalGutter key={comment.id}>
<CommentContainer
me={props.me}
settings={props.settings}
comment={comment}
story={props.story}
/>
<ReplyListContainer
settings={props.settings}
me={props.me}
comment={comment}
story={props.story}
/>
</HorizontalGutter>
))}
{props.hasMore && (
<Localized id="comments-stream-loadMore">
<Button
id={"talk-comments-stream-loadMore"}
onClick={props.onLoadMore}
variant="outlined"
fullWidth
disabled={props.disableLoadMore}
aria-controls="talk-comments-stream-log"
>
Load More
</Button>
</Localized>
)}
</HorizontalGutter>
)}
</HorizontalGutter>
);
};
@@ -0,0 +1,119 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly on big screens 1`] = `
<div>
<div
className="Flex-root Flex-flex Flex-itemGutter Flex-justifyFlexEnd Flex-alignCenter"
>
<label
className="Typography-root Typography-bodyCopyBold Typography-colorTextPrimary"
htmlFor="talk-comments-sortMenu"
>
Sort By
</label>
<span
className="SelectField-root"
>
<select
className="SelectField-select undefined"
id="talk-comments-sortMenu"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
value="CREATED_AT_ASC"
>
<option
value="CREATED_AT_DESC"
>
Newest
</option>
<option
value="CREATED_AT_ASC"
>
Oldest
</option>
<option
value="REPLIES_DESC"
>
Most Replies
</option>
<option
value="RESPECT_DESC"
>
Most Reactions
</option>
</select>
<span
aria-hidden={true}
className="SelectField-afterWrapper undefined"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
expand_more
</span>
</span>
</span>
</div>
<hr
className="Divider-root"
/>
</div>
`;
exports[`renders correctly on small screens 1`] = `
<div>
<div
className="Flex-root Flex-flex Flex-itemGutter Flex-justifyFlexEnd Flex-alignCenter"
>
<span
className="SelectField-root"
>
<select
className="SelectField-select SortMenu-mobileSelect"
id="talk-comments-sortMenu"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
value="CREATED_AT_ASC"
>
<option
value="CREATED_AT_DESC"
>
Newest
</option>
<option
value="CREATED_AT_ASC"
>
Oldest
</option>
<option
value="REPLIES_DESC"
>
Most Replies
</option>
<option
value="RESPECT_DESC"
>
Most Reactions
</option>
</select>
<span
aria-hidden={true}
className="SelectField-afterWrapper SortMenu-mobileAfterWrapper"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
sort
</span>
</span>
</span>
</div>
<hr
className="Divider-root"
/>
</div>
`;
@@ -21,6 +21,10 @@ exports[`renders correctly 1`] = `
/>
<PostCommentFormFake />
</ForwardRef(forwardRef)>
<SortMenu
onChange={[Function]}
orderBy="CREATED_AT_ASC"
/>
<ForwardRef(forwardRef)
aria-live="polite"
data-testid="comments-stream-log"
@@ -148,6 +152,10 @@ exports[`when there is more disables load more button 1`] = `
/>
<PostCommentFormFake />
</ForwardRef(forwardRef)>
<SortMenu
onChange={[Function]}
orderBy="CREATED_AT_ASC"
/>
<ForwardRef(forwardRef)
aria-live="polite"
data-testid="comments-stream-log"
@@ -289,6 +297,10 @@ exports[`when there is more renders a load more button 1`] = `
/>
<PostCommentFormFake />
</ForwardRef(forwardRef)>
<SortMenu
onChange={[Function]}
orderBy="CREATED_AT_ASC"
/>
<ForwardRef(forwardRef)
aria-live="polite"
data-testid="comments-stream-log"
@@ -432,6 +444,10 @@ exports[`when use is logged in renders correctly 1`] = `
storyID="story-id"
/>
</ForwardRef(forwardRef)>
<SortMenu
onChange={[Function]}
orderBy="CREATED_AT_ASC"
/>
<ForwardRef(forwardRef)
aria-live="polite"
data-testid="comments-stream-log"
@@ -31,6 +31,7 @@ it("renders correctly", () => {
hasMore: noop,
isLoading: noop,
} as any,
defaultOrderBy: "CREATED_AT_ASC",
};
const wrapper = shallow(<StreamContainerN {...props} />);
expect(wrapper).toMatchSnapshot();
@@ -58,6 +59,7 @@ describe("when has more comments", () => {
isLoading: () => false,
loadMore: (_: any, callback: () => void) => (finishLoading = callback),
} as any,
defaultOrderBy: "CREATED_AT_ASC",
};
let wrapper: ShallowWrapper;
@@ -1,4 +1,4 @@
import React from "react";
import React, { ChangeEvent } from "react";
import { graphql, RelayPaginationProp } from "react-relay";
import { withPaginationContainer } from "talk-framework/lib/relay";
@@ -18,6 +18,7 @@ interface InnerProps {
settings: SettingsData;
me: MeData | null;
relay: RelayPaginationProp;
defaultOrderBy: COMMENT_SORT;
}
// tslint:disable-next-line:no-unused-expression
@@ -32,6 +33,27 @@ graphql`
export class StreamContainer extends React.Component<InnerProps> {
public state = {
disableLoadMore: false,
refetching: false,
};
private orderBy = this.props.defaultOrderBy;
private handleOnChangeOrderBy = (e: ChangeEvent<HTMLSelectElement>) => {
this.orderBy = e.target.value as COMMENT_SORT;
this.setState({ refetching: true });
this.props.relay.refetchConnection(
5,
err => {
if (err) {
// tslint:disable-next-line:no-console
console.error(err);
return;
}
this.setState({ refetching: false });
},
{
orderBy: e.target.value as COMMENT_SORT,
}
);
};
public render() {
@@ -45,6 +67,9 @@ export class StreamContainer extends React.Component<InnerProps> {
hasMore={this.props.relay.hasMore()}
disableLoadMore={this.state.disableLoadMore}
me={this.props.me}
orderBy={this.orderBy}
onChangeOrderBy={this.handleOnChangeOrderBy}
refetching={this.state.refetching}
/>
);
}
@@ -14,7 +14,10 @@ exports[`renders correctly 1`] = `
}
disableLoadMore={false}
me={null}
onChangeOrderBy={[Function]}
onLoadMore={[Function]}
orderBy="CREATED_AT_ASC"
refetching={false}
settings={
Object {
"reaction": Object {
@@ -61,7 +64,10 @@ exports[`when has more comments renders hasMore 1`] = `
disableLoadMore={false}
hasMore={true}
me={null}
onChangeOrderBy={[Function]}
onLoadMore={[Function]}
orderBy="CREATED_AT_ASC"
refetching={false}
settings={
Object {
"reaction": Object {
@@ -108,7 +114,10 @@ exports[`when has more comments when loading more disables load more button 1`]
disableLoadMore={true}
hasMore={true}
me={null}
onChangeOrderBy={[Function]}
onLoadMore={[Function]}
orderBy="CREATED_AT_ASC"
refetching={false}
settings={
Object {
"reaction": Object {
@@ -155,7 +164,10 @@ exports[`when has more comments when loading more enable load more button after
disableLoadMore={false}
hasMore={true}
me={null}
onChangeOrderBy={[Function]}
onLoadMore={[Function]}
orderBy="CREATED_AT_ASC"
refetching={false}
settings={
Object {
"reaction": Object {
@@ -11,7 +11,7 @@ it("renders stream container", () => {
error: null,
};
const renderer = createRenderer();
renderer.render(React.createElement(() => render(data)));
renderer.render(React.createElement(() => render(data, "CREATED_AT_ASC")));
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -21,7 +21,7 @@ it("renders loading", () => {
error: null,
};
const renderer = createRenderer();
renderer.render(React.createElement(() => render(data)));
renderer.render(React.createElement(() => render(data, "CREATED_AT_ASC")));
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -31,6 +31,6 @@ it("renders error", () => {
error: new Error("error"),
};
const renderer = createRenderer();
renderer.render(React.createElement(() => render(data)));
renderer.render(React.createElement(() => render(data, "CREATED_AT_ASC")));
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -11,20 +11,20 @@ import { StreamQueryLocal as Local } from "talk-stream/__generated__/StreamQuery
import { Delay, Spinner } from "talk-ui/components";
import StreamContainer from "../containers/StreamContainer";
interface InnerProps {
interface Props {
local: Local;
}
export const render = ({
error,
props,
}: ReadyState<QueryTypes["response"]>) => {
if (error) {
return <div>{error.message}</div>;
export const render = (
data: ReadyState<QueryTypes["response"]>,
defaultStreamOrderBy: Props["local"]["defaultStreamOrderBy"]
) => {
if (data.error) {
return <div>{data.error.message}</div>;
}
if (props) {
if (!props.story) {
if (data.props) {
if (!data.props.story) {
return (
<Localized id="comments-streamQuery-storyNotFound">
<div>Story not found</div>
@@ -33,9 +33,10 @@ export const render = ({
}
return (
<StreamContainer
settings={props.settings}
me={props.me}
story={props.story}
settings={data.props.settings}
me={data.props.me}
story={data.props.story}
defaultOrderBy={defaultStreamOrderBy}
/>
);
}
@@ -47,36 +48,45 @@ export const render = ({
);
};
const StreamQuery: StatelessComponent<InnerProps> = ({
local: { storyID, storyURL },
}) => (
<QueryRenderer<QueryTypes>
query={graphql`
query StreamQuery($storyID: ID, $storyURL: String) {
me {
...StreamContainer_me
const StreamQuery: StatelessComponent<Props> = props => {
const {
local: { storyID, storyURL, defaultStreamOrderBy },
} = props;
return (
<QueryRenderer<QueryTypes>
query={graphql`
query StreamQuery(
$storyID: ID
$storyURL: String
$streamOrderBy: COMMENT_SORT
) {
me {
...StreamContainer_me
}
story(id: $storyID, url: $storyURL) {
...StreamContainer_story @arguments(orderBy: $streamOrderBy)
}
settings {
...StreamContainer_settings
}
}
story(id: $storyID, url: $storyURL) {
...StreamContainer_story
}
settings {
...StreamContainer_settings
}
}
`}
variables={{
storyID,
storyURL,
}}
render={render}
/>
);
`}
variables={{
storyID,
storyURL,
streamOrderBy: defaultStreamOrderBy,
}}
render={data => render(data, props.local.defaultStreamOrderBy)}
/>
);
};
const enhanced = withLocalStateContainer(
graphql`
fragment StreamQueryLocal on Local {
storyID
storyURL
defaultStreamOrderBy
}
`
)(StreamQuery);
@@ -16,6 +16,7 @@ exports[`renders loading 1`] = `
exports[`renders stream container 1`] = `
<Relay(StreamContainer)
defaultOrderBy="CREATED_AT_ASC"
story={Object {}}
/>
`;
@@ -171,6 +171,65 @@ exports[`renders app with comment stream 1`] = `
</button>
</div>
</div>
<div>
<div
className="Flex-root Flex-flex Flex-itemGutter Flex-justifyFlexEnd Flex-alignCenter"
>
<label
className="Typography-root Typography-bodyCopyBold Typography-colorTextPrimary"
htmlFor="talk-comments-sortMenu"
>
Sort By
</label>
<span
className="SelectField-root"
>
<select
className="SelectField-select undefined"
id="talk-comments-sortMenu"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
value="CREATED_AT_DESC"
>
<option
value="CREATED_AT_DESC"
>
Newest
</option>
<option
value="CREATED_AT_ASC"
>
Oldest
</option>
<option
value="REPLIES_DESC"
>
Most Replies
</option>
<option
value="RESPECT_DESC"
>
Most Reactions
</option>
</select>
<span
aria-hidden={true}
className="SelectField-afterWrapper undefined"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
expand_more
</span>
</span>
</span>
</div>
<hr
className="Divider-root"
/>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
@@ -0,0 +1,310 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders app with comment stream 1`] = `
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
data-testid="comments-stream-log"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
data-testid="comment-comment-2"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Indent-root"
>
<div
className=""
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root Flex-flex Flex-justifySpaceBetween Flex-directionRow"
>
<div
className="Flex-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Isabelle
</span>
<div
className="Flex-root Flex-flex Flex-itemGutter Flex-alignBaseline Flex-directionRow"
>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "Hey!",
}
}
/>
<div
className="Flex-root Flex-flex Flex-justifySpaceBetween"
>
<div
className="Flex-root Flex-flex Flex-halfItemGutter Flex-directionRow"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
id="comments-commentContainer-replyButton-comment-2"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Reply
</span>
</button>
<div
className="Popover-root"
>
<button
aria-controls="permalink-popover-comment-2"
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Share
</span>
</button>
<div
aria-hidden={true}
aria-labelledby="permalink-popover-comment-2-ariainfo"
id="permalink-popover-comment-2"
role="popup"
>
<div
className="AriaInfo-root"
id="permalink-popover-comment-2-ariainfo"
>
A dialog showing a permalink to the comment
</div>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Respect
</span>
</button>
</div>
<div
className="Flex-root Flex-flex Flex-halfItemGutter Flex-directionRow"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Report
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
data-testid="comment-comment-3"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Indent-root"
>
<div
className=""
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root Flex-flex Flex-justifySpaceBetween Flex-directionRow"
>
<div
className="Flex-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Isabelle
</span>
<div
className="Flex-root Flex-flex Flex-itemGutter Flex-alignBaseline Flex-directionRow"
>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="HTMLContent-root"
dangerouslySetInnerHTML={
Object {
"__html": "Comment Body 3",
}
}
/>
<div
className="Flex-root Flex-flex Flex-justifySpaceBetween"
>
<div
className="Flex-root Flex-flex Flex-halfItemGutter Flex-directionRow"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
id="comments-commentContainer-replyButton-comment-3"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Reply
</span>
</button>
<div
className="Popover-root"
>
<button
aria-controls="permalink-popover-comment-3"
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Share
</span>
</button>
<div
aria-hidden={true}
aria-labelledby="permalink-popover-comment-3-ariainfo"
id="permalink-popover-comment-3"
role="popup"
>
<div
className="AriaInfo-root"
id="permalink-popover-comment-3-ariainfo"
>
A dialog showing a permalink to the comment
</div>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Respect
</span>
</button>
</div>
<div
className="Flex-root Flex-flex Flex-halfItemGutter Flex-directionRow"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Report
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
@@ -8,6 +8,7 @@ export default function create(params: CreateParams) {
localRecord.setValue("COMMENTS", "activeTab");
localRecord.setValue(false, "loggedIn");
localRecord.setValue("jti", "accessTokenJTI");
localRecord.setValue("CREATED_AT_DESC", "defaultStreamOrderBy");
params.initLocalState(localRecord, source, environment);
}
},
@@ -0,0 +1,81 @@
import sinon from "sinon";
import {
createSinonStub,
waitForElement,
within,
} from "talk-framework/testHelpers";
import { settings, stories } from "../fixtures";
import create from "./create";
const createTestRenderer = async (resolver: any = {}) => {
const resolvers = {
...resolver,
Query: {
...resolver.Query,
settings: sinon.stub().returns(settings),
},
};
const { testRenderer } = create({
// Set this to true, to see graphql responses.
logNetwork: false,
resolvers,
initLocalState: localRecord => {
localRecord.setValue(stories[0].id, "storyID");
},
});
return {
testRenderer,
};
};
it("renders app with comment stream", async () => {
const commentsQueryStub = createSinonStub(
s =>
s.onFirstCall().callsFake((input: any) => {
expect(input).toEqual({ first: 5, orderBy: "CREATED_AT_DESC" });
return stories[0].comments;
}),
s =>
s.onSecondCall().callsFake((input: any) => {
expect(input).toEqual({
after: null,
first: 5,
orderBy: "CREATED_AT_ASC",
});
return stories[1].comments;
})
);
const storyQueryStub = createSinonStub(s =>
s.callsFake((_: any, input: any) => {
expect(input.id).toEqual("story-1");
expect(input.url).toBeFalsy();
return {
...stories[0],
comments: commentsQueryStub,
};
})
);
const { testRenderer } = await createTestRenderer({
Query: {
story: storyQueryStub,
},
});
let streamLog = await waitForElement(() =>
within(testRenderer.root).getByTestID("comments-stream-log")
);
const selectField = within(testRenderer.root).getByLabelText("Sort By");
const oldestOption = within(selectField).getByText("Oldest");
selectField.props.onChange({
target: { value: oldestOption.props.value.toString() },
});
streamLog = await waitForElement(() =>
within(testRenderer.root).getByTestID("comments-stream-log")
);
expect(within(streamLog).toJSON()).toMatchSnapshot();
});