diff --git a/src/__tests__/components/Navbar.test.tsx b/src/__tests__/components/Navbar.test.tsx index fea954f6cf..a7234502bf 100644 --- a/src/__tests__/components/Navbar.test.tsx +++ b/src/__tests__/components/Navbar.test.tsx @@ -54,6 +54,40 @@ describe('Navbar', () => { Permissions.ReportIncident, ] + describe('hamberger', () => { + it('should render a hamberger link list', () => { + const wrapper = setup(allPermissions) + const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) + const hamberger = hospitalRunNavbar.find('.nav-hamberger') + const { children } = hamberger.first().props() as any + + expect(children[0].props.children).toEqual('dashboard.label') + expect(children[1].props.children).toEqual('patients.newPatient') + expect(children[children.length - 1].props.children).toEqual('settings.label') + }) + + it('should not show an item if user does not have a permission', () => { + // exclude labs and incidents permissions + const wrapper = setup(cloneDeep(allPermissions).slice(0, 6)) + const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) + const hamberger = hospitalRunNavbar.find('.nav-hamberger') + const { children } = hamberger.first().props() as any + + const labels = [ + 'labs.requests.new', + 'labs.requests.label', + 'incidents.reports.new', + 'incidents.reports.label', + ] + + children.forEach((option: any) => { + labels.forEach((label) => { + expect(option.props.children).not.toEqual(label) + }) + }) + }) + }) + it('should render a HospitalRun Navbar', () => { const wrapper = setup(allPermissions) const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) @@ -83,125 +117,6 @@ describe('Navbar', () => { }) }) - describe('patients', () => { - it('should render a patients link list', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const patientsLinkList = hospitalRunNavbar.find('.patients-link-list') - const { children } = patientsLinkList.first().props() as any - - expect(patientsLinkList.first().props().title).toEqual('patients.label') - expect(children[0].props.children).toEqual('actions.list') - expect(children[1].props.children).toEqual('actions.new') - }) - - it('should navigate to /patients when the list option is selected', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const patientsLinkList = hospitalRunNavbar.find('.patients-link-list') - const { children } = patientsLinkList.first().props() as any - - act(() => { - children[0].props.onClick() - }) - - expect(history.location.pathname).toEqual('/patients') - }) - - it('should navigate to /patients/new when the list option is selected', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const patientsLinkList = hospitalRunNavbar.find('.patients-link-list') - const { children } = patientsLinkList.first().props() as any - - act(() => { - children[1].props.onClick() - }) - - expect(history.location.pathname).toEqual('/patients/new') - }) - }) - - describe('scheduling', () => { - it('should render a scheduling dropdown', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const scheduleLinkList = hospitalRunNavbar.find('.scheduling-link-list') - const { children } = scheduleLinkList.first().props() as any - - expect(scheduleLinkList.first().props().title).toEqual('scheduling.label') - if (scheduleLinkList.first().props().children) { - expect(children[0].props.children).toEqual('scheduling.appointments.label') - expect(children[1].props.children).toEqual('scheduling.appointments.new') - } - }) - - it('should navigate to to /appointments when the appointment list option is selected', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const scheduleLinkList = hospitalRunNavbar.find('.scheduling-link-list') - const { children } = scheduleLinkList.first().props() as any - - act(() => { - children[0].props.onClick() - }) - - expect(history.location.pathname).toEqual('/appointments') - }) - - it('should navigate to /appointments/new when the new appointment list option is selected', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const scheduleLinkList = hospitalRunNavbar.find('.scheduling-link-list') - const { children } = scheduleLinkList.first().props() as any - - act(() => { - children[1].props.onClick() - }) - - expect(history.location.pathname).toEqual('/appointments/new') - }) - }) - - describe('labs', () => { - it('should render a labs dropdown', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const labsLinkList = hospitalRunNavbar.find('.labs-link-list') - const { children } = labsLinkList.first().props() as any - - expect(labsLinkList.first().props().title).toEqual('labs.label') - expect(children[0].props.children).toEqual('labs.label') - expect(children[1].props.children).toEqual('labs.requests.new') - }) - - it('should navigate to to /labs when the labs list option is selected', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const labsLinkList = hospitalRunNavbar.find('.labs-link-list') - const { children } = labsLinkList.first().props() as any - - act(() => { - children[0].props.onClick() - }) - - expect(history.location.pathname).toEqual('/labs') - }) - - it('should navigate to /labs/new when the new labs list option is selected', () => { - const wrapper = setup(allPermissions) - const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const labsLinkList = hospitalRunNavbar.find('.labs-link-list') - const { children } = labsLinkList.first().props() as any - - act(() => { - children[1].props.onClick() - }) - - expect(history.location.pathname).toEqual('/labs/new') - }) - }) - describe('search', () => { it('should render Search as the search box placeholder', () => { const wrapper = setup(allPermissions) @@ -226,7 +141,7 @@ describe('Navbar', () => { it('should show a shortcut if user has a permission', () => { const wrapper = setup(allPermissions) const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const addNew = hospitalRunNavbar.find('.add-new') + const addNew = hospitalRunNavbar.find('.nav-add-new') const { children } = addNew.first().props() as any expect(children[0].props.children).toEqual('patients.newPatient') @@ -236,7 +151,7 @@ describe('Navbar', () => { // exclude labs and incidents permissions const wrapper = setup(cloneDeep(allPermissions).slice(0, 6)) const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) - const addNew = hospitalRunNavbar.find('.add-new') + const addNew = hospitalRunNavbar.find('.nav-add-new') const { children } = addNew.first().props() as any children.forEach((option: any) => { @@ -245,4 +160,28 @@ describe('Navbar', () => { }) }) }) + + describe('account', () => { + it('should render an account link list', () => { + const wrapper = setup(allPermissions) + const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) + const accountLinkList = hospitalRunNavbar.find('.nav-account') + const { children } = accountLinkList.first().props() as any + + expect(children[0].props.children).toEqual('settings.label') + }) + + it('should navigate to /settings when the list option is selected', () => { + const wrapper = setup(allPermissions) + const hospitalRunNavbar = wrapper.find(HospitalRunNavbar) + const accountLinkList = hospitalRunNavbar.find('.nav-account') + const { children } = accountLinkList.first().props() as any + + act(() => { + children[0].props.onClick() + }) + + expect(history.location.pathname).toEqual('/settings') + }) + }) }) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 24018f6645..cdd8dc9e0b 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { useHistory } from 'react-router-dom' -import Permissions from '../model/Permissions' +import pageMap, { Page } from '../pageMap' import { RootState } from '../store' const Navbar = () => { @@ -12,50 +12,45 @@ const Navbar = () => { const { t } = useTranslation() const history = useHistory() - const addPages = [ - { - permission: Permissions.WritePatients, - label: t('patients.newPatient'), - path: '/patients/new', - }, - { - permission: Permissions.WriteAppointments, - label: t('scheduling.appointments.new'), - path: '/appointments/new', - }, - { - permission: Permissions.RequestLab, - label: t('labs.requests.new'), - path: '/labs/new', - }, - { - permission: Permissions.ReportIncident, - label: t('incidents.reports.new'), - path: '/incidents/new', - }, - ] + const navigateTo = (location: string) => { + history.push(location) + } - const addDropdownList: { type: string; label: string; onClick: () => void }[] = addPages - .filter((page) => permissions.includes(page.permission)) - .map((page) => ({ - type: 'link', - label: page.label, - onClick: () => { - history.push(page.path) - }, - })) + function getDropdownListOfPages(pages: Page[]) { + return pages + .filter((page) => !page.permission || permissions.includes(page.permission)) + .map((page) => ({ + type: 'link', + label: t(page.label), + onClick: () => { + navigateTo(page.path) + }, + })) + } + + // For Mobile, hamburger menu + const hambergerPages = Object.keys(pageMap).map((key) => pageMap[key]) + + // For Desktop, add shortcuts menu + const addPages = [pageMap.newPatient, pageMap.newAppointment, pageMap.newLab, pageMap.newIncident] return ( { - history.push('/') + navigateTo('/') }, className: 'nav-icon', }, @@ -63,77 +58,14 @@ const Navbar = () => { type: 'header', label: 'HospitalRun', onClick: () => { - history.push('/') + navigateTo('/') }, className: 'nav-header', }, - { - type: 'link-list', - label: t('patients.label'), - className: 'patients-link-list d-md-none d-block', - children: [ - { - type: 'link', - label: t('actions.list'), - onClick: () => { - history.push('/patients') - }, - }, - { - type: 'link', - label: t('actions.new'), - onClick: () => { - history.push('/patients/new') - }, - }, - ], - }, - { - type: 'link-list', - label: t('scheduling.label'), - className: 'scheduling-link-list d-md-none d-block', - children: [ - { - type: 'link', - label: t('scheduling.appointments.label'), - onClick: () => { - history.push('/appointments') - }, - }, - { - type: 'link', - label: t('scheduling.appointments.new'), - onClick: () => { - history.push('/appointments/new') - }, - }, - ], - }, - { - type: 'link-list', - label: t('labs.label'), - className: 'labs-link-list d-md-none d-block', - children: [ - { - type: 'link', - label: t('labs.label'), - onClick: () => { - history.push('/labs') - }, - }, - { - type: 'link', - label: t('labs.requests.new'), - onClick: () => { - history.push('/labs/new') - }, - }, - ], - }, { type: 'search', placeholderText: t('actions.search'), - className: 'ml-auto nav-search', + className: 'ml-auto d-none d-md-block nav-search', buttonText: t('actions.search'), buttonColor: 'secondary', onClickButton: () => undefined, @@ -142,8 +74,8 @@ const Navbar = () => { { type: 'link-list-icon', alignRight: true, - children: addDropdownList, - className: 'pl-4 add-new', + children: getDropdownListOfPages(addPages), + className: 'pl-4 nav-add-new d-none d-md-block', iconClassName: 'align-bottom', label: 'Add', name: 'add', @@ -157,11 +89,11 @@ const Navbar = () => { type: 'link', label: t('settings.label'), onClick: () => { - history.push('/settings') + navigateTo('/settings') }, }, ], - className: 'pl-2', + className: 'pl-2 d-none d-md-block nav-account', iconClassName: 'align-bottom', label: 'Patient', name: 'patient', diff --git a/src/pageMap.tsx b/src/pageMap.tsx new file mode 100644 index 0000000000..235c71b9d2 --- /dev/null +++ b/src/pageMap.tsx @@ -0,0 +1,61 @@ +import Permissions from './model/Permissions' + +type Page = { permission: Permissions | null; label: string; path: string } + +const pageMap: { + [key: string]: Page +} = { + dashboard: { + permission: null, + label: 'dashboard.label', + path: '/', + }, + newPatient: { + permission: Permissions.WritePatients, + label: 'patients.newPatient', + path: '/patients/new', + }, + viewPatients: { + permission: Permissions.ReadPatients, + label: 'patients.patientsList', + path: '/patients', + }, + newAppointment: { + permission: Permissions.WriteAppointments, + label: 'scheduling.appointments.new', + path: '/appointments/new', + }, + viewAppointments: { + permission: Permissions.ReadAppointments, + label: 'scheduling.appointments.schedule', + path: '/appointments', + }, + newLab: { + permission: Permissions.RequestLab, + label: 'labs.requests.new', + path: '/labs/new', + }, + viewLabs: { + permission: Permissions.ViewLabs, + label: 'labs.requests.label', + path: '/labs', + }, + newIncident: { + permission: Permissions.ReportIncident, + label: 'incidents.reports.new', + path: '/incidents/new', + }, + viewIncidents: { + permission: Permissions.ViewIncidents, + label: 'incidents.reports.label', + path: '/incidents', + }, + settings: { + permission: null, + label: 'settings.label', + path: '/settings', + }, +} + +export default pageMap +export type { Page } diff --git a/src/user/user-slice.ts b/src/user/user-slice.ts index cd25a963c6..30d0f8e07b 100644 --- a/src/user/user-slice.ts +++ b/src/user/user-slice.ts @@ -4,7 +4,7 @@ import Permissions from '../model/Permissions' import User from '../model/User' interface UserState { - permissions: Permissions[] + permissions: (Permissions | null)[] user: User }