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

feat(network-status): Notify users when they're working offline #2109

Merged
merged 8 commits into from
Jun 7, 2020
2 changes: 2 additions & 0 deletions src/HospitalRun.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Switch, Route } from 'react-router-dom'

import Breadcrumbs from './breadcrumbs/Breadcrumbs'
import Navbar from './components/Navbar'
import { NetworkStatusMessage } from './components/network-status'
import PrivateRoute from './components/PrivateRoute'
import Sidebar from './components/Sidebar'
import Dashboard from './dashboard/Dashboard'
Expand All @@ -23,6 +24,7 @@ const HospitalRun = () => {

return (
<div>
<NetworkStatusMessage />
<Navbar />
<div className="container-fluid">
<Sidebar />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { render, shallow } from 'enzyme'
import React from 'react'

import { NetworkStatusMessage } from '../../../components/network-status'
import {
OFFLINE_MESSAGE,
ONLINE_MESSAGE,
} from '../../../components/network-status/NetworkStatusMessage'
import { useNetworkStatus } from '../../../components/network-status/useNetworkStatus'

jest.mock('../../../components/network-status/useNetworkStatus')
const useNetworkStatusCast = (useNetworkStatus as unknown) as jest.MockInstance<
ReturnType<typeof useNetworkStatus>,
any
>

describe('NetworkStatusMessage', () => {
it('returns null if the app has always been online', () => {
useNetworkStatusCast.mockReturnValue({
isOnline: true,
wasOffline: false,
})
const wrapper = shallow(<NetworkStatusMessage />)
expect(wrapper.equals(null as any)).toBe(true)
})
it(`shows the message "${OFFLINE_MESSAGE}" if the app goes offline`, () => {
useNetworkStatusCast.mockReturnValue({
isOnline: false,
wasOffline: false,
})
const wrapper = render(<NetworkStatusMessage />)
expect(wrapper.text()).toContain(OFFLINE_MESSAGE)
})
it(`shows the message "${ONLINE_MESSAGE}" if the app goes back online after it was offline`, () => {
useNetworkStatusCast.mockReturnValue({
isOnline: true,
wasOffline: true,
})
const wrapper = render(<NetworkStatusMessage />)
expect(wrapper.text()).toContain(ONLINE_MESSAGE)
})
})
39 changes: 39 additions & 0 deletions src/components/network-status/NetworkStatusMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react'

import { useNetworkStatus } from './useNetworkStatus'
import './styles.css'

export const OFFLINE_MESSAGE = 'you are working in offline mode'
export const ONLINE_MESSAGE = 'you are back online'
This conversation was marked as resolved.
Show resolved Hide resolved
const OPACITY_TRANSITION_TIME = 4000

export const NetworkStatusMessage = () => {
const { isOnline, wasOffline } = useNetworkStatus()
const [display, setDisplay] = useState('flex')
const [opacity, setOpacity] = useState(1)

if (isOnline && !wasOffline) {
return null
}

if (!isOnline && opacity !== 1) {
setDisplay('flex')
setOpacity(1)
}

if (isOnline && wasOffline && opacity !== 0) {
setOpacity(0)
setTimeout(() => {
setDisplay('none')
}, OPACITY_TRANSITION_TIME)
}

return (
<div
className={`network-status-message ${isOnline ? 'online' : 'offline'}`}
style={{ display, opacity, transition: `opacity ${OPACITY_TRANSITION_TIME}ms ease-in` }}
>
{isOnline ? ONLINE_MESSAGE : OFFLINE_MESSAGE}
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/network-status/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { NetworkStatusMessage } from './NetworkStatusMessage'
export { useNetworkStatus } from './useNetworkStatus'
18 changes: 18 additions & 0 deletions src/components/network-status/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.network-status-message {
align-items: center;
bottom: 0;
height: 50px;
justify-content: center;
pointer-events: none;
position: absolute;
width: 100vw;
z-index: 9999;
}

.network-status-message.offline {
background-color: rgba(255, 0, 0, 0.65);
}

.network-status-message.online {
background-color: rgba(0, 255, 0, 0.55);
}
This conversation was marked as resolved.
Show resolved Hide resolved
4 changes: 4 additions & 0 deletions src/components/network-status/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface NetworkStatus {
isOnline: boolean
wasOffline: boolean
}
27 changes: 27 additions & 0 deletions src/components/network-status/useNetworkStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useState, useEffect } from 'react'

import { NetworkStatus } from './types'

export const useNetworkStatus = (): NetworkStatus => {
const [networkStatus, setNetworkStatus] = useState({
isOnline: true,
wasOffline: false,
})
const handleOnline = () => {
setNetworkStatus((prevState) => ({ ...prevState, isOnline: true }))
}
const handleOffline = () => {
setNetworkStatus((prevState) => ({ ...prevState, isOnline: false, wasOffline: true }))
}
useEffect(() => {
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)

return () => {
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
}
}, [])

return networkStatus
}