[CORL-249] Mod queues empty states (#2289)

* feat: mod queues empty states

* fix: test
This commit is contained in:
Kiwi
2019-04-26 22:39:46 +00:00
committed by Wyatt Johnson
parent ab938985e4
commit b1efdad981
9 changed files with 103 additions and 21 deletions
@@ -0,0 +1,5 @@
.root {
composes: bodyCopy from "talk-ui/shared/typography.css";
color: var(--palette-grey-dark);
text-align: center;
}
@@ -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<typeof Card> {}
const EmptyMessage: StatelessComponent<Props> = props => (
<Card {...props} className={cn(props.className, styles.root)} />
);
export default EmptyMessage;
@@ -18,6 +18,7 @@ interface Props {
hasMore: boolean;
disableLoadMore: boolean;
danglingLogic: PropTypesOf<typeof ModerateCardContainer>["danglingLogic"];
emptyElement?: React.ReactElement;
}
const Queue: StatelessComponent<Props> = ({
@@ -27,6 +28,7 @@ const Queue: StatelessComponent<Props> = ({
disableLoadMore,
onLoadMore,
danglingLogic,
emptyElement,
}) => (
<HorizontalGutter className={styles.root} size="double">
<TransitionGroup component={null} appear={false} enter={false} exit>
@@ -56,6 +58,7 @@ const Queue: StatelessComponent<Props> = ({
/>
</Flex>
)}
{comments.length === 0 && emptyElement}
</HorizontalGutter>
);
@@ -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<QueueContainerProps> {
hasMore={this.props.relay.hasMore()}
disableLoadMore={this.state.disableLoadMore}
danglingLogic={danglingLogic}
emptyElement={this.props.emptyElement}
/>
</IntersectionProvider>
);
@@ -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 <Component queue={queue} settings={anyProps.settings} />;
return (
<Component
queue={queue}
settings={anyProps.settings}
emptyElement={emptyElement}
/>
);
}
return <LoadingQueue />;
},
@@ -167,7 +178,13 @@ export const PendingQueueContainer = createQueueContainer(
}
}
}
`
`,
// tslint:disable-next-line:jsx-wrap-multiline
<Localized id="moderate-emptyQueue-pending">
<EmptyMessage>
Nicely done! There are no more pending comments to moderate.
</EmptyMessage>
</Localized>
);
export const ReportedQueueContainer = createQueueContainer(
@@ -197,7 +214,13 @@ export const ReportedQueueContainer = createQueueContainer(
}
}
}
`
`,
// tslint:disable-next-line:jsx-wrap-multiline
<Localized id="moderate-emptyQueue-reported">
<EmptyMessage>
Nicely done! There are no more reported comments to moderate.
</EmptyMessage>
</Localized>
);
export const UnmoderatedQueueContainer = createQueueContainer(
@@ -227,5 +250,9 @@ export const UnmoderatedQueueContainer = createQueueContainer(
}
}
}
`
`,
// tslint:disable-next-line:jsx-wrap-multiline
<Localized id="moderate-emptyQueue-unmoderated">
<EmptyMessage>Nicely done! All comments have been moderated.</EmptyMessage>
</Localized>
);
@@ -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={
<Localized id="moderate-emptyQueue-rejected">
<EmptyMessage>There are no rejected comments.</EmptyMessage>
</Localized>
}
/>
</IntersectionProvider>
);
}
@@ -574,7 +574,6 @@ exports[`rejected queue renders rejected queue with comments 1`] = `
</div>
</div>
</div>
</main>
</div>
`;
@@ -1057,7 +1056,13 @@ exports[`reported queue renders empty reported queue 1`] = `
>
<div
className="HorizontalGutter-root Queue-root HorizontalGutter-double"
/>
>
<div
className="Card-root EmptyMessage-root"
>
Nicely done! There are no more reported comments to moderate.
</div>
</div>
</main>
</div>
`;
@@ -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<GQLResolver>({
+1 -1
View File
@@ -5,7 +5,7 @@ import { withStyles } from "talk-ui/hocs";
import styles from "./Card.css";
export interface CardProps {
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* The content of the component.
*/
+5
View File
@@ -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 <username><username>
moderate-viewContext = View Context
moderate-rejectButton =