├── .github ├── ISSUE_TEMPLATE └── PULL_REQUEST_TEMPLATE ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── demo ├── accounts-demo.csv └── src │ └── index.js ├── nwb.config.js ├── package-lock.json ├── package.json ├── src └── index.js └── tests ├── .eslintrc └── index-test.js /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Expected Behavior 4 | 5 | 6 | 7 | ## Current Behavior 8 | 9 | 10 | 11 | ## Possible Solution 12 | 13 | 14 | 15 | ## Steps to Reproduce (for bugs) 16 | 17 | 18 | 1. 19 | 2. 20 | 3. 21 | 4. 22 | 23 | ## Context 24 | 25 | 26 | 27 | ## Your Environment 28 | 29 | * Version used: 30 | * Environment name and version (e.g. Chrome 39, node.js 5.4): 31 | * Operating System and version (desktop or mobile): 32 | * Link to your project: 33 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | #### What is the purpose of this pull request? 2 | 3 | 4 | #### What problem is this solving? 5 | 6 | 7 | #### How should this be manually tested? 8 | 9 | #### Screenshots or example usage 10 | 11 | #### Types of changes 12 | - [ ] Bug fix (non-breaking change which fixes an issue) 13 | - [ ] New feature (non-breaking change which adds functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 15 | - [ ] Requires change to documentation, which has been updated accordingly. 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /demo/dist 3 | /es 4 | /lib 5 | /node_modules 6 | /umd 7 | npm-debug.log* 8 | .DS_Store 9 | *.tgz 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - 6 6 | 7 | before_install: 8 | - npm install codecov.io coveralls 9 | 10 | after_success: 11 | - cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js 12 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 13 | 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | [Node.js](http://nodejs.org/) >= v4 must be installed. 4 | 5 | ## Installation 6 | 7 | - Running `npm install` in the components's root directory will install everything you need for development. 8 | 9 | ## Demo Development Server 10 | 11 | - `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading. 12 | 13 | ## Running Tests 14 | 15 | - `npm test` will run the tests once. 16 | 17 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`. 18 | 19 | - `npm run test:watch` will run the tests on every change. 20 | 21 | ## Building 22 | 23 | - `npm run build` will build the component for publishing to npm and also bundle the demo app. 24 | 25 | - `npm run clean` will delete built resources. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 VTEX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Csv Parse 2 | 3 | Goal: Parse content of a csv file. 4 | 5 | ## Example 6 | 7 | From: 8 | 9 | ``` 10 | Account,Balance,Document,Document Type,Limit,Description,Email 11 | acc1,0,3i563784658,cpf,2000,,k@email.com 12 | acc2,10,3468723468,cpf,10000,Some text,j@email.com 13 | ``` 14 | 15 | To: 16 | 17 | ``` 18 | [ 19 | { 20 | account: "acc1", 21 | balance: "0", 22 | document: "3i563784658", 23 | documentType: "cpf", 24 | limit: "2000", 25 | description: "", 26 | email: "k@gmail.com" 27 | }, 28 | { 29 | account: "acc2", 30 | balance: "10", 31 | document: "3468723468", 32 | documentType: "cpf", 33 | limit: "10000", 34 | description: "Some text", 35 | email: "j@email.com" 36 | } 37 | ] 38 | ``` 39 | 40 | Given the following keys: 41 | 42 | ``` 43 | const keys = [ 44 | 'account', 45 | 'balance', 46 | 'document', 47 | 'documentType', 48 | 'limit', 49 | 'description', 50 | 'email' 51 | ] 52 | ``` 53 | 54 | Inspiration: [paypal/downshift](https://github.com/paypal/downshift) 55 | 56 | Development structure: [github.com/insin/nwb](https://github.com/insin/nwb) 57 | 58 | ## Development 59 | 60 | | Action | Command | 61 | | -------------- | ----------------------------- | 62 | | Install | `npm i -g nwb & npm i` | 63 | | Start | `npm start` | 64 | | Build | `nwb build` | 65 | | Local test | `npm pack` | 66 | | Publish to npm | `npm publish --access public` | 67 | 68 | ## Usage 69 | 70 | ``` 71 | npm install @vtex/react-csv-parse --save 72 | ``` 73 | 74 | ```js 75 | import CsvParse from '@vtex/react-csv-parse' 76 | ``` 77 | 78 | ```jsx 79 | handleData = data => { 80 | this.setState({ data }) 81 | } 82 | ``` 83 | 84 | ```jsx 85 | render() { 86 | const keys = [ 87 | "header1", 88 | "header2", 89 | "header3", 90 | "header4", 91 | "header5", 92 | ] 93 | 94 | return ( 95 | } 100 | /> 101 | ) 102 | } 103 | ``` 104 | 105 | `CsvParse` is the only component. It doesn't render anything itself, it just 106 | calls the child function and renders that. Wrap everything in 107 | `{/* your function here! */}`. 108 | 109 | ## Props 110 | 111 | | Prop name | Type | Default | Required | Description | 112 | | ---------------- | ----- | ------- | -------- | ------------------------------------------------------------------------------ | 113 | | `keys` | array | | true | The keys used to create the objects. | 114 | | `onDataUploaded` | func | | true | Callback function with the data parsed as parameter. | 115 | | `onError` | func | | false | Callback function with the following data: `{ err, file, inputElem, reason }`. | 116 | 117 | ### Data split rules 118 | 119 | Based on [Papaparse](https://github.com/mholt/PapaParse). 120 | -------------------------------------------------------------------------------- /demo/accounts-demo.csv: -------------------------------------------------------------------------------- 1 | Account (read-only) Balance (read-only) Document (read-only) Document Type (read-only) Credit limit Last Update (read-only) Description (read-only) Email (read-only) Visible credit limit (read-only) 2 | ambienteqa 34080 144.433.857-999 CPF 0 2017-05-11T19:38:17.9964469Z foo@bar.com 0 3 | ambienteqa 120 550066 CPF 120 2017-08-01T15:20:58.3358488Z foo@bar.com 120 4 | ambienteqa 90 777999531 CPF 90 2017-08-04T13:54:18.4089478Z foo@bar.com 90 5 | ambienteqa 220 123.456.778-01 CPF 240 2017-08-07T17:40:12.8552797Z test@test.com 240 6 | ambienteqa 0 556677788 CPF 0 2017-08-21T13:01:05.2280273Z foo@bar.com 0 7 | ambienteqa 0 5566742456 CPF 0 2017-08-21T13:01:05.2280273Z foo@bar.com 0 8 | ambienteqa 220 123.456.778-11 CPF 240 2017-08-21T13:01:05.2280273Z test@test.com 240 9 | ambienteqa 0 556.676.676-7676 CPF 0 2017-08-21T13:01:05.2280273Z foo@bar.com 0 10 | ambienteqa 0 435.652.211-11111 CPF 0 2017-08-21T13:01:05.2280273Z foo@bar.com 0 11 | ambienteqa 171.4285714286 994.400.631-117777 CPF 171.4285714286 2017-08-22T22:05:52.4294294Z foo@bar.com 171.4285714286 12 | ambienteqa 200 999.995.555-333ss1 CPF 200 2017-08-23T13:50:35.1368502Z foo@bar.com 200 13 | ambienteqa 600 999.995.555-3331 CPF 600 2017-08-23T13:51:37.2810363Z foo@bar.com 600 14 | ambienteqa 100 994.400.631-113333 CPF 100 2017-08-23T23:56:29.2173738Z foo@bar.com 100 15 | ambienteqa 99990 11111111111 Other 100000 2017-08-24T15:57:57.4227658Z testrunner@gmail.com 100000 16 | ambienteqa 0 999.995.555-33322 CPF 0 2017-08-25T14:28:04.7460977Z foo@bar.com 0 17 | ambienteqa 0 999.995.555-33344 CPF 0 2017-08-25T14:30:28.7789056Z foo@bar.com 0 18 | ambienteqa 0 999.995.455-33456 CPF 0 2017-08-28T17:44:54.835415Z foo@bar.com 0 19 | ambienteqa 1939 061.003.267-43 CPF 2395 2017-09-13T19:17:35.9697041Z foo@bar.com 2395 20 | ambienteqa 0 123.456.789-10 CPF 0 2017-09-18T17:10:26.8738001Z foo@bar.com 0 21 | ambienteqa 240 144.433.857-999 CPF 240 2017-09-18T17:39:41.7873198Z foo@bar.com 240 22 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import CsvParse from '../../src' 4 | 5 | class Demo extends React.Component { 6 | constructor() { 7 | super() 8 | 9 | this.state = { 10 | data: null, 11 | error: null, 12 | } 13 | } 14 | 15 | handleData = data => { 16 | this.setState({ data }) 17 | } 18 | 19 | handleError = error => { 20 | this.setState({ error }) 21 | } 22 | 23 | render() { 24 | const keys = [ 25 | 'account', 26 | 'balance', 27 | 'document', 28 | 'documentType', 29 | 'creditLimit', 30 | 'lastUpdate', 31 | 'createdAt', 32 | 'description', 33 | 'availableBalance', 34 | 'preAuthorizedBalance', 35 | 'email', 36 | 'tolerance', 37 | ] 38 | 39 | return ( 40 |
41 |

