@@ -7,6 +7,7 @@ import { __, _n, sprintf } from '@wordpress/i18n';
77import { home , link , seen } from '@wordpress/icons' ;
88import { useSearch } from '@wordpress/route' ;
99import { Button , Card , CollapsibleCard , Link , Notice , Stack , Text } from '@wordpress/ui' ;
10+ import clsx from 'clsx' ;
1011import CardTitleIcon from '../../components/card-title-icon' ;
1112import StatusIndicator from '../../components/status-indicator' ;
1213import UpsellBanner from '../../components/upsell-banner' ;
@@ -54,9 +55,9 @@ const platformManagedHelp = __(
5455 'This setting is managed by your hosting platform and cannot be changed here.' ,
5556 'jetpack-seo'
5657) ;
57- // Figures are what search *displays*, not a limit we enforce — the field is
58- // deliberately uncapped. Google measures a pixel width (920px desktop / 680px
59- // mobile), which works out around 155 and 120 characters.
58+ // Figures are what search *displays*, not a limit we enforce. Google measures a
59+ // pixel width (920px desktop / 680px mobile), which works out around 155 and 120
60+ // characters.
6061//
6162// No social figure is quoted on purpose. `functions.opengraph.php` truncates
6263// `og:description` at 197, but that happens *before* the `jetpack_open_graph_tags`
@@ -67,6 +68,9 @@ const frontPageHelp = __(
6768 'Shown under your site name in search results and social shares. About the first 155 characters display in search, or 120 on mobile.' ,
6869 'jetpack-seo'
6970) ;
71+ // Keep in step with the 155 quoted in `frontPageHelp`.
72+ const descriptionSearchLength = 155 ;
73+ const descriptionMaxLength = 300 ;
7074
7175interface Props {
7276 form : SettingsForm ;
@@ -159,6 +163,13 @@ const SettingsScreen: FC< Props > = ( { form } ) => {
159163 // Count code points, not UTF-16 units, so an emoji or an astral character
160164 // counts once rather than twice.
161165 const descriptionLength = [ ...local . front_page_description ] . length ;
166+ // `maxLength` caps UTF-16 units, so test the cap in those: an emoji spends two.
167+ const descriptionAtMax = local . front_page_description . length >= descriptionMaxLength ;
168+ const descriptionCount = sprintf (
169+ /* translators: %d: number of characters currently in the front-page description. */
170+ _n ( '%d character' , '%d characters' , descriptionLength , 'jetpack-seo' ) ,
171+ descriptionLength
172+ ) ;
162173
163174 // Single toggles and single fields are binary — they have no partial state,
164175 // so they move straight from not-started to complete.
@@ -200,20 +211,28 @@ const SettingsScreen: FC< Props > = ( { form } ) => {
200211 help = { frontPageHelp }
201212 value = { local . front_page_description }
202213 onChange = { next => setField ( { front_page_description : next } ) }
203- maxLength = { 300 }
214+ maxLength = { descriptionMaxLength }
204215 rows = { 3 }
205216 disabled = { isSaving }
206217 __nextHasNoMarginBottom
207218 />
208- { /* Count and Save share a row, matching the title-structure footer. The
209- count is informational only — nothing here is capped. */ }
219+ { /* Count and Save share a row, matching the title-structure footer. */ }
210220 < Stack direction = "row" justify = "space-between" align = "center" gap = "sm" >
211- < Text variant = "body-sm" className = { styles . charCount } >
212- { sprintf (
213- /* translators: %d: number of characters currently in the front-page description. */
214- _n ( '%d character' , '%d characters' , descriptionLength , 'jetpack-seo' ) ,
215- descriptionLength
216- ) }
221+ < Text
222+ variant = "body-sm"
223+ className = { clsx ( styles . charCount , {
224+ [ styles . charCountWarn ] :
225+ descriptionLength > descriptionSearchLength && ! descriptionAtMax ,
226+ [ styles . charCountMax ] : descriptionAtMax ,
227+ } ) }
228+ >
229+ { descriptionAtMax
230+ ? sprintf (
231+ /* translators: %s: the character count, e.g. "300 characters". */
232+ __ ( '%s — Maximum characters reached.' , 'jetpack-seo' ) ,
233+ descriptionCount
234+ )
235+ : descriptionCount }
217236 </ Text >
218237 < Button
219238 onClick = { ( ) => commitFields ( [ 'front_page_description' ] ) }
0 commit comments