[CORL-504] Configure reaction language (#2490)

* add reaction config to settings

* fix reaction preview styles

* update snaps and fixtures

* add i18n strings

* update spec

* ensure dynamic value shows in reaction config preview
This commit is contained in:
Tessa Thornton
2019-08-20 13:03:25 -04:00
committed by GitHub
parent 8dae0feb52
commit add5224338
10 changed files with 511 additions and 3 deletions
@@ -8,6 +8,7 @@ import ClosingCommentStreamsConfigContainer from "./ClosingCommentStreamsConfigC
import CommentEditingConfigContainer from "./CommentEditingConfigContainer";
import CommentLengthConfigContainer from "./CommentLengthConfigContainer";
import GuidelinesConfigContainer from "./GuidelinesConfigContainer";
import ReactionConfigContainer from "./ReactionConfigContainer";
import SitewideCommentingConfigContainer from "./SitewideCommentingConfigContainer";
interface Props {
@@ -16,6 +17,7 @@ interface Props {
PropTypesOf<typeof CommentLengthConfigContainer>["settings"] &
PropTypesOf<typeof CommentEditingConfigContainer>["settings"] &
PropTypesOf<typeof ClosedStreamMessageConfigContainer>["settings"] &
PropTypesOf<typeof ReactionConfigContainer>["settings"] &
PropTypesOf<typeof ClosingCommentStreamsConfigContainer>["settings"] &
PropTypesOf<typeof SitewideCommentingConfigContainer>["settings"];
onInitValues: (values: any) => void;
@@ -57,6 +59,11 @@ const General: FunctionComponent<Props> = ({
settings={settings}
onInitValues={onInitValues}
/>
<ReactionConfigContainer
disabled={disabled}
settings={settings}
onInitValues={onInitValues}
/>
</HorizontalGutter>
);
@@ -51,6 +51,7 @@ const enhanced = withFragmentContainer<Props>({
...ClosedStreamMessageConfigContainer_settings
...ClosingCommentStreamsConfigContainer_settings
...SitewideCommentingConfigContainer_settings
...ReactionConfigContainer_settings
}
`,
})(GeneralConfigContainer);
@@ -0,0 +1,8 @@
.textInput {
min-width: 300px;
}
.reactionButton {
cursor: not-allowed !important;
color: var(--palette-grey-main) !important;
}
@@ -0,0 +1,168 @@
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent } from "react";
import { Field } from "react-final-form";
import { ReactionConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ReactionConfigContainer_settings.graphql";
import { required } from "coral-framework/lib/validation";
import ReactionButton from "coral-stream/tabs/Comments/Comment/ReactionButton/ReactionButton";
import {
FieldSet,
Flex,
FormField,
HorizontalGutter,
InputLabel,
Option,
SelectField,
TextField,
Typography,
} from "coral-ui/components";
import Header from "../../Header";
import ValidationMessage from "../../ValidationMessage";
import styles from "./ReactionConfig.css";
interface Props {
disabled: boolean;
settings: SettingsData;
}
const ReactionsConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
<HorizontalGutter size="oneAndAHalf" container={<FieldSet />}>
<Localized id="configure-general-reactions-title">
<Header container="legend">Reactions</Header>
</Localized>
<Localized id="configure-general-reactions-explanation" strong={<strong />}>
<Typography variant="detail">
Allow your community to engage with one another and express themselves
with one-click reactions. By default, Coral allows commenters to
"Respect" each other's comments, but you may customize reaction text
based on the needs of your community.
</Typography>
</Localized>
<HorizontalGutter size="double">
<FormField>
<Field name="reaction.label" validate={required}>
{({ input, meta }) => (
<Flex itemGutter="double">
<HorizontalGutter>
<Localized id="configure-general-reactions-label">
<InputLabel>Reaction label</InputLabel>
</Localized>
<Localized id="configure-general-reactions-input">
<TextField
className={styles.textInput}
id={input.name}
type="text"
fullWidth
placeholder="E.g. Respect"
disabled={disabled}
{...input}
/>
</Localized>
<ValidationMessage fullWidth meta={meta} />
</HorizontalGutter>
<HorizontalGutter>
<Localized id="configure-general-reactions-preview">
<Typography variant="heading3">Preview</Typography>
</Localized>
<ReactionButton
readOnly
className={styles.reactionButton}
reacted={false}
label={input.value}
labelActive={settings.reaction.labelActive}
icon={settings.reaction.icon}
iconActive={settings.reaction.iconActive}
totalReactions={0}
onClick={() => null}
/>
</HorizontalGutter>
</Flex>
)}
</Field>
</FormField>
<FormField>
<Field name="reaction.labelActive" validate={required}>
{({ input, meta }) => (
<Flex itemGutter="double">
<HorizontalGutter>
<Localized id="configure-general-reactions-active-label">
<InputLabel>Active reaction label</InputLabel>
</Localized>
<Localized id="configure-general-reactions-active-input">
<TextField
className={styles.textInput}
id={input.name}
type="text"
placeholder="E.g. Respected"
fullWidth
disabled={disabled}
{...input}
/>
</Localized>
<ValidationMessage fullWidth meta={meta} />
</HorizontalGutter>
<HorizontalGutter>
<Localized id="configure-general-reactions-preview">
<Typography variant="heading3">Preview</Typography>
</Localized>
<ReactionButton
className={styles.reactionButton}
readOnly
reacted
label={settings.reaction.label}
labelActive={input.value}
icon={settings.reaction.icon}
iconActive={settings.reaction.iconActive}
totalReactions={0}
onClick={() => null}
/>
</HorizontalGutter>
</Flex>
)}
</Field>
</FormField>
<FormField>
<Field name="reaction.sortLabel" validate={required}>
{({ input, meta }) => (
<Flex itemGutter="double">
<HorizontalGutter>
<Localized id="configure-general-reactions-sort-label">
<InputLabel>Sort label</InputLabel>
</Localized>
<Localized id="configure-general-reactions-sort-input">
<TextField
id={input.name}
className={styles.textInput}
type="text"
placeholder="E.g. Most respected"
fullWidth
disabled={disabled}
{...input}
/>
</Localized>
<ValidationMessage fullWidth meta={meta} />
</HorizontalGutter>
<HorizontalGutter>
<Localized id="configure-general-reactions-preview">
<Typography variant="heading3">Preview</Typography>
</Localized>
<Flex justifyContent="center" itemGutter>
<Localized id="configure-general-reaction-sortMenu-sortBy">
<Typography variant="bodyCopyBold">Sort By</Typography>
</Localized>
<SelectField>
<Option value={input.value}>{input.value}</Option>{" "}
</SelectField>
</Flex>
</HorizontalGutter>
</Flex>
)}
</Field>
</FormField>
</HorizontalGutter>
</HorizontalGutter>
);
export default ReactionsConfig;
@@ -0,0 +1,41 @@
import React from "react";
import { graphql } from "react-relay";
import { ReactionConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ReactionConfigContainer_settings.graphql";
import { withFragmentContainer } from "coral-framework/lib/relay";
import ReactionConfig from "./ReactionConfig";
interface Props {
settings: SettingsData;
onInitValues: (values: SettingsData) => void;
disabled: boolean;
}
class ReactionConfigContainer extends React.Component<Props> {
constructor(props: Props) {
super(props);
props.onInitValues(props.settings);
}
public render() {
const { disabled, settings } = this.props;
return <ReactionConfig settings={settings} disabled={disabled} />;
}
}
const enhanced = withFragmentContainer<Props>({
settings: graphql`
fragment ReactionConfigContainer_settings on Settings {
reaction {
label
labelActive
sortLabel
icon
iconActive
}
}
`,
})(ReactionConfigContainer);
export default enhanced;
@@ -737,6 +737,219 @@ moderation panel.
/>
</div>
</div>
<fieldset
className="FieldSet-root Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
>
<legend
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary Header-root"
>
Reactions
</legend>
<p
className="Box-root Typography-root Typography-detail Typography-colorTextPrimary"
>
Allow your community to engage with one another and express themselves
with one-click reactions. By default, Coral allows commenters to
"Respect" each other's comments, but you may customize reaction text
based on the needs of your community.
</p>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<div
className="Box-root Flex-root Flex-flex Flex-doubleItemGutter gutter"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Reaction label
</label>
<div
className="TextField-root TextField-fullWidth ReactionConfig-textInput"
>
<input
className="TextField-input TextField-colorRegular"
disabled={false}
id="reaction.label"
name="reaction.label"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="E.g. Respect"
type="text"
value="Reaction"
/>
</div>
</div>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary"
>
Preview
</h1>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost Button-disabled ReactionButton-readOnly ReactionConfig-reactionButton"
disabled={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
Reaction
</span>
</button>
</div>
</div>
</div>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<div
className="Box-root Flex-root Flex-flex Flex-doubleItemGutter gutter"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Active reaction label
</label>
<div
className="TextField-root TextField-fullWidth ReactionConfig-textInput"
>
<input
className="TextField-input TextField-colorRegular"
disabled={false}
id="reaction.labelActive"
name="reaction.labelActive"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="E.g. Respected"
type="text"
value="reacted"
/>
</div>
</div>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary"
>
Preview
</h1>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorRegular Button-variantGhost Button-disabled ReactionButton-readOnly ReactionConfig-reactionButton"
disabled={true}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<span>
reacted
</span>
</button>
</div>
</div>
</div>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<div
className="Box-root Flex-root Flex-flex Flex-doubleItemGutter gutter"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Sort label
</label>
<div
className="TextField-root TextField-fullWidth ReactionConfig-textInput"
>
<input
className="TextField-input TextField-colorRegular"
disabled={false}
id="reaction.sortLabel"
name="reaction.sortLabel"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="E.g. Most respected"
type="text"
value="Reactions"
/>
</div>
</div>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary"
>
Preview
</h1>
<div
className="Box-root Flex-root Flex-flex Flex-itemGutter Flex-justifyCenter gutter"
>
<p
className="Box-root Typography-root Typography-bodyCopyBold Typography-colorTextPrimary"
>
Sort by
</p>
<span
className="SelectField-root"
>
<select
className="SelectField-select"
onBlur={[Function]}
onFocus={[Function]}
>
<option
value="Reactions"
>
Reactions
</option>
</select>
<span
aria-hidden={true}
className="SelectField-afterWrapper"
>
<span
aria-hidden="true"
className="Icon-root Icon-sm"
>
expand_more
</span>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
+6
View File
@@ -46,6 +46,12 @@ export const settings = createFixture<GQLSettings>({
timeout: 604800,
message: "Comments are closed on this story.",
},
reaction: {
label: "Reaction",
labelActive: "reacted",
sortLabel: "Reactions",
icon: "icon",
},
email: {
enabled: true,
},
@@ -2,6 +2,7 @@ import cn from "classnames";
import React from "react";
import { Button, ButtonIcon, MatchMedia } from "coral-ui/components";
import { ButtonProps } from "coral-ui/components/Button";
import styles from "./ReactionButton.css";
@@ -14,19 +15,20 @@ interface ReactionButtonProps {
icon: string;
iconActive: string | null;
readOnly?: boolean;
// color: string;
className?: string;
color?: typeof styles & ButtonProps["color"];
}
class ReactionButton extends React.Component<ReactionButtonProps> {
public render() {
const { totalReactions, reacted, readOnly } = this.props;
const { totalReactions, reacted, readOnly, className } = this.props;
return (
<Button
variant="ghost"
size="small"
onClick={this.props.onClick}
disabled={readOnly}
className={cn({ [styles.readOnly]: readOnly })}
className={cn({ [styles.readOnly]: readOnly }, className)}
>
<MatchMedia gtWidth="xs">
<ButtonIcon>
@@ -3043,6 +3043,45 @@ input StoryConfigurationInput {
disableLazy: Boolean
}
"""
ReactionConfigurationInput stores the configuration for reactions used across this
Tenant.
"""
input ReactionConfigurationInput {
"""
icon is the string representing the icon to be used for the reactions.
"""
icon: String
"""
iconActive is the string representing the icon that should be used when the
icon should be considered active.
"""
iconActive: String
"""
label is the string placed beside the reaction icon to provide better context.
"""
label: String
"""
labelActive is the string placed beside the reaction icon to provide better
context when it has been selected.
"""
labelActive: String
"""
sortLabel is the string placed inside of the sort menu to sort for comment
with most reactions.
"""
sortLabel: String
"""
color is the hex color code that can be used to change the color of the button.
"""
color: String
}
"""
SettingsInput is the partial type of the Settings type for performing mutations.
"""
@@ -3136,6 +3175,11 @@ input SettingsInput {
stories stores the configuration around stories.
"""
stories: StoryConfigurationInput
"""
reaction specifies the configuration for reactions.
"""
reaction: ReactionConfigurationInput
}
"""
+18
View File
@@ -727,3 +727,21 @@ userDetails-banned-by = <strong>by</strong> { $username }
userDetails-suspended-by = <strong>Suspended by</strong> { $username }
userDetails-suspension-start = <strong>Start:</strong> { $timestamp }
userDetails-suspension-end = <strong>End:</strong> { $timestamp }
configure-general-reactions-title = Reactions
configure-general-reactions-explanation =
Allow your community to engage with one another and express themselves
with one-click reactions. By default, Coral allows commenters to
"Respect" each other's comments, but you may customize reaction text
based on the needs of your community.
configure-general-reactions-label = Reaction label
configure-general-reactions-input =
.placehodlder = E.g. Respect
configure-general-reactions-active-label = Active reaction label
configure-general-reactions-active-input =
.placehodlder = E.g. Respected
configure-general-reactions-sort-label = Sort label
configure-general-reactions-sort-input =
.placehodlder = E.g. Most Respected
configure-general-reactions-preview = Preview
configure-general-reaction-sortMenu-sortBy = Sort by