Skip to content

Commit 3bd3943

Browse files
committed
feat(package): initial commit
1 parent d32e987 commit 3bd3943

File tree

12 files changed

+194
-69
lines changed

12 files changed

+194
-69
lines changed

README.md

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,4 @@
1-
# c2-[package]
2-
3-
## Creating a New Package From the Template
4-
1. Create a new GitHub repository without a readme
5-
2. Open Terminal
6-
3. Create a bare clone of this repository
7-
````
8-
$ git clone --bare git@github.com:ClearC2/package-template.git
9-
````
10-
4. Mirror push to your new repository
11-
````
12-
$ cd package-template.git
13-
$ git push --mirror <your new package git URI created in step 1>
14-
````
15-
5. Remove the temporary local repository you created in step 3
16-
````
17-
$ cd ..
18-
$ rm -rf package-template.git
19-
````
20-
6. Change your `package.json` to conform to your new package instead of `package-template`. This includes:
21-
- name
22-
- description
23-
- keywords
24-
- repository URL
25-
- bugs URL
26-
- homepage URL
27-
28-
7. Change the `README.md` to to conform to your package. This includes:
29-
- title
30-
- deleting the `Creating a New Package From the Template` section
31-
32-
8. Change the `alias` section in the `example/webpack.config.js` file to resolve your new package name (i.e. change `package-template` to your package name)
33-
34-
9. Change the import statement in `example/src/App.js` to use this new alias instead of `package-template`
35-
36-
10. Commit your changes to the `package.json`, `README.md`, `example/webpack.config.js`, and `example/src/App.js`, and push them up to GitHub. Your package is now ready for development
37-
38-
## TODO
39-
- [ ] Add Mocha/Chai/Enzyme/Istanbul test framework
1+
the data provider expects a function that receives props as the argument and returns an object.
2+
each top level key will be the name of the new prop that will be provided to the base component.
3+
each key will need to be given an 'api' function (required), and an 'args' array (optional).
4+
the api function will be fired with the given args, and again any time the args change.

example/src/App.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
import React from 'react'
2-
import PackageComponent, {PackageSubcomponent} from 'package-template'
2+
import dataProvider from 'c2-data-provider'
3+
import asyncSuccess from './asyncSuccess'
4+
import asyncFail from './asyncFail'
35

