-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add truncate * Fix truncate stories * Update tests and dependency versions * Update truncate and view READMEs * Fix package-lock.json Co-authored-by: Jon Q <[email protected]>
- Loading branch information
1 parent
946d54b
commit 5ced236
Showing
19 changed files
with
786 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# Truncate | ||
|
||
`Truncate` is a typography primitive that trims text content. For almost all cases, it is recommended that `Text`, `Heading`, or `Subheading` is used to render text content. However, `Truncate` is available for custom implementations. | ||
|
||
## Usage | ||
|
||
```jsx live | ||
import { Truncate } from '@wp-g2/components'; | ||
|
||
function Example() { | ||
return ( | ||
<Truncate> | ||
Where the north wind meets the sea, there's a river full of memory. | ||
Sleep, my darling, safe and sound, for in this river all is found. | ||
In her waters, deep and true, lay the answers and a path for you. | ||
Dive down deep into her sound, but not too far or you'll be drowned | ||
</Truncate> | ||
); | ||
} | ||
``` | ||
|
||
## Props | ||
|
||
##### ellipsis | ||
|
||
**Type**: `string` | ||
|
||
The ellipsis string when `truncate` is set. | ||
|
||
##### ellipsizeMode | ||
|
||
**Type**: `"auto"`,`"head"`,`"tail"`,`"middle"` | ||
|
||
Determines where to truncate. For example, we can truncate text right in the middle. To do this, we need to set `ellipsizeMode` to `middle` and a text `limit`. | ||
|
||
- `auto`: Trims content at the end automatically without a `limit`. | ||
- `head`: Trims content at the beginning. Requires a `limit`. | ||
- `middle`: Trims content in the middle. Requires a `limit`. | ||
- `tail`: Trims content at the end. Requires a `limit`. | ||
|
||
##### limit | ||
|
||
**Type**: `number` | ||
|
||
Determines the max characters when `truncate` is set. | ||
|
||
##### numberOfLines | ||
|
||
**Type**: `number` | ||
|
||
Clamps the text content to the specifiec `numberOfLines`, adding the `ellipsis` at the end. | ||
|
||
```jsx live | ||
import { Truncate } from '@wp-g2/components'; | ||
|
||
function Example() { | ||
return ( | ||
<Truncate numberOfLines={2}> | ||
Where the north wind meets the sea, there's a river full of memory. | ||
Sleep, my darling, safe and sound, for in this river all is found. | ||
In her waters, deep and true, lay the answers and a path for you. | ||
Dive down deep into her sound, but not too far or you'll be drowned | ||
</Truncate> | ||
); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { default as Truncate } from './truncate'; | ||
|
||
export * from './use-truncate'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { Truncate } from '../index'; | ||
|
||
export default { | ||
component: Truncate, | ||
title: 'Components/Truncate', | ||
}; | ||
|
||
export const _default = () => { | ||
return ( | ||
<Truncate numberOfLines={ 2 }> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut | ||
facilisis dictum tortor, eu tincidunt justo scelerisque tincidunt. | ||
Duis semper dui id augue malesuada, ut feugiat nisi aliquam. | ||
Vestibulum venenatis diam sem, finibus dictum massa semper in. Nulla | ||
facilisi. Nunc vulputate faucibus diam, in lobortis arcu ornare vel. | ||
In dignissim nunc sed facilisis finibus. Etiam imperdiet mattis | ||
arcu, sed rutrum sapien blandit gravida. Aenean sollicitudin neque | ||
eget enim blandit, sit amet rutrum leo vehicula. Nunc malesuada | ||
ultricies eros ut faucibus. Aliquam erat volutpat. Nulla nec feugiat | ||
risus. Vivamus iaculis dui aliquet ante ultricies feugiat. | ||
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices | ||
posuere cubilia curae; Vivamus nec pretium velit, sit amet | ||
consectetur ante. Praesent porttitor ex eget fermentum mattis. | ||
</Truncate> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { render } from '@testing-library/react'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { Truncate } from '../index'; | ||
|
||
describe( 'props', () => { | ||
test( 'should render correctly', () => { | ||
const { container } = render( | ||
<Truncate>Some people are worth melting for.</Truncate> | ||
); | ||
expect( container.firstChild.textContent ).toEqual( | ||
'Some people are worth melting for.' | ||
); | ||
} ); | ||
|
||
test( 'should render limit', () => { | ||
const { container } = render( | ||
<Truncate limit={ 1 } ellipsizeMode="tail"> | ||
Some | ||
</Truncate> | ||
); | ||
expect( container.firstChild.textContent ).toEqual( 'S…' ); | ||
} ); | ||
|
||
test( 'should render custom ellipsis', () => { | ||
const { container } = render( | ||
<Truncate ellipsis="!!!" limit={ 5 } ellipsizeMode="tail"> | ||
Some people are worth melting for. | ||
</Truncate> | ||
); | ||
expect( container.firstChild.textContent ).toEqual( 'Some !!!' ); | ||
} ); | ||
|
||
test( 'should render custom ellipsizeMode', () => { | ||
const { container } = render( | ||
<Truncate ellipsis="!!!" ellipsizeMode="middle" limit={ 5 }> | ||
Some people are worth melting for. | ||
</Truncate> | ||
); | ||
expect( container.firstChild.textContent ).toEqual( 'So!!!r.' ); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { css } from '@wp-g2/styles'; | ||
|
||
export const Truncate = css` | ||
display: block; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { isNil } from 'lodash'; | ||
|
||
export const TRUNCATE_ELLIPSIS = '…'; | ||
export const TRUNCATE_TYPE = { | ||
auto: 'auto', | ||
head: 'head', | ||
middle: 'middle', | ||
tail: 'tail', | ||
none: 'none', | ||
}; | ||
|
||
export const TRUNCATE_DEFAULT_PROPS = { | ||
ellipsis: TRUNCATE_ELLIPSIS, | ||
ellipsizeMode: TRUNCATE_TYPE.auto, | ||
limit: 0, | ||
numberOfLines: 0, | ||
}; | ||
|
||
// Source | ||
// https://github.com/kahwee/truncate-middle | ||
/** | ||
* @param {string} word | ||
* @param {number} headLength | ||
* @param {number} tailLength | ||
* @param {string} ellipsis | ||
*/ | ||
export function truncateMiddle( word, headLength, tailLength, ellipsis ) { | ||
if ( typeof word !== 'string' ) { | ||
return ''; | ||
} | ||
const wordLength = word.length; | ||
// Setting default values | ||
// eslint-disable-next-line no-bitwise | ||
const frontLength = ~~headLength; // will cast to integer | ||
// eslint-disable-next-line no-bitwise | ||
const backLength = ~~tailLength; | ||
/* istanbul ignore next */ | ||
const truncateStr = ! isNil( ellipsis ) ? ellipsis : TRUNCATE_ELLIPSIS; | ||
|
||
if ( | ||
( frontLength === 0 && backLength === 0 ) || | ||
frontLength >= wordLength || | ||
backLength >= wordLength || | ||
frontLength + backLength >= wordLength | ||
) { | ||
return word; | ||
} else if ( backLength === 0 ) { | ||
return word.slice( 0, frontLength ) + truncateStr; | ||
} | ||
return ( | ||
word.slice( 0, frontLength ) + | ||
truncateStr + | ||
word.slice( wordLength - backLength ) | ||
); | ||
} | ||
|
||
/** | ||
* | ||
* @param {string} words | ||
* @param {typeof TRUNCATE_DEFAULT_PROPS} props | ||
*/ | ||
export function truncateContent( words = '', props ) { | ||
const mergedProps = { ...TRUNCATE_DEFAULT_PROPS, ...props }; | ||
const { ellipsis, ellipsizeMode, limit } = mergedProps; | ||
|
||
if ( ellipsizeMode === TRUNCATE_TYPE.none ) { | ||
return words; | ||
} | ||
|
||
let truncateHead; | ||
let truncateTail; | ||
|
||
switch ( ellipsizeMode ) { | ||
case TRUNCATE_TYPE.head: | ||
truncateHead = 0; | ||
truncateTail = limit; | ||
break; | ||
case TRUNCATE_TYPE.middle: | ||
truncateHead = Math.floor( limit / 2 ); | ||
truncateTail = Math.floor( limit / 2 ); | ||
break; | ||
default: | ||
truncateHead = limit; | ||
truncateTail = 0; | ||
} | ||
|
||
const truncatedContent = | ||
ellipsizeMode !== TRUNCATE_TYPE.auto | ||
? truncateMiddle( words, truncateHead, truncateTail, ellipsis ) | ||
: words; | ||
|
||
return truncatedContent; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { createComponent } from '../utils'; | ||
import { useTruncate } from './use-truncate'; | ||
|
||
export default createComponent( { | ||
as: 'span', | ||
useHook: useTruncate, | ||
name: 'Truncate', | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { useContextSystem } from '@wp-g2/context'; | ||
import { css, cx } from '@wp-g2/styles'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useMemo } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import * as styles from './truncate-styles'; | ||
import { | ||
TRUNCATE_ELLIPSIS, | ||
TRUNCATE_TYPE, | ||
truncateContent, | ||
} from './truncate-utils'; | ||
|
||
/** | ||
* @typedef Props | ||
* @property {string} [ellipsis='...'] String to use to truncate the string with. | ||
* @property {'auto' | 'head' | 'tail' | 'middle' | 'none'} [ellipsizeMode='auto'] Mode to follow. | ||
* @property {number} [limit=0] Limit. | ||
* @property {number} [numberOfLines=0] Number of lines. | ||
*/ | ||
|
||
/** | ||
* @param {import('@wp-g2/create-styles').ViewOwnProps<Props, 'span'>} props | ||
*/ | ||
export function useTruncate( props ) { | ||
const { | ||
className, | ||
children, | ||
ellipsis = TRUNCATE_ELLIPSIS, | ||
ellipsizeMode = TRUNCATE_TYPE.auto, | ||
limit = 0, | ||
numberOfLines = 0, | ||
...otherProps | ||
} = useContextSystem( props, 'Truncate' ); | ||
|
||
const truncatedContent = truncateContent( | ||
typeof children === 'string' ? /** @type {string} */ ( children ) : '', | ||
{ | ||
ellipsis, | ||
ellipsizeMode, | ||
limit, | ||
numberOfLines, | ||
} | ||
); | ||
|
||
const shouldTruncate = ellipsizeMode === TRUNCATE_TYPE.auto; | ||
|
||
const classes = useMemo( () => { | ||
const sx = {}; | ||
|
||
sx.numberOfLines = css` | ||
-webkit-box-orient: vertical; | ||
-webkit-line-clamp: ${ numberOfLines }; | ||
display: -webkit-box; | ||
overflow: hidden; | ||
`; | ||
|
||
return cx( | ||
shouldTruncate && ! numberOfLines && styles.Truncate, | ||
shouldTruncate && !! numberOfLines && sx.numberOfLines, | ||
className | ||
); | ||
}, [ className, numberOfLines, shouldTruncate ] ); | ||
|
||
return { ...otherProps, className: classes, children: truncatedContent }; | ||
} |
Oops, something went wrong.