diff --git a/localization/react-intl/src/app/components/article/ArticleForm.json b/localization/react-intl/src/app/components/article/ArticleForm.json
index 795c26aeb4..d1c045ca50 100644
--- a/localization/react-intl/src/app/components/article/ArticleForm.json
+++ b/localization/react-intl/src/app/components/article/ArticleForm.json
@@ -19,6 +19,11 @@
"description": "Title for the slideout edit fact-check form",
"defaultMessage": "Edit Claim & Fact-Check Article"
},
+ {
+ "id": "articleForm.characterLimitReached",
+ "description": "Error message for when the character limit is reached",
+ "defaultMessage": "Character Limit Reached"
+ },
{
"id": "articleForm.saveArticleButton",
"description": "Button that saves the form data as a new article",
@@ -184,6 +189,11 @@
"description": "Label for article URL field",
"defaultMessage": "Article URL"
},
+ {
+ "id": "articleForm.characterCount",
+ "description": "Label for the character count remaining in the combined text fields",
+ "defaultMessage": "{count, plural, one {# character left} other {# characters left}}"
+ },
{
"id": "articleForm.selectLanguageLabel",
"description": "Label for input to select language",
diff --git a/src/app/components/article/ArticleForm.js b/src/app/components/article/ArticleForm.js
index 2e25616bf9..3be7c19f30 100644
--- a/src/app/components/article/ArticleForm.js
+++ b/src/app/components/article/ArticleForm.js
@@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import { graphql, createFragmentContainer } from 'react-relay/compat';
import PropTypes from 'prop-types';
+import cx from 'classnames/bind';
import { FormattedMessage, FormattedHTMLMessage, FormattedDate } from 'react-intl';
import ArticleTrash from './ArticleTrash.js';
import TagList from '../cds/menus-lists-dialogs/TagList.js';
@@ -9,10 +10,10 @@ import Slideout from '../cds/slideout/Slideout';
import ButtonMain from '../cds/buttons-checkboxes-chips/ButtonMain';
import IconReport from '../../icons/fact_check.svg';
import IconUnpublishedReport from '../../icons/unpublished_report.svg';
+import ErrorIcon from '../../icons/error.svg';
import TextArea from '../cds/inputs/TextArea';
import TextField from '../cds/inputs/TextField';
import LanguagePickerSelect from '../cds/inputs/LanguagePickerSelect';
-import LimitedTextArea from '../layout/inputs/LimitedTextArea';
import inputStyles from '../../styles/css/inputs.module.css';
import { safelyParseJSON, truncateLength, isFactCheckValueBlank } from '../../helpers';
import RatingSelector from '../cds/inputs/RatingSelector';
@@ -68,6 +69,11 @@ const ArticleForm = ({
const isStatusLocked = article.claim_description?.project_media?.last_status_obj?.locked || false;
const factCheckFieldsMissing = (articleType === 'fact-check' && (isFactCheckValueBlank(articleTitle) || isFactCheckValueBlank(summary) || !language));
+ const maxCount = articleType === 'explainer' ? 4096 : 900;
+ const [charCount, setCharCount] = React.useState(summary.length + url.length + articleTitle.length);
+ const [charCountError, setCharCountError] = React.useState(charCount > maxCount);
+ const maxCountErrorMessage = ;
+
React.useEffect(() => {
setLanguage(language || defaultArticleLanguage);
}, [language]);
@@ -87,6 +93,16 @@ const ArticleForm = ({
}
}, [articleTitle, summary, claimDescription, language]);
+ React.useEffect(() => {
+ const count = summary.length + url.length + articleTitle.length;
+ setCharCount(count);
+ if (count > maxCount) {
+ setCharCountError(true);
+ } else {
+ setCharCountError(false);
+ }
+ }, [articleTitle, summary, url]);
+
const handleGoToReport = (projectMediaDbid) => {
const teamSlug = window.location.pathname.match(/^\/([^/]+)/)[1];
// FIXME: use browserHistory.push instead of window.location.assign
@@ -358,195 +374,222 @@ const ArticleForm = ({
onButtonClick={() => handleGoToReport(article.claim_description?.project_media?.dbid)}
/>
)}
-
- { articleType === 'explainer' ?
-
- { placeholder => (}
- maxHeight="266px"
- name="title"
- placeholder={placeholder}
- required
- rows="1"
- onBlur={(e) => {
- const newValue = e.target.value.trim();
- if (newValue.length) {
- setTitleError(false);
- handleBlur('title', newValue);
- } else {
- setTitleError(true);
- }
- setArticleTitle(newValue);
- }}
- />)}
- :
-
- { placeholder => (}
- maxHeight="266px"
- name="title"
- placeholder={placeholder}
- rows="1"
- onBlur={(e) => {
- const newValue = e.target.value.trim();
- if (newValue.length) {
- handleBlur('title', newValue);
- }
- setArticleTitle(newValue);
- }}
- />)}
- }
-
-
- {articleType === 'explainer' ?
-
- { placeholder => (
-
+
+ { articleType === 'explainer' ?
+
+ { placeholder => (}
- maxChars={4096 - articleTitle.length - url.length}
- maxHeight="500px"
- name="summary"
+ defaultValue={articleTitle}
+ error={titleError || (charCountError && articleTitle.length)}
+ helpContent={charCountError && articleTitle.length ? maxCountErrorMessage : null}
+ label={}
+ maxHeight="266px"
+ name="title"
placeholder={placeholder}
required
rows="1"
- value={truncateLength(summary, 4096 - articleTitle.length - url.length - 3)}
onBlur={(e) => {
const newValue = e.target.value.trim();
if (newValue.length) {
- setSummaryError(false);
- handleBlur('description', newValue);
+ setTitleError(false);
+ handleBlur('title', newValue);
} else {
- setSummaryError(true);
+ setTitleError(true);
}
- setSummary(newValue);
}}
- />
- )}
- :
-
- { placeholder => (
- setArticleTitle(e.target.value.trim())}
+ />)}
+ :
+
+ { placeholder => (}
- maxChars={900 - articleTitle.length - url.length}
- name="summary"
+ error={titleError || (charCountError && articleTitle.length)}
+ helpContent={charCountError && articleTitle.length ? maxCountErrorMessage : null}
+ label={}
+ maxHeight="266px"
+ name="title"
placeholder={placeholder}
- required={false}
rows="1"
- value={isFactCheckValueBlank(summary) ? null : truncateLength(summary, 900 - articleTitle.length - url.length - 3)}
onBlur={(e) => {
const newValue = e.target.value.trim();
if (newValue.length) {
- handleBlur('summary', newValue);
+ handleBlur('title', newValue);
}
- setSummary(newValue);
- }}
- />
- )}
-
- }
-
-
- {articleType === 'explainer' ?
-
- { placeholder => (
- }
- placeholder={placeholder}
- onBlur={(e) => {
- const newValue = e.target.value;
- let newUrl = newValue;
- if (!/^https?:\/\//.test(newValue) && newValue && newValue.length > 0) {
- newUrl = `https://${newValue}`;
- }
- setUrl(newUrl);
- handleBlur('url', newUrl);
- }}
- />
- )}
- :
- setArticleTitle(e.target.value.trim())}
+ />)}
+ }
+
+
+ {articleType === 'explainer' ?
+
+ { placeholder => (
+ }
+ maxHeight="500px"
+ name="summary"
+ placeholder={placeholder}
+ required
+ rows="1"
+ onBlur={(e) => {
+ const newValue = e.target.value.trim();
+ if (newValue.length) {
+ setSummaryError(false);
+ handleBlur('description', newValue);
+ } else {
+ setSummaryError(true);
+ }
+ }}
+ onKeyDown={e => setSummary(e.target.value.trim())}
+ />
+ )}
+ :
+
+ { placeholder => (
+ }
+ name="summary"
+ placeholder={placeholder}
+ required={false}
+ rows="1"
+ onBlur={(e) => {
+ const newValue = e.target.value.trim();
+ if (newValue.length) {
+ handleBlur('summary', newValue);
+ }
+ }}
+ onKeyDown={e => setSummary(e.target.value.trim())}
+ />
+ )}
+
+ }
+
+
+ {articleType === 'explainer' ?
+
+ { placeholder => (
+ }
+ placeholder={placeholder}
+ onBlur={(e) => {
+ const newValue = e.target.value;
+ let newUrl = newValue;
+ if (!/^https?:\/\//.test(newValue) && newValue && newValue.length > 0) {
+ newUrl = `https://${newValue}`;
+ }
+ setUrl(newUrl);
+ handleBlur('url', newUrl);
+ }}
+ onKeyDown={e => setUrl(e.target.value.trim())}
+ />
+ )}
+ :
+
+ { placeholder => (
+ }
+ placeholder={placeholder}
+ onBlur={(e) => {
+ const newValue = e.target.value;
+ let newUrl = newValue;
+ if (!/^https?:\/\//.test(newValue) && newValue && newValue.length > 0) {
+ newUrl = `https://${newValue}`;
+ }
+ setUrl(newUrl);
+ handleBlur('url', newUrl);
+ }}
+ onKeyDown={e => setUrl(e.target.value.trim())}
+ />
+ )}
+
+ }
+
+
+
- { placeholder => (
- }
- placeholder={placeholder}
- onBlur={(e) => {
- const newValue = e.target.value;
- let newUrl = newValue;
- if (!/^https?:\/\//.test(newValue) && newValue && newValue.length > 0) {
- newUrl = `https://${newValue}`;
- }
- setUrl(newUrl);
- handleBlur('url', newUrl);
- }}
- />
- )}
-
- }
+ { charCountError && }
+
+
+
{ languages.length > 1 ?
diff --git a/src/app/styles/css/inputs.module.css b/src/app/styles/css/inputs.module.css
index efc65f2bdd..e052df7dcd 100644
--- a/src/app/styles/css/inputs.module.css
+++ b/src/app/styles/css/inputs.module.css
@@ -123,12 +123,10 @@
padding: 16px;
}
- + .form-inner-wrapper {
- margin: 16px 0 0;
- }
-
- + .form-footer-actions {
- margin: 16px 0 0;
+ + .form-inner-wrapper,
+ + .form-footer-actions,
+ + .form-fieldset-field {
+ margin-top: 16px;
}
}
@@ -146,7 +144,7 @@
}
.form-fieldset-field {
- margin: 0 0 16px;
+ margin-bottom: 16px;
&.form-autocomplete-create {
display: flex;
@@ -163,11 +161,11 @@
}
&:last-of-type {
- margin: 0;
+ margin-bottom: 0;
}
}
+ .form-fieldset {
- margin: 16px 0 0;
+ margin-top: 16px;
}
}