Refactor components

This commit is contained in:
Chi Vinh Le
2017-10-10 17:00:25 +07:00
parent 21342b8b81
commit dfcb9e24df
15 changed files with 241 additions and 367 deletions
@@ -1,9 +1,3 @@
.title {
color: black;
font-size: 1.26em;
font-weight: 500;
}
.card {
margin-bottom: 20px;
align-items: flex-start;
@@ -20,6 +14,8 @@
.wrapper {
width: 100%;
font-size: 14px;
letter-spacing: 0;
}
.action {
@@ -34,7 +30,6 @@
display: inline-block;
padding: 0px 30px;
box-sizing: border-box;
width: 100%;
}
.enabledSetting {
@@ -49,5 +44,6 @@
.disabledSettingText {
color: #ccc;
pointer-events: none;
}
@@ -0,0 +1,45 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ConfigureCard.css';
import {Card} from 'coral-ui';
import {Checkbox} from 'react-mdl';
import cn from 'classnames';
const ConfigureCard = ({title, children, className, onCheckbox, checked, ...rest}) => (
<Card {...rest} className={cn(
styles.card,
className,
{
[styles.enabledSetting]: checked === true,
[styles.disabledSetting]: checked === false,
},
)}>
{checked !== undefined &&
<div className={styles.action}>
<Checkbox
onChange={onCheckbox}
checked={checked} />
</div>
}
<div className={cn(styles.wrapper, {
[styles.content]: checked !== undefined,
})}>
<div className={styles.header}>{title}</div>
<div className={cn({
[styles.disabledSettingText]: checked === false,
})}>
{children}
</div>
</div>
</Card>
);
ConfigureCard.propTypes = {
title: PropTypes.string.isRequired,
className: PropTypes.string,
onCheckbox: PropTypes.func,
checked: PropTypes.bool,
children: PropTypes.node,
};
export default ConfigureCard;
@@ -0,0 +1,5 @@
.title {
color: black;
font-size: 1.26em;
font-weight: 500;
}
@@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ConfigurePage.css';
const ConfigurePage = ({title, children, ...rest}) => (
<div {...rest}>
<h3 className={styles.title}>{title}</h3>
{children}
</div>
);
ConfigurePage.propTypes = {
title: PropTypes.string.isRequired,
children: PropTypes.node,
};
export default ConfigurePage;
@@ -1,17 +0,0 @@
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
}
@@ -1,25 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Card} from 'coral-ui';
import styles from './Domainlist.css';
import TagsInput from 'coral-admin/src/components/TagsInput';
import t from 'coral-framework/services/i18n';
import ConfigureCard from './ConfigureCard';
const Domainlist = ({domains, onChangeDomainlist}) => {
return (
<Card className={styles.card}>
<div className={styles.wrapper}>
<div className={styles.header}>{t('configure.domain_list_title')}</div>
<p>{t('configure.domain_list_text')}</p>
<div className={styles.wrapper}>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
onChange={(tags) => onChangeDomainlist('whitelist', tags)}
/>
</div>
</div>
</Card>
<ConfigureCard title={t('configure.domain_list_title')}>
<p>{t('configure.domain_list_text')}</p>
<TagsInput
value={domains}
inputProps={{placeholder: 'URL'}}
onChange={(tags) => onChangeDomainlist('whitelist', tags)}
/>
</ConfigureCard>
);
};
@@ -1,21 +1,3 @@
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
}
.embedInput {
width: 100%;
display: block;
@@ -2,8 +2,9 @@ import React, {Component} from 'react';
import t from 'coral-framework/services/i18n';
import join from 'url-join';
import styles from './EmbedLink.css';
import {Button, Card} from 'coral-ui';
import {Button} from 'coral-ui';
import {BASE_URL} from 'coral-framework/constants/url';
import ConfigureCard from './ConfigureCard';
class EmbedLink extends Component {
@@ -34,21 +35,18 @@ class EmbedLink extends Component {
"></script>
`.trim();
return (
<Card shadow="2" className={styles.card}>
<div className={styles.wrapper}>
<div className={styles.header}>Embed Comment Stream</div>
<p>{t('configure.copy_and_paste')}</p>
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
<div className={styles.actions}>
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
{t('embedlink.copy')}
</Button>
<div className={styles.copiedText}>
{this.state.copied && 'Copied!'}
</div>
<ConfigureCard title={'Embed Comment Stream'}>
<p>{t('configure.copy_and_paste')}</p>
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
<div className={styles.actions}>
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
{t('embedlink.copy')}
</Button>
<div className={styles.copiedText}>
{this.state.copied && 'Copied!'}
</div>
</div>
</Card>
</ConfigureCard>
);
}
}
@@ -1,12 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ModerationSettings.css';
import {Card} from 'coral-ui';
import {Checkbox} from 'react-mdl';
import Wordlist from './Wordlist';
import Slot from 'coral-framework/components/Slot';
import t from 'coral-framework/services/i18n';
import cn from 'classnames';
import ConfigurePage from './ConfigurePage';
import ConfigureCard from './ConfigureCard';
class ModerationSettings extends React.Component {
@@ -43,52 +41,31 @@ class ModerationSettings extends React.Component {
render() {
const {settings, data, root} = this.props;
// just putting this here for shorthand below
const on = styles.enabledSetting;
const off = styles.disabledSetting;
return (
<div>
<h3 className={styles.title}>{t('configure.moderation_settings')}</h3>
<Card className={cn(styles.card, settings.requireEmailConfirmation ? on : off)}>
<div className={styles.action}>
<Checkbox
onChange={this.updateEmailConfirmation}
checked={settings.requireEmailConfirmation} />
</div>
<div className={styles.content}>
<div className={styles.header}>{t('configure.require_email_verification')}</div>
<p className={settings.requireEmailConfirmation ? '' : styles.disabledSettingText}>
{t('configure.require_email_verification_text')}
</p>
</div>
</Card>
<Card className={cn(styles.card, settings.moderation === 'PRE' ? on : off)}>
<div className={styles.action}>
<Checkbox
onChange={this.updateModeration}
checked={settings.moderation === 'PRE'} />
</div>
<div className={styles.content}>
<div className={styles.header}>{t('configure.enable_pre_moderation')}</div>
<p className={settings.moderation === 'PRE' ? '' : styles.disabledSettingText}>
{t('configure.enable_pre_moderation_text')}
</p>
</div>
</Card>
<Card className={`${styles.card} ${settings.premodLinksEnable ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={this.updatePremodLinksEnable}
checked={settings.premodLinksEnable} />
</div>
<div className={styles.content}>
<div className={styles.header}>{t('configure.enable_premod_links')}</div>
<p className={settings.premodLinksEnable ? '' : styles.disabledSettingText}>
{t('configure.enable_premod_links_text')}
</p>
</div>
</Card>
<ConfigurePage
title={t('configure.moderation_settings')}
>
<ConfigureCard
checked={settings.requireEmailConfirmation}
onCheckbox={this.updateEmailConfirmation}
title={t('configure.require_email_verification')}
>
{t('configure.require_email_verification_text')}
</ConfigureCard>
<ConfigureCard
checked={settings.moderation === 'PRE'}
onCheckbox={this.updateModeration}
title={t('configure.enable_pre_moderation')}
>
{t('configure.enable_pre_moderation_text')}
</ConfigureCard>
<ConfigureCard
checked={settings.premodLinksEnable}
onCheckbox={this.updatePremodLinksEnable}
title={t('configure.enable_premod_links')}
>
{t('configure.enable_premod_links_text')}
</ConfigureCard>
<Wordlist
bannedWords={settings.wordlist.banned}
suspectWords={settings.wordlist.suspect}
@@ -98,7 +75,7 @@ class ModerationSettings extends React.Component {
data={data}
queryData={{root, settings}}
/>
</div>
</ConfigurePage>
);
}
}
@@ -1,56 +1,3 @@
.title {
color: black;
font-size: 1.26em;
font-weight: 500;
}
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
}
.action {
display: inline-block;
position: absolute;
top: 0;
left: 0;
padding: 20px;
}
.content {
display: inline-block;
padding: 0px 30px;
box-sizing: border-box;
width: 100%;
}
.enabledSetting {
border-left-color: #00796b;
border-left-style: solid;
border-left-width: 7px;
}
.disabledSetting {
padding-left: 22px;
}
.disabledSettingText {
color: #ccc;
}
.configSettingInfoBox {
min-height: 100px;
margin-bottom: 20px;
@@ -78,8 +25,6 @@
}
}
.hidden {
display: none;
}
@@ -2,12 +2,14 @@ import React from 'react';
import {SelectField, Option} from 'react-mdl-selectfield';
import t from 'coral-framework/services/i18n';
import styles from './StreamSettings.css';
import {Checkbox, Textfield} from 'react-mdl';
import {Card, Icon, TextArea} from 'coral-ui';
import {Textfield} from 'react-mdl';
import {Icon, TextArea} from 'coral-ui';
import PropTypes from 'prop-types';
import Slot from 'coral-framework/components/Slot';
import MarkdownEditor from 'coral-framework/components/MarkdownEditor';
import cn from 'classnames';
import ConfigurePage from './ConfigurePage';
import ConfigureCard from './ConfigureCard';
const TIMESTAMPS = {
weeks: 60 * 60 * 24 * 7,
@@ -100,130 +102,109 @@ class StreamSettings extends React.Component {
render() {
const {settings, data, root, errors} = this.props;
// just putting this here for shorthand below
const on = styles.enabledSetting;
const off = styles.disabledSetting;
return (
<div>
<h3 className={styles.title}>{t('configure.stream_settings')}</h3>
<Card className={cn(styles.card, settings.charCountEnable ? on : off)}>
<div className={styles.action}>
<Checkbox
onChange={this.updateCharCountEnable}
checked={settings.charCountEnable} />
</div>
<div className={styles.content}>
<div className={styles.header}>{t('configure.comment_count_header')}</div>
<p className={settings.charCountEnable ? '' : styles.disabledSettingText}>
<span>{t('configure.comment_count_text_pre')}</span>
<input type='text'
className={cn(styles.inlineTextfield, styles.charCountTexfield, settings.charCountEnable && styles.charCountTexfieldEnable)}
htmlFor='charCount'
onChange={this.updateCharCount}
value={settings.charCount}
disabled={settings.charCountEnable ? '' : 'disabled'}
/>
<span>{t('configure.comment_count_text_post')}</span>
{
errors.charCount &&
<span className={styles.settingsError}>
<br/>
<Icon name="error_outline"/>
{t('configure.comment_count_error')}
</span>
}
</p>
</div>
</Card>
<Card className={cn(styles.card, styles.configSettingInfoBox, settings.infoBoxEnable ? on : off)}>
<div className={styles.action}>
<Checkbox
onChange={this.updateInfoBoxEnable}
checked={settings.infoBoxEnable} />
</div>
<div className={styles.content}>
<div className={styles.header}>
{t('configure.include_comment_stream')}
</div>
<p className={settings.infoBoxEnable ? '' : styles.disabledSettingText}>
{t('configure.include_comment_stream_desc')}
</p>
<div className={cn(styles.configSettingInfoBox, settings.infoBoxEnable ? null : styles.hidden)} >
<MarkdownEditor
className={styles.descriptionBox}
onChange={this.updateInfoBoxContent}
value={settings.infoBoxContent}
/>
</div>
</div>
</Card>
<Card className={cn(styles.card, styles.configSettingInfoBox)}>
<div className={styles.wrapper}>
<div className={styles.header}>{t('configure.closed_stream_settings')}</div>
<p>{t('configure.closed_comments_desc')}</p>
<div>
<TextArea className={styles.descriptionBox}
onChange={this.updateClosedMessage}
value={settings.closedMessage}
/>
</div>
</div>
</Card>
{/* Edit Comment Timeframe */}
<Card className={styles.card}>
<div className={styles.header}>{t('configure.edit_comment_timeframe_heading')}</div>
<ConfigurePage
title={t('configure.stream_settings')}
>
<ConfigureCard
checked={settings.charCountEnable}
onCheckbox={this.updateCharCountEnable}
title={t('configure.comment_count_header')}
>
<span>{t('configure.comment_count_text_pre')}</span>
<input type='text'
className={cn(styles.inlineTextfield, styles.charCountTexfield, settings.charCountEnable && styles.charCountTexfieldEnable)}
htmlFor='charCount'
onChange={this.updateCharCount}
value={settings.charCount}
disabled={settings.charCountEnable ? '' : 'disabled'}
/>
<span>{t('configure.comment_count_text_post')}</span>
{
errors.charCount &&
<span className={styles.settingsError}>
<br/>
<Icon name="error_outline"/>
{t('configure.comment_count_error')}
</span>
}
</ConfigureCard>
<ConfigureCard
checked={settings.infoBoxEnable}
onCheckbox={this.updateInfoBoxEnable}
title={t('configure.include_comment_stream')}
>
<p>
{t('configure.edit_comment_timeframe_text_pre')}
&nbsp;
<input
className={cn(styles.inlineTextfield, styles.editCommentTimeframeTextfield)}
type="number"
min="0"
onChange={this.updateEditCommentWindowLength}
placeholder="30"
defaultValue={(settings.editCommentWindowLength / 1000) /* saved as ms, rendered as seconds */}
pattern='[0-9]+([\.][0-9]*)?'
/>
&nbsp;
{t('configure.edit_comment_timeframe_text_post')}
{t('configure.include_comment_stream_desc')}
</p>
</Card>
<Card className={cn(styles.card, styles.configSettingInfoBox)}>
<div className={styles.action}>
<Checkbox
onChange={this.updateAutoClose}
checked={settings.autoCloseStream} />
<div className={cn(styles.configSettingInfoBox, settings.infoBoxEnable ? null : styles.hidden)} >
<MarkdownEditor
className={styles.descriptionBox}
onChange={this.updateInfoBoxContent}
value={settings.infoBoxContent}
/>
</div>
<div className={styles.content}>
<div className={styles.header}>{t('configure.close_after')}</div>
<br />
<Textfield
type='number'
pattern='[0-9]+'
style={{width: 50}}
onChange={this.updateClosedTimeout}
value={getTimeoutAmount(settings.closedTimeout)}
label={t('configure.closed_comments_label')} />
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={this.updateClosedTimeoutMeasure}>
<Option value={'hours'}>{t('configure.hours')}</Option>
<Option value={'days'}>{t('configure.days')}</Option>
<Option value={'weeks'}>{t('configure.weeks')}</Option>
</SelectField>
</div>
</ConfigureCard>
<ConfigureCard
checked={settings.configSettingInfoBox}
onCheckbox={this.updateClosedMessage}
title={t('configure.closed_stream_settings')}
>
<p>{t('configure.closed_comments_desc')}</p>
<div>
<TextArea className={styles.descriptionBox}
onChange={this.updateClosedMessage}
value={settings.closedMessage}
/>
</div>
</Card>
</ConfigureCard>
<ConfigureCard
title={t('configure.edit_comment_timeframe_heading')}
>
{t('configure.edit_comment_timeframe_text_pre')}
&nbsp;
<input
className={cn(styles.inlineTextfield, styles.editCommentTimeframeTextfield)}
type="number"
min="0"
onChange={this.updateEditCommentWindowLength}
placeholder="30"
defaultValue={(settings.editCommentWindowLength / 1000) /* saved as ms, rendered as seconds */}
pattern='[0-9]+([\.][0-9]*)?'
/>
&nbsp;
{t('configure.edit_comment_timeframe_text_post')}
</ConfigureCard>
<ConfigureCard
checked={settings.autoCloseStream}
onCheckbox={this.updateAutoClose}
title={t('configure.close_after')}
>
<Textfield
type='number'
pattern='[0-9]+'
style={{width: 50}}
onChange={this.updateClosedTimeout}
value={getTimeoutAmount(settings.closedTimeout)}
label={t('configure.closed_comments_label')} />
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={this.updateClosedTimeoutMeasure}>
<Option value={'hours'}>{t('configure.hours')}</Option>
<Option value={'days'}>{t('configure.days')}</Option>
<Option value={'weeks'}>{t('configure.weeks')}</Option>
</SelectField>
</div>
</ConfigureCard>
{/* the above card should be the last one if at all possible because of z-index issues with the selects */}
<Slot
fill="adminStreamSettings"
data={data}
queryData={{root, settings}}
/>
</div>
</ConfigurePage>
);
}
}
@@ -1,27 +1,3 @@
.title {
color: black;
font-size: 1.26em;
font-weight: 500;
}
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
}
.customCSSInput {
width: 100%;
font-size: 14px;
@@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Card} from 'coral-ui';
import Domainlist from './Domainlist';
import EmbedLink from './EmbedLink';
import styles from './TechSettings.css';
import Slot from 'coral-framework/components/Slot';
import t from 'coral-framework/services/i18n';
import ConfigurePage from './ConfigurePage';
import ConfigureCard from './ConfigureCard';
class TechSettings extends React.Component {
@@ -32,28 +33,26 @@ class TechSettings extends React.Component {
render() {
const {settings, data, root} = this.props;
return (
<div>
<h3 className={styles.title}>{t('configure.tech_settings')}</h3>
<ConfigurePage
title={t('configure.tech_settings')}
>
<Domainlist
domains={settings.domains.whitelist}
onChangeDomainlist={this.updateDomainlist} />
<EmbedLink />
<Card className={styles.card}>
<div className={styles.wrapper}>
<div className={styles.header}>{t('configure.custom_css_url')}</div>
<p>{t('configure.custom_css_url_desc')}</p>
<input
className={styles.customCSSInput}
value={settings.customCssUrl}
onChange={this.updateCustomCssUrl} />
</div>
</Card>
<ConfigureCard title={t('configure.custom_css_url')}>
<p>{t('configure.custom_css_url_desc')}</p>
<input
className={styles.customCSSInput}
value={settings.customCssUrl}
onChange={this.updateCustomCssUrl} />
</ConfigureCard>
<Slot
fill="adminTechSettings"
data={data}
queryData={{root, settings}}
/>
</div>
</ConfigurePage>
);
}
}
@@ -1,17 +0,0 @@
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
}
@@ -1,33 +1,26 @@
import React from 'react';
import t from 'coral-framework/services/i18n';
import TagsInput from 'coral-admin/src/components/TagsInput';
import styles from './Wordlist.css';
import PropTypes from 'prop-types';
import {Card} from 'coral-ui';
import ConfigureCard from './ConfigureCard';
const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
<div>
<Card className={styles.card}>
<div className={styles.header}>{t('configure.banned_words_title')}</div>
<ConfigureCard title={t('configure.banned_words_title')}>
<p>{t('configure.banned_word_text')}</p>
<div className={styles.wrapper}>
<TagsInput
value={bannedWords}
inputProps={{placeholder: 'word or phrase'}}
onChange={(tags) => onChangeWordlist('banned', tags)}
/>
</div>
</Card>
<Card className={styles.card}>
<div className={styles.header}>{t('configure.suspect_word_title')}</div>
<TagsInput
value={bannedWords}
inputProps={{placeholder: 'word or phrase'}}
onChange={(tags) => onChangeWordlist('banned', tags)}
/>
</ConfigureCard>
<ConfigureCard title={t('configure.suspect_word_title')}>
<p>{t('configure.suspect_word_text')}</p>
<div className={styles.wrapper}>
<TagsInput
value={suspectWords}
inputProps={{placeholder: 'word or phrase'}}
onChange={(tags) => onChangeWordlist('suspect', tags)} />
</div>
</Card>
<TagsInput
value={suspectWords}
inputProps={{placeholder: 'word or phrase'}}
onChange={(tags) => onChangeWordlist('suspect', tags)} />
</ConfigureCard>
</div>
);