Skip to content

Commit

Permalink
feat(ReadMore): ReadMore Component refactored (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
Utzel-Butzel committed Feb 8, 2019
1 parent a4705cb commit 4fb8b86
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 68 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
"react-table": "^6.8.0",
"react-test-renderer": "^16.4.0",
"react-tippy": "^1.2.3",
"react-truncate": "^2.4.0",
"react-truncate-html": "^0.1.7",
"redux-form": "^7.4.2",
"redux-form-website-template": "^0.0.112",
Expand Down
36 changes: 31 additions & 5 deletions src/components/ReadMore/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
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.

Links can have the same properties as an `<a>`-Element.
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.

### Use with react-router
### Use with custom content for collapsed and expanded state

Use the Link styling by adding the className `wfp--link` to `<NavLink />`
The `collapsed` props content will be displayed if the content is collapsed.
```js
<ReadMore collapsed="Collapsed content">
Expanded content
</ReadMore>
```
### Truncate text

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.

```js
<NavLink className="wfp--link">Link</NavLink>
import Truncate from 'react-truncate';
```

```js
<ReadMore
collapsed={
<Truncate lines={1} ellipsis="...">{moreText}</Truncate>
}
>
Expanded content
</ReadMore>
```

### Fade & animate

The `maxHeight` prop will allow the content to be collapsed if it extends a specific height. It will only work without the `collapsed` prop.
```js
<ReadMore fade maxHeight={30}>
The content
</ReadMore>
```
73 changes: 55 additions & 18 deletions src/components/ReadMore/ReadMore-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,65 @@

import React from 'react';
import { storiesOf } from '@storybook/react';

import { withReadme } from 'storybook-readme';
import readme from './README.md';

import ReadMore from '../ReadMore';
import Truncate from 'react-truncate-html';
import Truncate from 'react-truncate';

import { withKnobs, boolean, number, text } from '@storybook/addon-knobs';
import Button from '../Button';

const moreText =
'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.';
'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.';

const collapsed = (
<Truncate lines={1} ellipsis="...">
{moreText}
</Truncate>
);

const props = {
regular: () => ({
className: 'some-class',
collapsed: collapsed,
children: text('The expanded content (children)', moreText),
expandText: text('Expand text (expandText)', undefined),
collapseText: text('Collapse text (collapseText)', undefined),
maxHeight: number('Collapsed maximum Height (maxHeight)', undefined),
fade: boolean('Fade (fade)', false),
}),
fade: () => ({
className: 'some-class',
children: text('The expanded content (children)', moreText),
expandText: text('Expand text (expandText)', undefined),
collapseText: text('Collapse text (collapseText)', undefined),
maxHeight: number('Collapsed maximum Height (maxHeight)', 50),
fade: boolean('Fade (fade)', true),
}),
customButton: () => ({
className: 'some-class',
children: text('The expanded content (children)', moreText),
expandLink: <Button>Expand</Button>,
collapseLink: <Button>Collapse</Button>,
maxHeight: number('Collapsed maximum Height (maxHeight)', 50),
fade: boolean('Fade (fade)', true),
}),
};

storiesOf('ReadMore', module)
.addDecorator(withReadme([readme]))
.add('Default (Draft)', () => (
<ReadMore
collapsed={
<Truncate
lines={3}
dangerouslySetInnerHTML={{
__html: moreText,
}}
/>
}
expanded={moreText}
/>
));
.addDecorator(withKnobs)
.add('Default (draft)', () => <ReadMore {...props.regular()} />, {
info: {
text: readme,
},
})
.add('Fade & animate (draft)', () => <ReadMore {...props.fade()} />, {
info: {
text: readme,
},
})
.add('Custom Buttons', () => <ReadMore {...props.customButton()} />, {
info: {
text: readme,
},
});
4 changes: 2 additions & 2 deletions src/components/ReadMore/ReadMore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { shallow } from 'enzyme';
describe('Link', () => {
describe('Renders as expected', () => {
const readMore = shallow(
<ReadMore className="some-class" href="#" html="Lorem ipsum" />
<ReadMore className="some-class">Content</ReadMore>
);
it('should use the appropriate link class', () => {
expect(readMore.hasClass('wfp--readmore')).toEqual(true);
expect(readMore.hasClass('wfp--read-more')).toEqual(true);
});
it('should all for custom classes to be applied', () => {
expect(readMore.hasClass('some-class')).toEqual(true);
Expand Down
147 changes: 125 additions & 22 deletions src/components/ReadMore/ReadMore.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,150 @@
import PropTypes from 'prop-types';
import React from 'react';
import React, { Component } from 'react';
import classnames from 'classnames';

import Truncate from 'react-truncate-html';
import Link from '../Link';
import { iconCaretUp, iconCaretDown } from '@wfp/icons';
import Icon from '../Icon';
import classNames from 'classnames';
import settings from '../../globals/js/settings';

class ReadMore extends React.Component {
const { prefix } = settings;

const MoreLink = ({ handleToggleClick, link, text, showMore }) => {
if (link) {
var clonedLink = React.cloneElement(link, {
onClick: handleToggleClick,
});
return clonedLink;
} else {
return (
<Link
className={`${prefix}--read-more__trigger`}
small
onClick={handleToggleClick}>
{text}
<Icon
icon={showMore ? iconCaretUp : iconCaretDown}
width="10"
height="10"
description={showMore ? 'icon up' : 'icon down'}
/>
</Link>
);
}
};

export default class ReadMore extends Component {
constructor(props) {
super(props);
this.state = { showMore: false };
this.state = {
showMore: false,
innerHeight: 0,
};
}

static propTypes = {
/**
* Specify an optional className to be applied to the wrapper node
*/
className: PropTypes.string,
/**
* The content of the expanded element
*/
children: PropTypes.node.isRequired,
/**
* The content of the collapsed content (optional)
*/
collapsed: PropTypes.node,
/**
* A custom link for collapsing
*/
collapseLink: PropTypes.node,
/**
* A custom link for expanding
*/
expandLink: PropTypes.node,
/**
* Enables the fade effect when the content is collapsed (optional) when enabled collapsed will be ignored
*/
fade: PropTypes.bool,
/**
* The maximum height when the content is collapsed (optional)
*/
maxHeight: PropTypes.number,
};

static defaultProps = {
expandText: 'Read more',
collapseText: 'Read less',
};

handleToggleClick = e => {
e.preventDefault();
const innerHeight = this.divElement.clientHeight;
this.setState(prevState => ({
showMore: !prevState.showMore,
innerHeight: innerHeight,
}));
};

render() {
const { collapsed, className, expanded, moreText, html } = this.props;
const { showMore } = this.state;
const {
collapseLink,
collapseText,
children,
collapsed,
className,
expandLink,
expandText,
fade,
maxHeight,
} = this.props;
const { innerHeight, showMore } = this.state;

const classNames = classnames(className, {
[`${prefix}--read-more`]: true,
[`${prefix}--read-more--expanded`]: showMore,
[`${prefix}--read-more--fade`]: fade,
[`${prefix}--read-more--max-height`]: maxHeight,
});

const contentStyle = !maxHeight
? {
undefined,
}
: maxHeight && !showMore
? {
maxHeight: maxHeight,
}
: {
maxHeight: innerHeight + 20,
};

const classNames = classnames('wfp--readmore', className);
const collapseStyle = showMore
? {
display: 'none',
}
: {
display: 'block',
};

console.log('sss', collapseLink, expandLink);
return (
<div className={classNames}>
{showMore ? expanded : collapsed}
<Link
onClick={this.handleToggleClick}
small
style={{ marginTop: '0.9em' }}>
{showMore ? 'Read less' : 'Read more'}
</Link>
<div className={`${prefix}--read-more__content`} style={contentStyle}>
<div ref={divElement => (this.divElement = divElement)}>
{(showMore || !collapsed) && children}
{collapsed && <div style={collapseStyle}>{collapsed}</div>}
</div>
</div>
<MoreLink
handleToggleClick={this.handleToggleClick}
showMore={showMore}
link={showMore ? collapseLink : expandLink}
text={showMore ? collapseText : expandText}
/>
</div>
);
}
}

ReadMore.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
html: PropTypes.string,
};

export default ReadMore;
Loading

0 comments on commit 4fb8b86

Please sign in to comment.