npm package: @linio/react-notifications
Description: A notification system for React built with the Context API
Latest version: 0.2.1

<div align="center">
  <a href="https://www.emojione.com/emoji/1f50a">
    <img width="42" height="42" alt="speaker high volume" src="https://github.com/LinioIT/react-notifications/raw/master/logo.png" />
  </a>
  <h1>react-notifications</h1>
  
  <p>A notification system for React built with the <a href="https://reactjs.org/docs/context.html">Context API</a>.</p>
</div>

<br/>

```
yarn add @linio/react-notifications
```

## Features

- Load and store notifications to external services (defaults to `localStorage`)
- Read/unread status
- Toast notifications
- Push (web) notifications

## User Guide

### Table of Contents

- [Getting Started](#getting-started)
  - [`NotificationProvider`](#notificationprovider)
  - [`NotificationConsumer`](#notificationconsumer)
- [Creating a notification](#creating-a-notification)
  - [`context.create`](#contextcreate)
- [The notification object](#the-notification-object)
  - [`.persist()`](#persist)
  - [`.toast()`](#toast)
  - [`.push()`](#push)
- [Accessing persisted notifications](#accessing-persisted-notifications)
  - [`context.notifications`](#contextnotifications)
- [Marking a notification as read](#marking-a-notification-as-read)
  - [`context.markAsRead`](#contextmarkasread)
- [Writing your own `storageProvider`](#writing-your-own-storageprovider)
  - [`storageProvider.load`](#storageproviderload)
  - [`storageProvider.store`](#storageproviderstore)

### Getting Started

`@linio/react-notifications` is built on top of React's Context API, therefore exposing a `Provider` and `Consumer` pair:

```javascript
import { NotificationProvider, NotificationConsumer } from '@linio/react-notifications';
```

##### `NotificationProvider`

The `NotificationProvider` "provides" the context of your notification system to your app. It _should_ be added to the top of your component tree. If you are using other `Provider` components from libraries such as [redux](https://github.com/reduxjs/redux) or [styled-components](https://github.com/styled-components/styled-components) then this concept should be familiar to you.


```javascript
// App.js

import { NotificationProvider } from '@linio/react-notifications';

class App extends Component {
  // ...

  render() {
    return (
      <NotificationProvider>
        {/* App code */}
      </NotificationProvider>
    );
  }
}
```

##### `NotificationConsumer`

The `NotificationConsumer` allows you to "consume" the context of your notification system. It _must render at any level beneath_ `NotificationProvider` in the application tree.

```javascript
// MyComponent.js

import { NotificationConsumer } from '@linio/react-notifications';

const MyComponent = () => (
  <NotificationConsumer>
    {context => {
      /* where the magic happens! */
    }}
  </NotificationConsumer>
);
```

```javascript
// App.js

import { NotificationProvider } from '@linio/react-notifications'
import MyComponent from './MyComponent'

class App extends Component {
  // ...

  render() {
    return (
      <NotificationProvider>
        <MyComponent />
      </NotificationProvider>
    }
  }
}
```

`MyComponent` now has access to your notification system's context! 💥

### Creating a notification

A notification can be created using the `context.create` method. 

##### `context.create`

```javascript
<NotificationConsumer>
  {({ create }) => {
    const notification = create(options);
  }}
</NotificationConsumer>
```

**options**

##### title 
`type`: _string_

`required`: _true_

The title that will appear on the notification if rendered.

##### level 
`type`: _'SUCCESS'_, _'WARNING'_ or _'ERROR'_

`required`: _true_

Assigns a priority level to the notification. Also used internally for styling toast notifications.

##### body 
`type`: _string_ or _Function_

`required`: _false_

```javascript
// text body

const notification = create({
  title: 'Rich notification',
  level: 'SUCCESS',
  body: 'Im boring!'
});
```

```javascript
// rich notification body

const notification = create({
  title: 'Rich notification',
  level: 'SUCCESS',
  body: () => <p>Im rich!</p>
});
```

The `body` property is used by both toast and push notifications, however, push notification can _only_ receive a `body` with the type `string`. Toast notifications, on the other hand, can have a `body` with the type `string` or `Function`, where the `Function` should resemble a [stateless functional component](https://reactjs.org/docs/components-and-props.html#functional-and-class-components) and returns some `html`.

If a `Function` is passed, `@linio/react-notifications` will call that function and store the resulting `html` for later use. This allows you to add more than just text. A link perhaps? 😏

##### requireInteraction
`type`: _boolean_

`required`: _false_

If set to `true`, the notification will appear static on the screen (either as a toast or push notification) without dismissing itself after a set period of time (~5s).

### The notification object

When you call the `create` method, a notification object is returned, allowing you to take action in any way you see fit:

```javascript
const notification = create(options);

notification.persist();
notification.toast();
notification.push();
```

##### `.persist()`

Stores the notification in `context.notifications`.

##### `.toast()`

Renders a toast notification.

##### `.push()`

Creates a web notification by first checking the user's browser permissions. If denied, it will create a toast notification.

<br />

By default, the `toast` and `push` methods do not "persist" notifications. They will simply render them to the screen and then disappear quietly into the ether. However, you _can_ chain the `persist` method to opt-in to such functionality:

```javascript
// render a toast notification and persist it for later access

notification
  .toast()
  .persist();
```

```javascript
// render a push notification and persist it for later access

notification
  .push()
  .persist();
```

### Accessing persisted notifications

Notifications that have been persisted with `.persist` or that have been loaded from a `storageProvider` are accessible via the `context.notifications` array.

##### `context.notifications`


```javascript
<NotificationConsumer>
  {({ notifications }) => notifications.map(notification => { /* do stuff */})}
</NotificationConsumer>
```

```javascript
// structure of a notification

{
  id: string,
  title: string,
  level: 'SUCCESS' | 'WARNING' | 'ERROR',
  timestamp: Date,
  read: boolean,
  options: {
    body?: string | Function,
    requireInteraction?: boolean
  }
}
```

### Marking a notification as read

When a notification is created, it will contain a `read` property that defaults to `false`. If you are persisting notifications and rendering them somewhere in your application, it is possible to expose read/unread functionality to your users through the `context.markAsRead` method:

##### `context.markAsRead`

```javascript
<NotificationConsumer>
  {({ markAsRead }) => {
    // ...
    markAsRead(notification.id)
  }}
</NotificationConsumer>
```

**options**

##### id 
`type`: _string_

`required`: _true_

The id of the notification you wish to mark as read.

### Writing your own `storageProvider`

By default, `@linio/react-notifications` will load and store persisted notifications from `localStorage`. This behavior requires no configuration on your part. You can, however, override this behavior to load and store persisted notifications from an external service of your own using a `storageProvider`.

A `storageProvider` is an object containing two methods, `load` and `store`. Each method is called at certain points of your application's lifecycle.

```javascript
// storageProvider.js

export const load = () => notifications
export const store = data => {} 
```

To link the `storageProvider` to your notification system, pass the functions as `load` and `store` props to `NotificationProvider`.  

```javascript
// App.js

import { NotificationProvider } from '@linio/react-notifications';
import { load, store } from './storageProvider';

class App extends Component {
  // ...

  render() {
    return (
      <NotificationProvider load={load} store={store}>
        {/* App code */}
      </NotificationProvider>
    );
  }
}
```

Alternatively, if your app supports [object rest/spread](https://github.com/tc39/proposal-object-rest-spread), you can use this nifty syntactic sugar:

```javascript
// App.js
import { NotificationProvider } from '@linio/react-notifications';
import * as storageProvider from './storageProvider';

class App extends Component {
  // ...

  render() {
    return (
      <NotificationProvider {...storageProvider}>
        {/* App code */}
      </NotificationProvider>
    );
  }
}
```

##### `storageProvider.load`

Should return an array of persisted notifications.

```javascript
// storageProvider.js

const load = () => { /* returns an array of notifications */ }
```

**When is `storageProvider.load` called?**

The `storageProvider.load` method is called when `NotificationProvider` mounts, and it will initialize your notification system's context with the returned array.

##### `storageProvider.store`

Accepts a single notification or an array of notifications.

```javascript
// storageProvider.js

const store = data => { /* data === notification || Array<notification>*/}
```

**When is `storageProvider.store` called?**

Each time a notification is persisted using the `.persist()` method, `@linio/react-notifications` will check to see if you have provided a custom `storageProvider` with a `store` method on it. If you have, this method will receive the newly persisted notification.

## Contributing

```
git clone git@github.com:LinioIT/react-notifications.git

yarn test
```

### Tooling

`@linio/react-notifications` uses the following tools internally:

- [babel](https://babeljs.io/) 🐠 for transpiling JavaScript for the browser
- [rollup](https://rollupjs.org/guide/en) to bundle for production
- [prettier](https://github.com/prettier/prettier) for code formatting
- [jest](https://jestjs.io/) 🃏 test runner