initial commit
This commit is contained in:
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { RawHTML, useMemo } from '@wordpress/element';
|
||||
import { WordCountType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { generateSummary } from './utils';
|
||||
|
||||
interface SummaryProps {
|
||||
className?: string;
|
||||
source: string;
|
||||
maxLength?: number;
|
||||
countType?: WordCountType;
|
||||
}
|
||||
/**
|
||||
* Summary component.
|
||||
*
|
||||
* @param {Object} props Component props.
|
||||
* @param {string} props.source Source text.
|
||||
* @param {number} props.maxLength Max length of the summary, using countType.
|
||||
* @param {string} props.countType One of words, characters_excluding_spaces, or characters_including_spaces.
|
||||
* @param {string} props.className Class name for rendered component.
|
||||
*/
|
||||
export const Summary = ( {
|
||||
source,
|
||||
maxLength = 15,
|
||||
countType = 'words',
|
||||
className = '',
|
||||
}: SummaryProps ): JSX.Element => {
|
||||
const summaryText = useMemo( () => {
|
||||
return generateSummary( source, maxLength, countType );
|
||||
}, [ source, maxLength, countType ] );
|
||||
|
||||
return <RawHTML className={ className }>{ summaryText }</RawHTML>;
|
||||
};
|
||||
|
||||
export default Summary;
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { generateSummary } from '../utils';
|
||||
|
||||
describe( 'Summary Component', () => {
|
||||
describe( 'Test the generateSummary utility', () => {
|
||||
const testContent =
|
||||
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><p>Ut enim ad minim veniam, quis <strong>nostrud</strong> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p><p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>';
|
||||
|
||||
it( 'Default', async () => {
|
||||
const result = generateSummary( testContent );
|
||||
|
||||
expect( result.trim() ).toEqual(
|
||||
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore…</p>'
|
||||
);
|
||||
} );
|
||||
it( 'No max words - return full description', async () => {
|
||||
const result = generateSummary( testContent, 100000 );
|
||||
|
||||
expect( result.trim() ).toEqual(
|
||||
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>\n<p>Ut enim ad minim veniam, quis <strong>nostrud</strong> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>\n<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>'
|
||||
);
|
||||
} );
|
||||
it( 'Limit to 3 words', async () => {
|
||||
const result = generateSummary( testContent, 3 );
|
||||
|
||||
expect( result.trim() ).toEqual(
|
||||
'<p>Lorem ipsum dolor…</p>'
|
||||
);
|
||||
} );
|
||||
it( 'Limit to 1 word', async () => {
|
||||
const result = generateSummary( testContent, 1 );
|
||||
|
||||
expect( result.trim() ).toEqual( '<p>Lorem…</p>' );
|
||||
} );
|
||||
it( 'Limit to 15 characters, including spaces.', async () => {
|
||||
const result = generateSummary(
|
||||
testContent,
|
||||
15,
|
||||
'characters_including_spaces'
|
||||
);
|
||||
|
||||
expect( result.trim() ).toEqual( '<p>Lorem ipsum dol…</p>' );
|
||||
} );
|
||||
it( 'Limit to 15 characters, excluding spaces.', async () => {
|
||||
const result = generateSummary(
|
||||
testContent,
|
||||
15,
|
||||
'characters_excluding_spaces'
|
||||
);
|
||||
|
||||
expect( result.trim() ).toEqual(
|
||||
'<p>Lorem ipsum dolor…</p>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
describe( 'Test the generateSummary utility with HTML tags in strings', () => {
|
||||
const testContent =
|
||||
'<p>Lorem <strong class="classname">ipsum</strong> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</p>';
|
||||
|
||||
it( 'Limit string to 10 characters', async () => {
|
||||
const result = generateSummary(
|
||||
testContent,
|
||||
10,
|
||||
'characters_excluding_spaces'
|
||||
);
|
||||
|
||||
expect( result.trim() ).toEqual( '<p>Lorem ipsum…</p>' );
|
||||
} );
|
||||
it( 'Limit string to 5 words', async () => {
|
||||
const result = generateSummary( testContent, 5, 'words' );
|
||||
|
||||
expect( result.trim() ).toEqual(
|
||||
'<p>Lorem ipsum dolor sit amet…</p>'
|
||||
);
|
||||
} );
|
||||
it( 'First paragraph only - tags are not stripped.', async () => {
|
||||
const result = generateSummary( testContent, 9999, 'words' );
|
||||
|
||||
expect( result.trim() ).toEqual(
|
||||
'<p>Lorem <strong class="classname">ipsum</strong> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</p>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
describe( 'Test the generateSummary utility with special chars', () => {
|
||||
const testContent =
|
||||
'<p>我不知道这是否行得通。</p><p>我是用中文写的说明,因此我们可以测试如何修剪产品摘要中的单词。</p>';
|
||||
|
||||
it( 'Default', async () => {
|
||||
const result = generateSummary(
|
||||
testContent,
|
||||
15,
|
||||
'characters_excluding_spaces'
|
||||
);
|
||||
|
||||
expect( result.trim() ).toEqual( '<p>我不知道这是否行得通。</p>' );
|
||||
} );
|
||||
it( 'Limit to 3 words', async () => {
|
||||
const result = generateSummary(
|
||||
testContent,
|
||||
3,
|
||||
'characters_excluding_spaces'
|
||||
);
|
||||
|
||||
expect( result.trim() ).toEqual( '<p>我不知…</p>' );
|
||||
} );
|
||||
} );
|
||||
} );
|
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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 ) => {
|
||||
const tagsRegExp = /<\/?[a-z][^>]*?>/gi;
|
||||
return htmlString.replace( tagsRegExp, '' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove trailing punctuation and append some characters to a string.
|
||||
*
|
||||
* @param {string} text Text to append to.
|
||||
* @param {string} moreText Text to append.
|
||||
* @return {string} String with appended characters.
|
||||
*/
|
||||
const appendMoreText = ( text, moreText ) => {
|
||||
return text.replace( /[\s|\.\,]+$/i, '' ) + moreText;
|
||||
};
|
||||
|
||||
/**
|
||||
* Limit words in string and returned trimmed version.
|
||||
*
|
||||
* @param {string} text Text to trim.
|
||||
* @param {number} maxLength Number of countType to limit to.
|
||||
* @param {string} moreText Appended to the trimmed string.
|
||||
* @return {string} Trimmed string.
|
||||
*/
|
||||
const trimWords = ( text, maxLength, moreText = '…' ) => {
|
||||
const textToTrim = removeTags( text );
|
||||
const trimmedText = textToTrim
|
||||
.split( ' ' )
|
||||
.splice( 0, maxLength )
|
||||
.join( ' ' );
|
||||
|
||||
return autop( appendMoreText( trimmedText, moreText ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Limit characters in string and returned trimmed version.
|
||||
*
|
||||
* @param {string} text Text to trim.
|
||||
* @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.
|
||||
* @return {string} Trimmed string.
|
||||
*/
|
||||
const trimCharacters = (
|
||||
text,
|
||||
maxLength,
|
||||
includeSpaces = true,
|
||||
moreText = '…'
|
||||
) => {
|
||||
const textToTrim = removeTags( text );
|
||||
const trimmedText = textToTrim.slice( 0, maxLength );
|
||||
|
||||
if ( includeSpaces ) {
|
||||
return autop( appendMoreText( trimmedText, moreText ) );
|
||||
}
|
||||
|
||||
const matchSpaces = trimmedText.match( /([\s]+)/g );
|
||||
const spaceCount = matchSpaces ? matchSpaces.length : 0;
|
||||
const trimmedTextExcludingSpaces = textToTrim.slice(
|
||||
0,
|
||||
maxLength + spaceCount
|
||||
);
|
||||
|
||||
return autop( appendMoreText( trimmedTextExcludingSpaces, moreText ) );
|
||||
};
|
Reference in New Issue
Block a user