Demo React Csv Parse

42 | 43 | } 48 | /> 49 | 50 | {this.state.data && ( 51 |
{JSON.stringify(this.state.data, null, 2)}
52 | )} 53 | 54 | {this.state.error && ( 55 |
{JSON.stringify(this.state.error, null, 2)}
56 | )} 57 |
58 | ) 59 | } 60 | } 61 | 62 | render(, document.querySelector('#demo')) 63 | -------------------------------------------------------------------------------- /nwb.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'react-component', 3 | npm: { 4 | esModules: true, 5 | umd: false, 6 | }, 7 | babel: { 8 | plugins: ['ramda'], 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vtex/react-csv-parse", 3 | "version": "3.0.2", 4 | "description": "react-csv-parse React component", 5 | "main": "lib/index.js", 6 | "module": "es/index.js", 7 | "files": [ 8 | "css", 9 | "es", 10 | "lib", 11 | "umd" 12 | ], 13 | "scripts": { 14 | "build": "nwb build-react-component", 15 | "clean": "nwb clean-module && nwb clean-demo", 16 | "start": "nwb serve-react-demo", 17 | "test": "nwb test-react", 18 | "test:coverage": "nwb test-react --coverage", 19 | "test:watch": "nwb test-react --server" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git://github.com:vtex/react-csv-parse.git" 24 | }, 25 | "dependencies": { 26 | "papaparse": "^4.5.0", 27 | "ramda": "^0.25.0" 28 | }, 29 | "peerDependencies": { 30 | "react": "15.x" 31 | }, 32 | "devDependencies": { 33 | "babel-plugin-ramda": "^1.5.0", 34 | "expect": "^22.4.3", 35 | "nwb": "^0.21.5", 36 | "react": "^15.6.2", 37 | "react-dom": "^15.6.2" 38 | }, 39 | "author": "", 40 | "license": "MIT", 41 | "keywords": [ 42 | "react-component" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import Papa from 'papaparse' 5 | import { apply, compose, lift, splitAt, zipObj } from 'ramda' 6 | 7 | class CsvParse extends React.Component { 8 | handleFile = event => { 9 | const file = event.target.files[0] 10 | const keys = this.props.keys 11 | const onDataUploaded = this.props.onDataUploaded 12 | const onError = this.props.onError 13 | 14 | Papa.parse(file, { 15 | skipEmptyLines: true, 16 | error: function(err, file, inputElem, reason) { 17 | onError({ err, file, inputElem, reason }) 18 | }, 19 | complete: function(results) { 20 | const data = results.data 21 | 22 | // remove display headers 23 | data.shift() 24 | 25 | // add api headers 26 | data.unshift(keys) 27 | 28 | // convert arrays to objects 29 | const formatedResult = compose( 30 | apply(lift(zipObj)), 31 | splitAt(1), 32 | )(data) 33 | 34 | // send result to state 35 | onDataUploaded(formatedResult) 36 | }, 37 | }) 38 | } 39 | 40 | render() { 41 | return this.props.render(this.handleFile) 42 | } 43 | } 44 | 45 | CsvParse.propTypes = { 46 | keys: PropTypes.array.isRequired, 47 | onDataUploaded: PropTypes.func.isRequired, 48 | onError: PropTypes.func, 49 | } 50 | 51 | export default CsvParse 52 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/index-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import expect from 'expect' 3 | import { renderToStaticMarkup as render } from 'react-dom/server' 4 | 5 | import CsvParse from '../src/' 6 | 7 | const mockFunc = () => true 8 | const keys = ['account', 'balance'] 9 | 10 | describe('CsvParse', () => { 11 | it('renders an input with type="file"', () => { 12 | expect( 13 | render( 14 | } 18 | />, 19 | ), 20 | ).toContain('') 21 | }) 22 | }) 23 | --------------------------------------------------------------------------------