diff --git a/src/core/client/admin/routes/moderate/components/EmptyMessage.css b/src/core/client/admin/routes/moderate/components/EmptyMessage.css new file mode 100644 index 000000000..692d7de87 --- /dev/null +++ b/src/core/client/admin/routes/moderate/components/EmptyMessage.css @@ -0,0 +1,5 @@ +.root { + composes: bodyCopy from "talk-ui/shared/typography.css"; + color: var(--palette-grey-dark); + text-align: center; +} diff --git a/src/core/client/admin/routes/moderate/components/EmptyMessage.tsx b/src/core/client/admin/routes/moderate/components/EmptyMessage.tsx new file mode 100644 index 000000000..44799d0fb --- /dev/null +++ b/src/core/client/admin/routes/moderate/components/EmptyMessage.tsx @@ -0,0 +1,15 @@ +import cn from "classnames"; +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { Card } from "talk-ui/components"; + +import styles from "./EmptyMessage.css"; + +interface Props extends PropTypesOf {} + +const EmptyMessage: StatelessComponent = props => ( + +); + +export default EmptyMessage; diff --git a/src/core/client/admin/routes/moderate/components/Queue.tsx b/src/core/client/admin/routes/moderate/components/Queue.tsx index 9b4dd25fe..3c7f3c9f1 100644 --- a/src/core/client/admin/routes/moderate/components/Queue.tsx +++ b/src/core/client/admin/routes/moderate/components/Queue.tsx @@ -18,6 +18,7 @@ interface Props { hasMore: boolean; disableLoadMore: boolean; danglingLogic: PropTypesOf["danglingLogic"]; + emptyElement?: React.ReactElement; } const Queue: StatelessComponent = ({ @@ -27,6 +28,7 @@ const Queue: StatelessComponent = ({ disableLoadMore, onLoadMore, danglingLogic, + emptyElement, }) => ( @@ -56,6 +58,7 @@ const Queue: StatelessComponent = ({ /> )} + {comments.length === 0 && emptyElement} ); diff --git a/src/core/client/admin/routes/moderate/containers/QueueContainer.tsx b/src/core/client/admin/routes/moderate/containers/QueueContainer.tsx index 524684e5f..4f186a4e7 100644 --- a/src/core/client/admin/routes/moderate/containers/QueueContainer.tsx +++ b/src/core/client/admin/routes/moderate/containers/QueueContainer.tsx @@ -1,3 +1,4 @@ +import { Localized } from "fluent-react/compat"; import { RouteProps } from "found"; import React from "react"; import { graphql, GraphQLTaggedNode, RelayPaginationProp } from "react-relay"; @@ -8,6 +9,7 @@ import { QueueContainerPaginationPendingQueryVariables } from "talk-admin/__gene import { IntersectionProvider } from "talk-framework/lib/intersection"; import { withPaginationContainer } from "talk-framework/lib/relay"; +import EmptyMessage from "../components/EmptyMessage"; import LoadingQueue from "../components/LoadingQueue"; import Queue from "../components/Queue"; @@ -15,6 +17,7 @@ interface QueueContainerProps { queue: QueueData; settings: SettingsData; relay: RelayPaginationProp; + emptyElement: React.ReactElement; } // TODO: use generated types @@ -39,6 +42,7 @@ export class QueueContainer extends React.Component { hasMore={this.props.relay.hasMore()} disableLoadMore={this.state.disableLoadMore} danglingLogic={danglingLogic} + emptyElement={this.props.emptyElement} /> ); @@ -67,7 +71,8 @@ type FragmentVariables = QueueContainerPaginationPendingQueryVariables; const createQueueContainer = ( queueQuery: GraphQLTaggedNode, - paginationQuery: GraphQLTaggedNode + paginationQuery: GraphQLTaggedNode, + emptyElement: React.ReactElement ) => { const enhanced = (withPaginationContainer< QueueContainerProps, @@ -131,7 +136,13 @@ const createQueueContainer = ( if (Component && props) { const queue = anyProps.moderationQueues[Object.keys(anyProps.moderationQueues)[0]]; - return ; + return ( + + ); } return ; }, @@ -167,7 +178,13 @@ export const PendingQueueContainer = createQueueContainer( } } } - ` + `, + // tslint:disable-next-line:jsx-wrap-multiline + + + Nicely done! There are no more pending comments to moderate. + + ); export const ReportedQueueContainer = createQueueContainer( @@ -197,7 +214,13 @@ export const ReportedQueueContainer = createQueueContainer( } } } - ` + `, + // tslint:disable-next-line:jsx-wrap-multiline + + + Nicely done! There are no more reported comments to moderate. + + ); export const UnmoderatedQueueContainer = createQueueContainer( @@ -227,5 +250,9 @@ export const UnmoderatedQueueContainer = createQueueContainer( } } } - ` + `, + // tslint:disable-next-line:jsx-wrap-multiline + + Nicely done! All comments have been moderated. + ); diff --git a/src/core/client/admin/routes/moderate/containers/RejectedQueueContainer.tsx b/src/core/client/admin/routes/moderate/containers/RejectedQueueContainer.tsx index af46ecc3a..fac1f699e 100644 --- a/src/core/client/admin/routes/moderate/containers/RejectedQueueContainer.tsx +++ b/src/core/client/admin/routes/moderate/containers/RejectedQueueContainer.tsx @@ -1,3 +1,4 @@ +import { Localized } from "fluent-react/compat"; import { RouteProps } from "found"; import React from "react"; import { graphql, RelayPaginationProp } from "react-relay"; @@ -7,6 +8,7 @@ import { RejectedQueueContainerPaginationQueryVariables } from "talk-admin/__gen import { IntersectionProvider } from "talk-framework/lib/intersection"; import { withPaginationContainer } from "talk-framework/lib/relay"; +import EmptyMessage from "../components/EmptyMessage"; import LoadingQueue from "../components/LoadingQueue"; import Queue from "../components/Queue"; @@ -38,7 +40,12 @@ export class RejectedQueueContainer extends React.Component< hasMore={this.props.relay.hasMore()} disableLoadMore={this.state.disableLoadMore} danglingLogic={danglingLogic} - />{" "} + emptyElement={ + + There are no rejected comments. + + } + /> ); } diff --git a/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap b/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap index 73d338419..1d53451f2 100644 --- a/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap +++ b/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap @@ -574,7 +574,6 @@ exports[`rejected queue renders rejected queue with comments 1`] = ` - `; @@ -1057,7 +1056,13 @@ exports[`reported queue renders empty reported queue 1`] = ` >
+ > +
+ Nicely done! There are no more reported comments to moderate. +
+
`; diff --git a/src/core/client/admin/test/moderate/moderate.spec.tsx b/src/core/client/admin/test/moderate/moderate.spec.tsx index 5fb3ed5fd..d60f74abc 100644 --- a/src/core/client/admin/test/moderate/moderate.spec.tsx +++ b/src/core/client/admin/test/moderate/moderate.spec.tsx @@ -45,18 +45,8 @@ async function createTestRenderer( Query: { settings: () => settings, viewer: () => viewer, - moderationQueues: ({ variables }) => { - expectAndFail(variables).toEqual({ - storyID: null, - }); - return emptyModerationQueues; - }, - comments: ({ variables }) => { - expectAndFail(variables).toEqual({ - storyID: null, - }); - return emptyRejectedComments; - }, + moderationQueues: () => emptyModerationQueues, + comments: () => emptyRejectedComments, }, }), params.resolvers @@ -89,6 +79,31 @@ describe("reported queue", () => { expect(toJSON(getByTestID("moderate-main-container"))).toMatchSnapshot(); }); + it("renders empty pending queue", async () => { + replaceHistoryLocation("http://localhost/admin/moderate/pending"); + const testRenderer = await createTestRenderer(); + const { getByText } = within(testRenderer.root); + await waitForElement(() => getByText("no more pending", { exact: false })); + }); + + it("renders empty unmoderated queue", async () => { + replaceHistoryLocation("http://localhost/admin/moderate/unmoderated"); + const testRenderer = await createTestRenderer(); + const { getByText } = within(testRenderer.root); + await waitForElement(() => + getByText("comments have been moderated", { exact: false }) + ); + }); + + it("renders empty rejected queue", async () => { + replaceHistoryLocation("http://localhost/admin/moderate/rejected"); + const testRenderer = await createTestRenderer(); + const { getByText } = within(testRenderer.root); + await waitForElement(() => + getByText("no rejected comments", { exact: false }) + ); + }); + it("renders reported queue with comments", async () => { const testRenderer = await createTestRenderer({ resolvers: createResolversStub({ diff --git a/src/core/client/ui/components/Card/Card.tsx b/src/core/client/ui/components/Card/Card.tsx index 955469c83..18c7b2e39 100644 --- a/src/core/client/ui/components/Card/Card.tsx +++ b/src/core/client/ui/components/Card/Card.tsx @@ -5,7 +5,7 @@ import { withStyles } from "talk-ui/hocs"; import styles from "./Card.css"; -export interface CardProps { +export interface CardProps extends React.HTMLAttributes { /** * The content of the component. */ diff --git a/src/locales/en-US/admin.ftl b/src/locales/en-US/admin.ftl index 7c142856b..1c0d8a4d4 100644 --- a/src/locales/en-US/admin.ftl +++ b/src/locales/en-US/admin.ftl @@ -319,6 +319,11 @@ moderate-marker-karma = Karma moderate-marker-bodyCount = Body Count moderate-marker-offensive = Offensive +moderate-emptyQueue-pending = Nicely done! There are no more pending comments to moderate. +moderate-emptyQueue-reported = Nicely done! There are no more reported comments to moderate. +moderate-emptyQueue-unmoderated = Nicely done! All comments have been moderated. +moderate-emptyQueue-rejected = There are no rejected comments. + moderate-inReplyTo = Reply to moderate-viewContext = View Context moderate-rejectButton =