4-
export default function App () {
6+
function App (props) {
7+
const {propname, oopsie} = props
58
return (
6-
<div>
7-
<h1>{'C2 <Package>'}</h1>
8-
<PackageComponent />
9-
<PackageSubcomponent />
9+
<div className='page-container '>
10+
<div className='page-section'>
11+
<code>
12+
this.prop.propname = {JSON.stringify(propname)}
13+
</code>
14+
</div>
15+
<div className='page-section'>
16+
<code>
17+
this.prop.oopsie = {JSON.stringify(oopsie)}
18+
</code>
19+
</div>
1020
</div>
1121
)
1222
}
23+
24+
export default dataProvider(
25+
props => ({
26+
propname: {
27+
api: asyncSuccess,
28+
args: ['ABCDEFGHIJKLMNOPQRSTUVWXYZ123456']
29+
},
30+
oopsie: {
31+
api: asyncFail,
32+
args: [props.name]
33+
}
34+
})
35+
)(App)

example/src/asyncFail.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const asyncFail = arg => {
2+
return Promise.reject({errors: [{
3+
code: 777,
4+
message: `This is a mock failure and here is the arg you passed in - ${arg}`
5+
}]})
6+
}
7+
8+
export default asyncFail

example/src/asyncSuccess.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const asyncSuccess = id => {
2+
return Promise.resolve({data: {name: 'jake', id: id}})
3+
.then(resp => resp.data)
4+
}
5+
6+
export default asyncSuccess

example/src/example.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.page-container {
2+
width: 100vw;
3+
height: 100vh;
4+
display: flex;
5+
}
6+
7+
.page-section {
8+
display: flex;
9+
flex: 1;
10+
border: 1px solid black;
11+
justify-content: center;
12+
padding-top: 50px;
13+
}

example/webpack.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ let config = {
2929
},
3030
resolve: {
3131
alias: {
32-
'package-template': path.join(__dirname, '../src')
32+
'c2-data-provider': path.join(__dirname, '../src')
3333
}
3434
},
3535
module: {

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "package-template",
3-
"version": "0.0.0",
4-
"description": "package description",
2+
"name": "c2-data-provider",
3+
"version": "0.0.1",
4+
"description": "data provider for components",
55
"main": "dist/index.js",
66
"scripts": {
77
"start": "cd example && webpack-dev-server --inline --content-base dist --history-api-fallback --port 8083 --mode development",
@@ -34,7 +34,7 @@
3434
},
3535
"repository": {
3636
"type": "git",
37-
"url": "git+https://github.com/ClearC2/package-template.git"
37+
"url": "git+https://github.com/ClearC2/c2-data-provider.git"
3838
},
3939
"keywords": [
4040
"react",
@@ -44,9 +44,9 @@
4444
"author": "Clear C2, Inc.",
4545
"license": "ISC",
4646
"bugs": {
47-
"url": "https://github.com/ClearC2/package-template/issues"
47+
"url": "https://github.com/ClearC2/c2-data-provider/issues"
4848
},
49-
"homepage": "https://github.com/ClearC2/package-template#readme",
49+
"homepage": "https://github.com/ClearC2/c2-data-provider#readme",
5050
"devDependencies": {
5151
"babel-cli": "^6.26.0",
5252
"babel-core": "^6.26.0",

src/PackageComponent.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/PackageSubcomponent.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/Provider.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React, {Component} from 'react'
2+
import PropTypes from 'prop-types'
3+
4+
export default class Provider extends Component {
5+
static propTypes = {
6+
render: PropTypes.func,
7+
data: PropTypes.func
8+
}
9+
constructor (props) {
10+
super(props)
11+
let data = {}
12+
Object.keys(
13+
props.data(props)
14+
).forEach(key => (
15+
data[key] = {
16+
response: null,
17+
loading: false,
18+
errors: null
19+
})
20+
)
21+
this.state = { // initialize defaults for all APIs
22+
...data
23+
}
24+
}
25+
26+
fetchData = (key, api, args) => {
27+
if (!this.state[key].loading) { // spam prevention (will also pad this request if a developer mistakenly uses a rapidly changing arg and this starts spamming)
28+
this.setState(s => {
29+
delete s[key] // this object is not primitive, destroy the ref so componentDidUpdate of child behaves properly
30+
s[key] = {loading: true}
31+
return s
32+
}, () => {
33+
api(...args)
34+
.then(resp => this.updateAPIResponse(key, resp))
35+
.catch(resp => this.updateAPIError(key, resp))
36+
})
37+
}
38+
}
39+
40+
updateAPIResponse = (key, resp) => {
41+
this.setState(s => {
42+
delete s[key] // this object is not primitive, destroy the ref so componentDidUpdate of child behaves properly
43+
s[key] = {
44+
response: resp,
45+
loading: false,
46+
errors: null
47+
}
48+
return s
49+
})
50+
return resp
51+
}
52+
53+
updateAPIError = (key, resp) => {
54+
this.setState(s => {
55+
delete s[key] // this object is not primitive, destroy the ref so componentDidUpdate of child behaves properly
56+
let errors = resp
57+
if (resp.data && resp.data.errors) errors = resp.data.errors
58+
else if (resp.data && resp.data.error) errors = resp.data.error
59+
else if (resp.data) errors = resp.data
60+
else if (resp.errors) errors = resp.errors
61+
else if (resp.error) errors = resp.error // just trying to catch pretty common rejection shapes
62+
s[key] = {
63+
response: null,
64+
loading: false,
65+
errors
66+
}
67+
return s
68+
})
69+
return resp
70+
}
71+
72+
fetchAllData = () => {
73+
const {data} = this.props
74+
Object.keys(data(this.props)).map(key => {
75+
const {api = () => null, args = []} = data(this.props)[key]
76+
this.fetchData(key, api, args)
77+
})
78+
}
79+
80+
determineAPIRefetch = last => { // if the args of any API have changed, refire the API and refresh the data
81+
const {data} = this.props
82+
const newAPIs = data(this.props)
83+
const oldAPIs = last.data(last)
84+
const calls = {}
85+
Object.keys(newAPIs).forEach(key => {
86+
if (oldAPIs[key]) {
87+
let {args: oldArgs = []} = oldAPIs[key]
88+
let {args: newArgs = []} = newAPIs[key]
89+
if (newArgs.some((arg, i) => arg !== oldArgs[i])) {
90+
calls[key] = newAPIs[key]
91+
}
92+
}
93+
})
94+
if (Object.keys(calls).length > 0) {
95+
Object.keys(calls).map(key => {
96+
const {api = () => null, args = []} = calls[key]
97+
this.fetchData(key, api, args)
98+
})
99+
}
100+
}
101+
102+
componentDidUpdate = p => {
103+
this.determineAPIRefetch(p)
104+
}
105+
106+
componentDidMount () {
107+
this.fetchAllData()
108+
}
109+
110+
render () {
111+
const {render: C, data, ...rest} = this.props
112+
const loading = Object.keys(this.state).some(api => this.state[api].loading === true)
113+
return <C loading={loading} {...rest} {...this.state} refreshData={this.fetchAllData} />
114+
}
115+
}

0 commit comments

Comments
 (0)