diff --git a/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx b/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx index a9426767f..2d1acf433 100644 --- a/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx +++ b/src/core/client/admin/routes/configure/sections/general/components/ClosedStreamMessageConfig.tsx @@ -2,6 +2,7 @@ import { Localized } from "fluent-react/compat"; import React, { StatelessComponent, Suspense } from "react"; import { Field } from "react-final-form"; +import { MarkdownEditor } from "talk-framework/components/loadables"; import { HorizontalGutter, Spinner, @@ -10,7 +11,6 @@ import { } from "talk-ui/components"; import Header from "../../../components/Header"; -import LazyMarkdown from "./LazyMarkdown"; interface Props { disabled: boolean; @@ -39,7 +39,7 @@ const ClosedStreamMessageConfig: StatelessComponent = ({ disabled }) => ( {({ input, meta }) => ( <> }> - = ({ disabled }) => ( {({ input, meta }) => ( <> }> - = ({ disabled }) => ( {({ input, meta }) => ( <> }> - { + const props: PropTypesOf = { + className: "custom", + children: "Hello World", + }; + const renderer = TestRenderer.create(); + expect(renderer.toJSON()).toMatchSnapshot(); +}); + +it("renders icon", () => { + const renderer = TestRenderer.create( + + alertAlert MessageBox + + ); + expect(renderer.toJSON()).toMatchSnapshot(); +}); diff --git a/src/core/client/stream/components/MessageBox/MessageBox.tsx b/src/core/client/stream/components/MessageBox/MessageBox.tsx new file mode 100644 index 000000000..b72a34268 --- /dev/null +++ b/src/core/client/stream/components/MessageBox/MessageBox.tsx @@ -0,0 +1,35 @@ +import cn from "classnames"; +import React, { ReactNode, StatelessComponent } from "react"; +import { withStyles } from "talk-ui/hocs"; + +import styles from "./MessageBox.css"; + +interface Props { + /** + * The content of the component. + */ + children: ReactNode; + /** + * Convenient prop to override the root styling. + */ + className?: string; + /** + * Override or extend the styles applied to the component. + */ + classes: typeof styles; +} + +const MessageBox: StatelessComponent = props => { + const { className, classes, children, ...rest } = props; + + const rootClassName = cn(classes.root, className); + + return ( +
+ {children} +
+ ); +}; + +const enhanced = withStyles(styles)(MessageBox); +export default enhanced; diff --git a/src/core/client/stream/components/MessageBox/MessageBoxContent.css b/src/core/client/stream/components/MessageBox/MessageBoxContent.css new file mode 100644 index 000000000..60ef6f68d --- /dev/null +++ b/src/core/client/stream/components/MessageBox/MessageBoxContent.css @@ -0,0 +1,4 @@ +.root { + composes: root from "talk-stream/shared/htmlContent.css"; + color: var(--palette-text-light); +} diff --git a/src/core/client/stream/components/MessageBox/MessageBoxContent.tsx b/src/core/client/stream/components/MessageBox/MessageBoxContent.tsx new file mode 100644 index 000000000..43c91ff38 --- /dev/null +++ b/src/core/client/stream/components/MessageBox/MessageBoxContent.tsx @@ -0,0 +1,37 @@ +import cn from "classnames"; +import React, { StatelessComponent } from "react"; + +import { Markdown } from "talk-framework/components"; +import { withStyles } from "talk-ui/hocs"; + +import styles from "./MessageBoxContent.css"; + +interface Props { + /** + * The content of the component. + */ + children: string; + /** + * Convenient prop to override the root styling. + */ + className?: string; + /** + * Override or extend the styles applied to the component. + */ + classes: typeof styles; +} + +const MessageBox: StatelessComponent = props => { + const { className, classes, children, ...rest } = props; + + const rootClassName = cn(classes.root, className); + + return ( + + {children} + + ); +}; + +const enhanced = withStyles(styles)(MessageBox); +export default enhanced; diff --git a/src/core/client/stream/components/MessageBox/MessageBoxIcon.css b/src/core/client/stream/components/MessageBox/MessageBoxIcon.css new file mode 100644 index 000000000..a8b8502da --- /dev/null +++ b/src/core/client/stream/components/MessageBox/MessageBoxIcon.css @@ -0,0 +1,9 @@ +.root { + align-self: flex-start; + margin-top: 1px; + flex-shrink: 0; + color: var(--palette-text-light); + &:first-child { + margin-right: calc(0.5 * var(--spacing-unit)); + } +} diff --git a/src/core/client/stream/components/MessageBox/MessageBoxIcon.tsx b/src/core/client/stream/components/MessageBox/MessageBoxIcon.tsx new file mode 100644 index 000000000..bb5a91f55 --- /dev/null +++ b/src/core/client/stream/components/MessageBox/MessageBoxIcon.tsx @@ -0,0 +1,37 @@ +import cn from "classnames"; +import React, { HTMLAttributes, Ref, StatelessComponent } from "react"; + +import Icon, { IconProps } from "talk-ui/components/Icon"; +import { withForwardRef, withStyles } from "talk-ui/hocs"; +import { Omit } from "talk-ui/types"; + +import styles from "./MessageBoxIcon.css"; + +interface Props extends Omit, "color"> { + /** + * This prop can be used to add custom classnames. + * It is handled by the `withStyles `HOC. + */ + classes: typeof styles & IconProps["classes"]; + + size?: IconProps["size"]; + + /** The name of the icon to render */ + children: string; + + /** Internal: Forwarded Ref */ + forwardRef?: Ref; +} + +export const MessageBoxIcon: StatelessComponent = props => { + const { classes, className, forwardRef, ...rest } = props; + const rootClassName = cn(classes.root, className); + return ; +}; + +MessageBoxIcon.defaultProps = { + size: "md", +}; + +const enhanced = withForwardRef(withStyles(styles)(MessageBoxIcon)); +export default enhanced; diff --git a/src/core/client/stream/components/MessageBox/__snapshots__/MessageBox.spec.tsx.snap b/src/core/client/stream/components/MessageBox/__snapshots__/MessageBox.spec.tsx.snap new file mode 100644 index 000000000..c31491163 --- /dev/null +++ b/src/core/client/stream/components/MessageBox/__snapshots__/MessageBox.spec.tsx.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+ Hello World +
+`; + +exports[`renders icon 1`] = ` +
+ + Alert MessageBox +
+`; diff --git a/src/core/client/stream/components/MessageBox/index.ts b/src/core/client/stream/components/MessageBox/index.ts new file mode 100644 index 000000000..958ef0612 --- /dev/null +++ b/src/core/client/stream/components/MessageBox/index.ts @@ -0,0 +1,3 @@ +export { default, default as MessageBox } from "./MessageBox"; +export { default as MessageBoxIcon } from "./MessageBoxIcon"; +export { default as MessageBoxContent } from "./MessageBoxContent"; diff --git a/src/core/client/stream/tabs/comments/components/PostCommentForm.css b/src/core/client/stream/tabs/comments/components/PostCommentForm.css index 59669f339..d31a783e1 100644 --- a/src/core/client/stream/tabs/comments/components/PostCommentForm.css +++ b/src/core/client/stream/tabs/comments/components/PostCommentForm.css @@ -1,4 +1,5 @@ -.root { +.messageBox { + margin-bottom: -1px; } .poweredBy { diff --git a/src/core/client/stream/tabs/comments/components/PostCommentForm.tsx b/src/core/client/stream/tabs/comments/components/PostCommentForm.tsx index 3644be6d0..47641014f 100644 --- a/src/core/client/stream/tabs/comments/components/PostCommentForm.tsx +++ b/src/core/client/stream/tabs/comments/components/PostCommentForm.tsx @@ -8,18 +8,19 @@ import ValidationMessage from "talk-admin/routes/configure/components/Validation import { OnSubmit } from "talk-framework/lib/form"; import { AriaInfo, Button, Flex, HorizontalGutter } from "talk-ui/components"; +import MessageBoxContainer from "../containers/MessageBoxContainer"; +import PostCommentSubmitStatusContainer from "../containers/PostCommentSubmitStatusContainer"; import RemainingCharactersContainer from "../containers/RemainingCharactersContainer"; import { cleanupRTEEmptyHTML, getCommentBodyValidators } from "../helpers"; import RTE from "./RTE"; -import PostCommentSubmitStatusContainer from "../containers/PostCommentSubmitStatusContainer"; import styles from "./PostCommentForm.css"; interface FormProps { body: string; } -export interface PostCommentFormProps { +interface Props { onSubmit: OnSubmit; onChange?: (state: FormState, form: FormApi) => void; initialValues?: FormProps; @@ -28,103 +29,110 @@ export interface PostCommentFormProps { disabled?: boolean; disabledMessage?: React.ReactNode; submitStatus: PropTypesOf["status"]; + showMessageBox?: boolean; + story: PropTypesOf["story"]; } -const PostCommentForm: StatelessComponent = props => ( -
- {({ handleSubmit, submitting, submitError, form }) => ( - - props.onChange && props.onChange(state, form)} - /> - - - {({ input, meta }) => ( - <> - - - = props => ( +
+ {props.showMessageBox && ( + + )} + + {({ handleSubmit, submitting, submitError, form }) => ( + + props.onChange && props.onChange(state, form)} + /> + + + {({ input, meta }) => ( + <> + + + + Post a comment + + + - Post a comment - - - - - input.onChange(cleanupRTEEmptyHTML(html)) - } - value={input.value} - placeholder="Post a comment" - disabled={submitting || props.disabled} - /> - - {props.disabled ? ( - <> - {props.disabledMessage && ( - - {props.disabledMessage} - - )} - - ) : ( - <> - {meta.touched && - (meta.error || - (meta.submitError && !meta.dirtySinceLastSubmit)) && ( + + input.onChange(cleanupRTEEmptyHTML(html)) + } + value={input.value} + placeholder="Post a comment" + disabled={submitting || props.disabled} + /> + + {props.disabled ? ( + <> + {props.disabledMessage && ( - {meta.error || meta.submitError} + {props.disabledMessage} )} - {submitError && ( - - {submitError} - - )} - - {props.max && ( - + ) : ( + <> + {meta.touched && + (meta.error || + (meta.submitError && + !meta.dirtySinceLastSubmit)) && ( + + {meta.error || meta.submitError} + + )} + {submitError && ( + + {submitError} + + )} + - )} - - )} - - - - - - - - )} - - - - )} - + {props.max && ( + + )} + + )} + + + + + + + + )} + + + + )} + +
); export default PostCommentForm; diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormClosed.css b/src/core/client/stream/tabs/comments/components/PostCommentFormClosed.css new file mode 100644 index 000000000..904a3399a --- /dev/null +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormClosed.css @@ -0,0 +1,4 @@ +.messageBox { + margin-bottom: -1px; + z-index: 1; +} diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormClosed.tsx b/src/core/client/stream/tabs/comments/components/PostCommentFormClosed.tsx new file mode 100644 index 000000000..8ba9edb59 --- /dev/null +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormClosed.tsx @@ -0,0 +1,24 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { CallOut } from "talk-ui/components"; + +import MessageBoxContainer from "../containers/MessageBoxContainer"; + +import styles from "./PostCommentFormClosed.css"; + +interface Props { + showMessageBox?: boolean; + story: PropTypesOf["story"]; + children?: React.ReactNode; +} +const PostCommentFormClosed: StatelessComponent = props => ( +
+ {props.showMessageBox && ( + + )} + {props.children} +
+); + +export default PostCommentFormClosed; diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.css b/src/core/client/stream/tabs/comments/components/PostCommentFormClosedSitewide.css similarity index 89% rename from src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.css rename to src/core/client/stream/tabs/comments/components/PostCommentFormClosedSitewide.css index 025797a60..ccf863f53 100644 --- a/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.css +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormClosedSitewide.css @@ -1,7 +1,4 @@ .root { -} - -.sitewide { background-color: var(--palette-grey-dark); border-color: var(--palette-grey-dark); color: var(--palette-text-light); diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormClosedSitewide.tsx b/src/core/client/stream/tabs/comments/components/PostCommentFormClosedSitewide.tsx new file mode 100644 index 000000000..db6215522 --- /dev/null +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormClosedSitewide.tsx @@ -0,0 +1,24 @@ +import React, { StatelessComponent } from "react"; + +import { PropTypesOf } from "talk-framework/types"; +import { CallOut, HorizontalGutter } from "talk-ui/components"; + +import MessageBoxContainer from "../containers/MessageBoxContainer"; + +import styles from "./PostCommentFormClosedSitewide.css"; + +interface Props { + showMessageBox?: boolean; + story: PropTypesOf["story"]; + children?: React.ReactNode; +} +const PostCommentFormClosedSitewide: StatelessComponent = props => ( + + + {props.children} + + {props.showMessageBox && } + +); + +export default PostCommentFormClosedSitewide; diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.spec.tsx b/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.spec.tsx deleted file mode 100644 index 5ad920e2f..000000000 --- a/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.spec.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from "react"; -import { createRenderer } from "react-test-renderer/shallow"; -import { PropTypesOf } from "talk-framework/types"; - -import PostCommentFormCollapsed from "./PostCommentFormCollapsed"; - -it("renders correctly when comments are closed sitewide", () => { - const props: PropTypesOf = { - closedSitewide: true, - closedMessage: "closed site-wide", - }; - const renderer = createRenderer(); - renderer.render(); - expect(renderer.getRenderOutput()).toMatchSnapshot(); -}); - -it("renders correctly when story is closed", () => { - const props: PropTypesOf = { - closedSitewide: false, - closedMessage: "closed story", - }; - const renderer = createRenderer(); - renderer.render(); - expect(renderer.getRenderOutput()).toMatchSnapshot(); -}); diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.tsx b/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.tsx deleted file mode 100644 index 1e110f150..000000000 --- a/src/core/client/stream/tabs/comments/components/PostCommentFormCollapsed.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import cn from "classnames"; -import React, { StatelessComponent } from "react"; - -import { CallOut } from "talk-ui/components"; - -import styles from "./PostCommentFormCollapsed.css"; - -interface Props { - closedSitewide?: boolean; - closedMessage?: React.ReactNode; -} -const PostCommentFormCollapsed: StatelessComponent = props => ( - - {props.closedMessage} - -); - -export default PostCommentFormCollapsed; diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormFake.css b/src/core/client/stream/tabs/comments/components/PostCommentFormFake.css index 775481fb4..5af67eb0a 100644 --- a/src/core/client/stream/tabs/comments/components/PostCommentFormFake.css +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormFake.css @@ -1,6 +1,10 @@ .root { } +.messageBox { + margin-bottom: -1px; +} + .textarea { composes: bodyCopy from "talk-ui/shared/typography.css"; display: block; diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormFake.spec.tsx b/src/core/client/stream/tabs/comments/components/PostCommentFormFake.spec.tsx index ba8362f0c..0db20574c 100644 --- a/src/core/client/stream/tabs/comments/components/PostCommentFormFake.spec.tsx +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormFake.spec.tsx @@ -1,10 +1,13 @@ import React from "react"; import { createRenderer } from "react-test-renderer/shallow"; +import { removeFragmentRefs } from "talk-framework/testHelpers"; import PostCommentFormFake from "./PostCommentFormFake"; +const PostCommentFormFakeN = removeFragmentRefs(PostCommentFormFake); + it("renders correctly", () => { const renderer = createRenderer(); - renderer.render(); + renderer.render(); expect(renderer.getRenderOutput()).toMatchSnapshot(); }); diff --git a/src/core/client/stream/tabs/comments/components/PostCommentFormFake.tsx b/src/core/client/stream/tabs/comments/components/PostCommentFormFake.tsx index 50a6f6f9d..5655fb7cc 100644 --- a/src/core/client/stream/tabs/comments/components/PostCommentFormFake.tsx +++ b/src/core/client/stream/tabs/comments/components/PostCommentFormFake.tsx @@ -1,28 +1,46 @@ import { Localized } from "fluent-react/compat"; import React, { StatelessComponent } from "react"; +import { PropTypesOf } from "talk-framework/types"; import { Button, HorizontalGutter } from "talk-ui/components"; +import MessageBoxContainer from "../containers/MessageBoxContainer"; import RTE from "./RTE"; import styles from "./PostCommentFormFake.css"; -const PostCommentFormFake: StatelessComponent = props => ( - - ); export default PostCommentFormFake; diff --git a/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormCollapsed.spec.tsx.snap b/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormCollapsed.spec.tsx.snap deleted file mode 100644 index cc0e63412..000000000 --- a/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormCollapsed.spec.tsx.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly when comments are closed sitewide 1`] = ` - - closed site-wide - -`; - -exports[`renders correctly when story is closed 1`] = ` - - closed story - -`; diff --git a/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormFake.spec.tsx.snap b/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormFake.spec.tsx.snap index edf908b82..bd2a37213 100644 --- a/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormFake.spec.tsx.snap +++ b/src/core/client/stream/tabs/comments/components/__snapshots__/PostCommentFormFake.spec.tsx.snap @@ -1,38 +1,44 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders correctly 1`] = ` - - + - + fullWidth={true} + type="submit" + variant="filled" + > + Sign in and join the conversation +
- - - - Sign in and join the conversation - - - + + `; diff --git a/src/core/client/stream/tabs/comments/containers/MessageBoxContainer.tsx b/src/core/client/stream/tabs/comments/containers/MessageBoxContainer.tsx new file mode 100644 index 000000000..10d5006b6 --- /dev/null +++ b/src/core/client/stream/tabs/comments/containers/MessageBoxContainer.tsx @@ -0,0 +1,46 @@ +import React, { StatelessComponent } from "react"; +import { graphql } from "react-relay"; +import { withFragmentContainer } from "talk-framework/lib/relay"; + +import { MessageBoxContainer_story as StoryData } from "talk-stream/__generated__/MessageBoxContainer_story.graphql"; +import { + MessageBox, + MessageBoxContent, + MessageBoxIcon, +} from "talk-stream/components/MessageBox"; + +interface Props { + story: StoryData; + className?: string; +} + +const MessageBoxContainer: StatelessComponent = ({ + story, + className, +}) => { + return ( + + {story.settings.messageBox.icon && ( + {story.settings.messageBox.icon} + )} + + {story.settings.messageBox.content || ""} + + + ); +}; + +const enhanced = withFragmentContainer({ + story: graphql` + fragment MessageBoxContainer_story on Story { + settings { + messageBox { + content + icon + } + } + } + `, +})(MessageBoxContainer); + +export default enhanced; diff --git a/src/core/client/stream/tabs/comments/containers/PermalinkButtonContainer.tsx b/src/core/client/stream/tabs/comments/containers/PermalinkButtonContainer.tsx index eb253ec4e..270da6e36 100644 --- a/src/core/client/stream/tabs/comments/containers/PermalinkButtonContainer.tsx +++ b/src/core/client/stream/tabs/comments/containers/PermalinkButtonContainer.tsx @@ -11,7 +11,7 @@ interface Props { commentID: string; } -export const PermalinkButtonContainerProps: StatelessComponent = ({ +export const PermalinkButtonContainer: StatelessComponent = ({ story, commentID, }) => { @@ -29,6 +29,6 @@ const enhanced = withFragmentContainer({ url } `, -})(PermalinkButtonContainerProps); +})(PermalinkButtonContainer); export default enhanced; diff --git a/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.spec.tsx b/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.spec.tsx index bf75f2fd5..6206b5f7b 100644 --- a/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.spec.tsx +++ b/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.spec.tsx @@ -27,6 +27,11 @@ function createDefaultProps(add: DeepPartial = {}): Props { story: { id: "story-id", isClosed: false, + settings: { + messageBox: { + enabled: false, + }, + }, }, sessionStorage: createPromisifiedStorage(), settings: { diff --git a/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.tsx b/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.tsx index 895d76892..2d1b7fd12 100644 --- a/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.tsx +++ b/src/core/client/stream/tabs/comments/containers/PostCommentFormContainer.tsx @@ -21,10 +21,9 @@ import { withCreateCommentMutation, } from "talk-stream/mutations"; -import PostCommentForm, { - PostCommentFormProps, -} from "../components/PostCommentForm"; -import PostCommentFormCollapsed from "../components/PostCommentFormCollapsed"; +import PostCommentForm from "../components/PostCommentForm"; +import PostCommentFormClosed from "../components/PostCommentFormClosed"; +import PostCommentFormClosedSitewide from "../components/PostCommentFormClosedSitewide"; import PostCommentFormFake from "../components/PostCommentFormFake"; import { getSubmitStatus, @@ -42,7 +41,7 @@ interface Props { } interface State { - initialValues?: PostCommentFormProps["initialValues"]; + initialValues?: PropTypesOf["initialValues"]; initialized: boolean; keepFormWhenClosed: boolean; submitStatus: SubmitStatus | null; @@ -79,10 +78,9 @@ export class PostCommentFormContainer extends Component { }); } - private handleOnSubmit: PostCommentFormProps["onSubmit"] = async ( - input, - form - ) => { + private handleOnSubmit: PropTypesOf< + typeof PostCommentForm + >["onSubmit"] = async (input, form) => { try { const submitStatus = getSubmitStatus( await this.props.createComment({ @@ -107,7 +105,10 @@ export class PostCommentFormContainer extends Component { return; }; - private handleOnChange: PostCommentFormProps["onChange"] = (state, form) => { + private handleOnChange: PropTypesOf["onChange"] = ( + state, + form + ) => { if (this.state.submitStatus && state.dirty) { this.setState({ submitStatus: null }); } @@ -126,27 +127,39 @@ export class PostCommentFormContainer extends Component { if (!this.state.initialized) { return null; } - if ( - !this.state.keepFormWhenClosed && - (this.props.settings.disableCommenting.enabled || - this.props.story.isClosed) - ) { + if (!this.state.keepFormWhenClosed) { + if (this.props.settings.disableCommenting.enabled) { + return ( + + {this.props.settings.disableCommenting.message} + + ); + } + if (this.props.story.isClosed) { + return ( + + {this.props.settings.closeCommenting.message} + + ); + } + } + if (!this.props.local.loggedIn) { return ( - ); } - if (!this.props.local.loggedIn) { - return ; - } return ( { this.props.settings.closeCommenting.message } submitStatus={this.state.submitStatus} + showMessageBox={this.props.story.settings.messageBox.enabled} /> ); } @@ -208,6 +222,12 @@ const enhanced = withContext(({ sessionStorage }) => ({ fragment PostCommentFormContainer_story on Story { id isClosed + ...MessageBoxContainer_story + settings { + messageBox { + enabled + } + } } `, })(PostCommentFormContainer) diff --git a/src/core/client/stream/tabs/comments/containers/__snapshots__/CommentContainer.spec.tsx.snap b/src/core/client/stream/tabs/comments/containers/__snapshots__/CommentContainer.spec.tsx.snap index 2f212d084..9228984b7 100644 --- a/src/core/client/stream/tabs/comments/containers/__snapshots__/CommentContainer.spec.tsx.snap +++ b/src/core/client/stream/tabs/comments/containers/__snapshots__/CommentContainer.spec.tsx.snap @@ -15,7 +15,7 @@ exports[`hide reply button 1`] = ` justifyContent="space-between" > - - - - - - - `; exports[`renders when commenting has been disabled (collapsing) 1`] = ` - + + commenting disabled + `; exports[`renders when commenting has been disabled (non-collapsing) 1`] = ` @@ -27,15 +51,39 @@ exports[`renders when commenting has been disabled (non-collapsing) 1`] = ` min={3} onChange={[Function]} onSubmit={[Function]} + showMessageBox={false} + story={ + Object { + "id": "story-id", + "isClosed": false, + "settings": Object { + "messageBox": Object { + "enabled": false, + }, + }, + } + } submitStatus={null} /> `; exports[`renders when story has been closed (collapsing) 1`] = ` - + + story closed + `; exports[`renders when story has been closed (non-collapsing) 1`] = ` @@ -46,6 +94,18 @@ exports[`renders when story has been closed (non-collapsing) 1`] = ` min={3} onChange={[Function]} onSubmit={[Function]} + showMessageBox={false} + story={ + Object { + "id": "story-id", + "isClosed": true, + "settings": Object { + "messageBox": Object { + "enabled": false, + }, + }, + } + } submitStatus={null} /> `; @@ -63,6 +123,18 @@ exports[`renders with initialValues 1`] = ` min={3} onChange={[Function]} onSubmit={[Function]} + showMessageBox={false} + story={ + Object { + "id": "story-id", + "isClosed": false, + "settings": Object { + "messageBox": Object { + "enabled": false, + }, + }, + } + } submitStatus={null} /> `; diff --git a/src/core/client/stream/tabs/configure/components/ConfigureCommentStream.tsx b/src/core/client/stream/tabs/configure/components/ConfigureCommentStream.tsx index b7cfe895d..133719221 100644 --- a/src/core/client/stream/tabs/configure/components/ConfigureCommentStream.tsx +++ b/src/core/client/stream/tabs/configure/components/ConfigureCommentStream.tsx @@ -13,6 +13,7 @@ import { Typography, } from "talk-ui/components"; +import MessageBoxConfigContainer from "../containers/MessageBoxConfigContainer"; import PremodConfigContainer from "../containers/PremodConfigContainer"; import PremodLinksConfigContainer from "../containers/PremodLinksConfigContainer"; @@ -21,7 +22,8 @@ import styles from "./ConfigureCommentStream.css"; interface Props { onSubmit: (settings: any, form: FormApi) => void; storySettings: PropTypesOf["storySettings"] & - PropTypesOf["storySettings"]; + PropTypesOf["storySettings"] & + PropTypesOf["storySettings"]; } const ConfigureCommentStream: StatelessComponent = ({ @@ -66,6 +68,11 @@ const ConfigureCommentStream: StatelessComponent = ({ storySettings={storySettings} disabled={submitting} /> +
)} diff --git a/src/core/client/stream/tabs/configure/components/MessageBoxConfig.css b/src/core/client/stream/tabs/configure/components/MessageBoxConfig.css new file mode 100644 index 000000000..b2600ca45 --- /dev/null +++ b/src/core/client/stream/tabs/configure/components/MessageBoxConfig.css @@ -0,0 +1,3 @@ +.preview { + text-transform: uppercase; +} diff --git a/src/core/client/stream/tabs/configure/components/MessageBoxConfig.tsx b/src/core/client/stream/tabs/configure/components/MessageBoxConfig.tsx new file mode 100644 index 000000000..27f610da1 --- /dev/null +++ b/src/core/client/stream/tabs/configure/components/MessageBoxConfig.tsx @@ -0,0 +1,152 @@ +import { Localized } from "fluent-react/compat"; +import React, { StatelessComponent, Suspense } from "react"; +import { Field } from "react-final-form"; + +import { MarkdownEditor } from "talk-framework/components/loadables"; +import { parseBool } from "talk-framework/lib/form"; +import { + MessageBox, + MessageBoxContent, + MessageBoxIcon, +} from "talk-stream/components/MessageBox"; +import { + HorizontalGutter, + Icon, + Spinner, + TileOption, + TileSelector, + Typography, + ValidationMessage, +} from "talk-ui/components"; + +import ToggleConfig from "./ToggleConfig"; +import WidthLimitedDescription from "./WidthLimitedDescription"; + +import styles from "./MessageBoxConfig.css"; + +interface Props { + disabled: boolean; +} + +const MessageBoxConfig: StatelessComponent = ({ disabled }) => ( + + {({ input }) => ( + + Enable Message Box for this Stream + + } + > + + + + Add a message to the top of the comment box for your readers. Use + this to pose a topic, ask a question or make announcements + relating to this story. + + + {input.checked && ( + + {({ input: iconInput }) => ( + + {({ input: contentInput, meta }) => ( + <> + + + + PREVIEW + + + + {iconInput.value && ( + {iconInput.value} + )} + {/* Using a zero width join character to ensure that the space is used */} + + {contentInput.value || " "} + + + + + + + Select an Icon + + + + + question_answer + + + today + + + help_outline + + + warning + + + chat_bubble_outline + + No Icon + + + +
+ + + } + > + Write a Message + + +
+ }> + + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} +
+ + )} +
+ )} +
+ )} +
+
+ )} +
+); + +export default MessageBoxConfig; diff --git a/src/core/client/stream/tabs/configure/containers/ConfigureCommentStreamContainer.tsx b/src/core/client/stream/tabs/configure/containers/ConfigureCommentStreamContainer.tsx index 78fd8c311..fc42e1e3a 100644 --- a/src/core/client/stream/tabs/configure/containers/ConfigureCommentStreamContainer.tsx +++ b/src/core/client/stream/tabs/configure/containers/ConfigureCommentStreamContainer.tsx @@ -50,6 +50,7 @@ const enhanced = withFragmentContainer({ settings { ...PremodConfigContainer_storySettings ...PremodLinksConfigContainer_storySettings + ...MessageBoxConfigContainer_storySettings } } `, diff --git a/src/core/client/stream/tabs/configure/containers/MessageBoxConfigContainer.tsx b/src/core/client/stream/tabs/configure/containers/MessageBoxConfigContainer.tsx new file mode 100644 index 000000000..ae4438688 --- /dev/null +++ b/src/core/client/stream/tabs/configure/containers/MessageBoxConfigContainer.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { graphql } from "react-relay"; + +import { withFragmentContainer } from "talk-framework/lib/relay"; +import { MessageBoxConfigContainer_storySettings as StorySettingsData } from "talk-stream/__generated__/MessageBoxConfigContainer_storySettings.graphql"; + +import MessageBoxConfig from "../components/MessageBoxConfig"; + +interface Props { + storySettings: StorySettingsData; + onInitValues: (values: StorySettingsData) => void; + disabled: boolean; +} + +class MessageBoxConfigContainer extends React.Component { + constructor(props: Props) { + super(props); + props.onInitValues(props.storySettings); + } + + public render() { + const { disabled } = this.props; + return ; + } +} + +const enhanced = withFragmentContainer({ + storySettings: graphql` + fragment MessageBoxConfigContainer_storySettings on StorySettings { + messageBox { + enabled + content + icon + } + } + `, +})(MessageBoxConfigContainer); + +export default enhanced; diff --git a/src/core/client/stream/test/comments/__snapshots__/renderCommunityGuidelines.spec.tsx.snap b/src/core/client/stream/test/comments/__snapshots__/renderCommunityGuidelines.spec.tsx.snap index 37b57f448..58e5504bc 100644 --- a/src/core/client/stream/test/comments/__snapshots__/renderCommunityGuidelines.spec.tsx.snap +++ b/src/core/client/stream/test/comments/__snapshots__/renderCommunityGuidelines.spec.tsx.snap @@ -52,102 +52,104 @@ exports[`renders comment stream with community guidelines 1`] = ` } /> -
+