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,51 @@
import { render, shallow } from 'enzyme'
import React from 'react'

import { useTranslation } from '../../../__mocks__/react-i18next'
import { NetworkStatusMessage } from '../../../components/network-status'
import { useNetworkStatus } from '../../../components/network-status/useNetworkStatus'

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

const englishTranslationsMock = {
'networkStatus.offline': 'you are working in offline mode',
'networkStatus.online': 'you are back online',
}

const useTranslationReturnValue = useTranslation() as any
useTranslationReturnValue.t = (key: keyof typeof englishTranslationsMock) =>
englishTranslationsMock[key]
const { t } = useTranslationReturnValue

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

import { useNetworkStatus } from './useNetworkStatus'

const ONLINE_COLOR = 'rgba(0, 255, 0, 0.55)'
const OFFLINE_COLOR = 'rgba(255, 0, 0, 0.65)'
const OPACITY_TRANSITION_TIME = 4000
const BASE_STYLE = {
height: '50px',
pointerEvents: 'none' as 'none',
transition: `opacity ${OPACITY_TRANSITION_TIME}ms ease-in`,
}

export const NetworkStatusMessage = () => {
const { t } = useTranslation()
const { isOnline, wasOffline } = useNetworkStatus()
const [shouldRender, setShouldRender] = useState(true)
const [opacity, setOpacity] = useState(1)

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

if (!isOnline && opacity !== 1) {
setShouldRender(true)
setOpacity(1)
}

if (isOnline && wasOffline && opacity !== 0) {
setOpacity(0)
setTimeout(() => {
setShouldRender(false)
}, OPACITY_TRANSITION_TIME)
}

if (!shouldRender) {
return null
}

const style = {
...BASE_STYLE,
backgroundColor: isOnline ? ONLINE_COLOR : OFFLINE_COLOR,
opacity,
}

return (
<div
className={`fixed-bottom d-flex justify-content-center align-items-center ${
isOnline ? 'online' : 'offline'
}`}
style={style}
>
{isOnline ? t('networkStatus.online') : t('networkStatus.offline')}
</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'
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
}
28 changes: 28 additions & 0 deletions src/components/network-status/useNetworkStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useState, useEffect } from 'react'

import { NetworkStatus } from './types'

export const useNetworkStatus = (): NetworkStatus => {
const isOnline = navigator.onLine
const [networkStatus, setNetworkStatus] = useState({
isOnline,
wasOffline: !isOnline,
})
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
}
2 changes: 2 additions & 0 deletions src/locales/enUs/translations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import actions from './actions'
import dashboard from './dashboard'
import incidents from './incidents'
import labs from './labs'
import networkStatus from './network-status'
import patient from './patient'
import patients from './patients'
import scheduling from './scheduling'
Expand All @@ -12,6 +13,7 @@ import states from './states'
export default {
...actions,
...dashboard,
...networkStatus,
...patient,
...patients,
...scheduling,
Expand Down
6 changes: 6 additions & 0 deletions src/locales/enUs/translations/network-status/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
networkStatus: {
offline: 'you are working in offline mode',
online: 'you are back online',
},
}