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 { GlobalPaymentMethod } from '@woocommerce/types';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
|
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
|
||||||
|
import { blocksConfig } from '@woocommerce/block-settings';
|
||||||
|
import { trimCharacters, trimWords } from '@woocommerce/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -49,6 +51,7 @@ export const Edit = ( {
|
||||||
'Incompatible with block-based checkout',
|
'Incompatible with block-based checkout',
|
||||||
'woo-gutenberg-products-block'
|
'woo-gutenberg-products-block'
|
||||||
);
|
);
|
||||||
|
const wordCountType = blocksConfig.wordCountType;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormStepBlock
|
<FormStepBlock
|
||||||
|
@ -77,12 +80,32 @@ export const Edit = ( {
|
||||||
const isIncompatible =
|
const isIncompatible =
|
||||||
!! incompatiblePaymentMethods[ method.id ];
|
!! 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 (
|
return (
|
||||||
<ExternalLinkCard
|
<ExternalLinkCard
|
||||||
key={ method.id }
|
key={ method.id }
|
||||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout§ion=${ method.id }` }
|
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout§ion=${ method.id }` }
|
||||||
title={ method.title }
|
title={ method.title }
|
||||||
description={ method.description }
|
description={ trimmedDescription }
|
||||||
{ ...( isIncompatible
|
{ ...( isIncompatible
|
||||||
? {
|
? {
|
||||||
warning:
|
warning:
|
||||||
|
|
|
@ -9,3 +9,4 @@ export * from './shared-attributes';
|
||||||
export * from './sanitize-html';
|
export * from './sanitize-html';
|
||||||
export * from './is-site-editor-page';
|
export * from './is-site-editor-page';
|
||||||
export * from './is-widget-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
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { count } from '@wordpress/wordcount';
|
|
||||||
import { autop } from '@wordpress/autop';
|
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.
|
* Remove HTML tags from a string.
|
||||||
*
|
*
|
||||||
* @param {string} htmlString String to remove tags from.
|
* @param {string} htmlString String to remove tags from.
|
||||||
* @return {string} Plain text string.
|
* @return {string} Plain text string.
|
||||||
*/
|
*/
|
||||||
const removeTags = ( htmlString ) => {
|
export const removeTags = ( htmlString: string ) => {
|
||||||
const tagsRegExp = /<\/?[a-z][^>]*?>/gi;
|
const tagsRegExp = /<\/?[a-z][^>]*?>/gi;
|
||||||
return htmlString.replace( tagsRegExp, '' );
|
return htmlString.replace( tagsRegExp, '' );
|
||||||
};
|
};
|
||||||
|
@ -76,7 +21,7 @@ const removeTags = ( htmlString ) => {
|
||||||
* @param {string} moreText Text to append.
|
* @param {string} moreText Text to append.
|
||||||
* @return {string} String with appended characters.
|
* @return {string} String with appended characters.
|
||||||
*/
|
*/
|
||||||
const appendMoreText = ( text, moreText ) => {
|
export const appendMoreText = ( text: string, moreText: string ) => {
|
||||||
return text.replace( /[\s|\.\,]+$/i, '' ) + moreText;
|
return text.replace( /[\s|\.\,]+$/i, '' ) + moreText;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,15 +31,29 @@ const appendMoreText = ( text, moreText ) => {
|
||||||
* @param {string} text Text to trim.
|
* @param {string} text Text to trim.
|
||||||
* @param {number} maxLength Number of countType to limit to.
|
* @param {number} maxLength Number of countType to limit to.
|
||||||
* @param {string} moreText Appended to the trimmed string.
|
* @param {string} moreText Appended to the trimmed string.
|
||||||
|
* @param {string} useAutop Whether to format with autop before returning.
|
||||||
* @return {string} Trimmed string.
|
* @return {string} Trimmed string.
|
||||||
*/
|
*/
|
||||||
const trimWords = ( text, maxLength, moreText = '…' ) => {
|
export const trimWords = (
|
||||||
|
text: string,
|
||||||
|
maxLength: number,
|
||||||
|
moreText = '…',
|
||||||
|
useAutop = true
|
||||||
|
) => {
|
||||||
const textToTrim = removeTags( text );
|
const textToTrim = removeTags( text );
|
||||||
const trimmedText = textToTrim
|
const trimmedText = textToTrim
|
||||||
.split( ' ' )
|
.split( ' ' )
|
||||||
.splice( 0, maxLength )
|
.splice( 0, maxLength )
|
||||||
.join( ' ' );
|
.join( ' ' );
|
||||||
|
|
||||||
|
if ( trimmedText === textToTrim ) {
|
||||||
|
return useAutop ? autop( textToTrim ) : textToTrim;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! useAutop ) {
|
||||||
|
return appendMoreText( trimmedText, moreText );
|
||||||
|
}
|
||||||
|
|
||||||
return autop( 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 {number} maxLength Number of countType to limit to.
|
||||||
* @param {boolean} includeSpaces Should spaces be included in the count.
|
* @param {boolean} includeSpaces Should spaces be included in the count.
|
||||||
* @param {string} moreText Appended to the trimmed string.
|
* @param {string} moreText Appended to the trimmed string.
|
||||||
|
* @param {string} useAutop Whether to format with autop before returning.
|
||||||
* @return {string} Trimmed string.
|
* @return {string} Trimmed string.
|
||||||
*/
|
*/
|
||||||
const trimCharacters = (
|
export const trimCharacters = (
|
||||||
text,
|
text: string,
|
||||||
maxLength,
|
maxLength: number,
|
||||||
includeSpaces = true,
|
includeSpaces = true,
|
||||||
moreText = '…'
|
moreText = '…',
|
||||||
|
useAutop = true
|
||||||
) => {
|
) => {
|
||||||
const textToTrim = removeTags( text );
|
const textToTrim = removeTags( text );
|
||||||
const trimmedText = textToTrim.slice( 0, maxLength );
|
const trimmedText = textToTrim.slice( 0, maxLength );
|
||||||
|
|
||||||
|
if ( trimmedText === textToTrim ) {
|
||||||
|
return useAutop ? autop( textToTrim ) : textToTrim;
|
||||||
|
}
|
||||||
|
|
||||||
if ( includeSpaces ) {
|
if ( includeSpaces ) {
|
||||||
return autop( appendMoreText( trimmedText, moreText ) );
|
return autop( appendMoreText( trimmedText, moreText ) );
|
||||||
}
|
}
|
||||||
|
@ -127,5 +92,9 @@ const trimCharacters = (
|
||||||
maxLength + spaceCount
|
maxLength + spaceCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( ! useAutop ) {
|
||||||
|
return appendMoreText( trimmedTextExcludingSpaces, moreText );
|
||||||
|
}
|
||||||
|
|
||||||
return autop( appendMoreText( trimmedTextExcludingSpaces, moreText ) );
|
return autop( appendMoreText( trimmedTextExcludingSpaces, moreText ) );
|
||||||
};
|
};
|
|
@ -95,6 +95,7 @@
|
||||||
"@types/wordpress__data-controls": "2.2.0",
|
"@types/wordpress__data-controls": "2.2.0",
|
||||||
"@types/wordpress__editor": "^11.0.0",
|
"@types/wordpress__editor": "^11.0.0",
|
||||||
"@types/wordpress__notices": "^3.5.0",
|
"@types/wordpress__notices": "^3.5.0",
|
||||||
|
"@types/wordpress__wordcount": "^2.4.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.56.0",
|
"@typescript-eslint/eslint-plugin": "5.56.0",
|
||||||
"@typescript-eslint/parser": "5.35.1",
|
"@typescript-eslint/parser": "5.35.1",
|
||||||
"@woocommerce/api": "0.2.0",
|
"@woocommerce/api": "0.2.0",
|
||||||
|
@ -12027,6 +12028,12 @@
|
||||||
"@types/wordpress__rich-text": "*"
|
"@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": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.5.3",
|
"version": "8.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||||
|
@ -60299,6 +60306,12 @@
|
||||||
"@types/wordpress__rich-text": "*"
|
"@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": {
|
"@types/ws": {
|
||||||
"version": "8.5.3",
|
"version": "8.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
"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__data-controls": "2.2.0",
|
||||||
"@types/wordpress__editor": "^11.0.0",
|
"@types/wordpress__editor": "^11.0.0",
|
||||||
"@types/wordpress__notices": "^3.5.0",
|
"@types/wordpress__notices": "^3.5.0",
|
||||||
|
"@types/wordpress__wordcount": "^2.4.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.56.0",
|
"@typescript-eslint/eslint-plugin": "5.56.0",
|
||||||
"@typescript-eslint/parser": "5.35.1",
|
"@typescript-eslint/parser": "5.35.1",
|
||||||
"@woocommerce/api": "0.2.0",
|
"@woocommerce/api": "0.2.0",
|
||||||
|
|
Loading…
Reference in New Issue