mirror of
https://github.com/wassname/talk.git
synced 2026-07-19 11:28:50 +08:00
[CORL-916] conversation view (#2850)
* show comment counts for stories in story table * remove debug code * add modal to view comment * load converasation view into modal * style modal header * style conversation view modal * style conversation thread in conversation modal * styles conversation view comments * show replies to replies on click * update snaps * update snap * pass settings prop through to comment content, set default replies to 5
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
$moderateCardUsernameBackgroundHover: var(--v2-colors-grey-200);
|
||||
|
||||
.root {
|
||||
margin-right: var(--mini-unit);
|
||||
padding: var(--spacing-1);
|
||||
margin-left: calc(-1 * var(--v2-spacing-1));
|
||||
|
||||
line-height: var(--v2-line-height-reset);
|
||||
|
||||
&:hover {
|
||||
background-color: $moderateCardUsernameBackgroundHover;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $moderateCardUsernameBackgroundHover;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-style: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { BaseButton } from "coral-ui/components/v2";
|
||||
import Username from "./Username";
|
||||
|
||||
import styles from "./UsernameButton.css";
|
||||
|
||||
interface Props {
|
||||
username: string;
|
||||
className?: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const UsernameButton: FunctionComponent<Props> = ({
|
||||
username,
|
||||
onClick,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<BaseButton onClick={onClick} className={cn(styles.root, className)}>
|
||||
<Username>{username}</Username>
|
||||
</BaseButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsernameButton;
|
||||
@@ -0,0 +1,4 @@
|
||||
export { default as CommentContent } from "./CommentContent";
|
||||
export { default as InReplyTo } from "./InReplyTo";
|
||||
export { default as Username } from "./Username";
|
||||
export { default as UsernameButton } from "./UsernameButton";
|
||||
@@ -0,0 +1,38 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { Modal } from "coral-ui/components";
|
||||
|
||||
import ConversationModalQuery from "./ConversationModalQuery";
|
||||
|
||||
interface ConversationModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
commentID?: string;
|
||||
onUsernameClicked: (userID: string) => void;
|
||||
}
|
||||
|
||||
const ConversationModal: FunctionComponent<ConversationModalProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
commentID,
|
||||
onUsernameClicked,
|
||||
}) => {
|
||||
if (!commentID) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
{({ firstFocusableRef, lastFocusableRef }) => (
|
||||
<ConversationModalQuery
|
||||
commentID={commentID}
|
||||
onClose={onClose}
|
||||
firstFocusableRef={firstFocusableRef}
|
||||
lastFocusableRef={lastFocusableRef}
|
||||
onUsernameClicked={onUsernameClicked}
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConversationModal;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
$conversationModalHighlightBackground: var(--v2-colors-teal-100);
|
||||
$conversationModalCommentText: var(--v2-colors-mono-500);
|
||||
.root {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.line {
|
||||
border-color: var(--v2-colors-grey-400);
|
||||
}
|
||||
|
||||
.adornments {
|
||||
margin-right: var(--v2-spacing-2);
|
||||
}
|
||||
|
||||
.circle {
|
||||
margin-top: var(--v2-spacing-2);
|
||||
margin-bottom: var(--v2-spacing-2);
|
||||
}
|
||||
|
||||
.highlightedCircle {
|
||||
margin-top: var(--v2-spacing-1);
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
background-color: $conversationModalHighlightBackground;
|
||||
padding: var(--v2-spacing-2);
|
||||
padding-top: var(--v2-spacing-1);
|
||||
margin-bottom: var(--v2-spacing-2);
|
||||
}
|
||||
|
||||
.commentText {
|
||||
color: $conversationModalCommentText;
|
||||
}
|
||||
|
||||
.showReplies {
|
||||
padding-left: var(--v2-spacing-2);
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
|
||||
import { graphql, withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
HorizontalGutter,
|
||||
Timestamp,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import ConversationModalRepliesQuery from "./ConversationModalRepliesQuery";
|
||||
|
||||
import { ConversationModalCommentContainer_comment } from "coral-admin/__generated__/ConversationModalCommentContainer_comment.graphql";
|
||||
import { ConversationModalCommentContainer_settings } from "coral-admin/__generated__/ConversationModalCommentContainer_settings.graphql";
|
||||
|
||||
import { CommentContent, InReplyTo, UsernameButton } from "../Comment";
|
||||
import { Circle, Line } from "../Timeline";
|
||||
|
||||
import styles from "./ConversationModalCommentContainer.css";
|
||||
|
||||
interface Props {
|
||||
comment: ConversationModalCommentContainer_comment;
|
||||
isHighlighted: boolean;
|
||||
onUsernameClick: (id?: string) => void;
|
||||
isParent?: boolean;
|
||||
isReply?: boolean;
|
||||
settings: ConversationModalCommentContainer_settings;
|
||||
}
|
||||
|
||||
const ConversationModalCommentContainer: FunctionComponent<Props> = ({
|
||||
comment,
|
||||
isHighlighted,
|
||||
isParent,
|
||||
isReply,
|
||||
onUsernameClick,
|
||||
settings,
|
||||
}) => {
|
||||
const commentAuthorClick = useCallback(() => {
|
||||
if (comment.author) {
|
||||
onUsernameClick(comment.author.id);
|
||||
}
|
||||
}, [onUsernameClick, comment.author]);
|
||||
const commentParentAuthorClick = useCallback(() => {
|
||||
if (comment.parent && comment.parent.author) {
|
||||
onUsernameClick(comment.parent.author.id);
|
||||
}
|
||||
}, [onUsernameClick, comment.parent]);
|
||||
const [showReplies, setShowReplies] = useState(false);
|
||||
const onShowReplies = useCallback(() => {
|
||||
setShowReplies(true);
|
||||
}, []);
|
||||
return (
|
||||
<HorizontalGutter>
|
||||
<Flex>
|
||||
<Flex
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
className={cn(styles.adornments, {
|
||||
[styles.highlightedCircle]: isHighlighted,
|
||||
})}
|
||||
>
|
||||
{!isReply && <Circle className={styles.circle} />}
|
||||
{(isParent || isReply) && <Line className={styles.line} />}
|
||||
</Flex>
|
||||
<HorizontalGutter
|
||||
spacing={1}
|
||||
className={cn(styles.root, {
|
||||
[styles.highlighted]: isHighlighted,
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<Flex alignItems="center">
|
||||
{comment.author && comment.author.username && (
|
||||
<UsernameButton
|
||||
onClick={commentAuthorClick}
|
||||
username={comment.author.username}
|
||||
/>
|
||||
)}
|
||||
<Timestamp>{comment.createdAt}</Timestamp>
|
||||
</Flex>
|
||||
{comment.parent &&
|
||||
comment.parent.author &&
|
||||
comment.parent.author.username && (
|
||||
<InReplyTo onUsernameClick={commentParentAuthorClick}>
|
||||
{comment.parent.author.username}
|
||||
</InReplyTo>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{comment.body && (
|
||||
<CommentContent className={styles.commentText} phrases={settings}>
|
||||
{comment.body}
|
||||
</CommentContent>
|
||||
)}
|
||||
</div>
|
||||
</HorizontalGutter>
|
||||
</Flex>
|
||||
{isReply && comment.replyCount > 0 && (
|
||||
<div className={styles.showReplies}>
|
||||
{!showReplies && (
|
||||
<Localized id="conversation-modal-reply-show-replies">
|
||||
<Button
|
||||
color="mono"
|
||||
variant="outline"
|
||||
onClick={onShowReplies}
|
||||
fullWidth
|
||||
>
|
||||
Show replies
|
||||
</Button>
|
||||
</Localized>
|
||||
)}
|
||||
{showReplies && (
|
||||
<ConversationModalRepliesQuery
|
||||
onUsernameClicked={onUsernameClick}
|
||||
commentID={comment.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment ConversationModalCommentContainer_settings on Settings {
|
||||
locale
|
||||
wordList {
|
||||
banned
|
||||
suspect
|
||||
}
|
||||
multisite
|
||||
featureFlags
|
||||
...MarkersContainer_settings
|
||||
}
|
||||
`,
|
||||
comment: graphql`
|
||||
fragment ConversationModalCommentContainer_comment on Comment {
|
||||
id
|
||||
body
|
||||
createdAt
|
||||
author {
|
||||
username
|
||||
id
|
||||
}
|
||||
replyCount
|
||||
parent {
|
||||
author {
|
||||
username
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(ConversationModalCommentContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,10 @@
|
||||
.root {
|
||||
}
|
||||
.topCircle {
|
||||
margin-right: var(--v2-spacing-2);
|
||||
}
|
||||
|
||||
.bottomCircleContainer {
|
||||
width: 8px;
|
||||
padding-top: var(--v2-spacing-1);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql, RelayPaginationProp } from "react-relay";
|
||||
|
||||
import {
|
||||
useLoadMore,
|
||||
withPaginationContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import {
|
||||
Button,
|
||||
Counter,
|
||||
Flex,
|
||||
HorizontalGutter,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import { ConversationModalContainer_comment } from "coral-admin/__generated__/ConversationModalContainer_comment.graphql";
|
||||
import { ConversationModalContainer_settings } from "coral-admin/__generated__/ConversationModalContainer_settings.graphql";
|
||||
import { ConversationModalContainerPaginationQueryVariables } from "coral-admin/__generated__/ConversationModalContainerPaginationQuery.graphql";
|
||||
|
||||
import { Circle } from "../Timeline";
|
||||
import ConversationModalComment from "./ConversationModalCommentContainer";
|
||||
|
||||
import styles from "./ConversationModalContainer.css";
|
||||
|
||||
interface Props {
|
||||
relay: RelayPaginationProp;
|
||||
comment: ConversationModalContainer_comment;
|
||||
settings: ConversationModalContainer_settings;
|
||||
onClose: () => void;
|
||||
onUsernameClicked: (id?: string) => void;
|
||||
}
|
||||
|
||||
const ConversationModalContainer: FunctionComponent<Props> = ({
|
||||
comment,
|
||||
relay,
|
||||
settings,
|
||||
onUsernameClicked,
|
||||
}) => {
|
||||
const [loadMore] = useLoadMore(relay, 5);
|
||||
const parents = comment.parents.edges.map(edge => edge.node);
|
||||
return (
|
||||
<HorizontalGutter className={styles.root}>
|
||||
{comment.parentCount > parents.length && (
|
||||
<div>
|
||||
<Flex alignItems="center">
|
||||
<Circle hollow={true} className={styles.topCircle} />
|
||||
<Button underline variant="text" onClick={loadMore}>
|
||||
<Localized id="conversation-modal-show-more-parents">
|
||||
<span>Show more of this conversation</span>
|
||||
</Localized>
|
||||
<Counter>{comment.parentCount}</Counter>
|
||||
</Button>
|
||||
</Flex>
|
||||
<Flex
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
className={styles.bottomCircleContainer}
|
||||
>
|
||||
<Circle color="light" size="small" />
|
||||
</Flex>
|
||||
</div>
|
||||
)}
|
||||
{parents.map(parent => (
|
||||
<ConversationModalComment
|
||||
key={parent.id}
|
||||
isParent={true}
|
||||
comment={parent}
|
||||
onUsernameClick={onUsernameClicked}
|
||||
settings={settings}
|
||||
isHighlighted={false}
|
||||
/>
|
||||
))}
|
||||
<ConversationModalComment
|
||||
comment={comment}
|
||||
settings={settings}
|
||||
onUsernameClick={onUsernameClicked}
|
||||
isHighlighted={true}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: (cvle) If this could be autogenerated.
|
||||
type FragmentVariables = ConversationModalContainerPaginationQueryVariables;
|
||||
|
||||
const enhanced = withPaginationContainer<
|
||||
Props,
|
||||
ConversationModalContainerPaginationQueryVariables,
|
||||
FragmentVariables
|
||||
>(
|
||||
{
|
||||
settings: graphql`
|
||||
fragment ConversationModalContainer_settings on Settings {
|
||||
...ConversationModalCommentContainer_settings
|
||||
}
|
||||
`,
|
||||
comment: graphql`
|
||||
fragment ConversationModalContainer_comment on Comment
|
||||
@argumentDefinitions(
|
||||
count: { type: "Int!", defaultValue: 1 }
|
||||
cursor: { type: "Cursor" }
|
||||
) {
|
||||
id
|
||||
...ConversationModalCommentContainer_comment
|
||||
rootParent {
|
||||
id
|
||||
}
|
||||
parents(last: $count, before: $cursor)
|
||||
@connection(key: "ConversationModal_parents") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...ConversationModalCommentContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
parentCount
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
direction: "backward",
|
||||
getConnectionFromProps(props) {
|
||||
return props.comment && props.comment.parents;
|
||||
},
|
||||
// This is also the default implementation of `getFragmentVariables` if it isn't provided.
|
||||
getFragmentVariables(prevVars, totalCount) {
|
||||
return {
|
||||
...prevVars,
|
||||
count: totalCount,
|
||||
};
|
||||
},
|
||||
getVariables(props, { count, cursor }) {
|
||||
return {
|
||||
count,
|
||||
cursor,
|
||||
// commentID isn't specified as an @argument for the fragment, but it should be a
|
||||
// variable available for the fragment under the query root.
|
||||
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 ConversationModalContainerPaginationQuery(
|
||||
$count: Int!
|
||||
$cursor: Cursor
|
||||
$commentID: ID!
|
||||
) {
|
||||
comment(id: $commentID) {
|
||||
...ConversationModalContainer_comment
|
||||
@arguments(count: $count, cursor: $cursor)
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
)(ConversationModalContainer);
|
||||
|
||||
export default enhanced;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
$modal-header-text: var(--v2-colors-mono-900);
|
||||
|
||||
.title {
|
||||
font-size: var(--v2-font-size-4);
|
||||
font-family: var(--v2-font-family-primary);
|
||||
font-weight: var(--v2-font-weight-primary-regular);
|
||||
line-height: var(--v2-line-height-title);
|
||||
color: $modal-header-text;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.conversationTitle {
|
||||
font-weight: var(--v2-font-weight-primary-semi-bold);
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
margin-bottom: var(--v2-spacing-2);
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import { useRouter } from "found";
|
||||
import React, { FunctionComponent, RefObject, useCallback } from "react";
|
||||
|
||||
import { getModerationLink } from "coral-framework/helpers";
|
||||
import { graphql, withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { Button, HorizontalGutter, ModalHeader } from "coral-ui/components/v2";
|
||||
|
||||
import { ConversationModalHeaderContainer_comment } from "coral-admin/__generated__/ConversationModalHeaderContainer_comment.graphql";
|
||||
|
||||
import styles from "./ConversationModalHeaderContainer.css";
|
||||
|
||||
interface Props {
|
||||
comment: ConversationModalHeaderContainer_comment;
|
||||
onClose: () => void;
|
||||
focusableRef: RefObject<any>;
|
||||
}
|
||||
|
||||
const ConversationModalHeaderContainer: FunctionComponent<Props> = ({
|
||||
comment,
|
||||
onClose,
|
||||
focusableRef,
|
||||
}) => {
|
||||
const { router } = useRouter();
|
||||
const onModerate = useCallback(() => {
|
||||
const link = getModerationLink({ storyID: comment.story.id });
|
||||
router.push(link);
|
||||
}, [comment.id]);
|
||||
return (
|
||||
<ModalHeader onClose={onClose} focusableRef={focusableRef}>
|
||||
<HorizontalGutter spacing={3}>
|
||||
<h1 className={styles.title}>
|
||||
<Localized id="conversation-modal-header-title">
|
||||
<span className={styles.conversationTitle}>Conversation on:</span>
|
||||
</Localized>
|
||||
{comment.story.metadata
|
||||
? comment.story.metadata.title
|
||||
: comment.story.url}
|
||||
</h1>
|
||||
<Localized id="conversation-modal-header-moderate-link">
|
||||
<Button variant="outline" color="mono" uppercase onClick={onModerate}>
|
||||
Moderate story
|
||||
</Button>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
</ModalHeader>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
comment: graphql`
|
||||
fragment ConversationModalHeaderContainer_comment on Comment {
|
||||
story {
|
||||
id
|
||||
metadata {
|
||||
title
|
||||
}
|
||||
url
|
||||
}
|
||||
id
|
||||
}
|
||||
`,
|
||||
})(ConversationModalHeaderContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,8 @@
|
||||
.root {
|
||||
padding: 0;
|
||||
width: 635px;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: var(--v2-spacing-4);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import {
|
||||
graphql,
|
||||
QueryRenderData,
|
||||
QueryRenderer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { CallOut, Card, Spinner } from "coral-ui/components/v2";
|
||||
|
||||
import { ConversationModalQuery as QueryTypes } from "coral-admin/__generated__/ConversationModalQuery.graphql";
|
||||
|
||||
import ConversationModalContainer from "./ConversationModalContainer";
|
||||
import ConversationModalHeaderContainer from "./ConversationModalHeaderContainer";
|
||||
import ConversationModalRepliesContainer from "./ConversationModalRepliesContainer";
|
||||
|
||||
import styles from "./ConversationModalQuery.css";
|
||||
|
||||
interface Props {
|
||||
commentID: string;
|
||||
onClose: () => void;
|
||||
firstFocusableRef: React.RefObject<any>;
|
||||
onUsernameClicked: (userID: string) => void;
|
||||
lastFocusableRef: React.RefObject<any>;
|
||||
}
|
||||
|
||||
const ConversationModalQuery: FunctionComponent<Props> = ({
|
||||
commentID,
|
||||
onClose,
|
||||
lastFocusableRef,
|
||||
onUsernameClicked,
|
||||
}) => {
|
||||
return (
|
||||
<QueryRenderer<QueryTypes>
|
||||
query={graphql`
|
||||
query ConversationModalQuery($commentID: ID!) {
|
||||
settings {
|
||||
...ConversationModalContainer_settings
|
||||
...ConversationModalRepliesContainer_settings
|
||||
}
|
||||
comment(id: $commentID) {
|
||||
...ConversationModalContainer_comment
|
||||
...ConversationModalRepliesContainer_comment
|
||||
...ConversationModalHeaderContainer_comment
|
||||
}
|
||||
}
|
||||
`}
|
||||
variables={{ commentID }}
|
||||
cacheConfig={{ force: true }}
|
||||
render={({ props }: QueryRenderData<QueryTypes>) => {
|
||||
if (!props) {
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.comment) {
|
||||
return (
|
||||
<div>
|
||||
<CallOut>
|
||||
<Localized id="conversation-modal-comment-not-found">
|
||||
Comment not found.
|
||||
</Localized>
|
||||
</CallOut>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={styles.root}>
|
||||
<ConversationModalHeaderContainer
|
||||
onClose={onClose}
|
||||
comment={props.comment}
|
||||
focusableRef={lastFocusableRef}
|
||||
/>
|
||||
<div className={styles.body}>
|
||||
<ConversationModalContainer
|
||||
onClose={onClose}
|
||||
settings={props.settings}
|
||||
comment={props.comment}
|
||||
onUsernameClicked={onUsernameClicked}
|
||||
/>
|
||||
<ConversationModalRepliesContainer
|
||||
onClose={onClose}
|
||||
comment={props.comment}
|
||||
settings={props.settings}
|
||||
onUsernameClicked={onUsernameClicked}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConversationModalQuery;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
.comment {
|
||||
padding-left: calc(var(--v2-spacing-4));
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-left: calc(var(--v2-spacing-4));
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
import { graphql, RelayPaginationProp } from "react-relay";
|
||||
|
||||
import {
|
||||
useLoadMore,
|
||||
withPaginationContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { Omit } from "coral-framework/types";
|
||||
import { Button, HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import { ConversationModalRepliesContainer_comment } from "coral-admin/__generated__/ConversationModalRepliesContainer_comment.graphql";
|
||||
import { ConversationModalRepliesContainer_settings } from "coral-admin/__generated__/ConversationModalRepliesContainer_settings.graphql";
|
||||
import { ConversationModalRepliesContainerPaginationQueryVariables } from "coral-admin/__generated__/ConversationModalRepliesContainerPaginationQuery.graphql";
|
||||
import ConversationModalCommentContainer from "./ConversationModalCommentContainer";
|
||||
|
||||
import styles from "./ConversationModalRepliesContainer.css";
|
||||
|
||||
interface Props {
|
||||
relay: RelayPaginationProp;
|
||||
comment: ConversationModalRepliesContainer_comment;
|
||||
settings: ConversationModalRepliesContainer_settings;
|
||||
onClose: () => void;
|
||||
onUsernameClicked: (id?: string) => void;
|
||||
}
|
||||
|
||||
const ConversationModalRepliesContainer: FunctionComponent<Props> = ({
|
||||
comment,
|
||||
relay,
|
||||
settings,
|
||||
onUsernameClicked,
|
||||
}) => {
|
||||
const [loadMore] = useLoadMore(relay, 5);
|
||||
const replies = comment.replies.edges.map(edge => edge.node);
|
||||
const [showReplies, setShowReplies] = useState(false);
|
||||
const onShowReplies = useCallback(() => {
|
||||
setShowReplies(true);
|
||||
}, []);
|
||||
return (
|
||||
<HorizontalGutter>
|
||||
{showReplies &&
|
||||
replies.map(reply => (
|
||||
<div key={reply.id} className={styles.comment}>
|
||||
<ConversationModalCommentContainer
|
||||
key={reply.id}
|
||||
settings={settings}
|
||||
comment={reply}
|
||||
isHighlighted={false}
|
||||
isReply={true}
|
||||
onUsernameClick={onUsernameClicked}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className={styles.footer}>
|
||||
{!showReplies && comment.replyCount > 0 && (
|
||||
<Localized id="conversation-modal-replies-show">
|
||||
<Button variant="outline" fullWidth onClick={onShowReplies}>
|
||||
Show replies
|
||||
</Button>
|
||||
</Localized>
|
||||
)}
|
||||
{showReplies &&
|
||||
comment.replyCount > replies.length &&
|
||||
replies.length > 0 && (
|
||||
<Localized id="conversation-modal-replies-show-more">
|
||||
<Button variant="outline" fullWidth onClick={loadMore}>
|
||||
Show more replies
|
||||
</Button>
|
||||
</Localized>
|
||||
)}
|
||||
</div>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: (cvle) If this could be autogenerated.
|
||||
type FragmentVariables = Omit<
|
||||
ConversationModalRepliesContainerPaginationQueryVariables,
|
||||
"commentID"
|
||||
>;
|
||||
|
||||
const enhanced = withPaginationContainer<
|
||||
Props,
|
||||
ConversationModalRepliesContainerPaginationQueryVariables,
|
||||
FragmentVariables
|
||||
>(
|
||||
{
|
||||
settings: graphql`
|
||||
fragment ConversationModalRepliesContainer_settings on Settings {
|
||||
...ConversationModalCommentContainer_settings
|
||||
}
|
||||
`,
|
||||
comment: graphql`
|
||||
fragment ConversationModalRepliesContainer_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: "ConversationModalReplies_replies") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...ConversationModalCommentContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
replyCount
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
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 ConversationModalRepliesContainerPaginationQuery(
|
||||
$count: Int!
|
||||
$cursor: Cursor
|
||||
$orderBy: COMMENT_SORT!
|
||||
$commentID: ID!
|
||||
) {
|
||||
comment(id: $commentID) {
|
||||
...ConversationModalRepliesContainer_comment
|
||||
@arguments(count: $count, cursor: $cursor, orderBy: $orderBy)
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
)(ConversationModalRepliesContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import {
|
||||
graphql,
|
||||
QueryRenderData,
|
||||
QueryRenderer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { CallOut, Spinner } from "coral-ui/components/v2";
|
||||
|
||||
import { ConversationModalRepliesQuery as QueryTypes } from "coral-admin/__generated__/ConversationModalRepliesQuery.graphql";
|
||||
import ConversationModalCommentContainer from "./ConversationModalCommentContainer";
|
||||
|
||||
interface Props {
|
||||
commentID: string;
|
||||
onUsernameClicked: (userID: string) => void;
|
||||
}
|
||||
|
||||
const ConversationModalRepliesQuery: FunctionComponent<Props> = ({
|
||||
commentID,
|
||||
onUsernameClicked,
|
||||
}) => {
|
||||
return (
|
||||
<QueryRenderer<QueryTypes>
|
||||
query={graphql`
|
||||
query ConversationModalRepliesQuery($commentID: ID!) {
|
||||
settings {
|
||||
...ConversationModalCommentContainer_settings
|
||||
}
|
||||
comment(id: $commentID) {
|
||||
replies {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...ConversationModalCommentContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
variables={{ commentID }}
|
||||
cacheConfig={{ force: true }}
|
||||
render={({ props }: QueryRenderData<QueryTypes>) => {
|
||||
if (!props) {
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.comment) {
|
||||
return (
|
||||
<div>
|
||||
<CallOut>
|
||||
<Localized id="conversation-modal-comment-not-found">
|
||||
Comment not found.
|
||||
</Localized>
|
||||
</CallOut>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const replies = props.comment.replies.edges.map(edge => edge.node);
|
||||
return (
|
||||
<div>
|
||||
{replies.map(reply => (
|
||||
<div key={reply.id}>
|
||||
<ConversationModalCommentContainer
|
||||
key={reply.id}
|
||||
comment={reply}
|
||||
settings={props.settings}
|
||||
isHighlighted={false}
|
||||
isReply={true}
|
||||
onUsernameClick={onUsernameClicked}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConversationModalRepliesQuery;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ConversationModal";
|
||||
@@ -6,7 +6,7 @@ import { HorizontalGutter, Timestamp } from "coral-ui/components/v2";
|
||||
import { CommentRevisionContainer_comment as CommentData } from "coral-admin/__generated__/CommentRevisionContainer_comment.graphql";
|
||||
import { CommentRevisionContainer_settings as SettingsData } from "coral-admin/__generated__/CommentRevisionContainer_settings.graphql";
|
||||
|
||||
import CommentContent from "./CommentContent";
|
||||
import { CommentContent } from "../Comment";
|
||||
|
||||
interface Props {
|
||||
comment: CommentData;
|
||||
|
||||
@@ -15,26 +15,6 @@ $moderateCardLinkTextColor: var(--v2-colors-teal-700);
|
||||
margin-bottom: var(--v2-spacing-4);
|
||||
}
|
||||
|
||||
.usernameButton {
|
||||
margin-right: var(--mini-unit);
|
||||
padding: var(--spacing-1);
|
||||
margin-left: calc(-1 * var(--v2-spacing-1));
|
||||
|
||||
line-height: var(--v2-line-height-reset);
|
||||
|
||||
&:hover {
|
||||
background-color: $moderateCardUsernameBackgroundHover;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $moderateCardUsernameBackgroundHover;
|
||||
border-radius: var(--v2-round-corners);
|
||||
border-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
.inReplyTo {
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ const baseProps: PropTypesOf<typeof ModerateCardN> = {
|
||||
onBan: noop,
|
||||
onUsernameClick: noop,
|
||||
onFocusOrClick: noop,
|
||||
onConversationClick: noop,
|
||||
showStory: false,
|
||||
moderatedBy: null,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,8 @@ import { HOTKEYS } from "coral-admin/constants";
|
||||
import { GetPhrasesRegExpOptions } from "coral-admin/helpers";
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import {
|
||||
BaseButton,
|
||||
Button,
|
||||
ButtonIcon,
|
||||
Card,
|
||||
Flex,
|
||||
HorizontalGutter,
|
||||
@@ -22,14 +23,12 @@ import {
|
||||
Timestamp,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import { CommentContent, InReplyTo, UsernameButton } from "../Comment";
|
||||
import ApproveButton from "./ApproveButton";
|
||||
import CommentAuthorContainer from "./CommentAuthorContainer";
|
||||
import CommentContent from "./CommentContent";
|
||||
import FeatureButton from "./FeatureButton";
|
||||
import InReplyTo from "./InReplyTo";
|
||||
import MarkersContainer from "./MarkersContainer";
|
||||
import RejectButton from "./RejectButton";
|
||||
import Username from "./Username";
|
||||
|
||||
import styles from "./ModerateCard.css";
|
||||
|
||||
@@ -59,6 +58,7 @@ interface Props {
|
||||
onReject: () => void;
|
||||
onFeature: () => void;
|
||||
onUsernameClick: (id?: string) => void;
|
||||
onConversationClick: (id?: string) => void;
|
||||
onFocusOrClick: () => void;
|
||||
mini?: boolean;
|
||||
hideUsername?: boolean;
|
||||
@@ -102,6 +102,7 @@ const ModerateCard: FunctionComponent<Props> = ({
|
||||
moderatedBy,
|
||||
selected,
|
||||
onFocusOrClick,
|
||||
onConversationClick,
|
||||
mini = false,
|
||||
hideUsername = false,
|
||||
deleted = false,
|
||||
@@ -165,6 +166,9 @@ const ModerateCard: FunctionComponent<Props> = ({
|
||||
onUsernameClick(inReplyTo.id);
|
||||
}
|
||||
}, [onUsernameClick, inReplyTo]);
|
||||
const viewConversationClick = useCallback(() => {
|
||||
onConversationClick();
|
||||
}, [onConversationClick]);
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
@@ -190,12 +194,10 @@ const ModerateCard: FunctionComponent<Props> = ({
|
||||
<Flex alignItems="center">
|
||||
{!hideUsername && username && (
|
||||
<>
|
||||
<BaseButton
|
||||
<UsernameButton
|
||||
username={username}
|
||||
onClick={commentAuthorClick}
|
||||
className={styles.usernameButton}
|
||||
>
|
||||
<Username>{username}</Username>
|
||||
</BaseButton>
|
||||
/>
|
||||
<CommentAuthorContainer comment={comment} />
|
||||
</>
|
||||
)}
|
||||
@@ -224,15 +226,12 @@ const ModerateCard: FunctionComponent<Props> = ({
|
||||
{commentBody}
|
||||
</CommentContent>
|
||||
<div className={styles.viewContext}>
|
||||
<Localized id="moderate-comment-viewContext">
|
||||
<TextLink
|
||||
className={styles.link}
|
||||
href={viewContextHref}
|
||||
target="_blank"
|
||||
>
|
||||
View Context
|
||||
</TextLink>
|
||||
</Localized>
|
||||
<Button iconLeft variant="text" onClick={viewConversationClick}>
|
||||
<ButtonIcon>question_answer</ButtonIcon>
|
||||
<Localized id="moderate-comment-viewConversation">
|
||||
<span>View conversation</span>
|
||||
</Localized>
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className={cn(styles.separator, {
|
||||
|
||||
@@ -46,6 +46,7 @@ interface Props {
|
||||
mini?: boolean;
|
||||
hideUsername?: boolean;
|
||||
onUsernameClicked?: (userID: string) => void;
|
||||
onConversationClicked?: (commentID: string) => void;
|
||||
onSetSelected?: () => void;
|
||||
selected?: boolean;
|
||||
selectPrev?: () => void;
|
||||
@@ -86,6 +87,7 @@ const ModerateCardContainer: FunctionComponent<Props> = ({
|
||||
selectPrev,
|
||||
selectNext,
|
||||
onUsernameClicked: usernameClicked,
|
||||
onConversationClicked: conversationClicked,
|
||||
onSetSelected: setSelected,
|
||||
banUser,
|
||||
loadNext,
|
||||
@@ -160,6 +162,16 @@ const ModerateCardContainer: FunctionComponent<Props> = ({
|
||||
[usernameClicked, comment]
|
||||
);
|
||||
|
||||
const onConversationClicked = useCallback(
|
||||
(id?: string) => {
|
||||
if (!conversationClicked) {
|
||||
return;
|
||||
}
|
||||
conversationClicked(id || comment.id);
|
||||
},
|
||||
[conversationClicked, comment]
|
||||
);
|
||||
|
||||
const handleModerateStory = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
router.push(getModerationLink({ storyID: comment.story.id }));
|
||||
@@ -227,6 +239,7 @@ const ModerateCardContainer: FunctionComponent<Props> = ({
|
||||
onReject={handleReject}
|
||||
onFeature={onFeature}
|
||||
onUsernameClick={onUsernameClicked}
|
||||
onConversationClick={onConversationClicked}
|
||||
selected={selected}
|
||||
selectPrev={selectPrev}
|
||||
selectNext={selectNext}
|
||||
|
||||
+119
-112
@@ -19,14 +19,10 @@ exports[`renders approved correctly 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -67,17 +63,22 @@ exports[`renders approved correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -152,14 +153,10 @@ exports[`renders correctly 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -200,17 +197,22 @@ exports[`renders correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -285,14 +287,10 @@ exports[`renders dangling correctly 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -333,17 +331,22 @@ exports[`renders dangling correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -418,14 +421,10 @@ exports[`renders rejected correctly 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -466,17 +465,22 @@ exports[`renders rejected correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -551,14 +555,10 @@ exports[`renders reply correctly 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -608,17 +608,22 @@ exports[`renders reply correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -693,14 +698,10 @@ exports[`renders story info 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -741,17 +742,22 @@ exports[`renders story info 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -864,14 +870,10 @@ exports[`renders tombstoned when comment is deleted 1`] = `
|
||||
alignItems="center"
|
||||
>
|
||||
<React.Fragment>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-usernameButton"
|
||||
<UsernameButton
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
username="Theon"
|
||||
/>
|
||||
<Relay(CommentAuthorContainer)
|
||||
comment={Object {}}
|
||||
/>
|
||||
@@ -920,17 +922,22 @@ exports[`renders tombstoned when comment is deleted 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<Localized
|
||||
id="moderate-comment-viewContext"
|
||||
<ForwardRef(forwardRef)
|
||||
iconLeft={true}
|
||||
onClick={[Function]}
|
||||
variant="text"
|
||||
>
|
||||
<withPropsOnChange(TextLinkProps)
|
||||
className="ModerateCard-link"
|
||||
href="http://localhost/comment"
|
||||
target="_blank"
|
||||
<ForwardRef(forwardRef)>
|
||||
question_answer
|
||||
</ForwardRef(forwardRef)>
|
||||
<Localized
|
||||
id="moderate-comment-viewConversation"
|
||||
>
|
||||
View Context
|
||||
</withPropsOnChange(TextLinkProps)>
|
||||
</Localized>
|
||||
<span>
|
||||
View conversation
|
||||
</span>
|
||||
</Localized>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
.sizeReg {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.sizeSmall {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.colorReg {
|
||||
color: var(--v2-colors-grey-500);
|
||||
}
|
||||
|
||||
.colorLight {
|
||||
color: var(--v2-colors-grey-400);
|
||||
}
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.circle {
|
||||
fill: currentColor;
|
||||
stroke: currentColor;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import styles from "./Circle.css";
|
||||
|
||||
export interface CircleProps {
|
||||
className?: string;
|
||||
hollow?: boolean;
|
||||
size?: "small" | "regular";
|
||||
color?: "light" | "regular";
|
||||
}
|
||||
|
||||
const Circle: FunctionComponent<CircleProps> = props => {
|
||||
return (
|
||||
<div
|
||||
className={cn(styles.root, props.className, {
|
||||
[styles.sizeReg]: props.size === "regular",
|
||||
[styles.sizeSmall]: props.size === "small",
|
||||
[styles.colorReg]: props.color === "regular",
|
||||
[styles.colorLight]: props.color === "light",
|
||||
})}
|
||||
>
|
||||
<svg
|
||||
className={cn(styles.circle, {})}
|
||||
viewBox="0 0 100 100"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
{props.hollow && (
|
||||
<circle cx="50%" cy="50%" r="45" strokeWidth="10" fill="none" />
|
||||
)}
|
||||
{!props.hollow && <circle cx="50%" cy="50%" r="50" />}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Circle.defaultProps = {
|
||||
size: "regular",
|
||||
color: "regular",
|
||||
};
|
||||
|
||||
export default Circle;
|
||||
@@ -0,0 +1,9 @@
|
||||
.root {
|
||||
border-left: 1px solid var(--palette-grey-main);
|
||||
border-right: 1px solid var(--palette-grey-main);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dotted {
|
||||
border-left-style: dotted;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import { createRenderer } from "react-test-renderer/shallow";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
|
||||
import Line from "./Line";
|
||||
|
||||
it("renders correctly", () => {
|
||||
const props: PropTypesOf<typeof Line> = {
|
||||
className: "root",
|
||||
dotted: true,
|
||||
};
|
||||
const renderer = createRenderer();
|
||||
renderer.render(<Line {...props} />);
|
||||
expect(renderer.getRenderOutput()).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import styles from "./Line.css";
|
||||
|
||||
interface LineProps {
|
||||
className?: string;
|
||||
dotted?: boolean;
|
||||
}
|
||||
|
||||
const Line: FunctionComponent<LineProps> = props => {
|
||||
return (
|
||||
<div
|
||||
className={cn(styles.root, props.className, {
|
||||
[styles.dotted]: props.dotted,
|
||||
})}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Line;
|
||||
@@ -0,0 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<div
|
||||
className="Line-root root Line-dotted"
|
||||
/>
|
||||
`;
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as Circle } from "./Circle";
|
||||
export { default as Line } from "./Line";
|
||||
@@ -2,6 +2,7 @@ import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
|
||||
import AutoLoadMore from "coral-admin/components/AutoLoadMore";
|
||||
import ConversationModal from "coral-admin/components/ConversationModal";
|
||||
import ModerateCardContainer from "coral-admin/components/ModerateCard";
|
||||
import UserHistoryDrawer from "coral-admin/components/UserHistoryDrawer";
|
||||
import { HOTKEYS } from "coral-admin/constants";
|
||||
@@ -46,6 +47,10 @@ const Queue: FunctionComponent<Props> = ({
|
||||
const [userDrawerId, setUserDrawerID] = useState("");
|
||||
const [selectedComment, setSelectedComment] = useState<number | null>(0);
|
||||
const [singleView, setSingleView] = useState(false);
|
||||
const [conversationModalVisible, setConversationModalVisible] = useState(
|
||||
false
|
||||
);
|
||||
const [conversationCommentID, setConversationCommentID] = useState("");
|
||||
|
||||
const toggleView = useCallback(() => {
|
||||
if (!singleView) {
|
||||
@@ -94,6 +99,23 @@ const Queue: FunctionComponent<Props> = ({
|
||||
setUserDrawerID("");
|
||||
}, []);
|
||||
|
||||
const onShowConversationModal = useCallback((commentID: string) => {
|
||||
setConversationCommentID(commentID);
|
||||
setConversationModalVisible(true);
|
||||
}, []);
|
||||
|
||||
const onHideConversationModal = useCallback(() => {
|
||||
setConversationModalVisible(false);
|
||||
setConversationCommentID("");
|
||||
}, []);
|
||||
|
||||
const onUsernameClickedFromModal = useCallback((userID: string) => {
|
||||
setUserDrawerID(userID);
|
||||
setUserDrawerVisible(true);
|
||||
setConversationModalVisible(false);
|
||||
setConversationCommentID("");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<HorizontalGutter className={styles.root} size="double">
|
||||
{Boolean(viewNewCount && viewNewCount > 0) && (
|
||||
@@ -119,6 +141,7 @@ const Queue: FunctionComponent<Props> = ({
|
||||
danglingLogic={danglingLogic}
|
||||
showStoryInfo={Boolean(allStories)}
|
||||
onUsernameClicked={onShowUserDrawer}
|
||||
onConversationClicked={onShowConversationModal}
|
||||
onSetSelected={() => setSelectedComment(i)}
|
||||
selected={selectedComment === i}
|
||||
selectPrev={selectPrev}
|
||||
@@ -141,6 +164,12 @@ const Queue: FunctionComponent<Props> = ({
|
||||
onClose={onHideUserDrawer}
|
||||
userID={userDrawerId}
|
||||
/>
|
||||
<ConversationModal
|
||||
onUsernameClicked={onUsernameClickedFromModal}
|
||||
open={conversationModalVisible}
|
||||
onClose={onHideConversationModal}
|
||||
commentID={conversationCommentID}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,6 +24,12 @@ exports[`renders correctly with load more 1`] = `
|
||||
open={false}
|
||||
userID=""
|
||||
/>
|
||||
<ConversationModal
|
||||
commentID=""
|
||||
onClose={[Function]}
|
||||
onUsernameClicked={[Function]}
|
||||
open={false}
|
||||
/>
|
||||
</ForwardRef(forwardRef)>
|
||||
`;
|
||||
|
||||
@@ -43,5 +49,11 @@ exports[`renders correctly without load more 1`] = `
|
||||
open={false}
|
||||
userID=""
|
||||
/>
|
||||
<ConversationModal
|
||||
commentID=""
|
||||
onClose={[Function]}
|
||||
onUsernameClicked={[Function]}
|
||||
open={false}
|
||||
/>
|
||||
</ForwardRef(forwardRef)>
|
||||
`;
|
||||
|
||||
@@ -34,7 +34,7 @@ exports[`approves comment in reported queue: dangling 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -98,19 +98,28 @@ exports[`approves comment in reported queue: dangling 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -291,7 +300,7 @@ exports[`rejects comment in reported queue: dangling 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -355,19 +364,28 @@ exports[`rejects comment in reported queue: dangling 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -545,7 +563,7 @@ exports[`renders reported queue with comments 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -609,19 +627,28 @@ exports[`renders reported queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -786,7 +813,7 @@ exports[`renders reported queue with comments 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -850,19 +877,28 @@ exports[`renders reported queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/1"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -1043,7 +1079,7 @@ exports[`renders reported queue with comments 2`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -1107,19 +1143,28 @@ exports[`renders reported queue with comments 2`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -1284,7 +1329,7 @@ exports[`renders reported queue with comments 2`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -1348,19 +1393,28 @@ exports[`renders reported queue with comments 2`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/1"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -1531,7 +1585,7 @@ exports[`renders reported queue with comments and load more 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -1595,19 +1649,28 @@ exports[`renders reported queue with comments and load more 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/2"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
|
||||
@@ -34,7 +34,7 @@ exports[`approves comment in rejected queue: dangling 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -98,19 +98,28 @@ exports[`approves comment in rejected queue: dangling 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -288,7 +297,7 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -352,19 +361,28 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -529,7 +547,7 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -593,19 +611,28 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/1"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -776,7 +803,7 @@ exports[`renders rejected queue with comments and load more 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -840,19 +867,28 @@ exports[`renders rejected queue with comments and load more 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/2"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
|
||||
@@ -21,7 +21,7 @@ exports[`approves single comment 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -85,19 +85,28 @@ exports[`approves single comment 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -235,7 +244,7 @@ exports[`rejects single comment 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -299,19 +308,28 @@ exports[`rejects single comment 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
@@ -484,7 +502,7 @@ exports[`renders single comment view 1`] = `
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-usernameButton"
|
||||
className="BaseButton-root UsernameButton-root"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
@@ -548,19 +566,28 @@ exports[`renders single comment view 1`] = `
|
||||
<div
|
||||
className="ModerateCard-viewContext"
|
||||
>
|
||||
<a
|
||||
className="TextLink-root ModerateCard-link"
|
||||
href="http://localhost/comment/0"
|
||||
target="_blank"
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantText Button-uppercase Button-iconLeft"
|
||||
data-color="regular"
|
||||
data-variant="text"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View Context
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-xs TextLink-icon"
|
||||
className="Icon-root Icon-sm ButtonIcon-root"
|
||||
>
|
||||
open_in_new
|
||||
question_answer
|
||||
</i>
|
||||
</a>
|
||||
<span>
|
||||
View Conversation
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
|
||||
@@ -490,3 +490,7 @@ $button-text-dark-foreground-active: var(--v2-colors-blue-700);
|
||||
.linkButton {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ interface Props extends Omit<BaseButtonProps, "ref"> {
|
||||
|
||||
/** Internal: Forwarded Ref */
|
||||
forwardRef?: Ref<HTMLButtonElement>;
|
||||
|
||||
/* if true adds a link-style underline */
|
||||
underline?: boolean;
|
||||
}
|
||||
|
||||
export class Button extends React.Component<Props> {
|
||||
@@ -82,6 +85,7 @@ export class Button extends React.Component<Props> {
|
||||
iconRight,
|
||||
adornmentLeft,
|
||||
adornmentRight,
|
||||
underline,
|
||||
to,
|
||||
...rest
|
||||
} = this.props;
|
||||
@@ -108,6 +112,7 @@ export class Button extends React.Component<Props> {
|
||||
[classes.iconRight]: iconRight,
|
||||
[classes.adornmentLeft]: adornmentLeft,
|
||||
[classes.adornmentRight]: adornmentRight,
|
||||
[classes.underline]: underline,
|
||||
},
|
||||
className
|
||||
);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
$modal-header-background: var(--v2-colors-white-500);
|
||||
|
||||
.root {
|
||||
background-color: $modal-header-background;
|
||||
padding: var(--v2-spacing-4);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, { FunctionComponent, RefObject } from "react";
|
||||
|
||||
import { CardCloseButton, Flex } from "coral-ui/components/v2";
|
||||
|
||||
import styles from "./ModalHeader.css";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
focusableRef: RefObject<any>;
|
||||
}
|
||||
|
||||
const ModalHeader: FunctionComponent<Props> = ({
|
||||
onClose,
|
||||
focusableRef,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Flex justifyContent="space-between" alignItems="flex-start">
|
||||
{children}
|
||||
<CardCloseButton onClick={onClose} ref={focusableRef} />
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalHeader;
|
||||
@@ -1 +1,2 @@
|
||||
export { default, ModalProps } from "./Modal";
|
||||
export { default as ModalHeader } from "./ModalHeader";
|
||||
|
||||
@@ -39,7 +39,7 @@ export { default as Label } from "./Label";
|
||||
export { ListGroup, ListGroupRow } from "./ListGroup";
|
||||
export { Marker, Count as MarkerCount } from "./Marker";
|
||||
export { default as Message, MessageIcon } from "./Message";
|
||||
export { default as Modal, ModalProps } from "./Modal";
|
||||
export { default as Modal, ModalProps, ModalHeader } from "./Modal";
|
||||
export { default as PasswordField } from "./PasswordField";
|
||||
export { default as Popover } from "./Popover";
|
||||
export { default as RadioButton } from "./RadioButton";
|
||||
|
||||
@@ -716,6 +716,7 @@ moderate-emptyQueue-approved = There are no approved comments.
|
||||
moderate-comment-edited = (edited)
|
||||
moderate-comment-inReplyTo = Reply to <Username></Username>
|
||||
moderate-comment-viewContext = View Context
|
||||
moderate-comment-viewConversation = View Conversation
|
||||
moderate-comment-rejectButton =
|
||||
.aria-label = Reject
|
||||
moderate-comment-approveButton =
|
||||
|
||||
Reference in New Issue
Block a user