Skip to content
This repository has been archived by the owner on Jul 8, 2023. It is now read-only.

Commit

Permalink
➕ add with-online-status-props
Browse files Browse the repository at this point in the history
  • Loading branch information
deepsweet committed Aug 31, 2017
1 parent 59374e9 commit be94259
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 0 deletions.
11 changes: 11 additions & 0 deletions packages/with-online-status-props/demo/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

import withOnlineStatusProps from '../src/';

const Demo = (props) => (
<h1>props: {JSON.stringify(props)}</h1>
);

export default withOnlineStatusProps(
({ isOnline, isOffline }) => ({ isOnline, isOffline })
)(Demo);
35 changes: 35 additions & 0 deletions packages/with-online-status-props/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@hocs/with-online-status-props",
"library": "withOnlineStatusProps",
"version": "0.1.0",
"description": "Online status HOC for React",
"keywords": [
"react",
"hoc",
"recompose",
"online",
"offline",
"isonline",
"isoffline"
],
"main": "lib/index.js",
"module": "es/index.js",
"files": [
"dist/",
"es/",
"lib/"
],
"repository": "deepsweet/hocs",
"author": "Kir Belevich <kir@belevi.ch> (https://github.com/deepsweet)",
"license": {
"type": "MIT",
"url": "https://github.com/deepsweet/hocs/blob/master/license.md"
},
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"react": "^15.6.1",
"recompose": "^0.25.0"
}
}
36 changes: 36 additions & 0 deletions packages/with-online-status-props/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# :electric_plug: with-online-status-props

