Skip to content

Commit

Permalink
Merge c8c78bc into 332dfde
Browse files Browse the repository at this point in the history
  • Loading branch information
abdennour committed Feb 13, 2018
2 parents 332dfde + c8c78bc commit 8794faf
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 23 deletions.
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,83 @@ import {CSVLink} from 'react-csv';

```

### - **onClick** Props:
`onClick` is another props restricted to `CSVLink`.

If it is defined, it means 3 things:

1 - It will run at the top of the click handling logic.
2 - [Sync] If it returns an explicit `false`, the return will be interpreted as a claim to stop the click handling, then, the next logic will not be executed if so.
3 - [Async] If it is async, "done" argument must be called if you want to invoke the handling of the component. (check examples below)
4 - [Async] If it is async (includes api call, timeout,... ) and it calls done with `false` will be interpreted as a claim to stop the click handling, then, the next logic will not be executed if so.


**examples**

- 🔬 Sync + Proceed

```js
import {CSVLink} from 'react-csv';

<CSVLink data={data}
onClick={() => {
console.log('You click the link') ; // 👍🏻 Your click handling logic
}}>
Download me
</CSVLink>

```

- 🔬 Sync + Don't Proceed

```js
import {CSVLink} from 'react-csv';

<CSVLink data={data}
onClick={(event) => {
console.log('You click the link') ;
return false; // 👍🏻 You are stopping the handling of component
}}>
Download me
</CSVLink>

```

- 🔬 ASync + Proceed

```js
import {CSVLink} from 'react-csv';

<CSVLink data={data}
asyncOnClick={true}
onClick={(event, done) => {
axios.post('/spy/user').then(() => {
done(); // REQUIRED to invoke the logic of component
});
}}>
Download me
</CSVLink>

```

- 🔬 Async + Don't Proceed

```js
import {CSVLink} from 'react-csv';

<CSVLink data={data}
asyncOnClick={true}
onClick={(event, done) => {
axios.post('/spy/user').then(() => {
done(false); // Don't Proceed
});
}}>
Download me
</CSVLink>

```



## 2. CSVDownload Component:

Expand Down
80 changes: 62 additions & 18 deletions src/components/Link.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,97 @@
import React from 'react';
import {buildURI, toCSV} from '../core';
import { buildURI, toCSV } from '../core';
import {
defaultProps as commonDefaultProps,
propTypes as commonPropTypes} from '../metaProps';
defaultProps as commonDefaultProps,
propTypes as commonPropTypes
} from '../metaProps';

/**
*
* @example ../../sample-site/csvlink.example.md
*/
class CSVLink extends React.Component {

static defaultProps = commonDefaultProps;
static propTypes = commonPropTypes;

constructor(props) {
super(props);
this.buildURI= this.buildURI.bind(this);
this.buildURI = this.buildURI.bind(this);
}

buildURI() {
return buildURI(...arguments);
}

/**
/**
* In IE11 this method will trigger the file download
*/
handleLegacy(evt, data, headers, separator, filename) {
handleLegacy(event, data, headers, separator, filename) {
// If this browser is IE 11, it does not support the `download` attribute
if (window.navigator.msSaveOrOpenBlob) {
// Stop the click propagation
evt.preventDefault()
event.preventDefault();

let blob = new Blob([toCSV(data, headers, separator)]);
window.navigator.msSaveBlob(blob, filename);

return false;
}
}

handleAsyncClick(event, ...args) {
const done = proceed => {
if (proceed === false) {
event.preventDefault();
return;
}
this.handleLegacy(event, ...args);
};

let blob = new Blob([toCSV(data, headers, separator)])
window.navigator.msSaveBlob(blob, filename)
this.props.onClick(event, done);
}

return false
handleSyncClick(event, ...args) {
const stopEvent = this.props.onClick(event) === false;
if (stopEvent) {
event.preventDefault();
return;
}
this.handleLegacy(event, ...args);
}

handleClick(...args) {
return event => {
if (typeof this.props.onClick === 'function') {
return this.props.asyncOnClick
? handleAsyncClick(event, ...args)
: handleSyncClick(event, ...args);
}
this.handleLegacy(event, ...args);
};
}

render(){
const {data, headers, separator, filename, uFEFF, children , ...rest} = this.props;
render() {
const {
data,
headers,
separator,
filename,
uFEFF,
children,
onClick,
...rest
} = this.props;
return (
<a download={filename} {...rest}
ref={link => (this.link = link)}
href={this.buildURI(data, uFEFF, headers, separator)}
onClick={evt => this.handleLegacy(evt, data, headers, separator, filename)}>
<a
download={filename}
{...rest}
ref={link => (this.link = link)}
href={this.buildURI(data, uFEFF, headers, separator)}
onClick={this.handleClick(data, headers, separator, filename)}
>
{children}
</a>
)
);
}
}

Expand Down
9 changes: 6 additions & 3 deletions src/metaProps.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import {string, array, oneOfType, bool} from 'prop-types';
import { string, array, oneOfType, bool, func } from 'prop-types';


export const propTypes = {
Expand All @@ -8,13 +8,16 @@ export const propTypes = {
target: string,
separator: string,
filename: string,
uFEFF: bool
uFEFF: bool,
onClick: func,
asyncOnClick: bool
};

export const defaultProps = {
separator: ',',
filename: 'generatedBy_react-csv.csv',
uFEFF: true
uFEFF: true,
asyncOnClick: false
};

export const PropsNotForwarded = [
Expand Down
7 changes: 5 additions & 2 deletions test/ComponentsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('CSVLink', () => {
it(`assigns a download filename`, () => {
const filename= "persons.csv";
const wrapper = mount( <CSVLink {...minProps} filename={filename} > here </CSVLink>);
expect(wrapper.find('a').get(0).getAttribute('download')).toEqual(filename);
expect(wrapper.find('a').get(0).getAttribute('download')).toEqual(filename);
});

it(`renders anchor tag`, () => {
Expand Down Expand Up @@ -82,12 +82,15 @@ describe('CSVLink', () => {
expect(actualAnchorAttrs).toInclude(extraProps);

})

it(`generates "onClick" event for IE11 support`, () => {
const wrapper = shallow( <CSVLink {...minProps}> here </CSVLink>);
wrapper.find(`a`).simulate(`click`, { preventDefault() {}})
expect(wrapper.find(`a`).get(0).props).toContainKey('onClick');
});
// TODO write unit-tests for handleClick
// TODO write unit-tests for handleSyncClick
// TODO write unit-tests for handleAsyncClick

});

Expand Down

0 comments on commit 8794faf

Please sign in to comment.