Skip to content

Commit 4fb8b86

Browse files
committed
feat(ReadMore): ReadMore Component refactored (#180)
1 parent a4705cb commit 4fb8b86

File tree

8 files changed

+276
-68
lines changed

8 files changed

+276
-68
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@
216216
"react-table": "^6.8.0",
217217
"react-test-renderer": "^16.4.0",
218218
"react-tippy": "^1.2.3",
219+
"react-truncate": "^2.4.0",
219220
"react-truncate-html": "^0.1.7",
220221
"redux-form": "^7.4.2",
221222
"redux-form-website-template": "^0.0.112",

src/components/ReadMore/README.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
1-
Links are typically used as a means of navigation either within the application, to a place outside, or to a resource. For anything else, especially things that change data and actions, you should be using a button.
21

3-
Links can have the same properties as an `<a>`-Element.
2+
Using the **ReadMore** component is a simple way to keep longer content from cluttering up your page, giving you more control over how much content is displayed to visitors.
43

5-
### Use with react-router
4+
### Use with custom content for collapsed and expanded state
65

7-
Use the Link styling by adding the className `wfp--link` to `<NavLink />`
6+
The `collapsed` props content will be displayed if the content is collapsed.
7+
```js
8+
<ReadMore collapsed="Collapsed content">
9+
Expanded content
10+
</ReadMore>
11+
```
12+
### Truncate text
13+
14+
Based on the type of content use [react-truncate](https://www.npmjs.com/package/react-truncate) or [react-truncate-html](https://www.npmjs.com/package/react-truncate-html) to truncate the collapsed content.
815

916
```js
10-
<NavLink className="wfp--link">Link</NavLink>
17+
import Truncate from 'react-truncate';
1118
```
19+
20+
```js
21+
<ReadMore
22+
collapsed={
23+
<Truncate lines={1} ellipsis="...">{moreText}</Truncate>
24+
}
25+
>
26+
Expanded content
27+
</ReadMore>
28+
```
29+
30+
### Fade & animate
31+
32+
The `maxHeight` prop will allow the content to be collapsed if it extends a specific height. It will only work without the `collapsed` prop.
33+
```js
34+
<ReadMore fade maxHeight={30}>
35+
The content
36+
</ReadMore>
37+
```

src/components/ReadMore/ReadMore-story.js

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,65 @@
22

33
import React from 'react';
44
import { storiesOf } from '@storybook/react';
5-
6-
import { withReadme } from 'storybook-readme';
75
import readme from './README.md';
86

97
import ReadMore from '../ReadMore';
10-
import Truncate from 'react-truncate-html';
8+
import Truncate from 'react-truncate';
9+
10+
import { withKnobs, boolean, number, text } from '@storybook/addon-knobs';
11+
import Button from '../Button';
1112

1213
const moreText =
13-
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';
14+
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor inviduntLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor inviduntet dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';
15+
16+
const collapsed = (
17+
<Truncate lines={1} ellipsis="...">
18+
{moreText}
19+
</Truncate>
20+
);
21+
22+
const props = {
23+
regular: () => ({
24+
className: 'some-class',
25+
collapsed: collapsed,
26+
children: text('The expanded content (children)', moreText),
27+
expandText: text('Expand text (expandText)', undefined),
28+
collapseText: text('Collapse text (collapseText)', undefined),
29+
maxHeight: number('Collapsed maximum Height (maxHeight)', undefined),
30+
fade: boolean('Fade (fade)', false),
31+
}),
32+
fade: () => ({
33+
className: 'some-class',
34+
children: text('The expanded content (children)', moreText),
35+
expandText: text('Expand text (expandText)', undefined),
36+
collapseText: text('Collapse text (collapseText)', undefined),
37+
maxHeight: number('Collapsed maximum Height (maxHeight)', 50),
38+
fade: boolean('Fade (fade)', true),
39+
}),
40+
customButton: () => ({
41+
className: 'some-class',
42+
children: text('The expanded content (children)', moreText),
43+
expandLink: <Button>Expand</Button>,
44+
collapseLink: <Button>Collapse</Button>,
45+
maxHeight: number('Collapsed maximum Height (maxHeight)', 50),
46+
fade: boolean('Fade (fade)', true),
47+
}),
48+
};
1449

1550
storiesOf('ReadMore', module)
16-
.addDecorator(withReadme([readme]))
17-
.add('Default (Draft)', () => (
18-
<ReadMore
19-
collapsed={
20-
<Truncate
21-
lines={3}
22-
dangerouslySetInnerHTML={{
23-
__html: moreText,
24-
}}
25-
/>
26-
}
27-
expanded={moreText}
28-
/>
29-
));
51+
.addDecorator(withKnobs)
52+
.add('Default (draft)', () => <ReadMore {...props.regular()} />, {
53+
info: {
54+
text: readme,
55+
},
56+
})
57+
.add('Fade & animate (draft)', () => <ReadMore {...props.fade()} />, {
58+
info: {
59+
text: readme,
60+
},
61+
})
62+
.add('Custom Buttons', () => <ReadMore {...props.customButton()} />, {
63+
info: {
64+
text: readme,
65+
},
66+
});

src/components/ReadMore/ReadMore-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { shallow } from 'enzyme';
55
describe('Link', () => {
66
describe('Renders as expected', () => {
77
const readMore = shallow(
8-
<ReadMore className="some-class" href="#" html="Lorem ipsum" />
8+
<ReadMore className="some-class">Content</ReadMore>
99
);
1010
it('should use the appropriate link class', () => {
11-
expect(readMore.hasClass('wfp--readmore')).toEqual(true);
11+
expect(readMore.hasClass('wfp--read-more')).toEqual(true);
1212
});
1313
it('should all for custom classes to be applied', () => {
1414
expect(readMore.hasClass('some-class')).toEqual(true);

src/components/ReadMore/ReadMore.js

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,150 @@
11
import PropTypes from 'prop-types';
2-
import React from 'react';
2+
import React, { Component } from 'react';
33
import classnames from 'classnames';
44

5-
import Truncate from 'react-truncate-html';
65
import Link from '../Link';
6+
import { iconCaretUp, iconCaretDown } from '@wfp/icons';
7+
import Icon from '../Icon';
8+
import classNames from 'classnames';
9+
import settings from '../../globals/js/settings';
710

8-
class ReadMore extends React.Component {
11+
const { prefix } = settings;
12+
13+
const MoreLink = ({ handleToggleClick, link, text, showMore }) => {
14+
if (link) {
15+
var clonedLink = React.cloneElement(link, {
16+
onClick: handleToggleClick,
17+
});
18+
return clonedLink;
19+
} else {
20+
return (
21+
<Link
22+
className={`${prefix}--read-more__trigger`}
23+
small
24+
onClick={handleToggleClick}>
25+
{text}
26+
<Icon
27+
icon={showMore ? iconCaretUp : iconCaretDown}
28+
width="10"
29+
height="10"
30+
description={showMore ? 'icon up' : 'icon down'}
31+
/>
32+
</Link>
33+
);
34+
}
35+
};
36+
37+
export default class ReadMore extends Component {
938
constructor(props) {
1039
super(props);
11-
this.state = { showMore: false };
40+
this.state = {
41+
showMore: false,
42+
innerHeight: 0,
43+
};
1244
}
1345

46+
static propTypes = {
47+
/**
48+
* Specify an optional className to be applied to the wrapper node
49+
*/
50+
className: PropTypes.string,
51+
/**
52+
* The content of the expanded element
53+
*/
54+
children: PropTypes.node.isRequired,
55+
/**
56+
* The content of the collapsed content (optional)
57+
*/
58+
collapsed: PropTypes.node,
59+
/**
60+
* A custom link for collapsing
61+
*/
62+
collapseLink: PropTypes.node,
63+
/**
64+
* A custom link for expanding
65+
*/
66+
expandLink: PropTypes.node,
67+
/**
68+
* Enables the fade effect when the content is collapsed (optional) when enabled collapsed will be ignored
69+
*/
70+
fade: PropTypes.bool,
71+
/**
72+
* The maximum height when the content is collapsed (optional)
73+
*/
74+
maxHeight: PropTypes.number,
75+
};
76+
77+
static defaultProps = {
78+
expandText: 'Read more',
79+
collapseText: 'Read less',
80+
};
81+
1482
handleToggleClick = e => {
1583
e.preventDefault();
84+
const innerHeight = this.divElement.clientHeight;
1685
this.setState(prevState => ({
1786
showMore: !prevState.showMore,
87+
innerHeight: innerHeight,
1888
}));
1989
};
2090

2191
render() {
22-
const { collapsed, className, expanded, moreText, html } = this.props;
23-
const { showMore } = this.state;
92+
const {
93+
collapseLink,
94+
collapseText,
95+
children,
96+
collapsed,
97+
className,
98+
expandLink,
99+
expandText,
100+
fade,
101+
maxHeight,
102+
} = this.props;
103+
const { innerHeight, showMore } = this.state;
104+
105+
const classNames = classnames(className, {
106+
[`${prefix}--read-more`]: true,
107+
[`${prefix}--read-more--expanded`]: showMore,
108+
[`${prefix}--read-more--fade`]: fade,
109+
[`${prefix}--read-more--max-height`]: maxHeight,
110+
});
111+
112+
const contentStyle = !maxHeight
113+
? {
114+
undefined,
115+
}
116+
: maxHeight && !showMore
117+
? {
118+
maxHeight: maxHeight,
119+
}
120+
: {
121+
maxHeight: innerHeight + 20,
122+
};
24123

25-
const classNames = classnames('wfp--readmore', className);
124+
const collapseStyle = showMore
125+
? {
126+
display: 'none',
127+
}
128+
: {
129+
display: 'block',
130+
};
26131

132+
console.log('sss', collapseLink, expandLink);
27133
return (
28134
<div className={classNames}>
29-
{showMore ? expanded : collapsed}
30-
<Link
31-
onClick={this.handleToggleClick}
32-
small
33-
style={{ marginTop: '0.9em' }}>
34-
{showMore ? 'Read less' : 'Read more'}
35-
</Link>
135+
<div className={`${prefix}--read-more__content`} style={contentStyle}>
136+
<div ref={divElement => (this.divElement = divElement)}>
137+
{(showMore || !collapsed) && children}
138+
{collapsed && <div style={collapseStyle}>{collapsed}</div>}
139+
</div>
140+
</div>
141+
<MoreLink
142+
handleToggleClick={this.handleToggleClick}
143+
showMore={showMore}
144+
link={showMore ? collapseLink : expandLink}
145+
text={showMore ? collapseText : expandText}
146+
/>
36147
</div>
37148
);
38149
}
39150
}
40-
41-
ReadMore.propTypes = {
42-
children: PropTypes.node,
43-
className: PropTypes.string,
44-
html: PropTypes.string,
45-
};
46-
47-
export default ReadMore;

0 commit comments

Comments
 (0)