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 => (