Add text limit to payment method descriptions in the page editor (https://github.com/woocommerce/woocommerce-blocks/pull/9708)
* Convert summary/utils to TS * Add @wordpress/wordcount type defs * Move trimWords, trimCharacters, remoteTags & appendMoreText to own file * Add tests for trimWords and related functions * Trim payment method description if it is longer than 30 words
This commit is contained in:
parent
cbfd2977dd
commit
aa1fe5c308
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { autop } from '@wordpress/autop';
|
||||
import { trimCharacters, trimWords } from '@woocommerce/utils';
|
||||
import { count, CountType } from '@wordpress/wordcount';
|
||||
|
||||
/**
|
||||
* Get first paragraph from some HTML text, or return whole string.
|
||||
*
|
||||
* @param {string} source Source text.
|
||||
* @return {string} First paragraph found in string.
|
||||
*/
|
||||
const getFirstParagraph = ( source: string ) => {
|
||||
const pIndex = source.indexOf( '</p>' );
|
||||
|
||||
if ( pIndex === -1 ) {
|
||||
return source;
|
||||
}
|
||||
|
||||
return source.substr( 0, pIndex + 4 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates the summary text from a string of text.
|
||||
*
|
||||
* @param {string} source Source text.
|
||||
* @param {number} maxLength Limit number of countType returned if text has multiple paragraphs.
|
||||
* @param {string} countType What is being counted. One of words, characters_excluding_spaces, or characters_including_spaces.
|
||||
* @return {string} Generated summary.
|
||||
*/
|
||||
export const generateSummary = (
|
||||
source: string,
|
||||
maxLength = 15,
|
||||
countType: CountType = 'words'
|
||||
) => {
|
||||
const sourceWithParagraphs = autop( source );
|
||||
const sourceWordCount = count( sourceWithParagraphs, countType );
|
||||
|
||||
if ( sourceWordCount <= maxLength ) {
|
||||
return sourceWithParagraphs;
|
||||
}
|
||||
|
||||
const firstParagraph = getFirstParagraph( sourceWithParagraphs );
|
||||
const firstParagraphWordCount = count( firstParagraph, countType );
|
||||
|
||||
if ( firstParagraphWordCount <= maxLength ) {
|
||||
return firstParagraph;
|
||||
}
|
||||
|
||||
if ( countType === 'words' ) {
|
||||
return trimWords( firstParagraph, maxLength );
|
||||
}
|
||||
|
||||
return trimCharacters(
|
||||
firstParagraph,
|
||||
maxLength,
|
||||
countType === 'characters_including_spaces'
|
||||
);
|
||||
};
|
|
@ -12,6 +12,8 @@ import Noninteractive from '@woocommerce/base-components/noninteractive';
|
|||
import { GlobalPaymentMethod } from '@woocommerce/types';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
|
||||
import { blocksConfig } from '@woocommerce/block-settings';
|
||||
import { trimCharacters, trimWords } from '@woocommerce/utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -49,6 +51,7 @@ export const Edit = ( {
|
|||
'Incompatible with block-based checkout',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
||||
const wordCountType = blocksConfig.wordCountType;
|
||||
|
||||
return (
|
||||
<FormStepBlock
|
||||
|
@ -77,12 +80,32 @@ export const Edit = ( {
|
|||
const isIncompatible =
|
||||
!! incompatiblePaymentMethods[ method.id ];
|
||||
|
||||
let trimmedDescription;
|
||||
|
||||
if ( wordCountType === 'words' ) {
|
||||
trimmedDescription = trimWords(
|
||||
method.description,
|
||||
30,
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
trimmedDescription = trimCharacters(
|
||||
method.description,
|
||||
30,
|
||||
wordCountType ===
|
||||
'characters_including_spaces',
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ExternalLinkCard
|
||||
key={ method.id }
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout§ion=${ method.id }` }
|
||||
title={ method.title }
|
||||
description={ method.description }
|
||||
description={ trimmedDescription }
|
||||
{ ...( isIncompatible
|
||||
? {
|
||||
warning:
|
||||
|
|
|
@ -9,3 +9,4 @@ export * from './shared-attributes';
|
|||
export * from './sanitize-html';
|
||||
export * from './is-site-editor-page';
|
||||
export * from './is-widget-editor-page';
|
||||
export * from './trim-words';
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
appendMoreText,
|
||||
removeTags,
|
||||
trimCharacters,
|
||||
trimWords,
|
||||
} from '@woocommerce/utils';
|
||||
|
||||
describe( 'trim-words', () => {
|
||||
describe( 'removeTags', () => {
|
||||
it( 'Removes HTML tags from a string', () => {
|
||||
const string = '<div><a href="/index.php">trim-words.ts</a></div>';
|
||||
const trimmedString = removeTags( string );
|
||||
expect( trimmedString ).toEqual( 'trim-words.ts' );
|
||||
} );
|
||||
} );
|
||||
describe( 'appendMoreText', () => {
|
||||
it( 'Removes trailing punctuation and appends some characters to a string', () => {
|
||||
const string = 'trim-words.ts,';
|
||||
const appendedString = appendMoreText( string, '...' );
|
||||
expect( appendedString ).toEqual( 'trim-words.ts...' );
|
||||
} );
|
||||
} );
|
||||
describe( 'trimWords', () => {
|
||||
const testContent =
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.';
|
||||
it( 'Limits words in string and returns trimmed version', () => {
|
||||
const trimmedString = trimWords( testContent, 3 );
|
||||
expect( trimmedString ).toBe(
|
||||
'<p>Lorem ipsum dolor…</p>\n'
|
||||
);
|
||||
} );
|
||||
it( 'Limits words in string and returns trimmed version with custom moreText', () => {
|
||||
const trimmedString = trimWords( testContent, 4, '... read more.' );
|
||||
expect( trimmedString ).toEqual(
|
||||
'<p>Lorem ipsum dolor sit... read more.</p>\n'
|
||||
);
|
||||
} );
|
||||
it( 'Limits words in string and returns trimmed version without autop', () => {
|
||||
const trimmedString = trimWords(
|
||||
testContent,
|
||||
3,
|
||||
'…',
|
||||
false
|
||||
);
|
||||
expect( trimmedString ).toEqual( 'Lorem ipsum dolor…' );
|
||||
} );
|
||||
it( 'does not append anything if the text is shorter than the trim limit', () => {
|
||||
const trimmedString = trimWords( testContent, 100 );
|
||||
expect( trimmedString ).toEqual( '<p>' + testContent + '</p>\n' );
|
||||
} );
|
||||
} );
|
||||
describe( 'trimCharacters', () => {
|
||||
const testContent = 'Lorem ipsum dolor sit amet.';
|
||||
|
||||
it( 'Limits characters in string and returns trimmed version including spaces', () => {
|
||||
const result = trimCharacters( testContent, 10 );
|
||||
expect( result ).toEqual( '<p>Lorem ipsu…</p>\n' );
|
||||
} );
|
||||
it( 'Limits characters in string and returns trimmed version excluding spaces', () => {
|
||||
const result = trimCharacters( testContent, 10, false );
|
||||
expect( result ).toEqual( '<p>Lorem ipsum…</p>\n' );
|
||||
} );
|
||||
it( 'Limits characters in string and returns trimmed version with custom moreText', () => {
|
||||
const result = trimCharacters(
|
||||
testContent,
|
||||
10,
|
||||
false,
|
||||
'... read more.'
|
||||
);
|
||||
expect( result ).toEqual( '<p>Lorem ipsum... read more.</p>\n' );
|
||||
} );
|
||||
it( 'Limits characters in string and returns trimmed version without autop', () => {
|
||||
const result = trimCharacters(
|
||||
testContent,
|
||||
10,
|
||||
false,
|
||||
'... read more.',
|
||||
false
|
||||
);
|
||||
expect( result ).toEqual( 'Lorem ipsum... read more.' );
|
||||
} );
|
||||
|
||||
it( 'does not append anything if the text is shorter than the trim limit', () => {
|
||||
const trimmedString = trimCharacters( testContent, 1000 );
|
||||
expect( trimmedString ).toEqual( '<p>' + testContent + '</p>\n' );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -1,70 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { count } from '@wordpress/wordcount';
|
||||
import { autop } from '@wordpress/autop';
|
||||
|
||||
/**
|
||||
* Generates the summary text from a string of text.
|
||||
*
|
||||
* @param {string} source Source text.
|
||||
* @param {number} maxLength Limit number of countType returned if text has multiple paragraphs.
|
||||
* @param {string} countType What is being counted. One of words, characters_excluding_spaces, or characters_including_spaces.
|
||||
* @return {string} Generated summary.
|
||||
*/
|
||||
export const generateSummary = (
|
||||
source,
|
||||
maxLength = 15,
|
||||
countType = 'words'
|
||||
) => {
|
||||
const sourceWithParagraphs = autop( source );
|
||||
const sourceWordCount = count( sourceWithParagraphs, countType );
|
||||
|
||||
if ( sourceWordCount <= maxLength ) {
|
||||
return sourceWithParagraphs;
|
||||
}
|
||||
|
||||
const firstParagraph = getFirstParagraph( sourceWithParagraphs );
|
||||
const firstParagraphWordCount = count( firstParagraph, countType );
|
||||
|
||||
if ( firstParagraphWordCount <= maxLength ) {
|
||||
return firstParagraph;
|
||||
}
|
||||
|
||||
if ( countType === 'words' ) {
|
||||
return trimWords( firstParagraph, maxLength );
|
||||
}
|
||||
|
||||
return trimCharacters(
|
||||
firstParagraph,
|
||||
maxLength,
|
||||
countType === 'characters_including_spaces'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get first paragraph from some HTML text, or return whole string.
|
||||
*
|
||||
* @param {string} source Source text.
|
||||
* @return {string} First paragraph found in string.
|
||||
*/
|
||||
const getFirstParagraph = ( source ) => {
|
||||
const pIndex = source.indexOf( '</p>' );
|
||||
|
||||
if ( pIndex === -1 ) {
|
||||
return source;
|
||||
}
|
||||
|
||||
return source.substr( 0, pIndex + 4 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove HTML tags from a string.
|
||||
*
|
||||
* @param {string} htmlString String to remove tags from.
|
||||
* @return {string} Plain text string.
|
||||
*/
|
||||
const removeTags = ( htmlString ) => {
|
||||
export const removeTags = ( htmlString: string ) => {
|
||||
const tagsRegExp = /<\/?[a-z][^>]*?>/gi;
|
||||
return htmlString.replace( tagsRegExp, '' );
|
||||
};
|
||||
|
@ -76,7 +21,7 @@ const removeTags = ( htmlString ) => {
|
|||
* @param {string} moreText Text to append.
|
||||
* @return {string} String with appended characters.
|
||||
*/
|
||||
const appendMoreText = ( text, moreText ) => {
|
||||
export const appendMoreText = ( text: string, moreText: string ) => {
|
||||
return text.replace( /[\s|\.\,]+$/i, '' ) + moreText;
|
||||
};
|
||||
|
||||
|
@ -86,15 +31,29 @@ const appendMoreText = ( text, moreText ) => {
|
|||
* @param {string} text Text to trim.
|
||||
* @param {number} maxLength Number of countType to limit to.
|
||||
* @param {string} moreText Appended to the trimmed string.
|
||||
* @param {string} useAutop Whether to format with autop before returning.
|
||||
* @return {string} Trimmed string.
|
||||
*/
|
||||
const trimWords = ( text, maxLength, moreText = '…' ) => {
|
||||
export const trimWords = (
|
||||
text: string,
|
||||
maxLength: number,
|
||||
moreText = '…',
|
||||
useAutop = true
|
||||
) => {
|
||||
const textToTrim = removeTags( text );
|
||||
const trimmedText = textToTrim
|
||||
.split( ' ' )
|
||||
.splice( 0, maxLength )
|
||||
.join( ' ' );
|
||||
|
||||
if ( trimmedText === textToTrim ) {
|
||||
return useAutop ? autop( textToTrim ) : textToTrim;
|
||||
}
|
||||
|
||||
if ( ! useAutop ) {
|
||||
return appendMoreText( trimmedText, moreText );
|
||||
}
|
||||
|
||||
return autop( appendMoreText( trimmedText, moreText ) );
|
||||
};
|
||||
|
||||
|
@ -105,17 +64,23 @@ const trimWords = ( text, maxLength, moreText = '…' ) => {
|
|||
* @param {number} maxLength Number of countType to limit to.
|
||||
* @param {boolean} includeSpaces Should spaces be included in the count.
|
||||
* @param {string} moreText Appended to the trimmed string.
|
||||
* @param {string} useAutop Whether to format with autop before returning.
|
||||
* @return {string} Trimmed string.
|
||||
*/
|
||||
const trimCharacters = (
|
||||
text,
|
||||
maxLength,
|
||||
export const trimCharacters = (
|
||||
text: string,
|
||||
maxLength: number,
|
||||
includeSpaces = true,
|
||||
moreText = '…'
|
||||
moreText = '…',
|
||||
useAutop = true
|
||||
) => {
|
||||
const textToTrim = removeTags( text );
|
||||
const trimmedText = textToTrim.slice( 0, maxLength );
|
||||
|
||||
if ( trimmedText === textToTrim ) {
|
||||
return useAutop ? autop( textToTrim ) : textToTrim;
|
||||
}
|
||||
|
||||
if ( includeSpaces ) {
|
||||
return autop( appendMoreText( trimmedText, moreText ) );
|
||||
}
|
||||
|
@ -127,5 +92,9 @@ const trimCharacters = (
|
|||
maxLength + spaceCount
|
||||
);
|
||||
|
||||
if ( ! useAutop ) {
|
||||
return appendMoreText( trimmedTextExcludingSpaces, moreText );
|
||||
}
|
||||
|
||||
return autop( appendMoreText( trimmedTextExcludingSpaces, moreText ) );
|
||||
};
|
|
@ -95,6 +95,7 @@
|
|||
"@types/wordpress__data-controls": "2.2.0",
|
||||
"@types/wordpress__editor": "^11.0.0",
|
||||
"@types/wordpress__notices": "^3.5.0",
|
||||
"@types/wordpress__wordcount": "^2.4.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.56.0",
|
||||
"@typescript-eslint/parser": "5.35.1",
|
||||
"@woocommerce/api": "0.2.0",
|
||||
|
@ -12027,6 +12028,12 @@
|
|||
"@types/wordpress__rich-text": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/wordpress__wordcount": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/wordpress__wordcount/-/wordpress__wordcount-2.4.2.tgz",
|
||||
"integrity": "sha512-bMiBGqZTRV0/VzHjwlVcAVXsjvdlCWF7N6p7b5vOj2O+EWu35WQdTCWPxvBGp14CKdpK4AGm19v10z9zg3X5vA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
|
@ -60299,6 +60306,12 @@
|
|||
"@types/wordpress__rich-text": "*"
|
||||
}
|
||||
},
|
||||
"@types/wordpress__wordcount": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/wordpress__wordcount/-/wordpress__wordcount-2.4.2.tgz",
|
||||
"integrity": "sha512-bMiBGqZTRV0/VzHjwlVcAVXsjvdlCWF7N6p7b5vOj2O+EWu35WQdTCWPxvBGp14CKdpK4AGm19v10z9zg3X5vA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
|
|
|
@ -142,6 +142,7 @@
|
|||
"@types/wordpress__data-controls": "2.2.0",
|
||||
"@types/wordpress__editor": "^11.0.0",
|
||||
"@types/wordpress__notices": "^3.5.0",
|
||||
"@types/wordpress__wordcount": "^2.4.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.56.0",
|
||||
"@typescript-eslint/parser": "5.35.1",
|
||||
"@woocommerce/api": "0.2.0",
|
||||
|
|
Loading…
Reference in New Issue