[![npm](https://img.shields.io/npm/v/@hocs/with-online-status-props.svg?style=flat-square)](https://www.npmjs.com/package/@hocs/with-online-status-props) [![ci](https://img.shields.io/travis/deepsweet/hocs/master.svg?style=flat-square)](https://travis-ci.org/deepsweet/hocs) [![coverage](https://img.shields.io/codecov/c/github/deepsweet/hocs/master.svg?style=flat-square)](https://codecov.io/github/deepsweet/hocs) [![deps](https://david-dm.org/deepsweet/hocs.svg?path=packages/with-online-status-props&style=flat-square)](https://david-dm.org/deepsweet/hocs?path=packages/with-online-status-props)

Part of a [collection](https://github.com/deepsweet/hocs) of Higher-Order Components for React, especially useful with [Recompose](https://github.com/acdlite/recompose).

Dynamically map online/offline status to boolean props using [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine) ([Can I use?](https://caniuse.com/#feat=online-status)).

## Install

```
yarn add recompose @hocs/with-online-status-props
```

## Usage

```js
withOnlineStatusProps(
mapStatusToProps: (onlineStatus: Object) => Object,
): HigherOrderComponent
```

```js
import React from 'react';
import withOnlineStatusProps from '@hocs/with-online-status-props';

const Demo = (props) => (
<h1>props: {JSON.stringify(props)}</h1>
);

export default withOnlineStatusProps(
({ isOnline, isOffline }) => ({ isOnline, isOffline })
)(Demo);
```

:tv: [Check out live demo](https://www.webpackbin.com/bins/-KsrcK14q0MkyZZIFquj).
64 changes: 64 additions & 0 deletions packages/with-online-status-props/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Component } from 'react';
import { createEagerFactory, setDisplayName, wrapDisplayName } from 'recompose';

const isOnlineStatusSupported = global.navigator && typeof global.navigator.onLine !== 'undefined';

const withOnlineStatusProps = (mapStatusToProps) => (Target) => {
if (!isOnlineStatusSupported) {
return Target;
}

const factory = createEagerFactory(Target);

class WithOnlineStatusProps extends Component {
constructor(props, context) {
super(props, context);

this.state = {
isOnline: global.navigator.onLine,
isOffline: !global.navigator.onLine
};
this.handleOnline = this.handleOnline.bind(this);
this.handleOffline = this.handleOffline.bind(this);
}

componentDidMount() {
global.addEventListener('online', this.handleOnline, false);
global.addEventListener('offline', this.handleOffline, false);
}

componentWillUnmount() {
global.removeEventListener('online', this.handleOnline);
global.removeEventListener('offline', this.handleOffline);
}

handleOnline() {
this.setState({
isOnline: true,
isOffline: false
});
}

handleOffline() {
this.setState({
isOnline: false,
isOffline: true
});
}

render() {
return factory({
...this.props,
...mapStatusToProps(this.state)
});
}
}

if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(Target, 'withOnlineStatusProps'))(WithOnlineStatusProps);
}

return WithOnlineStatusProps;
};

export default withOnlineStatusProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`withOnlineStatusProps Online Status API is not supported should just pass Target component through 1`] = `
<Target
a={1}
b={2}
c={3}
/>
`;

exports[`withOnlineStatusProps Online Status API is supported display name should not wrap display name in production env 1`] = `<WithOnlineStatusProps />`;

exports[`withOnlineStatusProps Online Status API is supported display name should wrap display name in non-production env 1`] = `
<withOnlineStatusProps(Target)>
<Target />
</withOnlineStatusProps(Target)>
`;

exports[`withOnlineStatusProps Online Status API is supported should handle online status change 1`] = `
<Target
isOffline={true}
isOnline={false}
/>
`;

exports[`withOnlineStatusProps Online Status API is supported should handle online status change 2`] = `
<Target
isOffline={false}
isOnline={true}
/>
`;

exports[`withOnlineStatusProps Online Status API is supported should remove event listener on unmount 1`] = `
Object {
"isOffline": false,
"isOnline": true,
}
`;

exports[`withOnlineStatusProps Online Status API is supported should set initial state 1`] = `
<Target
isOffline={false}
isOnline={true}
/>
`;
143 changes: 143 additions & 0 deletions packages/with-online-status-props/test/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React from 'react';
import { mount } from 'enzyme';

const Target = () => null;
const dummyMapStateToProps = () => {};

describe('withOnlineStatusProps', () => {
describe('Online Status API is supported', () => {
let origOnlineStatus = null;
let withOnlineStatusProps = null;

beforeAll(() => {
origOnlineStatus = global.navigator.onLine;
});

beforeEach(() => {
jest.resetModules();

Object.defineProperty(global.navigator, 'onLine', {
get: () => true,
configurable: true
});
withOnlineStatusProps = require('../src/').default;
});

afterAll(() => {
Object.defineProperty(global.navigator, 'onLine', {
get: () => origOnlineStatus,
configurable: true
});
});

it('should set initial state', () => {
const EnhancedTarget = withOnlineStatusProps(
({ isOnline, isOffline }) => ({ isOnline, isOffline })
)(Target);
const wrapper = mount(
<EnhancedTarget/>
);

expect(wrapper.find(Target)).toMatchSnapshot();
});

it('should handle online status change', () => {
const EnhancedTarget = withOnlineStatusProps(
({ isOnline, isOffline }) => ({ isOnline, isOffline })
)(Target);
const wrapper = mount(
<EnhancedTarget/>
);

global.dispatchEvent(new CustomEvent('offline'));
expect(wrapper.find(Target)).toMatchSnapshot();
global.dispatchEvent(new CustomEvent('online'));
expect(wrapper.find(Target)).toMatchSnapshot();
});

it('should remove event listener on unmount', () => {
const EnhancedTarget = withOnlineStatusProps(
({ isOnline, isOffline }) => ({ isOnline, isOffline })
)(Target);
const wrapper = mount(
<EnhancedTarget/>
);
const instance = wrapper.instance();

wrapper.unmount();
global.document.dispatchEvent(new CustomEvent('offline'));
// TODO: mount again and check?
// https://github.com/airbnb/enzyme/pull/969
expect(instance.state).toMatchSnapshot();
});

describe('display name', () => {
let origNodeEnv = null;

beforeAll(() => {
origNodeEnv = process.env.NODE_ENV;
});

afterAll(() => {
process.env.NODE_ENV = origNodeEnv;
});

it('should wrap display name in non-production env', () => {
process.env.NODE_ENV = 'test';

const EnhancedTarget = withOnlineStatusProps(dummyMapStateToProps)(Target);
const wrapper = mount(
<EnhancedTarget/>
);

expect(wrapper).toMatchSnapshot();
});

it('should not wrap display name in production env', () => {
process.env.NODE_ENV = 'production';

const EnhancedTarget = withOnlineStatusProps(dummyMapStateToProps)(Target);
const wrapper = mount(
<EnhancedTarget/>
);

expect(wrapper).toMatchSnapshot();
});
});
});

describe('Online Status API is not supported', () => {
let origOnlineStatus = null;
let withOnlineStatusProps = null;

beforeAll(() => {
origOnlineStatus = global.navigator.onLine;
});

beforeEach(() => {
jest.resetModules();

Object.defineProperty(global.navigator, 'onLine', {
get: () => {},
configurable: true
});
withOnlineStatusProps = require('../src/').default;
});

afterAll(() => {
Object.defineProperty(global.navigator, 'onLine', {
get: () => origOnlineStatus,
configurable: true
});
});

it('should just pass Target component through', () => {
const EnhancedTarget = withOnlineStatusProps(dummyMapStateToProps)(Target);
const wrapper = mount(
<EnhancedTarget a={1} b={2} c={3}/>
);

expect(wrapper).toMatchSnapshot();
});
});
});
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ Dynamically map visibility of a component to boolean props using [Intersection O

Dynamically map page visibility state to boolean props using [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) ([Can I use?](https://caniuse.com/#feat=pagevisibility)).

### :electric_plug: [with-online-status-props](packages/with-online-status-props)

Dynamically map online/offline status to boolean props using [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine) ([Can I use?](https://caniuse.com/#feat=online-status)).

### :hourglass: [debounce-handler](packages/debounce-handler)

Helps to debounce handlers like `onChange`.
Expand Down

0 comments on commit be94259

Please sign in to comment.