Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-tunnels

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-tunnels - npm Package Compare versions

Comparing version 1.0.2 to 1.1.0

6

lib/Tunnel.js

@@ -52,5 +52,5 @@ 'use strict';

}, {
key: 'componentWillUpdate',
value: function componentWillUpdate(nextProps) {
this.setTunnelProps(nextProps);
key: 'componentDidUpdate',
value: function componentDidUpdate() {
this.setTunnelProps(this.props);
}

@@ -57,0 +57,0 @@ }, {

@@ -67,2 +67,3 @@ 'use strict';

renderChildren = _props.children,
Tag = _props.component,
multiple = _props.multiple;

@@ -74,7 +75,7 @@

if (Array.isArray(tunnelProps) || multiple) {
return !tunnelProps ? (0, _react.createElement)(renderChildren, { items: [] }) : (0, _react.createElement)(renderChildren, {
return !tunnelProps ? renderChildren({ items: [] }) : renderChildren({
items: Array.isArray(tunnelProps) ? tunnelProps : [tunnelProps]
});
} else {
return (0, _react.createElement)(renderChildren, tunnelProps || {});
return renderChildren(tunnelProps || {});
}

@@ -88,3 +89,3 @@ }

return _react2.default.createElement(
_react.Fragment,
Tag,
null,

@@ -101,5 +102,9 @@ tunnelProps.children

children: _propTypes2.default.func,
component: _propTypes2.default.oneOfType([_propTypes2.default.node, _propTypes2.default.symbol]),
id: _propTypes2.default.string.isRequired,
multiple: _propTypes2.default.bool
};
TunnelPlaceholder.defaultProps = {
component: _react.Fragment
};
TunnelPlaceholder.contextTypes = {

@@ -106,0 +111,0 @@ tunnelState: _propTypes2.default.object

{
"name": "react-tunnels",
"version": "1.0.2",
"version": "1.1.0",
"description": "A easy way to communicate rendering logic and data to ancestor components in React.",
"main": "./lib/index.js",
"repository": "javivelasco/react-tunnels",
"scripts": {

@@ -23,22 +24,22 @@ "build": "babel ./src --out-dir ./lib",

"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.1",
"babel-jest": "^22.1.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"eslint": "^4.16.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.6.1",
"jest": "^22.1.4",
"prettier": "^1.10.2",
"prop-types": "^15.6.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"rimraf": "^2.6.2"
"babel-cli": "6.26.0",
"babel-core": "6.26.3",
"babel-eslint": "9.0.0",
"babel-jest": "23.4.2",
"babel-preset-env": "1.7.0",
"babel-preset-react": "6.24.1",
"babel-preset-stage-2": "6.24.1",
"enzyme": "3.5.0",
"enzyme-adapter-react-16": "1.3.1",
"eslint": "5.5.0",
"eslint-config-prettier": "3.0.1",
"eslint-plugin-import": "2.14.0",
"eslint-plugin-prettier": "2.6.2",
"eslint-plugin-react": "7.11.1",
"jest": "23.5.0",
"prettier": "1.14.2",
"prop-types": "15.6.2",
"react": "16.4.2",
"react-dom": "16.4.2",
"rimraf": "2.6.2"
},

@@ -45,0 +46,0 @@ "peerDependencies": {

@@ -13,17 +13,17 @@ # React Tunnels 🚇 [![npm](https://img.shields.io/npm/v/react-tunnels.svg?style=flat)](https://www.npmjs.org/package/react-tunnels)[![Build Status](http://img.shields.io/travis/javivelasco/react-tunnels/master.svg?style=flat-square)](https://travis-ci.org/javivelasco/react-tunnels)

There is a common use case in React apps where you want to define a `Layout` where the content of some elements are defined by `children` components. For example, you want define `Layout` just once and reuse it for every page but it has a breadcrumb whose steps depend on `children` components. This tiny library allows you to define *tunnels* to render from a element to whatever other element in the App, even elements located on top of the tree. It's like `Portal` but the target is a *component* instead of a *DOM element*.
There is a common use case in React apps where you want to define a `Layout` where the content of some elements is defined by `children` components. For example, you want to define `Layout` just once and reuse it for every page but it has a breadcrumb whose steps depend on `children` components. This tiny library allows you to define *tunnels* to render from an element to whatever another element in the App, even elements located on top of the tree. It's like `Portal` but the target is a *component* instead of a *DOM element*.
### Usage
## Usage
Define a `TunnelPlaceholder` identified by an `id` and decide what properties are going to be passed to its `render` function by defining `Tunnel` components with the **same id** anywhere else in the app. If you define just a single `Tunnel` its props will be passed straight to the `render` function, if there are more than one `Tunnel` for a single `id`, the placeholder `render` function will receive a `item` argument which is an Array containing the `props` for each `Tunnel`. Let's see some examples.
Define a `TunnelPlaceholder` identified by an `id` and decide what properties are going to be passed to its `render` function by defining `Tunnel` components with the **same id** anywhere else in the app. If you define just a single `Tunnel` its props will be passed straight to the `render` function, if there is more than one `Tunnel` for a single `id`, the placeholder `render` function will receive an `item` argument which is an Array containing the `props` for each `Tunnel`. Let's see some examples.
### Simple example: tunneling children
### Simple example: children tunneling
Define a placeholder without any render function so it will render any children coming from `Tunnel` components.
Define a placeholder without any render function so it will render any children coming from a `Tunnel` component with the same id.
```jsx
import { TunnelsProvider, TunnelPlaceholder, Tunnel } from 'react-tunnels'
import { TunnelProvider, TunnelPlaceholder, Tunnel } from 'react-tunnels'
render(
<TunnelsProvider>
<TunnelProvider>
<div>

@@ -35,27 +35,19 @@ <TunnelPlaceholder id="my-tunnel" />

</div>
</TunnelsProvider>
</TunnelProvider>
)
```
### A more complex example: building a Breadcrumb
Check the real example [here](https://codesandbox.io/s/p79k8w0jnq)
### More complex example: building a Breadcrumb
It's easy to build a breadcrumb using the prop `multiple` in the `TunnelPlaceholder`. This allows to let it know that there will be multiple tunnels so the `render` function will be called with an array of props.
```jsx
render(
<TunnelsProvider>
{/* This will render the breadcrumb */}
<Breadcrumbs />
{/* Somewhere else in children */}
<Breadcrumb url="/products">Products</Breadcrumb>
<Breadcrumb url="/products/123">Product <strong>123</strong></Breadcrumb>
</TunnelsProvider>
)
const Breadcrumbs = () => (
<TunnelPlaceholder id="breadcrumb">
{({ items = [] }) => (
<ul>
{items.map(({ children, href }) => (
<li><a href={href}>{children}</a></li>
))}
</ul>
<TunnelPlaceholder id="breadcrumb" multiple>
{({ items }) => (
items.map(({ children, href }) => (
<span><a href={href}>{children}</a></span>
))
)}

@@ -66,15 +58,31 @@ </TunnelPlaceholder>

const Breadcrumb = ({ children, url }) => (
<Tunnel id="breadcrumb" url={url}>
<Tunnel id="breadcrumb" href={url}>
{children}
</Tunnel>
)
render(
<TunnelProvider>
{/* This will render the breadcrumb */}
<Breadcrumbs />
{/* Somewhere else in children */}
<Breadcrumb url="/products">Products</Breadcrumb>
<Breadcrumb url="/products/123">Product <strong>123</strong></Breadcrumb>
</TunnelProvider>
)
```
### Similar Libraries
Check the live example [here](https://codesandbox.io/s/0ym0n37jnl)
- [React Slot Fill](https://github.com/camwest/react-slot-fill): [@camwest](https://github.com/camwest) has built a similar project but with a different API and a bit more limited use cases. The main difference is that you can't pass content to a placeholder from multiple entry points while react-tunnels does it by passing an array with the props defined by each tunnel to the render function of the placeholder. For simple cases, it is pretty similar.
## Similar Libraries
- [React Slot Fill](https://github.com/camwest/react-slot-fill): A similar project built by [Cameron Westland](https://github.com/camwest) with a slightly different API and a bit more limited use cases. The main difference is that you can't pass content to a placeholder from multiple entry points. react-tunnels does this by passing an array with the props defined by each tunnel to the render function of the placeholder. For simple cases though, it is pretty similar.
- [Preact Slots](https://github.com/developit/preact-slots): A library similar to React Slot Fill but for [Preact](https://github.com/developit/preact) developed by [Jason Miller](https://twitter.com/_developit).
## About
This project has been developed by [Javi Velasco](https://twitter.com/javivelasco) as a way to build *Breadcrumb* components and `Layout` customizations for a variety of React projects. Any feeback, help or improvements is highly appreciated.
## License
This project is licensed under the terms of the [MIT license](https://github.com/javivelasco/react-tunnels/blob/master/LICENSE).

@@ -21,4 +21,4 @@ import { Component } from 'react'

componentWillUpdate(nextProps) {
this.setTunnelProps(nextProps)
componentDidUpdate() {
this.setTunnelProps(this.props)
}

@@ -25,0 +25,0 @@

import PropTypes from 'prop-types'
import React, { createElement, Component, Fragment } from 'react'
import React, { Component, Fragment } from 'react'

@@ -7,2 +7,3 @@ class TunnelPlaceholder extends Component {

children: PropTypes.func,
component: PropTypes.oneOfType([PropTypes.node, PropTypes.symbol]),
id: PropTypes.string.isRequired,

@@ -12,2 +13,6 @@ multiple: PropTypes.bool,

static defaultProps = {
component: Fragment,
}
static contextTypes = {

@@ -35,3 +40,8 @@ tunnelState: PropTypes.object,

const { tunnelState } = this.context
const { id, children: renderChildren, multiple } = this.props
const {
id,
children: renderChildren,
component: Tag,
multiple,
} = this.props
const tunnelProps = tunnelState.getTunnelProps(id)

@@ -42,8 +52,8 @@

return !tunnelProps
? createElement(renderChildren, { items: [] })
: createElement(renderChildren, {
? renderChildren({ items: [] })
: renderChildren({
items: Array.isArray(tunnelProps) ? tunnelProps : [tunnelProps],
})
} else {
return createElement(renderChildren, tunnelProps || {})
return renderChildren(tunnelProps || {})
}

@@ -56,3 +66,3 @@ }

return <Fragment>{tunnelProps.children}</Fragment>
return <Tag>{tunnelProps.children}</Tag>
}

@@ -59,0 +69,0 @@ }

@@ -12,6 +12,12 @@ import { configure, mount } from 'enzyme'

const Msg = ({ message }) => (
<div className="msg">{message || 'defaultMessage'}</div>
)
Msg.propTypes = { message: PropTypes.string }
class Msg extends React.Component {
componentDidMount() {
this.props.didMount && this.props.didMount()
}
render() {
const { message } = this.props
return <div className="msg">{message || 'defaultMessage'}</div>
}
}
Msg.propTypes = { message: PropTypes.string, didMount: PropTypes.func }

@@ -24,3 +30,3 @@ describe('Tunnel', () => {

<TunnelPlaceholder id={TUNNEL_ID}>
{({ message }) => <span>{message}</span>}
{({ message }) => <Msg message={message} />}
</TunnelPlaceholder>

@@ -40,3 +46,3 @@ <Tunnel id={TUNNEL_ID} {...props} />

<Tunnel id={TUNNEL_ID}>
<span>{props.message}</span>
<Msg message={props.message} />
</Tunnel>

@@ -89,3 +95,3 @@ </div>

<TunnelPlaceholder id={TUNNEL_ID}>
{({ message }) => <span>{message || 'Empty'}</span>}
{({ message }) => <Msg message={message || 'Empty'} />}
</TunnelPlaceholder>

@@ -109,3 +115,3 @@ </TunnelProvider>,

const Component = (
{ msg, visible }, //eslint-disable-line
{ msg, visible, didMount }, //eslint-disable-line
) => (

@@ -115,3 +121,5 @@ <TunnelProvider>

<TunnelPlaceholder id={TUNNEL_ID}>
{({ message }) => <span>{message || 'Empty'}</span>}
{({ message }) => (
<Msg message={message || 'Empty'} didMount={didMount} />
)}
</TunnelPlaceholder>

@@ -145,7 +153,16 @@ {visible && <Tunnel id={TUNNEL_ID} message={msg} />}

})
it('should keep children mounted on re-render', () => {
let mountSpy = jest.fn()
const wrapper = mount(
<Component msg={msg1} visible didMount={mountSpy} />,
)
wrapper.setProps({ msg: msg2 })
expect(mountSpy).toHaveBeenCalledTimes(1)
})
})
function assertTunnelPlaceholderContent(wrapper, expectedContent) {
expect(wrapper.find(TunnelPlaceholder).text()).toEqual(expectedContent)
expect(wrapper.find(Msg).text()).toEqual(expectedContent)
}
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc