pablosichert / react-truncate Goto Github PK
View Code? Open in Web Editor NEWReact component for truncating multi-line spans and adding an ellipsis.
Home Page: https://www.webpackbin.com/bins/-Kw6QnAkjmv1OD6Of-ZD
License: ISC License
React component for truncating multi-line spans and adding an ellipsis.
Home Page: https://www.webpackbin.com/bins/-Kw6QnAkjmv1OD6Of-ZD
License: ISC License
We have a scenario where the Truncate component will potentially not be visible, and this is leading to requestAnimationFrame
being called continuously.
In https://github.com/One-com/react-truncate/blob/master/src/Truncate.js#L133 on line 133 and following, it checks the width of the parent and if the width is zero, it will re-queue another check with requestAnimationFrame
. As the parent or a grandparent is invisible (display: none) in our scenario, the width will always be zero, resulting in this being calling indefinitely.
I think a check before might solve this. The generally accepted way of determining if an element is visible on the page or not is to use element.offsetHeight
.
const targetParentVisible = target.parentNode.offsetHeight;
// If target isn't visible on the page, then it'll start an infinite
// loop with requestAnimationFrame below
if(!targetParentVisible) {
return
}
I was having the same behavior described in #16, but while using web-safe font families. It turns out that when the css letter-spacing property is used, the component calculates it has room for more letters than it actually has room for.
It would be great if it could account for the pixels between letters. Thanks!
letter-spacing: 0;
Hi, thanks for creating an maintaining this repo.
I am running into an issue where the truncating is not working as expected. If i specify there to be 2 lines to be shown, it will show something like
lorem ipsum
lorem ipsum
lorem...
I did some debugging as I found it worked elsewhere, and found out removing letter-spacing makes it work correctly. Looking through the source, it indeed appears there is no mention of letter-spacing in the css properties.
Is there a way in which the letter spacing can be taken into account when calculating the width, either automatically or by specifying a custom prop?
Here is a WebpackBin of the problem https://www.webpackbin.com/bins/-KueNc5FP6xDfCRLM4zp
is it okay to do this?
<Truncate>
<span dangerouslySetInnerHTML={{__html: draft.content}}/>
<span dangerouslySetInnerHTML={{__html: draft.content}}/>
</Truncate>
Hello!
When I try to use your component on my React project, it throws an error:
Uncaught Error: Cannot find module 'babel-runtime/helpers/objectWithoutProperties' from 'react-truncate/lib/Truncate.js'
package.json
:
"dependencies": {
"autosize": "^3.0.15",
"babel-brunch": "^6.0.6",
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-react-display-name": "^2.0.0",
"babel-plugin-transform-class-properties": "^6.11.5",
"babel-plugin-transform-runtime": "^6.12.0",
"babel-polyfill": "^6.13.0",
"babel-preset-es2015": "^6.14.0",
"babel-preset-es2016": "^6.11.3",
"babel-preset-es2017": "^6.14.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"babel-runtime": "^6.11.6",
"babel-template": "^6.14.0",
"bower": "^1.7.7",
"brunch": "https://github.com/spyl94/brunch/tarball/6f20a214104f8db7842365b6063c18ebc65134f9",
"classnames": "^2.2.3",
"clean-css-brunch": "^2.0.0",
"css-brunch": "^2.0.0",
"diff": "^3.0.0",
"exports-loader": "^0.6.3",
"fancybox": "^3.0.0",
"flat": "^2.0.1",
"flux": "^2.1.1",
"imports-loader": "^0.6.5",
"javascript-brunch": "^2.0.0",
"jquery": "^3.1.0",
"json-brunch": "^1.5.4",
"mailcheck": "^1.1.1",
"moment": "^2.11.2",
"process-env-brunch": "^1.4.4-d",
"quill": "^0.20.1",
"react": "^15.3.1",
"react-bootstrap": "^0.29.5",
"react-checkbox-group": "https://github.com/spyl94/react-checkbox-group/tarball/296721a62a682c5e441bdd449704550b69257af9",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.3.1",
"react-dropzone": "^3.3.4",
"react-google-charts": "^1.0.2",
"react-google-recaptcha": "^0.5.2",
"react-intl": "1.2.0",
"react-linkify": "^0.1.1",
"react-nl2br": "^0.2.0",
"react-on-rails": "^6.1.0",
"react-radio": "^2.1.1",
"react-redux": "^4.4.5",
"react-router": "^2.7.0",
"react-router-bootstrap": "^0.23.1",
"react-truncate": "^1.1.4",
"readmore": "0.0.2",
"redux": "^3.4.0",
"redux-form": "6.0.0-rc.3",
"redux-saga": "^0.11.0",
"sass-brunch": "2.6.2",
"sinon": "^1.17.3",
"uglify-js-brunch": "^2.0.1",
"webpack": "^1.13.2",
"whatwg-fetch": "^1.0.0"
},
.babelrc
:
{
"presets": ["react", "es2015", "es2016", "es2017", "stage-0"],
"plugins": [
"add-module-exports"
]
}
If the text contains some kind of line break (\n,
or other block-level element), it is not being maintained, and we are ending up with a text in only one line.
Not sure why you are adding line breaks here, if my input text didn't contain any line breaks why add them at all? In my solution it makes for sloppy presentation. I pasted an example of what a 3 line text blob looks like.
I could fix this on a local branch, just wondering what the underlying reason was.
Lorem ipsum dolor sit amet, consectetur
adipiscing
elit, sed do eiusmod tempor incididunt ut
labore
et dolore magna aliqua. Ut enim ad
minim
renderLine(line, i, arr) {
if (i === arr.length - 1) {
return <span key={i}>{line}</span>;
} else {
const br = <br key={i + 'br'} />;
if (line) {
return [
<span key={i}>{line}</span>,
br
];
} else {
return br;
}
}
}
Hello,
Since i upgrade from 2.1.1 to 2.1.2, i have this error:
TypeError: this.context.measureText is not a function
Trace de la pile :
measureWidth@http://localhost:3000/app/vendor.js:12612:20
getLines@http://localhost:3000/app/vendor.js:12653:21
render@http://localhost:3000/app/vendor.js:12766:28
...
Do you know the problem ?
Thank you
We can't be 100% certain, but for some reason the resulting width of the text is <1px wider than it's containing span.
We think it may have to do with parent node width precision. Chrome seems to use 3 decimals precision, IE 2 and this works out most of the time, but in a list of < 10 items we 1 instance where it's less than 1 px too wide. When we add the extra pixel, it seems to be fixed.
I think you need to Math.floor() the result of target.parentNode.getBoundingClientRect().width to have more certainty of not exceeding the width of the bouding element.
Have you considered a fixed pixel height cutoff/fadeout for this? I'm asking because the resize/sizing detection seems broken for me (it inserts newlines based on where it thinks there is a wrap, and then the component then gets resized as part of page layout, and then causes it to be incorrectly wrapped.
Seems like it'd work a lot more seamlessly (without resize hacks, calculation of widths, etc) to just do a height-based solution with overflow auto, and also opens the possibility of nice fadeouts (like one sees on nytimes/wallstreetjournal)
When replying to issue #1 I noticed that we didn't have a peerDependency
on the react.
This test-case demonstates the current behavior for words that are too long for one line: https://github.com/One-com/react-truncate/blob/master/test/Truncate.js#L227..L239.
We would have expected react-truncate
to cut off long words and add ellipsis (and do this for every line that has too long words), e.g.:
Thereisasuperlo…
so that the next lines won't be
visible
Instead of omitting the last two lines.
But maybe this is not really possible due to the way react-truncate
is implemented. However, this is still a problem for us.
Would it be possible to implement the behavior that we expect? If yes, would you accept a PR? This could of course be guarded by a new option field.
Alternatively, would it be possible to somehow enable word-wrapping that allows breaking of words? I tried to get that to work with the css property overflow-wrap: break-word;
, but without success.
Any help appreciated.
Hey @pablosichert,
Do you expect the current spec suite to pass locally? I'm getting several failures from a fresh clone of the repo.
I have fixes (pending comment/approval) for both the padding and font size issues, and I'd love to be able to write some specs to go along with them, but I need to start from a working test bed.
Any thoughts?
There is a bug in the width calculation logic that does not take text-transform
CSS property into account. I have transformed my text to uppercase using this property, and the results are not what I expected, clearly because capital letters take up more space than normal ones.
As I observed the source code a bit, I think you should amend the styling query at L148 to also add the text-transform
property.
The current workaround is to call .toUpperCase()
on the input string, but it would be nice if this could be done through CSS.
Simple way to debug this,
Put a console.log inside render method, it will be called every second continuously which causes the browser tab to go unresponsive after some point.
thx
See http://keepachangelog.com/en/0.3.0/ - I can help with a PR unless the author has a strong opinion about this.
In some cases it is necessary to re-render a truncate component although its existent props (children, ellipsis and lines) are not modified. For example, when size of a parent DOM-element (component) is changed without window resizing.
Now I use the following workaround to re-render the truncate component when necessary: this.truncateRef.onResize();
But it would be useful to have special property which will trigger recalculation of targetWidth
.
Here's the error log:
npm ERR! Linux 4.2.0-36-generic
npm ERR! argv "/home/trevoke/.nvm/versions/node/v4.2.3/bin/node" "/home/trevoke/.nvm/versions/node/v4.2.3/bin/npm" "i" "--save" "react-truncate"
npm ERR! node v4.2.3
npm ERR! npm v2.14.7
npm ERR! code EPEERINVALIDnpm ERR! peerinvalid The package [email protected] does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants react@^15.0.2
npm ERR! peerinvalid Peer [email protected] wants react@^15.0.2
npm ERR! peerinvalid Peer [email protected] wants react@>= 0.14.0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0-0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0-0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.1
npm ERR! peerinvalid Peer [email protected] wants react@>=0.14.0 || ^15.0.0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0-rc.1
npm ERR! peerinvalid Peer [email protected] wants react@^15.0.2
npm ERR! peerinvalid Peer [email protected] wants react@>= 0.13 < 1.0.0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0-0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0-0
npm ERR! peerinvalid Peer [email protected] wants [email protected] || 0.14.x || ^15.0.0-0
I'm not entirely certain which part of my stack it doesn't support.
I am using this library with react-auto-scale.
I am using this library inside an autoscaled div. When the window gets resized it appears the fact that its getting the client bounding rectangle causes it to mess the line-wrapping.
One easy solution for me here would be the ability to turn off the on resize listener. If you agree, I'd be happy to implement this and send a PR.
Here is a screenshot of what I mean - you can see that on the original load, using a mobile device tool on Chrome, the text goes too far. (it should be aligned, to the left and to the right, with the blue bar underneath).
Fun fact, if I turn off the mobile device tool and then turn it on again, this happens:
So the code is doing the right thing, mostly. Let me know if you need more stuff from me.
React 15.2.0
emits warnings for unrecognised properties passed to native DOM elements (facebook/react#6800), which happens quite frequently when passing properties down a child component with { ...this.props }
.
React-truncate seems to be doing the same thing:
Warning: Unknown props `lines`, `ellipsis` on <span> tag. Remove these props from the element. For details, see https://fb.me/react-unknown-prop
I get an error when trying to do snapshot-testing with components that has a <Truncate>
element somewhere in the tree. The error is:
TypeError: Cannot use 'in' operator to search for 'nodeType' in null
at HTMLBodyElementImpl.appendChild (node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:330:22)
at HTMLBodyElement.appendChild (node_modules/jsdom/lib/jsdom/living/generated/Node.js:132:57)
at Truncate.componentDidMount (node_modules/react-truncate/lib/Truncate.CommonJS.js:77:27)
at node_modules/react-test-renderer/lib/ReactCompositeComponent.js:264:25
at measureLifeCyclePerf (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:75:12)
at node_modules/react-test-renderer/lib/ReactCompositeComponent.js:263:11
at CallbackQueue.notifyAll (node_modules/react-test-renderer/lib/CallbackQueue.js:76:22)
at ReactTestReconcileTransaction.close (node_modules/react-test-renderer/lib/ReactTestReconcileTransaction.js:36:26)
at ReactTestReconcileTransaction.closeAll (node_modules/react-test-renderer/lib/Transaction.js:209:25)
at ReactTestReconcileTransaction.perform (node_modules/react-test-renderer/lib/Transaction.js:156:16)`
the test that produces the error is:
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import Truncate from 'react-truncate';
it('Tests snapshots', () => {
const snap = renderer.create(<Truncate lines={2}>hello world</Truncate>)
.toJSON();
expect(snap).toMatchSnapshot();
});
I am using the command yarn test --env=jsdom
to run the test, which is the default way of doing it when using create-react-app.
Anything I'm missing here?
Hi, I'm using your library to implement three-lined Lists, which are part of the material design specification. They look like this:
It would be nice if I could add a prefix, e.g., like "Ali Connors", in the first list item, that had custom styling. So far, I haven't been able to find a way style only some of the text that is a child of Truncate
.
The closest I've been able to get truncates the ellipsis, e.g.,
<div>
<b>{'Ali Connors — '}</b>
<Truncate lines={2} ellipsis="...">
{description}
</Truncate>
</div>
Renders like this:
Hello, i have this code for truncate
<Truncate
key="body"
lines={!this.state.expanded && 5}
className={classnames({
[s.expand]: !this.state.expanded,
})}
ellipsis={
<div className={s.readMore} onClick={this.onTruncatedExpandClick}>
{'Read more'}
</div>
}
onTruncate={this.onTruncate}
>
{children.split('\n').map((item, key) => <span key={key}>{item}{childrenRows !== (key + 1) ? <br /> : '' }</span>)}
{status === 'edited' ? (
<span className={s.edited}>{`${this.props.intl.formatMessage(messages.edited)}`}</span>
) : null}
</Truncate>
if text lines more than 5, Read more is displayed and after expand i have this result
But if text lines 1, Edited text dispalyed as text without
How can i fix it?
I started using this great lib today but had a problems with it by not truncating to specified lines. It always truncated to more lines. I figured it this does not happen in 2.0.3.
When i revert the change made here: 324d1bf it works again.
Hy
I have div around the truncate-Component. The div has nothing but a padding:5px. However, truncate obviously considers the same width as without padding and jumps into the next line. If I replace the padding with a "margin: 5px", Truncate works as ist should.
The parent of the div (the "grandparent" of the truncate) has also 5px padding. But that seems to be stay respected in regards of the width.
It feels wrong to commit build artifacts to version control :-)
If we add lib
to .gitignore
it will still be there when we publish the npm package, but not be in the repo.
Render method really shouldn't have side-effects. This actually causes errors - you can see this sometimes in react-show-more - it updates state when onTruncate is called, and changing state during render is an error because it could trigger yet another render. At first I was going to post this issue in react-show-more but after looking into it I think it's a react-truncate bug.
Simple solution might be to wrap the call in a setImmediate()?
<Truncate lines={2} ellipsis={(<span>...</span>)}>
{
map((x, i) => (
<Attachment featuresFormatted={get(event, "featuresFormatted")}
index={i} key={get(x, "uri")}
attachment={x} highlightedAttachmentNames={highlightedAttachmentNames} />
), get(event, "attachments"))
}
</Truncate>
fails for me :(
Thank you, @TymchenkoOleksandr, for unveiling this problem.
See issue description in #10.
If I put a Truncate component in a div with padding, the generated elements are too long. Once I removed the padding, the generated elements fit to the appropriate number of lines.
It seems that you are calculating the appropriate width based on the width of the window and not the width of the Truncate component's parent. Consequently, if you calculate the width of the parent div instead of the window, you can rely on normal text wrapping instead of the current span + br pattern.
Example: go to your demo page, inside ReadMore.js, add this to div surrounding the Truncate component: style={{padding: 15}}
This is not really an issue but rather a question.
In your documentation for the "children" property it says "Anything that can be evaluated as text." and you list examples including markup. I've seen that you are using textContent
to extract the plain text from what's being passed in as children.
What do you think about preserving at least some markup, like a set of allowed tags? I know that it makes things more complicated, but it might be useful.
Hey, I came across an issue when using this component on some text with really large font (larger than body font size). Lines do not fit target element which causes line breaks.
I did some investigation and I found out that ellipsis that are being measured are actually located in document.body. This doesn't seem right, measured width is incorrect. Font and especially font-size can differ a lot at the place that ellipsis are actually rendered after that.
I blame this line:
https://github.com/One-com/react-truncate/blob/373aa7aded3e28c1957bf5c7414bec58f1c33257/src/Truncate.js#L50
When I remove it, everything seems to work perfectly. Ellipsis element is kept in the DOM at the same place that component was used and measured width is correct. What's the reasoning behind having this line? There's a comment that I don't really understand...
Can we remove this line?
Hello,
Perhaps I misunderstood the documentation but I have made sure to re-read it including the relevant source code.
For some reason, when setting lines
to a falsy values or value < 1 all text is hidden.
I have created this short example to demonstrate:
http://www.webpackbin.com/Vywx9ZEZf
I figured this could be caused by a change introduced not long ago on #21 - Where this change to the render()
method was made:
let text = children;
to
let text;
When looking in the React Dev Tools - the span with the "children" text appears, but the actual DOM does not contain the element. I think it has something to do with the removal of this node in ComponentDidMount.
Hope this is something simple I misunderstood..
Thanks!
the sample code has an issue:
the state is set as
this.state = {};
but this will default to 1 line since
lines={this.state.readMore && lines}
will always be false.
i fixed this and added a read more/less toggle
nice control btw saved me a ton of time thx.
`import React, {Component, PropTypes} from 'react';
import Truncate from 'react-truncate';
class ReadMoreText extends Component {
constructor(...args) {
super(...args);
this.state = {readMore: true};
this.toggleLines = this.toggleLines.bind(this);
}
toggleLines(event) {
event.preventDefault();
this.setState({
readMore: !this.state.readMore
});
}
render() {
let {children, moreText, lessText, lines, style, styleMore, styleLess} = this.props;
const divStyle = {
width: '100%',
wordWrap: 'break-word'
};
return (
<div style={{...divStyle, ...style}}>
<Truncate
lines={this.state.readMore && lines}
ellipsis={(
<span>... <a style={styleMore} onClick={this.toggleLines}>{moreText}</a></span>
)}
>
{children}
</Truncate>
{lessText && !this.state.readMore && <span> <a style={styleLess || styleMore} onClick={this.toggleLines}>{lessText}</a></span>}
</div>
);
}
}
ReadMoreText.defaultProps = {
lines: 3,
moreText: 'more',
lessText: 'less'
};
ReadMoreText.propTypes = {
children: PropTypes.node.isRequired,
text: PropTypes.node,
lines: PropTypes.number
};
export default ReadMoreText;`
Sometimes we have raw HTML and I'm not sure whether this component works when wrapped in another element, e.g., a <span>
, to which we can apply our dangerouslySetInnerHTML
.
Currently React gives a warning saying it cannot use this with children
.
Hello,
I have found a little bug that makes Truncate component break when the text width is equal to the parent container width.
In getLines() method, the line text width is measured and compared with container width. If text width is equal or greater than container the text is broken in multiple lines:
if (measureWidth(resultLine) < targetWidth) {
if (textLines.length === 1) {
// Line is end of text and fits without truncating //
didTruncate = false;
lines.push(resultLine);
break;
}
}
However, when the library try to determine when the line breaks, it repeats the same if-condition but adding equal operator:
if (measureWidth(testLine) <= targetWidth) {
lower = middle + 1;
} else {
upper = middle - 1;
}
If both widths are equal, lower value is equal to textWords length, making the following line to be empty (textLines[0]) and throwing an exception (Cannot read property 'length' of undefined):
const textWords = textLines[0];
// Handle newline
if (textWords.length === 0) {
lines.push();
textLines.shift();
line--;
continue;
}
Please review the code to make the truncate component works correctly when the width of text and parent container is equal.
Thanks!
First of all, thanks for this awesome component! Works like a charm for us.
It seems like it is not compatible with React 16.0, yet? I just naively tried to update my project and pages without Truncate just work, but pages with Truncate are giving me this error:
Uncaught Error: Element ref was specified as a string (target) but no owner was set. You may have multiple copies of React loaded.
I assume this is because Truncate's peer-dependencies? Any chance to bump them to 16.0?
So i know this is an edge case but these don't seem to play well OR i'm missing something.
accordion issue with ie9 and safari 6 and lower
using bootstrap-react OR plain old bootstrap and react-truncate
when using react-truncate inside of a bootstrap react accordion you get an error from truncate:
‘“SCRIPT5009: ‘requestAnimationFrame’ is undefined “
except when you remove the Accordion attr of defaultActiveKey AND the panel EventKey attr which makes the accordion useless.
With the error there it makes the panels unable to open and some text is not truncated.
Now i'm not sure but i'm nearly positive i had this working last week. Any insight to help me with this would be awesome. Great Package but unfortunately i may not be able to use it due to people still using ie9... for whatever reason
My application uses react router v4 for routing. When I start on a page that is using react truncate to limit descriptions to 5 lines, then click to a detail view, and then click back (or use back button), react truncate is messed up. See below gif for visualization of this.
It's worth noting that if I trigger the resize event 100ms after it rerenders after clicking back, it fixes it.
Refs should be functions now, not strings.
I think this might be causing issues in React 16, can't seem to get Truncate to work after re-render. It errors and destroys the whole React DOM when you need it to re-render (initial render seems fine?). (In our case, when the window is resized past a breakpoint, we change the number of lines Truncate should wrap to, and then the whole thing dies)
More info from eslint: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
I want 4 lines for example and i get 5 with an extra one with the ellipsis, the div around is just to see if it was that but the issue remains
<div>
<h2 className="title">
<Truncate lines={4}>
{titleToTruncate}
</Truncate>
</h2>
</div>
Lorem ipsum dolor sit amet,Lorem ipsum dolor sit amet,
Lorem ipsum dolor sit amet,
Lorem ipsum dolor sit amet,
Lorem ipsum dolor sit amet,Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Duis dui
…
something like that im getting, and is kind of random with the hot reload sometimes is like that, sometimes different, makes it not really reliable, I tried but will have to switch to another component.
Allowing a tab index to be set on the link would solve this issue. Even better, would be allowing in linkProps
that are passed directly to the link.
Since prop-types
was moved to devDependencies
in 2.1.2, it should be added to peerDependencies
. I believe that may break compatibility with anyone running older versions of react (< 15.6).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.