Merge branch 'permalink' of github.com:coralproject/talk into permalink

* 'permalink' of github.com:coralproject/talk: (42 commits)
  [next] Support server side jest testing (#1747)
  Update snapshots
  Add comments
  Remove precss
  Move react-responsive to dev deps
  Remove comment
  Mobile first approach
  Support standard css variables, dynamically set spacing-unit
  Add docs
  Fully implement Flex and MatchMedia
  Responsive Components <3
  fix: linting
  fix: adjusted pageInfo
  Remove obsoloe snapshot
  Move jsdom to dev deps
  Mark comments as always returning a value
  Add comment
  Fix unit tests
  Translate, concept for translation and id strings
  Add aria props
  ...
This commit is contained in:
Belén Curcio
2018-07-16 13:42:54 -03:00
114 changed files with 4399 additions and 1049 deletions
+5 -23
View File
@@ -1,38 +1,20 @@
import * as React from "react";
import { StatelessComponent } from "react";
import { Center } from "talk-ui/components";
import { Flex } from "talk-ui/components";
import AssetListContainer from "../containers/AssetListContainer";
import PostCommentFormContainer from "../containers/PostCommentFormContainer";
import StreamContainer from "../containers/StreamContainer";
import Logo from "./Logo";
export interface AppProps {
assets?: any | null;
asset?: {
id: string;
isClosed: boolean;
comments: any | null;
} | null;
comment?: any | null;
asset: {} | null;
}
const App: StatelessComponent<AppProps> = props => {
console.log(props);
if (props.comment) {
return <div>Comment</div>;
}
if (props.assets) {
return <AssetListContainer assets={props.assets} />;
}
if (props.asset) {
return (
<Center>
<Logo gutterBottom />
<StreamContainer comments={props.asset.comments} />
<PostCommentFormContainer assetID={props.asset.id} />
</Center>
<Flex justifyContent="center">
<StreamContainer asset={props.asset} />
</Flex>
);
}
return <div>Asset not found </div>;
@@ -1,16 +0,0 @@
import * as React from "react";
import { StatelessComponent } from "react";
export interface AssetListProps {
assets: ReadonlyArray<{ id: string; title: string | null }>;
}
const AssetList: StatelessComponent<AssetListProps> = props => {
return (
<div>
{props.assets.map(asset => <div key={asset.id}>{asset.title}</div>)}
</div>
);
};
export default AssetList;
@@ -1,11 +1,10 @@
.root {
width: 400px;
}
.gutterBottom {
margin-bottom: calc(2 px * $spacing-unit);
margin-bottom: calc(1px * var(--spacing-unit));
}
.author {
font-weight: $font-weight-medium;
.topBar {
margin-bottom: calc(0.5px * var(--spacing-unit));
}
@@ -1,5 +1,6 @@
import { shallow } from "enzyme";
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import Comment from "./Comment";
it("renders username and body", () => {
@@ -10,9 +11,8 @@ it("renders username and body", () => {
},
body: "Woof",
};
const renderer = createRenderer();
renderer.render(<Comment {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
const wrapper = shallow(<Comment {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with gutterBottom", () => {
@@ -24,7 +24,6 @@ it("renders with gutterBottom", () => {
body: "Woof",
gutterBottom: true,
};
const renderer = createRenderer();
renderer.render(<Comment {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
const wrapper = shallow(<Comment {...props} />);
expect(wrapper).toMatchSnapshot();
});
@@ -4,6 +4,7 @@ import { StatelessComponent } from "react";
import { Typography } from "talk-ui/components";
import * as styles from "./Comment.css";
import PermalinkPopover from "./PermalinkPopover";
import Username from "./Username";
export interface CommentProps {
id: string;
@@ -15,17 +16,15 @@ export interface CommentProps {
gutterBottom?: boolean;
}
// make a permalink popover
const Comment: StatelessComponent<CommentProps> = props => {
const rootClassName = cn(styles.root, props.className, {
[styles.gutterBottom]: props.gutterBottom,
});
return (
<div className={rootClassName}>
<Typography className={styles.author} gutterBottom>
{props.author && props.author.username}
</Typography>
<div className={rootClassName} role="article">
<div className={styles.topBar}>
{props.author && <Username>{props.author.username}</Username>}
</div>
<Typography>{props.body}</Typography>
<div className={cn("talk-comment-footer")}>
<PermalinkPopover commentId={props.id} />
@@ -0,0 +1,8 @@
.root {
border-left: 3px solid;
padding-left: calc(1px * var(--spacing-unit));
}
.level0 {
border-color: var(--palette-secondary-darkest);
}
@@ -0,0 +1,15 @@
import cn from "classnames";
import React, { StatelessComponent } from "react";
import * as styles from "./Indent.css";
export interface IndentProps {
level?: number;
children: React.ReactNode;
}
const Indent: StatelessComponent<IndentProps> = props => {
return <div className={cn(styles.root, styles.level0)}>{props.children}</div>;
};
export default Indent;
@@ -3,10 +3,12 @@
display: block;
height: 100px;
width: 400px;
margin-bottom: calc(2px * $spacing-unit);
width: 100%;
box-sizing: border-box;
margin-bottom: calc(1px * var(--spacing-unit));
}
.postButton {
float: right;
.postButtonContainer {
display: flex;
justify-content: flex-end;
}
@@ -39,11 +39,13 @@ const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
</div>
)}
</Field>
<Localized id="postCommentForm-submit">
<Button className={styles.postButton} disabled={submitting} primary>
Post
</Button>
</Localized>
<div className={styles.postButtonContainer}>
<Localized id="comments-postCommentForm-post">
<Button disabled={submitting} primary>
Post
</Button>
</Localized>
</div>
</form>
)}
</Form>
@@ -0,0 +1,40 @@
import { shallow } from "enzyme";
import { noop } from "lodash";
import React from "react";
import sinon, { SinonSpy } from "sinon";
import ReplyList, { ReplyListProps } from "./ReplyList";
it("renders correctly", () => {
const props: ReplyListProps = {
commentID: "comment-id",
comments: [{ id: "comment-1" }, { id: "comment-2" }],
onShowAll: noop,
hasMore: false,
disableShowAll: false,
};
const wrapper = shallow(<ReplyList {...props} />);
expect(wrapper).toMatchSnapshot();
});
describe("when there is more", () => {
const props: ReplyListProps = {
commentID: "comment-id",
comments: [{ id: "comment-1" }, { id: "comment-2" }],
onShowAll: sinon.spy(),
hasMore: true,
disableShowAll: false,
};
const wrapper = shallow(<ReplyList {...props} />);
it("renders a load more button", () => {
expect(wrapper).toMatchSnapshot();
});
it("calls onLoadMore", () => {
wrapper
.find("#talk-comments-replyList-showAll--comment-id")
.simulate("click");
expect((props.onShowAll as SinonSpy).calledOnce).toBe(true);
});
});
@@ -0,0 +1,45 @@
import { Localized } from "fluent-react/compat";
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 }>;
onShowAll: () => void;
hasMore: boolean;
disableShowAll: boolean;
}
const ReplyList: StatelessComponent<ReplyListProps> = props => {
return (
<Indent>
<div id={`talk-comments-replyList-log--${props.commentID}`} role="log">
{props.comments.map(comment => (
<CommentContainer key={comment.id} data={comment} gutterBottom />
))}
{props.hasMore && (
<Localized id="comments-replyList-showAll">
<Button
id={`talk-comments-replyList-showAll--${props.commentID}`}
aria-controls={`talk-comments-replyList-log--${props.commentID}`}
onClick={props.onShowAll}
disabled={props.disableShowAll}
secondary
invert
fullWidth
>
Show All Replies
</Button>
</Localized>
)}
</div>
</Indent>
);
};
export default ReplyList;
@@ -0,0 +1,4 @@
.root {
width: 100%;
max-width: 400px;
}
@@ -0,0 +1,45 @@
import { shallow } from "enzyme";
import { noop } from "lodash";
import React from "react";
import sinon from "sinon";
import Stream, { StreamProps } from "./Stream";
it("renders correctly", () => {
const props: StreamProps = {
assetID: "asset-id",
isClosed: false,
comments: [{ id: "comment-1" }, { id: "comment-2" }],
onLoadMore: noop,
disableLoadMore: false,
hasMore: false,
};
const wrapper = shallow(<Stream {...props} />);
expect(wrapper).toMatchSnapshot();
});
describe("when there is more", () => {
const props = {
assetID: "asset-id",
isClosed: false,
comments: [{ id: "comment-1" }, { id: "comment-2" }],
onLoadMore: sinon.spy(),
disableLoadMore: false,
hasMore: true,
};
const wrapper = shallow(<Stream {...props} />);
it("renders a load more button", () => {
expect(wrapper).toMatchSnapshot();
});
it("calls onLoadMore", () => {
wrapper.find("#talk-comments-stream-loadMore").simulate("click");
expect(props.onLoadMore.calledOnce).toBe(true);
});
const wrapperDisabledButton = shallow(<Stream {...props} disableLoadMore />);
it("disables load more button", () => {
expect(wrapperDisabledButton).toMatchSnapshot();
});
});
+38 -4
View File
@@ -1,18 +1,52 @@
import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
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";
import * as styles from "./Stream.css";
export interface StreamProps {
assetID: string;
isClosed: boolean;
comments: ReadonlyArray<{ id: string }>;
onLoadMore: () => void;
hasMore: boolean;
disableLoadMore: boolean;
}
const Stream: StatelessComponent<StreamProps> = props => {
return (
<div>
{props.comments.map(comment => (
<CommentContainer key={comment.id} data={comment} gutterBottom />
))}
<div className={styles.root}>
<Logo gutterBottom />
<PostCommentFormContainer assetID={props.assetID} />
<div id="talk-comments-stream-log" role="log" aria-live="polite">
{props.comments.map(comment => (
<div key={comment.id}>
<CommentContainer data={comment} gutterBottom />
<ReplyListContainer comment={comment} />
</div>
))}
{props.hasMore && (
<Localized id="comments-stream-loadMore">
<Button
id={"talk-comments-stream-loadMore"}
onClick={props.onLoadMore}
secondary
invert
fullWidth
disabled={props.disableLoadMore}
aria-controls="talk-comments-stream-log"
>
Load More
</Button>
</Localized>
)}
</div>
</div>
);
};
@@ -0,0 +1,3 @@
.root {
composes: heading4 from "talk-ui/shared/typography.css";
}
@@ -0,0 +1,12 @@
import { shallow } from "enzyme";
import React from "react";
import Username from "./Username";
it("renders correctly", () => {
const props = {
children: "Marvin",
};
const wrapper = shallow(<Username {...props} />);
expect(wrapper).toMatchSnapshot();
});
@@ -0,0 +1,14 @@
import React from "react";
import { StatelessComponent } from "react";
import * as styles from "./Username.css";
export interface CommentProps {
children: string;
}
const Username: StatelessComponent<CommentProps> = props => {
return <span className={styles.root}>{props.children}</span>;
};
export default Username;
@@ -2,14 +2,16 @@
exports[`renders username and body 1`] = `
<div
className="root"
className="Comment-root"
role="article"
>
<withPropsOnChange(Typography)
className="author"
gutterBottom={true}
<div
className="Comment-topBar"
>
Marvin
</withPropsOnChange(Typography)>
<Username>
Marvin
</Username>
</div>
<withPropsOnChange(Typography)>
Woof
</withPropsOnChange(Typography)>
@@ -18,14 +20,16 @@ exports[`renders username and body 1`] = `
exports[`renders with gutterBottom 1`] = `
<div
className="root gutterBottom"
className="Comment-root Comment-gutterBottom"
role="article"
>
<withPropsOnChange(Typography)
className="author"
gutterBottom={true}
<div
className="Comment-topBar"
>
Marvin
</withPropsOnChange(Typography)>
<Username>
Marvin
</Username>
</div>
<withPropsOnChange(Typography)>
Woof
</withPropsOnChange(Typography)>
@@ -0,0 +1,72 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Indent>
<div
id="talk-comments-replyList-log--comment-id"
role="log"
>
<Relay(CommentContainer)
data={
Object {
"id": "comment-1",
}
}
gutterBottom={true}
key="comment-1"
/>
<Relay(CommentContainer)
data={
Object {
"id": "comment-2",
}
}
gutterBottom={true}
key="comment-2"
/>
</div>
</Indent>
`;
exports[`when there is more renders a load more button 1`] = `
<Indent>
<div
id="talk-comments-replyList-log--comment-id"
role="log"
>
<Relay(CommentContainer)
data={
Object {
"id": "comment-1",
}
}
gutterBottom={true}
key="comment-1"
/>
<Relay(CommentContainer)
data={
Object {
"id": "comment-2",
}
}
gutterBottom={true}
key="comment-2"
/>
<Localized
id="comments-replyList-showAll"
>
<withPropsOnChange(Button)
aria-controls="talk-comments-replyList-log--comment-id"
disabled={false}
fullWidth={true}
id="talk-comments-replyList-showAll--comment-id"
invert={true}
onClick={[Function]}
secondary={true}
>
Show All Replies
</withPropsOnChange(Button)>
</Localized>
</div>
</Indent>
`;
@@ -0,0 +1,202 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<div
className="Stream-root"
>
<Logo
gutterBottom={true}
/>
<withContext(createMutationContainer(PostCommentFormContainer))
assetID="asset-id"
/>
<div
aria-live="polite"
id="talk-comments-stream-log"
role="log"
>
<div
key="comment-1"
>
<Relay(CommentContainer)
data={
Object {
"id": "comment-1",
}
}
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>
</div>
`;
exports[`when there is more disables load more button 1`] = `
<div
className="Stream-root"
>
<Logo
gutterBottom={true}
/>
<withContext(createMutationContainer(PostCommentFormContainer))
assetID="asset-id"
/>
<div
aria-live="polite"
id="talk-comments-stream-log"
role="log"
>
<div
key="comment-1"
>
<Relay(CommentContainer)
data={
Object {
"id": "comment-1",
}
}
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>
<Localized
id="comments-stream-loadMore"
>
<withPropsOnChange(Button)
aria-controls="talk-comments-stream-log"
disabled={true}
fullWidth={true}
id="talk-comments-stream-loadMore"
invert={true}
onClick={[Function]}
secondary={true}
>
Load More
</withPropsOnChange(Button)>
</Localized>
</div>
</div>
`;
exports[`when there is more renders a load more button 1`] = `
<div
className="Stream-root"
>
<Logo
gutterBottom={true}
/>
<withContext(createMutationContainer(PostCommentFormContainer))
assetID="asset-id"
/>
<div
aria-live="polite"
id="talk-comments-stream-log"
role="log"
>
<div
key="comment-1"
>
<Relay(CommentContainer)
data={
Object {
"id": "comment-1",
}
}
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>
<Localized
id="comments-stream-loadMore"
>
<withPropsOnChange(Button)
aria-controls="talk-comments-stream-log"
disabled={false}
fullWidth={true}
id="talk-comments-stream-loadMore"
invert={true}
onClick={[Function]}
secondary={true}
>
Load More
</withPropsOnChange(Button)>
</Localized>
</div>
</div>
`;
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<span
className="Username-root"
>
Marvin
</span>
`;