Skip to content

Commit

Permalink
Merge pull request #19 from nikrowell/plugins
Browse files Browse the repository at this point in the history
Extending the component API
  • Loading branch information
estrattonbailey authored Feb 12, 2020
2 parents 8319caa + 6e33ad1 commit ddf400d
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 6 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,26 @@ Given the below, `picoapp` will scan the DOM for both `data-component` and
app.mount(["data-component", "data-util"]);
```

## Plugins

The `picoapp` instance allows you to extend the component API through plugins. Plugins are functions that return objects, which then get merged into the `context` object passed to your `component`. Note that name conflicts with plugin properties will always be overriden by [picoapp's context](#state-&-events).

To define plugins, pass a function to the `use` method. The example below adds a `props` object extracted from the component node's `data-props` attribute:
```javascript
app.use(node => {
const props = JSON.parse(node.dataset.props || '{}')
return {props}
})
```

And then acccess plugin extensions from your component:
```javascript
const foo = component(node, ctx) => {
const { images = [] } = ctx.props
console.log(`start preloading ${images.length} images...`)
})
```

## License

MIT License © [Eric Bailey](https://estrattonbailey.com)
12 changes: 10 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function component (create) {
}
}

export function picoapp (components = {}, initialState = {}) {
export function picoapp (components = {}, initialState = {}, plugins = []) {
const evx = create(initialState)

let cache = []
Expand All @@ -37,6 +37,10 @@ export function picoapp (components = {}, initialState = {}) {
if (!isObj(index)) throw 'components should be an object'
Object.assign(components, index)
},
use (fn) {
if (!isFn(fn)) throw 'plugins should be a function'
plugins.push(fn);
},
hydrate (data) {
return evx.hydrate(data)
},
Expand All @@ -58,7 +62,11 @@ export function picoapp (components = {}, initialState = {}) {
node.removeAttribute(attr) // so can't be bound twice

try {
const instance = comp(node, evx)
const ext = plugins.reduce((res, fn) => {
const obj = fn(node, evx)
return isObj(obj) ? Object.assign(res, obj) : res
}, {})
const instance = comp(node, {...ext, ...evx})
isFn(instance.unmount) && cache.push(instance)
} catch (e) {
console.error(e)
Expand Down
34 changes: 30 additions & 4 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import test from 'ava'
import { picoapp, component } from './dist/picoapp.js'

const createNode = attr => ({
dataset: {
props: '{"hello": "World"}'
},
getAttribute () {
return attr
},
Expand Down Expand Up @@ -64,7 +67,7 @@ test('mount', t => {
})
test('unmount', t => {
t.plan(4)

const app = picoapp({
foo: component((node, ctx) => {
ctx.on('foo', () => t.truthy(1))
Expand All @@ -76,10 +79,33 @@ test('unmount', t => {
})

app.mount()

app.emit('foo')

app.unmount()

app.emit('foo')
})
test('plugins', t => {
t.plan(2)

function testContext(ctx) {
const internals = ['getState', 'hydrate', 'on', 'emit']
const preserved = internals.every(key => typeof ctx[key] === 'function')
t.true(preserved)
}

const app = picoapp({
foo: component((node, ctx) => {
t.true(ctx.props.hello === 'World')
testContext(ctx);
})
})

app.use((node, ctx) => ({
getState: undefined,
props: JSON.parse(node.dataset.props || '{}')
}))

app.mount()
})

0 comments on commit ddf400d

Please sign in to comment.